From 19949c49ff9c302f502ec28e773403071bdfce08 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 29 Jan 2015 18:10:47 +0000 Subject: dt-bindings: Add vendor prefix for Raspberry Pi Since the prefix is already in use, we need to add it in the vendor list. Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Stefan Wahren Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 80339192c93e..3fc90ac4f801 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -159,6 +159,7 @@ radxa Radxa raidsonic RaidSonic Technology GmbH ralink Mediatek/Ralink Technology Corp. ramtron Ramtron International +raspberrypi Raspberry Pi Foundation realtek Realtek Semiconductor Corp. renesas Renesas Electronics Corporation ricoh Ricoh Co. Ltd. -- cgit v1.2.1 From ba2853148649f489e98a397345927c54341496d9 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 29 Jan 2015 18:10:48 +0000 Subject: dt-bindings: Add root properties for Raspberry Pi This patch adds root compatible properties for the following boards: - Raspberry Pi Model A - Raspberry Pi Model A+ - Raspberry Pi Model B - Raspberry Pi Model B (no P5) - Raspberry Pi Model B rev2 - Raspberry Pi Model B+ - Raspberry Pi Compute Module Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Stefan Wahren Signed-off-by: Lee Jones --- .../devicetree/bindings/arm/bcm/brcm,bcm2835.txt | 31 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt index ac683480c486..c78576bb7729 100644 --- a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt +++ b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt @@ -1,8 +1,35 @@ Broadcom BCM2835 device tree bindings ------------------------------------------- -Boards with the BCM2835 SoC shall have the following properties: +Raspberry Pi Model A +Required root node properties: +compatible = "raspberrypi,model-a", "brcm,bcm2835"; -Required root node property: +Raspberry Pi Model A+ +Required root node properties: +compatible = "raspberrypi,model-a-plus", "brcm,bcm2835"; +Raspberry Pi Model B +Required root node properties: +compatible = "raspberrypi,model-b", "brcm,bcm2835"; + +Raspberry Pi Model B (no P5) +early model B with I2C0 rather than I2C1 routed to the expansion header +Required root node properties: +compatible = "raspberrypi,model-b-i2c0", "brcm,bcm2835"; + +Raspberry Pi Model B rev2 +Required root node properties: +compatible = "raspberrypi,model-b-rev2", "brcm,bcm2835"; + +Raspberry Pi Model B+ +Required root node properties: +compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; + +Raspberry Pi Compute Module +Required root node properties: +compatible = "raspberrypi,compute-module", "brcm,bcm2835"; + +Generic BCM2835 board +Required root node properties: compatible = "brcm,bcm2835"; -- cgit v1.2.1 From a0cfe6f1fed7682c204768673e73c49531f61ba9 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 29 Jan 2015 18:10:49 +0000 Subject: ARM: bcm2835: Add header file for pinctrl constants This new header file defines pincontrol constants to use from bcm2835 DTS files for pincontrol properties option. Reviewed-by: Stephen Warren Signed-off-by: Stefan Wahren Signed-off-by: Lee Jones --- include/dt-bindings/pinctrl/bcm2835.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 include/dt-bindings/pinctrl/bcm2835.h diff --git a/include/dt-bindings/pinctrl/bcm2835.h b/include/dt-bindings/pinctrl/bcm2835.h new file mode 100644 index 000000000000..6f0bc37af39c --- /dev/null +++ b/include/dt-bindings/pinctrl/bcm2835.h @@ -0,0 +1,27 @@ +/* + * Header providing constants for bcm2835 pinctrl bindings. + * + * Copyright (C) 2015 Stefan Wahren + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __DT_BINDINGS_PINCTRL_BCM2835_H__ +#define __DT_BINDINGS_PINCTRL_BCM2835_H__ + +/* brcm,function property */ +#define BCM2835_FSEL_GPIO_IN 0 +#define BCM2835_FSEL_GPIO_OUT 1 +#define BCM2835_FSEL_ALT5 2 +#define BCM2835_FSEL_ALT4 3 +#define BCM2835_FSEL_ALT0 4 +#define BCM2835_FSEL_ALT1 5 +#define BCM2835_FSEL_ALT2 6 +#define BCM2835_FSEL_ALT3 7 + +#endif /* __DT_BINDINGS_PINCTRL_BCM2835_H__ */ -- cgit v1.2.1 From 6e2320504b84336868ffb36ab4de5485291dc33f Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 29 Jan 2015 18:10:50 +0000 Subject: ARM: bcm2835: Use pinctrl header This patch converts all bcm2835 dts and dtsi files to use the pinctrl header file. Reviewed-by: Stephen Warren Signed-off-by: Stefan Wahren Signed-off-by: Lee Jones --- arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 4 ++-- arch/arm/boot/dts/bcm2835-rpi-b.dts | 4 ++-- arch/arm/boot/dts/bcm2835-rpi.dtsi | 8 ++++---- arch/arm/boot/dts/bcm2835.dtsi | 3 ++- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts index e479515099c3..668442b1bda5 100644 --- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts @@ -1,5 +1,5 @@ /dts-v1/; -/include/ "bcm2835-rpi.dtsi" +#include "bcm2835-rpi.dtsi" / { compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; @@ -25,6 +25,6 @@ /* I2S interface */ i2s_alt0: i2s_alt0 { brcm,pins = <18 19 20 21>; - brcm,function = <4>; /* alt0 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835-rpi-b.dts b/arch/arm/boot/dts/bcm2835-rpi-b.dts index bafa46fc226a..ee89b79426cf 100644 --- a/arch/arm/boot/dts/bcm2835-rpi-b.dts +++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts @@ -1,5 +1,5 @@ /dts-v1/; -/include/ "bcm2835-rpi.dtsi" +#include "bcm2835-rpi.dtsi" / { compatible = "raspberrypi,model-b", "brcm,bcm2835"; @@ -18,6 +18,6 @@ /* I2S interface */ i2s_alt2: i2s_alt2 { brcm,pins = <28 29 30 31>; - brcm,function = <6>; /* alt2 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi index c7064487017d..46780bb48bbf 100644 --- a/arch/arm/boot/dts/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi @@ -1,4 +1,4 @@ -/include/ "bcm2835.dtsi" +#include "bcm2835.dtsi" / { memory { @@ -21,17 +21,17 @@ gpioout: gpioout { brcm,pins = <6>; - brcm,function = <1>; /* GPIO out */ + brcm,function = ; }; alt0: alt0 { brcm,pins = <0 1 2 3 4 5 7 8 9 10 11 14 15 40 45>; - brcm,function = <4>; /* alt0 */ + brcm,function = ; }; alt3: alt3 { brcm,pins = <48 49 50 51 52 53>; - brcm,function = <7>; /* alt3 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 3342cb1407bc..be9c91439bcf 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -1,4 +1,5 @@ -/include/ "skeleton.dtsi" +#include +#include "skeleton.dtsi" / { compatible = "brcm,bcm2835"; -- cgit v1.2.1 From 2afe60620ecb6350fd27806f5185b8e870348732 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 18 Mar 2015 11:00:22 +0200 Subject: ARM: bcm2835: Fix i2c0 node name Device tree node names should contain the node's reg property address value. The i2c0 node was apparently forgotten in commit 25b2f1bd0b7e0 (ARM: bcm2835: node name unit address cleanup). Acked-by: Stephen Warren Signed-off-by: Baruch Siach Signed-off-by: Lee Jones --- arch/arm/boot/dts/bcm2835.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index be9c91439bcf..4d4c1294c082 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -113,7 +113,7 @@ status = "disabled"; }; - i2c0: i2c@20205000 { + i2c0: i2c@7e205000 { compatible = "brcm,bcm2835-i2c"; reg = <0x7e205000 0x1000>; interrupts = <2 21>; -- cgit v1.2.1 From a52e75959f466e8dea1347ff3b675784dce178e7 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 23 Apr 2015 10:49:11 -0700 Subject: ARM: bcm2835: Skip doing our own iotable_init() initialization The only thing we were using this 16MB mapping of IO peripherals for was the uart's early debug mapping. If we just drop the map_io hook, the kernel will call debug_ll_io_init() for us, which maps the single page needed for the device. Signed-off-by: Eric Anholt Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Lee Jones --- arch/arm/mach-bcm/board_bcm2835.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 70f2f3925f0e..9851ee8e5e87 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -31,10 +31,6 @@ #define PM_RSTC_WRCFG_FULL_RESET 0x00000020 #define PM_RSTS_HADWRH_SET 0x00000040 -#define BCM2835_PERIPH_PHYS 0x20000000 -#define BCM2835_PERIPH_VIRT 0xf0000000 -#define BCM2835_PERIPH_SIZE SZ_16M - static void __iomem *wdt_regs; /* @@ -93,18 +89,6 @@ static void bcm2835_power_off(void) bcm2835_restart(REBOOT_HARD, ""); } -static struct map_desc io_map __initdata = { - .virtual = BCM2835_PERIPH_VIRT, - .pfn = __phys_to_pfn(BCM2835_PERIPH_PHYS), - .length = BCM2835_PERIPH_SIZE, - .type = MT_DEVICE -}; - -static void __init bcm2835_map_io(void) -{ - iotable_init(&io_map, 1); -} - static void __init bcm2835_init(void) { int ret; @@ -129,7 +113,6 @@ static const char * const bcm2835_compat[] = { }; DT_MACHINE_START(BCM2835, "BCM2835") - .map_io = bcm2835_map_io, .init_irq = irqchip_init, .init_machine = bcm2835_init, .restart = bcm2835_restart, -- cgit v1.2.1 From 31fd7fe00b0d81f0d1707d1685df4bbe1df886ee Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 24 Apr 2015 12:08:53 -0700 Subject: ARM: bcm2835: Drop the init_irq() hook This is the default function that gets called if the hook is NULL. Signed-off-by: Eric Anholt Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Lee Jones --- arch/arm/mach-bcm/board_bcm2835.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 9851ee8e5e87..49dd5b05f702 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -113,7 +113,6 @@ static const char * const bcm2835_compat[] = { }; DT_MACHINE_START(BCM2835, "BCM2835") - .init_irq = irqchip_init, .init_machine = bcm2835_init, .restart = bcm2835_restart, .dt_compat = bcm2835_compat -- cgit v1.2.1 From e46abe80a9113a9b504aaecec518442ef1f5e666 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 24 Apr 2015 12:08:54 -0700 Subject: ARM: bcm2835: Move the restart/power_off handling to the WDT driver. Since the WDT is what's used to drive restart and power off, it makes more sense to keep it there, where the regs are already mapped and definitions for them provided. Note that this means you may need to add CONFIG_BCM2835_WDT to retain functionality of your kernel. Signed-off-by: Eric Anholt Acked-by: Guenter Roeck Signed-off-by: Lee Jones --- arch/arm/mach-bcm/board_bcm2835.c | 73 --------------------------------------- drivers/watchdog/bcm2835_wdt.c | 62 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 73 deletions(-) diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 49dd5b05f702..0f7b9eac3d15 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -12,7 +12,6 @@ * GNU General Public License for more details. */ -#include #include #include #include @@ -22,81 +21,10 @@ #include #include -#define PM_RSTC 0x1c -#define PM_RSTS 0x20 -#define PM_WDOG 0x24 - -#define PM_PASSWORD 0x5a000000 -#define PM_RSTC_WRCFG_MASK 0x00000030 -#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 -#define PM_RSTS_HADWRH_SET 0x00000040 - -static void __iomem *wdt_regs; - -/* - * The machine restart method can be called from an atomic context so we won't - * be able to ioremap the regs then. - */ -static void bcm2835_setup_restart(void) -{ - struct device_node *np = of_find_compatible_node(NULL, NULL, - "brcm,bcm2835-pm-wdt"); - if (WARN(!np, "unable to setup watchdog restart")) - return; - - wdt_regs = of_iomap(np, 0); - WARN(!wdt_regs, "failed to remap watchdog regs"); -} - -static void bcm2835_restart(enum reboot_mode mode, const char *cmd) -{ - u32 val; - - if (!wdt_regs) - return; - - /* use a timeout of 10 ticks (~150us) */ - writel_relaxed(10 | PM_PASSWORD, wdt_regs + PM_WDOG); - val = readl_relaxed(wdt_regs + PM_RSTC); - val &= ~PM_RSTC_WRCFG_MASK; - val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; - writel_relaxed(val, wdt_regs + PM_RSTC); - - /* No sleeping, possibly atomic. */ - mdelay(1); -} - -/* - * We can't really power off, but if we do the normal reset scheme, and - * indicate to bootcode.bin not to reboot, then most of the chip will be - * powered off. - */ -static void bcm2835_power_off(void) -{ - u32 val; - - /* - * We set the watchdog hard reset bit here to distinguish this reset - * from the normal (full) reset. bootcode.bin will not reboot after a - * hard reset. - */ - val = readl_relaxed(wdt_regs + PM_RSTS); - val &= ~PM_RSTC_WRCFG_MASK; - val |= PM_PASSWORD | PM_RSTS_HADWRH_SET; - writel_relaxed(val, wdt_regs + PM_RSTS); - - /* Continue with normal reset mechanism */ - bcm2835_restart(REBOOT_HARD, ""); -} - static void __init bcm2835_init(void) { int ret; - bcm2835_setup_restart(); - if (wdt_regs) - pm_power_off = bcm2835_power_off; - bcm2835_init_clocks(); ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, @@ -114,6 +42,5 @@ static const char * const bcm2835_compat[] = { DT_MACHINE_START(BCM2835, "BCM2835") .init_machine = bcm2835_init, - .restart = bcm2835_restart, .dt_compat = bcm2835_compat MACHINE_END diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 2b5a9bbf80b7..7116968dee12 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -13,20 +13,25 @@ * option) any later version. */ +#include +#include #include #include #include #include #include #include +#include #define PM_RSTC 0x1c +#define PM_RSTS 0x20 #define PM_WDOG 0x24 #define PM_PASSWORD 0x5a000000 #define PM_WDOG_TIME_SET 0x000fffff #define PM_RSTC_WRCFG_CLR 0xffffffcf +#define PM_RSTS_HADWRH_SET 0x00000040 #define PM_RSTC_WRCFG_SET 0x00000030 #define PM_RSTC_WRCFG_FULL_RESET 0x00000020 #define PM_RSTC_RESET 0x00000102 @@ -37,6 +42,7 @@ struct bcm2835_wdt { void __iomem *base; spinlock_t lock; + struct notifier_block restart_handler; }; static unsigned int heartbeat; @@ -106,6 +112,53 @@ static struct watchdog_device bcm2835_wdt_wdd = { .timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET), }; +static int +bcm2835_restart(struct notifier_block *this, unsigned long mode, void *cmd) +{ + struct bcm2835_wdt *wdt = container_of(this, struct bcm2835_wdt, + restart_handler); + u32 val; + + /* use a timeout of 10 ticks (~150us) */ + writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG); + val = readl_relaxed(wdt->base + PM_RSTC); + val &= PM_RSTC_WRCFG_CLR; + val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; + writel_relaxed(val, wdt->base + PM_RSTC); + + /* No sleeping, possibly atomic. */ + mdelay(1); + + return 0; +} + +/* + * We can't really power off, but if we do the normal reset scheme, and + * indicate to bootcode.bin not to reboot, then most of the chip will be + * powered off. + */ +static void bcm2835_power_off(void) +{ + struct device_node *np = + of_find_compatible_node(NULL, NULL, "brcm,bcm2835-pm-wdt"); + struct platform_device *pdev = of_find_device_by_node(np); + struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); + u32 val; + + /* + * We set the watchdog hard reset bit here to distinguish this reset + * from the normal (full) reset. bootcode.bin will not reboot after a + * hard reset. + */ + val = readl_relaxed(wdt->base + PM_RSTS); + val &= PM_RSTC_WRCFG_CLR; + val |= PM_PASSWORD | PM_RSTS_HADWRH_SET; + writel_relaxed(val, wdt->base + PM_RSTS); + + /* Continue with normal reset mechanism */ + bcm2835_restart(&wdt->restart_handler, REBOOT_HARD, NULL); +} + static int bcm2835_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -136,6 +189,12 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) return err; } + wdt->restart_handler.notifier_call = bcm2835_restart; + wdt->restart_handler.priority = 128; + register_restart_handler(&wdt->restart_handler); + if (pm_power_off == NULL) + pm_power_off = bcm2835_power_off; + dev_info(dev, "Broadcom BCM2835 watchdog timer"); return 0; } @@ -144,6 +203,9 @@ static int bcm2835_wdt_remove(struct platform_device *pdev) { struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); + unregister_restart_handler(&wdt->restart_handler); + if (pm_power_off == bcm2835_power_off) + pm_power_off = NULL; watchdog_unregister_device(&bcm2835_wdt_wdd); iounmap(wdt->base); -- cgit v1.2.1 From 59dd3f02615e52c42207a8e873f7f86bf7d447c4 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Mon, 4 May 2015 20:59:46 +0200 Subject: mailbox: altera: Add dependency on HAS_IOMEM Not all architectures have io memory. Fixes: drivers/built-in.o: In function `altera_mbox_probe': mailbox-altera.c:(.text+0x409fd2): undefined reference to `devm_ioremap_resource' Signed-off-by: Richard Weinberger Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 84b0a2d74d60..4a418d2f6564 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -56,6 +56,7 @@ config PCC config ALTERA_MBOX tristate "Altera Mailbox" + depends on HAS_IOMEM help An implementation of the Altera Mailbox soft core. It is used to send message between processors. Say Y here if you want to use the -- cgit v1.2.1 From 05ae797566a66d159cf1e2ee11bf3f6fae40c8eb Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Mon, 4 May 2015 10:36:35 -0700 Subject: mailbox: Make mbox_chan_ops const The mailbox controller's channel ops ought to be read-only. Update all the mailbox drivers to make their mbox_chan_ops const as well. Signed-off-by: Andrew Bresticker Cc: Ashwin Chaugule Cc: Ley Foon Tan Acked-by: Suman Anna Signed-off-by: Jassi Brar --- drivers/mailbox/arm_mhu.c | 2 +- drivers/mailbox/mailbox-altera.c | 2 +- drivers/mailbox/omap-mailbox.c | 2 +- drivers/mailbox/pcc.c | 2 +- include/linux/mailbox_controller.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c index ac693c635357..d9e99f981aa9 100644 --- a/drivers/mailbox/arm_mhu.c +++ b/drivers/mailbox/arm_mhu.c @@ -110,7 +110,7 @@ static void mhu_shutdown(struct mbox_chan *chan) free_irq(mlink->irq, chan); } -static struct mbox_chan_ops mhu_ops = { +static const struct mbox_chan_ops mhu_ops = { .send_data = mhu_send_data, .startup = mhu_startup, .shutdown = mhu_shutdown, diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c index a266265677d3..bb682c926b0a 100644 --- a/drivers/mailbox/mailbox-altera.c +++ b/drivers/mailbox/mailbox-altera.c @@ -285,7 +285,7 @@ static void altera_mbox_shutdown(struct mbox_chan *chan) } } -static struct mbox_chan_ops altera_mbox_ops = { +static const struct mbox_chan_ops altera_mbox_ops = { .send_data = altera_mbox_send_data, .startup = altera_mbox_startup, .shutdown = altera_mbox_shutdown, diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index 0f332c178b07..03f8545ba037 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -604,7 +604,7 @@ static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data) return ret; } -static struct mbox_chan_ops omap_mbox_chan_ops = { +static const struct mbox_chan_ops omap_mbox_chan_ops = { .startup = omap_mbox_chan_startup, .send_data = omap_mbox_chan_send_data, .shutdown = omap_mbox_chan_shutdown, diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 7e91d68a3ac3..26d121d1d501 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -198,7 +198,7 @@ static int pcc_send_data(struct mbox_chan *chan, void *data) return 0; } -static struct mbox_chan_ops pcc_chan_ops = { +static const struct mbox_chan_ops pcc_chan_ops = { .send_data = pcc_send_data, }; diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h index d4cf96f07cfc..68c42454439b 100644 --- a/include/linux/mailbox_controller.h +++ b/include/linux/mailbox_controller.h @@ -72,7 +72,7 @@ struct mbox_chan_ops { */ struct mbox_controller { struct device *dev; - struct mbox_chan_ops *ops; + const struct mbox_chan_ops *ops; struct mbox_chan *chans; int num_chans; bool txdone_irq; -- cgit v1.2.1 From 2d805fc1c6b2ec09f27bcd951c2aee3da919f81a Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Mon, 4 May 2015 10:36:36 -0700 Subject: mailbox: Fix up error handling in mbox_request_channel() mbox_request_channel() currently returns EBUSY in the event the controller is not present or if of_xlate() fails, but in neither case is EBUSY really appropriate. Return EPROBE_DEFER if the controller is not yet present and change of_xlate() to return an ERR_PTR instead of NULL so that the error can be propagated back to the caller of mbox_request_channel(). Signed-off-by: Benson Leung Signed-off-by: Andrew Bresticker Acked-by: Suman Anna Reviewed-by: Jon Hunter Tested-by: Jon Hunter Signed-off-by: Jassi Brar --- drivers/mailbox/mailbox.c | 11 ++++++++--- drivers/mailbox/omap-mailbox.c | 6 +++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c index 19b491d2964f..c3c42d42d017 100644 --- a/drivers/mailbox/mailbox.c +++ b/drivers/mailbox/mailbox.c @@ -318,7 +318,7 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index) return ERR_PTR(-ENODEV); } - chan = NULL; + chan = ERR_PTR(-EPROBE_DEFER); list_for_each_entry(mbox, &mbox_cons, node) if (mbox->dev->of_node == spec.np) { chan = mbox->of_xlate(mbox, &spec); @@ -327,7 +327,12 @@ struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index) of_node_put(spec.np); - if (!chan || chan->cl || !try_module_get(mbox->dev->driver->owner)) { + if (IS_ERR(chan)) { + mutex_unlock(&con_mutex); + return chan; + } + + if (chan->cl || !try_module_get(mbox->dev->driver->owner)) { dev_dbg(dev, "%s: mailbox not free\n", __func__); mutex_unlock(&con_mutex); return ERR_PTR(-EBUSY); @@ -390,7 +395,7 @@ of_mbox_index_xlate(struct mbox_controller *mbox, int ind = sp->args[0]; if (ind >= mbox->num_chans) - return NULL; + return ERR_PTR(-EINVAL); return &mbox->chans[ind]; } diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index 03f8545ba037..a3dbfd9c6479 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -639,18 +639,18 @@ static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller, mdev = container_of(controller, struct omap_mbox_device, controller); if (WARN_ON(!mdev)) - return NULL; + return ERR_PTR(-EINVAL); node = of_find_node_by_phandle(phandle); if (!node) { pr_err("%s: could not find node phandle 0x%x\n", __func__, phandle); - return NULL; + return ERR_PTR(-ENODEV); } mbox = omap_mbox_device_find(mdev, node->name); of_node_put(node); - return mbox ? mbox->chan : NULL; + return mbox ? mbox->chan : ERR_PTR(-ENOENT); } static int omap_mbox_probe(struct platform_device *pdev) -- cgit v1.2.1 From 95a808c4811f40c9450a49152fd8e4f30542a57c Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Mon, 27 Apr 2015 15:37:19 -0700 Subject: mailbox: Add NVIDIA Tegra XUSB mailbox binding Add device-tree bindings for the Tegra XUSB mailbox which will be used for communication between the Tegra xHCI controller's firmware and the host processor. Signed-off-by: Andrew Bresticker Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Jassi Brar --- .../bindings/mailbox/nvidia,tegra124-xusb-mbox.txt | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt b/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt new file mode 100644 index 000000000000..9d89afadc070 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra124-xusb-mbox.txt @@ -0,0 +1,30 @@ +NVIDIA Tegra XUSB mailbox +========================= + +The Tegra XUSB mailbox is used by the Tegra xHCI controller's firmware to +communicate requests to the host and PHY drivers. + +Refer to ./mailbox.txt for generic information about mailbox device-tree +bindings. + +Required properties: +-------------------- + - compatible: For Tegra124, must contain "nvidia,tegra124-xusb-mbox". + Otherwise, must contain '"nvidia,-xusb-mbox", + "nvidia,tegra124-xusb-mbox"' where is tegra132. + - #mbox-cells: Should be 0. There is only one physical channel. + +Example: +-------- + mailbox { + compatible = "nvidia,tegra124-xusb-mbox"; + + #mbox-cells = <0>; + }; + + usb-host { + ... + mboxes = <&xusb_mbox>; + mbox-names = "xusb"; + ... + }; -- cgit v1.2.1 From b1f10002b00a047b48d08c351f9ff79b6deed9d2 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Mon, 4 May 2015 10:36:40 -0700 Subject: mailbox: Add NVIDIA Tegra XUSB mailbox driver The Tegra xHCI controller's firmware communicates requests to the host processor through a mailbox interface. While there is only a single physical channel, messages sent by the controller can be divided into two groups: those intended for the PHY driver and those intended for the host-controller driver. The requesting driver is assigned one of two virtual channels when the single physical channel is requested. All incoming messages are sent to both virtual channels. Signed-off-by: Andrew Bresticker Reviewed-by: Jon Hunter Tested-by: Jon Hunter Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 8 + drivers/mailbox/Makefile | 2 + drivers/mailbox/tegra-xusb-mailbox.c | 290 +++++++++++++++++++++++++++++++++++ include/soc/tegra/xusb.h | 43 ++++++ 4 files changed, 343 insertions(+) create mode 100644 drivers/mailbox/tegra-xusb-mailbox.c create mode 100644 include/soc/tegra/xusb.h diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 4a418d2f6564..37a071887d5a 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -61,4 +61,12 @@ config ALTERA_MBOX An implementation of the Altera Mailbox soft core. It is used to send message between processors. Say Y here if you want to use the Altera mailbox support. + +config TEGRA_XUSB_MBOX + tristate "NVIDIA Tegra XUSB Mailbox" + depends on MFD_TEGRA_XUSB + help + Mailbox driver for the XUSB complex found on NVIDIA Tegra124 and + later SoCs. The XUSB mailbox is used to communicate between the + XUSB microcontroller and the host processor. endif diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index b18201e97e29..d77012a92312 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o obj-$(CONFIG_PCC) += pcc.o obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o + +obj-$(CONFIG_TEGRA_XUSB_MBOX) += tegra-xusb-mailbox.o diff --git a/drivers/mailbox/tegra-xusb-mailbox.c b/drivers/mailbox/tegra-xusb-mailbox.c new file mode 100644 index 000000000000..4e2477dd7881 --- /dev/null +++ b/drivers/mailbox/tegra-xusb-mailbox.c @@ -0,0 +1,290 @@ +/* + * NVIDIA Tegra XUSB mailbox driver + * + * Copyright (C) 2014 NVIDIA Corporation + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define XUSB_MBOX_NUM_CHANS 2 /* Host + PHY */ + +#define XUSB_CFG_ARU_MBOX_CMD 0xe4 +#define MBOX_DEST_FALC BIT(27) +#define MBOX_DEST_PME BIT(28) +#define MBOX_DEST_SMI BIT(29) +#define MBOX_DEST_XHCI BIT(30) +#define MBOX_INT_EN BIT(31) +#define XUSB_CFG_ARU_MBOX_DATA_IN 0xe8 +#define CMD_DATA_SHIFT 0 +#define CMD_DATA_MASK 0xffffff +#define CMD_TYPE_SHIFT 24 +#define CMD_TYPE_MASK 0xff +#define XUSB_CFG_ARU_MBOX_DATA_OUT 0xec +#define XUSB_CFG_ARU_MBOX_OWNER 0xf0 +#define MBOX_OWNER_NONE 0 +#define MBOX_OWNER_FW 1 +#define MBOX_OWNER_SW 2 +#define XUSB_CFG_ARU_SMI_INTR 0x428 +#define MBOX_SMI_INTR_FW_HANG BIT(1) +#define MBOX_SMI_INTR_EN BIT(3) + +struct tegra_xusb_mbox { + struct mbox_controller mbox; + struct regmap *fpci_regs; + spinlock_t lock; + int irq; +}; + +static inline u32 mbox_readl(struct tegra_xusb_mbox *mbox, unsigned long offset) +{ + u32 val; + + regmap_read(mbox->fpci_regs, offset, &val); + + return val; +} + +static inline void mbox_writel(struct tegra_xusb_mbox *mbox, u32 val, + unsigned long offset) +{ + regmap_write(mbox->fpci_regs, offset, val); +} + +static inline struct tegra_xusb_mbox *to_tegra_mbox(struct mbox_controller *c) +{ + return container_of(c, struct tegra_xusb_mbox, mbox); +} + +static inline u32 mbox_pack_msg(struct tegra_xusb_mbox_msg *msg) +{ + u32 val; + + val = (msg->cmd & CMD_TYPE_MASK) << CMD_TYPE_SHIFT; + val |= (msg->data & CMD_DATA_MASK) << CMD_DATA_SHIFT; + + return val; +} + +static inline void mbox_unpack_msg(u32 val, struct tegra_xusb_mbox_msg *msg) +{ + msg->cmd = (val >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK; + msg->data = (val >> CMD_DATA_SHIFT) & CMD_DATA_MASK; +} + +static bool mbox_cmd_requires_ack(enum tegra_xusb_mbox_cmd cmd) +{ + switch (cmd) { + case MBOX_CMD_SET_BW: + case MBOX_CMD_ACK: + case MBOX_CMD_NAK: + return false; + default: + return true; + } +} + +static int tegra_xusb_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct tegra_xusb_mbox *mbox = to_tegra_mbox(chan->mbox); + struct tegra_xusb_mbox_msg *msg = data; + unsigned long flags; + u32 reg; + + dev_dbg(mbox->mbox.dev, "TX message %#x:%#x\n", msg->cmd, msg->data); + + spin_lock_irqsave(&mbox->lock, flags); + /* + * Acquire the mailbox. The firmware still owns the mailbox for + * ACK/NAK messages. + */ + if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) { + if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) != + MBOX_OWNER_NONE) { + dev_err(mbox->mbox.dev, "Mailbox not idle\n"); + goto busy; + } + + mbox_writel(mbox, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER); + if (mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) != + MBOX_OWNER_SW) { + dev_err(mbox->mbox.dev, "Failed to acquire mailbox"); + goto busy; + } + } + + mbox_writel(mbox, mbox_pack_msg(msg), XUSB_CFG_ARU_MBOX_DATA_IN); + reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD); + reg |= MBOX_INT_EN | MBOX_DEST_FALC; + mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD); + + spin_unlock_irqrestore(&mbox->lock, flags); + + return 0; +busy: + spin_unlock_irqrestore(&mbox->lock, flags); + return -EBUSY; +} + +static int tegra_xusb_mbox_startup(struct mbox_chan *chan) +{ + return 0; +} + +static void tegra_xusb_mbox_shutdown(struct mbox_chan *chan) +{ +} + +static bool tegra_xusb_mbox_last_tx_done(struct mbox_chan *chan) +{ + struct tegra_xusb_mbox *mbox = to_tegra_mbox(chan->mbox); + + return mbox_readl(mbox, XUSB_CFG_ARU_MBOX_OWNER) == MBOX_OWNER_NONE; +} + +static const struct mbox_chan_ops tegra_xusb_mbox_chan_ops = { + .send_data = tegra_xusb_mbox_send_data, + .startup = tegra_xusb_mbox_startup, + .shutdown = tegra_xusb_mbox_shutdown, + .last_tx_done = tegra_xusb_mbox_last_tx_done, +}; + +static irqreturn_t tegra_xusb_mbox_irq(int irq, void *p) +{ + struct tegra_xusb_mbox *mbox = p; + struct tegra_xusb_mbox_msg msg; + unsigned int i; + u32 reg; + + spin_lock(&mbox->lock); + + /* Clear mbox interrupts */ + reg = mbox_readl(mbox, XUSB_CFG_ARU_SMI_INTR); + if (reg & MBOX_SMI_INTR_FW_HANG) + dev_err(mbox->mbox.dev, "Controller firmware hang\n"); + mbox_writel(mbox, reg, XUSB_CFG_ARU_SMI_INTR); + + reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_DATA_OUT); + mbox_unpack_msg(reg, &msg); + + reg = mbox_readl(mbox, XUSB_CFG_ARU_MBOX_CMD); + reg &= ~MBOX_DEST_SMI; + mbox_writel(mbox, reg, XUSB_CFG_ARU_MBOX_CMD); + + /* Clear mailbox owner if no ACK/NAK is required. */ + if (!mbox_cmd_requires_ack(msg.cmd)) + mbox_writel(mbox, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER); + + dev_dbg(mbox->mbox.dev, "RX message %#x:%#x\n", msg.cmd, msg.data); + for (i = 0; i < XUSB_MBOX_NUM_CHANS; i++) { + if (mbox->mbox.chans[i].cl) + mbox_chan_received_data(&mbox->mbox.chans[i], &msg); + } + + spin_unlock(&mbox->lock); + + return IRQ_HANDLED; +} + +static struct mbox_chan *tegra_xusb_mbox_of_xlate(struct mbox_controller *ctlr, + const struct of_phandle_args *sp) +{ + struct tegra_xusb_mbox *mbox = to_tegra_mbox(ctlr); + struct mbox_chan *chan = ERR_PTR(-EINVAL); + unsigned long flags; + unsigned int i; + + /* Pick the first available (virtual) channel. */ + spin_lock_irqsave(&mbox->lock, flags); + for (i = 0; XUSB_MBOX_NUM_CHANS; i++) { + if (!ctlr->chans[i].cl) { + chan = &ctlr->chans[i]; + break; + } + } + spin_unlock_irqrestore(&mbox->lock, flags); + + return chan; +} + +static const struct of_device_id tegra_xusb_mbox_of_match[] = { + { .compatible = "nvidia,tegra124-xusb-mbox" }, + { }, +}; +MODULE_DEVICE_TABLE(of, tegra_xusb_mbox_of_match); + +static int tegra_xusb_mbox_probe(struct platform_device *pdev) +{ + struct tegra_xusb_mbox *mbox; + int ret; + + mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + platform_set_drvdata(pdev, mbox); + spin_lock_init(&mbox->lock); + mbox->fpci_regs = dev_get_drvdata(pdev->dev.parent); + + mbox->mbox.dev = &pdev->dev; + mbox->mbox.chans = devm_kcalloc(&pdev->dev, XUSB_MBOX_NUM_CHANS, + sizeof(*mbox->mbox.chans), GFP_KERNEL); + if (!mbox->mbox.chans) + return -ENOMEM; + mbox->mbox.num_chans = XUSB_MBOX_NUM_CHANS; + mbox->mbox.ops = &tegra_xusb_mbox_chan_ops; + mbox->mbox.txdone_poll = true; + mbox->mbox.txpoll_period = 1; + mbox->mbox.of_xlate = tegra_xusb_mbox_of_xlate; + + mbox->irq = platform_get_irq(pdev, 0); + if (mbox->irq < 0) + return mbox->irq; + ret = devm_request_irq(&pdev->dev, mbox->irq, tegra_xusb_mbox_irq, 0, + dev_name(&pdev->dev), mbox); + if (ret < 0) + return ret; + + ret = mbox_controller_register(&mbox->mbox); + if (ret < 0) + dev_err(&pdev->dev, "failed to register mailbox: %d\n", ret); + + return ret; +} + +static int tegra_xusb_mbox_remove(struct platform_device *pdev) +{ + struct tegra_xusb_mbox *mbox = platform_get_drvdata(pdev); + + synchronize_irq(mbox->irq); + devm_free_irq(&pdev->dev, mbox->irq, mbox); + mbox_controller_unregister(&mbox->mbox); + + return 0; +} + +static struct platform_driver tegra_xusb_mbox_driver = { + .probe = tegra_xusb_mbox_probe, + .remove = tegra_xusb_mbox_remove, + .driver = { + .name = "tegra-xusb-mbox", + .of_match_table = tegra_xusb_mbox_of_match, + }, +}; +module_platform_driver(tegra_xusb_mbox_driver); + +MODULE_AUTHOR("Andrew Bresticker "); +MODULE_DESCRIPTION("NVIDIA Tegra XUSB mailbox driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/tegra/xusb.h b/include/soc/tegra/xusb.h new file mode 100644 index 000000000000..5ce5e12e99fc --- /dev/null +++ b/include/soc/tegra/xusb.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 NVIDIA Corporation + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#ifndef __SOC_TEGRA_XUSB_H__ +#define __SOC_TEGRA_XUSB_H__ + +/* Command requests from the firmware */ +enum tegra_xusb_mbox_cmd { + MBOX_CMD_MSG_ENABLED = 1, + MBOX_CMD_INC_FALC_CLOCK, + MBOX_CMD_DEC_FALC_CLOCK, + MBOX_CMD_INC_SSPI_CLOCK, + MBOX_CMD_DEC_SSPI_CLOCK, + MBOX_CMD_SET_BW, /* no ACK/NAK required */ + MBOX_CMD_SET_SS_PWR_GATING, + MBOX_CMD_SET_SS_PWR_UNGATING, + MBOX_CMD_SAVE_DFE_CTLE_CTX, + MBOX_CMD_AIRPLANE_MODE_ENABLED, /* unused */ + MBOX_CMD_AIRPLANE_MODE_DISABLED, /* unused */ + MBOX_CMD_START_HSIC_IDLE, + MBOX_CMD_STOP_HSIC_IDLE, + MBOX_CMD_DBC_WAKE_STACK, /* unused */ + MBOX_CMD_HSIC_PRETEND_CONNECT, + + MBOX_CMD_MAX, + + /* Response message to above commands */ + MBOX_CMD_ACK = 128, + MBOX_CMD_NAK +}; + +struct tegra_xusb_mbox_msg { + u32 cmd; + u32 data; +}; + +#endif /* __SOC_TEGRA_XUSB_H__ */ -- cgit v1.2.1 From 8c2e17a7d945cb5e89ad0d4329bc4b919064e0b1 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 5 May 2015 13:27:44 -0700 Subject: dt/bindings: Add binding for the BCM2835 mailbox driver This patch was split out of Lubomir's original mailbox patch by Eric Anholt, and the required properties documentation and examples have been filled out more completely and updated for the driver being changed to expose a single channel. Signed-off-by: Lubomir Rintel Signed-off-by: Craig McGeachie Signed-off-by: Eric Anholt Acked-by: Lee Jones Acked-by: Stephen Warren Signed-off-by: Jassi Brar --- .../bindings/mailbox/brcm,bcm2835-mbox.txt | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt diff --git a/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt b/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt new file mode 100644 index 000000000000..e893615ef635 --- /dev/null +++ b/Documentation/devicetree/bindings/mailbox/brcm,bcm2835-mbox.txt @@ -0,0 +1,26 @@ +Broadcom BCM2835 VideoCore mailbox IPC + +Required properties: + +- compatible: Should be "brcm,bcm2835-mbox" +- reg: Specifies base physical address and size of the registers +- interrupts: The interrupt number + See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt +- #mbox-cells: Specifies the number of cells needed to encode a mailbox + channel. The value shall be 0, since there is only one + mailbox channel implemented by the device. + +Example: + +mailbox: mailbox@7e00b800 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7e00b880 0x40>; + interrupts = <0 1>; + #mbox-cells = <0>; +}; + +firmware: firmware { + compatible = "raspberrypi,firmware"; + mboxes = <&mailbox>; + #power-domain-cells = <1>; +}; -- cgit v1.2.1 From 4c0f74bf82e8295ce711da81774178769f6f8215 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 5 May 2015 13:27:45 -0700 Subject: mailbox: Enable BCM2835 mailbox support This mailbox driver provides a single mailbox channel to write 32-bit values to the VPU and get a 32-bit response. The Raspberry Pi firmware uses this mailbox channel to implement firmware calls, while Roku 2 (despite being derived from the same firmware tree) doesn't. The driver was originally submitted by Lubomir, based on the out-of-tree 2708 mailbox driver. Eric Anholt fixed it up for upstreaming, with the major functional change being that it now has no notion of multiple channels (since that is a firmware-dependent concept) and instead the raspberrypi-firmware driver will do that bit-twiddling in its own messages. [Jassi: made the 'mbox_chan_ops' struct as const and removed a redundant variable] Signed-off-by: Lubomir Rintel Signed-off-by: Craig McGeachie Signed-off-by: Eric Anholt Acked-by: Stephen Warren Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 8 ++ drivers/mailbox/Makefile | 2 + drivers/mailbox/bcm2835-mailbox.c | 216 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 226 insertions(+) create mode 100644 drivers/mailbox/bcm2835-mailbox.c diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 37a071887d5a..76c11942ea23 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -62,6 +62,14 @@ config ALTERA_MBOX to send message between processors. Say Y here if you want to use the Altera mailbox support. +config BCM2835_MBOX + tristate "BCM2835 Mailbox" + depends on ARCH_BCM2835 + help + An implementation of the BCM2385 Mailbox. It is used to invoke + the services of the Videocore. Say Y here if you want to use the + BCM2835 Mailbox. + config TEGRA_XUSB_MBOX tristate "NVIDIA Tegra XUSB Mailbox" depends on MFD_TEGRA_XUSB diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index d77012a92312..1dc9f9acafdb 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -12,4 +12,6 @@ obj-$(CONFIG_PCC) += pcc.o obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o +obj-$(CONFIG_BCM2835_MBOX) += bcm2835-mailbox.o + obj-$(CONFIG_TEGRA_XUSB_MBOX) += tegra-xusb-mailbox.o diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c new file mode 100644 index 000000000000..4b13268529f9 --- /dev/null +++ b/drivers/mailbox/bcm2835-mailbox.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2010,2015 Broadcom + * Copyright (C) 2013-2014 Lubomir Rintel + * Copyright (C) 2013 Craig McGeachie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This device provides a mechanism for writing to the mailboxes, + * that are shared between the ARM and the VideoCore processor + * + * Parts of the driver are based on: + * - arch/arm/mach-bcm2708/vcio.c file written by Gray Girling that was + * obtained from branch "rpi-3.6.y" of git://github.com/raspberrypi/ + * linux.git + * - drivers/mailbox/bcm2835-ipc.c by Lubomir Rintel at + * https://github.com/hackerspace/rpi-linux/blob/lr-raspberry-pi/drivers/ + * mailbox/bcm2835-ipc.c + * - documentation available on the following web site: + * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Mailboxes */ +#define ARM_0_MAIL0 0x00 +#define ARM_0_MAIL1 0x20 + +/* + * Mailbox registers. We basically only support mailbox 0 & 1. We + * deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See + * BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about + * the placement of memory barriers. + */ +#define MAIL0_RD (ARM_0_MAIL0 + 0x00) +#define MAIL0_POL (ARM_0_MAIL0 + 0x10) +#define MAIL0_STA (ARM_0_MAIL0 + 0x18) +#define MAIL0_CNF (ARM_0_MAIL0 + 0x1C) +#define MAIL1_WRT (ARM_0_MAIL1 + 0x00) + +/* Status register: FIFO state. */ +#define ARM_MS_FULL BIT(31) +#define ARM_MS_EMPTY BIT(30) + +/* Configuration register: Enable interrupts. */ +#define ARM_MC_IHAVEDATAIRQEN BIT(0) + +struct bcm2835_mbox { + void __iomem *regs; + spinlock_t lock; + struct mbox_controller controller; +}; + +static struct bcm2835_mbox *bcm2835_link_mbox(struct mbox_chan *link) +{ + return container_of(link->mbox, struct bcm2835_mbox, controller); +} + +static irqreturn_t bcm2835_mbox_irq(int irq, void *dev_id) +{ + struct bcm2835_mbox *mbox = dev_id; + struct device *dev = mbox->controller.dev; + struct mbox_chan *link = &mbox->controller.chans[0]; + + while (!(readl(mbox->regs + MAIL0_STA) & ARM_MS_EMPTY)) { + u32 msg = readl(mbox->regs + MAIL0_RD); + dev_dbg(dev, "Reply 0x%08X\n", msg); + mbox_chan_received_data(link, &msg); + } + return IRQ_HANDLED; +} + +static int bcm2835_send_data(struct mbox_chan *link, void *data) +{ + struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); + u32 msg = *(u32 *)data; + + spin_lock(&mbox->lock); + writel(msg, mbox->regs + MAIL1_WRT); + dev_dbg(mbox->controller.dev, "Request 0x%08X\n", msg); + spin_unlock(&mbox->lock); + return 0; +} + +static int bcm2835_startup(struct mbox_chan *link) +{ + struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); + + /* Enable the interrupt on data reception */ + writel(ARM_MC_IHAVEDATAIRQEN, mbox->regs + MAIL0_CNF); + + return 0; +} + +static void bcm2835_shutdown(struct mbox_chan *link) +{ + struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); + + writel(0, mbox->regs + MAIL0_CNF); +} + +static bool bcm2835_last_tx_done(struct mbox_chan *link) +{ + struct bcm2835_mbox *mbox = bcm2835_link_mbox(link); + bool ret; + + spin_lock(&mbox->lock); + ret = !(readl(mbox->regs + MAIL0_STA) & ARM_MS_FULL); + spin_unlock(&mbox->lock); + return ret; +} + +static const struct mbox_chan_ops bcm2835_mbox_chan_ops = { + .send_data = bcm2835_send_data, + .startup = bcm2835_startup, + .shutdown = bcm2835_shutdown, + .last_tx_done = bcm2835_last_tx_done +}; + +static struct mbox_chan *bcm2835_mbox_index_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + if (sp->args_count != 0) + return NULL; + + return &mbox->chans[0]; +} + +static int bcm2835_mbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret = 0; + struct resource *iomem; + struct bcm2835_mbox *mbox; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (mbox == NULL) + return -ENOMEM; + spin_lock_init(&mbox->lock); + + ret = devm_request_irq(dev, irq_of_parse_and_map(dev->of_node, 0), + bcm2835_mbox_irq, 0, dev_name(dev), mbox); + if (ret) { + dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n", + ret); + return -ENODEV; + } + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mbox->regs = devm_ioremap_resource(&pdev->dev, iomem); + if (IS_ERR(mbox->regs)) { + ret = PTR_ERR(mbox->regs); + dev_err(&pdev->dev, "Failed to remap mailbox regs: %d\n", ret); + return ret; + } + + mbox->controller.txdone_poll = true; + mbox->controller.txpoll_period = 5; + mbox->controller.ops = &bcm2835_mbox_chan_ops; + mbox->controller.of_xlate = &bcm2835_mbox_index_xlate; + mbox->controller.dev = dev; + mbox->controller.num_chans = 1; + mbox->controller.chans = devm_kzalloc(dev, + sizeof(*mbox->controller.chans), GFP_KERNEL); + if (!mbox->controller.chans) + return -ENOMEM; + + ret = mbox_controller_register(&mbox->controller); + if (ret) + return ret; + + platform_set_drvdata(pdev, mbox); + dev_info(dev, "mailbox enabled\n"); + + return ret; +} + +static int bcm2835_mbox_remove(struct platform_device *pdev) +{ + struct bcm2835_mbox *mbox = platform_get_drvdata(pdev); + mbox_controller_unregister(&mbox->controller); + return 0; +} + +static const struct of_device_id bcm2835_mbox_of_match[] = { + { .compatible = "brcm,bcm2835-mbox", }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_mbox_of_match); + +static struct platform_driver bcm2835_mbox_driver = { + .driver = { + .name = "bcm2835-mbox", + .owner = THIS_MODULE, + .of_match_table = bcm2835_mbox_of_match, + }, + .probe = bcm2835_mbox_probe, + .remove = bcm2835_mbox_remove, +}; +module_platform_driver(bcm2835_mbox_driver); + +MODULE_AUTHOR("Lubomir Rintel "); +MODULE_DESCRIPTION("BCM2835 mailbox IPC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.1 From 9bf5b478da83465be49f9f54ff481783f20df6fb Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 5 May 2015 13:27:46 -0700 Subject: ARM: bcm2835: Add the mailbox to the device tree Signed-off-by: Eric Anholt Acked-by: Lee Jones Acked-by: Stephen Warren Signed-off-by: Jassi Brar --- arch/arm/boot/dts/bcm2835.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 3342cb1407bc..39bf201874cd 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -60,6 +60,13 @@ reg = <0x7e104000 0x10>; }; + mailbox: mailbox@7e00b800 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7e00b880 0x40>; + interrupts = <0 1>; + #mbox-cells = <0>; + }; + gpio: gpio@7e200000 { compatible = "brcm,bcm2835-gpio"; reg = <0x7e200000 0xb4>; -- cgit v1.2.1 From 3bf2017780a549c5c74c5e8c185b20a395d3556a Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 5 May 2015 13:27:46 -0700 Subject: ARM: bcm2835: Add the mailbox to the device tree Signed-off-by: Eric Anholt Acked-by: Lee Jones Acked-by: Stephen Warren Signed-off-by: Lee Jones --- arch/arm/boot/dts/bcm2835.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 4d4c1294c082..4ff1b83191a6 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -61,6 +61,13 @@ reg = <0x7e104000 0x10>; }; + mailbox: mailbox@7e00b800 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7e00b880 0x40>; + interrupts = <0 1>; + #mbox-cells = <0>; + }; + gpio: gpio@7e200000 { compatible = "brcm,bcm2835-gpio"; reg = <0x7e200000 0xb4>; -- cgit v1.2.1 From a828ccf56ec98c81693e21c917f9744cb4a3fa1e Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 26 Feb 2015 09:51:45 +0000 Subject: dt/bindings: Add binding for the Raspberry Pi firmware driver This driver will provide support for calls into the firmware that will be used by other drivers like cpufreq and vc4. v2: Improve commit message, point to mailbox.txt for how mboxes work. Signed-off-by: Eric Anholt --- .../devicetree/bindings/arm/bcm/raspberrypi,firmware.txt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/bcm/raspberrypi,firmware.txt diff --git a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,firmware.txt b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,firmware.txt new file mode 100644 index 000000000000..33b0043c1ca2 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,firmware.txt @@ -0,0 +1,16 @@ +Raspberry Pi VideoCore firmware driver + +Required properties: + +- compatible: Should be "rasbperrypi,firmware" +- mboxes: Single-entry list which specifies which mailbox + controller and channel is used. See + Documentation/devicetree/bindings/mailbox/mailbox.txt + for the semantics of this property + +Example: + +firmware { + compatible = "rasbperrypi,firmware"; + mboxes = <&mailbox>; +}; -- cgit v1.2.1 From ab7a778e4c4155dad7715c2bdf2363e418cbe8a0 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 26 Feb 2015 10:08:06 +0000 Subject: ARM: bcm2835: Add the Raspberry Pi firmware driver This gives us a function for making mailbox property channel requests of the firmware, which is most notable in that it will let us get and set clock rates. v2: Drop power-domains stuff for now since we don't have the driver core support to make it useful. Move to drivers/firmware/. Capitalize the enums. De-global the firmware variable. Use the firmware device to allocate our DMA buffer, so that the dma-ranges DT property gets respected. Simplify the property tag transaction interface even more, leaving a multi-tag interface still available. For conciseness, rename "raspberrypi" to "rpi" on all functions/enums/structs, and the "firmware" variable to "fw". Print when the driver is probed successfully, since debugging -EPROBE_DEFER handling is such a big part of bcm2835 development. Drop -EBUSY mailbox handling since the mailbox core has been fixed to return -EPROBE_DEFER in -next. Signed-off-by: Eric Anholt --- drivers/firmware/Makefile | 1 + drivers/firmware/raspberrypi.c | 224 +++++++++++++++++++++ .../soc/bcm2835/raspberrypi-firmware-property.h | 114 +++++++++++ 3 files changed, 339 insertions(+) create mode 100644 drivers/firmware/raspberrypi.c create mode 100644 include/soc/bcm2835/raspberrypi-firmware-property.h diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 3fdd3912709a..41ced2885b46 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o obj-$(CONFIG_QCOM_SCM) += qcom_scm.o CFLAGS_qcom_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) +obj-$(CONFIG_BCM2835_MBOX) += raspberrypi.o obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-$(CONFIG_EFI) += efi/ diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c new file mode 100644 index 000000000000..61bde1bcffa0 --- /dev/null +++ b/drivers/firmware/raspberrypi.c @@ -0,0 +1,224 @@ +/* + * Copyright © 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Defines interfaces for interacting wtih the Raspberry Pi firmware's + * property channel. + */ + +#include +#include +#include +#include +#include +#include + +#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) +#define MBOX_CHAN(msg) ((msg) & 0xf) +#define MBOX_DATA28(msg) ((msg) & ~0xf) +#define MBOX_CHAN_PROPERTY 8 + +struct rpi_firmware { + struct mbox_client cl; + struct mbox_chan *chan; /* The property channel. */ + struct completion c; + u32 enabled; +}; + +static DEFINE_MUTEX(transaction_lock); + +static void response_callback(struct mbox_client *cl, void *msg) +{ + struct rpi_firmware *fw = container_of(cl, struct rpi_firmware, cl); + complete(&fw->c); +} + +/* + * Sends a request to the firmware through the BCM2835 mailbox driver, + * and synchronously waits for the reply. + */ +static int +rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data) +{ + u32 message = MBOX_MSG(chan, data); + int ret; + + WARN_ON(data & 0xf); + + mutex_lock(&transaction_lock); + reinit_completion(&fw->c); + ret = mbox_send_message(fw->chan, &message); + if (ret >= 0) { + wait_for_completion(&fw->c); + ret = 0; + } else { + dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); + } + mutex_unlock(&transaction_lock); + + return ret; +} + +/* + * Submits a set of concatenated tags to the VPU firmware through the + * mailbox property interface. + * + * The buffer header and the ending tag are added by this function and + * don't need to be supplied, just the actual tags for your operation. + * See struct rpi_firmware_property_tag_header for the per-tag + * structure. + */ +int rpi_firmware_property_list(struct device_node *of_node, + void *data, size_t tag_size) +{ + struct platform_device *pdev = of_find_device_by_node(of_node); + struct rpi_firmware *fw = platform_get_drvdata(pdev); + size_t size = tag_size + 12; + u32 *buf; + dma_addr_t bus_addr; + int ret = 0; + + if (!fw) + return -EPROBE_DEFER; + + /* Packets are processed a dword at a time. */ + if (size & 3) + return -EINVAL; + + buf = dma_alloc_coherent(fw->cl.dev, PAGE_ALIGN(size), &bus_addr, + GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + /* The firmware will error out without parsing in this case. */ + WARN_ON(size >= 1024 * 1024); + + buf[0] = size; + buf[1] = RPI_FIRMWARE_STATUS_REQUEST; + memcpy(&buf[2], data, tag_size); + buf[size / 4 - 1] = RPI_FIRMWARE_PROPERTY_END; + wmb(); + + ret = rpi_firmware_transaction(fw, MBOX_CHAN_PROPERTY, bus_addr); + + rmb(); + memcpy(data, &buf[2], tag_size); + if (ret == 0 && buf[1] != RPI_FIRMWARE_STATUS_SUCCESS) { + /* + * The tag name here might not be the one causing the + * error, if there were multiple tags in the request. + * But single-tag is the most common, so go with it. + */ + dev_err(fw->cl.dev, "Request 0x%08x returned status 0x%08x\n", + buf[2], buf[1]); + ret = -EINVAL; + } + + dma_free_coherent(NULL, PAGE_ALIGN(size), buf, bus_addr); + + return ret; +} +EXPORT_SYMBOL_GPL(rpi_firmware_property_list); + +/* + * Submits a single tag to the VPU firmware through the mailbox + * property interface. + * + * This is a convenience wrapper around + * rpi_firmware_property_list() to avoid some of the + * boilerplate in property calls. + */ +int rpi_firmware_property(struct device_node *of_node, + u32 tag, void *tag_data, size_t buf_size) +{ + /* Single tags are very small (generally 8 bytes), so the + * stack should be safe. + */ + u8 data[buf_size + sizeof(struct rpi_firmware_property_tag_header)]; + struct rpi_firmware_property_tag_header *header = + (struct rpi_firmware_property_tag_header *)data; + int ret; + + header->tag = tag; + header->buf_size = buf_size; + header->req_resp_size = 0; + memcpy(data + sizeof(struct rpi_firmware_property_tag_header), + tag_data, buf_size); + + ret = rpi_firmware_property_list(of_node, &data, sizeof(data)); + memcpy(tag_data, + data + sizeof(struct rpi_firmware_property_tag_header), + buf_size); + + return ret; +} +EXPORT_SYMBOL_GPL(rpi_firmware_property); + +static int rpi_firmware_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret = 0; + struct rpi_firmware *fw; + + fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return -ENOMEM; + + fw->cl.dev = dev; + fw->cl.rx_callback = response_callback; + fw->cl.tx_block = true; + + fw->chan = mbox_request_channel(&fw->cl, 0); + if (IS_ERR(fw->chan)) { + ret = PTR_ERR(fw->chan); + /* An -EBUSY from the core means it couldn't find our + * channel, because the mailbox driver hadn't + * registered yet. + */ + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get mbox channel: %d\n", ret); + return ret; + } + + init_completion(&fw->c); + + dev_info(dev, "Firmware driver\n"); + platform_set_drvdata(pdev, fw); + + return 0; +} + +static int rpi_firmware_remove(struct platform_device *pdev) +{ + struct rpi_firmware *fw = platform_get_drvdata(pdev); + + mbox_free_channel(fw->chan); + + return 0; +} + +static const struct of_device_id rpi_firmware_of_match[] = { + { .compatible = "raspberrypi,firmware", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rpi_firmware_of_match); + +static struct platform_driver rpi_firmware_driver = { + .driver = { + .name = "raspberrypi-firmware", + .owner = THIS_MODULE, + .of_match_table = rpi_firmware_of_match, + }, + .probe = rpi_firmware_probe, + .remove = rpi_firmware_remove, +}; +module_platform_driver(rpi_firmware_driver); + +MODULE_AUTHOR("Eric Anholt "); +MODULE_DESCRIPTION("Raspberry Pi firmware driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/bcm2835/raspberrypi-firmware-property.h b/include/soc/bcm2835/raspberrypi-firmware-property.h new file mode 100644 index 000000000000..2557cabe6e23 --- /dev/null +++ b/include/soc/bcm2835/raspberrypi-firmware-property.h @@ -0,0 +1,114 @@ +/* + * Copyright © 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +enum rpi_firmware_property_status { + RPI_FIRMWARE_STATUS_REQUEST = 0, + RPI_FIRMWARE_STATUS_SUCCESS = 0x80000000, + RPI_FIRMWARE_STATUS_ERROR = 0x80000001, +}; + +struct rpi_firmware_property_tag_header { + /* One of enum_mbox_property_tag. */ + u32 tag; + + /* The number of bytes in the value buffer following this + * struct. + */ + u32 buf_size; + + /* + * On submit, the length of the request (though it doesn't + * appear to be currently used by the firmware). On return, + * the length of the response (always 4 byte aligned), with + * the low bit set. + */ + u32 req_resp_size; +}; + +enum rpi_firmware_property_tag { + RPI_FIRMWARE_PROPERTY_END = 0, + RPI_FIRMWARE_GET_FIRMWARE_REVISION = 0x00000001, + + RPI_FIRMWARE_SET_CURSOR_INFO = 0x00008010, + RPI_FIRMWARE_SET_CURSOR_STATE = 0x00008011, + + RPI_FIRMWARE_GET_BOARD_MODEL = 0x00010001, + RPI_FIRMWARE_GET_BOARD_REVISION = 0x00010002, + RPI_FIRMWARE_GET_BOARD_MAC_ADDRESS = 0x00010003, + RPI_FIRMWARE_GET_BOARD_SERIAL = 0x00010004, + RPI_FIRMWARE_GET_ARM_MEMORY = 0x00010005, + RPI_FIRMWARE_GET_VC_MEMORY = 0x00010006, + RPI_FIRMWARE_GET_CLOCKS = 0x00010007, + RPI_FIRMWARE_GET_POWER_STATE = 0x00020001, + RPI_FIRMWARE_GET_TIMING = 0x00020002, + RPI_FIRMWARE_SET_POWER_STATE = 0x00028001, + RPI_FIRMWARE_GET_CLOCK_STATE = 0x00030001, + RPI_FIRMWARE_GET_CLOCK_RATE = 0x00030002, + RPI_FIRMWARE_GET_VOLTAGE = 0x00030003, + RPI_FIRMWARE_GET_MAX_CLOCK_RATE = 0x00030004, + RPI_FIRMWARE_GET_MAX_VOLTAGE = 0x00030005, + RPI_FIRMWARE_GET_TEMPERATURE = 0x00030006, + RPI_FIRMWARE_GET_MIN_CLOCK_RATE = 0x00030007, + RPI_FIRMWARE_GET_MIN_VOLTAGE = 0x00030008, + RPI_FIRMWARE_GET_TURBO = 0x00030009, + RPI_FIRMWARE_GET_MAX_TEMPERATURE = 0x0003000a, + RPI_FIRMWARE_ALLOCATE_MEMORY = 0x0003000c, + RPI_FIRMWARE_LOCK_MEMORY = 0x0003000d, + RPI_FIRMWARE_UNLOCK_MEMORY = 0x0003000e, + RPI_FIRMWARE_RELEASE_MEMORY = 0x0003000f, + RPI_FIRMWARE_EXECUTE_CODE = 0x00030010, + RPI_FIRMWARE_EXECUTE_QPU = 0x00030011, + RPI_FIRMWARE_SET_ENABLE_QPU = 0x00030012, + RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014, + RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020, + RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001, + RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002, + RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, + RPI_FIRMWARE_SET_TURBO = 0x00038009, + + /* Dispmanx TAGS */ + RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, + RPI_FIRMWARE_FRAMEBUFFER_BLANK = 0x00040002, + RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, + RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, + RPI_FIRMWARE_FRAMEBUFFER_GET_DEPTH = 0x00040005, + RPI_FIRMWARE_FRAMEBUFFER_GET_PIXEL_ORDER = 0x00040006, + RPI_FIRMWARE_FRAMEBUFFER_GET_ALPHA_MODE = 0x00040007, + RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH = 0x00040008, + RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_OFFSET = 0x00040009, + RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, + RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE = 0x0004000b, + RPI_FIRMWARE_FRAMEBUFFER_RELEASE = 0x00048001, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, + RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, + RPI_FIRMWARE_FRAMEBUFFER_TEST_DEPTH = 0x00044005, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PIXEL_ORDER = 0x00044006, + RPI_FIRMWARE_FRAMEBUFFER_TEST_ALPHA_MODE = 0x00044007, + RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_OFFSET = 0x00044009, + RPI_FIRMWARE_FRAMEBUFFER_TEST_OVERSCAN = 0x0004400a, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PALETTE = 0x0004400b, + RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, + RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, + RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH = 0x00048005, + RPI_FIRMWARE_FRAMEBUFFER_SET_PIXEL_ORDER = 0x00048006, + RPI_FIRMWARE_FRAMEBUFFER_SET_ALPHA_MODE = 0x00048007, + RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET = 0x00048009, + RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, + RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b, + + RPI_FIRMWARE_GET_COMMAND_LINE = 0x00050001, + RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, +}; + +int rpi_firmware_property(struct device_node *of_node, + u32 tag, void *data, size_t len); +int rpi_firmware_property_list(struct device_node *of_node, + void *data, size_t tag_size); -- cgit v1.2.1 From 7f659d71cd62fc202d872c03da0fae9caeb729f6 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 25 Feb 2015 15:18:14 +0000 Subject: ARM: bcm2835: Add the firmware driver information to the RPi DT v2: Drop pm-domains stuff since I've dropped it from the firmware driver for now, until we get drivers/base fixed. Signed-off-by: Eric Anholt Acked-by: Lee Jones (previous version with pm-domains) --- arch/arm/boot/dts/bcm2835-rpi.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi index 46780bb48bbf..ace33f69c5aa 100644 --- a/arch/arm/boot/dts/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi @@ -14,6 +14,13 @@ linux,default-trigger = "heartbeat"; }; }; + + soc { + firmware: firmware { + compatible = "raspberrypi,firmware"; + mboxes = <&mailbox>; + }; + }; }; &gpio { -- cgit v1.2.1 From da5249d179a762929e471f1c16939476cae5bbee Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 4 May 2015 11:08:21 -0700 Subject: ARM: bcm2835: Use 0x4 prefix for DMA bus addresses to SDRAM. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There exists a tiny MMU, configurable only by the VC (running the closed firmware), which maps from the ARM's physical addresses to bus addresses. These bus addresses determine the caching behavior in the VC's L1/L2 (note: separate from the ARM's L1/L2) according to the top 2 bits. The bits in the bus address mean: From the VideoCore processor: 0x0... L1 and L2 cache allocating and coherent 0x4... L1 non-allocating, but coherent. L2 allocating and coherent 0x8... L1 non-allocating, but coherent. L2 non-allocating, but coherent 0xc... SDRAM alias. Cache is bypassed. Not L1 or L2 allocating or coherent From the GPU peripherals (note: all peripherals bypass the L1 cache. The ARM will see this view once through the VC MMU): 0x0... Do not use 0x4... L1 non-allocating, and incoherent. L2 allocating and coherent. 0x8... L1 non-allocating, and incoherent. L2 non-allocating, but coherent 0xc... SDRAM alias. Cache is bypassed. Not L1 or L2 allocating or coherent The 2835 firmware always configures the MMU to turn ARM physical addresses with 0x0 top bits to 0x4, meaning present in L2 but incoherent with L1. However, any bus addresses we were generating in the kernel to be passed to a device had 0x0 bits. That would be a reserved (possibly totally incoherent) value if sent to a GPU peripheral like USB, or L1 allocating if sent to the VC (like a firmware property request). By setting dma-ranges, all of the devices below it get a dev->dma_pfn_offset, so that dma_alloc_coherent() and friends return addresses with 0x4 bits and avoid cache incoherency. This matches the behavior in the downstream 2708 kernel (see BUS_OFFSET in arch/arm/mach-bcm2708/include/mach/memory.h). v2: Fix length of the range from 0x1f000000 to 0x20000000, fixing the translation for the last 16MB. Signed-off-by: Eric Anholt Tested-by: Noralf Trønnes Acked-by: Stephen Warren Cc: popcornmix@gmail.com --- arch/arm/boot/dts/bcm2835.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 4ff1b83191a6..301c73f4ca33 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -15,6 +15,7 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0x7e000000 0x20000000 0x02000000>; + dma-ranges = <0x40000000 0x00000000 0x20000000>; timer@7e003000 { compatible = "brcm,bcm2835-system-timer"; -- cgit v1.2.1 From 398d35af64e2cc7946f37674e3d5f9c17afeddbc Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 12 May 2015 11:37:40 -0700 Subject: mailbox: Fix mailbox full detection on bcm2835. This is a port of a patch by Noralf from the downstream Raspberry Pi tree. With the VC reader blocked and the ARM writing, MAIL0_STA reads empty permanently while MAIL1_STA goes from empty (0x40000000) to non-empty (0x00000001-0x00000007) to full (0x80000008). This bug ended up having no effect on us, because all of our transactions in the client driver were synchronous and under a mutex. Suggested-by: Phil Elwell Signed-off-by: Eric Anholt --- drivers/mailbox/bcm2835-mailbox.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mailbox/bcm2835-mailbox.c b/drivers/mailbox/bcm2835-mailbox.c index 4b13268529f9..0b47dd42f3bd 100644 --- a/drivers/mailbox/bcm2835-mailbox.c +++ b/drivers/mailbox/bcm2835-mailbox.c @@ -49,6 +49,7 @@ #define MAIL0_STA (ARM_0_MAIL0 + 0x18) #define MAIL0_CNF (ARM_0_MAIL0 + 0x1C) #define MAIL1_WRT (ARM_0_MAIL1 + 0x00) +#define MAIL1_STA (ARM_0_MAIL1 + 0x18) /* Status register: FIFO state. */ #define ARM_MS_FULL BIT(31) @@ -117,7 +118,7 @@ static bool bcm2835_last_tx_done(struct mbox_chan *link) bool ret; spin_lock(&mbox->lock); - ret = !(readl(mbox->regs + MAIL0_STA) & ARM_MS_FULL); + ret = !(readl(mbox->regs + MAIL1_STA) & ARM_MS_FULL); spin_unlock(&mbox->lock); return ret; } -- cgit v1.2.1 From 5c7851328cd30409edf718e2c43d6032234c5942 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 10 Mar 2015 15:32:08 -0700 Subject: PM / Domains: If an OF node is found but no device probed yet, defer If we've declared a power domain in the OF, and the OF node is found but the requested domain hasn't been registered on it yet, then we probably have just tried to probe before the power domain driver has. Defer our device's probe until it shows up. Signed-off-by: Eric Anholt --- drivers/base/power/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 2327613d4539..dd8a86b248b6 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2091,7 +2091,7 @@ EXPORT_SYMBOL_GPL(of_genpd_del_provider); struct generic_pm_domain *of_genpd_get_from_provider( struct of_phandle_args *genpdspec) { - struct generic_pm_domain *genpd = ERR_PTR(-ENOENT); + struct generic_pm_domain *genpd = ERR_PTR(-EPROBE_DEFER); struct of_genpd_provider *provider; mutex_lock(&of_genpd_mutex); -- cgit v1.2.1 From ed9caacf0f21aaef17cf0d55997a8a18a5b1eb25 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 2 Mar 2015 14:36:16 -0800 Subject: ARM: bcm2835: Add VC4 to the device tree. Signed-off-by: Eric Anholt --- arch/arm/boot/dts/bcm2835-rpi.dtsi | 4 ++++ arch/arm/boot/dts/bcm2835.dtsi | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi index ace33f69c5aa..9a19fa519856 100644 --- a/arch/arm/boot/dts/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi @@ -56,3 +56,7 @@ status = "okay"; bus-width = <4>; }; + +&vc4 { + firmware = <&firmware>; +}; diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 301c73f4ca33..c641c713f5dc 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -158,6 +158,51 @@ arm-pmu { compatible = "arm,arm1176-pmu"; }; + + v3d: brcm,vc4-v3d@7ec00000 { + compatible = "brcm,vc4-v3d"; + reg = <0x7ec00000 0x1000>; + interrupts = <1 10>; + }; + + hdmi: brcm,vc4-hdmi@7e902000 { + compatible = "brcm,vc4-hdmi"; + reg = <0x7e902000 0x600>; + interrupts = <2 8>, <2 9>; + crtc = <&pv2>; + }; + + pv0: brcm,vc4-pixelvalve@7e206000 { + compatible = "brcm,vc4-pixelvalve"; + reg = <0x7e206000 0x100>; + interrupts = <2 13>; /* pwa2 */ + }; + + pv1: brcm,vc4-pixelvalve@7e207000 { + compatible = "brcm,vc4-pixelvalve"; + reg = <0x7e207000 0x100>; + interrupts = <2 14>; /* pwa1 */ + }; + + pv2: brcm,vc4-pixelvalve@7e807000 { + compatible = "brcm,vc4-pixelvalve"; + reg = <0x7e807000 0x100>; + interrupts = <2 10>; /* pixelvalve */ + }; + + hvs: brcm,hvs@7e400000 { + compatible = "brcm,vc4-hvs"; + reg = <0x7e400000 0x6000>; + }; + + vc4: vc4@0x7e4c0000 { + compatible = "brcm,vc4"; + + gpus = <&gpu>; + crtcs = <&pv0>, <&pv1>, <&pv2>; + encoders = <&hdmi>; + hvss = <&hvs>; + }; }; clocks { -- cgit v1.2.1 From fa8892ee1eb86f9fd5f739c86cdfb63282c9ae8c Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 2 Mar 2015 13:01:12 -0800 Subject: drm/vc4: Add stub KMS support for Raspberry Pi. This is the start of a full VC4 driver. For now we just edit the CRTC display lists to point at kernel-allocated BO memory. Signed-off-by: Eric Anholt --- MAINTAINERS | 6 + arch/arm/boot/dts/bcm2835.dtsi | 2 +- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/vc4/Kconfig | 10 + drivers/gpu/drm/vc4/Makefile | 19 ++ drivers/gpu/drm/vc4/vc4_bo.c | 58 +++++ drivers/gpu/drm/vc4/vc4_crtc.c | 417 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_debugfs.c | 39 ++++ drivers/gpu/drm/vc4/vc4_drv.c | 262 ++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_drv.h | 138 ++++++++++++ drivers/gpu/drm/vc4/vc4_hdmi.c | 446 ++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_hvs.c | 159 ++++++++++++++ drivers/gpu/drm/vc4/vc4_kms.c | 80 +++++++ drivers/gpu/drm/vc4/vc4_plane.c | 299 +++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_regs.h | 376 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_v3d.c | 255 ++++++++++++++++++++++ 17 files changed, 2568 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/vc4/Kconfig create mode 100644 drivers/gpu/drm/vc4/Makefile create mode 100644 drivers/gpu/drm/vc4/vc4_bo.c create mode 100644 drivers/gpu/drm/vc4/vc4_crtc.c create mode 100644 drivers/gpu/drm/vc4/vc4_debugfs.c create mode 100644 drivers/gpu/drm/vc4/vc4_drv.c create mode 100644 drivers/gpu/drm/vc4/vc4_drv.h create mode 100644 drivers/gpu/drm/vc4/vc4_hdmi.c create mode 100644 drivers/gpu/drm/vc4/vc4_hvs.c create mode 100644 drivers/gpu/drm/vc4/vc4_kms.c create mode 100644 drivers/gpu/drm/vc4/vc4_plane.c create mode 100644 drivers/gpu/drm/vc4/vc4_regs.h create mode 100644 drivers/gpu/drm/vc4/vc4_v3d.c diff --git a/MAINTAINERS b/MAINTAINERS index 590304b96b03..cfab17bf208a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2144,6 +2144,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rpi/linux-rpi.git S: Maintained N: bcm2835 +BROADCOM BCM2835 DRM DRIVER +M: Eric Anholt +T: git git://github.com/anholt/linux +S: Maintained +F: drivers/gpu/drm/vc4/* + BROADCOM BCM33XX MIPS ARCHITECTURE M: Kevin Cernekee L: linux-mips@linux-mips.org diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index c641c713f5dc..a64d5924e2c2 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -198,7 +198,7 @@ vc4: vc4@0x7e4c0000 { compatible = "brcm,vc4"; - gpus = <&gpu>; + gpus = <&v3d>; crtcs = <&pv0>, <&pv1>, <&pv2>; encoders = <&hdmi>; hvss = <&hvs>; diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 47f2ce81b412..3151dc84894f 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -217,3 +217,5 @@ source "drivers/gpu/drm/sti/Kconfig" source "drivers/gpu/drm/amd/amdkfd/Kconfig" source "drivers/gpu/drm/imx/Kconfig" + +source "drivers/gpu/drm/vc4/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7d4944e1a60c..c6b413e7929f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_DRM_MGA) += mga/ obj-$(CONFIG_DRM_I810) += i810/ obj-$(CONFIG_DRM_I915) += i915/ obj-$(CONFIG_DRM_MGAG200) += mgag200/ +obj-$(CONFIG_DRM_VC4) += vc4/ obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/ obj-$(CONFIG_DRM_SIS) += sis/ obj-$(CONFIG_DRM_SAVAGE)+= savage/ diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig new file mode 100644 index 000000000000..09706c1eeaca --- /dev/null +++ b/drivers/gpu/drm/vc4/Kconfig @@ -0,0 +1,10 @@ +config DRM_VC4 + tristate "Broadcom VC4 Graphics" + depends on ARCH_BCM2835 + depends on DRM + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_KMS_CMA_HELPER + help + Choose this option if you have a system that has a Broadcom + VC4 GPU, such as the Raspberry Pi or other BCM2708/BCM2835. diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile new file mode 100644 index 000000000000..18d5197e4892 --- /dev/null +++ b/drivers/gpu/drm/vc4/Makefile @@ -0,0 +1,19 @@ +ccflags-y := -Iinclude/drm + +# Please keep these build lists sorted! + +# core driver code +vc4-y := \ + vc4_bo.o \ + vc4_crtc.o \ + vc4_drv.o \ + vc4_kms.o \ + vc4_hdmi.o \ + vc4_hvs.o \ + vc4_plane.o \ + vc4_v3d.o \ + $() + +vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o + +obj-$(CONFIG_DRM_VC4) += vc4.o diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c new file mode 100644 index 000000000000..1459395d6e94 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -0,0 +1,58 @@ +/* + * Copyright © 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * VC4 GEM BO management support. + * + * The VC4 GPU architecture (both scanout and rendering) has direct + * access to system memory with no MMU in between. To support it, we + * use the GEM CMA helper functions to allocate contiguous ranges of + * physical memory for our BOs. + */ + +#include "vc4_drv.h" + +struct vc4_bo * +vc4_bo_create(struct drm_device *dev, size_t size) +{ + struct drm_gem_cma_object *cma_obj; + + /* Otherwise, make a new BO. */ + cma_obj = drm_gem_cma_create(dev, size); + if (IS_ERR(cma_obj)) + return NULL; + else + return to_vc4_bo(&cma_obj->base); +} + +int +vc4_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + struct vc4_bo *bo = NULL; + int ret; + + if (args->pitch < min_pitch) + args->pitch = min_pitch; + + if (args->size < args->pitch * args->height) + args->size = args->pitch * args->height; + + mutex_lock(&dev->struct_mutex); + bo = vc4_bo_create(dev, roundup(args->size, PAGE_SIZE)); + mutex_unlock(&dev->struct_mutex); + if (!bo) + return -ENOMEM; + + ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); + drm_gem_object_unreference_unlocked(&bo->base.base); + + return ret; +} diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c new file mode 100644 index 000000000000..0c33dd1f7468 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Controls the timings of the hardware's pixel valve. + */ + +#include "drm_atomic_helper.h" +#include "drm_crtc_helper.h" +#include "linux/component.h" +#include "vc4_drv.h" +#include "vc4_regs.h" + +#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) +#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset)) + +#define CRTC_REG(reg) { reg, #reg } +static const struct { + u32 reg; + const char *name; +} crtc_regs[] = { + CRTC_REG(PV_CONTROL), + CRTC_REG(PV_V_CONTROL), + CRTC_REG(PV_VSYNCD), + CRTC_REG(PV_HORZA), + CRTC_REG(PV_HORZB), + CRTC_REG(PV_VERTA), + CRTC_REG(PV_VERTB), + CRTC_REG(PV_VERTA_EVEN), + CRTC_REG(PV_VERTB_EVEN), + CRTC_REG(PV_INTEN), + CRTC_REG(PV_INTSTAT), + CRTC_REG(PV_STAT), +}; + +static void +vc4_crtc_dump_regs(struct vc4_crtc *vc4_crtc) +{ + int i; + + rmb(); + for (i = 0; i < ARRAY_SIZE(crtc_regs); i++) { + DRM_INFO("0x%04x (%s): 0x%08x\n", + crtc_regs[i].reg, crtc_regs[i].name, + CRTC_READ(crtc_regs[i].reg)); + } +} + +static void vc4_crtc_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); +} + +static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ +} + +static void vc4_crtc_disable(struct drm_crtc *crtc) +{ +} + +static void vc4_crtc_enable(struct drm_crtc *crtc) +{ +} + +static int vc4_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_plane *plane; + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + u32 dlist_count = 0; + + drm_atomic_crtc_state_for_each_plane(plane, state) { + struct drm_plane_state *plane_state = + state->state->plane_states[drm_plane_index(plane)]; + + /* plane might not have changed, in which case take + * current state: + */ + if (!plane_state) + plane_state = plane->state; + + dlist_count += vc4_plane_dlist_size(plane_state); + } + + dlist_count++; /* Account for SCALER_CTL0_END. */ + + if (!vc4_crtc->dlist || dlist_count > vc4_crtc->dlist_size) { + vc4_crtc->dlist = ((u32 __iomem *)vc4->hvs->dlist + + HVS_BOOTLOADER_DLIST_END); + vc4_crtc->dlist_size = ((SCALER_DLIST_SIZE >> 2) - + HVS_BOOTLOADER_DLIST_END); + + if (dlist_count > vc4_crtc->dlist_size) { + DRM_DEBUG_KMS("dlist too large for CRTC (%d > %d).\n", + dlist_count, vc4_crtc->dlist_size); + return -EINVAL; + } + } + + return 0; +} + +static void vc4_crtc_atomic_begin(struct drm_crtc *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; + 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; + } +} + +static void vc4_crtc_atomic_flush(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 drm_display_mode *mode = &crtc->hwmode; + struct drm_plane *plane; + bool debug_dump_regs = false; + u32 __iomem *dlist_next = vc4_crtc->dlist; + + if (debug_dump_regs) { + DRM_INFO("CRTC regs before:\n"); + vc4_crtc_dump_regs(vc4_crtc); + vc4_hvs_dump_state(dev); + } + + /* + * Copy all the active planes' dlist contents to the hardware dlist. + * + * XXX: If the new display list was large enough that it + * overlapped a currently-read display list, we need to do + * something like disable scanout before putting in the new + * list. + */ + drm_atomic_crtc_for_each_plane(plane, crtc) { + dlist_next += vc4_plane_write_dlist(plane, dlist_next); + } + + if (dlist_next == vc4_crtc->dlist) { + /* If no planes were enabled, use the SCALER_CTL0_END + * at the start of the display list memory (in the + * bootloader section). We'll rewrite that + * SCALER_CTL0_END, just in case, though. + */ + writel(SCALER_CTL0_END, vc4->hvs->dlist); + HVS_WRITE(vc4_crtc->displist_reg, 0); + } else { + writel(SCALER_CTL0_END, dlist_next); + dlist_next++; + + HVS_WRITE(vc4_crtc->displist_reg, + (u32 *)vc4_crtc->dlist - (u32 *)vc4->hvs->dlist); + + /* Make the next display list start after ours. */ + vc4_crtc->dlist_size -= (dlist_next - vc4_crtc->dlist); + vc4_crtc->dlist = dlist_next; + } + + if (0) { + u32 verta = (VC4_SET_FIELD(mode->vtotal - mode->vdisplay, + PV_VERTA_VBP) | + VC4_SET_FIELD(mode->vsync_start, PV_VERTA_VSYNC)); + u32 vertb = (VC4_SET_FIELD(0, PV_VERTB_VFP), + VC4_SET_FIELD(mode->vdisplay, PV_VERTB_VACTIVE)); + u32 format = PV_CONTROL_FORMAT_24; + + CRTC_WRITE(PV_V_CONTROL, + CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN); + + do { + /* XXX SLEEP */ + } while (CRTC_READ(PV_STAT) & (PV_STAT_RUNNING_MASK)); + + CRTC_WRITE(PV_HORZA, + VC4_SET_FIELD(mode->htotal - mode->hdisplay, + PV_HORZA_HBP) | + VC4_SET_FIELD(mode->hsync_start, PV_HORZA_HSYNC)); + CRTC_WRITE(PV_HORZB, + VC4_SET_FIELD(0, PV_HORZB_HFP) | + VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE)); + + CRTC_WRITE(PV_VERTA, verta); + CRTC_WRITE(PV_VERTB, vertb); + CRTC_WRITE(PV_VERTA_EVEN, verta); + CRTC_WRITE(PV_VERTB_EVEN, vertb); + + CRTC_WRITE(PV_DSI_HACT, mode->htotal - mode->hdisplay); + + CRTC_WRITE(PV_CONTROL, + VC4_SET_FIELD(format, PV_CONTROL_FORMAT) | + VC4_SET_FIELD(vc4_get_fifo_full_level(format), + PV_CONTROL_FIFO_LEVEL) | + PV_CONTROL_CLR_AT_START | + PV_CONTROL_TRIGGER_UNDERFLOW | + PV_CONTROL_WAIT_HSTART | + PV_CONTROL_CLK_MUX_EN | + VC4_SET_FIELD(0, PV_CONTROL_CLK_SELECT) | + PV_CONTROL_FIFO_CLR | + PV_CONTROL_EN); + + CRTC_WRITE(PV_V_CONTROL, + PV_VCONTROL_CONTINUOUS | + PV_VCONTROL_VIDEN); + } + + if (debug_dump_regs) { + DRM_INFO("CRTC regs after:\n"); + vc4_crtc_dump_regs(vc4_crtc); + vc4_hvs_dump_state(dev); + } +} + +int vc4_enable_vblank(struct drm_device *dev, int crtc_id) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + + CRTC_WRITE(PV_INTEN, PV_INT_VFP_START); + + return 0; +} + +void vc4_disable_vblank(struct drm_device *dev, int crtc_id) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = vc4->crtc[crtc_id]; + + CRTC_WRITE(PV_INTEN, 0); +} + +static irqreturn_t vc4_crtc_irq_handler(int irq, void *data) +{ + struct vc4_crtc *vc4_crtc = data; + u32 stat = CRTC_READ(PV_INTSTAT); + irqreturn_t ret = IRQ_NONE; + + if (stat & PV_INT_VFP_START) { + drm_crtc_handle_vblank(&vc4_crtc->base); + CRTC_WRITE(PV_INTSTAT, PV_INT_VFP_START); + ret = IRQ_HANDLED; + } + + return ret; +} + +static const struct drm_crtc_funcs vc4_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = vc4_crtc_destroy, + .page_flip = drm_atomic_helper_page_flip, + .set_property = NULL, + .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ + .cursor_move = NULL, /* handled by drm_mode_cursor_universal */ + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, +}; + +static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { + .mode_fixup = vc4_crtc_mode_fixup, + .mode_set_nofb = vc4_crtc_mode_set_nofb, + .disable = vc4_crtc_disable, + .enable = vc4_crtc_enable, + .atomic_check = vc4_crtc_atomic_check, + .atomic_begin = vc4_crtc_atomic_begin, + .atomic_flush = vc4_crtc_atomic_flush, +}; + +static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_crtc *vc4_crtc; + struct drm_crtc *crtc; + struct drm_plane *primary_plane, *cursor_plane; + int ret; + + primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY); + if (!primary_plane) { + dev_err(dev, "failed to construct primary plane\n"); + ret = PTR_ERR(primary_plane); + goto fail; + } + + cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); + if (!cursor_plane) { + dev_err(dev, "failed to construct cursor_plane\n"); + ret = PTR_ERR(cursor_plane); + goto fail; + } + + vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL); + if (!vc4_crtc) + return -ENOMEM; + crtc = &vc4_crtc->base; + + vc4_crtc->regs = vc4_ioremap_regs(pdev); + if (IS_ERR(vc4_crtc->regs)) + return PTR_ERR(vc4_crtc->regs); + + drm_crtc_init_with_planes(drm, crtc, primary_plane, cursor_plane, + &vc4_crtc_funcs); + drm_crtc_helper_add(crtc, &vc4_crtc_helper_funcs); + primary_plane->crtc = crtc; + cursor_plane->crtc = crtc; + vc4->crtc[drm_crtc_index(crtc)] = vc4_crtc; + + /* Until we have full scanout setup to route things through to + * encoders, line things up like the firmware did. + */ + switch (drm_crtc_index(crtc)) { + case 0: + vc4_crtc->displist_reg = SCALER_DISPLIST0; + break; + case 1: + vc4_crtc->displist_reg = SCALER_DISPLIST2; + break; + default: + case 2: + vc4_crtc->displist_reg = SCALER_DISPLIST1; + break; + } + + 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); + + platform_set_drvdata(pdev, vc4_crtc); + + return 0; + +fail: + return ret; +} + +static void vc4_crtc_unbind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev); + + vc4_crtc_destroy(&vc4_crtc->base); + + CRTC_WRITE(PV_INTEN, 0); + + platform_set_drvdata(pdev, NULL); +} + +static const struct component_ops vc4_crtc_ops = { + .bind = vc4_crtc_bind, + .unbind = vc4_crtc_unbind, +}; + +static int vc4_crtc_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_crtc_ops); +} + +static int vc4_crtc_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_crtc_ops); + return 0; +} + +static const struct of_device_id vc4_crtc_dt_match[] = { + { .compatible = "brcm,vc4-pixelvalve" }, + {} +}; + +static struct platform_driver vc4_crtc_driver = { + .probe = vc4_crtc_dev_probe, + .remove = vc4_crtc_dev_remove, + .driver = { + .name = "vc4_crtc", + .of_match_table = vc4_crtc_dt_match, + }, +}; + +void __init vc4_crtc_register(void) +{ + platform_driver_register(&vc4_crtc_driver); +} + +void __exit vc4_crtc_unregister(void) +{ + platform_driver_unregister(&vc4_crtc_driver); +} diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c new file mode 100644 index 000000000000..abd541f470ca --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -0,0 +1,39 @@ +/* + * Copyright © 2014 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "vc4_drv.h" +#include "vc4_regs.h" + +static const struct drm_info_list vc4_debugfs_list[] = { + {"v3d_ident", vc4_v3d_debugfs_ident, 0}, + {"v3d_regs", vc4_v3d_debugfs_regs, 0}, + {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, + {"hvs_regs", vc4_hvs_debugfs_regs, 0}, +}; +#define VC4_DEBUGFS_ENTRIES ARRAY_SIZE(vc4_debugfs_list) + +int +vc4_debugfs_init(struct drm_minor *minor) +{ + return drm_debugfs_create_files(vc4_debugfs_list, + VC4_DEBUGFS_ENTRIES, + minor->debugfs_root, minor); +} + +void +vc4_debugfs_cleanup(struct drm_minor *minor) +{ + drm_debugfs_remove_files(vc4_debugfs_list, + VC4_DEBUGFS_ENTRIES, minor); +} diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c new file mode 100644 index 000000000000..94dcc8aa48de --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2014-2015 Broadcom + * Copyright (C) 2013 Red Hat + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "vc4_drv.h" +#include "vc4_regs.h" + +#define DRIVER_NAME "vc4" +#define DRIVER_DESC "Broadcom VC4 graphics" +#define DRIVER_DATE "20140616" +#define DRIVER_MAJOR 0 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +/* + * Helper function for mapping the regs on a platform device. + * + * We assume only one register range per device, so we use index 0. + */ +void __iomem * +vc4_ioremap_regs(struct platform_device *dev) +{ + struct resource *res; + void __iomem *map; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + map = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(map)) { + int ret = PTR_ERR(map); + DRM_ERROR("Failed to map registers: %d\n", ret); + return map; + } + + return map; +} + +static int +vc4_drm_load(struct drm_device *dev, unsigned long flags) +{ + struct platform_device *firmware_pdev; + struct vc4_dev *vc4; + int ret; + + vc4 = devm_kzalloc(dev->dev, sizeof(*vc4), GFP_KERNEL); + if (!vc4) + return -ENOMEM; + + vc4->firmware_node = of_parse_phandle(dev->dev->of_node, "firmware", 0); + if (!vc4->firmware_node) { + DRM_ERROR("Failed to parse firmware node.\n"); + return -EINVAL; + } + firmware_pdev = of_find_device_by_node(vc4->firmware_node); + if (!platform_get_drvdata(firmware_pdev)) { + DRM_DEBUG("firmware device not probed yet.\n"); + return -EPROBE_DEFER; + } + + dev_set_drvdata(dev->dev, dev); + vc4->dev = dev; + dev->dev_private = vc4; + + drm_mode_config_init(dev); + + ret = component_bind_all(dev->dev, dev); + if (ret) + return ret; + + vc4_kms_load(dev); + + return 0; +} + +static int vc4_drm_unload(struct drm_device *dev) +{ + drm_mode_config_cleanup(dev); + + component_unbind_all(dev->dev, dev); + + return 0; +} + +static const struct file_operations vc4_drm_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_gem_cma_mmap, + .poll = drm_poll, + .read = drm_read, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static const struct drm_ioctl_desc vc4_drm_ioctls[] = { +}; + +static struct drm_driver vc4_drm_driver = { + .driver_features = (DRIVER_MODESET | + DRIVER_GEM | + DRIVER_PRIME), + .load = vc4_drm_load, + .unload = vc4_drm_unload, + .set_busid = drm_platform_set_busid, + + .enable_vblank = vc4_enable_vblank, + .disable_vblank = vc4_disable_vblank, + .get_vblank_counter = drm_vblank_count, + +#if defined(CONFIG_DEBUG_FS) + .debugfs_init = vc4_debugfs_init, + .debugfs_cleanup = vc4_debugfs_cleanup, +#endif + + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + + .dumb_create = vc4_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + + .ioctls = vc4_drm_ioctls, + .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), + .fops = &vc4_drm_fops, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +static int vc4_drm_bind(struct device *dev) +{ + return drm_platform_init(&vc4_drm_driver, to_platform_device(dev)); +} + +static void vc4_drm_unbind(struct device *dev) +{ + drm_put_dev(platform_get_drvdata(to_platform_device(dev))); +} + +static const struct component_master_ops vc4_drm_ops = { + .bind = vc4_drm_bind, + .unbind = vc4_drm_unbind, +}; + +/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx + * (or probably any other).. so probably some room for some helpers + */ +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int add_components(struct device *dev, struct component_match **matchptr, + const char *name) +{ + struct device_node *np = dev->of_node; + unsigned i; + + for (i = 0; ; i++) { + struct device_node *node; + + node = of_parse_phandle(np, name, i); + if (!node) + break; + + component_match_add(dev, matchptr, compare_of, node); + } + + return 0; +} + +static int +vc4_platform_drm_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + + add_components(&pdev->dev, &match, "gpus"); + add_components(&pdev->dev, &match, "crtcs"); + add_components(&pdev->dev, &match, "encoders"); + add_components(&pdev->dev, &match, "hvss"); + + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + return component_master_add_with_match(&pdev->dev, &vc4_drm_ops, match); +} + +static int +vc4_platform_drm_remove(struct platform_device *pdev) +{ + drm_put_dev(platform_get_drvdata(pdev)); + + return 0; +} + +static const struct of_device_id vc4_of_match[] = { + { .compatible = "brcm,vc4", }, + {}, +}; +MODULE_DEVICE_TABLE(of, vc4_of_match); + +static struct platform_driver vc4_platform_driver = { + .probe = vc4_platform_drm_probe, + .remove = vc4_platform_drm_remove, + .driver = { + .name = "vc4-drm", + .owner = THIS_MODULE, + .of_match_table = vc4_of_match, + }, +}; + +static int __init vc4_drm_register(void) +{ + vc4_v3d_register(); + vc4_hdmi_register(); + vc4_crtc_register(); + vc4_hvs_register(); + return platform_driver_register(&vc4_platform_driver); +} + +static void __exit vc4_drm_unregister(void) +{ + platform_driver_unregister(&vc4_platform_driver); + vc4_hvs_unregister(); + vc4_crtc_unregister(); + vc4_hdmi_unregister(); + vc4_v3d_unregister(); +} + +module_init(vc4_drm_register); +module_exit(vc4_drm_unregister); + +MODULE_ALIAS("platform:vc4-drm"); +MODULE_DESCRIPTION("Broadcom VC4 DRM Driver"); +MODULE_AUTHOR("Eric Anholt "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h new file mode 100644 index 000000000000..aa143717a71b --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "drmP.h" +#include "drm_gem_cma_helper.h" + +struct vc4_dev { + struct drm_device *dev; + + struct device_node *firmware_node; + + struct vc4_hdmi *hdmi; + struct vc4_hvs *hvs; + struct vc4_crtc *crtc[3]; + struct vc4_v3d *v3d; +}; + +static inline struct vc4_dev * +to_vc4_dev(struct drm_device *dev) +{ + return (struct vc4_dev *)dev->dev_private; +} + +struct vc4_bo { + struct drm_gem_cma_object base; +}; + +static inline struct vc4_bo * +to_vc4_bo(struct drm_gem_object *bo) +{ + return (struct vc4_bo *)bo; +} + +struct vc4_v3d { + struct platform_device *pdev; + void __iomem *regs; +}; + +struct vc4_hvs { + struct platform_device *pdev; + void __iomem *regs; + void __iomem *dlist; +}; + +struct vc4_crtc { + struct drm_crtc base; + void __iomem *regs; + + u32 displist_reg; + + /* + * Pointer to the actual hardware display list memory for the + * crtc. + */ + u32 __iomem *dlist; + + u32 dlist_size; /* in dwords */ +}; + +static inline struct vc4_crtc * +to_vc4_crtc(struct drm_crtc *crtc) +{ + return (struct vc4_crtc *)crtc; +} + +struct vc4_plane { + struct drm_plane base; +}; + +static inline struct vc4_plane * +to_vc4_plane(struct drm_plane *plane) +{ + return (struct vc4_plane *)plane; +} + +#define V3D_READ(offset) readl(vc4->v3d->regs + offset) +#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) +#define HVS_READ(offset) readl(vc4->hvs->regs + offset) +#define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) +#define HDMI_READ(offset) readl(vc4->hdmi->regs + offset) +#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->regs + offset) + +/* vc4_bo.c */ +void vc4_free_object(struct drm_gem_object *gem_obj); +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); +int vc4_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); +struct dma_buf *vc4_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); + +/* vc4_crtc.c */ +void vc4_crtc_register(void); +void vc4_crtc_unregister(void); +int vc4_enable_vblank(struct drm_device *dev, int crtc_id); +void vc4_disable_vblank(struct drm_device *dev, int crtc_id); + +/* vc4_debugfs.c */ +int vc4_debugfs_init(struct drm_minor *minor); +void vc4_debugfs_cleanup(struct drm_minor *minor); + +/* vc4_drv.c */ +void __iomem *vc4_ioremap_regs(struct platform_device *dev); + +/* vc4_hdmi.c */ +void vc4_hdmi_register(void); +void vc4_hdmi_unregister(void); +struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev); +struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, + struct drm_encoder *encoder); +int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); + +/* vc4_hvs.c */ +void vc4_hvs_register(void); +void vc4_hvs_unregister(void); +void vc4_hvs_dump_state(struct drm_device *dev); +int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused); + +/* vc4_kms.c */ +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); +u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); +u32 vc4_plane_dlist_size(struct drm_plane_state *state); + +/* vc4_v3d.c */ +void vc4_v3d_register(void); +void vc4_v3d_unregister(void); +int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); +int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); +int vc4_v3d_set_power(struct vc4_dev *vc4, bool on); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c new file mode 100644 index 000000000000..6774db8124a9 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2014 The Linux Foundation. All rights reserved. + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "drm_atomic_helper.h" +#include "drm_crtc_helper.h" +#include "drm_edid.h" +#include "linux/component.h" +#include "linux/of_platform.h" +#include "vc4_drv.h" +#include "vc4_regs.h" + +/* General HDMI hardware state. */ +struct vc4_hdmi { + struct platform_device *pdev; + void __iomem *regs; +}; + +/* VC4 HDMI encoder KMS struct */ +struct vc4_hdmi_encoder { + struct drm_encoder base; +}; +static inline struct vc4_hdmi_encoder * +to_vc4_hdmi_encoder(struct drm_encoder *encoder) +{ + return container_of(encoder, struct vc4_hdmi_encoder, 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); +} + +#define HDMI_REG(reg) { reg, #reg } +static const struct { + u32 reg; + const char *name; +} hdmi_regs[] = { + HDMI_REG(VC4_HDMI_CORE_REV), + HDMI_REG(VC4_HDMI_SW_RESET_CONTROL), + HDMI_REG(VC4_HDMI_HOTPLUG_INT), + HDMI_REG(VC4_HDMI_HOTPLUG), + HDMI_REG(VC4_HDMI_FIFO_CTL), + HDMI_REG(VC4_HDMI_HORZA), + HDMI_REG(VC4_HDMI_HORZB), + HDMI_REG(VC4_HDMI_VERTA0), + HDMI_REG(VC4_HDMI_VERTA1), + HDMI_REG(VC4_HDMI_VERTB0), + HDMI_REG(VC4_HDMI_VERTB1), +}; + +#ifdef CONFIG_DEBUG_FS +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); + int i; + + for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + hdmi_regs[i].name, hdmi_regs[i].reg, + HDMI_READ(hdmi_regs[i].reg)); + } + + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +static void vc4_hdmi_dump_regs(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i; + + rmb(); + for (i = 0; i < ARRAY_SIZE(hdmi_regs); i++) { + DRM_INFO("0x%04x (%s): 0x%08x\n", + hdmi_regs[i].reg, hdmi_regs[i].name, + HDMI_READ(hdmi_regs[i].reg)); + } +} + +static enum drm_connector_status +vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static void vc4_hdmi_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const u8 edid_1920_1080[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78, + 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, + 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, + 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, + 0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, + 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, + 0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, + 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46, + 0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05, +}; + +static int vc4_get_fixed_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) +{ + if (block != 0) + return -EINVAL; + if (len > sizeof(edid_1920_1080)) + return -EINVAL; + memcpy(buf, edid_1920_1080, len); + return 0; +} + + +static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) +{ + int ret = 0; + + struct edid *edid = drm_do_get_edid(connector, vc4_get_fixed_edid_block, + NULL); + + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + + return ret; +} + +static struct drm_encoder * +vc4_hdmi_connector_best_encoder(struct drm_connector *connector) +{ + struct vc4_hdmi_connector *hdmi_connector = + to_vc4_hdmi_connector(connector); + return hdmi_connector->encoder; +} + +static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = vc4_hdmi_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = vc4_hdmi_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = { + .get_modes = vc4_hdmi_connector_get_modes, + .mode_valid = NULL, + .best_encoder = vc4_hdmi_connector_best_encoder, +}; + +struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, + struct drm_encoder *encoder) +{ + struct drm_connector *connector = NULL; + struct vc4_hdmi_connector *hdmi_connector; + int ret = 0; + + hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector), + GFP_KERNEL); + if (!hdmi_connector) { + ret = -ENOMEM; + goto fail; + } + connector = &hdmi_connector->base; + + 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); + + connector->polled = (DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT); + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_connector_register(connector); + + drm_mode_connector_attach_encoder(connector, encoder); + + return connector; + + fail: + if (connector) + vc4_hdmi_connector_destroy(connector); + + return ERR_PTR(ret); +} + +static void vc4_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = { + .destroy = vc4_encoder_destroy, +}; + +static bool vc4_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + bool debug_dump_regs = false; + + mode = adjusted_mode; + + if (debug_dump_regs) { + DRM_INFO("HDMI regs before:\n"); + vc4_hdmi_dump_regs(dev); + } + + if (0) { + bool hsync_pos = !(mode->flags & DRM_MODE_FLAG_NHSYNC); + bool vsync_pos = !(mode->flags & DRM_MODE_FLAG_NVSYNC); + u32 vactive = (mode->vdisplay >> + (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0); + u32 verta = (VC4_SET_FIELD(mode->vsync_start, + VC4_HDMI_VERTA_VFP) | + VC4_SET_FIELD(mode->vsync_end - mode->vsync_start, + VC4_HDMI_VERTA_VSP) | + VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL)); + + HDMI_WRITE(VC4_HDMI_HORZA, + (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) | + (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0)); + HDMI_WRITE(VC4_HDMI_HORZB, + VC4_SET_FIELD(mode->htotal - mode->hdisplay, + VC4_HDMI_HORZB_HBP) | + VC4_SET_FIELD(mode->hsync_end - mode->hsync_start, + VC4_HDMI_HORZB_HSP) | + 0 /* XXX: HFP? */); + HDMI_WRITE(VC4_HDMI_VERTA0, verta); + HDMI_WRITE(VC4_HDMI_VERTA1, verta); + HDMI_WRITE(VC4_HDMI_VERTB0, + VC4_SET_FIELD(mode->vsync_start, + VC4_HDMI_VERTB_VSPO) | + VC4_SET_FIELD(mode->vtotal - mode->vdisplay, + VC4_HDMI_VERTB_VBP)); + HDMI_WRITE(VC4_HDMI_VERTB1, + VC4_SET_FIELD(mode->vsync_start, + VC4_HDMI_VERTB_VSPO) | + VC4_SET_FIELD(mode->vtotal - mode->vsync_end, + VC4_HDMI_VERTB_VBP)); + + /* XXX: HD VID CTL */ + HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); + /* XXX: HD CSC CTL */ + } + + if (debug_dump_regs) { + DRM_INFO("HDMI regs after:\n"); + vc4_hdmi_dump_regs(dev); + } +} + +static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) +{ +} + +static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) +{ +} + +static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { + .mode_fixup = vc4_hdmi_encoder_mode_fixup, + .mode_set = vc4_hdmi_encoder_mode_set, + .disable = vc4_hdmi_encoder_disable, + .enable = vc4_hdmi_encoder_enable, +}; + +static struct drm_crtc * +vc4_get_crtc_node(struct platform_device *pdev) +{ + struct device_node *crtc_node; + struct platform_device *crtc_pdev; + + crtc_node = of_parse_phandle(pdev->dev.of_node, "crtc", 0); + if (!crtc_node) { + DRM_ERROR ("No CRTC for hdmi in DT\n"); + return ERR_PTR(-EINVAL); + } + + crtc_pdev = of_find_device_by_node(crtc_node); + if (!crtc_pdev) { + DRM_ERROR ("No CRTC device attached to OF node\n"); + return ERR_PTR(-EINVAL); + } + + return platform_get_drvdata(crtc_pdev); +} + +/* initialize encoder */ +struct drm_encoder *vc4_hdmi_encoder_init(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_encoder *encoder = NULL; + struct vc4_hdmi_encoder *vc4_hdmi_encoder; + struct drm_crtc *crtc; + int ret; + + vc4_hdmi_encoder = devm_kzalloc(dev->dev, sizeof(*vc4_hdmi_encoder), + GFP_KERNEL); + if (!vc4_hdmi_encoder) { + ret = -ENOMEM; + goto fail; + } + encoder = &vc4_hdmi_encoder->base; + + crtc = vc4_get_crtc_node(vc4->hdmi->pdev); + if (IS_ERR(crtc)) { + ret = PTR_ERR(crtc); + goto fail; + } + + drm_encoder_init(dev, encoder, &vc4_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs); + + encoder->possible_crtcs = drm_crtc_mask(crtc); + + return encoder; + +fail: + if (encoder) + vc4_encoder_destroy(encoder); + + return ERR_PTR(ret); +} + +static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; + struct vc4_hdmi *hdmi; + + hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); + if (!hdmi) + return -ENOMEM; + + hdmi->pdev = pdev; + hdmi->regs = vc4_ioremap_regs(pdev); + if (IS_ERR(hdmi->regs)) + return PTR_ERR(hdmi->regs); + + vc4->hdmi = hdmi; + + return 0; +} + +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; + + vc4->hdmi = NULL; +} + +static const struct component_ops vc4_hdmi_ops = { + .bind = vc4_hdmi_bind, + .unbind = vc4_hdmi_unbind, +}; + +static int vc4_hdmi_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_hdmi_ops); +} + +static int vc4_hdmi_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_hdmi_ops); + return 0; +} + +static const struct of_device_id vc4_hdmi_dt_match[] = { + { .compatible = "brcm,vc4-hdmi" }, + {} +}; + +static struct platform_driver vc4_hdmi_driver = { + .probe = vc4_hdmi_dev_probe, + .remove = vc4_hdmi_dev_remove, + .driver = { + .name = "vc4_hdmi", + .of_match_table = vc4_hdmi_dt_match, + }, +}; + +void __init vc4_hdmi_register(void) +{ + platform_driver_register(&vc4_hdmi_driver); +} + +void __exit vc4_hdmi_unregister(void) +{ + platform_driver_unregister(&vc4_hdmi_driver); +} diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c new file mode 100644 index 000000000000..f50a67910fb4 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "linux/component.h" +#include "vc4_drv.h" +#include "vc4_regs.h" + +#define HVS_REG(reg) { reg, #reg } +static const struct { + u32 reg; + const char *name; +} hvs_regs[] = { + HVS_REG(SCALER_DISPCTRL), + HVS_REG(SCALER_DISPSTAT), + HVS_REG(SCALER_DISPID), + HVS_REG(SCALER_DISPECTRL), + HVS_REG(SCALER_DISPPROF), + HVS_REG(SCALER_DISPDITHER), + HVS_REG(SCALER_DISPEOLN), + HVS_REG(SCALER_DISPLIST0), + HVS_REG(SCALER_DISPLIST1), + HVS_REG(SCALER_DISPLIST2), + HVS_REG(SCALER_DISPLSTAT), + HVS_REG(SCALER_DISPLACT0), + HVS_REG(SCALER_DISPLACT1), + HVS_REG(SCALER_DISPLACT2), + HVS_REG(SCALER_DISPCTRL0), + HVS_REG(SCALER_DISPBKGND0), + HVS_REG(SCALER_DISPSTAT0), + HVS_REG(SCALER_DISPBASE0), + HVS_REG(SCALER_DISPCTRL1), + HVS_REG(SCALER_DISPBKGND1), + HVS_REG(SCALER_DISPSTAT1), + HVS_REG(SCALER_DISPBASE1), + HVS_REG(SCALER_DISPCTRL2), + HVS_REG(SCALER_DISPBKGND2), + HVS_REG(SCALER_DISPSTAT2), + HVS_REG(SCALER_DISPBASE2), + HVS_REG(SCALER_DISPALPHA2), +}; + +void +vc4_hvs_dump_state(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i; + + rmb(); + for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) { + DRM_INFO("0x%04x (%s): 0x%08x\n", + hvs_regs[i].reg, hvs_regs[i].name, + HVS_READ(hvs_regs[i].reg)); + } + + DRM_INFO("HVS ctx:\n"); + for (i = 0; i < 64; i += 4) { + DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n", + i * 4, i < HVS_BOOTLOADER_DLIST_END ? "B" : "D", + ((uint32_t *)vc4->hvs->dlist)[i + 0], + ((uint32_t *)vc4->hvs->dlist)[i + 1], + ((uint32_t *)vc4->hvs->dlist)[i + 2], + ((uint32_t *)vc4->hvs->dlist)[i + 3]); + } +} + +#ifdef CONFIG_DEBUG_FS +int vc4_hvs_debugfs_regs(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(hvs_regs); i++) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + hvs_regs[i].name, hvs_regs[i].reg, + HVS_READ(hvs_regs[i].reg)); + } + + return 0; +} +#endif + +static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; + struct vc4_hvs *hvs = NULL; + + hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); + if (!hvs) + return -ENOMEM; + + hvs->pdev = pdev; + + hvs->regs = vc4_ioremap_regs(pdev); + if (IS_ERR(hvs->regs)) + return PTR_ERR(hvs->regs); + + hvs->dlist = hvs->regs + SCALER_DLIST_START; + + vc4->hvs = hvs; + return 0; +} + +static void vc4_hvs_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = drm->dev_private; + + vc4->hvs = NULL; +} + +static const struct component_ops vc4_hvs_ops = { + .bind = vc4_hvs_bind, + .unbind = vc4_hvs_unbind, +}; + +static int vc4_hvs_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_hvs_ops); +} + +static int vc4_hvs_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_hvs_ops); + return 0; +} + +static const struct of_device_id vc4_hvs_dt_match[] = { + { .compatible = "brcm,vc4-hvs" }, + {} +}; + +static struct platform_driver vc4_hvs_driver = { + .probe = vc4_hvs_dev_probe, + .remove = vc4_hvs_dev_remove, + .driver = { + .name = "vc4_hvs", + .of_match_table = vc4_hvs_dt_match, + }, +}; + +void __init vc4_hvs_register(void) +{ + platform_driver_register(&vc4_hvs_driver); +} + +void __exit vc4_hvs_unregister(void) +{ + platform_driver_unregister(&vc4_hvs_driver); +} diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c new file mode 100644 index 000000000000..98b57bd0fc80 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "drm_crtc.h" +#include "drm_atomic_helper.h" +#include "drm_crtc_helper.h" +#include "drm_plane_helper.h" +#include "drm_fb_cma_helper.h" +#include "vc4_drv.h" + +static const struct drm_mode_config_funcs vc4_mode_funcs = { + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .fb_create = drm_fb_cma_create, +}; + +/* + * Calls out to initialize all of the VC4 KMS objects. + */ +static int +vc4_init_modeset_objects(struct drm_device *dev) +{ + struct drm_encoder *encoder; + struct drm_connector *connector; + int ret = 0; + + encoder = vc4_hdmi_encoder_init(dev); + if (IS_ERR(encoder)) { + dev_err(dev->dev, "failed to construct HDMI encoder\n"); + ret = PTR_ERR(encoder); + goto fail; + } + + connector = vc4_hdmi_connector_init(dev, encoder); + if (IS_ERR(connector)) { + ret = PTR_ERR(connector); + dev_err(dev->dev, "failed to initialize HDMI connector\n"); + goto fail; + } + +fail: + return ret; +} + +int +vc4_kms_load(struct drm_device *dev) +{ + int ret; + + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); + if (ret < 0) { + dev_err(dev->dev, "failed to initialize vblank\n"); + return ret; + } + + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + dev->mode_config.funcs = &vc4_mode_funcs; + dev->mode_config.preferred_depth = 24; + + ret = vc4_init_modeset_objects(dev); + if (ret) + goto fail; + + drm_mode_config_reset(dev); + + drm_fbdev_cma_init(dev, 32, + dev->mode_config.num_crtc, + dev->mode_config.num_connector); + + drm_kms_helper_poll_init(dev); + +fail: + return ret; +} diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c new file mode 100644 index 000000000000..cfae803389d9 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Controls an individual layer of pixels being scanned out by the HVS. + */ + +#include "vc4_drv.h" +#include "vc4_regs.h" +#include "drm_atomic_helper.h" +#include "drm_fb_cma_helper.h" +#include "drm_plane_helper.h" + +struct vc4_plane_state { + struct drm_plane_state base; + u32 *dlist; + u32 dlist_size; /* Number of dwords in allocated for the display list */ + u32 dlist_count; /* Number of used dwords in the display list. */ +}; + +static inline struct vc4_plane_state * +to_vc4_plane_state(struct drm_plane_state *state) +{ + return (struct vc4_plane_state *)state; +} + +static const struct hvs_format { + u32 drm; /* DRM_FORMAT_* */ + u32 hvs; /* HVS_FORMAT_* */ + u32 pixel_order; + bool has_alpha; +} hvs_formats[] = { + { + .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, + .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false, + }, + { + .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888, + .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true, + }, +}; + +static const struct hvs_format * +vc4_get_hvs_format(u32 drm_format) +{ + unsigned i; + + for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { + if (hvs_formats[i].drm == drm_format) + return &hvs_formats[i]; + } + + return NULL; +} + +static bool plane_enabled(struct drm_plane_state *state) +{ + return state->fb && state->crtc; +} + +struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane) +{ + struct vc4_plane_state *vc4_state; + + if (WARN_ON(!plane->state)) + return NULL; + + vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL); + if (!vc4_state) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base); + + if (vc4_state->dlist) { + vc4_state->dlist = kmemdup(vc4_state->dlist, + vc4_state->dlist_count * 4, + GFP_KERNEL); + if (!vc4_state->dlist) { + kfree(vc4_state); + return NULL; + } + vc4_state->dlist_size = vc4_state->dlist_count; + } + + return &vc4_state->base; +} + +void vc4_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + + kfree(vc4_state->dlist); + __drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base); + kfree(state); +} + +/* Called during init to allocate the plane's atomic state. */ +void vc4_plane_reset(struct drm_plane *plane) +{ + struct vc4_plane_state *vc4_state; + + WARN_ON(plane->state); + + vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL); + if (!vc4_state) + return; + + plane->state = &vc4_state->base; + vc4_state->base.plane = plane; +} + +static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) +{ + if (vc4_state->dlist_count == vc4_state->dlist_size) { + u32 new_size = max(4u, vc4_state->dlist_count * 2); + u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL); + if (!new_dlist) + return; + memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4); + + kfree(vc4_state->dlist); + vc4_state->dlist = new_dlist; + vc4_state->dlist_size = new_size; + } + + vc4_state->dlist[vc4_state->dlist_count++] = val; +} + +/* + * Writes out a full display list for an active plane to the plane's + * private dlist state. + */ +static int +vc4_plane_mode_set(struct drm_plane *plane, struct drm_plane_state *state) +{ + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); + u32 ctl0_offset = vc4_state->dlist_count; + const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format); + + vc4_dlist_write(vc4_state, + SCALER_CTL0_VALID | + (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | + (format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | + SCALER_CTL0_UNITY); + + /* Position Word 0: Image Positions and Alpha Value */ + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) | + VC4_SET_FIELD(state->crtc_x, SCALER_POS0_START_X) | + VC4_SET_FIELD(state->crtc_y, SCALER_POS0_START_Y)); + + /* + * Position Word 1: Scaled Image Dimensions. + * Skipped due to SCALER_CTL0_UNITY scaling. + */ + + /* Position Word 2: Source Image Size, Alpha Mode */ + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(format->has_alpha ? + SCALER_POS2_ALPHA_MODE_PIPELINE : + SCALER_POS2_ALPHA_MODE_FIXED, + SCALER_POS2_ALPHA_MODE) | + VC4_SET_FIELD(state->crtc_w, SCALER_POS2_WIDTH) | + VC4_SET_FIELD(state->crtc_h, SCALER_POS2_HEIGHT)); + + /* Position Word 3: Context. Written by the HVS. */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); + + /* Pointer Word 0: RGB / Y Pointer */ + vc4_dlist_write(vc4_state, bo->paddr + fb->offsets[0]); + + /* Pointer Context Word 0: Written by the HVS */ + vc4_dlist_write(vc4_state, 0xc0c0c0c0); + + /* Pitch word 0: Pointer 0 Pitch */ + vc4_dlist_write(vc4_state, + VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH)); + + vc4_state->dlist[ctl0_offset] |= + VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE); + + return 0; +} + +/* + * If a modeset involves changing the setup of a plane, the atomic + * infrastructure will call this to validate a proposed plane setup. + * However, if a plane isn't getting updated, this (and the + * corresponding vc4_plane_atomic_update) won't get called. Thus, we + * compute the dlist here and have all active plane dlists get updated + * in the CRTC's flush. + */ +static int vc4_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + + vc4_state->dlist_count = 0; + + if (plane_enabled(state)) { + return vc4_plane_mode_set(plane, state); + } else { + return 0; + } +} + +static void vc4_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + /* No contents here. Since we don't know where in the CRTC's + * dlist we should be stored, our dlist is uploaded to the + * hardware with vc4_plane_write_dlist() at CRTC atomic_flush + * time. + */ +} + +u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist) +{ + struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); + int i; + + /* Can't memcpy_toio() because it needs to be 32-bit writes. */ + for (i = 0; i < vc4_state->dlist_count; i++) + writel(vc4_state->dlist[i], &dlist[i]); + + return vc4_state->dlist_count; +} + +u32 vc4_plane_dlist_size(struct drm_plane_state *state) +{ + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + + return vc4_state->dlist_count; +} + +static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { + .prepare_fb = NULL, + .cleanup_fb = NULL, + .atomic_check = vc4_plane_atomic_check, + .atomic_update = vc4_plane_atomic_update, +}; + +static void vc4_plane_destroy(struct drm_plane *plane) +{ + drm_plane_helper_disable(plane); + drm_plane_cleanup(plane); +} + +static const struct drm_plane_funcs vc4_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = vc4_plane_destroy, + .set_property = NULL, + .reset = vc4_plane_reset, + .atomic_duplicate_state = vc4_plane_duplicate_state, + .atomic_destroy_state = vc4_plane_destroy_state, +}; + +struct drm_plane *vc4_plane_init(struct drm_device *dev, + enum drm_plane_type type) +{ + struct drm_plane *plane = NULL; + struct vc4_plane *vc4_plane; + u32 formats[ARRAY_SIZE(hvs_formats)]; + int ret = 0; + unsigned i; + + vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane), + GFP_KERNEL); + if (!vc4_plane) { + ret = -ENOMEM; + goto fail; + } + + for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) + formats[i] = hvs_formats[i].drm; + plane = &vc4_plane->base; + ret = drm_universal_plane_init(dev, plane, 0xff, + &vc4_plane_funcs, + formats, ARRAY_SIZE(formats), + type); + + drm_plane_helper_add(plane, &vc4_plane_helper_funcs); + + return plane; +fail: + if (plane) + vc4_plane_destroy(plane); + + return ERR_PTR(ret); +} diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h new file mode 100644 index 000000000000..dea65180ccad --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -0,0 +1,376 @@ +/* + * Copyright © 2014-2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define VC4_MASK(high, low) (((1 << ((high) - (low) + 1)) - 1) << (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_GET_FIELD(word, field) (((word) & field ## _MASK) >> field ## _SHIFT) + +#define V3D_IDENT0 0x00000 +# define V3D_EXPECTED_IDENT0 \ + ((2 << 24) | \ + ('V' << 0) | \ + ('3' << 8) | \ + ('D' << 16)) + +#define V3D_IDENT1 0x00004 +/* Multiples of 1kb */ +# define V3D_IDENT1_VPM_SIZE_MASK VC4_MASK(31, 28) +# define V3D_IDENT1_VPM_SIZE_SHIFT 28 +# define V3D_IDENT1_NSEM_MASK VC4_MASK(23, 16) +# define V3D_IDENT1_NSEM_SHIFT 16 +# define V3D_IDENT1_TUPS_MASK VC4_MASK(15, 12) +# define V3D_IDENT1_TUPS_SHIFT 12 +# define V3D_IDENT1_QUPS_MASK VC4_MASK(11, 8) +# define V3D_IDENT1_QUPS_SHIFT 8 +# define V3D_IDENT1_NSLC_MASK VC4_MASK(7, 4) +# define V3D_IDENT1_NSLC_SHIFT 4 +# define V3D_IDENT1_REV_MASK VC4_MASK(3, 0) +# define V3D_IDENT1_REV_SHIFT 0 + +#define V3D_IDENT2 0x00008 +#define V3D_SCRATCH 0x00010 +#define V3D_L2CACTL 0x00020 +# define V3D_L2CACTL_L2CCLR (1 << 2) +# define V3D_L2CACTL_L2CDIS (1 << 1) +# define V3D_L2CACTL_L2CENA (1 << 0) + +#define V3D_SLCACTL 0x00024 +# define V3D_SLCACTL_T1CC_MASK VC4_MASK(27, 24) +# define V3D_SLCACTL_T1CC_SHIFT 24 +# define V3D_SLCACTL_T0CC_MASK VC4_MASK(19, 16) +# define V3D_SLCACTL_T0CC_SHIFT 16 +# define V3D_SLCACTL_UCC_MASK VC4_MASK(11, 8) +# define V3D_SLCACTL_UCC_SHIFT 8 +# define V3D_SLCACTL_ICC_MASK VC4_MASK(3, 0) +# define V3D_SLCACTL_ICC_SHIFT 0 + +#define V3D_INTCTL 0x00030 +#define V3D_INTENA 0x00034 +#define V3D_INTDIS 0x00038 +# define V3D_INT_SPILLUSE (1 << 3) +# define V3D_INT_OUTOMEM (1 << 2) +# define V3D_INT_FLDONE (1 << 1) +# define V3D_INT_FRDONE (1 << 0) + +#define V3D_CT0CS 0x00100 +#define V3D_CT1CS 0x00104 +#define V3D_CTNCS(n) (V3D_CT0CS + 4 * n) +# define V3D_CTRSTA (1 << 15) +# define V3D_CTSEMA (1 << 12) +# define V3D_CTRTSD (1 << 8) +# define V3D_CTRUN (1 << 5) +# define V3D_CTSUBS (1 << 4) +# define V3D_CTERR (1 << 3) +# define V3D_CTMODE (1 << 0) + +#define V3D_CT0EA 0x00108 +#define V3D_CT1EA 0x0010c +#define V3D_CTNEA(n) (V3D_CT0EA + 4 * (n)) +#define V3D_CT0CA 0x00110 +#define V3D_CT1CA 0x00114 +#define V3D_CTNCA(n) (V3D_CT0CA + 4 * (n)) +#define V3D_CT00RA0 0x00118 +#define V3D_CT01RA0 0x0011c +#define V3D_CTNRA0(n) (V3D_CT00RA0 + 4 * (n)) +#define V3D_CT0LC 0x00120 +#define V3D_CT1LC 0x00124 +#define V3D_CTNLC(n) (V3D_CT0LC + 4 * (n)) +#define V3D_CT0PC 0x00128 +#define V3D_CT1PC 0x0012c +#define V3D_CTNPC(n) (V3D_CT0PC + 4 * (n)) + +#define V3D_PCS 0x00130 +# define V3D_BMOOM (1 << 8) +# define V3D_RMBUSY (1 << 3) +# define V3D_RMACTIVE (1 << 2) +# define V3D_BMBUSY (1 << 1) +# define V3D_BMACTIVE (1 << 0) + +#define V3D_BFC 0x00134 +#define V3D_RFC 0x00138 +#define V3D_BPCA 0x00300 +#define V3D_BPCS 0x00304 +#define V3D_BPOA 0x00308 +#define V3D_BPOS 0x0030c +#define V3D_BXCF 0x00310 +#define V3D_SQRSV0 0x00410 +#define V3D_SQRSV1 0x00414 +#define V3D_SQCNTL 0x00418 +#define V3D_SRQPC 0x00430 +#define V3D_SRQUA 0x00434 +#define V3D_SRQUL 0x00438 +#define V3D_SRQCS 0x0043c +#define V3D_VPACNTL 0x00500 +#define V3D_VPMBASE 0x00504 +#define V3D_PCTRC 0x00670 +#define V3D_PCTRE 0x00674 +#define V3D_PCTR0 0x00680 +#define V3D_PCTRS0 0x00684 +#define V3D_PCTR1 0x00688 +#define V3D_PCTRS1 0x0068c +#define V3D_PCTR2 0x00690 +#define V3D_PCTRS2 0x00694 +#define V3D_PCTR3 0x00698 +#define V3D_PCTRS3 0x0069c +#define V3D_PCTR4 0x006a0 +#define V3D_PCTRS4 0x006a4 +#define V3D_PCTR5 0x006a8 +#define V3D_PCTRS5 0x006ac +#define V3D_PCTR6 0x006b0 +#define V3D_PCTRS6 0x006b4 +#define V3D_PCTR7 0x006b8 +#define V3D_PCTRS7 0x006bc +#define V3D_PCTR8 0x006c0 +#define V3D_PCTRS8 0x006c4 +#define V3D_PCTR9 0x006c8 +#define V3D_PCTRS9 0x006cc +#define V3D_PCTR10 0x006d0 +#define V3D_PCTRS10 0x006d4 +#define V3D_PCTR11 0x006d8 +#define V3D_PCTRS11 0x006dc +#define V3D_PCTR12 0x006e0 +#define V3D_PCTRS12 0x006e4 +#define V3D_PCTR13 0x006e8 +#define V3D_PCTRS13 0x006ec +#define V3D_PCTR14 0x006f0 +#define V3D_PCTRS14 0x006f4 +#define V3D_PCTR15 0x006f8 +#define V3D_PCTRS15 0x006fc +#define V3D_BGE 0x00f00 +#define V3D_FDBGO 0x00f04 +#define V3D_FDBGB 0x00f08 +#define V3D_FDBGR 0x00f0c +#define V3D_FDBGS 0x00f10 +#define V3D_ERRSTAT 0x00f20 + +/* XXX: mask widths */ +#define PV_CONTROL 0x00 +# define PV_CONTROL_CLK_MUX_EN (1 << 24) +# define PV_CONTROL_FORMAT_MASK VC4_MASK(23, 21) +# define PV_CONTROL_FORMAT_SHIFT 21 +# define PV_CONTROL_FORMAT_24 0 +# define PV_CONTROL_FORMAT_DSIV_16 1 +# define PV_CONTROL_FORMAT_DSIC_16 2 +# define PV_CONTROL_FORMAT_DSIV_18 3 +# define PV_CONTROL_FORMAT_DSIV_24 4 + +# define PV_CONTROL_FIFO_LEVEL_MASK VC4_MASK(20, 15) +# define PV_CONTROL_FIFO_LEVEL_SHIFT 15 +# define PV_CONTROL_CLR_AT_START (1 << 14) +# define PV_CONTROL_TRIGGER_UNDERFLOW (1 << 13) +# define PV_CONTROL_WAIT_HSTART (1 << 12) +# define PV_CONTROL_CLK_SELECT_MASK VC4_MASK(3, 2) +# define PV_CONTROL_CLK_SELECT_SHIFT 2 +# define PV_CONTROL_FIFO_CLR (1 << 1) +# define PV_CONTROL_EN (1 << 0) + +#define PV_V_CONTROL 0x04 +# define PV_VCONTROL_CONTINUOUS (1 << 1) +# define PV_VCONTROL_VIDEN (1 << 0) + +#define PV_VSYNCD 0x08 + +#define PV_HORZA 0x0c +# define PV_HORZA_HBP_MASK VC4_MASK(31, 16) +# define PV_HORZA_HBP_SHIFT 16 +# define PV_HORZA_HSYNC_MASK VC4_MASK(15, 0) +# define PV_HORZA_HSYNC_SHIFT 0 + +#define PV_HORZB 0x10 +# define PV_HORZB_HFP_MASK VC4_MASK(31, 16) +# define PV_HORZB_HFP_SHIFT 16 +# define PV_HORZB_HACTIVE_MASK VC4_MASK(15, 0) +# define PV_HORZB_HACTIVE_SHIFT 0 + +#define PV_VERTA 0x14 +# define PV_VERTA_VBP_MASK VC4_MASK(31, 16) +# define PV_VERTA_VBP_SHIFT 16 +# define PV_VERTA_VSYNC_MASK VC4_MASK(15, 0) +# define PV_VERTA_VSYNC_SHIFT 0 + +#define PV_VERTB 0x18 +# define PV_VERTB_VFP_MASK VC4_MASK(31, 16) +# define PV_VERTB_VFP_SHIFT 16 +# define PV_VERTB_VACTIVE_MASK VC4_MASK(15, 0) +# define PV_VERTB_VACTIVE_SHIFT 0 + +#define PV_VERTA_EVEN 0x1c +#define PV_VERTB_EVEN 0x20 + +#define PV_INTEN 0x24 +#define PV_INTSTAT 0x28 +# define PV_INT_VID_IDLE (1 << 9) +# define PV_INT_VFP_END (1 << 8) +# define PV_INT_VFP_START (1 << 7) +# define PV_INT_VACT_START (1 << 6) +# define PV_INT_VBP_START (1 << 5) +# define PV_INT_VSYNC_START (1 << 4) +# define PV_INT_HFP_START (1 << 3) +# define PV_INT_HACT_START (1 << 2) +# define PV_INT_HBP_START (1 << 1) +# define PV_INT_HSYNC_START (1 << 0) + +#define PV_STAT 0x2c +# define PV_STAT_IDLE (1 << 8) +# define PV_STAT_RUNNING_MASK VC4_MASK(7, 0) + +#define PV_DSI_HACT 0x30 + +#define SCALER_DISPCTRL 0x00000000 +#define SCALER_DISPSTAT 0x00000004 +#define SCALER_DISPID 0x00000008 +#define SCALER_DISPECTRL 0x0000000c +#define SCALER_DISPPROF 0x00000010 +#define SCALER_DISPDITHER 0x00000014 +#define SCALER_DISPEOLN 0x00000018 +#define SCALER_DISPLIST0 0x00000020 +#define SCALER_DISPLIST1 0x00000024 +#define SCALER_DISPLIST2 0x00000028 +#define SCALER_DISPLSTAT 0x0000002c +#define SCALER_DISPLACT0 0x00000030 +#define SCALER_DISPLACT1 0x00000034 +#define SCALER_DISPLACT2 0x00000038 +#define SCALER_DISPCTRL0 0x00000040 +#define SCALER_DISPBKGND0 0x00000044 +#define SCALER_DISPSTAT0 0x00000048 +#define SCALER_DISPBASE0 0x0000004c +#define SCALER_DISPCTRL1 0x00000050 +#define SCALER_DISPBKGND1 0x00000054 +#define SCALER_DISPSTAT1 0x00000058 +#define SCALER_DISPBASE1 0x0000005c +#define SCALER_DISPCTRL2 0x00000060 +#define SCALER_DISPBKGND2 0x00000064 +#define SCALER_DISPSTAT2 0x00000068 +#define SCALER_DISPBASE2 0x0000006c +#define SCALER_DISPALPHA2 0x00000070 +#define SCALER_GAMADDR 0x00000078 +#define SCALER_GAMDATA 0x000000e0 +#define SCALER_DLIST_START 0x00002000 +#define SCALER_DLIST_SIZE 0x00004000 + +#define VC4_HDMI_CORE_REV 0x00 +#define VC4_HDMI_SW_RESET_CONTROL 0x04 +#define VC4_HDMI_HOTPLUG_INT 0x08 + +#define VC4_HDMI_HOTPLUG 0x0c +# define VC4_HDMI_HOTPLUG_CONNECTED (1 << 0) + +#define VC4_HDMI_HORZA 0xc4 +# define VC4_HDMI_HORZA_VPOS (1 << 14) +# define VC4_HDMI_HORZA_HPOS (1 << 13) + +#define VC4_HDMI_HORZB 0xc8 +# define VC4_HDMI_HORZB_HBP_MASK VC4_MASK(29, 20) +# define VC4_HDMI_HORZB_HBP_SHIFT 20 +# define VC4_HDMI_HORZB_HSP_MASK VC4_MASK(19, 10) +# define VC4_HDMI_HORZB_HSP_SHIFT 10 + +#define VC4_HDMI_FIFO_CTL 0x5c +# define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N (1 << 0) + +#define VC4_HDMI_VERTA0 0xcc +#define VC4_HDMI_VERTA1 0xd4 +# define VC4_HDMI_VERTA_VSP_MASK VC4_MASK(24, 20) +# define VC4_HDMI_VERTA_VSP_SHIFT 20 +# define VC4_HDMI_VERTA_VFP_MASK VC4_MASK(19, 13) +# define VC4_HDMI_VERTA_VFP_SHIFT 13 +# define VC4_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0) +# define VC4_HDMI_VERTA_VAL_SHIFT 0 + +#define VC4_HDMI_VERTB0 0xd0 +#define VC4_HDMI_VERTB1 0xd8 +# define VC4_HDMI_VERTB_VSPO_MASK VC4_MASK(21, 9) +# define VC4_HDMI_VERTB_VSPO_SHIFT 9 +# define VC4_HDMI_VERTB_VBP_MASK VC4_MASK(8, 0) +# define VC4_HDMI_VERTB_VBP_SHIFT 0 + +/* HVS display list information. */ +#define HVS_BOOTLOADER_DLIST_END 32 + +enum hvs_pixel_format { + /* 8bpp */ + HVS_PIXEL_FORMAT_RGB332 = 0, + /* 16bpp */ + HVS_PIXEL_FORMAT_RGBA4444 = 1, + HVS_PIXEL_FORMAT_RGB555 = 2, + HVS_PIXEL_FORMAT_RGBA5551 = 3, + HVS_PIXEL_FORMAT_RGB565 = 4, + /* 24bpp */ + HVS_PIXEL_FORMAT_RGB888 = 5, + HVS_PIXEL_FORMAT_RGBA6666 = 6, + /* 32bpp */ + HVS_PIXEL_FORMAT_RGBA8888 = 7 +}; + +/* Note: the LSB is the rightmost character shown. Only valid for + * HVS_PIXEL_FORMAT_RGB8888, not RGB888. + */ +#define HVS_PIXEL_ORDER_RGBA 0 +#define HVS_PIXEL_ORDER_BGRA 1 +#define HVS_PIXEL_ORDER_ARGB 2 +#define HVS_PIXEL_ORDER_ABGR 3 + +#define HVS_PIXEL_ORDER_XBRG 0 +#define HVS_PIXEL_ORDER_XRBG 1 +#define HVS_PIXEL_ORDER_XRGB 2 +#define HVS_PIXEL_ORDER_XBGR 3 + +#define HVS_PIXEL_ORDER_XYCBCR 0 +#define HVS_PIXEL_ORDER_XYCRCB 1 +#define HVS_PIXEL_ORDER_YXCBCR 2 +#define HVS_PIXEL_ORDER_YXCRCB 3 + +#define SCALER_CTL0_END (1 << 31) +#define SCALER_CTL0_VALID (1 << 30) + +#define SCALER_CTL0_SIZE_MASK VC4_MASK(29, 24) +#define SCALER_CTL0_SIZE_SHIFT 24 + +#define SCALER_CTL0_HFLIP (1 << 16) +#define SCALER_CTL0_VFLIP (1 << 15) + +#define SCALER_CTL0_ORDER_MASK VC4_MASK(14, 13) +#define SCALER_CTL0_ORDER_SHIFT 13 + +/* Set to indicate no scaling. */ +#define SCALER_CTL0_UNITY (1 << 4) + +#define SCALER_CTL0_PIXEL_FORMAT_MASK VC4_MASK(3, 0) +#define SCALER_CTL0_PIXEL_FORMAT_SHIFT 0 + +#define SCALER_POS0_FIXED_ALPHA_MASK VC4_MASK(31, 24) +#define SCALER_POS0_FIXED_ALPHA_SHIFT 24 + +#define SCALER_POS0_START_Y_MASK VC4_MASK(23, 12) +#define SCALER_POS0_START_Y_SHIFT 12 + +#define SCALER_POS0_START_X_MASK VC4_MASK(11, 0) +#define SCALER_POS0_START_X_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 +#define SCALER_POS2_ALPHA_MODE_FIXED 1 +#define SCALER_POS2_ALPHA_MODE_FIXED_NONZERO 2 +#define SCALER_POS2_ALPHA_MODE_FIXED_OVER_0x07 3 + +#define SCALER_POS2_HEIGHT_MASK VC4_MASK(27, 16) +#define SCALER_POS2_HEIGHT_SHIFT 16 + +#define SCALER_POS2_WIDTH_MASK VC4_MASK(11, 0) +#define SCALER_POS2_WIDTH_SHIFT 0 + +#define SCALER_SRC_PITCH_MASK VC4_MASK(15, 0) +#define SCALER_SRC_PITCH_SHIFT 0 diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c new file mode 100644 index 000000000000..4bfd894ca88c --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2014 The Linux Foundation. All rights reserved. + * Copyright (C) 2013 Red Hat + * Author: Rob Clark + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "linux/component.h" +#include "soc/bcm2835/raspberrypi-firmware-property.h" +#include "vc4_drv.h" +#include "vc4_regs.h" + +#ifdef CONFIG_DEBUG_FS +#define REGDEF(reg) { reg, #reg } +static const struct { + uint32_t reg; + const char *name; +} vc4_reg_defs[] = { + REGDEF(V3D_IDENT0), + REGDEF(V3D_IDENT1), + REGDEF(V3D_IDENT2), + REGDEF(V3D_SCRATCH), + REGDEF(V3D_L2CACTL), + REGDEF(V3D_SLCACTL), + REGDEF(V3D_INTCTL), + REGDEF(V3D_INTENA), + REGDEF(V3D_INTDIS), + REGDEF(V3D_CT0CS), + REGDEF(V3D_CT1CS), + REGDEF(V3D_CT0EA), + REGDEF(V3D_CT1EA), + REGDEF(V3D_CT0CA), + REGDEF(V3D_CT1CA), + REGDEF(V3D_CT00RA0), + REGDEF(V3D_CT01RA0), + REGDEF(V3D_CT0LC), + REGDEF(V3D_CT1LC), + REGDEF(V3D_CT0PC), + REGDEF(V3D_CT1PC), + REGDEF(V3D_PCS), + REGDEF(V3D_BFC), + REGDEF(V3D_RFC), + REGDEF(V3D_BPCA), + REGDEF(V3D_BPCS), + REGDEF(V3D_BPOA), + REGDEF(V3D_BPOS), + REGDEF(V3D_BXCF), + REGDEF(V3D_SQRSV0), + REGDEF(V3D_SQRSV1), + REGDEF(V3D_SQCNTL), + REGDEF(V3D_SRQPC), + REGDEF(V3D_SRQUA), + REGDEF(V3D_SRQUL), + REGDEF(V3D_SRQCS), + REGDEF(V3D_VPACNTL), + REGDEF(V3D_VPMBASE), + REGDEF(V3D_PCTRC), + REGDEF(V3D_PCTRE), + REGDEF(V3D_PCTR0), + REGDEF(V3D_PCTRS0), + REGDEF(V3D_PCTR1), + REGDEF(V3D_PCTRS1), + REGDEF(V3D_PCTR2), + REGDEF(V3D_PCTRS2), + REGDEF(V3D_PCTR3), + REGDEF(V3D_PCTRS3), + REGDEF(V3D_PCTR4), + REGDEF(V3D_PCTRS4), + REGDEF(V3D_PCTR5), + REGDEF(V3D_PCTRS5), + REGDEF(V3D_PCTR6), + REGDEF(V3D_PCTRS6), + REGDEF(V3D_PCTR7), + REGDEF(V3D_PCTRS7), + REGDEF(V3D_PCTR8), + REGDEF(V3D_PCTRS8), + REGDEF(V3D_PCTR9), + REGDEF(V3D_PCTRS9), + REGDEF(V3D_PCTR10), + REGDEF(V3D_PCTRS10), + REGDEF(V3D_PCTR11), + REGDEF(V3D_PCTRS11), + REGDEF(V3D_PCTR12), + REGDEF(V3D_PCTRS12), + REGDEF(V3D_PCTR13), + REGDEF(V3D_PCTRS13), + REGDEF(V3D_PCTR14), + REGDEF(V3D_PCTRS14), + REGDEF(V3D_PCTR15), + REGDEF(V3D_PCTRS15), + REGDEF(V3D_BGE), + REGDEF(V3D_FDBGO), + REGDEF(V3D_FDBGB), + REGDEF(V3D_FDBGR), + REGDEF(V3D_FDBGS), + REGDEF(V3D_ERRSTAT), +}; + +int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(vc4_reg_defs); i++) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + vc4_reg_defs[i].name, vc4_reg_defs[i].reg, + V3D_READ(vc4_reg_defs[i].reg)); + } + + return 0; +} + +int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t ident1 = V3D_READ(V3D_IDENT1); + uint32_t nslc = VC4_GET_FIELD(ident1, V3D_IDENT1_NSLC); + uint32_t tups = VC4_GET_FIELD(ident1, V3D_IDENT1_TUPS); + uint32_t qups = VC4_GET_FIELD(ident1, V3D_IDENT1_QUPS); + + seq_printf(m, "Revision: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_REV)); + seq_printf(m, "Slices: %d\n", nslc); + seq_printf(m, "TMUs: %d\n", nslc * tups); + seq_printf(m, "QPUs: %d\n", nslc * qups); + seq_printf(m, "Semaphores: %d\n", VC4_GET_FIELD(ident1, V3D_IDENT1_NSEM)); + + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +/* + * Asks the firmware to turn on power to the V3D engine. + * + * This may be doable with just the clocks interface, though this + * packet does some other register setup from the firmware, too. + */ +int +vc4_v3d_set_power(struct vc4_dev *vc4, bool on) +{ + u32 packet = on; + + return rpi_firmware_property(vc4->firmware_node, + RPI_FIRMWARE_SET_ENABLE_QPU, + &packet, sizeof(packet)); +} + +static void vc4_v3d_init_hw(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + /* Take all the memory that would have been reserved for user + * QPU programs, since we don't have an interface for running + * them, anyway. + */ + V3D_WRITE(V3D_VPMBASE, 0); +} + +static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_v3d *v3d = NULL; + int ret; + + v3d = devm_kzalloc(&pdev->dev, sizeof(*v3d), GFP_KERNEL); + if (!v3d) + return -ENOMEM; + + v3d->pdev = pdev; + + v3d->regs = vc4_ioremap_regs(pdev); + if (IS_ERR(v3d->regs)) + return PTR_ERR(v3d->regs); + + vc4->v3d = v3d; + + ret = vc4_v3d_set_power(vc4, true); + if (ret) + return ret; + + if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { + DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", + V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0); + return -EINVAL; + } + + vc4_v3d_init_hw(drm); + + return 0; +} + +static void vc4_v3d_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + + vc4->v3d = NULL; +} + +static const struct component_ops vc4_v3d_ops = { + .bind = vc4_v3d_bind, + .unbind = vc4_v3d_unbind, +}; + +static int vc4_v3d_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_v3d_ops); +} + +static int vc4_v3d_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_v3d_ops); + return 0; +} + +static const struct of_device_id vc4_v3d_dt_match[] = { + { .compatible = "brcm,vc4-v3d" }, + {} +}; + +static struct platform_driver vc4_v3d_driver = { + .probe = vc4_v3d_dev_probe, + .remove = vc4_v3d_dev_remove, + .driver = { + .name = "vc4_v3d", + .of_match_table = vc4_v3d_dt_match, + }, +}; + +void __init vc4_v3d_register(void) +{ + platform_driver_register(&vc4_v3d_driver); +} + +void __exit vc4_v3d_unregister(void) +{ + platform_driver_unregister(&vc4_v3d_driver); +} -- cgit v1.2.1 From e39ef2ad50a3826d14e26ee0a8fe224d244c1680 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sat, 14 Mar 2015 15:24:43 -0700 Subject: ARM: bcm2835: Add HDMI's I2C bus for DDC to the device tree. Signed-off-by: Eric Anholt --- arch/arm/boot/dts/bcm2835.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index a64d5924e2c2..679f82e389c4 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -149,6 +149,15 @@ status = "disabled"; }; + i2c2: i2c@7e805000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e805000 0x1000>; + interrupts = <2 21>; + clocks = <&clk_i2c>; + #address-cells = <1>; + #size-cells = <0>; + }; + usb@7e980000 { compatible = "brcm,bcm2835-usb"; reg = <0x7e980000 0x10000>; @@ -169,6 +178,7 @@ compatible = "brcm,vc4-hdmi"; reg = <0x7e902000 0x600>; interrupts = <2 8>, <2 9>; + ddc = <&i2c2>; crtc = <&pv2>; }; -- cgit v1.2.1 From f8de7ad08786772e539203f1581aa7eb5ea2c73f Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sat, 14 Mar 2015 15:30:40 -0700 Subject: drm/vc4: Add support for DDC on the HDMI connector. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_hdmi.c | 56 +++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 6774db8124a9..81cd414b88f2 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -27,6 +27,7 @@ /* General HDMI hardware state. */ struct vc4_hdmi { struct platform_device *pdev; + struct i2c_adapter *ddc; void __iomem *regs; }; @@ -118,43 +119,16 @@ static void vc4_hdmi_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } -static const u8 edid_1920_1080[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, - 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78, - 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25, - 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, - 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, - 0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e, - 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b, - 0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20, - 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, - 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46, - 0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05, -}; - -static int vc4_get_fixed_edid_block(void *data, u8 *buf, unsigned int block, - size_t len) -{ - if (block != 0) - return -EINVAL; - if (len > sizeof(edid_1920_1080)) - return -EINVAL; - memcpy(buf, edid_1920_1080, len); - return 0; -} - - static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) { + struct drm_device *dev = connector->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); int ret = 0; + struct edid *edid; - struct edid *edid = drm_do_get_edid(connector, vc4_get_fixed_edid_block, - NULL); + edid = drm_get_edid(connector, vc4->hdmi->ddc); + if (!edid) + return -ENODEV; drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); @@ -381,6 +355,7 @@ static int vc4_hdmi_bind(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 *hdmi; + struct device_node *ddc_node; hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) @@ -391,6 +366,19 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (IS_ERR(hdmi->regs)) return PTR_ERR(hdmi->regs); + /* DDC i2c driver */ + ddc_node = of_parse_phandle(dev->of_node, "ddc", 0); + if (!ddc_node) { + DRM_ERROR("Failed to find ddc node in device tree\n"); + return -ENODEV; + } + + hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node); + if (!hdmi->ddc) { + DRM_ERROR("Failed to get ddc i2c adapter by node\n"); + return -EPROBE_DEFER; + } + vc4->hdmi = hdmi; return 0; @@ -402,6 +390,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = drm->dev_private; + put_device(&vc4->hdmi->ddc->dev); + vc4->hdmi = NULL; } -- cgit v1.2.1 From b1e15a47717bde5bb064ee84b92842c0a298c5ea Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sat, 14 Mar 2015 18:12:28 -0700 Subject: ARM: BCM2835: Add the GPIO for HDMI hotplug detect to the device tree. Signed-off-by: Eric Anholt --- arch/arm/boot/dts/bcm2835.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 679f82e389c4..6caaa1fa4db3 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -1,4 +1,5 @@ #include +#include #include "skeleton.dtsi" / { @@ -179,6 +180,7 @@ reg = <0x7e902000 0x600>; interrupts = <2 8>, <2 9>; ddc = <&i2c2>; + hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>; crtc = <&pv2>; }; -- cgit v1.2.1 From 897a7c0d8c56c2c8da903384ce63878095b381ef Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sat, 14 Mar 2015 16:33:31 -0700 Subject: drm/vc4: Return actual HDMI connector connection status. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_hdmi.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 81cd414b88f2..7d47cfc12640 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -20,6 +20,7 @@ #include "drm_crtc_helper.h" #include "drm_edid.h" #include "linux/component.h" +#include "linux/of_gpio.h" #include "linux/of_platform.h" #include "vc4_drv.h" #include "vc4_regs.h" @@ -29,6 +30,7 @@ struct vc4_hdmi { struct platform_device *pdev; struct i2c_adapter *ddc; void __iomem *regs; + int hpd_gpio; }; /* VC4 HDMI encoder KMS struct */ @@ -110,7 +112,20 @@ static void vc4_hdmi_dump_regs(struct drm_device *dev) static enum drm_connector_status vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) { - return connector_status_connected; + struct drm_device *dev = connector->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + + if (vc4->hdmi->hpd_gpio) { + if (gpio_get_value(vc4->hdmi->hpd_gpio)) + return connector_status_connected; + else + return connector_status_disconnected; + } + + if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED) + return connector_status_connected; + else + return connector_status_disconnected; } static void vc4_hdmi_connector_destroy(struct drm_connector *connector) @@ -356,6 +371,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) struct vc4_dev *vc4 = drm->dev_private; struct vc4_hdmi *hdmi; struct device_node *ddc_node; + u32 value; hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) @@ -379,6 +395,15 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) return -EPROBE_DEFER; } + /* Only use the GPIO HPD pin if present in the DT, otherwise + * we'll use the HDMI core's register. + */ + if (of_find_property(dev->of_node, "hpd-gpio", &value)) { + hdmi->hpd_gpio = of_get_named_gpio(dev->of_node, "hpd-gpio", 0); + if (hdmi->hpd_gpio < 0) + return hdmi->hpd_gpio; + } + vc4->hdmi = hdmi; return 0; -- cgit v1.2.1 From e93326f124c3d096048a9228c1bf4fb650a3985f Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 31 Mar 2015 00:00:24 -0700 Subject: drm/vc4: Add some actual HDMI modesetting bits. This isn't nearly enough, but it's a work in progress. Note that the HD_VID_CTL write has 0x0 as a before value (even though my HDMI was on), and the write doesn't seem to be fully landing (just the top bit sticks), which is concerning. Signed-off-by: Eric Anholt --- arch/arm/boot/dts/bcm2835.dtsi | 3 +- drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- drivers/gpu/drm/vc4/vc4_drv.c | 4 +- drivers/gpu/drm/vc4/vc4_drv.h | 4 +- drivers/gpu/drm/vc4/vc4_hdmi.c | 196 +++++++++++++++++++++++++++++++---------- drivers/gpu/drm/vc4/vc4_hvs.c | 2 +- drivers/gpu/drm/vc4/vc4_regs.h | 52 ++++++++--- drivers/gpu/drm/vc4/vc4_v3d.c | 2 +- 8 files changed, 200 insertions(+), 65 deletions(-) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 6caaa1fa4db3..318eb9b82769 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -177,7 +177,8 @@ hdmi: brcm,vc4-hdmi@7e902000 { compatible = "brcm,vc4-hdmi"; - reg = <0x7e902000 0x600>; + reg = <0x7e902000 0x600>, + <0x7e808000 0x100>; interrupts = <2 8>, <2 9>; ddc = <&i2c2>; hpd-gpio = <&gpio 46 GPIO_ACTIVE_HIGH>; diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 0c33dd1f7468..5c12e2472dc5 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -323,7 +323,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) return -ENOMEM; crtc = &vc4_crtc->base; - vc4_crtc->regs = vc4_ioremap_regs(pdev); + vc4_crtc->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(vc4_crtc->regs)) return PTR_ERR(vc4_crtc->regs); diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 94dcc8aa48de..3fa362d19a78 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -30,12 +30,12 @@ * We assume only one register range per device, so we use index 0. */ void __iomem * -vc4_ioremap_regs(struct platform_device *dev) +vc4_ioremap_regs(struct platform_device *dev, int index) { struct resource *res; void __iomem *map; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); + res = platform_get_resource(dev, IORESOURCE_MEM, index); map = devm_ioremap_resource(&dev->dev, res); if (IS_ERR(map)) { int ret = PTR_ERR(map); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index aa143717a71b..3e0240f103c3 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -82,8 +82,6 @@ to_vc4_plane(struct drm_plane *plane) #define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset) #define HVS_READ(offset) readl(vc4->hvs->regs + offset) #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) -#define HDMI_READ(offset) readl(vc4->hdmi->regs + offset) -#define HDMI_WRITE(offset, val) writel(val, vc4->hdmi->regs + offset) /* vc4_bo.c */ void vc4_free_object(struct drm_gem_object *gem_obj); @@ -105,7 +103,7 @@ int vc4_debugfs_init(struct drm_minor *minor); void vc4_debugfs_cleanup(struct drm_minor *minor); /* vc4_drv.c */ -void __iomem *vc4_ioremap_regs(struct platform_device *dev); +void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); /* vc4_hdmi.c */ void vc4_hdmi_register(void); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 7d47cfc12640..85e6a91a4d33 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -22,6 +22,7 @@ #include "linux/component.h" #include "linux/of_gpio.h" #include "linux/of_platform.h" +#include "soc/bcm2835/raspberrypi-firmware-property.h" #include "vc4_drv.h" #include "vc4_regs.h" @@ -29,13 +30,19 @@ struct vc4_hdmi { struct platform_device *pdev; struct i2c_adapter *ddc; - void __iomem *regs; + void __iomem *hdmicore_regs; + void __iomem *hd_regs; int hpd_gpio; }; +#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 drm_encoder base; + bool hdmi_monitor; }; static inline struct vc4_hdmi_encoder * to_vc4_hdmi_encoder(struct drm_encoder *encoder) @@ -69,13 +76,21 @@ static const struct { HDMI_REG(VC4_HDMI_SW_RESET_CONTROL), HDMI_REG(VC4_HDMI_HOTPLUG_INT), HDMI_REG(VC4_HDMI_HOTPLUG), - HDMI_REG(VC4_HDMI_FIFO_CTL), HDMI_REG(VC4_HDMI_HORZA), HDMI_REG(VC4_HDMI_HORZB), + HDMI_REG(VC4_HDMI_FIFO_CTL), + HDMI_REG(VC4_HDMI_SCHEDULER_CONTROL), HDMI_REG(VC4_HDMI_VERTA0), HDMI_REG(VC4_HDMI_VERTA1), HDMI_REG(VC4_HDMI_VERTB0), HDMI_REG(VC4_HDMI_VERTB1), + HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL), +}; +static const struct { + u32 reg; + const char *name; +} hd_regs[] = { + HDMI_REG(VC4_HD_VID_CTL), }; #ifdef CONFIG_DEBUG_FS @@ -92,6 +107,12 @@ int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) HDMI_READ(hdmi_regs[i].reg)); } + for (i = 0; i < ARRAY_SIZE(hd_regs); i++) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + hd_regs[i].name, hd_regs[i].reg, + HD_READ(hd_regs[i].reg)); + } + return 0; } #endif /* CONFIG_DEBUG_FS */ @@ -107,6 +128,11 @@ static void vc4_hdmi_dump_regs(struct drm_device *dev) hdmi_regs[i].reg, hdmi_regs[i].name, HDMI_READ(hdmi_regs[i].reg)); } + for (i = 0; i < ARRAY_SIZE(hd_regs); i++) { + DRM_INFO("0x%04x (%s): 0x%08x\n", + hd_regs[i].reg, hd_regs[i].name, + HDMI_READ(hd_regs[i].reg)); + } } static enum drm_connector_status @@ -136,6 +162,10 @@ static void vc4_hdmi_connector_destroy(struct drm_connector *connector) 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); int ret = 0; @@ -145,6 +175,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) if (!edid) return -ENODEV; + vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid); drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); @@ -231,57 +262,128 @@ static bool vc4_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, return true; } +/* + * Asks the firmware to set the pixel clock. + * + * This should probably be in a separate firmware clock provider + * driver. + */ +static void +vc4_set_pixel_clock(struct vc4_dev *vc4, u32 clock) +{ + u32 packet[2]; + int ret; + + packet[0] = 9; /* Pixel clock. */ + packet[1] = clock; + + ret = rpi_firmware_property(vc4->firmware_node, + RPI_FIRMWARE_SET_CLOCK_RATE, + &packet, sizeof(packet)); + + if (ret) + DRM_ERROR("Failed to set pixel clock: %d\n", ret); +} + static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) + struct drm_display_mode *unadjusted_mode, + struct drm_display_mode *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); bool debug_dump_regs = false; - - mode = adjusted_mode; + bool hsync_pos = !(mode->flags & DRM_MODE_FLAG_NHSYNC); + bool vsync_pos = !(mode->flags & DRM_MODE_FLAG_NVSYNC); + u32 vactive = (mode->vdisplay >> + ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0)); + u32 verta = (VC4_SET_FIELD(mode->vsync_end - mode->vsync_start, + VC4_HDMI_VERTA_VSP) | + VC4_SET_FIELD(mode->vsync_start - mode->vdisplay, + VC4_HDMI_VERTA_VFP) | + VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL)); + u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) | + VC4_SET_FIELD(mode->vtotal - mode->vsync_end, + VC4_HDMI_VERTB_VBP)); if (debug_dump_regs) { DRM_INFO("HDMI regs before:\n"); vc4_hdmi_dump_regs(dev); } - if (0) { - bool hsync_pos = !(mode->flags & DRM_MODE_FLAG_NHSYNC); - bool vsync_pos = !(mode->flags & DRM_MODE_FLAG_NVSYNC); - u32 vactive = (mode->vdisplay >> - (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0); - u32 verta = (VC4_SET_FIELD(mode->vsync_start, - VC4_HDMI_VERTA_VFP) | - VC4_SET_FIELD(mode->vsync_end - mode->vsync_start, - VC4_HDMI_VERTA_VSP) | - VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL)); - - HDMI_WRITE(VC4_HDMI_HORZA, - (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) | - (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0)); - HDMI_WRITE(VC4_HDMI_HORZB, - VC4_SET_FIELD(mode->htotal - mode->hdisplay, - VC4_HDMI_HORZB_HBP) | - VC4_SET_FIELD(mode->hsync_end - mode->hsync_start, - VC4_HDMI_HORZB_HSP) | - 0 /* XXX: HFP? */); - HDMI_WRITE(VC4_HDMI_VERTA0, verta); - HDMI_WRITE(VC4_HDMI_VERTA1, verta); - HDMI_WRITE(VC4_HDMI_VERTB0, - VC4_SET_FIELD(mode->vsync_start, - VC4_HDMI_VERTB_VSPO) | - VC4_SET_FIELD(mode->vtotal - mode->vdisplay, - VC4_HDMI_VERTB_VBP)); - HDMI_WRITE(VC4_HDMI_VERTB1, - VC4_SET_FIELD(mode->vsync_start, - VC4_HDMI_VERTB_VSPO) | - VC4_SET_FIELD(mode->vtotal - mode->vsync_end, - VC4_HDMI_VERTB_VBP)); - - /* XXX: HD VID CTL */ - HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); - /* XXX: HD CSC CTL */ + HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); + + /* XXX: Pixel valve must be stopped. */ + HD_WRITE(VC4_HD_VID_CTL, 0); + /* XXX: Set state machine clock. */ + + if (0) /* XXX: Kills the screen. */ + vc4_set_pixel_clock(vc4, mode->clock * 1000); + + HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, + HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | + VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT | + VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS); + + HDMI_WRITE(VC4_HDMI_HORZA, + (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) | + (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) | + VC4_SET_FIELD(mode->hdisplay, VC4_HDMI_HORZA_HAP)); + + HDMI_WRITE(VC4_HDMI_HORZB, + VC4_SET_FIELD(mode->htotal - mode->hsync_end, + VC4_HDMI_HORZB_HBP) | + VC4_SET_FIELD(mode->hsync_end - mode->hsync_start, + VC4_HDMI_HORZB_HSP) | + VC4_SET_FIELD(mode->hsync_start - mode->hdisplay, + VC4_HDMI_HORZB_HFP)); + + HDMI_WRITE(VC4_HDMI_VERTA0, verta); + HDMI_WRITE(VC4_HDMI_VERTA1, verta); + + HDMI_WRITE(VC4_HDMI_VERTB0, vertb); + HDMI_WRITE(VC4_HDMI_VERTB1, vertb); + + /* XXX: HD VID CTL set up sync polarities */ + HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); + /* XXX: HD CSC CTL = 0x20 */ + + /* XXX: Wait for video. */ + + /* XXX: Enable pixel valve. */ + HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0); + HD_WRITE(VC4_HD_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) | + VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); + } else { + HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, + HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & + ~(VC4_HDMI_RAM_PACKET_ENABLE)); + HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, + HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & + ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); + } + + /* Wait for set pending done. */ + + if (vc4_encoder->hdmi_monitor) { + WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & + VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)); + HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, + HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | + VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT); + + /* XXX: Set up HDMI_RAM_PACKET_CONFIG (1 << 16) and + * set up infoframe. + */ + + /* XXX: Set up drift fifo? */ } if (debug_dump_regs) { @@ -378,9 +480,13 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) return -ENOMEM; hdmi->pdev = pdev; - hdmi->regs = vc4_ioremap_regs(pdev); - if (IS_ERR(hdmi->regs)) - return PTR_ERR(hdmi->regs); + 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); /* DDC i2c driver */ ddc_node = of_parse_phandle(dev->of_node, "ddc", 0); diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index f50a67910fb4..4ba37162f39c 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -99,7 +99,7 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) hvs->pdev = pdev; - hvs->regs = vc4_ioremap_regs(pdev); + hvs->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(hvs->regs)) return PTR_ERR(hvs->regs); diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index dea65180ccad..b40610743a2c 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -260,42 +260,72 @@ #define SCALER_DLIST_START 0x00002000 #define SCALER_DLIST_SIZE 0x00004000 -#define VC4_HDMI_CORE_REV 0x00 -#define VC4_HDMI_SW_RESET_CONTROL 0x04 -#define VC4_HDMI_HOTPLUG_INT 0x08 +#define VC4_HDMI_CORE_REV 0x000 +#define VC4_HDMI_SW_RESET_CONTROL 0x004 +#define VC4_HDMI_HOTPLUG_INT 0x008 -#define VC4_HDMI_HOTPLUG 0x0c +#define VC4_HDMI_HOTPLUG 0x00c # define VC4_HDMI_HOTPLUG_CONNECTED (1 << 0) -#define VC4_HDMI_HORZA 0xc4 +#define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0 +# define VC4_HDMI_RAM_PACKET_ENABLE (1 << 16) + +#define VC4_HDMI_HORZA 0x0c4 # define VC4_HDMI_HORZA_VPOS (1 << 14) # define VC4_HDMI_HORZA_HPOS (1 << 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 0xc8 +#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 +/* Horizontal sync pulse (hsync_end - hsync_start). */ # define VC4_HDMI_HORZB_HSP_MASK VC4_MASK(19, 10) # define VC4_HDMI_HORZB_HSP_SHIFT 10 +/* Horizontal front porch (hsync_start - hdisplay). */ +# define VC4_HDMI_HORZB_HFP_MASK VC4_MASK(9, 0) +# define VC4_HDMI_HORZB_HFP_SHIFT 0 -#define VC4_HDMI_FIFO_CTL 0x5c +#define VC4_HDMI_FIFO_CTL 0x05c # define VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N (1 << 0) -#define VC4_HDMI_VERTA0 0xcc -#define VC4_HDMI_VERTA1 0xd4 +#define VC4_HDMI_SCHEDULER_CONTROL 0x0c0 +# define VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT (1 << 15) +# define VC4_HDMI_SCHEDULER_CONTROL_IGNORE_VSYNC_PREDICTS (1 << 5) +# define VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT (1 << 3) +# define VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE (1 << 1) +# define VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI (1 << 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 +/* Vertical front porch (vsync_start - vdisplay). */ # define VC4_HDMI_VERTA_VFP_MASK VC4_MASK(19, 13) # define VC4_HDMI_VERTA_VFP_SHIFT 13 +/* Vertical active lines (vdisplay). */ # define VC4_HDMI_VERTA_VAL_MASK VC4_MASK(12, 0) # define VC4_HDMI_VERTA_VAL_SHIFT 0 -#define VC4_HDMI_VERTB0 0xd0 -#define VC4_HDMI_VERTB1 0xd8 +#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 +/* Vertical pack porch (vtotal - vsync_end). */ # define VC4_HDMI_VERTB_VBP_MASK VC4_MASK(8, 0) # define VC4_HDMI_VERTB_VBP_SHIFT 0 +#define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 + +#define VC4_HD_VID_CTL 0x038 +# define VC4_HD_VID_CTL_ENABLE (1 << 31) +# define VC4_HD_VID_CTL_UNDERFLOW_ENABLE (1 << 30) +# define VC4_HD_VID_CTL_FRAME_COUNTER_RESET (1 << 29) + /* HVS display list information. */ #define HVS_BOOTLOADER_DLIST_END 32 diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index 4bfd894ca88c..5a18cf41558a 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -184,7 +184,7 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) v3d->pdev = pdev; - v3d->regs = vc4_ioremap_regs(pdev); + v3d->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(v3d->regs)) return PTR_ERR(v3d->regs); -- cgit v1.2.1 From 6f1b5605f3d0b6325c69c2be288a93c5d5c294dc Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 29 Jan 2015 18:10:47 +0000 Subject: ARM: bcm2835: dt: Add vendor prefix for Raspberry Pi Since the prefix is already in use, we need to add it in the vendor list. Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Stefan Wahren Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 80339192c93e..3fc90ac4f801 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -159,6 +159,7 @@ radxa Radxa raidsonic RaidSonic Technology GmbH ralink Mediatek/Ralink Technology Corp. ramtron Ramtron International +raspberrypi Raspberry Pi Foundation realtek Realtek Semiconductor Corp. renesas Renesas Electronics Corporation ricoh Ricoh Co. Ltd. -- cgit v1.2.1 From 4c059b0b2b59b16c1d053b781056b7e934a36121 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 29 Jan 2015 18:10:48 +0000 Subject: ARM: bcm2835: dt: Add root properties for Raspberry Pi This patch adds root compatible properties for the following boards: - Raspberry Pi Model A - Raspberry Pi Model A+ - Raspberry Pi Model B - Raspberry Pi Model B (no P5) - Raspberry Pi Model B rev2 - Raspberry Pi Model B+ - Raspberry Pi Compute Module Reviewed-by: Stephen Warren Acked-by: Rob Herring Signed-off-by: Stefan Wahren Signed-off-by: Lee Jones --- .../devicetree/bindings/arm/bcm/brcm,bcm2835.txt | 31 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt index ac683480c486..c78576bb7729 100644 --- a/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt +++ b/Documentation/devicetree/bindings/arm/bcm/brcm,bcm2835.txt @@ -1,8 +1,35 @@ Broadcom BCM2835 device tree bindings ------------------------------------------- -Boards with the BCM2835 SoC shall have the following properties: +Raspberry Pi Model A +Required root node properties: +compatible = "raspberrypi,model-a", "brcm,bcm2835"; -Required root node property: +Raspberry Pi Model A+ +Required root node properties: +compatible = "raspberrypi,model-a-plus", "brcm,bcm2835"; +Raspberry Pi Model B +Required root node properties: +compatible = "raspberrypi,model-b", "brcm,bcm2835"; + +Raspberry Pi Model B (no P5) +early model B with I2C0 rather than I2C1 routed to the expansion header +Required root node properties: +compatible = "raspberrypi,model-b-i2c0", "brcm,bcm2835"; + +Raspberry Pi Model B rev2 +Required root node properties: +compatible = "raspberrypi,model-b-rev2", "brcm,bcm2835"; + +Raspberry Pi Model B+ +Required root node properties: +compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; + +Raspberry Pi Compute Module +Required root node properties: +compatible = "raspberrypi,compute-module", "brcm,bcm2835"; + +Generic BCM2835 board +Required root node properties: compatible = "brcm,bcm2835"; -- cgit v1.2.1 From 62c69d7635d6f12377897429f98baf504a6d000f Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 29 Jan 2015 18:10:49 +0000 Subject: ARM: bcm2835: dt: Add header file for pinctrl constants This new header file defines pincontrol constants to use from bcm2835 DTS files for pincontrol properties option. Reviewed-by: Stephen Warren Signed-off-by: Stefan Wahren Signed-off-by: Lee Jones --- include/dt-bindings/pinctrl/bcm2835.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 include/dt-bindings/pinctrl/bcm2835.h diff --git a/include/dt-bindings/pinctrl/bcm2835.h b/include/dt-bindings/pinctrl/bcm2835.h new file mode 100644 index 000000000000..6f0bc37af39c --- /dev/null +++ b/include/dt-bindings/pinctrl/bcm2835.h @@ -0,0 +1,27 @@ +/* + * Header providing constants for bcm2835 pinctrl bindings. + * + * Copyright (C) 2015 Stefan Wahren + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __DT_BINDINGS_PINCTRL_BCM2835_H__ +#define __DT_BINDINGS_PINCTRL_BCM2835_H__ + +/* brcm,function property */ +#define BCM2835_FSEL_GPIO_IN 0 +#define BCM2835_FSEL_GPIO_OUT 1 +#define BCM2835_FSEL_ALT5 2 +#define BCM2835_FSEL_ALT4 3 +#define BCM2835_FSEL_ALT0 4 +#define BCM2835_FSEL_ALT1 5 +#define BCM2835_FSEL_ALT2 6 +#define BCM2835_FSEL_ALT3 7 + +#endif /* __DT_BINDINGS_PINCTRL_BCM2835_H__ */ -- cgit v1.2.1 From 120911120911e0f84eea4fd7d1e9d6c4c65f1586 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Thu, 29 Jan 2015 18:10:50 +0000 Subject: ARM: bcm2835: dt: Use pinctrl header This patch converts all bcm2835 dts and dtsi files to use the pinctrl header file. Reviewed-by: Stephen Warren Signed-off-by: Stefan Wahren Signed-off-by: Lee Jones --- arch/arm/boot/dts/bcm2835-rpi-b-plus.dts | 4 ++-- arch/arm/boot/dts/bcm2835-rpi-b.dts | 4 ++-- arch/arm/boot/dts/bcm2835-rpi.dtsi | 8 ++++---- arch/arm/boot/dts/bcm2835.dtsi | 3 ++- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts index e479515099c3..668442b1bda5 100644 --- a/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts +++ b/arch/arm/boot/dts/bcm2835-rpi-b-plus.dts @@ -1,5 +1,5 @@ /dts-v1/; -/include/ "bcm2835-rpi.dtsi" +#include "bcm2835-rpi.dtsi" / { compatible = "raspberrypi,model-b-plus", "brcm,bcm2835"; @@ -25,6 +25,6 @@ /* I2S interface */ i2s_alt0: i2s_alt0 { brcm,pins = <18 19 20 21>; - brcm,function = <4>; /* alt0 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835-rpi-b.dts b/arch/arm/boot/dts/bcm2835-rpi-b.dts index bafa46fc226a..ee89b79426cf 100644 --- a/arch/arm/boot/dts/bcm2835-rpi-b.dts +++ b/arch/arm/boot/dts/bcm2835-rpi-b.dts @@ -1,5 +1,5 @@ /dts-v1/; -/include/ "bcm2835-rpi.dtsi" +#include "bcm2835-rpi.dtsi" / { compatible = "raspberrypi,model-b", "brcm,bcm2835"; @@ -18,6 +18,6 @@ /* I2S interface */ i2s_alt2: i2s_alt2 { brcm,pins = <28 29 30 31>; - brcm,function = <6>; /* alt2 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi index c7064487017d..46780bb48bbf 100644 --- a/arch/arm/boot/dts/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi @@ -1,4 +1,4 @@ -/include/ "bcm2835.dtsi" +#include "bcm2835.dtsi" / { memory { @@ -21,17 +21,17 @@ gpioout: gpioout { brcm,pins = <6>; - brcm,function = <1>; /* GPIO out */ + brcm,function = ; }; alt0: alt0 { brcm,pins = <0 1 2 3 4 5 7 8 9 10 11 14 15 40 45>; - brcm,function = <4>; /* alt0 */ + brcm,function = ; }; alt3: alt3 { brcm,pins = <48 49 50 51 52 53>; - brcm,function = <7>; /* alt3 */ + brcm,function = ; }; }; diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 3342cb1407bc..be9c91439bcf 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -1,4 +1,5 @@ -/include/ "skeleton.dtsi" +#include +#include "skeleton.dtsi" / { compatible = "brcm,bcm2835"; -- cgit v1.2.1 From 64146f20eb05bb193060ba50dddacaf0d7a48f83 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 18 Mar 2015 11:00:22 +0200 Subject: ARM: bcm2835: dt: Fix i2c0 node name Device tree node names should contain the node's reg property address value. The i2c0 node was apparently forgotten in commit 25b2f1bd0b7e0 (ARM: bcm2835: node name unit address cleanup). Acked-by: Stephen Warren Signed-off-by: Baruch Siach Signed-off-by: Lee Jones --- arch/arm/boot/dts/bcm2835.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index be9c91439bcf..4d4c1294c082 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -113,7 +113,7 @@ status = "disabled"; }; - i2c0: i2c@20205000 { + i2c0: i2c@7e205000 { compatible = "brcm,bcm2835-i2c"; reg = <0x7e205000 0x1000>; interrupts = <2 21>; -- cgit v1.2.1 From 05b682b7a3b28dfd27f810cbb509f7211c208b1f Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 5 May 2015 13:27:46 -0700 Subject: ARM: bcm2835: dt: Add the mailbox to the device tree Signed-off-by: Eric Anholt Acked-by: Lee Jones Acked-by: Stephen Warren Signed-off-by: Lee Jones --- arch/arm/boot/dts/bcm2835.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 4d4c1294c082..4ff1b83191a6 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -61,6 +61,13 @@ reg = <0x7e104000 0x10>; }; + mailbox: mailbox@7e00b800 { + compatible = "brcm,bcm2835-mbox"; + reg = <0x7e00b880 0x40>; + interrupts = <0 1>; + #mbox-cells = <0>; + }; + gpio: gpio@7e200000 { compatible = "brcm,bcm2835-gpio"; reg = <0x7e200000 0xb4>; -- cgit v1.2.1 From 1215baa7a391713dd1d015c209bd87c0a5d89db6 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 5 May 2015 13:10:11 -0700 Subject: ARM: bcm2835: dt: Use 0x4 prefix for DMA bus addresses to SDRAM. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There exists a tiny MMU, configurable only by the VC (running the closed firmware), which maps from the ARM's physical addresses to bus addresses. These bus addresses determine the caching behavior in the VC's L1/L2 (note: separate from the ARM's L1/L2) according to the top 2 bits. The bits in the bus address mean: From the VideoCore processor: 0x0... L1 and L2 cache allocating and coherent 0x4... L1 non-allocating, but coherent. L2 allocating and coherent 0x8... L1 non-allocating, but coherent. L2 non-allocating, but coherent 0xc... SDRAM alias. Cache is bypassed. Not L1 or L2 allocating or coherent From the GPU peripherals (note: all peripherals bypass the L1 cache. The ARM will see this view once through the VC MMU): 0x0... Do not use 0x4... L1 non-allocating, and incoherent. L2 allocating and coherent. 0x8... L1 non-allocating, and incoherent. L2 non-allocating, but coherent 0xc... SDRAM alias. Cache is bypassed. Not L1 or L2 allocating or coherent The 2835 firmware always configures the MMU to turn ARM physical addresses with 0x0 top bits to 0x4, meaning present in L2 but incoherent with L1. However, any bus addresses we were generating in the kernel to be passed to a device had 0x0 bits. That would be a reserved (possibly totally incoherent) value if sent to a GPU peripheral like USB, or L1 allocating if sent to the VC (like a firmware property request). By setting dma-ranges, all of the devices below it get a dev->dma_pfn_offset, so that dma_alloc_coherent() and friends return addresses with 0x4 bits and avoid cache incoherency. This matches the behavior in the downstream 2708 kernel (see BUS_OFFSET in arch/arm/mach-bcm2708/include/mach/memory.h). Signed-off-by: Eric Anholt Tested-by: Noralf Trønnes Acked-by: Stephen Warren Signed-off-by: Lee Jones --- arch/arm/boot/dts/bcm2835.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi index 4ff1b83191a6..301c73f4ca33 100644 --- a/arch/arm/boot/dts/bcm2835.dtsi +++ b/arch/arm/boot/dts/bcm2835.dtsi @@ -15,6 +15,7 @@ #address-cells = <1>; #size-cells = <1>; ranges = <0x7e000000 0x20000000 0x02000000>; + dma-ranges = <0x40000000 0x00000000 0x20000000>; timer@7e003000 { compatible = "brcm,bcm2835-system-timer"; -- cgit v1.2.1 From 2383321183cf6c1f0e6dcb435c751ef5ebe285a0 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 23 Apr 2015 10:49:11 -0700 Subject: ARM: bcm2835: Skip doing our own iotable_init() initialization The only thing we were using this 16MB mapping of IO peripherals for was the uart's early debug mapping. If we just drop the map_io hook, the kernel will call debug_ll_io_init() for us, which maps the single page needed for the device. Signed-off-by: Eric Anholt Tested-by: Stephen Warren Acked-by: Stephen Warren Signed-off-by: Lee Jones --- arch/arm/mach-bcm/board_bcm2835.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 70f2f3925f0e..9851ee8e5e87 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -31,10 +31,6 @@ #define PM_RSTC_WRCFG_FULL_RESET 0x00000020 #define PM_RSTS_HADWRH_SET 0x00000040 -#define BCM2835_PERIPH_PHYS 0x20000000 -#define BCM2835_PERIPH_VIRT 0xf0000000 -#define BCM2835_PERIPH_SIZE SZ_16M - static void __iomem *wdt_regs; /* @@ -93,18 +89,6 @@ static void bcm2835_power_off(void) bcm2835_restart(REBOOT_HARD, ""); } -static struct map_desc io_map __initdata = { - .virtual = BCM2835_PERIPH_VIRT, - .pfn = __phys_to_pfn(BCM2835_PERIPH_PHYS), - .length = BCM2835_PERIPH_SIZE, - .type = MT_DEVICE -}; - -static void __init bcm2835_map_io(void) -{ - iotable_init(&io_map, 1); -} - static void __init bcm2835_init(void) { int ret; @@ -129,7 +113,6 @@ static const char * const bcm2835_compat[] = { }; DT_MACHINE_START(BCM2835, "BCM2835") - .map_io = bcm2835_map_io, .init_irq = irqchip_init, .init_machine = bcm2835_init, .restart = bcm2835_restart, -- cgit v1.2.1 From ba9acf9c0f66e09b6947d96d517083736e1c60d0 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 24 Apr 2015 12:08:53 -0700 Subject: ARM: bcm2835: Drop the init_irq() hook This is the default function that gets called if the hook is NULL. Signed-off-by: Eric Anholt Acked-by: Stephen Warren Tested-by: Stephen Warren Signed-off-by: Lee Jones --- arch/arm/mach-bcm/board_bcm2835.c | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 9851ee8e5e87..49dd5b05f702 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -113,7 +113,6 @@ static const char * const bcm2835_compat[] = { }; DT_MACHINE_START(BCM2835, "BCM2835") - .init_irq = irqchip_init, .init_machine = bcm2835_init, .restart = bcm2835_restart, .dt_compat = bcm2835_compat -- cgit v1.2.1 From 33a9f5bc1547b563b072b8ac1c453d861aeef0b8 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 24 Apr 2015 12:08:54 -0700 Subject: ARM: bcm2835: Move the restart/power_off handling to the WDT driver Since the WDT is what's used to drive restart and power off, it makes more sense to keep it there, where the regs are already mapped and definitions for them provided. Note that this means you may need to add CONFIG_BCM2835_WDT to retain functionality of your kernel. Signed-off-by: Eric Anholt Acked-by: Guenter Roeck Signed-off-by: Lee Jones --- arch/arm/mach-bcm/board_bcm2835.c | 73 --------------------------------------- drivers/watchdog/bcm2835_wdt.c | 62 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 73 deletions(-) diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 49dd5b05f702..0f7b9eac3d15 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -12,7 +12,6 @@ * GNU General Public License for more details. */ -#include #include #include #include @@ -22,81 +21,10 @@ #include #include -#define PM_RSTC 0x1c -#define PM_RSTS 0x20 -#define PM_WDOG 0x24 - -#define PM_PASSWORD 0x5a000000 -#define PM_RSTC_WRCFG_MASK 0x00000030 -#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 -#define PM_RSTS_HADWRH_SET 0x00000040 - -static void __iomem *wdt_regs; - -/* - * The machine restart method can be called from an atomic context so we won't - * be able to ioremap the regs then. - */ -static void bcm2835_setup_restart(void) -{ - struct device_node *np = of_find_compatible_node(NULL, NULL, - "brcm,bcm2835-pm-wdt"); - if (WARN(!np, "unable to setup watchdog restart")) - return; - - wdt_regs = of_iomap(np, 0); - WARN(!wdt_regs, "failed to remap watchdog regs"); -} - -static void bcm2835_restart(enum reboot_mode mode, const char *cmd) -{ - u32 val; - - if (!wdt_regs) - return; - - /* use a timeout of 10 ticks (~150us) */ - writel_relaxed(10 | PM_PASSWORD, wdt_regs + PM_WDOG); - val = readl_relaxed(wdt_regs + PM_RSTC); - val &= ~PM_RSTC_WRCFG_MASK; - val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; - writel_relaxed(val, wdt_regs + PM_RSTC); - - /* No sleeping, possibly atomic. */ - mdelay(1); -} - -/* - * We can't really power off, but if we do the normal reset scheme, and - * indicate to bootcode.bin not to reboot, then most of the chip will be - * powered off. - */ -static void bcm2835_power_off(void) -{ - u32 val; - - /* - * We set the watchdog hard reset bit here to distinguish this reset - * from the normal (full) reset. bootcode.bin will not reboot after a - * hard reset. - */ - val = readl_relaxed(wdt_regs + PM_RSTS); - val &= ~PM_RSTC_WRCFG_MASK; - val |= PM_PASSWORD | PM_RSTS_HADWRH_SET; - writel_relaxed(val, wdt_regs + PM_RSTS); - - /* Continue with normal reset mechanism */ - bcm2835_restart(REBOOT_HARD, ""); -} - static void __init bcm2835_init(void) { int ret; - bcm2835_setup_restart(); - if (wdt_regs) - pm_power_off = bcm2835_power_off; - bcm2835_init_clocks(); ret = of_platform_populate(NULL, of_default_bus_match_table, NULL, @@ -114,6 +42,5 @@ static const char * const bcm2835_compat[] = { DT_MACHINE_START(BCM2835, "BCM2835") .init_machine = bcm2835_init, - .restart = bcm2835_restart, .dt_compat = bcm2835_compat MACHINE_END diff --git a/drivers/watchdog/bcm2835_wdt.c b/drivers/watchdog/bcm2835_wdt.c index 2b5a9bbf80b7..7116968dee12 100644 --- a/drivers/watchdog/bcm2835_wdt.c +++ b/drivers/watchdog/bcm2835_wdt.c @@ -13,20 +13,25 @@ * option) any later version. */ +#include +#include #include #include #include #include #include #include +#include #define PM_RSTC 0x1c +#define PM_RSTS 0x20 #define PM_WDOG 0x24 #define PM_PASSWORD 0x5a000000 #define PM_WDOG_TIME_SET 0x000fffff #define PM_RSTC_WRCFG_CLR 0xffffffcf +#define PM_RSTS_HADWRH_SET 0x00000040 #define PM_RSTC_WRCFG_SET 0x00000030 #define PM_RSTC_WRCFG_FULL_RESET 0x00000020 #define PM_RSTC_RESET 0x00000102 @@ -37,6 +42,7 @@ struct bcm2835_wdt { void __iomem *base; spinlock_t lock; + struct notifier_block restart_handler; }; static unsigned int heartbeat; @@ -106,6 +112,53 @@ static struct watchdog_device bcm2835_wdt_wdd = { .timeout = WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET), }; +static int +bcm2835_restart(struct notifier_block *this, unsigned long mode, void *cmd) +{ + struct bcm2835_wdt *wdt = container_of(this, struct bcm2835_wdt, + restart_handler); + u32 val; + + /* use a timeout of 10 ticks (~150us) */ + writel_relaxed(10 | PM_PASSWORD, wdt->base + PM_WDOG); + val = readl_relaxed(wdt->base + PM_RSTC); + val &= PM_RSTC_WRCFG_CLR; + val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET; + writel_relaxed(val, wdt->base + PM_RSTC); + + /* No sleeping, possibly atomic. */ + mdelay(1); + + return 0; +} + +/* + * We can't really power off, but if we do the normal reset scheme, and + * indicate to bootcode.bin not to reboot, then most of the chip will be + * powered off. + */ +static void bcm2835_power_off(void) +{ + struct device_node *np = + of_find_compatible_node(NULL, NULL, "brcm,bcm2835-pm-wdt"); + struct platform_device *pdev = of_find_device_by_node(np); + struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); + u32 val; + + /* + * We set the watchdog hard reset bit here to distinguish this reset + * from the normal (full) reset. bootcode.bin will not reboot after a + * hard reset. + */ + val = readl_relaxed(wdt->base + PM_RSTS); + val &= PM_RSTC_WRCFG_CLR; + val |= PM_PASSWORD | PM_RSTS_HADWRH_SET; + writel_relaxed(val, wdt->base + PM_RSTS); + + /* Continue with normal reset mechanism */ + bcm2835_restart(&wdt->restart_handler, REBOOT_HARD, NULL); +} + static int bcm2835_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -136,6 +189,12 @@ static int bcm2835_wdt_probe(struct platform_device *pdev) return err; } + wdt->restart_handler.notifier_call = bcm2835_restart; + wdt->restart_handler.priority = 128; + register_restart_handler(&wdt->restart_handler); + if (pm_power_off == NULL) + pm_power_off = bcm2835_power_off; + dev_info(dev, "Broadcom BCM2835 watchdog timer"); return 0; } @@ -144,6 +203,9 @@ static int bcm2835_wdt_remove(struct platform_device *pdev) { struct bcm2835_wdt *wdt = platform_get_drvdata(pdev); + unregister_restart_handler(&wdt->restart_handler); + if (pm_power_off == bcm2835_power_off) + pm_power_off = NULL; watchdog_unregister_device(&bcm2835_wdt_wdd); iounmap(wdt->base); -- cgit v1.2.1 From f32b961b591f6a915481fd28c4ee0d99bc728667 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 26 Feb 2015 09:51:45 +0000 Subject: dt/bindings: Add binding for the Raspberry Pi firmware driver This driver will provide support for calls into the firmware that will be used by other drivers like cpufreq and vc4. v2: Improve commit message, point to mailbox.txt for how mboxes work. v3: Use Lee's suggestion for mailbox phandle docs, fix spelling of "raspberry". --- .../devicetree/bindings/arm/bcm/raspberrypi,firmware.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/bcm/raspberrypi,firmware.txt diff --git a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,firmware.txt b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,firmware.txt new file mode 100644 index 000000000000..dbf60f90c01b --- /dev/null +++ b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,firmware.txt @@ -0,0 +1,14 @@ +Raspberry Pi VideoCore firmware driver + +Required properties: + +- compatible: Should be "rasbperrypi,firmware" +- mboxes: Phandle to the firmware device's Mailbox. + (See: ../mailbox/mailbox.txt for more information) + +Example: + +firmware { + compatible = "raspberrypi,firmware"; + mboxes = <&mailbox>; +}; -- cgit v1.2.1 From f26388a4e63f52f32234ee34a979c6ffa61a3180 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 26 Feb 2015 10:08:06 +0000 Subject: ARM: bcm2835: Add the Raspberry Pi firmware driver This gives us a function for making mailbox property channel requests of the firmware, which is most notable in that it will let us get and set clock rates. v2: Drop power-domains stuff for now since we don't have the driver core support to make it useful. Move to drivers/firmware/. Capitalize the enums. De-global the firmware variable. Use the firmware device to allocate our DMA buffer, so that the dma-ranges DT property gets respected. Simplify the property tag transaction interface even more, leaving a multi-tag interface still available. For conciseness, rename "raspberrypi" to "rpi" on all functions/enums/structs, and the "firmware" variable to "fw". Print when the driver is probed successfully, since debugging -EPROBE_DEFER handling is such a big part of bcm2835 development. Drop -EBUSY mailbox handling since the mailbox core has been fixed to return -EPROBE_DEFER in -next. v3: Use kernel-doc style for big comments (from Noralf), drop stale comment, use "dev" when freeing DMA as well. Signed-off-by: Eric Anholt --- drivers/firmware/Makefile | 1 + drivers/firmware/raspberrypi.c | 230 +++++++++++++++++++++ .../soc/bcm2835/raspberrypi-firmware-property.h | 112 ++++++++++ 3 files changed, 343 insertions(+) create mode 100644 drivers/firmware/raspberrypi.c create mode 100644 include/soc/bcm2835/raspberrypi-firmware-property.h diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 3fdd3912709a..41ced2885b46 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o obj-$(CONFIG_QCOM_SCM) += qcom_scm.o CFLAGS_qcom_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) +obj-$(CONFIG_BCM2835_MBOX) += raspberrypi.o obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-$(CONFIG_EFI) += efi/ diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c new file mode 100644 index 000000000000..d612815fa106 --- /dev/null +++ b/drivers/firmware/raspberrypi.c @@ -0,0 +1,230 @@ +/* + * Copyright © 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Defines interfaces for interacting wtih the Raspberry Pi firmware's + * property channel. + */ + +#include +#include +#include +#include +#include +#include + +#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) +#define MBOX_CHAN(msg) ((msg) & 0xf) +#define MBOX_DATA28(msg) ((msg) & ~0xf) +#define MBOX_CHAN_PROPERTY 8 + +struct rpi_firmware { + struct mbox_client cl; + struct mbox_chan *chan; /* The property channel. */ + struct completion c; + u32 enabled; +}; + +static DEFINE_MUTEX(transaction_lock); + +static void response_callback(struct mbox_client *cl, void *msg) +{ + struct rpi_firmware *fw = container_of(cl, struct rpi_firmware, cl); + complete(&fw->c); +} + +/* + * Sends a request to the firmware through the BCM2835 mailbox driver, + * and synchronously waits for the reply. + */ +static int +rpi_firmware_transaction(struct rpi_firmware *fw, u32 chan, u32 data) +{ + u32 message = MBOX_MSG(chan, data); + int ret; + + WARN_ON(data & 0xf); + + mutex_lock(&transaction_lock); + reinit_completion(&fw->c); + ret = mbox_send_message(fw->chan, &message); + if (ret >= 0) { + wait_for_completion(&fw->c); + ret = 0; + } else { + dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret); + } + mutex_unlock(&transaction_lock); + + return ret; +} + +/** + * rpi_firmware_property_list - Submit firmware property list + * @of_node: Pointer to the firmware Device Tree node. + * @data: Buffer holding tags. + * @tag_size: Size of tags buffer. + * + * Submits a set of concatenated tags to the VPU firmware through the + * mailbox property interface. + * + * The buffer header and the ending tag are added by this function and + * don't need to be supplied, just the actual tags for your operation. + * See struct rpi_firmware_property_tag_header for the per-tag + * structure. + */ +int rpi_firmware_property_list(struct device_node *of_node, + void *data, size_t tag_size) +{ + struct platform_device *pdev = of_find_device_by_node(of_node); + struct rpi_firmware *fw = platform_get_drvdata(pdev); + size_t size = tag_size + 12; + u32 *buf; + dma_addr_t bus_addr; + int ret = 0; + + if (!fw) + return -EPROBE_DEFER; + + /* Packets are processed a dword at a time. */ + if (size & 3) + return -EINVAL; + + buf = dma_alloc_coherent(fw->cl.dev, PAGE_ALIGN(size), &bus_addr, + GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + /* The firmware will error out without parsing in this case. */ + WARN_ON(size >= 1024 * 1024); + + buf[0] = size; + buf[1] = RPI_FIRMWARE_STATUS_REQUEST; + memcpy(&buf[2], data, tag_size); + buf[size / 4 - 1] = RPI_FIRMWARE_PROPERTY_END; + wmb(); + + ret = rpi_firmware_transaction(fw, MBOX_CHAN_PROPERTY, bus_addr); + + rmb(); + memcpy(data, &buf[2], tag_size); + if (ret == 0 && buf[1] != RPI_FIRMWARE_STATUS_SUCCESS) { + /* + * The tag name here might not be the one causing the + * error, if there were multiple tags in the request. + * But single-tag is the most common, so go with it. + */ + dev_err(fw->cl.dev, "Request 0x%08x returned status 0x%08x\n", + buf[2], buf[1]); + ret = -EINVAL; + } + + dma_free_coherent(fw->cl.dev, PAGE_ALIGN(size), buf, bus_addr); + + return ret; +} +EXPORT_SYMBOL_GPL(rpi_firmware_property_list); + +/** + * rpi_firmware_property - Submit single firmware property + * @of_node: Pointer to the firmware Device Tree node. + * @tag: One of enum_mbox_property_tag. + * @tag_data: Tag data buffer. + * @buf_size: Buffer size. + * + * Submits a single tag to the VPU firmware through the mailbox + * property interface. + * + * This is a convenience wrapper around + * rpi_firmware_property_list() to avoid some of the + * boilerplate in property calls. + */ +int rpi_firmware_property(struct device_node *of_node, + u32 tag, void *tag_data, size_t buf_size) +{ + /* Single tags are very small (generally 8 bytes), so the + * stack should be safe. + */ + u8 data[buf_size + sizeof(struct rpi_firmware_property_tag_header)]; + struct rpi_firmware_property_tag_header *header = + (struct rpi_firmware_property_tag_header *)data; + int ret; + + header->tag = tag; + header->buf_size = buf_size; + header->req_resp_size = 0; + memcpy(data + sizeof(struct rpi_firmware_property_tag_header), + tag_data, buf_size); + + ret = rpi_firmware_property_list(of_node, &data, sizeof(data)); + memcpy(tag_data, + data + sizeof(struct rpi_firmware_property_tag_header), + buf_size); + + return ret; +} +EXPORT_SYMBOL_GPL(rpi_firmware_property); + +static int rpi_firmware_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rpi_firmware *fw; + + fw = devm_kzalloc(dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return -ENOMEM; + + fw->cl.dev = dev; + fw->cl.rx_callback = response_callback; + fw->cl.tx_block = true; + + fw->chan = mbox_request_channel(&fw->cl, 0); + if (IS_ERR(fw->chan)) { + int ret = PTR_ERR(fw->chan); + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get mbox channel: %d\n", ret); + return ret; + } + + init_completion(&fw->c); + + dev_info(dev, "Firmware driver\n"); + platform_set_drvdata(pdev, fw); + + return 0; +} + +static int rpi_firmware_remove(struct platform_device *pdev) +{ + struct rpi_firmware *fw = platform_get_drvdata(pdev); + + mbox_free_channel(fw->chan); + + return 0; +} + +static const struct of_device_id rpi_firmware_of_match[] = { + { .compatible = "raspberrypi,firmware", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rpi_firmware_of_match); + +static struct platform_driver rpi_firmware_driver = { + .driver = { + .name = "raspberrypi-firmware", + .owner = THIS_MODULE, + .of_match_table = rpi_firmware_of_match, + }, + .probe = rpi_firmware_probe, + .remove = rpi_firmware_remove, +}; +module_platform_driver(rpi_firmware_driver); + +MODULE_AUTHOR("Eric Anholt "); +MODULE_DESCRIPTION("Raspberry Pi firmware driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/soc/bcm2835/raspberrypi-firmware-property.h b/include/soc/bcm2835/raspberrypi-firmware-property.h new file mode 100644 index 000000000000..b007e924d5dc --- /dev/null +++ b/include/soc/bcm2835/raspberrypi-firmware-property.h @@ -0,0 +1,112 @@ +/* + * Copyright © 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +enum rpi_firmware_property_status { + RPI_FIRMWARE_STATUS_REQUEST = 0, + RPI_FIRMWARE_STATUS_SUCCESS = 0x80000000, + RPI_FIRMWARE_STATUS_ERROR = 0x80000001, +}; + +/** + * struct rpi_firmware_property_tag_header - Firmware property tag header + * @tag: One of enum_mbox_property_tag. + * @buf_size: The number of bytes in the value buffer following this + * struct. + * @req_resp_size: On submit, the length of the request (though it doesn't + * appear to be currently used by the firmware). On return, + * the length of the response (always 4 byte aligned), with + * the low bit set. + */ +struct rpi_firmware_property_tag_header { + u32 tag; + u32 buf_size; + u32 req_resp_size; +}; + +enum rpi_firmware_property_tag { + RPI_FIRMWARE_PROPERTY_END = 0, + RPI_FIRMWARE_GET_FIRMWARE_REVISION = 0x00000001, + + RPI_FIRMWARE_SET_CURSOR_INFO = 0x00008010, + RPI_FIRMWARE_SET_CURSOR_STATE = 0x00008011, + + RPI_FIRMWARE_GET_BOARD_MODEL = 0x00010001, + RPI_FIRMWARE_GET_BOARD_REVISION = 0x00010002, + RPI_FIRMWARE_GET_BOARD_MAC_ADDRESS = 0x00010003, + RPI_FIRMWARE_GET_BOARD_SERIAL = 0x00010004, + RPI_FIRMWARE_GET_ARM_MEMORY = 0x00010005, + RPI_FIRMWARE_GET_VC_MEMORY = 0x00010006, + RPI_FIRMWARE_GET_CLOCKS = 0x00010007, + RPI_FIRMWARE_GET_POWER_STATE = 0x00020001, + RPI_FIRMWARE_GET_TIMING = 0x00020002, + RPI_FIRMWARE_SET_POWER_STATE = 0x00028001, + RPI_FIRMWARE_GET_CLOCK_STATE = 0x00030001, + RPI_FIRMWARE_GET_CLOCK_RATE = 0x00030002, + RPI_FIRMWARE_GET_VOLTAGE = 0x00030003, + RPI_FIRMWARE_GET_MAX_CLOCK_RATE = 0x00030004, + RPI_FIRMWARE_GET_MAX_VOLTAGE = 0x00030005, + RPI_FIRMWARE_GET_TEMPERATURE = 0x00030006, + RPI_FIRMWARE_GET_MIN_CLOCK_RATE = 0x00030007, + RPI_FIRMWARE_GET_MIN_VOLTAGE = 0x00030008, + RPI_FIRMWARE_GET_TURBO = 0x00030009, + RPI_FIRMWARE_GET_MAX_TEMPERATURE = 0x0003000a, + RPI_FIRMWARE_ALLOCATE_MEMORY = 0x0003000c, + RPI_FIRMWARE_LOCK_MEMORY = 0x0003000d, + RPI_FIRMWARE_UNLOCK_MEMORY = 0x0003000e, + RPI_FIRMWARE_RELEASE_MEMORY = 0x0003000f, + RPI_FIRMWARE_EXECUTE_CODE = 0x00030010, + RPI_FIRMWARE_EXECUTE_QPU = 0x00030011, + RPI_FIRMWARE_SET_ENABLE_QPU = 0x00030012, + RPI_FIRMWARE_GET_DISPMANX_RESOURCE_MEM_HANDLE = 0x00030014, + RPI_FIRMWARE_GET_EDID_BLOCK = 0x00030020, + RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001, + RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002, + RPI_FIRMWARE_SET_VOLTAGE = 0x00038003, + RPI_FIRMWARE_SET_TURBO = 0x00038009, + + /* Dispmanx TAGS */ + RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001, + RPI_FIRMWARE_FRAMEBUFFER_BLANK = 0x00040002, + RPI_FIRMWARE_FRAMEBUFFER_GET_PHYSICAL_WIDTH_HEIGHT = 0x00040003, + RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_WIDTH_HEIGHT = 0x00040004, + RPI_FIRMWARE_FRAMEBUFFER_GET_DEPTH = 0x00040005, + RPI_FIRMWARE_FRAMEBUFFER_GET_PIXEL_ORDER = 0x00040006, + RPI_FIRMWARE_FRAMEBUFFER_GET_ALPHA_MODE = 0x00040007, + RPI_FIRMWARE_FRAMEBUFFER_GET_PITCH = 0x00040008, + RPI_FIRMWARE_FRAMEBUFFER_GET_VIRTUAL_OFFSET = 0x00040009, + RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, + RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE = 0x0004000b, + RPI_FIRMWARE_FRAMEBUFFER_RELEASE = 0x00048001, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, + RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004, + RPI_FIRMWARE_FRAMEBUFFER_TEST_DEPTH = 0x00044005, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PIXEL_ORDER = 0x00044006, + RPI_FIRMWARE_FRAMEBUFFER_TEST_ALPHA_MODE = 0x00044007, + RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_OFFSET = 0x00044009, + RPI_FIRMWARE_FRAMEBUFFER_TEST_OVERSCAN = 0x0004400a, + RPI_FIRMWARE_FRAMEBUFFER_TEST_PALETTE = 0x0004400b, + RPI_FIRMWARE_FRAMEBUFFER_SET_PHYSICAL_WIDTH_HEIGHT = 0x00048003, + RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_WIDTH_HEIGHT = 0x00048004, + RPI_FIRMWARE_FRAMEBUFFER_SET_DEPTH = 0x00048005, + RPI_FIRMWARE_FRAMEBUFFER_SET_PIXEL_ORDER = 0x00048006, + RPI_FIRMWARE_FRAMEBUFFER_SET_ALPHA_MODE = 0x00048007, + RPI_FIRMWARE_FRAMEBUFFER_SET_VIRTUAL_OFFSET = 0x00048009, + RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, + RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b, + + RPI_FIRMWARE_GET_COMMAND_LINE = 0x00050001, + RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, +}; + +int rpi_firmware_property(struct device_node *of_node, + u32 tag, void *data, size_t len); +int rpi_firmware_property_list(struct device_node *of_node, + void *data, size_t tag_size); -- cgit v1.2.1 From 3095984d60b6d9b9a9b68fab9b133d19f726a5f2 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 25 Feb 2015 15:18:14 +0000 Subject: ARM: bcm2835: Add the firmware driver information to the RPi DT v2: Drop pm-domains stuff since I've dropped it from the firmware driver for now, until we get drivers/base fixed. Signed-off-by: Eric Anholt Acked-by: Lee Jones (previous version with pm-domains) --- arch/arm/boot/dts/bcm2835-rpi.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi index 46780bb48bbf..ace33f69c5aa 100644 --- a/arch/arm/boot/dts/bcm2835-rpi.dtsi +++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi @@ -14,6 +14,13 @@ linux,default-trigger = "heartbeat"; }; }; + + soc { + firmware: firmware { + compatible = "raspberrypi,firmware"; + mboxes = <&mailbox>; + }; + }; }; &gpio { -- cgit v1.2.1 From 7c41123fe4ad935e292a7dedfd6bc2b557fbfdfe Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 18 May 2015 18:44:17 -0700 Subject: drm/vc4: Hook up the HDMI disable/enable funcs. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_hdmi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 85e6a91a4d33..aac320e97274 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -311,7 +311,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, vc4_hdmi_dump_regs(dev); } - HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); /* XXX: Pixel valve must be stopped. */ HD_WRITE(VC4_HD_VID_CTL, 0); @@ -351,7 +350,7 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, /* XXX: Wait for video. */ /* XXX: Enable pixel valve. */ - HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0); + HD_WRITE(VC4_HD_VID_CTL, VC4_HD_VID_CTL_ENABLE | VC4_HD_VID_CTL_UNDERFLOW_ENABLE | @@ -394,10 +393,18 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder) { + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + + HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16); } static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder) { + struct drm_device *dev = encoder->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + + HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0); } static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { -- cgit v1.2.1 From 75d31d39cb3dd5521b130bedd7fecff478e703b8 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 18 May 2015 18:48:23 -0700 Subject: drm/vc4: Fill out the CRTC enable/disable functions. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_crtc.c | 24 +++++++++++++++--------- drivers/gpu/drm/vc4/vc4_hdmi.c | 3 --- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 5c12e2472dc5..f929d560a13a 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -69,10 +69,24 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) static void vc4_crtc_disable(struct drm_crtc *crtc) { + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + + CRTC_WRITE(PV_V_CONTROL, + CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN); + + if (drm_crtc_index(crtc) == 0) { + do { + cpu_relax(); + } while (CRTC_READ(PV_STAT) & (PV_STAT_RUNNING_MASK)); + } } static void vc4_crtc_enable(struct drm_crtc *crtc) { + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + + CRTC_WRITE(PV_V_CONTROL, + CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); } static int vc4_crtc_atomic_check(struct drm_crtc *crtc, @@ -193,13 +207,6 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc) VC4_SET_FIELD(mode->vdisplay, PV_VERTB_VACTIVE)); u32 format = PV_CONTROL_FORMAT_24; - CRTC_WRITE(PV_V_CONTROL, - CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN); - - do { - /* XXX SLEEP */ - } while (CRTC_READ(PV_STAT) & (PV_STAT_RUNNING_MASK)); - CRTC_WRITE(PV_HORZA, VC4_SET_FIELD(mode->htotal - mode->hdisplay, PV_HORZA_HBP) | @@ -228,8 +235,7 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc) PV_CONTROL_EN); CRTC_WRITE(PV_V_CONTROL, - PV_VCONTROL_CONTINUOUS | - PV_VCONTROL_VIDEN); + PV_VCONTROL_CONTINUOUS); } if (debug_dump_regs) { diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index aac320e97274..a72390d0e41c 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -312,7 +312,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, } - /* XXX: Pixel valve must be stopped. */ HD_WRITE(VC4_HD_VID_CTL, 0); /* XXX: Set state machine clock. */ @@ -349,8 +348,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, /* XXX: Wait for video. */ - /* XXX: Enable pixel valve. */ - HD_WRITE(VC4_HD_VID_CTL, VC4_HD_VID_CTL_ENABLE | VC4_HD_VID_CTL_UNDERFLOW_ENABLE | -- cgit v1.2.1 From 13954f4609dc07278d00fb5dc509dea2986bbb91 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 31 Mar 2015 00:00:24 -0700 Subject: drm/vc4: Add some PV modesetting bits. This matches what the firmware was doing, and the display successfully comes back. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_crtc.c | 146 ++++++++++++++++++++++++----------------- drivers/gpu/drm/vc4/vc4_regs.h | 6 +- 2 files changed, 88 insertions(+), 64 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index f929d560a13a..ff99c8d96e0c 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -36,6 +36,7 @@ static const struct { CRTC_REG(PV_INTEN), CRTC_REG(PV_INTSTAT), CRTC_REG(PV_STAT), + CRTC_REG(PV_HACT_ACT), }; static void @@ -63,8 +64,89 @@ static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc, return true; } +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; + 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; + } +} + static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc) { + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct drm_crtc_state *state = crtc->state; + struct drm_display_mode *mode = &state->adjusted_mode; + u32 vactive = (mode->vdisplay >> + ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0)); + u32 format = PV_CONTROL_FORMAT_24; + bool debug_dump_regs = false; + + if (debug_dump_regs) { + DRM_INFO("CRTC %d regs before:\n", drm_crtc_index(crtc)); + vc4_crtc_dump_regs(vc4_crtc); + } + + /* Reset the PV fifo. */ + CRTC_WRITE(PV_CONTROL, 0); + CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR | PV_CONTROL_EN); + CRTC_WRITE(PV_CONTROL, 0); + + CRTC_WRITE(PV_HORZA, + VC4_SET_FIELD(mode->htotal - mode->hsync_end, + PV_HORZA_HBP) | + VC4_SET_FIELD(mode->hsync_end - mode->hsync_start, + PV_HORZA_HSYNC)); + CRTC_WRITE(PV_HORZB, + VC4_SET_FIELD(mode->hsync_start - mode->hdisplay, + PV_HORZB_HFP) | + VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE)); + + CRTC_WRITE(PV_VERTA, + VC4_SET_FIELD(mode->vtotal - mode->vsync_end, + PV_VERTA_VBP) | + VC4_SET_FIELD(mode->vsync_end - mode->vsync_start, + PV_VERTA_VSYNC)); + CRTC_WRITE(PV_VERTB, + VC4_SET_FIELD(mode->vsync_start - mode->vdisplay, + PV_VERTB_VFP) | + VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE)); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + /* Write PV_VERTA_EVEN/VERTB_EVEN */ + } + + CRTC_WRITE(PV_HACT_ACT, mode->hdisplay); + + CRTC_WRITE(PV_CONTROL, + VC4_SET_FIELD(format, PV_CONTROL_FORMAT) | + VC4_SET_FIELD(vc4_get_fifo_full_level(format), + PV_CONTROL_FIFO_LEVEL) | + PV_CONTROL_CLR_AT_START | + PV_CONTROL_TRIGGER_UNDERFLOW | + PV_CONTROL_WAIT_HSTART | + PV_CONTROL_CLK_MUX_EN | + VC4_SET_FIELD(PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI, + PV_CONTROL_CLK_SELECT) | + PV_CONTROL_FIFO_CLR | + PV_CONTROL_EN); + + CRTC_WRITE(PV_V_CONTROL, + PV_VCONTROL_CONTINUOUS); + + if (debug_dump_regs) { + DRM_INFO("CRTC %d regs after:\n", drm_crtc_index(crtc)); + vc4_crtc_dump_regs(vc4_crtc); + } } static void vc4_crtc_disable(struct drm_crtc *crtc) @@ -133,37 +215,17 @@ static void vc4_crtc_atomic_begin(struct drm_crtc *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; - 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; - } -} - static void vc4_crtc_atomic_flush(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 drm_display_mode *mode = &crtc->hwmode; struct drm_plane *plane; bool debug_dump_regs = false; u32 __iomem *dlist_next = vc4_crtc->dlist; if (debug_dump_regs) { - DRM_INFO("CRTC regs before:\n"); - vc4_crtc_dump_regs(vc4_crtc); + DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); vc4_hvs_dump_state(dev); } @@ -199,48 +261,8 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc) vc4_crtc->dlist = dlist_next; } - if (0) { - u32 verta = (VC4_SET_FIELD(mode->vtotal - mode->vdisplay, - PV_VERTA_VBP) | - VC4_SET_FIELD(mode->vsync_start, PV_VERTA_VSYNC)); - u32 vertb = (VC4_SET_FIELD(0, PV_VERTB_VFP), - VC4_SET_FIELD(mode->vdisplay, PV_VERTB_VACTIVE)); - u32 format = PV_CONTROL_FORMAT_24; - - CRTC_WRITE(PV_HORZA, - VC4_SET_FIELD(mode->htotal - mode->hdisplay, - PV_HORZA_HBP) | - VC4_SET_FIELD(mode->hsync_start, PV_HORZA_HSYNC)); - CRTC_WRITE(PV_HORZB, - VC4_SET_FIELD(0, PV_HORZB_HFP) | - VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE)); - - CRTC_WRITE(PV_VERTA, verta); - CRTC_WRITE(PV_VERTB, vertb); - CRTC_WRITE(PV_VERTA_EVEN, verta); - CRTC_WRITE(PV_VERTB_EVEN, vertb); - - CRTC_WRITE(PV_DSI_HACT, mode->htotal - mode->hdisplay); - - CRTC_WRITE(PV_CONTROL, - VC4_SET_FIELD(format, PV_CONTROL_FORMAT) | - VC4_SET_FIELD(vc4_get_fifo_full_level(format), - PV_CONTROL_FIFO_LEVEL) | - PV_CONTROL_CLR_AT_START | - PV_CONTROL_TRIGGER_UNDERFLOW | - PV_CONTROL_WAIT_HSTART | - PV_CONTROL_CLK_MUX_EN | - VC4_SET_FIELD(0, PV_CONTROL_CLK_SELECT) | - PV_CONTROL_FIFO_CLR | - PV_CONTROL_EN); - - CRTC_WRITE(PV_V_CONTROL, - PV_VCONTROL_CONTINUOUS); - } - if (debug_dump_regs) { - DRM_INFO("CRTC regs after:\n"); - vc4_crtc_dump_regs(vc4_crtc); + DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); vc4_hvs_dump_state(dev); } } diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index b40610743a2c..9332932d2a17 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -171,6 +171,8 @@ # define PV_CONTROL_CLR_AT_START (1 << 14) # define PV_CONTROL_TRIGGER_UNDERFLOW (1 << 13) # define PV_CONTROL_WAIT_HSTART (1 << 12) +# define PV_CONTROL_CLK_SELECT_DSI 0 +# define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI 1 # define PV_CONTROL_CLK_SELECT_MASK VC4_MASK(3, 2) # define PV_CONTROL_CLK_SELECT_SHIFT 2 # define PV_CONTROL_FIFO_CLR (1 << 1) @@ -212,7 +214,7 @@ #define PV_INTEN 0x24 #define PV_INTSTAT 0x28 # define PV_INT_VID_IDLE (1 << 9) -# define PV_INT_VFP_END (1 << 8) +# define PV_INT_VFP_END (1 << 8) # define PV_INT_VFP_START (1 << 7) # define PV_INT_VACT_START (1 << 6) # define PV_INT_VBP_START (1 << 5) @@ -226,7 +228,7 @@ # define PV_STAT_IDLE (1 << 8) # define PV_STAT_RUNNING_MASK VC4_MASK(7, 0) -#define PV_DSI_HACT 0x30 +#define PV_HACT_ACT 0x30 #define SCALER_DISPCTRL 0x00000000 #define SCALER_DISPSTAT 0x00000004 -- cgit v1.2.1 From a0126bd8e3991be56c97ca17d0b28797f2a80546 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 18 May 2015 19:51:43 -0700 Subject: drm/vc4: Improve HDMI debug. Dump more regs we might be interested in, and dump them correctly. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_hdmi.c | 6 +++++- drivers/gpu/drm/vc4/vc4_regs.h | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index a72390d0e41c..ac5b1f81c0c0 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -90,7 +90,11 @@ static const struct { u32 reg; const char *name; } hd_regs[] = { + HDMI_REG(VC4_HD_M_CTL), + HDMI_REG(VC4_HD_MAI_CTL), HDMI_REG(VC4_HD_VID_CTL), + HDMI_REG(VC4_HD_CSC_CTL), + HDMI_REG(VC4_HD_FRAME_COUNT), }; #ifdef CONFIG_DEBUG_FS @@ -131,7 +135,7 @@ static void vc4_hdmi_dump_regs(struct drm_device *dev) for (i = 0; i < ARRAY_SIZE(hd_regs); i++) { DRM_INFO("0x%04x (%s): 0x%08x\n", hd_regs[i].reg, hd_regs[i].name, - HDMI_READ(hd_regs[i].reg)); + HD_READ(hd_regs[i].reg)); } } diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 9332932d2a17..5c43680fee2d 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -323,11 +323,17 @@ #define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 +#define VC4_HD_M_CTL 0x000 +#define VC4_HD_MAI_CTL 0x014 + #define VC4_HD_VID_CTL 0x038 # define VC4_HD_VID_CTL_ENABLE (1 << 31) # define VC4_HD_VID_CTL_UNDERFLOW_ENABLE (1 << 30) # define VC4_HD_VID_CTL_FRAME_COUNTER_RESET (1 << 29) +#define VC4_HD_CSC_CTL 0x040 +#define VC4_HD_FRAME_COUNT 0x068 + /* HVS display list information. */ #define HVS_BOOTLOADER_DLIST_END 32 -- cgit v1.2.1 From 44392985d1d0a3526aa22a1c7592ce527e9aabbf Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 18 May 2015 20:00:43 -0700 Subject: drm/vc4: Add the remaining HDMI sync polarity bits. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_hdmi.c | 6 +++++- drivers/gpu/drm/vc4/vc4_regs.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index ac5b1f81c0c0..ace1493427b6 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -346,13 +346,17 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, HDMI_WRITE(VC4_HDMI_VERTB0, vertb); HDMI_WRITE(VC4_HDMI_VERTB1, vertb); - /* XXX: HD VID CTL set up sync polarities */ + HD_WRITE(VC4_HD_VID_CTL, + (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) | + (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); + HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); /* XXX: HD CSC CTL = 0x20 */ /* XXX: Wait for video. */ 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); diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 5c43680fee2d..bab7d1b56152 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -330,6 +330,8 @@ # define VC4_HD_VID_CTL_ENABLE (1 << 31) # define VC4_HD_VID_CTL_UNDERFLOW_ENABLE (1 << 30) # define VC4_HD_VID_CTL_FRAME_COUNTER_RESET (1 << 29) +# define VC4_HD_VID_CTL_VSYNC_LOW (1 << 28) +# define VC4_HD_VID_CTL_HSYNC_LOW (1 << 27) #define VC4_HD_CSC_CTL 0x040 #define VC4_HD_FRAME_COUNT 0x068 -- cgit v1.2.1 From b04df131dc279418c9ed181cda0960f7fd43adb1 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 18 May 2015 20:17:50 -0700 Subject: drm/vc4: Wait for HDMI activate/deactivate to actually happen. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_hdmi.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index ace1493427b6..b98547e432c3 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -358,13 +358,17 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, 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); if (vc4_encoder->hdmi_monitor) { HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); + + while (!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & + VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE)) { + cpu_relax(); + } } else { HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & @@ -372,9 +376,12 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI); - } - /* Wait for set pending done. */ + while (HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & + VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE) { + cpu_relax(); + } + } if (vc4_encoder->hdmi_monitor) { WARN_ON(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) & -- cgit v1.2.1 From 52947f85d94a021bfb89a868ef0cc2d85096c69c Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 18 May 2015 20:23:56 -0700 Subject: drm/vc4: Actually set the pixel clock. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_hdmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index b98547e432c3..5ccd3b495947 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -278,7 +278,7 @@ vc4_set_pixel_clock(struct vc4_dev *vc4, u32 clock) u32 packet[2]; int ret; - packet[0] = 9; /* Pixel clock. */ + packet[0] = 8; /* Pixel clock. */ packet[1] = clock; ret = rpi_firmware_property(vc4->firmware_node, @@ -319,8 +319,7 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, HD_WRITE(VC4_HD_VID_CTL, 0); /* XXX: Set state machine clock. */ - if (0) /* XXX: Kills the screen. */ - vc4_set_pixel_clock(vc4, mode->clock * 1000); + vc4_set_pixel_clock(vc4, mode->clock * 1000); HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL, HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) | -- cgit v1.2.1 From 8c53425bc6f7fea7a6cbc2376182f026b34288c9 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 26 May 2015 20:29:31 -0700 Subject: drm/vc4: Make a vc4_crtc->channel instead of displist_reg. We're going to need to use the channel number for our other regs, too. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_crtc.c | 10 +++++----- drivers/gpu/drm/vc4/vc4_drv.h | 3 ++- drivers/gpu/drm/vc4/vc4_regs.h | 4 ++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index ff99c8d96e0c..d1ded0b5a547 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -248,12 +248,12 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc) * SCALER_CTL0_END, just in case, though. */ writel(SCALER_CTL0_END, vc4->hvs->dlist); - HVS_WRITE(vc4_crtc->displist_reg, 0); + HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), 0); } else { writel(SCALER_CTL0_END, dlist_next); dlist_next++; - HVS_WRITE(vc4_crtc->displist_reg, + HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), (u32 *)vc4_crtc->dlist - (u32 *)vc4->hvs->dlist); /* Make the next display list start after ours. */ @@ -367,14 +367,14 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) */ switch (drm_crtc_index(crtc)) { case 0: - vc4_crtc->displist_reg = SCALER_DISPLIST0; + vc4_crtc->channel = 0; break; case 1: - vc4_crtc->displist_reg = SCALER_DISPLIST2; + vc4_crtc->channel = 2; break; default: case 2: - vc4_crtc->displist_reg = SCALER_DISPLIST1; + vc4_crtc->channel = 1; break; } diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 3e0240f103c3..9104d10e6ce2 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -51,7 +51,8 @@ struct vc4_crtc { struct drm_crtc base; void __iomem *regs; - u32 displist_reg; + /* Which HVS channel we're using for our CRTC. */ + int channel; /* * Pointer to the actual hardware display list memory for the diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index bab7d1b56152..581412d0c352 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -241,6 +241,10 @@ #define SCALER_DISPLIST1 0x00000024 #define SCALER_DISPLIST2 0x00000028 #define SCALER_DISPLSTAT 0x0000002c +#define SCALER_DISPLISTX(x) (SCALER_DISPLIST0 + \ + (x) * (SCALER_DISPLIST1 - \ + SCALER_DISPLIST0)) + #define SCALER_DISPLACT0 0x00000030 #define SCALER_DISPLACT1 0x00000034 #define SCALER_DISPLACT2 0x00000038 -- cgit v1.2.1 From 0f2d30dc57a58eb5d30416f043bd250e14c128be Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 28 May 2015 14:49:24 -0700 Subject: drm/vc4: Configure the colorspace conversion of the HDMI output. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_hdmi.c | 5 ++++- drivers/gpu/drm/vc4/vc4_regs.h | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 5ccd3b495947..5529b7bb5503 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -349,8 +349,11 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder, (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) | (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW)); + /* The RGB order applies even when CSC is disabled. */ + HD_WRITE(VC4_HD_CSC_CTL, VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR, + VC4_HD_CSC_CTL_ORDER)); + HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); - /* XXX: HD CSC CTL = 0x20 */ /* XXX: Wait for video. */ diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 581412d0c352..6efd872a32dd 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -338,6 +338,23 @@ # define VC4_HD_VID_CTL_HSYNC_LOW (1 << 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 +# define VC4_HD_CSC_CTL_ORDER_BGR 1 +# define VC4_HD_CSC_CTL_ORDER_BRG 2 +# define VC4_HD_CSC_CTL_ORDER_GRB 3 +# define VC4_HD_CSC_CTL_ORDER_GBR 4 +# define VC4_HD_CSC_CTL_ORDER_RBG 5 +# define VC4_HD_CSC_CTL_PADMSB (1 << 4) +# define VC4_HD_CSC_CTL_MODE_MASK VC4_MASK(3, 2) +# define VC4_HD_CSC_CTL_MODE_SHIFT 2 +# define VC4_HD_CSC_CTL_MODE_RGB_TO_SD_YPRPB 0 +# define VC4_HD_CSC_CTL_MODE_RGB_TO_HD_YPRPB 1 +# define VC4_HD_CSC_CTL_MODE_CUSTOM 2 +# define VC4_HD_CSC_CTL_RGB2YCC (1 << 1) +# define VC4_HD_CSC_CTL_ENABLE (1 << 0) + #define VC4_HD_FRAME_COUNT 0x068 /* HVS display list information. */ -- cgit v1.2.1 From 20caaa8485f8873defcf17e7e5998e5cffed3bd5 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 18 Jun 2014 07:35:07 +0100 Subject: drm/vc4: Add minimal GEM support. With this I fired off a demo triangle from a hacked up version of https://github.com/phire/hackdriver -- now to build an actually usable interface. This is totally insecure. Don't use it. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/Makefile | 1 + drivers/gpu/drm/vc4/vc4_drv.c | 2 + drivers/gpu/drm/vc4/vc4_drv.h | 4 + drivers/gpu/drm/vc4/vc4_gem.c | 193 ++++++++++++++++++++++++++++++++++++++++++ include/uapi/drm/vc4_drm.h | 35 ++++++++ 5 files changed, 235 insertions(+) create mode 100644 drivers/gpu/drm/vc4/vc4_gem.c create mode 100644 include/uapi/drm/vc4_drm.h diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index 18d5197e4892..a8515f570b5f 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -8,6 +8,7 @@ vc4-y := \ vc4_crtc.o \ vc4_drv.o \ vc4_kms.o \ + vc4_gem.o \ vc4_hdmi.o \ vc4_hvs.o \ vc4_plane.o \ diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 3fa362d19a78..ba1145a98e38 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -14,6 +14,7 @@ #include #include +#include "uapi/drm/vc4_drm.h" #include "vc4_drv.h" #include "vc4_regs.h" @@ -107,6 +108,7 @@ static const struct file_operations vc4_drm_fops = { }; static const struct drm_ioctl_desc vc4_drm_ioctls[] = { + DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, 0), }; static struct drm_driver vc4_drm_driver = { diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 9104d10e6ce2..703027043b59 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -106,6 +106,10 @@ void vc4_debugfs_cleanup(struct drm_minor *minor); /* vc4_drv.c */ void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); +/* vc4_gem.c */ +int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + /* vc4_hdmi.c */ void vc4_hdmi_register(void); void vc4_hdmi_unregister(void); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c new file mode 100644 index 000000000000..9eb73eedc9e5 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -0,0 +1,193 @@ +/* + * Copyright © 2014 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "uapi/drm/vc4_drm.h" +#include "vc4_drv.h" +#include "vc4_regs.h" + +static void +thread_reset(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + DRM_INFO("Resetting threads\n"); + V3D_WRITE(V3D_CT0CS, V3D_CTRSTA); + V3D_WRITE(V3D_CT1CS, V3D_CTRSTA); + barrier(); +} + +static void +submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + /* Stop any existing thread and set state to "stopped at halt" */ + V3D_WRITE(V3D_CTNCS(thread), V3D_CTRUN); + barrier(); + + V3D_WRITE(V3D_CTNCA(thread), start); + barrier(); + + /* Set the end address of the control list. Writing this + * register is what starts the job. + */ + V3D_WRITE(V3D_CTNEA(thread), end); + barrier(); +} + +static bool +thread_stopped(struct drm_device *dev, uint32_t thread) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + barrier(); + return !(V3D_READ(V3D_CTNCS(thread)) & V3D_CTRUN); +} + +static int +wait_for_bin_thread(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i; + + for (i = 0; i < 1000000; i++) { + if (thread_stopped(dev, 0)) { + if (V3D_READ(V3D_PCS) & V3D_BMOOM) { + /* XXX */ + DRM_ERROR("binner oom and stopped\n"); + return -EINVAL; + } + return 0; + } + + if (V3D_READ(V3D_PCS) & V3D_BMOOM) { + /* XXX */ + DRM_ERROR("binner oom\n"); + return -EINVAL; + } + } + + DRM_ERROR("timeout waiting for bin thread idle\n"); + return -EINVAL; +} + +static int +wait_for_idle(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i; + + for (i = 0; i < 1000000; i++) { + if (V3D_READ(V3D_PCS) == 0) + return 0; + } + + DRM_ERROR("timeout waiting for idle\n"); + return -EINVAL; +} + +/* +static int +wait_for_render_thread(struct drm_device *dev, u32 initial_rfc) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i; + + for (i = 0; i < 1000000; i++) { + if ((V3D_READ(V3D_RFC) & 0xff) == ((initial_rfc + 1) & 0xff)) + return 0; + } + + DRM_ERROR("timeout waiting for render thread idle: " + "0x%08x start vs 0x%08x end\n", + initial_rfc, V3D_READ(V3D_RFC)); + return -EINVAL; +} +*/ + +static int +vc4_submit(struct drm_device *dev, struct drm_vc4_submit_cl *args) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t ct0ca = args->ct0ca, ct0ea = args->ct0ea; /* XXX */ + uint32_t ct1ca = args->ct1ca, ct1ea = args->ct1ea; /* XXX */ + int ret; + + /* flushes caches */ + V3D_WRITE(V3D_L2CACTL, 1 << 2); + barrier(); + + /* Disable the binner's pre-loaded overflow memory address */ + V3D_WRITE(V3D_BPOA, 0); + V3D_WRITE(V3D_BPOS, 0); + + submit_cl(dev, 0, ct0ca, ct0ea); + + ret = wait_for_bin_thread(dev); + if (ret) + return ret; + + ret = wait_for_idle(dev); + if (ret) + return ret; + + WARN_ON(!thread_stopped(dev, 0)); + if (V3D_READ(V3D_CTNCS(0)) & V3D_CTERR) { + DRM_ERROR("thread 0 stopped with error\n"); + return -EINVAL; + } + + submit_cl(dev, 1, ct1ca, ct1ea); + + ret = wait_for_idle(dev); + if (ret) + return ret; + + return 0; +} + +/** + * Submits a command list to the VC4. + * + * This is what is called batchbuffer emitting on other hardware. + */ +int +vc4_submit_cl_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vc4_submit_cl *args = data; + int ret; + + mutex_lock(&dev->struct_mutex); + ret = vc4_submit(dev, args); + if (ret) + thread_reset(dev); + mutex_unlock(&dev->struct_mutex); + + return ret; +} diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h new file mode 100644 index 000000000000..9af5e89df332 --- /dev/null +++ b/include/uapi/drm/vc4_drm.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2014 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _UAPI_VC4_DRM_H_ +#define _UAPI_VC4_DRM_H_ + +#define DRM_VC4_SUBMIT_CL 0x00 + +#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) + +struct drm_vc4_submit_cl { + uint32_t ct0ca, ct0ea, ct1ca, ct1ea; +}; + +#endif /* _UAPI_VC4_DRM_H_ */ -- cgit v1.2.1 From b74f37725808e7fd963bb6f1676655c3c9120ae6 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 18 Jun 2014 13:48:16 +0100 Subject: drm/vc4: Start building toward relocs. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 94 +++++++++++++++++++++++++++++++++++++++++-- include/uapi/drm/vc4_drm.h | 5 ++- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 9eb73eedc9e5..b12971788b12 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -30,6 +30,13 @@ #include "vc4_drv.h" #include "vc4_regs.h" +struct exec_info { + struct drm_gem_cma_object *bin_bo; + struct drm_gem_cma_object *render_bo; + uint32_t ct0ca, ct0ea; + uint32_t ct1ca, ct1ea; +}; + static void thread_reset(struct drm_device *dev) { @@ -131,11 +138,11 @@ wait_for_render_thread(struct drm_device *dev, u32 initial_rfc) */ static int -vc4_submit(struct drm_device *dev, struct drm_vc4_submit_cl *args) +vc4_submit(struct drm_device *dev, struct exec_info *args) { struct vc4_dev *vc4 = to_vc4_dev(dev); - uint32_t ct0ca = args->ct0ca, ct0ea = args->ct0ea; /* XXX */ - uint32_t ct1ca = args->ct1ca, ct1ea = args->ct1ea; /* XXX */ + uint32_t ct0ca = args->ct0ca, ct0ea = args->ct0ea; + uint32_t ct1ca = args->ct1ca, ct1ea = args->ct1ea; int ret; /* flushes caches */ @@ -171,6 +178,68 @@ vc4_submit(struct drm_device *dev, struct drm_vc4_submit_cl *args) return 0; } +static int +vc4_cl_validate(struct drm_device *dev, struct drm_vc4_submit_cl *args, + struct exec_info *exec_info) +{ + void *bin_contents, *render_contents; + int ret = 0; + + bin_contents = kmalloc(args->bin_thread_len, GFP_KERNEL); + render_contents = kmalloc(args->render_thread_len, GFP_KERNEL); + if (!bin_contents || !render_contents) { + DRM_ERROR("Failed to allocate storage for copying " + "in bin/render CLs.\n"); + ret = -ENOMEM; + goto fail; + } + + ret = copy_from_user(bin_contents, args->bin_thread, + args->bin_thread_len); + if (ret) { + DRM_ERROR("Failed to copy in bin thread\n"); + goto fail; + } + + ret = copy_from_user(render_contents, args->render_thread, + args->render_thread_len); + if (ret) { + DRM_ERROR("Failed to copy in render thread\n"); + goto fail; + } + + exec_info->bin_bo = drm_gem_cma_create(dev, args->bin_thread_len); + if (IS_ERR(exec_info->bin_bo)) { + DRM_ERROR("Couldn't allocate BO for bin thread\n"); + ret = PTR_ERR(exec_info->bin_bo); + exec_info->bin_bo = NULL; + goto fail; + } + + exec_info->render_bo = drm_gem_cma_create(dev, args->render_thread_len); + if (IS_ERR(exec_info->render_bo)) { + DRM_ERROR("Couldn't allocate BO for render thread\n"); + ret = PTR_ERR(exec_info->render_bo); + exec_info->render_bo = NULL; + goto fail; + } + + memcpy(exec_info->bin_bo->vaddr, bin_contents, + args->bin_thread_len); + memcpy(exec_info->render_bo->vaddr, render_contents, + args->render_thread_len); + + exec_info->ct0ca = exec_info->bin_bo->paddr; + exec_info->ct0ea = exec_info->ct0ca + args->bin_thread_len; + exec_info->ct1ca = exec_info->render_bo->paddr; + exec_info->ct1ea = exec_info->ct1ca + args->render_thread_len; + +fail: + kfree(bin_contents); + kfree(render_contents); + return ret; +} + /** * Submits a command list to the VC4. * @@ -181,13 +250,30 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_vc4_submit_cl *args = data; + struct exec_info exec_info; int ret; + memset(&exec_info, 0, sizeof(exec_info)); + mutex_lock(&dev->struct_mutex); - ret = vc4_submit(dev, args); + + ret = vc4_cl_validate(dev, args, &exec_info); + if (ret) { + mutex_unlock(&dev->struct_mutex); + goto fail; + } + + ret = vc4_submit(dev, &exec_info); if (ret) thread_reset(dev); + mutex_unlock(&dev->struct_mutex); +fail: + + /* XXX */ + //drm_gem_object_unreference(&exec_info.bin_bo->base); + //drm_gem_object_unreference(&exec_info.render_bo->base); + return ret; } diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index 9af5e89df332..7a1dedfa02d6 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -29,7 +29,10 @@ #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) struct drm_vc4_submit_cl { - uint32_t ct0ca, ct0ea, ct1ca, ct1ea; + void __user *bin_thread; + void __user *render_thread; + uint32_t bin_thread_len; + uint32_t render_thread_len; }; #endif /* _UAPI_VC4_DRM_H_ */ -- cgit v1.2.1 From 875999f207e12877dbf5fd36e8acb130b5da583c Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 18 Jun 2014 15:55:34 +0100 Subject: drm/vc4: Start building the command validator. This rendered a triangle with an even more hacked-up hackdriver. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/Makefile | 1 + drivers/gpu/drm/vc4/vc4_drv.h | 59 ++++++ drivers/gpu/drm/vc4/vc4_gem.c | 207 ++++++++++++++----- drivers/gpu/drm/vc4/vc4_validate.c | 406 +++++++++++++++++++++++++++++++++++++ include/uapi/drm/vc4_drm.h | 15 +- 5 files changed, 634 insertions(+), 54 deletions(-) create mode 100644 drivers/gpu/drm/vc4/vc4_validate.c diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index a8515f570b5f..c35c07dfec8d 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -13,6 +13,7 @@ vc4-y := \ vc4_hvs.o \ vc4_plane.o \ vc4_v3d.o \ + vc4_validate.o \ $() vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 703027043b59..2d449ad2bb80 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -84,6 +84,49 @@ to_vc4_plane(struct drm_plane *plane) #define HVS_READ(offset) readl(vc4->hvs->regs + offset) #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) +struct exec_info { + /* This is the array of BOs that were looked up at the start of exec. + * Command validation will use indices into this array. + */ + struct drm_gem_cma_object **bo; + uint32_t bo_count; + + /* Current indices into @bo loaded by the non-hardware packet + * that passes in indices. This can be used even without + * checking that we've seen one of those packets, because + * @bo_count is always >= 1, and this struct is initialized to + * 0. + */ + uint32_t bo_index[2]; + uint32_t max_width, max_height; + + /** + * This is the BO where we store the validated command lists + * and shader records. + */ + struct drm_gem_cma_object *exec_bo; + + /** + * This tracks the per-shader-record state (packet 64) that + * determines the length of the shader record and the offset + * it's expected to be found at. It gets read in from the + * command lists. + */ + uint32_t *shader_state; + /** How many shader states the user declared they were using. */ + uint32_t shader_state_size; + /** How many shader state records the validator has seen. */ + uint32_t shader_state_count; + + /** + * Computed addresses pointing into exec_bo where we start the + * bin thread (ct0) and render thread (ct1). + */ + uint32_t ct0ca, ct0ea; + uint32_t ct1ca, ct1ea; + uint32_t shader_paddr; +}; + /* vc4_bo.c */ void vc4_free_object(struct drm_gem_object *gem_obj); struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); @@ -139,3 +182,19 @@ void vc4_v3d_unregister(void); int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused); int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused); int vc4_v3d_set_power(struct vc4_dev *vc4, bool on); + +/* vc4_validate.c */ +int +vc4_validate_cl(struct drm_device *dev, + void *validated, + void *unvalidated, + uint32_t len, + bool is_bin, + struct exec_info *exec); + +int +vc4_validate_shader_recs(struct drm_device *dev, + void *validated, + void *unvalidated, + uint32_t len, + struct exec_info *exec); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index b12971788b12..81eeb401d519 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -30,13 +30,6 @@ #include "vc4_drv.h" #include "vc4_regs.h" -struct exec_info { - struct drm_gem_cma_object *bin_bo; - struct drm_gem_cma_object *render_bo; - uint32_t ct0ca, ct0ea; - uint32_t ct1ca, ct1ea; -}; - static void thread_reset(struct drm_device *dev) { @@ -178,65 +171,171 @@ vc4_submit(struct drm_device *dev, struct exec_info *args) return 0; } +/** + * Looks up a bunch of GEM handles for BOs and stores the array for + * use in the command validator that actually writes relocated + * addresses pointing to them. + */ +static int +vc4_cl_lookup_bos(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_vc4_submit_cl *args, + struct exec_info *exec) +{ + uint32_t *handles; + int ret = 0; + int i; + + exec->bo_count = args->bo_handle_count; + + if (!exec->bo_count) { + /* See comment on bo_index for why we have to check + * this. + */ + DRM_ERROR("Rendering requires BOs to validate\n"); + return -EINVAL; + } + + exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_object *), + GFP_KERNEL); + if (!exec->bo) { + DRM_ERROR("Failed to allocate validated BO pointers\n"); + return -ENOMEM; + } + + handles = drm_malloc_ab(exec->bo_count, sizeof(uint32_t)); + if (!handles) { + DRM_ERROR("Failed to allocate incoming GEM handles\n"); + goto fail; + } + + ret = copy_from_user(handles, args->bo_handles, + exec->bo_count * sizeof(uint32_t)); + if (ret) { + DRM_ERROR("Failed to copy in GEM handles\n"); + goto fail; + } + + for (i = 0; i < exec->bo_count; i++) { + struct drm_gem_object *bo; + + bo = drm_gem_object_lookup(dev, file_priv, handles[i]); + if (!bo) { + DRM_ERROR("Failed to look up GEM BO %d: %d\n", + i, handles[i]); + ret = -EINVAL; + goto fail; + } + exec->bo[i] = (struct drm_gem_cma_object *)bo; + } + +fail: + kfree(handles); + return 0; +} + static int vc4_cl_validate(struct drm_device *dev, struct drm_vc4_submit_cl *args, - struct exec_info *exec_info) + struct exec_info *exec) { - void *bin_contents, *render_contents; + void *temp = NULL; + void *bin, *render, *shader_rec; int ret = 0; + uint32_t bin_offset = 0; + uint32_t render_offset = bin_offset + args->bin_cl_len; + uint32_t shader_rec_offset = roundup(render_offset + + args->render_cl_len, 16); + uint32_t exec_size = shader_rec_offset + args->shader_record_len; + uint32_t temp_size = exec_size + (sizeof(uint32_t) * + args->shader_record_count); + + if (shader_rec_offset < render_offset || + exec_size < shader_rec_offset || + args->shader_record_count >= (UINT_MAX / sizeof(uint32_t)) || + temp_size < exec_size) { + DRM_ERROR("overflow in exec arguments\n"); + goto fail; + } - bin_contents = kmalloc(args->bin_thread_len, GFP_KERNEL); - render_contents = kmalloc(args->render_thread_len, GFP_KERNEL); - if (!bin_contents || !render_contents) { + /* Allocate space where we'll store the copied in user command lists + * and shader records. + * + * We don't just copy directly into the BOs because we need to + * read the contents back for validation, and I think the + * bo->vaddr is uncached access. + */ + temp = kmalloc(temp_size, GFP_KERNEL); + if (!temp) { DRM_ERROR("Failed to allocate storage for copying " "in bin/render CLs.\n"); ret = -ENOMEM; goto fail; } + bin = temp + bin_offset; + render = temp + render_offset; + shader_rec = temp + shader_rec_offset; + exec->shader_state = temp + exec_size; + exec->shader_state_size = args->shader_record_count; - ret = copy_from_user(bin_contents, args->bin_thread, - args->bin_thread_len); + ret = copy_from_user(bin, args->bin_cl, args->bin_cl_len); if (ret) { - DRM_ERROR("Failed to copy in bin thread\n"); + DRM_ERROR("Failed to copy in bin cl\n"); goto fail; } - ret = copy_from_user(render_contents, args->render_thread, - args->render_thread_len); + ret = copy_from_user(render, args->render_cl, args->render_cl_len); if (ret) { - DRM_ERROR("Failed to copy in render thread\n"); + DRM_ERROR("Failed to copy in render cl\n"); goto fail; } - exec_info->bin_bo = drm_gem_cma_create(dev, args->bin_thread_len); - if (IS_ERR(exec_info->bin_bo)) { - DRM_ERROR("Couldn't allocate BO for bin thread\n"); - ret = PTR_ERR(exec_info->bin_bo); - exec_info->bin_bo = NULL; + ret = copy_from_user(shader_rec, args->shader_records, + args->shader_record_len); + if (ret) { + DRM_ERROR("Failed to copy in shader recs\n"); goto fail; } - exec_info->render_bo = drm_gem_cma_create(dev, args->render_thread_len); - if (IS_ERR(exec_info->render_bo)) { - DRM_ERROR("Couldn't allocate BO for render thread\n"); - ret = PTR_ERR(exec_info->render_bo); - exec_info->render_bo = NULL; + exec->exec_bo = drm_gem_cma_create(dev, exec_size); + if (IS_ERR(exec->exec_bo)) { + DRM_ERROR("Couldn't allocate BO for exec\n"); + ret = PTR_ERR(exec->exec_bo); + exec->exec_bo = NULL; goto fail; } - memcpy(exec_info->bin_bo->vaddr, bin_contents, - args->bin_thread_len); - memcpy(exec_info->render_bo->vaddr, render_contents, - args->render_thread_len); + exec->ct0ca = exec->exec_bo->paddr + bin_offset; + exec->ct0ea = exec->ct0ca + args->bin_cl_len; + exec->ct1ca = exec->exec_bo->paddr + render_offset; + exec->ct1ea = exec->ct1ca + args->render_cl_len; + exec->shader_paddr = exec->exec_bo->paddr + shader_rec_offset; + + ret = vc4_validate_cl(dev, + exec->exec_bo->vaddr + bin_offset, + bin, + args->bin_cl_len, + true, + exec); + if (ret) + goto fail; + + ret = vc4_validate_cl(dev, + exec->exec_bo->vaddr + render_offset, + render, + args->render_cl_len, + false, + exec); + if (ret) + goto fail; - exec_info->ct0ca = exec_info->bin_bo->paddr; - exec_info->ct0ea = exec_info->ct0ca + args->bin_thread_len; - exec_info->ct1ca = exec_info->render_bo->paddr; - exec_info->ct1ea = exec_info->ct1ca + args->render_thread_len; + ret = vc4_validate_shader_recs(dev, + exec->exec_bo->vaddr + shader_rec_offset, + shader_rec, + args->shader_record_len, + exec); fail: - kfree(bin_contents); - kfree(render_contents); + kfree(temp); return ret; } @@ -250,30 +349,38 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_vc4_submit_cl *args = data; - struct exec_info exec_info; + struct exec_info exec; int ret; + int i; - memset(&exec_info, 0, sizeof(exec_info)); + memset(&exec, 0, sizeof(exec)); mutex_lock(&dev->struct_mutex); - ret = vc4_cl_validate(dev, args, &exec_info); - if (ret) { - mutex_unlock(&dev->struct_mutex); + ret = vc4_cl_lookup_bos(dev, file_priv, args, &exec); + if (ret) goto fail; - } - ret = vc4_submit(dev, &exec_info); + ret = vc4_cl_validate(dev, args, &exec); if (ret) - thread_reset(dev); + goto fail; - mutex_unlock(&dev->struct_mutex); + ret = vc4_submit(dev, &exec); + if (ret) { + thread_reset(dev); + goto fail; + } fail: + if (exec.bo) { + for (i = 0; i < exec.bo_count; i++) + drm_gem_object_unreference(&exec.bo[i]->base); + kfree(exec.bo); + } + + drm_gem_object_unreference(&exec.exec_bo->base); - /* XXX */ - //drm_gem_object_unreference(&exec_info.bin_bo->base); - //drm_gem_object_unreference(&exec_info.render_bo->base); + mutex_unlock(&dev->struct_mutex); return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c new file mode 100644 index 000000000000..438e2257a64b --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -0,0 +1,406 @@ +/* + * Copyright © 2014 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * Command list validator for VC4. + * + * The VC4 has no IOMMU between it and system memory. So, a user with + * access to execute command lists could escalate privilege by + * overwriting system memory (drawing to it as a framebuffer) or + * reading system memory it shouldn't (reading it as a texture, or + * uniform data, or vertex data). + * + * This validates command lists to ensure that all accesses are within + * the bounds of the GEM objects referenced. It explicitly whitelists + * packets, and looks at the offsets in any address fields to make + * sure they're constrained within the BOs they reference. + * + * Note that because of the validation that's happening anyway, this + * is where GEM relocation processing happens. + */ + +#include "vc4_drv.h" +#include "vc4_regs.h" + +#define VALIDATE_ARGS \ + struct exec_info *exec, \ + void *validated, \ + void *untrusted + +static int +validate_branch_to_sublist(VALIDATE_ARGS) +{ + struct drm_gem_cma_object *target; + + /* XXX: Validate address jumped to */ + + target = exec->bo[exec->bo_index[0]]; + + *(uint32_t *)(validated + 0) = + *(uint32_t *)(untrusted + 0) + target->paddr; + + return 0; +} + +static int +validate_store_tile_buffer_general(VALIDATE_ARGS) +{ + struct drm_gem_cma_object *fbo; + + /* XXX: Validate address offset */ + + fbo = exec->bo[exec->bo_index[0]]; + + /* XXX */ + /* + *(uint32_t *)(validated + 2) = + *(uint32_t *)(untrusted + 2) + fbo->paddr; + */ + + return 0; +} + +static int +validate_indexed_prim_list(VALIDATE_ARGS) +{ + struct drm_gem_cma_object *ib; + uint32_t max_index = *(uint32_t *)(untrusted + 9); + uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; + uint32_t ib_access_end = (max_index + 1) * index_size; + + /* Check overflow condition */ + if (max_index == ~0) { + DRM_ERROR("unlimited max index\n"); + return -EINVAL; + } + + if (ib_access_end < max_index) { + DRM_ERROR("IB access overflow\n"); + return -EINVAL; + } + + ib = exec->bo[exec->bo_index[0]]; + if (ib_access_end > ib->base.size) { + DRM_ERROR("IB access out of bounds (%d/%d)\n", + ib_access_end, ib->base.size); + return -EINVAL; + } + + *(uint32_t *)(validated + 5) = + *(uint32_t *)(untrusted + 5) + ib->paddr; + + return 0; +} + +static int +validate_gl_shader_state(VALIDATE_ARGS) +{ + struct drm_gem_cma_object *shader; + uint32_t i = exec->shader_state_count++; + + shader = exec->bo[exec->bo_index[0]]; + + if (i >= exec->shader_state_size) { + DRM_ERROR("More requests for shader states than declared\n"); + return -EINVAL; + } + + exec->shader_state[i] = *(uint32_t *)untrusted; + if (exec->shader_state[i] & 15) { + DRM_ERROR("shader record must by 16-byte aligned\n"); + return -EINVAL; + } + *(uint32_t *)validated = exec->shader_state[i] + exec->shader_paddr; + + return 0; +} + +static int +validate_nv_shader_state(VALIDATE_ARGS) +{ + uint32_t i = exec->shader_state_count++; + + if (i >= exec->shader_state_size) { + DRM_ERROR("More requests for shader states than declared\n"); + return -EINVAL; + } + + /* XXX: validate alignment */ + exec->shader_state[i] = *(uint32_t *)untrusted; + *(uint32_t *)validated = exec->shader_state[i] + exec->shader_paddr; + + return 0; +} + +static int +validate_tile_binning_config(VALIDATE_ARGS) +{ + struct drm_gem_cma_object *tile_allocation; + struct drm_gem_cma_object *tile_state_data_array; + + tile_allocation = exec->bo[exec->bo_index[0]]; + tile_state_data_array = exec->bo[exec->bo_index[1]]; + + /* XXX: Validate offsets */ + *(uint32_t *)validated = + *(uint32_t *)untrusted + tile_allocation->paddr; + + *(uint32_t *)(validated + 8) = + *(uint32_t *)(untrusted + 8) + tile_state_data_array->paddr; + + return 0; +} + +static int +validate_tile_rendering_mode_config(VALIDATE_ARGS) +{ + struct drm_gem_cma_object *fbo; + + fbo = exec->bo[exec->bo_index[0]]; + + /* XXX: Validate offsets */ + *(uint32_t *)validated = + *(uint32_t *)untrusted + fbo->paddr; + + return 0; +} + +static int +validate_gem_handles(VALIDATE_ARGS) +{ + int i; + + memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index)); + + for (i = 0; i < ARRAY_SIZE(exec->bo_index); i++) { + if (exec->bo_index[i] >= exec->bo_count) { + DRM_ERROR("Validated BO index %d >= %d\n", + exec->bo_index[i], exec->bo_count); + return -EINVAL; + } + } + + return 0; +} + +static const struct cmd_info { + bool bin; + bool render; + uint16_t len; + const char *name; + int (*func)(struct exec_info *exec, void *validated, void *untrusted); +} cmd_info[] = { + [0] = { 1, 1, 1, "halt", NULL }, + [1] = { 1, 1, 1, "nop", NULL }, + [4] = { 1, 1, 1, "flush", NULL }, + [5] = { 1, 0, 1, "flush all state", NULL }, + [6] = { 1, 0, 1, "start tile binning", NULL }, + [7] = { 1, 0, 1, "increment semaphore", NULL }, + [8] = { 1, 1, 1, "wait on semaphore", NULL }, + [17] = { 1, 1, 5, "branch to sublist", validate_branch_to_sublist }, + [24] = { 0, 1, 1, "store MS resolved tile color buffer", NULL }, + [25] = { 0, 1, 1, "store MS resolved tile color buffer and EOF", NULL }, + + [28] = { 0, 1, 7, "Store Tile Buffer General", + validate_store_tile_buffer_general }, + + [32] = { 1, 1, 14, "Indexed Primitive List", + validate_indexed_prim_list }, + + /* XXX: bounds check verts? */ + [33] = { 1, 1, 10, "Vertex Array Primitives", NULL }, + + [56] = { 1, 1, 2, "primitive list format", NULL }, /* XXX: bin valid? */ + + [64] = { 1, 1, 5, "GL Shader State", validate_gl_shader_state }, + [65] = { 1, 1, 5, "NV Shader State", validate_nv_shader_state }, + + [96] = { 1, 1, 4, "configuration bits", NULL }, + [97] = { 1, 1, 5, "flat shade flags", NULL }, + [98] = { 1, 1, 5, "point size", NULL }, + [99] = { 1, 1, 5, "line width", NULL }, + [100] = { 1, 1, 3, "RHT X boundary", NULL }, + [101] = { 1, 1, 5, "Depth Offset", NULL }, + [102] = { 1, 1, 9, "Clip Window", NULL }, + [103] = { 1, 1, 5, "Viewport Offset", NULL }, + [105] = { 1, 1, 9, "Clipper XY Scaling", NULL }, + /* Note: The docs say this was also 105, but it was 106 in the + * initial userland code drop. + */ + [106] = { 1, 1, 9, "Clipper Z Scale and Offset", NULL }, + + [112] = { 1, 0, 16, "tile binning configuration", + validate_tile_binning_config }, + + /* XXX: Do we need to validate this one? It's got width/height in it. + */ + [113] = { 0, 1, 11, "tile rendering mode configuration", + validate_tile_rendering_mode_config}, + + [114] = { 0, 1, 14, "Clear Colors", NULL }, + + /* XXX: Do we need to validate here? It's got tile x/y number for + * rendering + */ + [115] = { 0, 1, 3, "Tile Coordinates", NULL }, + + [254] = { 1, 1, 9, "GEM handles", validate_gem_handles }, +}; + +int +vc4_validate_cl(struct drm_device *dev, + void *validated, + void *unvalidated, + uint32_t len, + bool is_bin, + struct exec_info *exec) +{ + uint32_t dst_offset = 0; + uint32_t src_offset = 0; + + while (src_offset < len) { + void *dst_pkt = validated + dst_offset; + void *src_pkt = unvalidated + src_offset; + u8 cmd = *(uint8_t *)src_pkt; + const struct cmd_info *info; + + if (cmd > ARRAY_SIZE(cmd_info)) { + DRM_ERROR("0x%08x: packet %d out of bounds\n", + src_offset, cmd); + return -EINVAL; + } + + info = &cmd_info[cmd]; + if (!info->name) { + DRM_ERROR("0x%08x: packet %d invalid\n", + src_offset, cmd); + return -EINVAL; + } + +#if 0 + DRM_INFO("0x%08x: packet %d (%s) size %d processing...\n", + src_offset, cmd, info->name, info->len); +#endif + + if ((is_bin && !info->bin) || + (!is_bin && !info->render)) { + DRM_ERROR("0x%08x: packet %d (%s) invalid for %s\n", + src_offset, cmd, info->name, + is_bin ? "binner" : "render"); + return -EINVAL; + } + + if (src_offset + info->len > len) { + DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " + "exceeds bounds (0x%08x)\n", + src_offset, cmd, info->name, info->len, + src_offset + len); + return -EINVAL; + } + + if (cmd != 254) + memcpy(dst_pkt, src_pkt, info->len); + + if (info->func && info->func(exec, + dst_pkt + 1, + src_pkt + 1)) { + DRM_ERROR("0x%08x: packet %d (%s) failed to " + "validate\n", + src_offset, cmd, info->name); + return -EINVAL; + } + + src_offset += info->len; + /* GEM handle loading doesn't produce HW packets. */ + if (cmd != 254) + dst_offset += info->len; + + /* When the CL hits halt, it'll stop reading anything else. */ + if (cmd == 0) + break; + } + + return 0; +} + +int +vc4_validate_shader_recs(struct drm_device *dev, + void *validated, + void *unvalidated, + uint32_t len, + struct exec_info *exec) +{ + uint32_t dst_offset = 0; + uint32_t src_offset = 0; + uint32_t i; + + /* Look, all I'm doing here is relocating the one packet in + * phire's hackdriver code. + */ + for (i = 0; i < exec->shader_state_count; i++) { + /* XXX: lots of overflow */ + uint32_t *src_handles = unvalidated + src_offset; + void *src_pkt = &src_handles[3]; + void *dst_pkt = validated + dst_offset; + uint32_t fs_code_index = src_handles[0]; + uint32_t fs_uniform_index = src_handles[1]; + uint32_t vbo_index = src_handles[2]; + struct drm_gem_cma_object *fs_code_bo, *fs_uniform_bo, *vbo; + + if (fs_code_index >= exec->bo_count) { + DRM_ERROR("FS index %d > %d\n", + fs_code_index, exec->bo_count); + return -EINVAL; + } + if (fs_uniform_index >= exec->bo_count) { + DRM_ERROR("FS index %d > %d\n", + fs_uniform_index, exec->bo_count); + return -EINVAL; + } + if (vbo_index >= exec->bo_count) { + DRM_ERROR("FS index %d > %d\n", + vbo_index, exec->bo_count); + return -EINVAL; + } + + if (src_offset + len > + src_pkt + 16 - unvalidated) { + DRM_ERROR("weird overflow of src data\n"); + return -EINVAL; + } + + fs_code_bo = exec->bo[fs_code_index]; + fs_uniform_bo = exec->bo[fs_uniform_index]; + vbo = exec->bo[vbo_index]; + + memcpy(dst_pkt, src_pkt, 16); + *(uint32_t *)(dst_pkt + 4) = + fs_code_bo->paddr + *(uint32_t *)(src_pkt + 4); + *(uint32_t *)(dst_pkt + 8) = + fs_uniform_bo->paddr + *(uint32_t *)(src_pkt + 8); + *(uint32_t *)(dst_pkt + 12) = + vbo->paddr + *(uint32_t *)(src_pkt + 12); + } + + return 0; +} diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index 7a1dedfa02d6..e286204ace0f 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -24,15 +24,22 @@ #ifndef _UAPI_VC4_DRM_H_ #define _UAPI_VC4_DRM_H_ +#include + #define DRM_VC4_SUBMIT_CL 0x00 #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) struct drm_vc4_submit_cl { - void __user *bin_thread; - void __user *render_thread; - uint32_t bin_thread_len; - uint32_t render_thread_len; + void __user *bin_cl; + void __user *render_cl; + void __user *shader_records; + void __user *bo_handles; + uint32_t bin_cl_len; + uint32_t render_cl_len; + uint32_t shader_record_len; + uint32_t shader_record_count; + uint32_t bo_handle_count; }; #endif /* _UAPI_VC4_DRM_H_ */ -- cgit v1.2.1 From 1908b5c35eebc52f64cc7f6decedcc22a931985c Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 24 Jun 2014 17:14:51 +0100 Subject: drm/vc4: Extend the validator for VS/CS. We need the GL shader packets to light up the VS/coord shader pair. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 6 +- drivers/gpu/drm/vc4/vc4_gem.c | 5 +- drivers/gpu/drm/vc4/vc4_packet.h | 194 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_validate.c | 164 +++++++++++++++++++++---------- 4 files changed, 314 insertions(+), 55 deletions(-) create mode 100644 drivers/gpu/drm/vc4/vc4_packet.h diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 2d449ad2bb80..a2eff695de68 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -112,7 +112,11 @@ struct exec_info { * it's expected to be found at. It gets read in from the * command lists. */ - uint32_t *shader_state; + struct vc4_shader_state { + uint8_t packet; + uint32_t addr; + } *shader_state; + /** How many shader states the user declared they were using. */ uint32_t shader_state_size; /** How many shader state records the validator has seen. */ diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 81eeb401d519..b1409a830f6e 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -246,12 +246,13 @@ vc4_cl_validate(struct drm_device *dev, struct drm_vc4_submit_cl *args, uint32_t shader_rec_offset = roundup(render_offset + args->render_cl_len, 16); uint32_t exec_size = shader_rec_offset + args->shader_record_len; - uint32_t temp_size = exec_size + (sizeof(uint32_t) * + uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) * args->shader_record_count); if (shader_rec_offset < render_offset || exec_size < shader_rec_offset || - args->shader_record_count >= (UINT_MAX / sizeof(uint32_t)) || + args->shader_record_count >= (UINT_MAX / + sizeof(struct vc4_shader_state)) || temp_size < exec_size) { DRM_ERROR("overflow in exec arguments\n"); goto fail; diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h new file mode 100644 index 000000000000..e7c334c55569 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_packet.h @@ -0,0 +1,194 @@ +/* + * Copyright © 2014 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef VC4_PACKET_H +#define VC4_PACKET_H + +enum vc4_packet { + VC4_PACKET_HALT = 0, + VC4_PACKET_NOP = 1, + + VC4_PACKET_FLUSH = 4, + VC4_PACKET_FLUSH_ALL = 5, + VC4_PACKET_START_TILE_BINNING = 6, + VC4_PACKET_INCREMENT_SEMAPHORE = 7, + VC4_PACKET_WAIT_ON_SEMAPHORE = 8, + + VC4_PACKET_BRANCH = 16, + VC4_PACKET_BRANCH_TO_SUB_LIST = 17, + + VC4_PACKET_STORE_MS_TILE_BUFFER = 24, + VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF = 25, + VC4_PACKET_STORE_FULL_RES_TILE_BUFFER = 26, + VC4_PACKET_LOAD_FULL_RES_TILE_BUFFER = 27, + VC4_PACKET_STORE_TILE_BUFFER_GENERAL = 28, + VC4_PACKET_LOAD_TILE_BUFFER_GENERAL = 29, + + VC4_PACKET_GL_INDEXED_PRIMITIVE = 32, + VC4_PACKET_GL_ARRAY_PRIMITIVE = 33, + + VC4_PACKET_COMPRESSED_PRIMITIVE = 48, + VC4_PACKET_CLIPPED_COMPRESSED_PRIMITIVE = 49, + + VC4_PACKET_PRIMITIVE_LIST_FORMAT = 56, + + VC4_PACKET_GL_SHADER_STATE = 64, + VC4_PACKET_NV_SHADER_STATE = 65, + VC4_PACKET_VG_SHADER_STATE = 66, + + VC4_PACKET_CONFIGURATION_BITS = 96, + VC4_PACKET_FLAT_SHADE_FLAGS = 97, + VC4_PACKET_POINT_SIZE = 98, + VC4_PACKET_LINE_WIDTH = 99, + VC4_PACKET_RHT_X_BOUNDARY = 100, + VC4_PACKET_DEPTH_OFFSET = 101, + VC4_PACKET_CLIP_WINDOW = 102, + VC4_PACKET_VIEWPORT_OFFSET = 103, + VC4_PACKET_Z_CLIPPING = 104, + VC4_PACKET_CLIPPER_XY_SCALING = 105, + VC4_PACKET_CLIPPER_Z_SCALING = 106, + + VC4_PACKET_TILE_BINNING_MODE_CONFIG = 112, + VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, + VC4_PACKET_CLEAR_COLORS = 114, + VC4_PACKET_TILE_COORDINATES = 115, + GEM_HANDLES = 254, +} __attribute__ ((__packed__)); + +/** @{ + * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL (low bits of the + * address) + */ + +#define VC4_STORE_TILE_BUFFER_DISABLE_FULL_VG_MASK_DUMP (1 << 2) +#define VC4_STORE_TILE_BUFFER_DISABLE_FULL_ZS_DUMP (1 << 1) +#define VC4_STORE_TILE_BUFFER_DISABLE_FULL_COLOR_DUMP (1 << 0) + +/** @} */ + +/** @{ byte 1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL */ +#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR (1 << 7) +#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR (1 << 6) +#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR (1 << 5) +#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP (1 << 4) + +#define VC4_STORE_TILE_BUFFER_RGBA8888 (0 << 0) +#define VC4_STORE_TILE_BUFFER_BGR565_DITHER (1 << 0) +#define VC4_STORE_TILE_BUFFER_BGR565 (2 << 0) +/** @} */ + +/** @{ byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL */ +#define VC4_STORE_TILE_BUFFER_MODE_SAMPLE0 (0 << 6) +#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4 (1 << 6) +#define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16 (2 << 6) + +#define VC4_STORE_TILE_BUFFER_FORMAT_RASTER (0 << 4) +#define VC4_STORE_TILE_BUFFER_FORMAT_T (1 << 4) +#define VC4_STORE_TILE_BUFFER_FORMAT_LT (2 << 4) + +#define VC4_STORE_TILE_BUFFER_NONE (0 << 0) +#define VC4_STORE_TILE_BUFFER_COLOR (1 << 0) +#define VC4_STORE_TILE_BUFFER_ZS (2 << 0) +#define VC4_STORE_TILE_BUFFER_Z (3 << 0) +#define VC4_STORE_TILE_BUFFER_VG_MASK (4 << 0) +#define VC4_STORE_TILE_BUFFER_FULL (5 << 0) +/** @} */ + +/* This flag is only present in NV shader state. */ +#define VC4_SHADER_FLAG_SHADED_CLIP_COORDS (1 << 3) +#define VC4_SHADER_FLAG_ENABLE_CLIPPING (1 << 2) +#define VC4_SHADER_FLAG_VS_POINT_SIZE (1 << 1) +#define VC4_SHADER_FLAG_FS_SINGLE_THREAD (1 << 0) + +/** @{ byte 2 of config bits. */ +#define VC4_CONFIG_BITS_EARLY_Z_UPDATE (1 << 1) +#define VC4_CONFIG_BITS_EARLY_Z (1 << 0) +/** @} */ + +/** @{ byte 1 of config bits. */ +#define VC4_CONFIG_BITS_Z_UPDATE (1 << 7) +/** same values in this 3-bit field as PIPE_FUNC_* */ +#define VC4_CONFIG_BITS_DEPTH_FUNC_SHIFT 4 +#define VC4_CONFIG_BITS_COVERAGE_READ_LEAVE (1 << 3) + +#define VC4_CONFIG_BITS_COVERAGE_UPDATE_NONZERO (0 << 1) +#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ODD (1 << 1) +#define VC4_CONFIG_BITS_COVERAGE_UPDATE_OR (2 << 1) +#define VC4_CONFIG_BITS_COVERAGE_UPDATE_ZERO (3 << 1) + +#define VC4_CONFIG_BITS_COVERAGE_PIPE_SELECT (1 << 0) +/** @} */ + +/** @{ byte 0 of config bits. */ +#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_NONE (0 << 6) +#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_4X (1 << 6) +#define VC4_CONFIG_BITS_RASTERIZER_OVERSAMPLE_16X (2 << 6) + +#define VC4_CONFIG_BITS_AA_POINTS_AND_LINES (1 << 4) +#define VC4_CONFIG_BITS_ENABLE_DEPTH_OFFSET (1 << 3) +#define VC4_CONFIG_BITS_CW_PRIMITIVES (1 << 2) +#define VC4_CONFIG_BITS_ENABLE_PRIM_BACK (1 << 1) +#define VC4_CONFIG_BITS_ENABLE_PRIM_FRONT (1 << 0) +/** @} */ + +/** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */ +#define VC4_BIN_CONFIG_DB_NON_MS (1 << 7) + +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_32 (0 << 5) +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_64 (1 << 5) +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128 (2 << 5) +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_256 (3 << 5) + +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32 (0 << 3) +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_64 (1 << 3) +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 (2 << 3) +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 (3 << 3) + +#define VC4_BIN_CONFIG_AUTO_INIT_TSDA (1 << 2) +#define VC4_BIN_CONFIG_TILE_BUFFER_64BIT (1 << 1) +#define VC4_BIN_CONFIG_MS_MODE_4X (1 << 0) +/** @} */ + +/** @{ bits in the last u16 of VC4_PACKET_TILE_RENDERING_MODE_CONFIG */ +#define VC4_RENDER_CONFIG_DB_NON_MS (1 << 12) +#define VC4_RENDER_CONFIG_EARLY_Z_COVERAGE_DISABLE (1 << 11) +#define VC4_RENDER_CONFIG_EARLY_Z_DIRECTION_G (1 << 10) +#define VC4_RENDER_CONFIG_COVERAGE_MODE (1 << 9) +#define VC4_RENDER_CONFIG_ENABLE_VG_MASK (1 << 8) + +#define VC4_RENDER_CONFIG_MEMORY_FORMAT_LINEAR (0 << 6) +#define VC4_RENDER_CONFIG_MEMORY_FORMAT_T (1 << 6) +#define VC4_RENDER_CONFIG_MEMORY_FORMAT_LT (2 << 6) + +#define VC4_RENDER_CONFIG_DECIMATE_MODE_1X (0 << 4) +#define VC4_RENDER_CONFIG_DECIMATE_MODE_4X (1 << 4) +#define VC4_RENDER_CONFIG_DECIMATE_MODE_16X (2 << 4) + +#define VC4_RENDER_CONFIG_FORMAT_BGR565 (0 << 2) +#define VC4_RENDER_CONFIG_FORMAT_RGBA8888 (1 << 2) +#define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED (2 << 2) + +#define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT (1 << 1) +#define VC4_RENDER_CONFIG_MS_MODE_4X (1 << 0) + +#endif /* VC4_PACKET_H */ diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 438e2257a64b..cb1c67e061f2 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -40,6 +40,7 @@ */ #include "vc4_drv.h" +#include "vc4_packet.h" #include "vc4_regs.h" #define VALIDATE_ARGS \ @@ -125,12 +126,11 @@ validate_gl_shader_state(VALIDATE_ARGS) return -EINVAL; } - exec->shader_state[i] = *(uint32_t *)untrusted; - if (exec->shader_state[i] & 15) { - DRM_ERROR("shader record must by 16-byte aligned\n"); - return -EINVAL; - } - *(uint32_t *)validated = exec->shader_state[i] + exec->shader_paddr; + exec->shader_state[i].packet = VC4_PACKET_GL_SHADER_STATE; + exec->shader_state[i].addr = *(uint32_t *)untrusted; + + *(uint32_t *)validated = exec->shader_state[i].addr + + exec->shader_paddr; return 0; } @@ -145,9 +145,17 @@ validate_nv_shader_state(VALIDATE_ARGS) return -EINVAL; } - /* XXX: validate alignment */ - exec->shader_state[i] = *(uint32_t *)untrusted; - *(uint32_t *)validated = exec->shader_state[i] + exec->shader_paddr; + exec->shader_state[i].packet = VC4_PACKET_NV_SHADER_STATE; + exec->shader_state[i].addr = *(uint32_t *)untrusted; + + if (exec->shader_state[i].addr & 15) { + DRM_ERROR("NV shader state address 0x%08x misaligned\n", + exec->shader_state[i].addr); + return -EINVAL; + } + + *(uint32_t *)validated = + exec->shader_state[i].addr + exec->shader_paddr; return 0; } @@ -343,6 +351,81 @@ vc4_validate_cl(struct drm_device *dev, return 0; } +static int +validate_shader_rec(struct drm_device *dev, + struct exec_info *exec, + void *validated, + void *unvalidated, + uint32_t len, + struct vc4_shader_state *state) +{ + uint32_t *src_handles = unvalidated; + void *src_pkt; + void *dst_pkt = validated; + static const int gl_bo_offsets[] = { + 4, 8, /* fs code, ubo */ + 16, 20, /* vs code, ubo */ + 28, 32, /* cs code, ubo */ + }; + static const int nv_bo_offsets[] = { + 4, 8, /* fs code, ubo */ + 12, /* vbo */ + }; + struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_bo_offsets) + 8]; + const int *bo_offsets; + uint32_t nr_attributes = 0, nr_bo, packet_size; + int i; + + if (state->packet == VC4_PACKET_NV_SHADER_STATE) { + bo_offsets = nv_bo_offsets; + nr_bo = ARRAY_SIZE(nv_bo_offsets); + + packet_size = 16; + } else { + bo_offsets = gl_bo_offsets; + nr_bo = ARRAY_SIZE(gl_bo_offsets); + + nr_attributes = state->addr & 0x7; + if (nr_attributes == 0) + nr_attributes = 8; + packet_size = 36 + nr_attributes * 8; + } + if ((nr_bo + nr_attributes) * 4 + packet_size > len) { + DRM_ERROR("overflowed shader packet read " + "(handles %d, packet %d, len %d)\n", + (nr_bo + nr_attributes) * 4, packet_size, len); + return -EINVAL; + } + + src_pkt = unvalidated + 4 * (nr_bo + nr_attributes); + memcpy(dst_pkt, src_pkt, packet_size); + + for (i = 0; i < nr_bo + nr_attributes; i++) { + if (src_handles[i] >= exec->bo_count) { + DRM_ERROR("shader rec bo index %d > %d\n", + src_handles[i], exec->bo_count); + return -EINVAL; + } + bo[i] = exec->bo[src_handles[i]]; + } + + for (i = 0; i < nr_bo; i++) { + /* XXX: validation */ + uint32_t o = bo_offsets[i]; + *(uint32_t *)(dst_pkt + o) = + bo[i]->paddr + *(uint32_t *)(src_pkt + o); + } + + for (i = 0; i < nr_attributes; i++) { + /* XXX: validation */ + uint32_t o = 36 + i * 8; + *(uint32_t *)(dst_pkt + o) = + bo[nr_bo + i]->paddr + *(uint32_t *)(src_pkt + o); + } + + return 0; +} + int vc4_validate_shader_recs(struct drm_device *dev, void *validated, @@ -353,54 +436,31 @@ vc4_validate_shader_recs(struct drm_device *dev, uint32_t dst_offset = 0; uint32_t src_offset = 0; uint32_t i; + int ret = 0; - /* Look, all I'm doing here is relocating the one packet in - * phire's hackdriver code. - */ for (i = 0; i < exec->shader_state_count; i++) { - /* XXX: lots of overflow */ - uint32_t *src_handles = unvalidated + src_offset; - void *src_pkt = &src_handles[3]; - void *dst_pkt = validated + dst_offset; - uint32_t fs_code_index = src_handles[0]; - uint32_t fs_uniform_index = src_handles[1]; - uint32_t vbo_index = src_handles[2]; - struct drm_gem_cma_object *fs_code_bo, *fs_uniform_bo, *vbo; - - if (fs_code_index >= exec->bo_count) { - DRM_ERROR("FS index %d > %d\n", - fs_code_index, exec->bo_count); - return -EINVAL; - } - if (fs_uniform_index >= exec->bo_count) { - DRM_ERROR("FS index %d > %d\n", - fs_uniform_index, exec->bo_count); - return -EINVAL; - } - if (vbo_index >= exec->bo_count) { - DRM_ERROR("FS index %d > %d\n", - vbo_index, exec->bo_count); + if ((exec->shader_state[i].addr & ~0xf) != + (validated - exec->exec_bo->vaddr - + (exec->shader_paddr - exec->exec_bo->paddr))) { + DRM_ERROR("unexpected shader rec offset: " + "0x%08x vs 0x%08x\n", + exec->shader_state[i].addr & ~0xf, + (int)(validated - + exec->exec_bo->vaddr - + (exec->shader_paddr - + exec->exec_bo->paddr))); return -EINVAL; } - if (src_offset + len > - src_pkt + 16 - unvalidated) { - DRM_ERROR("weird overflow of src data\n"); - return -EINVAL; - } - - fs_code_bo = exec->bo[fs_code_index]; - fs_uniform_bo = exec->bo[fs_uniform_index]; - vbo = exec->bo[vbo_index]; - - memcpy(dst_pkt, src_pkt, 16); - *(uint32_t *)(dst_pkt + 4) = - fs_code_bo->paddr + *(uint32_t *)(src_pkt + 4); - *(uint32_t *)(dst_pkt + 8) = - fs_uniform_bo->paddr + *(uint32_t *)(src_pkt + 8); - *(uint32_t *)(dst_pkt + 12) = - vbo->paddr + *(uint32_t *)(src_pkt + 12); + ret = validate_shader_rec(dev, exec, + validated + dst_offset, + unvalidated + src_offset, + len - src_offset, + &exec->shader_state[i]); + if (ret) + return ret; + /* XXX: incr dst/src offset */ } - return 0; + return ret; } -- cgit v1.2.1 From d10553ad5cbd77825ad12a5c8e456273ee664d79 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 18 Jul 2014 16:08:26 -0700 Subject: drm/vc4: Improve support for load/store tile buffers Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_packet.h | 50 +++++++++++++++++++++++--------------- drivers/gpu/drm/vc4/vc4_validate.c | 18 +++++++------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h index e7c334c55569..cc3786677782 100644 --- a/drivers/gpu/drm/vc4/vc4_packet.h +++ b/drivers/gpu/drm/vc4/vc4_packet.h @@ -76,44 +76,56 @@ enum vc4_packet { } __attribute__ ((__packed__)); /** @{ - * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL (low bits of the - * address) + * + * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and + * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address) */ -#define VC4_STORE_TILE_BUFFER_DISABLE_FULL_VG_MASK_DUMP (1 << 2) -#define VC4_STORE_TILE_BUFFER_DISABLE_FULL_ZS_DUMP (1 << 1) -#define VC4_STORE_TILE_BUFFER_DISABLE_FULL_COLOR_DUMP (1 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK (1 << 2) +#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS (1 << 1) +#define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR (1 << 0) /** @} */ -/** @{ byte 1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL */ +/** @{ + * + * byte 1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and + * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL + */ #define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR (1 << 7) #define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR (1 << 6) #define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR (1 << 5) #define VC4_STORE_TILE_BUFFER_DISABLE_SWAP (1 << 4) -#define VC4_STORE_TILE_BUFFER_RGBA8888 (0 << 0) -#define VC4_STORE_TILE_BUFFER_BGR565_DITHER (1 << 0) -#define VC4_STORE_TILE_BUFFER_BGR565 (2 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_RGBA8888 (0 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER (1 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_BGR565 (2 << 0) /** @} */ -/** @{ byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL */ +/** @{ + * + * byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and + * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL + */ #define VC4_STORE_TILE_BUFFER_MODE_SAMPLE0 (0 << 6) #define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4 (1 << 6) #define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16 (2 << 6) -#define VC4_STORE_TILE_BUFFER_FORMAT_RASTER (0 << 4) -#define VC4_STORE_TILE_BUFFER_FORMAT_T (1 << 4) -#define VC4_STORE_TILE_BUFFER_FORMAT_LT (2 << 4) +#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_RASTER (0 << 4) +#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_T (1 << 4) +#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_LT (2 << 4) -#define VC4_STORE_TILE_BUFFER_NONE (0 << 0) -#define VC4_STORE_TILE_BUFFER_COLOR (1 << 0) -#define VC4_STORE_TILE_BUFFER_ZS (2 << 0) -#define VC4_STORE_TILE_BUFFER_Z (3 << 0) -#define VC4_STORE_TILE_BUFFER_VG_MASK (4 << 0) -#define VC4_STORE_TILE_BUFFER_FULL (5 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_NONE (0 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_COLOR (1 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_ZS (2 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_Z (3 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_VG_MASK (4 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_FULL (5 << 0) /** @} */ +#define VC4_INDEX_BUFFER_U8 (0 << 4) +#define VC4_INDEX_BUFFER_U16 (1 << 4) + /* This flag is only present in NV shader state. */ #define VC4_SHADER_FLAG_SHADED_CLIP_COORDS (1 << 3) #define VC4_SHADER_FLAG_ENABLE_CLIPPING (1 << 2) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index cb1c67e061f2..a68d331f0497 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -64,19 +64,17 @@ validate_branch_to_sublist(VALIDATE_ARGS) } static int -validate_store_tile_buffer_general(VALIDATE_ARGS) +validate_loadstore_tile_buffer_general(VALIDATE_ARGS) { - struct drm_gem_cma_object *fbo; + uint32_t packet_b0 = *(uint8_t *)(untrusted + 0); + struct drm_gem_cma_object *fbo = exec->bo[exec->bo_index[0]]; - /* XXX: Validate address offset */ + if ((packet_b0 & 0xf) == VC4_LOADSTORE_TILE_BUFFER_NONE) + return 0; - fbo = exec->bo[exec->bo_index[0]]; - - /* XXX */ - /* + /* XXX: Validate address offset */ *(uint32_t *)(validated + 2) = *(uint32_t *)(untrusted + 2) + fbo->paddr; - */ return 0; } @@ -230,7 +228,9 @@ static const struct cmd_info { [25] = { 0, 1, 1, "store MS resolved tile color buffer and EOF", NULL }, [28] = { 0, 1, 7, "Store Tile Buffer General", - validate_store_tile_buffer_general }, + validate_loadstore_tile_buffer_general }, + [29] = { 0, 1, 7, "Load Tile Buffer General", + validate_loadstore_tile_buffer_general }, [32] = { 1, 1, 14, "Indexed Primitive List", validate_indexed_prim_list }, -- cgit v1.2.1 From d91801885a55146f4145233f4a14ddb6815541cb Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 21 Jul 2014 10:42:01 -0700 Subject: drm/vc4: Add some comments to vc4_drm.h Signed-off-by: Eric Anholt --- include/uapi/drm/vc4_drm.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index e286204ace0f..6cccad5f6c8b 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -30,15 +30,68 @@ #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) +/** + * Structure for submitting commands to the 3D engine. + * + * Drivers typically use GPU BOs to store batchbuffers / command lists and + * their associated state. However, because the VC4 lacks an MMU, we have to + * do validation of memory accesses by the GPU commands. If we were to store + * our commands in BOs, we'd need to do uncached readback from them to do the + * validation process, which is too expensive. Instead, userspace accumulates + * commands and associated state in plain memory, then the kernel copies the + * data to its own address space, and then validates and stores it in a GPU + * BO. + */ struct drm_vc4_submit_cl { + /** + * Pointer to the binner command list. + * + * This is the first set of commands executed, which runs the + * coordinate shader to determine where primitives land on the screen, + * then writes out the state updates and draw calls necessary per tile + * to the tile allocation BO. + */ void __user *bin_cl; + + /** + * Pointer to the render command list. + * + * The render command list contains a set of packets to load the + * current tile's state (reading from memory, or just clearing it) + * into the GPU, then call into the tile allocation BO to run the + * stored rendering for that tile, then store the tile's state back to + * memory. + */ void __user *render_cl; + + /** Pointer to the shader records. + * + * Shader records are the structures read by the hardware that contain + * pointers to uniforms, shaders, and vertex attributes. The + * reference to the shader record has enough information to determine + * how many pointers are necessary (fixed number for shaders/uniforms, + * and an attribute count), so those BO indices into bo_handles are + * just stored as uint32_ts before each shader record passed in. + */ void __user *shader_records; void __user *bo_handles; + + /** Size in bytes of the binner command list. */ uint32_t bin_cl_len; + /** Size in bytes of the render command list */ uint32_t render_cl_len; + /** Size in bytes of the list of shader records. */ uint32_t shader_record_len; + /** + * Number of shader records. + * + * This could just be computed from the contents of shader_records, + * but it keeps the kernel from having to resize various allocations + * it makes. + */ uint32_t shader_record_count; + + /** Number of BO handles passed in (size is that times 4). */ uint32_t bo_handle_count; }; -- cgit v1.2.1 From 51bd38241bdc3cee795668518dea68a567df72c9 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 5 Aug 2014 10:04:10 -0700 Subject: drm/vc4: Introduce shader validation and better command stream validation. This is an import of the code I developed up in Mesa against the simulator. It rewrites the kernel ABI to support validation of uniforms, which is required for relocating references to texture contents. Note that the docs in the shader validator are aspirational -- we don't yet handle the force-unmapping of shader BO contents, nor do we do any caching. v2: Don't forget to git add vc4_qpu_defines.h Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/Makefile | 1 + drivers/gpu/drm/vc4/vc4_drv.h | 108 +++- drivers/gpu/drm/vc4/vc4_gem.c | 75 +-- drivers/gpu/drm/vc4/vc4_packet.h | 32 +- drivers/gpu/drm/vc4/vc4_qpu_defines.h | 268 +++++++++ drivers/gpu/drm/vc4/vc4_validate.c | 855 +++++++++++++++++++++++------ drivers/gpu/drm/vc4/vc4_validate_shaders.c | 318 +++++++++++ include/uapi/drm/vc4_drm.h | 58 +- 8 files changed, 1472 insertions(+), 243 deletions(-) create mode 100644 drivers/gpu/drm/vc4/vc4_qpu_defines.h create mode 100644 drivers/gpu/drm/vc4/vc4_validate_shaders.c diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index c35c07dfec8d..09dad06a75b9 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -14,6 +14,7 @@ vc4-y := \ vc4_plane.o \ vc4_v3d.o \ vc4_validate.o \ + vc4_validate_shaders.o \ $() vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index a2eff695de68..e741627211a1 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -84,25 +84,36 @@ to_vc4_plane(struct drm_plane *plane) #define HVS_READ(offset) readl(vc4->hvs->regs + offset) #define HVS_WRITE(offset, val) writel(val, vc4->hvs->regs + offset) +enum vc4_bo_mode { + VC4_MODE_UNDECIDED, + VC4_MODE_TILE_ALLOC, + VC4_MODE_TSDA, + VC4_MODE_RENDER, + VC4_MODE_SHADER, +}; + +struct vc4_bo_exec_state { + struct drm_gem_cma_object *bo; + enum vc4_bo_mode mode; +}; + struct exec_info { + /* Kernel-space copy of the ioctl arguments */ + struct drm_vc4_submit_cl *args; + /* This is the array of BOs that were looked up at the start of exec. * Command validation will use indices into this array. */ - struct drm_gem_cma_object **bo; + struct vc4_bo_exec_state *bo; uint32_t bo_count; - /* Current indices into @bo loaded by the non-hardware packet - * that passes in indices. This can be used even without - * checking that we've seen one of those packets, because - * @bo_count is always >= 1, and this struct is initialized to - * 0. + /* Current unvalidated indices into @bo loaded by the non-hardware + * VC4_PACKET_GEM_HANDLES. */ uint32_t bo_index[2]; - uint32_t max_width, max_height; - /** - * This is the BO where we store the validated command lists - * and shader records. + /* This is the BO where we store the validated command lists, shader + * records, and uniforms. */ struct drm_gem_cma_object *exec_bo; @@ -115,6 +126,10 @@ struct exec_info { struct vc4_shader_state { uint8_t packet; uint32_t addr; + /* Maximum vertex index referenced by any primitive using this + * shader state. + */ + uint32_t max_index; } *shader_state; /** How many shader states the user declared they were using. */ @@ -122,13 +137,74 @@ struct exec_info { /** How many shader state records the validator has seen. */ uint32_t shader_state_count; + bool found_tile_binning_mode_config_packet; + bool found_tile_rendering_mode_config_packet; + bool found_start_tile_binning_packet; + uint8_t bin_tiles_x, bin_tiles_y; + uint32_t fb_width, fb_height; + uint32_t tile_alloc_init_block_size; + struct drm_gem_cma_object *tile_alloc_bo; + /** * Computed addresses pointing into exec_bo where we start the * bin thread (ct0) and render thread (ct1). */ uint32_t ct0ca, ct0ea; uint32_t ct1ca, ct1ea; - uint32_t shader_paddr; + + /* Pointers to the shader recs. These paddr gets incremented as CL + * packets are relocated in validate_gl_shader_state, and the vaddrs + * (u and v) get incremented and size decremented as the shader recs + * themselves are validated. + */ + void *shader_rec_u; + void *shader_rec_v; + uint32_t shader_rec_p; + uint32_t shader_rec_size; + + /* Pointers to the uniform data. These pointers are incremented, and + * size decremented, as each batch of uniforms is uploaded. + */ + void *uniforms_u; + void *uniforms_v; + uint32_t uniforms_p; + uint32_t uniforms_size; +}; + +/** + * struct vc4_texture_sample_info - saves the offsets into the UBO for texture + * setup parameters. + * + * This will be used at draw time to relocate the reference to the texture + * contents in p0, and validate that the offset combined with + * width/height/stride/etc. from p1 and p2/p3 doesn't sample outside the BO. + * Note that the hardware treats unprovided config parameters as 0, so not all + * of them need to be set up for every texure sample, and we'll store ~0 as + * the offset to mark the unused ones. + * + * See the VC4 3D architecture guide page 41 ("Texture and Memory Lookup Unit + * Setup") for definitions of the texture parameters. + */ +struct vc4_texture_sample_info { + uint32_t p_offset[4]; +}; + +/** + * struct vc4_validated_shader_info - information about validated shaders that + * needs to be used from command list validation. + * + * For a given shader, each time a shader state record references it, we need + * to verify that the shader doesn't read more uniforms than the shader state + * record's uniform BO pointer can provide, and we need to apply relocations + * and validate the shader state record's uniforms that define the texture + * samples. + */ +struct vc4_validated_shader_info +{ + uint32_t uniforms_size; + uint32_t uniforms_src_size; + uint32_t num_texture_samples; + struct vc4_texture_sample_info *texture_samples; }; /* vc4_bo.c */ @@ -197,8 +273,8 @@ vc4_validate_cl(struct drm_device *dev, struct exec_info *exec); int -vc4_validate_shader_recs(struct drm_device *dev, - void *validated, - void *unvalidated, - uint32_t len, - struct exec_info *exec); +vc4_validate_shader_recs(struct drm_device *dev, struct exec_info *exec); + +struct vc4_validated_shader_info * +vc4_validate_shader(struct drm_gem_cma_object *shader_obj, + uint32_t start_offset); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index b1409a830f6e..854216f92482 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -179,9 +179,9 @@ vc4_submit(struct drm_device *dev, struct exec_info *args) static int vc4_cl_lookup_bos(struct drm_device *dev, struct drm_file *file_priv, - struct drm_vc4_submit_cl *args, struct exec_info *exec) { + struct drm_vc4_submit_cl *args = exec->args; uint32_t *handles; int ret = 0; int i; @@ -196,7 +196,7 @@ vc4_cl_lookup_bos(struct drm_device *dev, return -EINVAL; } - exec->bo = kcalloc(exec->bo_count, sizeof(struct drm_gem_object *), + exec->bo = kcalloc(exec->bo_count, sizeof(struct vc4_bo_exec_state), GFP_KERNEL); if (!exec->bo) { DRM_ERROR("Failed to allocate validated BO pointers\n"); @@ -226,7 +226,7 @@ vc4_cl_lookup_bos(struct drm_device *dev, ret = -EINVAL; goto fail; } - exec->bo[i] = (struct drm_gem_cma_object *)bo; + exec->bo[i].bo = (struct drm_gem_cma_object *)bo; } fail: @@ -235,23 +235,25 @@ fail: } static int -vc4_cl_validate(struct drm_device *dev, struct drm_vc4_submit_cl *args, - struct exec_info *exec) +vc4_cl_validate(struct drm_device *dev, struct exec_info *exec) { + struct drm_vc4_submit_cl *args = exec->args; void *temp = NULL; - void *bin, *render, *shader_rec; + void *bin, *render; int ret = 0; uint32_t bin_offset = 0; - uint32_t render_offset = bin_offset + args->bin_cl_len; + uint32_t render_offset = bin_offset + args->bin_cl_size; uint32_t shader_rec_offset = roundup(render_offset + - args->render_cl_len, 16); - uint32_t exec_size = shader_rec_offset + args->shader_record_len; + args->render_cl_size, 16); + uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size; + uint32_t exec_size = uniforms_offset + args->uniforms_size; uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) * - args->shader_record_count); + args->shader_rec_count); if (shader_rec_offset < render_offset || - exec_size < shader_rec_offset || - args->shader_record_count >= (UINT_MAX / + uniforms_offset < shader_rec_offset || + exec_size < uniforms_offset || + args->shader_rec_count >= (UINT_MAX / sizeof(struct vc4_shader_state)) || temp_size < exec_size) { DRM_ERROR("overflow in exec arguments\n"); @@ -274,29 +276,37 @@ vc4_cl_validate(struct drm_device *dev, struct drm_vc4_submit_cl *args, } bin = temp + bin_offset; render = temp + render_offset; - shader_rec = temp + shader_rec_offset; + exec->shader_rec_u = temp + shader_rec_offset; + exec->uniforms_u = temp + uniforms_offset; exec->shader_state = temp + exec_size; - exec->shader_state_size = args->shader_record_count; + exec->shader_state_size = args->shader_rec_count; - ret = copy_from_user(bin, args->bin_cl, args->bin_cl_len); + ret = copy_from_user(bin, args->bin_cl, args->bin_cl_size); if (ret) { DRM_ERROR("Failed to copy in bin cl\n"); goto fail; } - ret = copy_from_user(render, args->render_cl, args->render_cl_len); + ret = copy_from_user(render, args->render_cl, args->render_cl_size); if (ret) { DRM_ERROR("Failed to copy in render cl\n"); goto fail; } - ret = copy_from_user(shader_rec, args->shader_records, - args->shader_record_len); + ret = copy_from_user(exec->shader_rec_u, args->shader_rec, + args->shader_rec_size); if (ret) { DRM_ERROR("Failed to copy in shader recs\n"); goto fail; } + ret = copy_from_user(exec->uniforms_u, args->uniforms, + args->uniforms_size); + if (ret) { + DRM_ERROR("Failed to copy in uniforms cl\n"); + goto fail; + } + exec->exec_bo = drm_gem_cma_create(dev, exec_size); if (IS_ERR(exec->exec_bo)) { DRM_ERROR("Couldn't allocate BO for exec\n"); @@ -306,15 +316,20 @@ vc4_cl_validate(struct drm_device *dev, struct drm_vc4_submit_cl *args, } exec->ct0ca = exec->exec_bo->paddr + bin_offset; - exec->ct0ea = exec->ct0ca + args->bin_cl_len; exec->ct1ca = exec->exec_bo->paddr + render_offset; - exec->ct1ea = exec->ct1ca + args->render_cl_len; - exec->shader_paddr = exec->exec_bo->paddr + shader_rec_offset; + + exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset; + exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset; + exec->shader_rec_size = args->shader_rec_size; + + exec->uniforms_v = exec->exec_bo->vaddr + uniforms_offset; + exec->uniforms_p = exec->exec_bo->paddr + uniforms_offset; + exec->uniforms_size = args->uniforms_size; ret = vc4_validate_cl(dev, exec->exec_bo->vaddr + bin_offset, bin, - args->bin_cl_len, + args->bin_cl_size, true, exec); if (ret) @@ -323,17 +338,13 @@ vc4_cl_validate(struct drm_device *dev, struct drm_vc4_submit_cl *args, ret = vc4_validate_cl(dev, exec->exec_bo->vaddr + render_offset, render, - args->render_cl_len, + args->render_cl_size, false, exec); if (ret) goto fail; - ret = vc4_validate_shader_recs(dev, - exec->exec_bo->vaddr + shader_rec_offset, - shader_rec, - args->shader_record_len, - exec); + ret = vc4_validate_shader_recs(dev, exec); fail: kfree(temp); @@ -349,20 +360,20 @@ int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_vc4_submit_cl *args = data; struct exec_info exec; int ret; int i; memset(&exec, 0, sizeof(exec)); + exec.args = data; mutex_lock(&dev->struct_mutex); - ret = vc4_cl_lookup_bos(dev, file_priv, args, &exec); + ret = vc4_cl_lookup_bos(dev, file_priv, &exec); if (ret) goto fail; - ret = vc4_cl_validate(dev, args, &exec); + ret = vc4_cl_validate(dev, &exec); if (ret) goto fail; @@ -375,7 +386,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, fail: if (exec.bo) { for (i = 0; i < exec.bo_count; i++) - drm_gem_object_unreference(&exec.bo[i]->base); + drm_gem_object_unreference(&exec.bo[i].bo->base); kfree(exec.bo); } diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h index cc3786677782..e455c2fe76a5 100644 --- a/drivers/gpu/drm/vc4/vc4_packet.h +++ b/drivers/gpu/drm/vc4/vc4_packet.h @@ -72,9 +72,24 @@ enum vc4_packet { VC4_PACKET_TILE_RENDERING_MODE_CONFIG = 113, VC4_PACKET_CLEAR_COLORS = 114, VC4_PACKET_TILE_COORDINATES = 115, - GEM_HANDLES = 254, + + /* Not an actual hardware packet -- this is what we use to put + * references to GEM bos in the command stream, since we need the u32 + * int the actual address packet in order to store the offset from the + * start of the BO. + */ + VC4_PACKET_GEM_HANDLES = 254, } __attribute__ ((__packed__)); +/** @{ + * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and + * VC4_PACKET_TILE_RENDERING_MODE_CONFIG. +*/ +#define VC4_TILING_FORMAT_LINEAR 0 +#define VC4_TILING_FORMAT_T 1 +#define VC4_TILING_FORMAT_LT 2 +/** @} */ + /** @{ * * byte 2 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and @@ -100,6 +115,7 @@ enum vc4_packet { #define VC4_LOADSTORE_TILE_BUFFER_RGBA8888 (0 << 0) #define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER (1 << 0) #define VC4_LOADSTORE_TILE_BUFFER_BGR565 (2 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_MASK (3 << 0) /** @} */ /** @{ @@ -111,9 +127,10 @@ enum vc4_packet { #define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4 (1 << 6) #define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16 (2 << 6) -#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_RASTER (0 << 4) -#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_T (1 << 4) -#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_LT (2 << 4) +/** The values of the field are VC4_TILING_FORMAT_* */ +#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK (3 << 4) +#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 4 + #define VC4_LOADSTORE_TILE_BUFFER_NONE (0 << 0) #define VC4_LOADSTORE_TILE_BUFFER_COLOR (1 << 0) @@ -188,9 +205,9 @@ enum vc4_packet { #define VC4_RENDER_CONFIG_COVERAGE_MODE (1 << 9) #define VC4_RENDER_CONFIG_ENABLE_VG_MASK (1 << 8) -#define VC4_RENDER_CONFIG_MEMORY_FORMAT_LINEAR (0 << 6) -#define VC4_RENDER_CONFIG_MEMORY_FORMAT_T (1 << 6) -#define VC4_RENDER_CONFIG_MEMORY_FORMAT_LT (2 << 6) +/** The values of the field are VC4_TILING_FORMAT_* */ +#define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK (3 << 6) +#define VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT 6 #define VC4_RENDER_CONFIG_DECIMATE_MODE_1X (0 << 4) #define VC4_RENDER_CONFIG_DECIMATE_MODE_4X (1 << 4) @@ -199,6 +216,7 @@ enum vc4_packet { #define VC4_RENDER_CONFIG_FORMAT_BGR565 (0 << 2) #define VC4_RENDER_CONFIG_FORMAT_RGBA8888 (1 << 2) #define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED (2 << 2) +#define VC4_RENDER_CONFIG_FORMAT_MASK (3 << 2) #define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT (1 << 1) #define VC4_RENDER_CONFIG_MS_MODE_4X (1 << 0) diff --git a/drivers/gpu/drm/vc4/vc4_qpu_defines.h b/drivers/gpu/drm/vc4/vc4_qpu_defines.h new file mode 100644 index 000000000000..e47c994d36bf --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_qpu_defines.h @@ -0,0 +1,268 @@ +/* + * Copyright © 2014 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef VC4_QPU_DEFINES_H +#define VC4_QPU_DEFINES_H + +enum qpu_op_add { + QPU_A_NOP, + QPU_A_FADD, + QPU_A_FSUB, + QPU_A_FMIN, + QPU_A_FMAX, + QPU_A_FMINABS, + QPU_A_FMAXABS, + QPU_A_FTOI, + QPU_A_ITOF, + QPU_A_ADD = 12, + QPU_A_SUB, + QPU_A_SHR, + QPU_A_ASR, + QPU_A_ROR, + QPU_A_SHL, + QPU_A_MIN, + QPU_A_MAX, + QPU_A_AND, + QPU_A_OR, + QPU_A_XOR, + QPU_A_NOT, + QPU_A_CLZ, + QPU_A_V8ADDS = 30, + QPU_A_V8SUBS = 31, +}; + +enum qpu_op_mul { + QPU_M_NOP, + QPU_M_FMUL, + QPU_M_MUL24, + QPU_M_V8MULD, + QPU_M_V8MIN, + QPU_M_V8MAX, + QPU_M_V8ADDS, + QPU_M_V8SUBS, +}; + +enum qpu_raddr { + QPU_R_FRAG_PAYLOAD_ZW = 15, /* W for A file, Z for B file */ + /* 0-31 are the plain regfile a or b fields */ + QPU_R_UNIF = 32, + QPU_R_VARY = 35, + QPU_R_ELEM_QPU = 38, + QPU_R_NOP, + QPU_R_XY_PIXEL_COORD = 41, + QPU_R_MS_REV_FLAGS = 41, + QPU_R_VPM = 48, + QPU_R_VPM_LD_BUSY, + QPU_R_VPM_LD_WAIT, + QPU_R_MUTEX_ACQUIRE, +}; + +enum qpu_waddr { + /* 0-31 are the plain regfile a or b fields */ + QPU_W_ACC0 = 32, /* aka r0 */ + QPU_W_ACC1, + QPU_W_ACC2, + QPU_W_ACC3, + QPU_W_TMU_NOSWAP, + QPU_W_ACC5, + QPU_W_HOST_INT, + QPU_W_NOP, + QPU_W_UNIFORMS_ADDRESS, + QPU_W_QUAD_XY, /* X for regfile a, Y for regfile b */ + QPU_W_MS_FLAGS = 42, + QPU_W_REV_FLAG = 42, + QPU_W_TLB_STENCIL_SETUP = 43, + QPU_W_TLB_Z, + QPU_W_TLB_COLOR_MS, + QPU_W_TLB_COLOR_ALL, + QPU_W_TLB_ALPHA_MASK, + QPU_W_VPM, + QPU_W_VPMVCD_SETUP, /* LD for regfile a, ST for regfile b */ + QPU_W_VPM_ADDR, /* LD for regfile a, ST for regfile b */ + QPU_W_MUTEX_RELEASE, + QPU_W_SFU_RECIP, + QPU_W_SFU_RECIPSQRT, + QPU_W_SFU_EXP, + QPU_W_SFU_LOG, + QPU_W_TMU0_S, + QPU_W_TMU0_T, + QPU_W_TMU0_R, + QPU_W_TMU0_B, + QPU_W_TMU1_S, + QPU_W_TMU1_T, + QPU_W_TMU1_R, + QPU_W_TMU1_B, +}; + +enum qpu_sig_bits { + QPU_SIG_SW_BREAKPOINT, + QPU_SIG_NONE, + QPU_SIG_THREAD_SWITCH, + QPU_SIG_PROG_END, + QPU_SIG_WAIT_FOR_SCOREBOARD, + QPU_SIG_SCOREBOARD_UNLOCK, + QPU_SIG_LAST_THREAD_SWITCH, + QPU_SIG_COVERAGE_LOAD, + QPU_SIG_COLOR_LOAD, + QPU_SIG_COLOR_LOAD_END, + QPU_SIG_LOAD_TMU0, + QPU_SIG_LOAD_TMU1, + QPU_SIG_ALPHA_MASK_LOAD, + QPU_SIG_SMALL_IMM, + QPU_SIG_LOAD_IMM, + QPU_SIG_BRANCH +}; + +enum qpu_mux { + /* hardware mux values */ + QPU_MUX_R0, + QPU_MUX_R1, + QPU_MUX_R2, + QPU_MUX_R3, + QPU_MUX_R4, + QPU_MUX_R5, + QPU_MUX_A, + QPU_MUX_B, + + /* non-hardware mux values */ + QPU_MUX_IMM, +}; + +enum qpu_cond { + QPU_COND_NEVER, + QPU_COND_ALWAYS, + QPU_COND_ZS, + QPU_COND_ZC, + QPU_COND_NS, + QPU_COND_NC, + QPU_COND_CS, + QPU_COND_CC, +}; + +enum qpu_pack_mul { + QPU_PACK_MUL_NOP, + QPU_PACK_MUL_8888 = 3, /* replicated to each 8 bits of the 32-bit dst. */ + QPU_PACK_MUL_8A, + QPU_PACK_MUL_8B, + QPU_PACK_MUL_8C, + QPU_PACK_MUL_8D, +}; + +enum qpu_pack_a { + QPU_PACK_A_NOP, + /* convert to 16 bit float if float input, or to int16. */ + QPU_PACK_A_16A, + QPU_PACK_A_16B, + /* replicated to each 8 bits of the 32-bit dst. */ + QPU_PACK_A_8888, + /* Convert to 8-bit unsigned int. */ + QPU_PACK_A_8A, + QPU_PACK_A_8B, + QPU_PACK_A_8C, + QPU_PACK_A_8D, + + /* Saturating variants of the previous instructions. */ + QPU_PACK_A_32_SAT, /* int-only */ + QPU_PACK_A_16A_SAT, /* int or float */ + QPU_PACK_A_16B_SAT, + QPU_PACK_A_8888_SAT, + QPU_PACK_A_8A_SAT, + QPU_PACK_A_8B_SAT, + QPU_PACK_A_8C_SAT, + QPU_PACK_A_8D_SAT, +}; + +enum qpu_unpack_r4 { + QPU_UNPACK_R4_NOP, + QPU_UNPACK_R4_F16A_TO_F32, + QPU_UNPACK_R4_F16B_TO_F32, + QPU_UNPACK_R4_8D_REP, + QPU_UNPACK_R4_8A, + QPU_UNPACK_R4_8B, + QPU_UNPACK_R4_8C, + QPU_UNPACK_R4_8D, +}; + +#define QPU_MASK(high, low) ((((uint64_t)1<<((high)-(low)+1))-1)<<(low)) +/* Using the GNU statement expression extension */ +#define QPU_SET_FIELD(value, field) \ + ({ \ + uint64_t fieldval = (uint64_t)(value) << field ## _SHIFT; \ + assert((fieldval & ~ field ## _MASK) == 0); \ + fieldval & field ## _MASK; \ + }) + +#define QPU_GET_FIELD(word, field) ((uint32_t)(((word) & field ## _MASK) >> field ## _SHIFT)) + +#define QPU_SIG_SHIFT 60 +#define QPU_SIG_MASK QPU_MASK(63, 60) + +#define QPU_UNPACK_SHIFT 57 +#define QPU_UNPACK_MASK QPU_MASK(59, 57) + +/** + * If set, the pack field means PACK_MUL or R4 packing, instead of normal + * regfile a packing. + */ +#define QPU_PM ((uint64_t)1 << 56) + +#define QPU_PACK_SHIFT 52 +#define QPU_PACK_MASK QPU_MASK(55, 52) + +#define QPU_COND_ADD_SHIFT 49 +#define QPU_COND_ADD_MASK QPU_MASK(51, 49) +#define QPU_COND_MUL_SHIFT 46 +#define QPU_COND_MUL_MASK QPU_MASK(48, 46) + +#define QPU_SF ((uint64_t)1 << 45) + +#define QPU_WADDR_ADD_SHIFT 38 +#define QPU_WADDR_ADD_MASK QPU_MASK(43, 38) +#define QPU_WADDR_MUL_SHIFT 32 +#define QPU_WADDR_MUL_MASK QPU_MASK(37, 32) + +#define QPU_OP_MUL_SHIFT 29 +#define QPU_OP_MUL_MASK QPU_MASK(31, 29) + +#define QPU_RADDR_A_SHIFT 18 +#define QPU_RADDR_A_MASK QPU_MASK(23, 18) +#define QPU_RADDR_B_SHIFT 12 +#define QPU_RADDR_B_MASK QPU_MASK(17, 12) +#define QPU_SMALL_IMM_SHIFT 12 +#define QPU_SMALL_IMM_MASK QPU_MASK(17, 12) + +#define QPU_ADD_A_SHIFT 9 +#define QPU_ADD_A_MASK QPU_MASK(11, 9) +#define QPU_ADD_B_SHIFT 6 +#define QPU_ADD_B_MASK QPU_MASK(8, 6) +#define QPU_MUL_A_SHIFT 3 +#define QPU_MUL_A_MASK QPU_MASK(5, 3) +#define QPU_MUL_B_SHIFT 0 +#define QPU_MUL_B_MASK QPU_MASK(2, 0) + +#define QPU_WS ((uint64_t)1 << 44) + +#define QPU_OP_ADD_SHIFT 24 +#define QPU_OP_ADD_MASK QPU_MASK(28, 24) + +#endif /* VC4_QPU_DEFINES_H */ diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index a68d331f0497..5cf4643f8c0b 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -41,40 +41,221 @@ #include "vc4_drv.h" #include "vc4_packet.h" -#include "vc4_regs.h" #define VALIDATE_ARGS \ struct exec_info *exec, \ void *validated, \ void *untrusted +static bool +vc4_use_bo(struct exec_info *exec, + uint32_t hindex, + enum vc4_bo_mode mode, + struct drm_gem_cma_object **obj) +{ + *obj = NULL; + + if (hindex >= exec->bo_count) { + DRM_ERROR("BO index %d greater than BO count %d\n", + hindex, exec->bo_count); + return false; + } + + if (exec->bo[hindex].mode != mode) { + if (exec->bo[hindex].mode == VC4_MODE_UNDECIDED) { + exec->bo[hindex].mode = mode; + } else { + DRM_ERROR("BO index %d reused with mode %d vs %d\n", + hindex, exec->bo[hindex].mode, mode); + return false; + } + } + + *obj = exec->bo[hindex].bo; + return true; +} + +static bool +vc4_use_handle(struct exec_info *exec, + uint32_t gem_handles_packet_index, + enum vc4_bo_mode mode, + struct drm_gem_cma_object **obj) +{ + return vc4_use_bo(exec, exec->bo_index[gem_handles_packet_index], + mode, obj); +} + +static uint32_t +gl_shader_rec_size(uint32_t pointer_bits) +{ + uint32_t attribute_count = pointer_bits & 7; + bool extended = pointer_bits & 8; + + if (attribute_count == 0) + attribute_count = 8; + + if (extended) + return 100 + attribute_count * 4; + else + return 36 + attribute_count * 8; +} + +static bool +check_tex_size(struct exec_info *exec, struct drm_gem_cma_object *fbo, + uint32_t offset, uint8_t tiling_format, + uint32_t width, uint32_t height, uint8_t cpp) +{ + uint32_t width_align, height_align; + uint32_t aligned_row_len, aligned_h, size; + + switch (tiling_format) { + case VC4_TILING_FORMAT_LINEAR: + width_align = 16; + height_align = 1; + break; + case VC4_TILING_FORMAT_T: + width_align = 128; + height_align = 32; + break; + case VC4_TILING_FORMAT_LT: + width_align = 16; + height_align = 4; + break; + default: + DRM_ERROR("buffer tiling %d unsupported\n", tiling_format); + return false; + } + + /* The values are limited by the packet/texture parameter bitfields, + * so we don't need to worry as much about integer overflow. + */ + BUG_ON(width > 65535); + BUG_ON(height > 65535); + + aligned_row_len = roundup(width * cpp, width_align); + aligned_h = roundup(height, height_align); + + if (INT_MAX / aligned_row_len < aligned_h) { + DRM_ERROR("Overflow in fbo size (%d * %d)\n", + aligned_row_len, aligned_h); + return false; + } + size = aligned_row_len * aligned_h; + + if (size + offset < size || + size + offset > fbo->base.size) { + DRM_ERROR("Overflow in %dx%d fbo size (%d + %d > %d)\n", + width, height, size, offset, fbo->base.size); + return false; + } + + return true; +} + +static int +validate_start_tile_binning(VALIDATE_ARGS) +{ + if (exec->found_start_tile_binning_packet) { + DRM_ERROR("Duplicate VC4_PACKET_START_TILE_BINNING\n"); + return -EINVAL; + } + exec->found_start_tile_binning_packet = true; + + if (!exec->found_tile_binning_mode_config_packet) { + DRM_ERROR("missing VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); + return -EINVAL; + } + + return 0; +} + static int validate_branch_to_sublist(VALIDATE_ARGS) { struct drm_gem_cma_object *target; + uint32_t offset; + + if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &target)) + return -EINVAL; - /* XXX: Validate address jumped to */ + if (target != exec->tile_alloc_bo) { + DRM_ERROR("Jumping to BOs other than tile alloc unsupported\n"); + return -EINVAL; + } - target = exec->bo[exec->bo_index[0]]; + offset = *(uint32_t *)(untrusted + 0); + if (offset % exec->tile_alloc_init_block_size || + offset / exec->tile_alloc_init_block_size > + exec->bin_tiles_x * exec->bin_tiles_y) { + DRM_ERROR("VC4_PACKET_BRANCH_TO_SUB_LIST must jump to initial " + "tile allocation space.\n"); + return -EINVAL; + } - *(uint32_t *)(validated + 0) = - *(uint32_t *)(untrusted + 0) + target->paddr; + *(uint32_t *)(validated + 0) = target->paddr + offset; return 0; } +/** + * validate_loadstore_tile_buffer_general() - Validation for + * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL and + * VC4_PACKET_STORE_TILE_BUFFER_GENERAL. + * + * The two packets are nearly the same, except for the TLB-clearing management + * bits not being present for loads. Additionally, while stores are executed + * immediately (using the current tile coordinates), loads are queued to be + * executed when the tile coordinates packet occurs. + * + * Note that coordinates packets are validated to be within the declared + * bin_x/y, which themselves are verified to match the rendering-configuration + * FB width and height (which the hardware uses to clip loads and stores). + */ static int validate_loadstore_tile_buffer_general(VALIDATE_ARGS) { uint32_t packet_b0 = *(uint8_t *)(untrusted + 0); - struct drm_gem_cma_object *fbo = exec->bo[exec->bo_index[0]]; + uint32_t packet_b1 = *(uint8_t *)(untrusted + 1); + struct drm_gem_cma_object *fbo; + uint32_t buffer_type = packet_b0 & 0xf; + uint32_t offset, cpp; - if ((packet_b0 & 0xf) == VC4_LOADSTORE_TILE_BUFFER_NONE) + switch (buffer_type) { + case VC4_LOADSTORE_TILE_BUFFER_NONE: return 0; + case VC4_LOADSTORE_TILE_BUFFER_COLOR: + if ((packet_b1 & VC4_LOADSTORE_TILE_BUFFER_MASK) == + VC4_LOADSTORE_TILE_BUFFER_RGBA8888) { + cpp = 4; + } else { + cpp = 2; + } + break; - /* XXX: Validate address offset */ - *(uint32_t *)(validated + 2) = - *(uint32_t *)(untrusted + 2) + fbo->paddr; + case VC4_LOADSTORE_TILE_BUFFER_Z: + case VC4_LOADSTORE_TILE_BUFFER_ZS: + cpp = 4; + break; + + default: + DRM_ERROR("Load/store type %d unsupported\n", buffer_type); + return -EINVAL; + } + + if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo)) + return -EINVAL; + + offset = *(uint32_t *)(untrusted + 2); + + if (!check_tex_size(exec, fbo, offset, + ((packet_b0 & + VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK) >> + VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT), + exec->fb_width, exec->fb_height, cpp)) { + return -EINVAL; + } + + *(uint32_t *)(validated + 2) = offset + fbo->paddr; return 0; } @@ -83,30 +264,60 @@ static int validate_indexed_prim_list(VALIDATE_ARGS) { struct drm_gem_cma_object *ib; + uint32_t length = *(uint32_t *)(untrusted + 1); + uint32_t offset = *(uint32_t *)(untrusted + 5); uint32_t max_index = *(uint32_t *)(untrusted + 9); uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; - uint32_t ib_access_end = (max_index + 1) * index_size; + struct vc4_shader_state *shader_state; /* Check overflow condition */ - if (max_index == ~0) { - DRM_ERROR("unlimited max index\n"); + if (exec->shader_state_count == 0) { + DRM_ERROR("shader state must precede primitives\n"); return -EINVAL; } + shader_state = &exec->shader_state[exec->shader_state_count - 1]; + + if (max_index > shader_state->max_index) + shader_state->max_index = max_index; - if (ib_access_end < max_index) { - DRM_ERROR("IB access overflow\n"); + if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &ib)) + return -EINVAL; + + if (offset > ib->base.size || + (ib->base.size - offset) / index_size < length) { + DRM_ERROR("IB access overflow (%d + %d*%d > %d)\n", + offset, length, index_size, ib->base.size); return -EINVAL; } - ib = exec->bo[exec->bo_index[0]]; - if (ib_access_end > ib->base.size) { - DRM_ERROR("IB access out of bounds (%d/%d)\n", - ib_access_end, ib->base.size); + *(uint32_t *)(validated + 5) = ib->paddr + offset; + + return 0; +} + +static int +validate_gl_array_primitive(VALIDATE_ARGS) +{ + uint32_t length = *(uint32_t *)(untrusted + 1); + uint32_t base_index = *(uint32_t *)(untrusted + 5); + uint32_t max_index; + struct vc4_shader_state *shader_state; + + /* Check overflow condition */ + if (exec->shader_state_count == 0) { + DRM_ERROR("shader state must precede primitives\n"); return -EINVAL; } + shader_state = &exec->shader_state[exec->shader_state_count - 1]; - *(uint32_t *)(validated + 5) = - *(uint32_t *)(untrusted + 5) + ib->paddr; + if (length + base_index < length) { + DRM_ERROR("primitive vertex count overflow\n"); + return -EINVAL; + } + max_index = length + base_index - 1; + + if (max_index > shader_state->max_index) + shader_state->max_index = max_index; return 0; } @@ -114,11 +325,8 @@ validate_indexed_prim_list(VALIDATE_ARGS) static int validate_gl_shader_state(VALIDATE_ARGS) { - struct drm_gem_cma_object *shader; uint32_t i = exec->shader_state_count++; - shader = exec->bo[exec->bo_index[0]]; - if (i >= exec->shader_state_size) { DRM_ERROR("More requests for shader states than declared\n"); return -EINVAL; @@ -126,9 +334,18 @@ validate_gl_shader_state(VALIDATE_ARGS) exec->shader_state[i].packet = VC4_PACKET_GL_SHADER_STATE; exec->shader_state[i].addr = *(uint32_t *)untrusted; + exec->shader_state[i].max_index = 0; + + if (exec->shader_state[i].addr & ~0xf) { + DRM_ERROR("high bits set in GL shader rec reference\n"); + return -EINVAL; + } + + *(uint32_t *)validated = (exec->shader_rec_p + + exec->shader_state[i].addr); - *(uint32_t *)validated = exec->shader_state[i].addr + - exec->shader_paddr; + exec->shader_rec_p += + roundup(gl_shader_rec_size(exec->shader_state[i].addr), 16); return 0; } @@ -152,8 +369,8 @@ validate_nv_shader_state(VALIDATE_ARGS) return -EINVAL; } - *(uint32_t *)validated = - exec->shader_state[i].addr + exec->shader_paddr; + *(uint32_t *)validated = (exec->shader_state[i].addr + + exec->shader_rec_p); return 0; } @@ -163,16 +380,79 @@ validate_tile_binning_config(VALIDATE_ARGS) { struct drm_gem_cma_object *tile_allocation; struct drm_gem_cma_object *tile_state_data_array; + uint8_t flags; + uint32_t tile_allocation_size; + + if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &tile_allocation) || + !vc4_use_handle(exec, 1, VC4_MODE_TSDA, &tile_state_data_array)) + return -EINVAL; + + if (exec->found_tile_binning_mode_config_packet) { + DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); + return -EINVAL; + } + exec->found_tile_binning_mode_config_packet = true; + + exec->bin_tiles_x = *(uint8_t *)(untrusted + 12); + exec->bin_tiles_y = *(uint8_t *)(untrusted + 13); + flags = *(uint8_t *)(untrusted + 14); - tile_allocation = exec->bo[exec->bo_index[0]]; - tile_state_data_array = exec->bo[exec->bo_index[1]]; + if (exec->bin_tiles_x == 0 || + exec->bin_tiles_y == 0) { + DRM_ERROR("Tile binning config of %dx%d too small\n", + exec->bin_tiles_x, exec->bin_tiles_y); + return -EINVAL; + } - /* XXX: Validate offsets */ - *(uint32_t *)validated = - *(uint32_t *)untrusted + tile_allocation->paddr; + /* Our validation relies on the user not getting to set up their own + * tile state/tile allocation BO contents. + */ + if (!(flags & VC4_BIN_CONFIG_AUTO_INIT_TSDA)) { + DRM_ERROR("binning config missing " + "VC4_BIN_CONFIG_AUTO_INIT_TSDA\n"); + return -EINVAL; + } - *(uint32_t *)(validated + 8) = - *(uint32_t *)(untrusted + 8) + tile_state_data_array->paddr; + if (flags & (VC4_BIN_CONFIG_DB_NON_MS | + VC4_BIN_CONFIG_TILE_BUFFER_64BIT | + VC4_BIN_CONFIG_MS_MODE_4X)) { + DRM_ERROR("unsupported bining config flags 0x%02x\n", flags); + return -EINVAL; + } + + if (*(uint32_t *)(untrusted + 0) != 0) { + DRM_ERROR("tile allocation offset != 0 unsupported\n"); + return -EINVAL; + } + tile_allocation_size = *(uint32_t *)(untrusted + 4); + if (tile_allocation_size > tile_allocation->base.size) { + DRM_ERROR("tile allocation size %d > BO size %d\n", + tile_allocation_size, tile_allocation->base.size); + return -EINVAL; + } + *(uint32_t *)validated = tile_allocation->paddr; + exec->tile_alloc_bo = tile_allocation; + + exec->tile_alloc_init_block_size = 1 << (5 + ((flags >> 5) & 3)); + if (exec->bin_tiles_x * exec->bin_tiles_y * + exec->tile_alloc_init_block_size > tile_allocation_size) { + DRM_ERROR("tile init exceeds tile alloc size (%d vs %d)\n", + exec->bin_tiles_x * exec->bin_tiles_y * + exec->tile_alloc_init_block_size, + tile_allocation_size); + return -EINVAL; + } + if (*(uint32_t *)(untrusted + 8) != 0) { + DRM_ERROR("TSDA offset != 0 unsupported\n"); + return -EINVAL; + } + if (exec->bin_tiles_x * exec->bin_tiles_y * 48 > + tile_state_data_array->base.size) { + DRM_ERROR("TSDA of %db too small for %dx%d bin config\n", + tile_state_data_array->base.size, + exec->bin_tiles_x, exec->bin_tiles_y); + } + *(uint32_t *)(validated + 8) = tile_state_data_array->paddr; return 0; } @@ -181,34 +461,83 @@ static int validate_tile_rendering_mode_config(VALIDATE_ARGS) { struct drm_gem_cma_object *fbo; + uint32_t flags, offset, cpp; - fbo = exec->bo[exec->bo_index[0]]; + if (exec->found_tile_rendering_mode_config_packet) { + DRM_ERROR("Duplicate VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n"); + return -EINVAL; + } + exec->found_tile_rendering_mode_config_packet = true; - /* XXX: Validate offsets */ - *(uint32_t *)validated = - *(uint32_t *)untrusted + fbo->paddr; + if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo)) + return -EINVAL; + + exec->fb_width = *(uint16_t *)(untrusted + 4); + exec->fb_height = *(uint16_t *)(untrusted + 6); + + /* Make sure that the fb width/height matches the binning config -- we + * rely on being able to interchange these for various assertions. + * (Within a tile, loads and stores will be clipped to the + * width/height, but we allow load/storing to any binned tile). + */ + if (exec->fb_width <= (exec->bin_tiles_x - 1) * 64 || + exec->fb_width > exec->bin_tiles_x * 64 || + exec->fb_height <= (exec->bin_tiles_y - 1) * 64 || + exec->fb_height > exec->bin_tiles_y * 64) { + DRM_ERROR("bin config %dx%d doesn't match FB %dx%d\n", + exec->bin_tiles_x, exec->bin_tiles_y, + exec->fb_width, exec->fb_height); + return -EINVAL; + } + + flags = *(uint16_t *)(untrusted + 8); + if ((flags & VC4_RENDER_CONFIG_FORMAT_MASK) == + VC4_RENDER_CONFIG_FORMAT_RGBA8888) { + cpp = 4; + } else { + cpp = 2; + } + + offset = *(uint32_t *)untrusted; + if (!check_tex_size(exec, fbo, offset, + ((flags & + VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK) >> + VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT), + exec->fb_width, exec->fb_height, cpp)) { + return -EINVAL; + } + + *(uint32_t *)validated = fbo->paddr + offset; return 0; } static int -validate_gem_handles(VALIDATE_ARGS) +validate_tile_coordinates(VALIDATE_ARGS) { - int i; - - memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index)); - - for (i = 0; i < ARRAY_SIZE(exec->bo_index); i++) { - if (exec->bo_index[i] >= exec->bo_count) { - DRM_ERROR("Validated BO index %d >= %d\n", - exec->bo_index[i], exec->bo_count); - return -EINVAL; - } + uint8_t tile_x = *(uint8_t *)(untrusted + 0); + uint8_t tile_y = *(uint8_t *)(untrusted + 1); + + if (tile_x >= exec->bin_tiles_x || + tile_y >= exec->bin_tiles_y) { + DRM_ERROR("Tile coordinates %d,%d > bin config %d,%d\n", + tile_x, + tile_y, + exec->bin_tiles_x, + exec->bin_tiles_y); + return -EINVAL; } return 0; } +static int +validate_gem_handles(VALIDATE_ARGS) +{ + memcpy(exec->bo_index, untrusted, sizeof(exec->bo_index)); + return 0; +} + static const struct cmd_info { bool bin; bool render; @@ -216,63 +545,59 @@ static const struct cmd_info { const char *name; int (*func)(struct exec_info *exec, void *validated, void *untrusted); } cmd_info[] = { - [0] = { 1, 1, 1, "halt", NULL }, - [1] = { 1, 1, 1, "nop", NULL }, - [4] = { 1, 1, 1, "flush", NULL }, - [5] = { 1, 0, 1, "flush all state", NULL }, - [6] = { 1, 0, 1, "start tile binning", NULL }, - [7] = { 1, 0, 1, "increment semaphore", NULL }, - [8] = { 1, 1, 1, "wait on semaphore", NULL }, - [17] = { 1, 1, 5, "branch to sublist", validate_branch_to_sublist }, - [24] = { 0, 1, 1, "store MS resolved tile color buffer", NULL }, - [25] = { 0, 1, 1, "store MS resolved tile color buffer and EOF", NULL }, - - [28] = { 0, 1, 7, "Store Tile Buffer General", - validate_loadstore_tile_buffer_general }, - [29] = { 0, 1, 7, "Load Tile Buffer General", - validate_loadstore_tile_buffer_general }, - - [32] = { 1, 1, 14, "Indexed Primitive List", - validate_indexed_prim_list }, - - /* XXX: bounds check verts? */ - [33] = { 1, 1, 10, "Vertex Array Primitives", NULL }, - - [56] = { 1, 1, 2, "primitive list format", NULL }, /* XXX: bin valid? */ - - [64] = { 1, 1, 5, "GL Shader State", validate_gl_shader_state }, - [65] = { 1, 1, 5, "NV Shader State", validate_nv_shader_state }, - - [96] = { 1, 1, 4, "configuration bits", NULL }, - [97] = { 1, 1, 5, "flat shade flags", NULL }, - [98] = { 1, 1, 5, "point size", NULL }, - [99] = { 1, 1, 5, "line width", NULL }, - [100] = { 1, 1, 3, "RHT X boundary", NULL }, - [101] = { 1, 1, 5, "Depth Offset", NULL }, - [102] = { 1, 1, 9, "Clip Window", NULL }, - [103] = { 1, 1, 5, "Viewport Offset", NULL }, - [105] = { 1, 1, 9, "Clipper XY Scaling", NULL }, + [VC4_PACKET_HALT] = { 1, 1, 1, "halt", NULL }, + [VC4_PACKET_NOP] = { 1, 1, 1, "nop", NULL }, + [VC4_PACKET_FLUSH] = { 1, 1, 1, "flush", NULL }, + [VC4_PACKET_FLUSH_ALL] = { 1, 0, 1, "flush all state", NULL }, + [VC4_PACKET_START_TILE_BINNING] = { 1, 0, 1, "start tile binning", validate_start_tile_binning }, + [VC4_PACKET_INCREMENT_SEMAPHORE] = { 1, 0, 1, "increment semaphore", NULL }, + [VC4_PACKET_WAIT_ON_SEMAPHORE] = { 1, 1, 1, "wait on semaphore", NULL }, + /* BRANCH_TO_SUB_LIST is actually supported in the binner as well, but + * we only use it from the render CL in order to jump into the tile + * allocation BO. + */ + [VC4_PACKET_BRANCH_TO_SUB_LIST] = { 0, 1, 5, "branch to sublist", validate_branch_to_sublist }, + [VC4_PACKET_STORE_MS_TILE_BUFFER] = { 0, 1, 1, "store MS resolved tile color buffer", NULL }, + [VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF] = { 0, 1, 1, "store MS resolved tile color buffer and EOF", NULL }, + + [VC4_PACKET_STORE_TILE_BUFFER_GENERAL] = { 0, 1, 7, "Store Tile Buffer General", validate_loadstore_tile_buffer_general }, + [VC4_PACKET_LOAD_TILE_BUFFER_GENERAL] = { 0, 1, 7, "Load Tile Buffer General", validate_loadstore_tile_buffer_general }, + + [VC4_PACKET_GL_INDEXED_PRIMITIVE] = { 1, 1, 14, "Indexed Primitive List", validate_indexed_prim_list }, + + [VC4_PACKET_GL_ARRAY_PRIMITIVE] = { 1, 1, 10, "Vertex Array Primitives", validate_gl_array_primitive }, + + /* This is only used by clipped primitives (packets 48 and 49), which + * we don't support parsing yet. + */ + [VC4_PACKET_PRIMITIVE_LIST_FORMAT] = { 1, 1, 2, "primitive list format", NULL }, + + [VC4_PACKET_GL_SHADER_STATE] = { 1, 1, 5, "GL Shader State", validate_gl_shader_state }, + [VC4_PACKET_NV_SHADER_STATE] = { 1, 1, 5, "NV Shader State", validate_nv_shader_state }, + + [VC4_PACKET_CONFIGURATION_BITS] = { 1, 1, 4, "configuration bits", NULL }, + [VC4_PACKET_FLAT_SHADE_FLAGS] = { 1, 1, 5, "flat shade flags", NULL }, + [VC4_PACKET_POINT_SIZE] = { 1, 1, 5, "point size", NULL }, + [VC4_PACKET_LINE_WIDTH] = { 1, 1, 5, "line width", NULL }, + [VC4_PACKET_RHT_X_BOUNDARY] = { 1, 1, 3, "RHT X boundary", NULL }, + [VC4_PACKET_DEPTH_OFFSET] = { 1, 1, 5, "Depth Offset", NULL }, + [VC4_PACKET_CLIP_WINDOW] = { 1, 1, 9, "Clip Window", NULL }, + [VC4_PACKET_VIEWPORT_OFFSET] = { 1, 1, 5, "Viewport Offset", NULL }, + [VC4_PACKET_CLIPPER_XY_SCALING] = { 1, 1, 9, "Clipper XY Scaling", NULL }, /* Note: The docs say this was also 105, but it was 106 in the * initial userland code drop. */ - [106] = { 1, 1, 9, "Clipper Z Scale and Offset", NULL }, + [VC4_PACKET_CLIPPER_Z_SCALING] = { 1, 1, 9, "Clipper Z Scale and Offset", NULL }, - [112] = { 1, 0, 16, "tile binning configuration", - validate_tile_binning_config }, + [VC4_PACKET_TILE_BINNING_MODE_CONFIG] = { 1, 0, 16, "tile binning configuration", validate_tile_binning_config }, - /* XXX: Do we need to validate this one? It's got width/height in it. - */ - [113] = { 0, 1, 11, "tile rendering mode configuration", - validate_tile_rendering_mode_config}, + [VC4_PACKET_TILE_RENDERING_MODE_CONFIG] = { 0, 1, 11, "tile rendering mode configuration", validate_tile_rendering_mode_config}, - [114] = { 0, 1, 14, "Clear Colors", NULL }, + [VC4_PACKET_CLEAR_COLORS] = { 0, 1, 14, "Clear Colors", NULL }, - /* XXX: Do we need to validate here? It's got tile x/y number for - * rendering - */ - [115] = { 0, 1, 3, "Tile Coordinates", NULL }, + [VC4_PACKET_TILE_COORDINATES] = { 0, 1, 3, "Tile Coordinates", validate_tile_coordinates }, - [254] = { 1, 1, 9, "GEM handles", validate_gem_handles }, + [VC4_PACKET_GEM_HANDLES] = { 1, 1, 9, "GEM handles", validate_gem_handles }, }; int @@ -326,7 +651,7 @@ vc4_validate_cl(struct drm_device *dev, return -EINVAL; } - if (cmd != 254) + if (cmd != VC4_PACKET_GEM_HANDLES) memcpy(dst_pkt, src_pkt, info->len); if (info->func && info->func(exec, @@ -340,126 +665,322 @@ vc4_validate_cl(struct drm_device *dev, src_offset += info->len; /* GEM handle loading doesn't produce HW packets. */ - if (cmd != 254) + if (cmd != VC4_PACKET_GEM_HANDLES) dst_offset += info->len; /* When the CL hits halt, it'll stop reading anything else. */ - if (cmd == 0) + if (cmd == VC4_PACKET_HALT) break; } + if (is_bin) { + exec->ct0ea = exec->ct0ca + dst_offset; + + if (!exec->found_start_tile_binning_packet) { + DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); + return -EINVAL; + } + } else { + if (!exec->found_tile_rendering_mode_config_packet) { + DRM_ERROR("Render CL missing VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n"); + return -EINVAL; + } + exec->ct1ea = exec->ct1ca + dst_offset; + } + return 0; } +static bool +reloc_tex(struct exec_info *exec, + void *uniform_data_u, + struct vc4_texture_sample_info *sample, + uint32_t texture_handle_index) + +{ + struct drm_gem_cma_object *tex; + uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]); + uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]); + uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; + uint32_t offset = p0 & ~0xfff; + uint32_t miplevels = (p0 & 0x15); + uint32_t width = (p1 >> 8) & 2047; + uint32_t height = (p1 >> 20) & 2047; + uint32_t type, cpp, tiling_format; + uint32_t i; + + if (width == 0) + width = 2048; + if (height == 0) + height = 2048; + + if (p0 & (1 << 9)) { + DRM_ERROR("Cube maps unsupported\n"); + return false; + } + + type = ((p0 >> 4) & 15) | ((p1 >> 31) << 4); + + switch (type) { + case 0: /* RGBA8888 */ + case 1: /* RGBX8888 */ + case 16: /* RGBA32R */ + cpp = 4; + break; + case 2: /* RGBA4444 */ + case 3: /* RGBA5551 */ + case 4: /* RGB565 */ + case 7: /* LUMALPHA */ + case 9: /* S16F */ + case 11: /* S16 */ + cpp = 2; + break; + case 5: /* LUMINANCE */ + case 6: /* ALPHA */ + case 10: /* S8 */ + cpp = 1; + break; + case 8: /* ETC1 */ + case 12: /* BW1 */ + case 13: /* A4 */ + case 14: /* A1 */ + case 15: /* RGBA64 */ + case 17: /* YUV422R */ + default: + DRM_ERROR("Texture format %d unsupported\n", type); + return false; + } + + if (type == 16) { + tiling_format = VC4_TILING_FORMAT_LINEAR; + } else { + DRM_ERROR("Tiling formats not yet supported\n"); + return false; + } + + if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) + return false; + + if (!check_tex_size(exec, tex, offset, tiling_format, + width, height, cpp)) { + return false; + } + + /* The mipmap levels are stored before the base of the texture. Make + * sure there is actually space in the BO. + */ + for (i = 1; i <= miplevels; i++) { + uint32_t level_width = roundup(max(width >> i, 1u), 16 / cpp); + uint32_t level_height = max(height >> i, 1u); + uint32_t level_size = level_width * level_height * cpp; + + if (offset < level_size) { + DRM_ERROR("Level %d (%dx%d) size %db overflowed " + "buffer bounds (offset %d)\n", + i, level_width, level_height, + level_size, offset); + return false; + } + } + + *validated_p0 = tex->paddr + p0; + + return true; +} + static int validate_shader_rec(struct drm_device *dev, struct exec_info *exec, - void *validated, - void *unvalidated, - uint32_t len, struct vc4_shader_state *state) { - uint32_t *src_handles = unvalidated; - void *src_pkt; - void *dst_pkt = validated; - static const int gl_bo_offsets[] = { - 4, 8, /* fs code, ubo */ - 16, 20, /* vs code, ubo */ - 28, 32, /* cs code, ubo */ + uint32_t *src_handles; + void *pkt_u, *pkt_v; + enum shader_rec_reloc_type { + RELOC_CODE, + RELOC_VBO, + }; + struct shader_rec_reloc { + enum shader_rec_reloc_type type; + uint32_t offset; }; - static const int nv_bo_offsets[] = { - 4, 8, /* fs code, ubo */ - 12, /* vbo */ + static const struct shader_rec_reloc gl_relocs[] = { + { RELOC_CODE, 4 }, /* fs */ + { RELOC_CODE, 16 }, /* vs */ + { RELOC_CODE, 28 }, /* cs */ }; - struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_bo_offsets) + 8]; - const int *bo_offsets; - uint32_t nr_attributes = 0, nr_bo, packet_size; + static const struct shader_rec_reloc nv_relocs[] = { + { RELOC_CODE, 4 }, /* fs */ + { RELOC_VBO, 12 } + }; + const struct shader_rec_reloc *relocs; + struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; + uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; int i; + struct vc4_validated_shader_info *validated_shader = NULL; if (state->packet == VC4_PACKET_NV_SHADER_STATE) { - bo_offsets = nv_bo_offsets; - nr_bo = ARRAY_SIZE(nv_bo_offsets); + relocs = nv_relocs; + nr_fixed_relocs = ARRAY_SIZE(nv_relocs); packet_size = 16; } else { - bo_offsets = gl_bo_offsets; - nr_bo = ARRAY_SIZE(gl_bo_offsets); + relocs = gl_relocs; + nr_fixed_relocs = ARRAY_SIZE(gl_relocs); nr_attributes = state->addr & 0x7; if (nr_attributes == 0) nr_attributes = 8; - packet_size = 36 + nr_attributes * 8; + packet_size = gl_shader_rec_size(state->addr); + } + nr_relocs = nr_fixed_relocs + nr_attributes; + + if (nr_relocs * 4 > exec->shader_rec_size) { + DRM_ERROR("overflowed shader recs reading %d handles " + "from %d bytes left\n", + nr_relocs, exec->shader_rec_size); + return -EINVAL; } - if ((nr_bo + nr_attributes) * 4 + packet_size > len) { - DRM_ERROR("overflowed shader packet read " - "(handles %d, packet %d, len %d)\n", - (nr_bo + nr_attributes) * 4, packet_size, len); + src_handles = exec->shader_rec_u; + exec->shader_rec_u += nr_relocs * 4; + exec->shader_rec_size -= nr_relocs * 4; + + if (packet_size > exec->shader_rec_size) { + DRM_ERROR("overflowed shader recs copying %db packet " + "from %d bytes left\n", + packet_size, exec->shader_rec_size); return -EINVAL; } + pkt_u = exec->shader_rec_u; + pkt_v = exec->shader_rec_v; + memcpy(pkt_v, pkt_u, packet_size); + exec->shader_rec_u += packet_size; + /* Shader recs have to be aligned to 16 bytes (due to the attribute + * flags being in the low bytes), so round the next validated shader + * rec address up. This should be safe, since we've got so many + * relocations in a shader rec packet. + */ + BUG_ON(roundup(packet_size, 16) - packet_size > nr_relocs * 4); + exec->shader_rec_v += roundup(packet_size, 16); + exec->shader_rec_size -= packet_size; - src_pkt = unvalidated + 4 * (nr_bo + nr_attributes); - memcpy(dst_pkt, src_pkt, packet_size); + for (i = 0; i < nr_relocs; i++) { + enum vc4_bo_mode mode; - for (i = 0; i < nr_bo + nr_attributes; i++) { - if (src_handles[i] >= exec->bo_count) { - DRM_ERROR("shader rec bo index %d > %d\n", - src_handles[i], exec->bo_count); - return -EINVAL; + if (i < nr_fixed_relocs && relocs[i].type == RELOC_CODE) + mode = VC4_MODE_SHADER; + else + mode = VC4_MODE_RENDER; + + if (!vc4_use_bo(exec, src_handles[i], mode, &bo[i])) { + return false; } - bo[i] = exec->bo[src_handles[i]]; } - for (i = 0; i < nr_bo; i++) { - /* XXX: validation */ - uint32_t o = bo_offsets[i]; - *(uint32_t *)(dst_pkt + o) = - bo[i]->paddr + *(uint32_t *)(src_pkt + o); + for (i = 0; i < nr_fixed_relocs; i++) { + uint32_t o = relocs[i].offset; + uint32_t src_offset = *(uint32_t *)(pkt_u + o); + uint32_t *texture_handles_u; + void *uniform_data_u; + uint32_t tex; + + *(uint32_t *)(pkt_v + o) = bo[i]->paddr + src_offset; + + switch (relocs[i].type) { + case RELOC_CODE: + kfree(validated_shader); + validated_shader = vc4_validate_shader(bo[i], + src_offset); + if (!validated_shader) + goto fail; + + if (validated_shader->uniforms_src_size > + exec->uniforms_size) { + DRM_ERROR("Uniforms src buffer overflow\n"); + goto fail; + } + + texture_handles_u = exec->uniforms_u; + uniform_data_u = (texture_handles_u + + validated_shader->num_texture_samples); + + memcpy(exec->uniforms_v, uniform_data_u, + validated_shader->uniforms_size); + + for (tex = 0; + tex < validated_shader->num_texture_samples; + tex++) { + if (!reloc_tex(exec, + uniform_data_u, + &validated_shader->texture_samples[tex], + texture_handles_u[tex])) { + goto fail; + } + } + + *(uint32_t *)(pkt_v + o + 4) = exec->uniforms_p; + + exec->uniforms_u += validated_shader->uniforms_src_size; + exec->uniforms_v += validated_shader->uniforms_size; + exec->uniforms_p += validated_shader->uniforms_size; + + break; + + case RELOC_VBO: + break; + } } for (i = 0; i < nr_attributes; i++) { - /* XXX: validation */ + struct drm_gem_cma_object *vbo = bo[nr_fixed_relocs + i]; uint32_t o = 36 + i * 8; - *(uint32_t *)(dst_pkt + o) = - bo[nr_bo + i]->paddr + *(uint32_t *)(src_pkt + o); + uint32_t offset = *(uint32_t *)(pkt_u + o + 0); + uint32_t attr_size = *(uint8_t *)(pkt_u + o + 4) + 1; + uint32_t stride = *(uint8_t *)(pkt_u + o + 5); + uint32_t max_index; + + if (state->addr & 0x8) + stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff; + + if (vbo->base.size < offset || + vbo->base.size - offset < attr_size) { + DRM_ERROR("BO offset overflow (%d + %d > %d)\n", + offset, attr_size, vbo->base.size); + return -EINVAL; + } + + if (stride != 0) { + max_index = ((vbo->base.size - offset - attr_size) / + stride); + if (state->max_index > max_index) { + DRM_ERROR("primitives use index %d out of supplied %d\n", + state->max_index, max_index); + return -EINVAL; + } + } + + *(uint32_t *)(pkt_v + o) = vbo->paddr + offset; } + kfree(validated_shader); + return 0; + +fail: + kfree(validated_shader); + return -EINVAL; } int vc4_validate_shader_recs(struct drm_device *dev, - void *validated, - void *unvalidated, - uint32_t len, struct exec_info *exec) { - uint32_t dst_offset = 0; - uint32_t src_offset = 0; uint32_t i; int ret = 0; for (i = 0; i < exec->shader_state_count; i++) { - if ((exec->shader_state[i].addr & ~0xf) != - (validated - exec->exec_bo->vaddr - - (exec->shader_paddr - exec->exec_bo->paddr))) { - DRM_ERROR("unexpected shader rec offset: " - "0x%08x vs 0x%08x\n", - exec->shader_state[i].addr & ~0xf, - (int)(validated - - exec->exec_bo->vaddr - - (exec->shader_paddr - - exec->exec_bo->paddr))); - return -EINVAL; - } - - ret = validate_shader_rec(dev, exec, - validated + dst_offset, - unvalidated + src_offset, - len - src_offset, - &exec->shader_state[i]); + ret = validate_shader_rec(dev, exec, &exec->shader_state[i]); if (ret) return ret; - /* XXX: incr dst/src offset */ } return ret; diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c new file mode 100644 index 000000000000..c53807603a51 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -0,0 +1,318 @@ +/* + * Copyright © 2014 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * DOC: Shader validator for VC4. + * + * The VC4 has no IOMMU between it and system memory. So, a user with access + * to execute shaders could escalate privilege by overwriting system memory + * (using the VPM write address register in the general-purpose DMA mode) or + * reading system memory it shouldn't (reading it as a texture, or uniform + * data, or vertex data). + * + * This walks over a shader starting from some offset within a BO, ensuring + * that its accesses are appropriately bounded, and recording how many texture + * accesses are made and where so that we can do relocations for them in the + * uniform stream. + * + * The kernel API has shaders stored in user-mapped BOs. The BOs will be + * forcibly unmapped from the process before validation, and any cache of + * validated state will be flushed if the mapping is faulted back in. + * + * Storing the shaders in BOs means that the validation process will be slow + * due to uncached reads, but since shaders are long-lived and shader BOs are + * never actually modified, this shouldn't be a problem. + */ + +#include "vc4_drv.h" +#include "vc4_qpu_defines.h" + +struct vc4_shader_validation_state { + struct vc4_texture_sample_info tmu_setup[2]; + int tmu_write_count[2]; +}; + +static bool +is_tmu_write(uint32_t waddr) +{ + return (waddr >= QPU_W_TMU0_S && + waddr <= QPU_W_TMU1_B); +} + +static bool +record_validated_texture_sample(struct vc4_validated_shader_info *validated_shader, + struct vc4_shader_validation_state *validation_state, + int tmu) +{ + uint32_t s = validated_shader->num_texture_samples; + int i; + struct vc4_texture_sample_info *temp_samples; + + temp_samples = krealloc(validated_shader->texture_samples, + (s + 1) * sizeof(*temp_samples), + GFP_KERNEL); + if (!temp_samples) + return false; + + memcpy(temp_samples[s].p_offset, + validation_state->tmu_setup[tmu].p_offset, + validation_state->tmu_write_count[tmu] * sizeof(uint32_t)); + for (i = validation_state->tmu_write_count[tmu]; i < 4; i++) + temp_samples[s].p_offset[i] = ~0; + + validated_shader->num_texture_samples = s + 1; + validated_shader->texture_samples = temp_samples; + + return true; +} + +static bool +check_tmu_write(struct vc4_validated_shader_info *validated_shader, + struct vc4_shader_validation_state *validation_state, + uint32_t waddr) +{ + int tmu = waddr > QPU_W_TMU0_B; + + if (!is_tmu_write(waddr)) + return true; + + if (validation_state->tmu_write_count[tmu] >= 4) { + DRM_ERROR("TMU%d got too many parameters before dispatch\n", + tmu); + return false; + } + validation_state->tmu_setup[tmu].p_offset[validation_state->tmu_write_count[tmu]] = + validated_shader->uniforms_size; + validation_state->tmu_write_count[tmu]++; + validated_shader->uniforms_size += 4; + + if (waddr == QPU_W_TMU0_S || waddr == QPU_W_TMU1_S) { + if (!record_validated_texture_sample(validated_shader, + validation_state, tmu)) { + return false; + } + + validation_state->tmu_write_count[tmu] = 0; + } + + return true; +} + +static bool +check_register_write(struct vc4_validated_shader_info *validated_shader, + struct vc4_shader_validation_state *validation_state, + uint32_t waddr) +{ + switch (waddr) { + case QPU_W_UNIFORMS_ADDRESS: + /* XXX: We'll probably need to support this for reladdr, but + * it's definitely a security-related one. + */ + DRM_ERROR("uniforms address load unsupported\n"); + return false; + + case QPU_W_TLB_COLOR_MS: + case QPU_W_TLB_COLOR_ALL: + case QPU_W_TLB_Z: + /* These only interact with the tile buffer, not main memory, + * so they're safe. + */ + return true; + + case QPU_W_TMU0_S: + case QPU_W_TMU0_T: + case QPU_W_TMU0_R: + case QPU_W_TMU0_B: + case QPU_W_TMU1_S: + case QPU_W_TMU1_T: + case QPU_W_TMU1_R: + case QPU_W_TMU1_B: + return check_tmu_write(validated_shader, validation_state, + waddr); + + case QPU_W_HOST_INT: + case QPU_W_TMU_NOSWAP: + case QPU_W_TLB_STENCIL_SETUP: + case QPU_W_TLB_ALPHA_MASK: + case QPU_W_MUTEX_RELEASE: + /* XXX: I haven't thought about these, so don't support them + * for now. + */ + DRM_ERROR("Unsupported waddr %d\n", waddr); + return false; + + case QPU_W_VPM_ADDR: + DRM_ERROR("General VPM DMA unsupported\n"); + return false; + + case QPU_W_VPM: + case QPU_W_VPMVCD_SETUP: + /* We allow VPM setup in general, even including VPM DMA + * configuration setup, because the (unsafe) DMA can only be + * triggered by QPU_W_VPM_ADDR writes. + */ + return true; + } + + return true; +} + +static bool +check_instruction_writes(uint64_t inst, + struct vc4_validated_shader_info *validated_shader, + struct vc4_shader_validation_state *validation_state) +{ + uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); + uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); + + if (is_tmu_write(waddr_add) && is_tmu_write(waddr_mul)) { + DRM_ERROR("ADD and MUL both set up textures\n"); + return false; + } + + return (check_register_write(validated_shader, validation_state, waddr_add) && + check_register_write(validated_shader, validation_state, waddr_mul)); +} + +static bool +check_instruction_reads(uint64_t inst, + struct vc4_validated_shader_info *validated_shader) +{ + uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); + uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); + uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); + uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); + + if (raddr_a == QPU_R_UNIF || + raddr_b == QPU_R_UNIF) { + if (is_tmu_write(waddr_add) || is_tmu_write(waddr_mul)) { + DRM_ERROR("uniform read in the same instruction as " + "texture setup"); + return false; + } + + /* This can't overflow the uint32_t, because we're reading 8 + * bytes of instruction to increment by 4 here, so we'd + * already be OOM. + */ + validated_shader->uniforms_size += 4; + } + + return true; +} + +struct vc4_validated_shader_info * +vc4_validate_shader(struct drm_gem_cma_object *shader_obj, + uint32_t start_offset) +{ + bool found_shader_end = false; + int shader_end_ip = 0; + uint32_t ip, max_ip; + uint64_t *shader; + struct vc4_validated_shader_info *validated_shader; + struct vc4_shader_validation_state validation_state; + + memset(&validation_state, 0, sizeof(validation_state)); + + if (start_offset + sizeof(uint64_t) > shader_obj->base.size) { + DRM_ERROR("shader starting at %d outside of BO sized %d\n", + start_offset, + shader_obj->base.size); + return NULL; + } + shader = shader_obj->vaddr + start_offset; + max_ip = (shader_obj->base.size - start_offset) / sizeof(uint64_t); + + validated_shader = kcalloc(sizeof(*validated_shader), 1, GFP_KERNEL); + if (!validated_shader) + return NULL; + + for (ip = 0; ip < max_ip; ip++) { + uint64_t inst = shader[ip]; + uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); + + switch (sig) { + case QPU_SIG_NONE: + case QPU_SIG_WAIT_FOR_SCOREBOARD: + case QPU_SIG_SCOREBOARD_UNLOCK: + case QPU_SIG_LOAD_TMU0: + case QPU_SIG_LOAD_TMU1: + if (!check_instruction_writes(inst, validated_shader, + &validation_state)) { + DRM_ERROR("Bad write at ip %d\n", ip); + goto fail; + } + + if (!check_instruction_reads(inst, validated_shader)) + goto fail; + + break; + + case QPU_SIG_LOAD_IMM: + if (!check_instruction_writes(inst, validated_shader, + &validation_state)) { + DRM_ERROR("Bad LOAD_IMM write at ip %d\n", ip); + goto fail; + } + break; + + case QPU_SIG_PROG_END: + found_shader_end = true; + shader_end_ip = ip; + break; + + default: + DRM_ERROR("Unsupported QPU signal %d at " + "instruction %d\n", sig, ip); + goto fail; + } + + /* There are two delay slots after program end is signaled + * that are still executed, then we're finished. + */ + if (found_shader_end && ip == shader_end_ip + 2) + break; + } + + if (ip == max_ip) { + DRM_ERROR("shader starting at %d failed to terminate before " + "shader BO end at %d\n", + start_offset, + shader_obj->base.size); + goto fail; + } + + /* Again, no chance of integer overflow here because the worst case + * scenario is 8 bytes of uniforms plus handles per 8-byte + * instruction. + */ + validated_shader->uniforms_src_size = + (validated_shader->uniforms_size + + 4 * validated_shader->num_texture_samples); + + return validated_shader; + +fail: + kfree(validated_shader); + return NULL; +} diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index 6cccad5f6c8b..fa9d2372b3e1 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -28,10 +28,11 @@ #define DRM_VC4_SUBMIT_CL 0x00 -#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) +#define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) /** - * Structure for submitting commands to the 3D engine. + * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D + * engine. * * Drivers typically use GPU BOs to store batchbuffers / command lists and * their associated state. However, because the VC4 lacks an MMU, we have to @@ -43,8 +44,7 @@ * BO. */ struct drm_vc4_submit_cl { - /** - * Pointer to the binner command list. + /* Pointer to the binner command list. * * This is the first set of commands executed, which runs the * coordinate shader to determine where primitives land on the screen, @@ -53,8 +53,7 @@ struct drm_vc4_submit_cl { */ void __user *bin_cl; - /** - * Pointer to the render command list. + /* Pointer to the render command list. * * The render command list contains a set of packets to load the * current tile's state (reading from memory, or just clearing it) @@ -64,7 +63,7 @@ struct drm_vc4_submit_cl { */ void __user *render_cl; - /** Pointer to the shader records. + /* Pointer to the shader records. * * Shader records are the structures read by the hardware that contain * pointers to uniforms, shaders, and vertex attributes. The @@ -73,25 +72,42 @@ struct drm_vc4_submit_cl { * and an attribute count), so those BO indices into bo_handles are * just stored as uint32_ts before each shader record passed in. */ - void __user *shader_records; + void __user *shader_rec; + + /* Pointer to uniform data and texture handles for the textures + * referenced by the shader. + * + * For each shader state record, there is a set of uniform data in the + * order referenced by the record (FS, VS, then CS). Each set of + * uniform data has a uint32_t index into bo_handles per texture + * sample operation, in the order the QPU_W_TMUn_S writes appear in + * the program. Following the texture BO handle indices is the actual + * uniform data. + * + * The individual uniform state blocks don't have sizes passed in, + * because the kernel has to determine the sizes anyway during shader + * code validation. + */ + void __user *uniforms; void __user *bo_handles; - /** Size in bytes of the binner command list. */ - uint32_t bin_cl_len; - /** Size in bytes of the render command list */ - uint32_t render_cl_len; - /** Size in bytes of the list of shader records. */ - uint32_t shader_record_len; - /** - * Number of shader records. + /* Size in bytes of the binner command list. */ + uint32_t bin_cl_size; + /* Size in bytes of the render command list */ + uint32_t render_cl_size; + /* Size in bytes of the set of shader records. */ + uint32_t shader_rec_size; + /* Number of shader records. * - * This could just be computed from the contents of shader_records, - * but it keeps the kernel from having to resize various allocations - * it makes. + * This could just be computed from the contents of shader_records and + * the address bits of references to them from the bin CL, but it + * keeps the kernel from having to resize some allocations it makes. */ - uint32_t shader_record_count; + uint32_t shader_rec_count; + /* Size in bytes of the uniform state. */ + uint32_t uniforms_size; - /** Number of BO handles passed in (size is that times 4). */ + /* Number of BO handles passed in (size is that times 4). */ uint32_t bo_handle_count; }; -- cgit v1.2.1 From 318fbbe04d301ee577289c02ca8632a3f3674ca1 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 11 Aug 2014 11:32:40 -0700 Subject: drm/vc4: Rename a struct exec_info argument for consistency with the rest. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 854216f92482..3918d5bc8723 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -131,11 +131,11 @@ wait_for_render_thread(struct drm_device *dev, u32 initial_rfc) */ static int -vc4_submit(struct drm_device *dev, struct exec_info *args) +vc4_submit(struct drm_device *dev, struct exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); - uint32_t ct0ca = args->ct0ca, ct0ea = args->ct0ea; - uint32_t ct1ca = args->ct1ca, ct1ea = args->ct1ea; + uint32_t ct0ca = exec->ct0ca, ct0ea = exec->ct0ea; + uint32_t ct1ca = exec->ct1ca, ct1ea = exec->ct1ea; int ret; /* flushes caches */ -- cgit v1.2.1 From b02eb5ff854b847710f7df4527f90c2d12beb094 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 11 Aug 2014 11:45:10 -0700 Subject: drm/vc4: Add support for binner overflow memory allocation. We should probably hang on to some of this memory across renders, since most renders will want some binner overflow. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 11 ++++++++++ drivers/gpu/drm/vc4/vc4_gem.c | 47 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index e741627211a1..82482a3fb5f6 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -92,6 +92,11 @@ enum vc4_bo_mode { VC4_MODE_SHADER, }; +struct vc4_bo_list_entry { + struct list_head head; + struct drm_gem_cma_object *bo; +}; + struct vc4_bo_exec_state { struct drm_gem_cma_object *bo; enum vc4_bo_mode mode; @@ -117,6 +122,12 @@ struct exec_info { */ struct drm_gem_cma_object *exec_bo; + /* List of struct vc4_list_bo_entry allocated to accomodate + * binner overflow. These will be freed when the exec is + * done. + */ + struct list_head overflow_list; + /** * This tracks the per-shader-record state (packet 64) that * determines the length of the shader record and the offset diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 3918d5bc8723..c449420d06c2 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -70,10 +70,35 @@ thread_stopped(struct drm_device *dev, uint32_t thread) } static int -wait_for_bin_thread(struct drm_device *dev) +try_adding_overflow_memory(struct drm_device *dev, struct exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; + struct vc4_bo_list_entry *entry = kmalloc(sizeof(*entry), GFP_KERNEL); + + if (!entry) + return -ENOMEM; + + entry->bo = drm_gem_cma_create(dev, 256 * 1024); + if (IS_ERR(entry->bo)) { + int ret = PTR_ERR(entry->bo); + DRM_ERROR("Couldn't allocate binner overflow mem\n"); + kfree(entry); + return ret; + } + + list_add_tail(&entry->head, &exec->overflow_list); + + V3D_WRITE(V3D_BPOA, entry->bo->paddr); + V3D_WRITE(V3D_BPOS, entry->bo->base.size); + + return 0; +} + +static int +wait_for_bin_thread(struct drm_device *dev, struct exec_info *exec) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + int i, ret; for (i = 0; i < 1000000; i++) { if (thread_stopped(dev, 0)) { @@ -86,9 +111,9 @@ wait_for_bin_thread(struct drm_device *dev) } if (V3D_READ(V3D_PCS) & V3D_BMOOM) { - /* XXX */ - DRM_ERROR("binner oom\n"); - return -EINVAL; + ret = try_adding_overflow_memory(dev, exec); + if (ret) + return ret; } } @@ -148,7 +173,7 @@ vc4_submit(struct drm_device *dev, struct exec_info *exec) submit_cl(dev, 0, ct0ca, ct0ea); - ret = wait_for_bin_thread(dev); + ret = wait_for_bin_thread(dev, exec); if (ret) return ret; @@ -366,6 +391,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, memset(&exec, 0, sizeof(exec)); exec.args = data; + INIT_LIST_HEAD(&exec.overflow_list); mutex_lock(&dev->struct_mutex); @@ -390,6 +416,15 @@ fail: kfree(exec.bo); } + while (!list_empty(&exec.overflow_list)) { + struct vc4_bo_list_entry *entry = + list_first_entry(&exec.overflow_list, + struct vc4_bo_list_entry, head); + drm_gem_object_unreference(&entry->bo->base); + list_del(&entry->head); + kfree(entry); + } + drm_gem_object_unreference(&exec.exec_bo->base); mutex_unlock(&dev->struct_mutex); -- cgit v1.2.1 From 314f621460514b8fa081ce976e279fdecf2d1cc0 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 21 Aug 2014 14:34:01 -0700 Subject: drm/vc4: Add texture tiling support and fix multi-level texture validation. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_packet.h | 22 ++++ drivers/gpu/drm/vc4/vc4_validate.c | 180 +++++++++++++++++++++-------- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 1 + 3 files changed, 154 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h index e455c2fe76a5..eef5be99a046 100644 --- a/drivers/gpu/drm/vc4/vc4_packet.h +++ b/drivers/gpu/drm/vc4/vc4_packet.h @@ -221,4 +221,26 @@ enum vc4_packet { #define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT (1 << 1) #define VC4_RENDER_CONFIG_MS_MODE_4X (1 << 0) + +enum vc4_texture_data_type { + VC4_TEXTURE_TYPE_RGBA8888 = 0, + VC4_TEXTURE_TYPE_RGBX8888 = 1, + VC4_TEXTURE_TYPE_RGBA4444 = 2, + VC4_TEXTURE_TYPE_RGBA5551 = 3, + VC4_TEXTURE_TYPE_RGB565 = 4, + VC4_TEXTURE_TYPE_LUMINANCE = 5, + VC4_TEXTURE_TYPE_ALPHA = 6, + VC4_TEXTURE_TYPE_LUMALPHA = 7, + VC4_TEXTURE_TYPE_ETC1 = 8, + VC4_TEXTURE_TYPE_S16F = 9, + VC4_TEXTURE_TYPE_S8 = 10, + VC4_TEXTURE_TYPE_S16 = 11, + VC4_TEXTURE_TYPE_BW1 = 12, + VC4_TEXTURE_TYPE_A4 = 13, + VC4_TEXTURE_TYPE_A1 = 14, + VC4_TEXTURE_TYPE_RGBA64 = 15, + VC4_TEXTURE_TYPE_RGBA32R = 16, + VC4_TEXTURE_TYPE_YUV422R = 17, +}; + #endif /* VC4_PACKET_H */ diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 5cf4643f8c0b..55de3cd433e5 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -47,6 +47,53 @@ void *validated, \ void *untrusted + +/** Return the width in pixels of a 64-byte microtile. */ +static uint32_t +utile_width(int cpp) +{ + switch (cpp) { + case 1: + case 2: + return 8; + case 4: + return 4; + case 8: + return 2; + default: + DRM_ERROR("unknown cpp: %d\n", cpp); + return 1; + } +} + +/** Return the height in pixels of a 64-byte microtile. */ +static uint32_t +utile_height(int cpp) +{ + switch (cpp) { + case 1: + return 8; + case 2: + case 4: + case 8: + return 4; + default: + DRM_ERROR("unknown cpp: %d\n", cpp); + return 1; + } +} + +/** + * The texture unit decides what tiling format a particular miplevel is using + * this function, so we lay out our miptrees accordingly. + */ +static bool +size_is_lt(uint32_t width, uint32_t height, int cpp) +{ + return (width <= 4 * utile_width(cpp) || + height <= 4 * utile_height(cpp)); +} + static bool vc4_use_bo(struct exec_info *exec, uint32_t hindex, @@ -105,47 +152,50 @@ check_tex_size(struct exec_info *exec, struct drm_gem_cma_object *fbo, uint32_t offset, uint8_t tiling_format, uint32_t width, uint32_t height, uint8_t cpp) { - uint32_t width_align, height_align; - uint32_t aligned_row_len, aligned_h, size; + uint32_t aligned_width, aligned_height, stride, size; + uint32_t utile_w = utile_width(cpp); + uint32_t utile_h = utile_height(cpp); + + /* The values are limited by the packet/texture parameter bitfields, + * so we don't need to worry as much about integer overflow. + */ + BUG_ON(width > 65535); + BUG_ON(height > 65535); switch (tiling_format) { case VC4_TILING_FORMAT_LINEAR: - width_align = 16; - height_align = 1; + aligned_width = roundup(width, 16 / cpp); + aligned_height = height; break; case VC4_TILING_FORMAT_T: - width_align = 128; - height_align = 32; + aligned_width = roundup(width, utile_w * 8); + aligned_height = roundup(height, utile_h * 8); break; case VC4_TILING_FORMAT_LT: - width_align = 16; - height_align = 4; + aligned_width = roundup(width, utile_w); + aligned_height = roundup(height, utile_h); break; default: DRM_ERROR("buffer tiling %d unsupported\n", tiling_format); return false; } - /* The values are limited by the packet/texture parameter bitfields, - * so we don't need to worry as much about integer overflow. - */ - BUG_ON(width > 65535); - BUG_ON(height > 65535); + stride = aligned_width * cpp; - aligned_row_len = roundup(width * cpp, width_align); - aligned_h = roundup(height, height_align); - - if (INT_MAX / aligned_row_len < aligned_h) { - DRM_ERROR("Overflow in fbo size (%d * %d)\n", - aligned_row_len, aligned_h); + if (INT_MAX / stride < aligned_height) { + DRM_ERROR("Overflow in fbo size (%dx%d -> %dx%d)\n", + width, height, + aligned_width, aligned_height); return false; } - size = aligned_row_len * aligned_h; + size = stride * aligned_height; if (size + offset < size || size + offset > fbo->base.size) { - DRM_ERROR("Overflow in %dx%d fbo size (%d + %d > %d)\n", - width, height, size, offset, fbo->base.size); + DRM_ERROR("Overflow in %dx%d (%dx%d) fbo size (%d + %d > %d)\n", + width, height, + aligned_width, aligned_height, + size, offset, fbo->base.size); return false; } @@ -703,11 +753,12 @@ reloc_tex(struct exec_info *exec, uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]); uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; uint32_t offset = p0 & ~0xfff; - uint32_t miplevels = (p0 & 0x15); + uint32_t miplevels = (p0 & 15); uint32_t width = (p1 >> 8) & 2047; uint32_t height = (p1 >> 20) & 2047; - uint32_t type, cpp, tiling_format; + uint32_t cpp, tiling_format, utile_w, utile_h; uint32_t i; + enum vc4_texture_data_type type; if (width == 0) width = 2048; @@ -722,40 +773,44 @@ reloc_tex(struct exec_info *exec, type = ((p0 >> 4) & 15) | ((p1 >> 31) << 4); switch (type) { - case 0: /* RGBA8888 */ - case 1: /* RGBX8888 */ - case 16: /* RGBA32R */ + case VC4_TEXTURE_TYPE_RGBA8888: + case VC4_TEXTURE_TYPE_RGBX8888: + case VC4_TEXTURE_TYPE_RGBA32R: cpp = 4; break; - case 2: /* RGBA4444 */ - case 3: /* RGBA5551 */ - case 4: /* RGB565 */ - case 7: /* LUMALPHA */ - case 9: /* S16F */ - case 11: /* S16 */ + case VC4_TEXTURE_TYPE_RGBA4444: + case VC4_TEXTURE_TYPE_RGBA5551: + case VC4_TEXTURE_TYPE_RGB565: + case VC4_TEXTURE_TYPE_LUMALPHA: + case VC4_TEXTURE_TYPE_S16F: + case VC4_TEXTURE_TYPE_S16: cpp = 2; break; - case 5: /* LUMINANCE */ - case 6: /* ALPHA */ - case 10: /* S8 */ + case VC4_TEXTURE_TYPE_LUMINANCE: + case VC4_TEXTURE_TYPE_ALPHA: + case VC4_TEXTURE_TYPE_S8: cpp = 1; break; - case 8: /* ETC1 */ - case 12: /* BW1 */ - case 13: /* A4 */ - case 14: /* A1 */ - case 15: /* RGBA64 */ - case 17: /* YUV422R */ + case VC4_TEXTURE_TYPE_ETC1: + case VC4_TEXTURE_TYPE_BW1: + case VC4_TEXTURE_TYPE_A4: + case VC4_TEXTURE_TYPE_A1: + case VC4_TEXTURE_TYPE_RGBA64: + case VC4_TEXTURE_TYPE_YUV422R: default: DRM_ERROR("Texture format %d unsupported\n", type); return false; } + utile_w = utile_width(cpp); + utile_h = utile_height(cpp); - if (type == 16) { + if (type == VC4_TEXTURE_TYPE_RGBA32R) { tiling_format = VC4_TILING_FORMAT_LINEAR; } else { - DRM_ERROR("Tiling formats not yet supported\n"); - return false; + if (size_is_lt(width, height, cpp)) + tiling_format = VC4_TILING_FORMAT_LT; + else + tiling_format = VC4_TILING_FORMAT_T; } if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) @@ -770,17 +825,44 @@ reloc_tex(struct exec_info *exec, * sure there is actually space in the BO. */ for (i = 1; i <= miplevels; i++) { - uint32_t level_width = roundup(max(width >> i, 1u), 16 / cpp); + uint32_t level_width = max(width >> i, 1u); uint32_t level_height = max(height >> i, 1u); - uint32_t level_size = level_width * level_height * cpp; + uint32_t aligned_width, aligned_height; + uint32_t level_size; + + /* Once the levels get small enough, they drop from T to LT. */ + if (tiling_format == VC4_TILING_FORMAT_T && + size_is_lt(level_width, level_height, cpp)) { + tiling_format = VC4_TILING_FORMAT_LT; + } + + switch (tiling_format) { + case VC4_TILING_FORMAT_T: + aligned_width = roundup(level_width, utile_w * 8); + aligned_height = roundup(level_height, utile_h * 8); + break; + case VC4_TILING_FORMAT_LT: + aligned_width = roundup(level_width, utile_w); + aligned_height = roundup(level_height, utile_h); + break; + default: + aligned_width = roundup(level_width, 16 / cpp); + aligned_height = height; + break; + } + + level_size = aligned_width * cpp * aligned_height; if (offset < level_size) { - DRM_ERROR("Level %d (%dx%d) size %db overflowed " - "buffer bounds (offset %d)\n", + DRM_ERROR("Level %d (%dx%d -> %dx%d) size %db " + "overflowed buffer bounds (offset %d)\n", i, level_width, level_height, + aligned_width, aligned_height, level_size, offset); return false; } + + offset -= level_size; } *validated_p0 = tex->paddr + p0; diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index c53807603a51..f7f35cebd228 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -255,6 +255,7 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj, case QPU_SIG_NONE: case QPU_SIG_WAIT_FOR_SCOREBOARD: case QPU_SIG_SCOREBOARD_UNLOCK: + case QPU_SIG_COLOR_LOAD: case QPU_SIG_LOAD_TMU0: case QPU_SIG_LOAD_TMU1: if (!check_instruction_writes(inst, validated_shader, -- cgit v1.2.1 From 41512b2aba7c87395620419a21460341246d42b4 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 17 Sep 2014 15:35:27 -0700 Subject: drm/vc4: Fix validation of store tile buffer general address. The "don't clear some of the tile buffer" bits are in the low bits of the address. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 55de3cd433e5..aa8f09dbe4dc 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -295,7 +295,7 @@ validate_loadstore_tile_buffer_general(VALIDATE_ARGS) if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo)) return -EINVAL; - offset = *(uint32_t *)(untrusted + 2); + offset = *(uint32_t *)(untrusted + 2) & ~0xf; if (!check_tex_size(exec, fbo, offset, ((packet_b0 & -- cgit v1.2.1 From 680af4ecbc4ac8606105c9e42308897641f5ea9a Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 17 Sep 2014 17:51:52 -0700 Subject: drm/vc4: Use real waits with measured timeouts. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 28 +++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_gem.c | 57 +++++++++++++++++++++++++------------------ 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 82482a3fb5f6..39b6ea8a8065 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -233,6 +233,34 @@ void vc4_crtc_unregister(void); int vc4_enable_vblank(struct drm_device *dev, int crtc_id); void vc4_disable_vblank(struct drm_device *dev, int crtc_id); +/** + * _wait_for - magic (register) wait macro + * + * Does the right thing for modeset paths when run under kdgb or similar atomic + * contexts. Note that it's important that we check the condition again after + * having timed out, since the timeout could be due to preemption or similar and + * we've never had a chance to check the condition before the timeout. + */ +#define _wait_for(COND, MS, W) ({ \ + unsigned long timeout__ = jiffies + msecs_to_jiffies(MS) + 1; \ + int ret__ = 0; \ + while (!(COND)) { \ + if (time_after(jiffies, timeout__)) { \ + if (!(COND)) \ + ret__ = -ETIMEDOUT; \ + break; \ + } \ + if (W && drm_can_sleep()) { \ + msleep(W); \ + } else { \ + cpu_relax(); \ + } \ + } \ + ret__; \ +}) + +#define wait_for(COND, MS) _wait_for(COND, MS, 1) + /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); void vc4_debugfs_cleanup(struct drm_minor *minor); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index c449420d06c2..a5695a9e7a8b 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -94,46 +94,55 @@ try_adding_overflow_memory(struct drm_device *dev, struct exec_info *exec) return 0; } +static bool +vc4_thread_finished(struct drm_device *dev, struct exec_info *exec) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + bool stopped = thread_stopped(dev, 0); + + /* If the thread is merely paused waiting for overflow memory, + * hand some to it so we can make progress. + */ + if (V3D_READ(V3D_PCS) & V3D_BMOOM) { + int ret = try_adding_overflow_memory(dev, exec); + return ret != 0; + } + + return stopped; +} + static int wait_for_bin_thread(struct drm_device *dev, struct exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); - int i, ret; + int ret; - for (i = 0; i < 1000000; i++) { - if (thread_stopped(dev, 0)) { - if (V3D_READ(V3D_PCS) & V3D_BMOOM) { - /* XXX */ - DRM_ERROR("binner oom and stopped\n"); - return -EINVAL; - } - return 0; - } + ret = wait_for(vc4_thread_finished(dev, exec), 1000); + if (ret) { + DRM_ERROR("timeout waiting for bin thread idle\n"); + return ret; + } - if (V3D_READ(V3D_PCS) & V3D_BMOOM) { - ret = try_adding_overflow_memory(dev, exec); - if (ret) - return ret; - } + if (V3D_READ(V3D_PCS) & V3D_BMOOM) { + /* XXX */ + DRM_ERROR("binner oom and stopped\n"); + return -EINVAL; } - DRM_ERROR("timeout waiting for bin thread idle\n"); - return -EINVAL; + return 0; } static int wait_for_idle(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; + int ret; - for (i = 0; i < 1000000; i++) { - if (V3D_READ(V3D_PCS) == 0) - return 0; - } + ret = wait_for(V3D_READ(V3D_PCS) == 0, 1000); + if (ret) + DRM_ERROR("timeout waiting for idle\n"); - DRM_ERROR("timeout waiting for idle\n"); - return -EINVAL; + return ret; } /* -- cgit v1.2.1 From dcced03bd1272b9633152daae38cc500afd3d5ff Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 18 Sep 2014 17:49:10 -0700 Subject: drm/vc4: Allow stencil uniform setup. Nothing in this register affects memory access, just how writes impact the tile buffer. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index f7f35cebd228..708c4fe864cb 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -152,7 +152,6 @@ check_register_write(struct vc4_validated_shader_info *validated_shader, case QPU_W_HOST_INT: case QPU_W_TMU_NOSWAP: - case QPU_W_TLB_STENCIL_SETUP: case QPU_W_TLB_ALPHA_MASK: case QPU_W_MUTEX_RELEASE: /* XXX: I haven't thought about these, so don't support them @@ -172,6 +171,9 @@ check_register_write(struct vc4_validated_shader_info *validated_shader, * triggered by QPU_W_VPM_ADDR writes. */ return true; + + case QPU_W_TLB_STENCIL_SETUP: + return true; } return true; -- cgit v1.2.1 From 3f57bab50c243821715b8ed538771d9a28e25d56 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 26 Sep 2014 14:08:37 -0700 Subject: drm/vc4: Don't forget to flush the other VC4 caches. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index a5695a9e7a8b..702d19dc9a6c 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -164,6 +164,23 @@ wait_for_render_thread(struct drm_device *dev, u32 initial_rfc) } */ +static void +vc4_flush_caches(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + V3D_WRITE(V3D_L2CACTL, + V3D_L2CACTL_L2CCLR); + + V3D_WRITE(V3D_SLCACTL, + VC4_SET_FIELD(0xf, V3D_SLCACTL_T1CC) | + VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) | + VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) | + VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC)); + + barrier(); +} + static int vc4_submit(struct drm_device *dev, struct exec_info *exec) { @@ -172,9 +189,7 @@ vc4_submit(struct drm_device *dev, struct exec_info *exec) uint32_t ct1ca = exec->ct1ca, ct1ea = exec->ct1ea; int ret; - /* flushes caches */ - V3D_WRITE(V3D_L2CACTL, 1 << 2); - barrier(); + vc4_flush_caches(dev); /* Disable the binner's pre-loaded overflow memory address */ V3D_WRITE(V3D_BPOA, 0); -- cgit v1.2.1 From fe321deeab8be919a785b676694909d339c0046b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 26 Sep 2014 15:05:06 -0700 Subject: drm/vc4: Wait for the OOM fixup to be acked before continuing. Otherwise, we might try to provide even more overflow memory, before it's been asked for. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 702d19dc9a6c..940807988f40 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -74,6 +74,7 @@ try_adding_overflow_memory(struct drm_device *dev, struct exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_bo_list_entry *entry = kmalloc(sizeof(*entry), GFP_KERNEL); + int ret; if (!entry) return -ENOMEM; @@ -91,6 +92,18 @@ try_adding_overflow_memory(struct drm_device *dev, struct exec_info *exec) V3D_WRITE(V3D_BPOA, entry->bo->paddr); V3D_WRITE(V3D_BPOS, entry->bo->base.size); + /* Wait for the hardware to ack our supplied memory before + * continuing. There's an ugly race here where if the + * hardware gets the request and continues on to overflow + * again before we read the reg, we'll time out. + */ + ret = wait_for((V3D_READ(V3D_PCS) & V3D_BMOOM) == 0, 1000); + if (ret) { + DRM_ERROR("Timed out waiting for hardware to ack " + "overflow memory\n"); + return ret; + } + return 0; } -- cgit v1.2.1 From 98a77991dd8791f1c2f194306a6199cbe807011d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 29 Sep 2014 15:17:40 -0700 Subject: drm/vc4: Fix validation of multi-level raster textures. By not minifying our size, we'd reject valid texture configurations. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index aa8f09dbe4dc..9729a30d7798 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -847,7 +847,7 @@ reloc_tex(struct exec_info *exec, break; default: aligned_width = roundup(level_width, 16 / cpp); - aligned_height = height; + aligned_height = level_height; break; } -- cgit v1.2.1 From f15c062df0d2fbe27635251f827347ec188df79b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 29 Sep 2014 15:18:11 -0700 Subject: drm/vc4: Add support for cube map textures. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 9729a30d7798..1baf1e45dad3 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -751,6 +751,10 @@ reloc_tex(struct exec_info *exec, struct drm_gem_cma_object *tex; uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]); uint32_t p1 = *(uint32_t *)(uniform_data_u + sample->p_offset[1]); + uint32_t p2 = (sample->p_offset[2] != ~0 ? + *(uint32_t *)(uniform_data_u + sample->p_offset[2]) : 0); + uint32_t p3 = (sample->p_offset[3] != ~0 ? + *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0); uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; uint32_t offset = p0 & ~0xfff; uint32_t miplevels = (p0 & 15); @@ -758,6 +762,7 @@ reloc_tex(struct exec_info *exec, uint32_t height = (p1 >> 20) & 2047; uint32_t cpp, tiling_format, utile_w, utile_h; uint32_t i; + uint32_t cube_map_stride = 0; enum vc4_texture_data_type type; if (width == 0) @@ -766,8 +771,20 @@ reloc_tex(struct exec_info *exec, height = 2048; if (p0 & (1 << 9)) { - DRM_ERROR("Cube maps unsupported\n"); - return false; + if ((p2 & (3 << 30)) == (1 << 30)) + cube_map_stride = p2 & 0x3ffff000; + if ((p3 & (3 << 30)) == (1 << 30)) { + if (cube_map_stride) { + DRM_ERROR("Cube map stride set twice\n"); + return false; + } + + cube_map_stride = p3 & 0x3ffff000; + } + if (!cube_map_stride) { + DRM_ERROR("Cube map stride not set\n"); + return false; + } } type = ((p0 >> 4) & 15) | ((p1 >> 31) << 4); @@ -816,8 +833,8 @@ reloc_tex(struct exec_info *exec, if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) return false; - if (!check_tex_size(exec, tex, offset, tiling_format, - width, height, cpp)) { + if (!check_tex_size(exec, tex, offset + cube_map_stride * 5, + tiling_format, width, height, cpp)) { return false; } -- cgit v1.2.1 From 0837817a3a5ce630a4fcd43c952435f104591fcf Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 15 Oct 2014 11:29:23 +0100 Subject: drm/vc4: Flush the CPU cache before firing off command lists. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 940807988f40..5b791b88b5fc 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -182,6 +182,10 @@ vc4_flush_caches(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); + /* Flush the GPU L2 caches. These caches sit on top of system + * L3 (the 128kb or so shared with the CPU), and are + * non-allocating in the L3. + */ V3D_WRITE(V3D_L2CACTL, V3D_L2CACTL_L2CCLR); @@ -191,6 +195,12 @@ vc4_flush_caches(struct drm_device *dev) VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) | VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC)); + /* Flush the CPU L1/L2 caches. Since the GPU reads from L3 + * don't snoop up the L1/L2, we have to either do this or + * manually clflush the cachelines we (and userspace) dirtied. + */ + flush_cache_all(); + barrier(); } -- cgit v1.2.1 From 849e0a0edd98f5488128cf2ea671ea87c97d223d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 15 Oct 2014 14:05:04 +0100 Subject: drm/vc4: Make sure that the thread is not merely parsed, it's finished. The CTnCS register will say it's not running when the thread has finished parsing the command list, but there may be outstanding work still. Instead, we need to look at the PCS bits, which indicate whether the pipeline is totally finished. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 5b791b88b5fc..34ec3db7baf9 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -66,7 +66,7 @@ thread_stopped(struct drm_device *dev, uint32_t thread) struct vc4_dev *vc4 = to_vc4_dev(dev); barrier(); - return !(V3D_READ(V3D_CTNCS(thread)) & V3D_CTRUN); + return !(V3D_READ(V3D_PCS) & (0x3 << thread)); } static int -- cgit v1.2.1 From 73f926307c79091abcce9c72533de51b09f70bb5 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 17 Sep 2014 15:34:27 -0700 Subject: drm/vc4: Try harder at resetting the GPU. Instead of just asking it to please stop the threads and start again, cut the power to the chip. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 34ec3db7baf9..d5eeb6c685f7 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -31,14 +31,13 @@ #include "vc4_regs.h" static void -thread_reset(struct drm_device *dev) +vc4_reset(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - DRM_INFO("Resetting threads\n"); - V3D_WRITE(V3D_CT0CS, V3D_CTRSTA); - V3D_WRITE(V3D_CT1CS, V3D_CTRSTA); - barrier(); + DRM_INFO("Resetting GPU.\n"); + vc4_v3d_set_power(vc4, false); + vc4_v3d_set_power(vc4, true); } static void @@ -137,8 +136,7 @@ wait_for_bin_thread(struct drm_device *dev, struct exec_info *exec) } if (V3D_READ(V3D_PCS) & V3D_BMOOM) { - /* XXX */ - DRM_ERROR("binner oom and stopped\n"); + DRM_ERROR("binner oom and stopped.\n"); return -EINVAL; } @@ -452,7 +450,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, ret = vc4_submit(dev, &exec); if (ret) { - thread_reset(dev); + vc4_reset(dev); goto fail; } -- cgit v1.2.1 From eab29b82f179a733e0d4e21bd33b757b0dfd57d4 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 15 Oct 2014 14:33:33 +0100 Subject: drm/vc4: Rename the "check if bin needs OOM handling or is idle" function. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index d5eeb6c685f7..0f3743a5a8de 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -107,7 +107,7 @@ try_adding_overflow_memory(struct drm_device *dev, struct exec_info *exec) } static bool -vc4_thread_finished(struct drm_device *dev, struct exec_info *exec) +vc4_bin_finished(struct drm_device *dev, struct exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); bool stopped = thread_stopped(dev, 0); @@ -129,7 +129,7 @@ wait_for_bin_thread(struct drm_device *dev, struct exec_info *exec) struct vc4_dev *vc4 = to_vc4_dev(dev); int ret; - ret = wait_for(vc4_thread_finished(dev, exec), 1000); + ret = wait_for(vc4_bin_finished(dev, exec), 1000); if (ret) { DRM_ERROR("timeout waiting for bin thread idle\n"); return ret; -- cgit v1.2.1 From 6b6ad00fbb3f3e7dca47f82f6563a619c920d202 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 15 Oct 2014 14:34:57 +0100 Subject: drm/vc4: We only need to wait for render idle after we submit render. Of course, no bins can be submitted during this time anyway, so it's not really relevant, but it cleans up the code. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 0f3743a5a8de..c9fdc365ff0f 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -144,36 +144,18 @@ wait_for_bin_thread(struct drm_device *dev, struct exec_info *exec) } static int -wait_for_idle(struct drm_device *dev) +wait_for_render_thread(struct drm_device *dev) { - struct vc4_dev *vc4 = to_vc4_dev(dev); int ret; - ret = wait_for(V3D_READ(V3D_PCS) == 0, 1000); - if (ret) - DRM_ERROR("timeout waiting for idle\n"); - - return ret; -} - -/* -static int -wait_for_render_thread(struct drm_device *dev, u32 initial_rfc) -{ - struct vc4_dev *vc4 = to_vc4_dev(dev); - int i; - - for (i = 0; i < 1000000; i++) { - if ((V3D_READ(V3D_RFC) & 0xff) == ((initial_rfc + 1) & 0xff)) - return 0; + ret = wait_for(thread_stopped(dev, 1), 1000); + if (ret) { + DRM_ERROR("timeout waiting for render thread idle\n"); + return ret; } - DRM_ERROR("timeout waiting for render thread idle: " - "0x%08x start vs 0x%08x end\n", - initial_rfc, V3D_READ(V3D_RFC)); - return -EINVAL; + return 0; } -*/ static void vc4_flush_caches(struct drm_device *dev) @@ -222,10 +204,6 @@ vc4_submit(struct drm_device *dev, struct exec_info *exec) if (ret) return ret; - ret = wait_for_idle(dev); - if (ret) - return ret; - WARN_ON(!thread_stopped(dev, 0)); if (V3D_READ(V3D_CTNCS(0)) & V3D_CTERR) { DRM_ERROR("thread 0 stopped with error\n"); @@ -234,7 +212,7 @@ vc4_submit(struct drm_device *dev, struct exec_info *exec) submit_cl(dev, 1, ct1ca, ct1ea); - ret = wait_for_idle(dev); + ret = wait_for_render_thread(dev); if (ret) return ret; -- cgit v1.2.1 From 5a7327694bb9c3a207a21499b6ecc09d2adf7cd4 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 17 Oct 2014 12:19:19 +0100 Subject: drm/vc4: Pass through the EOF flag in the low bits of tile store general. We'll use the frame counts to make sure that the render job was actually completed. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 1baf1e45dad3..977e071d22dc 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -268,7 +268,7 @@ validate_loadstore_tile_buffer_general(VALIDATE_ARGS) uint32_t packet_b1 = *(uint8_t *)(untrusted + 1); struct drm_gem_cma_object *fbo; uint32_t buffer_type = packet_b0 & 0xf; - uint32_t offset, cpp; + uint32_t untrusted_address, offset, cpp; switch (buffer_type) { case VC4_LOADSTORE_TILE_BUFFER_NONE: @@ -295,7 +295,8 @@ validate_loadstore_tile_buffer_general(VALIDATE_ARGS) if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo)) return -EINVAL; - offset = *(uint32_t *)(untrusted + 2) & ~0xf; + untrusted_address = *(uint32_t *)(untrusted + 2); + offset = untrusted_address & ~0xf; if (!check_tex_size(exec, fbo, offset, ((packet_b0 & @@ -305,7 +306,8 @@ validate_loadstore_tile_buffer_general(VALIDATE_ARGS) return -EINVAL; } - *(uint32_t *)(validated + 2) = offset + fbo->paddr; + *(uint32_t *)(validated + 2) = (offset + fbo->paddr + + (untrusted_address & 0xf)); return 0; } -- cgit v1.2.1 From 048a333da6064c02b847e6e4a4f0be1f615a9195 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 29 Oct 2014 13:39:03 -0700 Subject: drm/vc4: Add support for validating direct-address TMU reads. This is used for variable-index uniform array lookups in GLSL. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 1 + drivers/gpu/drm/vc4/vc4_validate.c | 20 ++- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 189 +++++++++++++++++++++++++---- 3 files changed, 182 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 39b6ea8a8065..b6842eeb0bee 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -197,6 +197,7 @@ struct exec_info { * Setup") for definitions of the texture parameters. */ struct vc4_texture_sample_info { + bool is_direct; uint32_t p_offset[4]; }; diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 977e071d22dc..8b04eb991958 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -767,6 +767,23 @@ reloc_tex(struct exec_info *exec, uint32_t cube_map_stride = 0; enum vc4_texture_data_type type; + if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) + return false; + + if (sample->is_direct) { + uint32_t remaining_size = tex->base.size - p0; + if (p0 > tex->base.size - 4) { + DRM_ERROR("UBO offset greater than UBO size\n"); + return false; + } + if (p1 > remaining_size - 4) { + DRM_ERROR("UBO clamp would allow reads outside of UBO\n"); + return false; + } + *validated_p0 = tex->paddr + p0; + return true; + } + if (width == 0) width = 2048; if (height == 0) @@ -832,9 +849,6 @@ reloc_tex(struct exec_info *exec, tiling_format = VC4_TILING_FORMAT_T; } - if (!vc4_use_bo(exec, texture_handle_index, VC4_MODE_RENDER, &tex)) - return false; - if (!check_tex_size(exec, tex, offset + cube_map_stride * 5, tiling_format, width, height, cpp)) { return false; diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 708c4fe864cb..97032aa4e197 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -50,8 +50,39 @@ struct vc4_shader_validation_state { struct vc4_texture_sample_info tmu_setup[2]; int tmu_write_count[2]; + + /* For registers that were last written to by a MIN instruction with + * one argument being a uniform, the address of the uniform. + * Otherwise, ~0. + * + * This is used for the validation of direct address memory reads. + */ + uint32_t live_clamp_offsets[32 + 32 + 4]; }; +static uint32_t +waddr_to_live_reg_index(uint32_t waddr, bool is_b) +{ + if (waddr < 32) { + if (is_b) + return 32 + waddr; + else + return waddr; + } else if (waddr <= QPU_W_ACC3) { + + return 64 + waddr - QPU_W_ACC0; + } else { + return ~0; + } +} + +static bool +is_tmu_submit(uint32_t waddr) +{ + return (waddr == QPU_W_TMU0_S || + waddr == QPU_W_TMU1_S); +} + static bool is_tmu_write(uint32_t waddr) { @@ -74,27 +105,86 @@ record_validated_texture_sample(struct vc4_validated_shader_info *validated_shad if (!temp_samples) return false; - memcpy(temp_samples[s].p_offset, - validation_state->tmu_setup[tmu].p_offset, - validation_state->tmu_write_count[tmu] * sizeof(uint32_t)); - for (i = validation_state->tmu_write_count[tmu]; i < 4; i++) - temp_samples[s].p_offset[i] = ~0; + memcpy(&temp_samples[s], + &validation_state->tmu_setup[tmu], + sizeof(*temp_samples)); validated_shader->num_texture_samples = s + 1; validated_shader->texture_samples = temp_samples; + for (i = 0; i < 4; i++) + validation_state->tmu_setup[tmu].p_offset[i] = ~0; + return true; } static bool -check_tmu_write(struct vc4_validated_shader_info *validated_shader, +check_tmu_write(uint64_t inst, + struct vc4_validated_shader_info *validated_shader, struct vc4_shader_validation_state *validation_state, - uint32_t waddr) + bool is_mul) { + uint32_t waddr = (is_mul ? + QPU_GET_FIELD(inst, QPU_WADDR_MUL) : + QPU_GET_FIELD(inst, QPU_WADDR_ADD)); + uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); + uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); int tmu = waddr > QPU_W_TMU0_B; + bool submit = is_tmu_submit(waddr); + bool is_direct = submit && validation_state->tmu_write_count[tmu] == 0; - if (!is_tmu_write(waddr)) - return true; + if (is_direct) { + uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); + uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); + uint32_t clamp_offset = ~0; + + /* Make sure that this texture load is an add of the base + * address of the UBO to a clamped offset within the UBO. + */ + if (is_mul || + QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_ADD) { + DRM_ERROR("direct TMU load wasn't an add\n"); + return false; + } + + /* We assert that the the clamped address is the first + * argument, and the UBO base address is the second argument. + * This is arbitrary, but simpler than supporting flipping the + * two either way. + */ + if (add_a == QPU_MUX_A) { + clamp_offset = validation_state->live_clamp_offsets[raddr_a]; + } else if (add_a == QPU_MUX_B) { + clamp_offset = validation_state->live_clamp_offsets[32 + raddr_b]; + } else if (add_a <= QPU_MUX_R4) { + clamp_offset = validation_state->live_clamp_offsets[64 + add_a]; + } + + if (clamp_offset == ~0) { + DRM_ERROR("direct TMU load wasn't clamped\n"); + return false; + } + + /* Store the clamp value's offset in p1 (see reloc_tex() in + * vc4_validate.c). + */ + validation_state->tmu_setup[tmu].p_offset[1] = + clamp_offset; + + if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && + !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF)) { + DRM_ERROR("direct TMU load didn't add to a uniform\n"); + return false; + } + + validation_state->tmu_setup[tmu].is_direct = true; + } else { + if (raddr_a == QPU_R_UNIF || raddr_b == QPU_R_UNIF) { + DRM_ERROR("uniform read in the same instruction as " + "texture setup.\n"); + return false; + } + } if (validation_state->tmu_write_count[tmu] >= 4) { DRM_ERROR("TMU%d got too many parameters before dispatch\n", @@ -104,9 +194,13 @@ check_tmu_write(struct vc4_validated_shader_info *validated_shader, validation_state->tmu_setup[tmu].p_offset[validation_state->tmu_write_count[tmu]] = validated_shader->uniforms_size; validation_state->tmu_write_count[tmu]++; - validated_shader->uniforms_size += 4; + /* Since direct uses a RADDR uniform reference, it will get counted in + * check_instruction_reads() + */ + if (!is_direct) + validated_shader->uniforms_size += 4; - if (waddr == QPU_W_TMU0_S || waddr == QPU_W_TMU1_S) { + if (submit) { if (!record_validated_texture_sample(validated_shader, validation_state, tmu)) { return false; @@ -119,10 +213,17 @@ check_tmu_write(struct vc4_validated_shader_info *validated_shader, } static bool -check_register_write(struct vc4_validated_shader_info *validated_shader, +check_register_write(uint64_t inst, + struct vc4_validated_shader_info *validated_shader, struct vc4_shader_validation_state *validation_state, - uint32_t waddr) + bool is_mul) { + uint32_t waddr = (is_mul ? + QPU_GET_FIELD(inst, QPU_WADDR_MUL) : + QPU_GET_FIELD(inst, QPU_WADDR_ADD)); + bool is_b = is_mul != ((inst & QPU_PM) != 0); + uint32_t live_reg_index; + switch (waddr) { case QPU_W_UNIFORMS_ADDRESS: /* XXX: We'll probably need to support this for reladdr, but @@ -147,8 +248,8 @@ check_register_write(struct vc4_validated_shader_info *validated_shader, case QPU_W_TMU1_T: case QPU_W_TMU1_R: case QPU_W_TMU1_B: - return check_tmu_write(validated_shader, validation_state, - waddr); + return check_tmu_write(inst, validated_shader, validation_state, + is_mul); case QPU_W_HOST_INT: case QPU_W_TMU_NOSWAP: @@ -176,9 +277,44 @@ check_register_write(struct vc4_validated_shader_info *validated_shader, return true; } + /* Clear out the live offset clamp tracking for the written register. + * If this particular instruction is setting up an offset clamp, it'll + * get tracked immediately after we return. + */ + live_reg_index = waddr_to_live_reg_index(waddr, is_b); + if (live_reg_index != ~0) + validation_state->live_clamp_offsets[live_reg_index] = ~0; + return true; } +static void +track_live_clamps(uint64_t inst, + struct vc4_validated_shader_info *validated_shader, + struct vc4_shader_validation_state *validation_state) +{ + uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); + uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); + uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); + uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); + bool pm = inst & QPU_PM; + uint32_t live_reg_index; + + if (QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_MIN) + return; + + if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && + !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF)) { + return; + } + + live_reg_index = waddr_to_live_reg_index(waddr_add, pm); + if (live_reg_index != ~0) { + validation_state->live_clamp_offsets[live_reg_index] = + validated_shader->uniforms_size; + } +} + static bool check_instruction_writes(uint64_t inst, struct vc4_validated_shader_info *validated_shader, @@ -186,33 +322,30 @@ check_instruction_writes(uint64_t inst, { uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); + bool ok; if (is_tmu_write(waddr_add) && is_tmu_write(waddr_mul)) { DRM_ERROR("ADD and MUL both set up textures\n"); return false; } - return (check_register_write(validated_shader, validation_state, waddr_add) && - check_register_write(validated_shader, validation_state, waddr_mul)); + ok = (check_register_write(inst, validated_shader, validation_state, false) && + check_register_write(inst, validated_shader, validation_state, true)); + + track_live_clamps(inst, validated_shader, validation_state); + + return ok; } static bool check_instruction_reads(uint64_t inst, struct vc4_validated_shader_info *validated_shader) { - uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); - uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); if (raddr_a == QPU_R_UNIF || raddr_b == QPU_R_UNIF) { - if (is_tmu_write(waddr_add) || is_tmu_write(waddr_mul)) { - DRM_ERROR("uniform read in the same instruction as " - "texture setup"); - return false; - } - /* This can't overflow the uint32_t, because we're reading 8 * bytes of instruction to increment by 4 here, so we'd * already be OOM. @@ -233,9 +366,15 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj, uint64_t *shader; struct vc4_validated_shader_info *validated_shader; struct vc4_shader_validation_state validation_state; + int i; memset(&validation_state, 0, sizeof(validation_state)); + for (i = 0; i < 8; i++) + validation_state.tmu_setup[i / 4].p_offset[i % 4] = ~0; + for (i = 0; i < ARRAY_SIZE(validation_state.live_clamp_offsets); i++) + validation_state.live_clamp_offsets[i] = ~0; + if (start_offset + sizeof(uint64_t) > shader_obj->base.size) { DRM_ERROR("shader starting at %d outside of BO sized %d\n", start_offset, -- cgit v1.2.1 From 8d943471316c6f6c8a57f2a3fce9bd776c010cd5 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 29 Oct 2014 13:39:57 -0700 Subject: drm/vc4: Fix failure to validate instructions with PROG_END flagged. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 97032aa4e197..752c873f3aa7 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -399,6 +399,7 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj, case QPU_SIG_COLOR_LOAD: case QPU_SIG_LOAD_TMU0: case QPU_SIG_LOAD_TMU1: + case QPU_SIG_PROG_END: if (!check_instruction_writes(inst, validated_shader, &validation_state)) { DRM_ERROR("Bad write at ip %d\n", ip); @@ -408,6 +409,11 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj, if (!check_instruction_reads(inst, validated_shader)) goto fail; + if (sig == QPU_SIG_PROG_END) { + found_shader_end = true; + shader_end_ip = ip; + } + break; case QPU_SIG_LOAD_IMM: @@ -418,11 +424,6 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj, } break; - case QPU_SIG_PROG_END: - found_shader_end = true; - shader_end_ip = ip; - break; - default: DRM_ERROR("Unsupported QPU signal %d at " "instruction %d\n", sig, ip); -- cgit v1.2.1 From 3c5db5e5e5f77d6a589b3a11f18d6a5e7744fec7 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 17 Nov 2014 20:00:41 -0800 Subject: drm/vc4: Use the semaphore to trigger render from bin. Instead of making userland submit the right structure of their frames, we just tack on the prefix for them. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 2 + drivers/gpu/drm/vc4/vc4_gem.c | 37 +++--------------- drivers/gpu/drm/vc4/vc4_validate.c | 77 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 34 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index b6842eeb0bee..bf0d749a312f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -151,6 +151,8 @@ struct exec_info { bool found_tile_binning_mode_config_packet; bool found_tile_rendering_mode_config_packet; bool found_start_tile_binning_packet; + bool found_increment_semaphore_packet; + bool found_wait_on_semaphore_packet; uint8_t bin_tiles_x, bin_tiles_y; uint32_t fb_width, fb_height; uint32_t tile_alloc_init_block_size; diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index c9fdc365ff0f..9dc661ee3798 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -107,10 +107,10 @@ try_adding_overflow_memory(struct drm_device *dev, struct exec_info *exec) } static bool -vc4_bin_finished(struct drm_device *dev, struct exec_info *exec) +vc4_job_finished(struct drm_device *dev, struct exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); - bool stopped = thread_stopped(dev, 0); + bool stopped = thread_stopped(dev, 1); /* If the thread is merely paused waiting for overflow memory, * hand some to it so we can make progress. @@ -124,14 +124,14 @@ vc4_bin_finished(struct drm_device *dev, struct exec_info *exec) } static int -wait_for_bin_thread(struct drm_device *dev, struct exec_info *exec) +wait_for_job(struct drm_device *dev, struct exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); int ret; - ret = wait_for(vc4_bin_finished(dev, exec), 1000); + ret = wait_for(vc4_job_finished(dev, exec), 1000); if (ret) { - DRM_ERROR("timeout waiting for bin thread idle\n"); + DRM_ERROR("timeout waiting for render thread idle\n"); return ret; } @@ -143,20 +143,6 @@ wait_for_bin_thread(struct drm_device *dev, struct exec_info *exec) return 0; } -static int -wait_for_render_thread(struct drm_device *dev) -{ - int ret; - - ret = wait_for(thread_stopped(dev, 1), 1000); - if (ret) { - DRM_ERROR("timeout waiting for render thread idle\n"); - return ret; - } - - return 0; -} - static void vc4_flush_caches(struct drm_device *dev) { @@ -199,20 +185,9 @@ vc4_submit(struct drm_device *dev, struct exec_info *exec) V3D_WRITE(V3D_BPOS, 0); submit_cl(dev, 0, ct0ca, ct0ea); - - ret = wait_for_bin_thread(dev, exec); - if (ret) - return ret; - - WARN_ON(!thread_stopped(dev, 0)); - if (V3D_READ(V3D_CTNCS(0)) & V3D_CTERR) { - DRM_ERROR("thread 0 stopped with error\n"); - return -EINVAL; - } - submit_cl(dev, 1, ct1ca, ct1ea); - ret = wait_for_render_thread(dev); + ret = wait_for_job(dev, exec); if (ret) return ret; diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 8b04eb991958..ba6e46f20411 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -202,6 +202,18 @@ check_tex_size(struct exec_info *exec, struct drm_gem_cma_object *fbo, return true; } +static int +validate_flush_all(VALIDATE_ARGS) +{ + if (exec->found_increment_semaphore_packet) { + DRM_ERROR("VC4_PACKET_FLUSH_ALL after " + "VC4_PACKET_INCREMENT_SEMAPHORE\n"); + return -EINVAL; + } + + return 0; +} + static int validate_start_tile_binning(VALIDATE_ARGS) { @@ -219,6 +231,41 @@ validate_start_tile_binning(VALIDATE_ARGS) return 0; } +static int +validate_increment_semaphore(VALIDATE_ARGS) +{ + if (exec->found_increment_semaphore_packet) { + DRM_ERROR("Duplicate VC4_PACKET_INCREMENT_SEMAPHORE\n"); + return -EINVAL; + } + exec->found_increment_semaphore_packet = true; + + /* Once we've found the semaphore increment, there should be one FLUSH + * then the end of the command list. The FLUSH actually triggers the + * increment, so we only need to make sure there + */ + + return 0; +} + +static int +validate_wait_on_semaphore(VALIDATE_ARGS) +{ + if (exec->found_wait_on_semaphore_packet) { + DRM_ERROR("Duplicate VC4_PACKET_WAIT_ON_SEMAPHORE\n"); + return -EINVAL; + } + exec->found_wait_on_semaphore_packet = true; + + if (!exec->found_increment_semaphore_packet) { + DRM_ERROR("VC4_PACKET_WAIT_ON_SEMAPHORE without " + "VC4_PACKET_INCREMENT_SEMAPHORE\n"); + return -EINVAL; + } + + return 0; +} + static int validate_branch_to_sublist(VALIDATE_ARGS) { @@ -233,6 +280,11 @@ validate_branch_to_sublist(VALIDATE_ARGS) return -EINVAL; } + if (!exec->found_wait_on_semaphore_packet) { + DRM_ERROR("Jumping to tile alloc before binning finished.\n"); + return -EINVAL; + } + offset = *(uint32_t *)(untrusted + 0); if (offset % exec->tile_alloc_init_block_size || offset / exec->tile_alloc_init_block_size > @@ -322,6 +374,11 @@ validate_indexed_prim_list(VALIDATE_ARGS) uint32_t index_size = (*(uint8_t *)(untrusted + 0) >> 4) ? 2 : 1; struct vc4_shader_state *shader_state; + if (exec->found_increment_semaphore_packet) { + DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); + return -EINVAL; + } + /* Check overflow condition */ if (exec->shader_state_count == 0) { DRM_ERROR("shader state must precede primitives\n"); @@ -355,6 +412,11 @@ validate_gl_array_primitive(VALIDATE_ARGS) uint32_t max_index; struct vc4_shader_state *shader_state; + if (exec->found_increment_semaphore_packet) { + DRM_ERROR("Drawing after VC4_PACKET_INCREMENT_SEMAPHORE\n"); + return -EINVAL; + } + /* Check overflow condition */ if (exec->shader_state_count == 0) { DRM_ERROR("shader state must precede primitives\n"); @@ -600,10 +662,10 @@ static const struct cmd_info { [VC4_PACKET_HALT] = { 1, 1, 1, "halt", NULL }, [VC4_PACKET_NOP] = { 1, 1, 1, "nop", NULL }, [VC4_PACKET_FLUSH] = { 1, 1, 1, "flush", NULL }, - [VC4_PACKET_FLUSH_ALL] = { 1, 0, 1, "flush all state", NULL }, + [VC4_PACKET_FLUSH_ALL] = { 1, 0, 1, "flush all state", validate_flush_all }, [VC4_PACKET_START_TILE_BINNING] = { 1, 0, 1, "start tile binning", validate_start_tile_binning }, - [VC4_PACKET_INCREMENT_SEMAPHORE] = { 1, 0, 1, "increment semaphore", NULL }, - [VC4_PACKET_WAIT_ON_SEMAPHORE] = { 1, 1, 1, "wait on semaphore", NULL }, + [VC4_PACKET_INCREMENT_SEMAPHORE] = { 1, 0, 1, "increment semaphore", validate_increment_semaphore }, + [VC4_PACKET_WAIT_ON_SEMAPHORE] = { 0, 1, 1, "wait on semaphore", validate_wait_on_semaphore }, /* BRANCH_TO_SUB_LIST is actually supported in the binner as well, but * we only use it from the render CL in order to jump into the tile * allocation BO. @@ -737,6 +799,15 @@ vc4_validate_cl(struct drm_device *dev, DRM_ERROR("Render CL missing VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n"); return -EINVAL; } + + /* Make sure that they actually consumed the semaphore + * increment from the bin CL. Otherwise a later submit would + * have render execute immediately. + */ + if (!exec->found_wait_on_semaphore_packet) { + DRM_ERROR("Render CL missing VC4_PACKET_WAIT_ON_SEMAPHORE\n"); + return -EINVAL; + } exec->ct1ea = exec->ct1ca + dst_offset; } -- cgit v1.2.1 From 631c7adff119fc2fedb09e64a9e2057501648d59 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 16 Oct 2014 10:48:30 +0100 Subject: drm/vc4: Use interrupts for binner overflow memory allocation handling. This avoids some of the checks in the spinloop waiting for frame done, and avoids the race I had documented, as far as I know. Note that this reportedly requires "mask_gpu_interrupt0=0x400" in config.txt to keep the VPU firmware from trying to do things based on our interrupts. v2: Rebase on 3.19 DRM core APIs. v3: Rebase on the KMS work. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/Makefile | 1 + drivers/gpu/drm/vc4/vc4_drv.c | 8 +++ drivers/gpu/drm/vc4/vc4_drv.h | 20 +++++-- drivers/gpu/drm/vc4/vc4_gem.c | 79 +++++--------------------- drivers/gpu/drm/vc4/vc4_irq.c | 129 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_v3d.c | 8 +++ 6 files changed, 174 insertions(+), 71 deletions(-) create mode 100644 drivers/gpu/drm/vc4/vc4_irq.c diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index 09dad06a75b9..0abf23d62f9e 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -11,6 +11,7 @@ vc4-y := \ vc4_gem.o \ vc4_hdmi.o \ vc4_hvs.o \ + vc4_irq.o \ vc4_plane.o \ vc4_v3d.o \ vc4_validate.o \ diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index ba1145a98e38..433be8c09efa 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -58,6 +58,8 @@ vc4_drm_load(struct drm_device *dev, unsigned long flags) if (!vc4) return -ENOMEM; + INIT_LIST_HEAD(&vc4->overflow_list); + vc4->firmware_node = of_parse_phandle(dev->dev->of_node, "firmware", 0); if (!vc4->firmware_node) { DRM_ERROR("Failed to parse firmware node.\n"); @@ -114,11 +116,17 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { static struct drm_driver vc4_drm_driver = { .driver_features = (DRIVER_MODESET | DRIVER_GEM | + DRIVER_HAVE_IRQ | DRIVER_PRIME), .load = vc4_drm_load, .unload = vc4_drm_unload, .set_busid = drm_platform_set_busid, + .irq_handler = vc4_irq, + .irq_preinstall = vc4_irq_preinstall, + .irq_postinstall = vc4_irq_postinstall, + .irq_uninstall = vc4_irq_uninstall, + .enable_vblank = vc4_enable_vblank, .disable_vblank = vc4_disable_vblank, .get_vblank_counter = drm_vblank_count, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index bf0d749a312f..8b745c3bac40 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -18,6 +18,13 @@ struct vc4_dev { struct vc4_hvs *hvs; struct vc4_crtc *crtc[3]; struct vc4_v3d *v3d; + + /* List of struct vc4_list_bo_entry allocated to accomodate + * binner overflow. These will be freed when the exec is + * done. + */ + struct list_head overflow_list; + struct work_struct overflow_mem_work; }; static inline struct vc4_dev * @@ -122,12 +129,6 @@ struct exec_info { */ struct drm_gem_cma_object *exec_bo; - /* List of struct vc4_list_bo_entry allocated to accomodate - * binner overflow. These will be freed when the exec is - * done. - */ - struct list_head overflow_list; - /** * This tracks the per-shader-record state (packet 64) that * determines the length of the shader record and the offset @@ -283,6 +284,13 @@ struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, struct drm_encoder *encoder); int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); +/* vc4_irq.c */ +irqreturn_t vc4_irq(int irq, void *arg); +void vc4_irq_preinstall(struct drm_device *dev); +int vc4_irq_postinstall(struct drm_device *dev); +void vc4_irq_uninstall(struct drm_device *dev); +void vc4_irq_reset(struct drm_device *dev); + /* vc4_hvs.c */ void vc4_hvs_register(void); void vc4_hvs_unregister(void); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 9dc661ee3798..3c38e41bd41f 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -38,6 +38,8 @@ vc4_reset(struct drm_device *dev) DRM_INFO("Resetting GPU.\n"); vc4_v3d_set_power(vc4, false); vc4_v3d_set_power(vc4, true); + + vc4_irq_reset(dev); } static void @@ -68,78 +70,17 @@ thread_stopped(struct drm_device *dev, uint32_t thread) return !(V3D_READ(V3D_PCS) & (0x3 << thread)); } -static int -try_adding_overflow_memory(struct drm_device *dev, struct exec_info *exec) -{ - struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_bo_list_entry *entry = kmalloc(sizeof(*entry), GFP_KERNEL); - int ret; - - if (!entry) - return -ENOMEM; - - entry->bo = drm_gem_cma_create(dev, 256 * 1024); - if (IS_ERR(entry->bo)) { - int ret = PTR_ERR(entry->bo); - DRM_ERROR("Couldn't allocate binner overflow mem\n"); - kfree(entry); - return ret; - } - - list_add_tail(&entry->head, &exec->overflow_list); - - V3D_WRITE(V3D_BPOA, entry->bo->paddr); - V3D_WRITE(V3D_BPOS, entry->bo->base.size); - - /* Wait for the hardware to ack our supplied memory before - * continuing. There's an ugly race here where if the - * hardware gets the request and continues on to overflow - * again before we read the reg, we'll time out. - */ - ret = wait_for((V3D_READ(V3D_PCS) & V3D_BMOOM) == 0, 1000); - if (ret) { - DRM_ERROR("Timed out waiting for hardware to ack " - "overflow memory\n"); - return ret; - } - - return 0; -} - -static bool -vc4_job_finished(struct drm_device *dev, struct exec_info *exec) -{ - struct vc4_dev *vc4 = to_vc4_dev(dev); - bool stopped = thread_stopped(dev, 1); - - /* If the thread is merely paused waiting for overflow memory, - * hand some to it so we can make progress. - */ - if (V3D_READ(V3D_PCS) & V3D_BMOOM) { - int ret = try_adding_overflow_memory(dev, exec); - return ret != 0; - } - - return stopped; -} - static int wait_for_job(struct drm_device *dev, struct exec_info *exec) { - struct vc4_dev *vc4 = to_vc4_dev(dev); int ret; - ret = wait_for(vc4_job_finished(dev, exec), 1000); + ret = wait_for(thread_stopped(dev, 1), 1000); if (ret) { DRM_ERROR("timeout waiting for render thread idle\n"); return ret; } - if (V3D_READ(V3D_PCS) & V3D_BMOOM) { - DRM_ERROR("binner oom and stopped.\n"); - return -EINVAL; - } - return 0; } @@ -383,13 +324,13 @@ int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct vc4_dev *vc4 = to_vc4_dev(dev); struct exec_info exec; int ret; int i; memset(&exec, 0, sizeof(exec)); exec.args = data; - INIT_LIST_HEAD(&exec.overflow_list); mutex_lock(&dev->struct_mutex); @@ -414,10 +355,18 @@ fail: kfree(exec.bo); } - while (!list_empty(&exec.overflow_list)) { + /* Release all but the very last overflow list entry (which is + * still sitting in the BPOS/BPOA registers for use by the + * next job). + */ + while (true) { struct vc4_bo_list_entry *entry = - list_first_entry(&exec.overflow_list, + list_first_entry(&vc4->overflow_list, struct vc4_bo_list_entry, head); + + if (entry->head.next == &vc4->overflow_list) + break; + drm_gem_object_unreference(&entry->bo->base); list_del(&entry->head); kfree(entry); diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c new file mode 100644 index 000000000000..dfc302606762 --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -0,0 +1,129 @@ +/* + * Copyright © 2014 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "vc4_drv.h" +#include "vc4_regs.h" + +#define V3D_DRIVER_IRQS V3D_INT_OUTOMEM + +static void +vc4_overflow_mem_work(struct work_struct *work) +{ + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, overflow_mem_work); + struct drm_device *dev = vc4->dev; + struct vc4_bo_list_entry *entry = kmalloc(sizeof(*entry), GFP_KERNEL); + + if (!entry) { + DRM_ERROR("Couldn't allocate binner overflow mem record\n"); + return; + } + + entry->bo = drm_gem_cma_create(dev, 256 * 1024); + if (IS_ERR(entry->bo)) { + DRM_ERROR("Couldn't allocate binner overflow mem\n"); + kfree(entry); + return; + } + + list_add_tail(&entry->head, &vc4->overflow_list); + + V3D_WRITE(V3D_BPOA, entry->bo->paddr); + V3D_WRITE(V3D_BPOS, entry->bo->base.size); + V3D_WRITE(V3D_INTDIS, 0); + V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); +} + +irqreturn_t +vc4_irq(int irq, void *arg) +{ + struct drm_device *dev = arg; + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t intctl; + + barrier(); + intctl = V3D_READ(V3D_INTCTL); + V3D_WRITE(V3D_INTCTL, intctl); + + if (intctl & V3D_INT_OUTOMEM) { + V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); + schedule_work(&vc4->overflow_mem_work); + } + + return intctl ? IRQ_HANDLED : IRQ_NONE; +} + +void +vc4_irq_preinstall(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); + + /* Clear any pending interrupts someone might have left around + * for us. + */ + V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); +} + +int +vc4_irq_postinstall(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + /* Enable both the bin and render done interrupts, as well as + * out of memory. Eventually, we'll have the bin use internal + * semaphores with render to sync between the two, but for now + * we're driving that from the ARM. + */ + V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); + + /* No interrupts disabled. */ + V3D_WRITE(V3D_INTDIS, 0); + + return 0; +} + +void +vc4_irq_uninstall(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + cancel_work_sync(&vc4->overflow_mem_work); + + V3D_WRITE(V3D_INTENA, 0); + V3D_WRITE(V3D_INTDIS, 0); + + /* Clear any pending interrupts we might have left. */ + V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); +} + +/** Reinitializes interrupt registers when a GPU reset is performed. */ +void vc4_irq_reset(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); + V3D_WRITE(V3D_INTDIS, 0); + V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); +} diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index 5a18cf41558a..9321fa2a2f23 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -202,6 +202,12 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) vc4_v3d_init_hw(drm); + ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); + if (ret) { + DRM_ERROR("Failed to install IRQ handler\n"); + return ret; + } + return 0; } @@ -211,6 +217,8 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master, struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = to_vc4_dev(drm); + drm_irq_uninstall(drm); + vc4->v3d = NULL; } -- cgit v1.2.1 From c8ddb165e37f939f405ddfbc24f859fdc9c798dd Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 17 Nov 2014 19:03:22 -0800 Subject: drm/vc4: Use interrupts for frame end handling. We still exec synchronously, but this gets rid of the busy wait. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 3 +++ drivers/gpu/drm/vc4/vc4_gem.c | 39 ++++++++++++++++++++++++++++----------- drivers/gpu/drm/vc4/vc4_irq.c | 14 +++++++++++++- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 8b745c3bac40..4c600e0d3a85 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -19,6 +19,9 @@ struct vc4_dev { struct vc4_crtc *crtc[3]; struct vc4_v3d *v3d; + wait_queue_head_t frame_done_queue; + bool frame_done; + /* List of struct vc4_list_bo_entry allocated to accomodate * binner overflow. These will be freed when the exec is * done. diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 3c38e41bd41f..35e14f81ddbf 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -61,21 +61,37 @@ submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) barrier(); } -static bool -thread_stopped(struct drm_device *dev, uint32_t thread) +static int +vc4_wait_for_job(struct drm_device *dev, struct exec_info *exec, + unsigned timeout) { struct vc4_dev *vc4 = to_vc4_dev(dev); + int ret = 0; + unsigned long timeout_expire; + DEFINE_WAIT(wait); - barrier(); - return !(V3D_READ(V3D_PCS) & (0x3 << thread)); -} + if (vc4->frame_done) + return 0; -static int -wait_for_job(struct drm_device *dev, struct exec_info *exec) -{ - int ret; + timeout_expire = jiffies + msecs_to_jiffies(timeout); + + for (;;) { + prepare_to_wait(&vc4->frame_done_queue, &wait, + TASK_UNINTERRUPTIBLE); + + if (time_after_eq(jiffies, timeout_expire)) { + ret = -ETIME; + break; + } + + if (vc4->frame_done) + break; + + schedule_timeout(timeout_expire - jiffies); + } + + finish_wait(&vc4->frame_done_queue, &wait); - ret = wait_for(thread_stopped(dev, 1), 1000); if (ret) { DRM_ERROR("timeout waiting for render thread idle\n"); return ret; @@ -125,10 +141,11 @@ vc4_submit(struct drm_device *dev, struct exec_info *exec) V3D_WRITE(V3D_BPOA, 0); V3D_WRITE(V3D_BPOS, 0); + vc4->frame_done = false; submit_cl(dev, 0, ct0ca, ct0ea); submit_cl(dev, 1, ct1ca, ct1ea); - ret = wait_for_job(dev, exec); + ret = vc4_wait_for_job(dev, exec, 1000); if (ret) return ret; diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index dfc302606762..b0e6fa847367 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -24,7 +24,10 @@ #include "vc4_drv.h" #include "vc4_regs.h" -#define V3D_DRIVER_IRQS V3D_INT_OUTOMEM +#define V3D_DRIVER_IRQS (V3D_INT_OUTOMEM | \ + V3D_INT_FRDONE) + +DECLARE_WAIT_QUEUE_HEAD(render_wait); static void vc4_overflow_mem_work(struct work_struct *work) @@ -70,6 +73,11 @@ vc4_irq(int irq, void *arg) schedule_work(&vc4->overflow_mem_work); } + if (intctl & V3D_INT_FRDONE) { + vc4->frame_done = true; + wake_up_all(&vc4->frame_done_queue); + } + return intctl ? IRQ_HANDLED : IRQ_NONE; } @@ -78,6 +86,7 @@ vc4_irq_preinstall(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); + init_waitqueue_head(&vc4->frame_done_queue); INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); /* Clear any pending interrupts someone might have left around @@ -126,4 +135,7 @@ void vc4_irq_reset(struct drm_device *dev) V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); V3D_WRITE(V3D_INTDIS, 0); V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); + + vc4->frame_done = true; + wake_up_all(&vc4->frame_done_queue); } -- cgit v1.2.1 From 6f8e8eb7f5008decbd69b3b14cd3fbe34113d319 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 18 Nov 2014 15:00:40 -0800 Subject: drm/vc4: Prefix 'struct exec_info' with 'vc4_' All the other vc4 structs in vc4_drv.h have vc4_ in front of them, and it seems like just good style. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 6 +++--- drivers/gpu/drm/vc4/vc4_gem.c | 10 +++++----- drivers/gpu/drm/vc4/vc4_validate.c | 19 ++++++++++--------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 4c600e0d3a85..5e69d7611d26 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -112,7 +112,7 @@ struct vc4_bo_exec_state { enum vc4_bo_mode mode; }; -struct exec_info { +struct vc4_exec_info { /* Kernel-space copy of the ioctl arguments */ struct drm_vc4_submit_cl *args; @@ -323,10 +323,10 @@ vc4_validate_cl(struct drm_device *dev, void *unvalidated, uint32_t len, bool is_bin, - struct exec_info *exec); + struct vc4_exec_info *exec); int -vc4_validate_shader_recs(struct drm_device *dev, struct exec_info *exec); +vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); struct vc4_validated_shader_info * vc4_validate_shader(struct drm_gem_cma_object *shader_obj, diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 35e14f81ddbf..abfc242b8025 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -62,7 +62,7 @@ submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) } static int -vc4_wait_for_job(struct drm_device *dev, struct exec_info *exec, +vc4_wait_for_job(struct drm_device *dev, struct vc4_exec_info *exec, unsigned timeout) { struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -128,7 +128,7 @@ vc4_flush_caches(struct drm_device *dev) } static int -vc4_submit(struct drm_device *dev, struct exec_info *exec) +vc4_submit(struct drm_device *dev, struct vc4_exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); uint32_t ct0ca = exec->ct0ca, ct0ea = exec->ct0ea; @@ -160,7 +160,7 @@ vc4_submit(struct drm_device *dev, struct exec_info *exec) static int vc4_cl_lookup_bos(struct drm_device *dev, struct drm_file *file_priv, - struct exec_info *exec) + struct vc4_exec_info *exec) { struct drm_vc4_submit_cl *args = exec->args; uint32_t *handles; @@ -216,7 +216,7 @@ fail: } static int -vc4_cl_validate(struct drm_device *dev, struct exec_info *exec) +vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) { struct drm_vc4_submit_cl *args = exec->args; void *temp = NULL; @@ -342,7 +342,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vc4_dev *vc4 = to_vc4_dev(dev); - struct exec_info exec; + struct vc4_exec_info exec; int ret; int i; diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index ba6e46f20411..db1f98281019 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -43,7 +43,7 @@ #include "vc4_packet.h" #define VALIDATE_ARGS \ - struct exec_info *exec, \ + struct vc4_exec_info *exec, \ void *validated, \ void *untrusted @@ -95,7 +95,7 @@ size_is_lt(uint32_t width, uint32_t height, int cpp) } static bool -vc4_use_bo(struct exec_info *exec, +vc4_use_bo(struct vc4_exec_info *exec, uint32_t hindex, enum vc4_bo_mode mode, struct drm_gem_cma_object **obj) @@ -123,7 +123,7 @@ vc4_use_bo(struct exec_info *exec, } static bool -vc4_use_handle(struct exec_info *exec, +vc4_use_handle(struct vc4_exec_info *exec, uint32_t gem_handles_packet_index, enum vc4_bo_mode mode, struct drm_gem_cma_object **obj) @@ -148,7 +148,7 @@ gl_shader_rec_size(uint32_t pointer_bits) } static bool -check_tex_size(struct exec_info *exec, struct drm_gem_cma_object *fbo, +check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, uint32_t offset, uint8_t tiling_format, uint32_t width, uint32_t height, uint8_t cpp) { @@ -657,7 +657,8 @@ static const struct cmd_info { bool render; uint16_t len; const char *name; - int (*func)(struct exec_info *exec, void *validated, void *untrusted); + int (*func)(struct vc4_exec_info *exec, void *validated, + void *untrusted); } cmd_info[] = { [VC4_PACKET_HALT] = { 1, 1, 1, "halt", NULL }, [VC4_PACKET_NOP] = { 1, 1, 1, "nop", NULL }, @@ -720,7 +721,7 @@ vc4_validate_cl(struct drm_device *dev, void *unvalidated, uint32_t len, bool is_bin, - struct exec_info *exec) + struct vc4_exec_info *exec) { uint32_t dst_offset = 0; uint32_t src_offset = 0; @@ -815,7 +816,7 @@ vc4_validate_cl(struct drm_device *dev, } static bool -reloc_tex(struct exec_info *exec, +reloc_tex(struct vc4_exec_info *exec, void *uniform_data_u, struct vc4_texture_sample_info *sample, uint32_t texture_handle_index) @@ -976,7 +977,7 @@ reloc_tex(struct exec_info *exec, static int validate_shader_rec(struct drm_device *dev, - struct exec_info *exec, + struct vc4_exec_info *exec, struct vc4_shader_state *state) { uint32_t *src_handles; @@ -1158,7 +1159,7 @@ fail: int vc4_validate_shader_recs(struct drm_device *dev, - struct exec_info *exec) + struct vc4_exec_info *exec) { uint32_t i; int ret = 0; -- cgit v1.2.1 From 89717fa400c0a77850c7ca55ce43c4ca0085f548 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 19 Nov 2014 16:37:35 -0800 Subject: drm/vc4: Move the hang detection to a timer. We're going to need to do this for async execution, so make the switch now. Since the detection is smarter now (it can see when you're making progess through the lists), we can crank up the default timeout for executing a job. v2: Move vc4_gem_init() out from in between drm_irq_install() and its error check. v3: Rebase on the KMS work. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.c | 4 +-- drivers/gpu/drm/vc4/vc4_drv.h | 7 +++++ drivers/gpu/drm/vc4/vc4_gem.c | 71 ++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 433be8c09efa..bc1e54ca207a 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -58,8 +58,6 @@ vc4_drm_load(struct drm_device *dev, unsigned long flags) if (!vc4) return -ENOMEM; - INIT_LIST_HEAD(&vc4->overflow_list); - vc4->firmware_node = of_parse_phandle(dev->dev->of_node, "firmware", 0); if (!vc4->firmware_node) { DRM_ERROR("Failed to parse firmware node.\n"); @@ -81,6 +79,8 @@ vc4_drm_load(struct drm_device *dev, unsigned long flags) if (ret) return ret; + vc4_gem_init(dev); + vc4_kms_load(dev); return 0; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 5e69d7611d26..f8142ab244a1 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -28,6 +28,12 @@ struct vc4_dev { */ struct list_head overflow_list; struct work_struct overflow_mem_work; + + struct { + uint32_t last_ct0ca, last_ct1ca; + struct timer_list timer; + struct work_struct reset_work; + } hangcheck; }; static inline struct vc4_dev * @@ -276,6 +282,7 @@ void vc4_debugfs_cleanup(struct drm_minor *minor); void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); /* vc4_gem.c */ +void vc4_gem_init(struct drm_device *dev); int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index abfc242b8025..1ed42e8e1fd1 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -30,6 +30,15 @@ #include "vc4_drv.h" #include "vc4_regs.h" +static void +vc4_queue_hangcheck(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + mod_timer(&vc4->hangcheck.timer, + round_jiffies_up(jiffies + msecs_to_jiffies(100))); +} + static void vc4_reset(struct drm_device *dev) { @@ -42,6 +51,47 @@ vc4_reset(struct drm_device *dev) vc4_irq_reset(dev); } +static void +vc4_reset_work(struct work_struct *work) +{ + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, hangcheck.reset_work); + + vc4_reset(vc4->dev); +} + +static void +vc4_hangcheck_elapsed(unsigned long data) +{ + struct drm_device *dev = (struct drm_device *)data; + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t ct0ca, ct1ca; + + /* If idle, we can stop watching for hangs. */ + if (vc4->frame_done) + return; + + ct0ca = V3D_READ(V3D_CTNCA(0)); + ct1ca = V3D_READ(V3D_CTNCA(1)); + + /* If we've made any progress in execution, rearm the timer + * and wait. + */ + if (ct0ca != vc4->hangcheck.last_ct0ca || + ct1ca != vc4->hangcheck.last_ct1ca) { + vc4->hangcheck.last_ct0ca = ct0ca; + vc4->hangcheck.last_ct1ca = ct1ca; + vc4_queue_hangcheck(dev); + return; + } + + /* We've gone too long with no progress, reset. This has to + * be done from a work struct, since resetting can sleep and + * this timer hook isn't allowed to. + */ + schedule_work(&vc4->hangcheck.reset_work); +} + static void submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) { @@ -145,7 +195,9 @@ vc4_submit(struct drm_device *dev, struct vc4_exec_info *exec) submit_cl(dev, 0, ct0ca, ct0ea); submit_cl(dev, 1, ct1ca, ct1ea); - ret = vc4_wait_for_job(dev, exec, 1000); + vc4_queue_hangcheck(dev); + + ret = vc4_wait_for_job(dev, exec, 10000); if (ret) return ret; @@ -360,10 +412,8 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, goto fail; ret = vc4_submit(dev, &exec); - if (ret) { - vc4_reset(dev); + if (ret) goto fail; - } fail: if (exec.bo) { @@ -395,3 +445,16 @@ fail: return ret; } + +void +vc4_gem_init(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + INIT_LIST_HEAD(&vc4->overflow_list); + + INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); + setup_timer(&vc4->hangcheck.timer, + vc4_hangcheck_elapsed, + (unsigned long) dev); +} -- cgit v1.2.1 From 558caeba4ece63112452d0a454554d011a465917 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 19 Nov 2014 12:06:38 -0800 Subject: drm: Put an optional field in the driver struct for GEM obj struct size. This allows a driver to derive from the CMA object without copying all of the code. Signed-off-by: Eric Anholt --- drivers/gpu/drm/drm_gem_cma_helper.c | 5 ++++- include/drm/drmP.h | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index e419eedf751d..be121c9a8e92 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -58,8 +58,11 @@ __drm_gem_cma_create(struct drm_device *drm, size_t size) struct drm_gem_cma_object *cma_obj; struct drm_gem_object *gem_obj; int ret; + size_t obj_size = (drm->driver->gem_obj_size ? + drm->driver->gem_obj_size : + sizeof(*cma_obj)); - cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); + cma_obj = kzalloc(obj_size, GFP_KERNEL); if (!cma_obj) return ERR_PTR(-ENOMEM); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 62c40777c009..f59a177aa344 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -621,6 +621,7 @@ struct drm_driver { u32 driver_features; int dev_priv_size; + size_t gem_obj_size; const struct drm_ioctl_desc *ioctls; int num_ioctls; const struct file_operations *fops; -- cgit v1.2.1 From 45a529ee9a39cb1f62aa6b69b137026599c8ab00 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 18 Nov 2014 16:54:36 -0800 Subject: drm/vc4: Make VC4_SUBMIT_CL return immediately, and add wait ioctls. This allows userland to start queuing up the next command lists while the current one is executing. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.c | 4 + drivers/gpu/drm/vc4/vc4_drv.h | 65 +++++++++-- drivers/gpu/drm/vc4/vc4_gem.c | 248 +++++++++++++++++++++++++++++++++--------- drivers/gpu/drm/vc4/vc4_irq.c | 76 ++++++++++--- include/uapi/drm/vc4_drm.h | 38 +++++++ 5 files changed, 349 insertions(+), 82 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index bc1e54ca207a..7d59dc5b6d0d 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -111,6 +111,8 @@ static const struct file_operations vc4_drm_fops = { static const struct drm_ioctl_desc vc4_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, 0), }; static struct drm_driver vc4_drm_driver = { @@ -157,6 +159,8 @@ static struct drm_driver vc4_drm_driver = { .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls), .fops = &vc4_drm_fops, + .gem_obj_size = sizeof(struct vc4_bo), + .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index f8142ab244a1..2d3a27332400 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -19,14 +19,35 @@ struct vc4_dev { struct vc4_crtc *crtc[3]; struct vc4_v3d *v3d; - wait_queue_head_t frame_done_queue; - bool frame_done; + /* Sequence number for the last job queued in job_list. + * Starts at 0 (no jobs emitted). + */ + uint64_t emit_seqno; + + /* Sequence number for the last completed job on the GPU. + * Starts at 0 (no jobs completed). + */ + uint64_t finished_seqno; - /* List of struct vc4_list_bo_entry allocated to accomodate - * binner overflow. These will be freed when the exec is - * done. + /* List of all struct vc4_exec_info for jobs to be executed. + * The first job in the list is the one currently programmed + * into ct0ca/ct1ca for execution. + */ + struct list_head job_list; + /* List of the finished vc4_exec_infos waiting to be freed by + * job_done_work. + */ + struct list_head job_done_list; + spinlock_t job_lock; + wait_queue_head_t job_wait_queue; + struct work_struct job_done_work; + + /* The binner overflow memory that's currently set up in + * BPOA/BPOS registers. When overflow occurs and a new one is + * allocated, the previous one will be moved to + * vc4->current_exec's free list. */ - struct list_head overflow_list; + struct vc4_bo *overflow_mem; struct work_struct overflow_mem_work; struct { @@ -44,6 +65,9 @@ to_vc4_dev(struct drm_device *dev) struct vc4_bo { struct drm_gem_cma_object base; + /* seqno of the last job to render to this BO. */ + uint64_t seqno; + struct list_head unref_head; }; static inline struct vc4_bo * @@ -108,17 +132,15 @@ enum vc4_bo_mode { VC4_MODE_SHADER, }; -struct vc4_bo_list_entry { - struct list_head head; - struct drm_gem_cma_object *bo; -}; - struct vc4_bo_exec_state { struct drm_gem_cma_object *bo; enum vc4_bo_mode mode; }; struct vc4_exec_info { + /* Sequence number for this bin/render job. */ + uint64_t seqno; + /* Kernel-space copy of the ioctl arguments */ struct drm_vc4_submit_cl *args; @@ -128,6 +150,14 @@ struct vc4_exec_info { struct vc4_bo_exec_state *bo; uint32_t bo_count; + /* Pointers for our position in vc4->job_list */ + struct list_head head; + + /* List of other BOs used in the job that need to be released + * once the job is complete. + */ + struct list_head unref_list; + /* Current unvalidated indices into @bo loaded by the non-hardware * VC4_PACKET_GEM_HANDLES. */ @@ -194,6 +224,14 @@ struct vc4_exec_info { uint32_t uniforms_size; }; +static inline struct vc4_exec_info * +vc4_first_job(struct vc4_dev *vc4) +{ + if (list_empty(&vc4->job_list)) + return NULL; + return list_first_entry(&vc4->job_list, struct vc4_exec_info, head); +} + /** * struct vc4_texture_sample_info - saves the offsets into the UBO for texture * setup parameters. @@ -285,6 +323,11 @@ void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); void vc4_gem_init(struct drm_device *dev); int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int vc4_wait_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +void vc4_submit_next_job(struct drm_device *dev); /* vc4_hdmi.c */ void vc4_hdmi_register(void); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 1ed42e8e1fd1..37dd79a863b1 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -49,6 +49,12 @@ vc4_reset(struct drm_device *dev) vc4_v3d_set_power(vc4, true); vc4_irq_reset(dev); + + /* Rearm the hangcheck -- another job might have been waiting + * for our hung one to get kicked off, and vc4_irq_reset() + * would have started it. + */ + vc4_queue_hangcheck(dev); } static void @@ -68,7 +74,7 @@ vc4_hangcheck_elapsed(unsigned long data) uint32_t ct0ca, ct1ca; /* If idle, we can stop watching for hangs. */ - if (vc4->frame_done) + if (list_empty(&vc4->job_list)) return; ct0ca = V3D_READ(V3D_CTNCA(0)); @@ -112,37 +118,44 @@ submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) } static int -vc4_wait_for_job(struct drm_device *dev, struct vc4_exec_info *exec, - unsigned timeout) +vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns) { struct vc4_dev *vc4 = to_vc4_dev(dev); int ret = 0; unsigned long timeout_expire; DEFINE_WAIT(wait); - if (vc4->frame_done) + if (vc4->finished_seqno >= seqno) return 0; - timeout_expire = jiffies + msecs_to_jiffies(timeout); + if (timeout_ns == 0) + return -ETIME; + + timeout_expire = jiffies + nsecs_to_jiffies(timeout_ns); for (;;) { - prepare_to_wait(&vc4->frame_done_queue, &wait, - TASK_UNINTERRUPTIBLE); + prepare_to_wait(&vc4->job_wait_queue, &wait, + TASK_INTERRUPTIBLE); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } if (time_after_eq(jiffies, timeout_expire)) { ret = -ETIME; break; } - if (vc4->frame_done) + if (vc4->finished_seqno >= seqno) break; schedule_timeout(timeout_expire - jiffies); } - finish_wait(&vc4->frame_done_queue, &wait); + finish_wait(&vc4->job_wait_queue, &wait); - if (ret) { + if (ret && ret != -ERESTARTSYS) { DRM_ERROR("timeout waiting for render thread idle\n"); return ret; } @@ -177,13 +190,19 @@ vc4_flush_caches(struct drm_device *dev) barrier(); } -static int -vc4_submit(struct drm_device *dev, struct vc4_exec_info *exec) +/* Sets the registers for the next job to be actually be executed in + * the hardware. + * + * The job_lock should be held during this. + */ +void +vc4_submit_next_job(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - uint32_t ct0ca = exec->ct0ca, ct0ea = exec->ct0ea; - uint32_t ct1ca = exec->ct1ca, ct1ea = exec->ct1ea; - int ret; + struct vc4_exec_info *exec = vc4_first_job(vc4); + + if (!exec) + return; vc4_flush_caches(dev); @@ -191,17 +210,57 @@ vc4_submit(struct drm_device *dev, struct vc4_exec_info *exec) V3D_WRITE(V3D_BPOA, 0); V3D_WRITE(V3D_BPOS, 0); - vc4->frame_done = false; - submit_cl(dev, 0, ct0ca, ct0ea); - submit_cl(dev, 1, ct1ca, ct1ea); + submit_cl(dev, 0, exec->ct0ca, exec->ct0ea); + submit_cl(dev, 1, exec->ct1ca, exec->ct1ea); +} - vc4_queue_hangcheck(dev); +static void +vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno) +{ + struct vc4_bo *bo; + unsigned i; - ret = vc4_wait_for_job(dev, exec, 10000); - if (ret) - return ret; + for (i = 0; i < exec->bo_count; i++) { + bo = to_vc4_bo(&exec->bo[i].bo->base); + bo->seqno = seqno; + } - return 0; + list_for_each_entry(bo, &exec->unref_list, unref_head) { + bo->seqno = seqno; + } +} + +/* Queues a struct vc4_exec_info for execution. If no job is + * currently executing, then submits it. + * + * Unlike most GPUs, our hardware only handles one command list at a + * time. To queue multiple jobs at once, we'd need to edit the + * previous command list to have a jump to the new one at the end, and + * then bump the end address. That's a change for a later date, + * though. + */ +static void +vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint64_t seqno = ++vc4->emit_seqno; + + exec->seqno = seqno; + vc4_update_bo_seqnos(exec, seqno); + + spin_lock(&vc4->job_lock); + list_add_tail(&exec->head, &vc4->job_list); + + /* If no job was executing, kick ours off. Otherwise, it'll + * get started when the previous job's frame done interrupt + * occurs. + */ + if (vc4_first_job(vc4) == exec) { + vc4_submit_next_job(dev); + vc4_queue_hangcheck(dev); + } + + spin_unlock(&vc4->job_lock); } /** @@ -348,6 +407,9 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) goto fail; } + list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head, + &exec->unref_list); + exec->ct0ca = exec->exec_bo->paddr + bin_offset; exec->ct1ca = exec->exec_bo->paddr + render_offset; @@ -384,6 +446,88 @@ fail: return ret; } +static void +vc4_complete_exec(struct vc4_exec_info *exec) +{ + unsigned i; + + if (exec->bo) { + for (i = 0; i < exec->bo_count; i++) + drm_gem_object_unreference(&exec->bo[i].bo->base); + kfree(exec->bo); + } + + while (!list_empty(&exec->unref_list)) { + struct vc4_bo *bo = list_first_entry(&exec->unref_list, + struct vc4_bo, unref_head); + list_del(&bo->unref_head); + drm_gem_object_unreference(&bo->base.base); + } + + kfree(exec); +} + +/* Scheduled when any job has been completed, this walks the list of + * jobs that had completed and unrefs their BOs and frees their exec + * structs. + */ +static void +vc4_job_done_work(struct work_struct *work) +{ + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, job_done_work); + struct drm_device *dev = vc4->dev; + + /* Need the struct lock for drm_gem_object_unreference(). */ + mutex_lock(&dev->struct_mutex); + + spin_lock(&vc4->job_lock); + while (!list_empty(&vc4->job_done_list)) { + struct vc4_exec_info *exec = + list_first_entry(&vc4->job_done_list, + struct vc4_exec_info, head); + list_del(&exec->head); + + spin_unlock(&vc4->job_lock); + vc4_complete_exec(exec); + spin_lock(&vc4->job_lock); + } + spin_unlock(&vc4->job_lock); + + mutex_unlock(&dev->struct_mutex); +} + +int +vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vc4_wait_seqno *args = data; + + return vc4_wait_for_seqno(dev, args->seqno, args->timeout_ns); +} + +int +vc4_wait_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + int ret; + struct drm_vc4_wait_bo *args = data; + struct drm_gem_object *gem_obj; + struct vc4_bo *bo; + + gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!gem_obj) { + DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); + return -EINVAL; + } + bo = to_vc4_bo(gem_obj); + + ret = vc4_wait_for_seqno(dev, bo->seqno, args->timeout_ns); + + drm_gem_object_unreference(gem_obj); + return ret; +} + /** * Submits a command list to the VC4. * @@ -394,52 +538,44 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_exec_info exec; + struct drm_vc4_submit_cl *args = data; + struct vc4_exec_info *exec; int ret; - int i; - memset(&exec, 0, sizeof(exec)); - exec.args = data; + exec = kcalloc(1, sizeof(*exec), GFP_KERNEL); + if (!exec) { + DRM_ERROR("malloc failure on exec struct\n"); + return -ENOMEM; + } - mutex_lock(&dev->struct_mutex); + exec->args = args; + INIT_LIST_HEAD(&exec->unref_list); - ret = vc4_cl_lookup_bos(dev, file_priv, &exec); - if (ret) - goto fail; + mutex_lock(&dev->struct_mutex); - ret = vc4_cl_validate(dev, &exec); + ret = vc4_cl_lookup_bos(dev, file_priv, exec); if (ret) goto fail; - ret = vc4_submit(dev, &exec); + ret = vc4_cl_validate(dev, exec); if (ret) goto fail; -fail: - if (exec.bo) { - for (i = 0; i < exec.bo_count; i++) - drm_gem_object_unreference(&exec.bo[i].bo->base); - kfree(exec.bo); - } + /* Return the seqno for our job. */ + args->seqno = vc4->emit_seqno; - /* Release all but the very last overflow list entry (which is - * still sitting in the BPOS/BPOA registers for use by the - * next job). + /* Clear this out of the struct we'll be putting in the queue, + * since it's part of our stack. */ - while (true) { - struct vc4_bo_list_entry *entry = - list_first_entry(&vc4->overflow_list, - struct vc4_bo_list_entry, head); + exec->args = NULL; - if (entry->head.next == &vc4->overflow_list) - break; + vc4_queue_submit(dev, exec); - drm_gem_object_unreference(&entry->bo->base); - list_del(&entry->head); - kfree(entry); - } + mutex_unlock(&dev->struct_mutex); + return 0; - drm_gem_object_unreference(&exec.exec_bo->base); +fail: + vc4_complete_exec(exec); mutex_unlock(&dev->struct_mutex); @@ -451,10 +587,14 @@ vc4_gem_init(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - INIT_LIST_HEAD(&vc4->overflow_list); + INIT_LIST_HEAD(&vc4->job_list); + INIT_LIST_HEAD(&vc4->job_done_list); + spin_lock_init(&vc4->job_lock); INIT_WORK(&vc4->hangcheck.reset_work, vc4_reset_work); setup_timer(&vc4->hangcheck.timer, vc4_hangcheck_elapsed, (unsigned long) dev); + + INIT_WORK(&vc4->job_done_work, vc4_job_done_work); } diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index b0e6fa847367..cf0b0f61bf01 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -35,28 +35,72 @@ vc4_overflow_mem_work(struct work_struct *work) struct vc4_dev *vc4 = container_of(work, struct vc4_dev, overflow_mem_work); struct drm_device *dev = vc4->dev; - struct vc4_bo_list_entry *entry = kmalloc(sizeof(*entry), GFP_KERNEL); + struct drm_gem_cma_object *cma_obj; + struct vc4_bo *bo; - if (!entry) { - DRM_ERROR("Couldn't allocate binner overflow mem record\n"); - return; - } - - entry->bo = drm_gem_cma_create(dev, 256 * 1024); - if (IS_ERR(entry->bo)) { + cma_obj = drm_gem_cma_create(dev, 256 * 1024); + if (IS_ERR(cma_obj)) { DRM_ERROR("Couldn't allocate binner overflow mem\n"); - kfree(entry); return; } + bo = to_vc4_bo(&cma_obj->base); + + /* If there's a job executing currently, then our previous + * overflow allocation is getting used in that job and we need + * to queue it to be released when the job is done. But if no + * job is executing at all, then we can free the old overflow + * object direcctly. + * + * No lock necessary for this pointer since we're the only + * ones that update the pointer, and our workqueue won't + * reenter. + */ + if (vc4->overflow_mem) { + struct vc4_exec_info *current_exec; + spin_lock(&vc4->job_lock); + current_exec = vc4_first_job(vc4); + if (current_exec) { + vc4->overflow_mem->seqno = vc4->finished_seqno + 1; + list_add_tail(&vc4->overflow_mem->unref_head, + ¤t_exec->unref_list); + vc4->overflow_mem = NULL; + } + spin_unlock(&vc4->job_lock); + } - list_add_tail(&entry->head, &vc4->overflow_list); + if (vc4->overflow_mem) { + drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); + } + vc4->overflow_mem = bo; - V3D_WRITE(V3D_BPOA, entry->bo->paddr); - V3D_WRITE(V3D_BPOS, entry->bo->base.size); + V3D_WRITE(V3D_BPOA, cma_obj->paddr); + V3D_WRITE(V3D_BPOS, cma_obj->base.size); V3D_WRITE(V3D_INTDIS, 0); V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); } +static void +vc4_irq_finish_job(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_exec_info *exec = vc4_first_job(vc4); + + spin_lock(&vc4->job_lock); + if (!exec) { + spin_unlock(&vc4->job_lock); + return; + } + + vc4->finished_seqno++; + list_move_tail(&exec->head, &vc4->job_done_list); + vc4_submit_next_job(dev); + + spin_unlock(&vc4->job_lock); + + wake_up_all(&vc4->job_wait_queue); + schedule_work(&vc4->job_done_work); +} + irqreturn_t vc4_irq(int irq, void *arg) { @@ -74,8 +118,7 @@ vc4_irq(int irq, void *arg) } if (intctl & V3D_INT_FRDONE) { - vc4->frame_done = true; - wake_up_all(&vc4->frame_done_queue); + vc4_irq_finish_job(dev); } return intctl ? IRQ_HANDLED : IRQ_NONE; @@ -86,7 +129,7 @@ vc4_irq_preinstall(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - init_waitqueue_head(&vc4->frame_done_queue); + init_waitqueue_head(&vc4->job_wait_queue); INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work); /* Clear any pending interrupts someone might have left around @@ -136,6 +179,5 @@ void vc4_irq_reset(struct drm_device *dev) V3D_WRITE(V3D_INTDIS, 0); V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); - vc4->frame_done = true; - wake_up_all(&vc4->frame_done_queue); + vc4_irq_finish_job(dev); } diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index fa9d2372b3e1..258eb9d37158 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -27,8 +27,13 @@ #include #define DRM_VC4_SUBMIT_CL 0x00 +#define DRM_VC4_WAIT_SEQNO 0x01 +#define DRM_VC4_WAIT_BO 0x02 #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) +#define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) +#define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) + /** * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D @@ -109,6 +114,39 @@ struct drm_vc4_submit_cl { /* Number of BO handles passed in (size is that times 4). */ uint32_t bo_handle_count; + + uint32_t pad; + + /* Returned value of the seqno of this render job (for the + * wait ioctl). + */ + uint64_t seqno; +}; + +/** + * struct drm_vc4_wait_seqno - ioctl argument for waiting for + * DRM_VC4_SUBMIT_CL completion using its returned seqno. + * + * timeout_ns is the timeout in nanoseconds, where "0" means "don't + * block, just return the status." + */ +struct drm_vc4_wait_seqno { + uint64_t seqno; + uint64_t timeout_ns; +}; + +/** + * struct drm_vc4_wait_bo - ioctl argument for waiting for + * completion of the last DRM_VC4_SUBMIT_CL on a BO. + * + * This is useful for cases where multiple processes might be + * rendering to a BO and you want to wait for all rendering to be + * completed. + */ +struct drm_vc4_wait_bo { + uint32_t handle; + uint32_t pad; + uint64_t timeout_ns; }; #endif /* _UAPI_VC4_DRM_H_ */ -- cgit v1.2.1 From 690eaaa38a3bcd48bc549ecc7e7933576a8c47c6 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 2 Dec 2014 15:08:09 -0800 Subject: drm/vc4: Reenable IRQs in the OOM handler. Somehow the interrupt enable for OOM gets turned off, which is really not cool. This meant a job with multiple bin OOMs would just get stuck. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index cf0b0f61bf01..c9a59b1c78e5 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -76,6 +76,7 @@ vc4_overflow_mem_work(struct work_struct *work) V3D_WRITE(V3D_BPOA, cma_obj->paddr); V3D_WRITE(V3D_BPOS, cma_obj->base.size); V3D_WRITE(V3D_INTDIS, 0); + V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); } -- cgit v1.2.1 From f5d2accfdf936dfe4f30263ffeea514485fb5b0d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 8 Dec 2014 11:29:48 -0800 Subject: drm/vc4: Fix decision for whether the TMU-read MIN clamping is to the B file. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 752c873f3aa7..cdb43e69409f 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -221,7 +221,7 @@ check_register_write(uint64_t inst, uint32_t waddr = (is_mul ? QPU_GET_FIELD(inst, QPU_WADDR_MUL) : QPU_GET_FIELD(inst, QPU_WADDR_ADD)); - bool is_b = is_mul != ((inst & QPU_PM) != 0); + bool is_b = is_mul != ((inst & QPU_WS) != 0); uint32_t live_reg_index; switch (waddr) { @@ -297,7 +297,7 @@ track_live_clamps(uint64_t inst, uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); - bool pm = inst & QPU_PM; + bool is_b = inst & QPU_WS; uint32_t live_reg_index; if (QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_MIN) @@ -308,7 +308,7 @@ track_live_clamps(uint64_t inst, return; } - live_reg_index = waddr_to_live_reg_index(waddr_add, pm); + live_reg_index = waddr_to_live_reg_index(waddr_add, is_b); if (live_reg_index != ~0) { validation_state->live_clamp_offsets[live_reg_index] = validated_shader->uniforms_size; -- cgit v1.2.1 From 3b3ab45ff8d36c61142e57b40f4d9f46cb3fef9f Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 11 Dec 2014 12:25:33 -0800 Subject: drm/vc4: Add shader validation support for small immediates. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index cdb43e69409f..4c4ac1a87cbf 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -132,12 +132,18 @@ check_tmu_write(uint64_t inst, int tmu = waddr > QPU_W_TMU0_B; bool submit = is_tmu_submit(waddr); bool is_direct = submit && validation_state->tmu_write_count[tmu] == 0; + uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); if (is_direct) { uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); uint32_t clamp_offset = ~0; + if (sig == QPU_SIG_SMALL_IMM) { + DRM_ERROR("direct TMU read used small immediate\n"); + return false; + } + /* Make sure that this texture load is an add of the base * address of the UBO to a clamped offset within the UBO. */ @@ -179,7 +185,8 @@ check_tmu_write(uint64_t inst, validation_state->tmu_setup[tmu].is_direct = true; } else { - if (raddr_a == QPU_R_UNIF || raddr_b == QPU_R_UNIF) { + if (raddr_a == QPU_R_UNIF || (sig != QPU_SIG_SMALL_IMM && + raddr_b == QPU_R_UNIF)) { DRM_ERROR("uniform read in the same instruction as " "texture setup.\n"); return false; @@ -297,6 +304,7 @@ track_live_clamps(uint64_t inst, uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); + uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); bool is_b = inst & QPU_WS; uint32_t live_reg_index; @@ -304,7 +312,8 @@ track_live_clamps(uint64_t inst, return; if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && - !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF)) { + !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF && + sig != QPU_SIG_SMALL_IMM)) { return; } @@ -343,9 +352,10 @@ check_instruction_reads(uint64_t inst, { uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); + uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); if (raddr_a == QPU_R_UNIF || - raddr_b == QPU_R_UNIF) { + (raddr_b == QPU_R_UNIF && sig != QPU_SIG_SMALL_IMM)) { /* This can't overflow the uint32_t, because we're reading 8 * bytes of instruction to increment by 4 here, so we'd * already be OOM. @@ -400,6 +410,7 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj, case QPU_SIG_LOAD_TMU0: case QPU_SIG_LOAD_TMU1: case QPU_SIG_PROG_END: + case QPU_SIG_SMALL_IMM: if (!check_instruction_writes(inst, validated_shader, &validation_state)) { DRM_ERROR("Bad write at ip %d\n", ip); -- cgit v1.2.1 From 0022f7fa57d6009812d8558bf7afe5f22a9bcf4b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 18 Dec 2014 16:07:15 -0800 Subject: mm: Remove the PFN busy warning See commit dae803e165a11bc88ca8dbc07a11077caf97bbcb -- the warning is expected sometimes when using CMA. However, that commit still spams my kernel log with these warnings. Signed-off-by: Eric Anholt --- mm/page_alloc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ebffa0e4a9c0..997b7e955034 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -6482,8 +6482,6 @@ int alloc_contig_range(unsigned long start, unsigned long end, /* Make sure the range is really isolated. */ if (test_pages_isolated(outer_start, end, false)) { - pr_info("%s: [%lx, %lx) PFNs busy\n", - __func__, outer_start, end); ret = -EBUSY; goto done; } -- cgit v1.2.1 From c00050d0a663f2dc8d2f6399097dcef58c495434 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 18 Dec 2014 11:31:38 -0800 Subject: drm/vc4: Add a BO cache for freed BOs. The kernel needs to allocate BOs to execute rendering (both the exec BO to store the user's commands, and the binner overflow BO to handle the hardware's own needs for memory). We need to cache these, because getting a new CMA allocation is very expensive. Note that userspace has its own BO cache because it wants to avoid the cost of mmaping BOs in addition to the cost of allocation. This is about a 15% performance improvement on glxgears under X, which is thoroughly CPU limited and not hitting refresh rate yet. Next step is to cache shader validation (half of the ioctl overhead) and avoid unnecessary depth writes (a big chunk of memory bandwidth overhead). Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 158 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_drv.c | 2 +- drivers/gpu/drm/vc4/vc4_drv.h | 39 +++++++++++ drivers/gpu/drm/vc4/vc4_gem.c | 9 ++- drivers/gpu/drm/vc4/vc4_irq.c | 10 ++- 5 files changed, 208 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 1459395d6e94..8bfca3b0b3e1 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -16,12 +16,75 @@ */ #include "vc4_drv.h" +#include "uapi/drm/vc4_drm.h" + +static uint32_t +bo_page_index(size_t size) +{ + return (size / PAGE_SIZE) - 1; +} + +static struct list_head * +vc4_get_cache_list_for_size(struct drm_device *dev, size_t size) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t page_index = bo_page_index(size); + + if (vc4->bo_cache.size_list_size <= page_index) { + uint32_t new_size = max(vc4->bo_cache.size_list_size * 2, + page_index + 1); + struct list_head *new_list; + uint32_t i; + + new_list = kmalloc(new_size * sizeof(struct list_head), + GFP_KERNEL); + if (!new_list) + return NULL; + + /* Rebase the old cached BO lists to their new list + * head locations. + */ + for (i = 0; i < vc4->bo_cache.size_list_size; i++) { + struct list_head *old_list = &vc4->bo_cache.size_list[i]; + if (list_empty(old_list)) + INIT_LIST_HEAD(&new_list[i]); + else + list_replace(old_list, &new_list[i]); + } + /* And initialize the brand new BO list heads. */ + for (i = vc4->bo_cache.size_list_size; i < new_size; i++) + INIT_LIST_HEAD(&new_list[i]); + + kfree(vc4->bo_cache.size_list); + vc4->bo_cache.size_list = new_list; + vc4->bo_cache.size_list_size = new_size; + } + + return &vc4->bo_cache.size_list[page_index]; +} struct vc4_bo * vc4_bo_create(struct drm_device *dev, size_t size) { + struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t page_index = bo_page_index(size); + struct vc4_bo *bo = NULL; struct drm_gem_cma_object *cma_obj; + /* First, try to get a vc4_bo from the kernel BO cache. */ + if (vc4->bo_cache.size_list_size > page_index) { + if (!list_empty(&vc4->bo_cache.size_list[page_index])) { + bo = list_first_entry(&vc4->bo_cache.size_list[page_index], + struct vc4_bo, size_head); + list_del(&bo->size_head); + list_del(&bo->unref_head); + } + } + if (bo) { + kref_init(&bo->base.base.refcount); + return bo; + } + /* Otherwise, make a new BO. */ cma_obj = drm_gem_cma_create(dev, size); if (IS_ERR(cma_obj)) @@ -56,3 +119,98 @@ vc4_dumb_create(struct drm_file *file_priv, return ret; } + +static void +vc4_bo_cache_free_old(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + unsigned long expire_time = jiffies - msecs_to_jiffies(1000); + + while (!list_empty(&vc4->bo_cache.time_list)) { + struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, + struct vc4_bo, unref_head); + if (time_before(expire_time, bo->free_time)) { + mod_timer(&vc4->bo_cache.time_timer, + round_jiffies_up(jiffies + + msecs_to_jiffies(1000))); + return; + } + + list_del(&bo->unref_head); + list_del(&bo->size_head); + drm_gem_cma_free_object(&bo->base.base); + } +} + +/* Called on the last userspace/kernel unreference of the BO. Returns + * it to the BO cache if possible, otherwise frees it. + * + * Note that this is called with the struct_mutex held. + */ +void +vc4_free_object(struct drm_gem_object *gem_bo) +{ + struct drm_device *dev = gem_bo->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_bo *bo = to_vc4_bo(gem_bo); + struct list_head *cache_list; + + /* If the object references someone else's memory, we can't cache it. + */ + if (gem_bo->import_attach) { + drm_gem_cma_free_object(gem_bo); + return; + } + + /* Don't cache if it was publicly named. */ + if (gem_bo->name) { + drm_gem_cma_free_object(gem_bo); + return; + } + + cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); + if (!cache_list) { + drm_gem_cma_free_object(gem_bo); + return; + } + + bo->free_time = jiffies; + list_add(&bo->size_head, cache_list); + list_add(&bo->unref_head, &vc4->bo_cache.time_list); + + vc4_bo_cache_free_old(dev); +} + +static void +vc4_bo_cache_time_work(struct work_struct *work) +{ + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, bo_cache.time_work); + struct drm_device *dev = vc4->dev; + + mutex_lock(&dev->struct_mutex); + vc4_bo_cache_free_old(dev); + mutex_unlock(&dev->struct_mutex); +} + +static void +vc4_bo_cache_time_timer(unsigned long data) +{ + struct drm_device *dev = (struct drm_device *)data; + struct vc4_dev *vc4 = to_vc4_dev(dev); + + schedule_work(&vc4->bo_cache.time_work); +} + +void +vc4_bo_cache_init(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + INIT_LIST_HEAD(&vc4->bo_cache.time_list); + + INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); + setup_timer(&vc4->bo_cache.time_timer, + vc4_bo_cache_time_timer, + (unsigned long) dev); +} diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 7d59dc5b6d0d..0bdc0f9069af 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -138,7 +138,7 @@ static struct drm_driver vc4_drm_driver = { .debugfs_cleanup = vc4_debugfs_cleanup, #endif - .gem_free_object = drm_gem_cma_free_object, + .gem_free_object = vc4_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 2d3a27332400..6d29b034b603 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -50,6 +50,27 @@ struct vc4_dev { struct vc4_bo *overflow_mem; struct work_struct overflow_mem_work; + /* The kernel-space BO cache. Tracks buffers that have been + * unreferenced by all other users (refcounts of 0!) but not + * yet freed, so we can do cheap allocations. + */ + struct vc4_bo_cache { + /* Array of list heads for entries in the BO cache, + * based on number of pages, so we can do O(1) lookups + * in the cache when allocating. + */ + struct list_head *size_list; + uint32_t size_list_size; + + /* List of all BOs in the cache, ordered by age, so we + * can do O(1) lookups when trying to free old + * buffers. + */ + struct list_head time_list; + struct work_struct time_work; + struct timer_list time_timer; + } bo_cache; + struct { uint32_t last_ct0ca, last_ct1ca; struct timer_list timer; @@ -67,7 +88,17 @@ struct vc4_bo { struct drm_gem_cma_object base; /* seqno of the last job to render to this BO. */ uint64_t seqno; + + /* List entry for the BO's position in either + * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list + */ struct list_head unref_head; + + /* Time in jiffies when the BO was put in vc4->bo_cache. */ + unsigned long free_time; + + /* List entry for the BO's position in vc4_dev->bo_cache.size_list */ + struct list_head size_head; }; static inline struct vc4_bo * @@ -312,6 +343,14 @@ void vc4_disable_vblank(struct drm_device *dev, int crtc_id); #define wait_for(COND, MS) _wait_for(COND, MS, 1) +/* vc4_bo.c */ +void vc4_bo_cache_init(struct drm_device *dev); +void vc4_free_object(struct drm_gem_object *gem_obj); +struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); +int vc4_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, + struct drm_mode_create_dumb *args); + /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); void vc4_debugfs_cleanup(struct drm_minor *minor); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 37dd79a863b1..e0627f446667 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -341,6 +341,7 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) uint32_t exec_size = uniforms_offset + args->uniforms_size; uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) * args->shader_rec_count); + struct vc4_bo *bo; if (shader_rec_offset < render_offset || uniforms_offset < shader_rec_offset || @@ -399,13 +400,13 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) goto fail; } - exec->exec_bo = drm_gem_cma_create(dev, exec_size); - if (IS_ERR(exec->exec_bo)) { + bo = vc4_bo_create(dev, 256 * 1024); + if (!bo) { DRM_ERROR("Couldn't allocate BO for exec\n"); ret = PTR_ERR(exec->exec_bo); - exec->exec_bo = NULL; goto fail; } + exec->exec_bo = &bo->base; list_add_tail(&to_vc4_bo(&exec->exec_bo->base)->unref_head, &exec->unref_list); @@ -597,4 +598,6 @@ vc4_gem_init(struct drm_device *dev) (unsigned long) dev); INIT_WORK(&vc4->job_done_work, vc4_job_done_work); + + vc4_bo_cache_init(dev); } diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index c9a59b1c78e5..66201229801c 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -35,15 +35,13 @@ vc4_overflow_mem_work(struct work_struct *work) struct vc4_dev *vc4 = container_of(work, struct vc4_dev, overflow_mem_work); struct drm_device *dev = vc4->dev; - struct drm_gem_cma_object *cma_obj; struct vc4_bo *bo; - cma_obj = drm_gem_cma_create(dev, 256 * 1024); - if (IS_ERR(cma_obj)) { + bo = vc4_bo_create(dev, 256 * 1024); + if (!bo) { DRM_ERROR("Couldn't allocate binner overflow mem\n"); return; } - bo = to_vc4_bo(&cma_obj->base); /* If there's a job executing currently, then our previous * overflow allocation is getting used in that job and we need @@ -73,8 +71,8 @@ vc4_overflow_mem_work(struct work_struct *work) } vc4->overflow_mem = bo; - V3D_WRITE(V3D_BPOA, cma_obj->paddr); - V3D_WRITE(V3D_BPOS, cma_obj->base.size); + V3D_WRITE(V3D_BPOA, bo->base.paddr); + V3D_WRITE(V3D_BPOS, bo->base.base.size); V3D_WRITE(V3D_INTDIS, 0); V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); -- cgit v1.2.1 From 291ad365c0fb29d1f19c0111c480b8b698b793ba Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 31 Dec 2014 14:19:39 -0800 Subject: drm/vc4: Enforce that shaders start at bo offset 0. This is what userspace does currently, and it will simplify the caching of validated shader state. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 3 +-- drivers/gpu/drm/vc4/vc4_validate.c | 9 +++++++-- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 16 ++++------------ 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 6d29b034b603..c7da68b854fe 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -418,5 +418,4 @@ int vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); struct vc4_validated_shader_info * -vc4_validate_shader(struct drm_gem_cma_object *shader_obj, - uint32_t start_offset); +vc4_validate_shader(struct drm_gem_cma_object *shader_obj); diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index db1f98281019..0691a8d769ca 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -1074,9 +1074,14 @@ validate_shader_rec(struct drm_device *dev, switch (relocs[i].type) { case RELOC_CODE: + if (src_offset != 0) { + DRM_ERROR("Shaders must be at offset 0 of " + "the BO.\n"); + goto fail; + } + kfree(validated_shader); - validated_shader = vc4_validate_shader(bo[i], - src_offset); + validated_shader = vc4_validate_shader(bo[i]); if (!validated_shader) goto fail; diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 4c4ac1a87cbf..ad7ebb11f2d8 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -367,8 +367,7 @@ check_instruction_reads(uint64_t inst, } struct vc4_validated_shader_info * -vc4_validate_shader(struct drm_gem_cma_object *shader_obj, - uint32_t start_offset) +vc4_validate_shader(struct drm_gem_cma_object *shader_obj) { bool found_shader_end = false; int shader_end_ip = 0; @@ -385,14 +384,8 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj, for (i = 0; i < ARRAY_SIZE(validation_state.live_clamp_offsets); i++) validation_state.live_clamp_offsets[i] = ~0; - if (start_offset + sizeof(uint64_t) > shader_obj->base.size) { - DRM_ERROR("shader starting at %d outside of BO sized %d\n", - start_offset, - shader_obj->base.size); - return NULL; - } - shader = shader_obj->vaddr + start_offset; - max_ip = (shader_obj->base.size - start_offset) / sizeof(uint64_t); + shader = shader_obj->vaddr; + max_ip = shader_obj->base.size / sizeof(uint64_t); validated_shader = kcalloc(sizeof(*validated_shader), 1, GFP_KERNEL); if (!validated_shader) @@ -449,9 +442,8 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj, } if (ip == max_ip) { - DRM_ERROR("shader starting at %d failed to terminate before " + DRM_ERROR("shader failed to terminate before " "shader BO end at %d\n", - start_offset, shader_obj->base.size); goto fail; } -- cgit v1.2.1 From 37737b6747d4804f88f6b787f2132efe62f5047b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 26 Dec 2014 09:37:27 -1000 Subject: drm/vc4: Store the validated shader state. This massively reduces the overhead of CL submission in the kernel. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 3 +++ drivers/gpu/drm/vc4/vc4_drv.h | 3 +++ drivers/gpu/drm/vc4/vc4_validate.c | 6 +----- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 5 +++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 8bfca3b0b3e1..93be736c87bf 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -174,6 +174,9 @@ vc4_free_object(struct drm_gem_object *gem_bo) return; } + kfree(bo->validated_shader); + bo->validated_shader = NULL; + bo->free_time = jiffies; list_add(&bo->size_head, cache_list); list_add(&bo->unref_head, &vc4->bo_cache.time_list); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index c7da68b854fe..0f71effb1cab 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -99,6 +99,9 @@ struct vc4_bo { /* List entry for the BO's position in vc4_dev->bo_cache.size_list */ struct list_head size_head; + + /* Cached state from validation of the shader code. */ + struct vc4_validated_shader_info *validated_shader; }; static inline struct vc4_bo * diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 0691a8d769ca..5ecd640f625a 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -1003,7 +1003,7 @@ validate_shader_rec(struct drm_device *dev, struct drm_gem_cma_object *bo[ARRAY_SIZE(gl_relocs) + 8]; uint32_t nr_attributes = 0, nr_fixed_relocs, nr_relocs, packet_size; int i; - struct vc4_validated_shader_info *validated_shader = NULL; + struct vc4_validated_shader_info *validated_shader; if (state->packet == VC4_PACKET_NV_SHADER_STATE) { relocs = nv_relocs; @@ -1080,7 +1080,6 @@ validate_shader_rec(struct drm_device *dev, goto fail; } - kfree(validated_shader); validated_shader = vc4_validate_shader(bo[i]); if (!validated_shader) goto fail; @@ -1153,12 +1152,9 @@ validate_shader_rec(struct drm_device *dev, *(uint32_t *)(pkt_v + o) = vbo->paddr + offset; } - kfree(validated_shader); - return 0; fail: - kfree(validated_shader); return -EINVAL; } diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index ad7ebb11f2d8..17c5bdcf5aa7 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -369,6 +369,7 @@ check_instruction_reads(uint64_t inst, struct vc4_validated_shader_info * vc4_validate_shader(struct drm_gem_cma_object *shader_obj) { + struct vc4_bo *shader_bo = to_vc4_bo(&shader_obj->base); bool found_shader_end = false; int shader_end_ip = 0; uint32_t ip, max_ip; @@ -377,6 +378,9 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj) struct vc4_shader_validation_state validation_state; int i; + if (shader_bo->validated_shader) + return shader_bo->validated_shader; + memset(&validation_state, 0, sizeof(validation_state)); for (i = 0; i < 8; i++) @@ -456,6 +460,7 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj) (validated_shader->uniforms_size + 4 * validated_shader->num_texture_samples); + shader_bo->validated_shader = validated_shader; return validated_shader; fail: -- cgit v1.2.1 From 885d01c7d368b2049b207b7ea1fba622172acfc9 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 31 Dec 2014 14:51:20 -0800 Subject: drm/vc4: Disallow sending shader BOs that are busy on the GPU. This is a step in eliminating the rewrite-the-shaders root hole. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 17c5bdcf5aa7..1f5e02783c96 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -381,6 +381,14 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj) if (shader_bo->validated_shader) return shader_bo->validated_shader; + /* Our validation relies on nothing modifying the shader + * contents after us, so just ban sending us busy BOs. + */ + if (shader_bo->seqno > vc4->finished_seqno) { + DRM_ERROR("shader BO is currently busy on the GPU.\n"); + return NULL; + } + memset(&validation_state, 0, sizeof(validation_state)); for (i = 0; i < 8; i++) -- cgit v1.2.1 From 92640f289261e674398a712e96aa022418c596d6 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 31 Dec 2014 14:58:51 -0800 Subject: drm/vc4: Disallow using dmabuf BOs as shaders. This is a step in eliminating the rewrite-the-shaders root hole. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 36 ++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_drv.c | 4 ++-- drivers/gpu/drm/vc4/vc4_drv.h | 9 ++++++++ drivers/gpu/drm/vc4/vc4_validate_shaders.c | 6 +++++ 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 93be736c87bf..ae3ade165888 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -177,6 +177,12 @@ vc4_free_object(struct drm_gem_object *gem_bo) kfree(bo->validated_shader); bo->validated_shader = NULL; + /* If the BO was exported, and it's made it to this point, + * then the dmabuf usage has been completely finished (so it's + * safe now to let it turn into a shader again). + */ + bo->dma_buf_import_export = false; + bo->free_time = jiffies; list_add(&bo->size_head, cache_list); list_add(&bo->unref_head, &vc4->bo_cache.time_list); @@ -217,3 +223,33 @@ vc4_bo_cache_init(struct drm_device *dev) vc4_bo_cache_time_timer, (unsigned long) dev); } + +struct drm_gem_object * +vc4_prime_import(struct drm_device *dev, struct dma_buf *dma_buf) +{ + struct drm_gem_object *obj = drm_gem_prime_import(dev, dma_buf); + + if (!IS_ERR_OR_NULL(obj)) { + struct vc4_bo *bo = to_vc4_bo(obj); + bo->dma_buf_import_export = true; + } + + return obj; +} + +struct dma_buf * +vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) +{ + struct vc4_bo *bo = to_vc4_bo(obj); + + mutex_lock(&dev->struct_mutex); + if (bo->validated_shader) { + mutex_unlock(&dev->struct_mutex); + DRM_ERROR("Attempting to export shader BO\n"); + return ERR_PTR(-EINVAL); + } + bo->dma_buf_import_export = true; + mutex_unlock(&dev->struct_mutex); + + return drm_gem_prime_export(dev, obj, flags); +} diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 0bdc0f9069af..7408deecc857 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -143,8 +143,8 @@ static struct drm_driver vc4_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_import = drm_gem_prime_import, - .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = vc4_prime_import, + .gem_prime_export = vc4_prime_export, .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, .gem_prime_vmap = drm_gem_cma_prime_vmap, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 0f71effb1cab..57ace7cfb322 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -102,6 +102,11 @@ struct vc4_bo { /* Cached state from validation of the shader code. */ struct vc4_validated_shader_info *validated_shader; + + /* Set if the buffer has been either imported or exported via + * dmabufs. Used for shader mapping security checks. + */ + bool dma_buf_import_export; }; static inline struct vc4_bo * @@ -353,6 +358,10 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); int vc4_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); +struct drm_gem_object *vc4_prime_import(struct drm_device *dev, + struct dma_buf *dma_buf); +struct dma_buf *vc4_prime_export(struct drm_device *dev, + struct drm_gem_object *obj, int flags); /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 1f5e02783c96..839d5521cfe5 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -369,6 +369,7 @@ check_instruction_reads(uint64_t inst, struct vc4_validated_shader_info * vc4_validate_shader(struct drm_gem_cma_object *shader_obj) { + struct vc4_dev *vc4 = to_vc4_dev(shader_obj->base.dev); struct vc4_bo *shader_bo = to_vc4_bo(&shader_obj->base); bool found_shader_end = false; int shader_end_ip = 0; @@ -389,6 +390,11 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj) return NULL; } + if (shader_bo->dma_buf_import_export) { + DRM_ERROR("shader BO was exported through dmabuf.\n"); + return NULL; + } + memset(&validation_state, 0, sizeof(validation_state)); for (i = 0; i < 8; i++) -- cgit v1.2.1 From 6fb045e5d83019fe4f7ece6b0161adb2c9e61cb8 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 2 Jan 2015 10:48:08 -0800 Subject: drm/vc4: Evict user mappings of shaders while they're being executed. If the user could rewrite shader code while the GPU was executing it, then all of vc4_validate_shaders.c's safety checks would be invalidated, notably by using the direct-addressing TMU fetches to read arbitrary system memory. This hopefully closes our last remaining root hole. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 107 +++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_drv.c | 5 +- drivers/gpu/drm/vc4/vc4_drv.h | 5 ++ drivers/gpu/drm/vc4/vc4_gem.c | 2 +- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 2 + 5 files changed, 118 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index ae3ade165888..952b7c0cc325 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -253,3 +253,110 @@ vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) return drm_gem_prime_export(dev, obj, flags); } + + +/* vc4_gem_fault - fault handler for user mappings of objects. + * + * We don't just use the GEM helpers because we have to make sure that + * the user can't touch shader contents while they're being executed. + */ +static int +vc4_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct drm_gem_object *gem_bo = vma->vm_private_data; + struct vc4_bo *bo = to_vc4_bo(gem_bo); + struct drm_device *dev = gem_bo->dev; + pgoff_t page_offset; + unsigned long pfn; + int ret = 0; + + /* We don't use vmf->pgoff since that has the fake offset */ + page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> + PAGE_SHIFT; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + goto out; + + if (bo->validated_shader) { + ret = vc4_wait_for_seqno(dev, bo->seqno, ~0ull); + if (ret) + goto unlock; + + kfree(bo->validated_shader); + bo->validated_shader = NULL; + } + + pfn = (bo->base.paddr >> PAGE_SHIFT) + page_offset; + + ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); +unlock: + mutex_unlock(&dev->struct_mutex); +out: + switch (ret) { + case 0: + case -ERESTARTSYS: + case -EINTR: + case -EBUSY: + ret = VM_FAULT_NOPAGE; + break; + case -ENOMEM: + ret = VM_FAULT_OOM; + break; + case -ENOSPC: + case -EFAULT: + ret = VM_FAULT_SIGBUS; + break; + default: + WARN_ONCE(ret, "unhandled error in vc4_gem_fault: %i\n", ret); + ret = VM_FAULT_SIGBUS; + break; + } + + return ret; +} + +const struct vm_operations_struct vc4_vm_ops = { + .fault = vc4_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +int +vc4_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret; + + ret = drm_gem_mmap(filp, vma); + if (ret) + return ret; + + /* Since our objects all come from normal system memory, clear + * PFNMAP that was defaulted by drm_gem_mmap_obj() to indicate + * that they have a "struct page" managing them. + */ + vma->vm_flags &= ~VM_PFNMAP; + + /* Not sure why we need to do this. */ + vma->vm_flags |= VM_MIXEDMAP; + + /* We only do whole-object mappings. */ + vma->vm_pgoff = 0; + + return 0; +} + +/* Removes all user mappings of the object. + * + * This is used to ensure that the user can't modify shaders while the + * GPU is executing them. If the user tries to access these unmapped + * pages, they'll hit a pagefault and end up in vc4_gem_fault(), which + * then can wait for execution to finish. + */ +void +vc4_force_user_unmap(struct drm_gem_object *gem_obj) +{ + struct drm_device *dev = gem_obj->dev; + + drm_vma_node_unmap(&gem_obj->vma_node, dev->anon_inode->i_mapping); +} diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 7408deecc857..0d16e13014a4 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -100,7 +100,7 @@ static const struct file_operations vc4_drm_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, - .mmap = drm_gem_cma_mmap, + .mmap = vc4_mmap, .poll = drm_poll, .read = drm_read, #ifdef CONFIG_COMPAT @@ -115,6 +115,7 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, 0), }; + static struct drm_driver vc4_drm_driver = { .driver_features = (DRIVER_MODESET | DRIVER_GEM | @@ -139,7 +140,7 @@ static struct drm_driver vc4_drm_driver = { #endif .gem_free_object = vc4_free_object, - .gem_vm_ops = &drm_gem_cma_vm_ops, + .gem_vm_ops = &vc4_vm_ops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 57ace7cfb322..bce9493d5383 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -352,6 +352,7 @@ void vc4_disable_vblank(struct drm_device *dev, int crtc_id); #define wait_for(COND, MS) _wait_for(COND, MS, 1) /* vc4_bo.c */ +extern const struct vm_operations_struct vc4_vm_ops; void vc4_bo_cache_init(struct drm_device *dev); void vc4_free_object(struct drm_gem_object *gem_obj); struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); @@ -362,6 +363,8 @@ struct drm_gem_object *vc4_prime_import(struct drm_device *dev, struct dma_buf *dma_buf); struct dma_buf *vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags); +int vc4_mmap(struct file *filp, struct vm_area_struct *vma); +void vc4_force_user_unmap(struct drm_gem_object *gem_obj); /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); @@ -379,6 +382,8 @@ int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, int vc4_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void vc4_submit_next_job(struct drm_device *dev); +int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, + uint64_t timeout_ns); /* vc4_hdmi.c */ void vc4_hdmi_register(void); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index e0627f446667..d0cef8627d1b 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -117,7 +117,7 @@ submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) barrier(); } -static int +int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns) { struct vc4_dev *vc4 = to_vc4_dev(dev); diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 839d5521cfe5..2b4a4ec64f7a 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -382,6 +382,8 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj) if (shader_bo->validated_shader) return shader_bo->validated_shader; + vc4_force_user_unmap(&shader_obj->base); + /* Our validation relies on nothing modifying the shader * contents after us, so just ban sending us busy BOs. */ -- cgit v1.2.1 From 2d8c710ff9482bfa0fba069b5681929d2cbe9ecd Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 16 Jan 2015 15:54:49 +1300 Subject: drm/vc4: Follow danvet's alignment rules for ioctl struct ABI. This is a flag day for the kernel ABI, but it's a requirement for merging the code. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 17 ++++++++++++----- include/uapi/drm/vc4_drm.h | 10 +++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index d0cef8627d1b..67edd30b9a72 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -301,7 +301,8 @@ vc4_cl_lookup_bos(struct drm_device *dev, goto fail; } - ret = copy_from_user(handles, args->bo_handles, + ret = copy_from_user(handles, + (void __user *)(uintptr_t)args->bo_handles, exec->bo_count * sizeof(uint32_t)); if (ret) { DRM_ERROR("Failed to copy in GEM handles\n"); @@ -374,26 +375,32 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) exec->shader_state = temp + exec_size; exec->shader_state_size = args->shader_rec_count; - ret = copy_from_user(bin, args->bin_cl, args->bin_cl_size); + ret = copy_from_user(bin, + (void __user *)(uintptr_t)args->bin_cl, + args->bin_cl_size); if (ret) { DRM_ERROR("Failed to copy in bin cl\n"); goto fail; } - ret = copy_from_user(render, args->render_cl, args->render_cl_size); + ret = copy_from_user(render, + (void __user *)(uintptr_t)args->render_cl, + args->render_cl_size); if (ret) { DRM_ERROR("Failed to copy in render cl\n"); goto fail; } - ret = copy_from_user(exec->shader_rec_u, args->shader_rec, + ret = copy_from_user(exec->shader_rec_u, + (void __user *)(uintptr_t)args->shader_rec, args->shader_rec_size); if (ret) { DRM_ERROR("Failed to copy in shader recs\n"); goto fail; } - ret = copy_from_user(exec->uniforms_u, args->uniforms, + ret = copy_from_user(exec->uniforms_u, + (void __user *)(uintptr_t)args->uniforms, args->uniforms_size); if (ret) { DRM_ERROR("Failed to copy in uniforms cl\n"); diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index 258eb9d37158..453fb7013c68 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -56,7 +56,7 @@ struct drm_vc4_submit_cl { * then writes out the state updates and draw calls necessary per tile * to the tile allocation BO. */ - void __user *bin_cl; + uint64_t bin_cl; /* Pointer to the render command list. * @@ -66,7 +66,7 @@ struct drm_vc4_submit_cl { * stored rendering for that tile, then store the tile's state back to * memory. */ - void __user *render_cl; + uint64_t render_cl; /* Pointer to the shader records. * @@ -77,7 +77,7 @@ struct drm_vc4_submit_cl { * and an attribute count), so those BO indices into bo_handles are * just stored as uint32_ts before each shader record passed in. */ - void __user *shader_rec; + uint64_t shader_rec; /* Pointer to uniform data and texture handles for the textures * referenced by the shader. @@ -93,8 +93,8 @@ struct drm_vc4_submit_cl { * because the kernel has to determine the sizes anyway during shader * code validation. */ - void __user *uniforms; - void __user *bo_handles; + uint64_t uniforms; + uint64_t bo_handles; /* Size in bytes of the binner command list. */ uint32_t bin_cl_size; -- cgit v1.2.1 From 19d37d01bc3ff4545149cfce00992fc58b28377e Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 16 Jan 2015 16:12:14 +1300 Subject: drm/vc4: Add a "flags" arg to submit_cl. This was one of danvet's recommendations from his ioctls talk, and it makes sense. It also happens to occupy a 32-bit padding slot in the struct. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 3 +++ include/uapi/drm/vc4_drm.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 67edd30b9a72..9343f36272c0 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -550,6 +550,9 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct vc4_exec_info *exec; int ret; + if (args->flags != 0) + return -EINVAL; + exec = kcalloc(1, sizeof(*exec), GFP_KERNEL); if (!exec) { DRM_ERROR("malloc failure on exec struct\n"); diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index 453fb7013c68..4dabfeab4dcc 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -115,6 +115,7 @@ struct drm_vc4_submit_cl { /* Number of BO handles passed in (size is that times 4). */ uint32_t bo_handle_count; + uint32_t flags; uint32_t pad; /* Returned value of the seqno of this render job (for the -- cgit v1.2.1 From cf24e99f0ac4918658db934ff0ab027bc41b646b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sun, 18 Jan 2015 09:19:31 +1300 Subject: drm/vc4: Make sure that waits that get interrupted don't wait forever. danvet's talk recommended avoiding relative waits entirely, but his example was of relative waits with units (vblank numbers) that were potentially longer than the interval between signals coming in, so you couldn't adjust the ioctl arguments appropriately. This change also has a wait unit that might be too large (jiffies), but given that the other argument (an emitted seqno) is guaranteed to eventually result in a nonblocking successful return, this isn't really a big deal. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 9343f36272c0..34036c3f0f50 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -505,13 +505,31 @@ vc4_job_done_work(struct work_struct *work) mutex_unlock(&dev->struct_mutex); } +static int +vc4_wait_for_seqno_ioctl_helper(struct drm_device *dev, + uint64_t seqno, + uint64_t *timeout_ns) +{ + unsigned long start = jiffies; + int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns); + + if (ret == -EINTR || ret == -ERESTARTSYS) { + uint64_t delta = jiffies_to_nsecs(jiffies - start); + if (*timeout_ns >= delta) + *timeout_ns -= delta; + } + + return ret; +} + int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_vc4_wait_seqno *args = data; - return vc4_wait_for_seqno(dev, args->seqno, args->timeout_ns); + return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno, + &args->timeout_ns); } int @@ -530,7 +548,7 @@ vc4_wait_bo_ioctl(struct drm_device *dev, void *data, } bo = to_vc4_bo(gem_obj); - ret = vc4_wait_for_seqno(dev, bo->seqno, args->timeout_ns); + ret = vc4_wait_for_seqno_ioctl_helper(dev, bo->seqno, &args->timeout_ns); drm_gem_object_unreference(gem_obj); return ret; -- cgit v1.2.1 From c963b9937092fb79d79992cb8c7937ee53459bd8 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sun, 18 Jan 2015 09:33:17 +1300 Subject: drm/vc4: Add create and map BO ioctls. I've been relying on the dumb APIs until now, but that's against the rules. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 46 ++++++++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/vc4/vc4_drv.c | 2 ++ drivers/gpu/drm/vc4/vc4_drv.h | 4 ++++ include/uapi/drm/vc4_drm.h | 39 +++++++++++++++++++++++++++++++++++- 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 952b7c0cc325..0cdfdd98b5e5 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -1,5 +1,5 @@ /* - * Copyright © 2015 Broadcom + * Copyright © 2014-2015 Broadcom * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -360,3 +360,47 @@ vc4_force_user_unmap(struct drm_gem_object *gem_obj) drm_vma_node_unmap(&gem_obj->vma_node, dev->anon_inode->i_mapping); } + +int +vc4_create_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vc4_create_bo *args = data; + struct vc4_bo *bo = NULL; + int ret; + + args->size = roundup(args->size, PAGE_SIZE); + if (args->size == 0) + return -EINVAL; + + mutex_lock(&dev->struct_mutex); + bo = vc4_bo_create(dev, args->size); + mutex_unlock(&dev->struct_mutex); + if (!bo) + return -ENOMEM; + + ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); + drm_gem_object_unreference_unlocked(&bo->base.base); + + return ret; +} + +int +vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vc4_mmap_bo *args = data; + struct drm_gem_object *gem_obj; + + gem_obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!gem_obj) { + DRM_ERROR("Failed to look up GEM BO %d\n", args->handle); + return -EINVAL; + } + + /* The mmap offset was set up at BO allocation time. */ + args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node); + + drm_gem_object_unreference(gem_obj); + return 0; +} diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 0d16e13014a4..9fbd3b949479 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -113,6 +113,8 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(VC4_SUBMIT_CL, vc4_submit_cl_ioctl, 0), DRM_IOCTL_DEF_DRV(VC4_WAIT_SEQNO, vc4_wait_seqno_ioctl, 0), DRM_IOCTL_DEF_DRV(VC4_WAIT_BO, vc4_wait_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_CREATE_BO, vc4_create_bo_ioctl, 0), + DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), }; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index bce9493d5383..3226ef432e32 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -365,6 +365,10 @@ struct dma_buf *vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags); int vc4_mmap(struct file *filp, struct vm_area_struct *vma); void vc4_force_user_unmap(struct drm_gem_object *gem_obj); +int vc4_create_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index 4dabfeab4dcc..9dd54403a5d8 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Broadcom + * Copyright © 2014-2015 Broadcom * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,10 +29,14 @@ #define DRM_VC4_SUBMIT_CL 0x00 #define DRM_VC4_WAIT_SEQNO 0x01 #define DRM_VC4_WAIT_BO 0x02 +#define DRM_VC4_CREATE_BO 0x03 +#define DRM_VC4_MMAP_BO 0x04 #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) #define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) #define DRM_IOCTL_VC4_WAIT_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_WAIT_BO, struct drm_vc4_wait_bo) +#define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) +#define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) /** @@ -150,4 +154,37 @@ struct drm_vc4_wait_bo { uint64_t timeout_ns; }; +/** + * struct drm_vc4_create_bo - ioctl argument for creating VC4 BOs. + * + * There are currently no values for the flags argument, but it may be + * used in a future extension. + */ +struct drm_vc4_create_bo { + uint32_t size; + uint32_t flags; + /** Returned GEM handle for the BO. */ + uint32_t handle; + uint32_t pad; +}; + +/** + * struct drm_vc4_mmap_bo - ioctl argument for mapping VC4 BOs. + * + * This doesn't actually perform an mmap. Instead, it returns the + * offset you need to use in an mmap on the DRM device node. This + * means that tools like valgrind end up knowing about the mapped + * memory. + * + * There are currently no values for the flags argument, but it may be + * used in a future extension. + */ +struct drm_vc4_mmap_bo { + /** Handle for the object being mapped. */ + uint32_t handle; + uint32_t flags; + /** offset into the drm node to use for subsequent mmap call. */ + uint64_t offset; +}; + #endif /* _UAPI_VC4_DRM_H_ */ -- cgit v1.2.1 From 97a9c41ccdb7a7f1b6808583b9c3f6ca7c32ff3d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 24 Feb 2015 10:39:20 +0000 Subject: drm/vc4: Revert "drm/vc4: Evict user mappings of shaders while they're being executed." This reverts commit cd95e2a4feb4cf7a5e47da3e320f9fc3336899e4. This appears to be broken on 3.18 on rpi2, and I haven't figured out why yet. It causes a system lockup with sometimes an angry RCU complaint beforehand. --- drivers/gpu/drm/vc4/vc4_bo.c | 107 ----------------------------- drivers/gpu/drm/vc4/vc4_drv.c | 5 +- drivers/gpu/drm/vc4/vc4_drv.h | 5 -- drivers/gpu/drm/vc4/vc4_gem.c | 2 +- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 2 - 5 files changed, 3 insertions(+), 118 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 0cdfdd98b5e5..c39413f6bcbc 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -254,113 +254,6 @@ vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) return drm_gem_prime_export(dev, obj, flags); } - -/* vc4_gem_fault - fault handler for user mappings of objects. - * - * We don't just use the GEM helpers because we have to make sure that - * the user can't touch shader contents while they're being executed. - */ -static int -vc4_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct drm_gem_object *gem_bo = vma->vm_private_data; - struct vc4_bo *bo = to_vc4_bo(gem_bo); - struct drm_device *dev = gem_bo->dev; - pgoff_t page_offset; - unsigned long pfn; - int ret = 0; - - /* We don't use vmf->pgoff since that has the fake offset */ - page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> - PAGE_SHIFT; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - goto out; - - if (bo->validated_shader) { - ret = vc4_wait_for_seqno(dev, bo->seqno, ~0ull); - if (ret) - goto unlock; - - kfree(bo->validated_shader); - bo->validated_shader = NULL; - } - - pfn = (bo->base.paddr >> PAGE_SHIFT) + page_offset; - - ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); -unlock: - mutex_unlock(&dev->struct_mutex); -out: - switch (ret) { - case 0: - case -ERESTARTSYS: - case -EINTR: - case -EBUSY: - ret = VM_FAULT_NOPAGE; - break; - case -ENOMEM: - ret = VM_FAULT_OOM; - break; - case -ENOSPC: - case -EFAULT: - ret = VM_FAULT_SIGBUS; - break; - default: - WARN_ONCE(ret, "unhandled error in vc4_gem_fault: %i\n", ret); - ret = VM_FAULT_SIGBUS; - break; - } - - return ret; -} - -const struct vm_operations_struct vc4_vm_ops = { - .fault = vc4_gem_fault, - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; - -int -vc4_mmap(struct file *filp, struct vm_area_struct *vma) -{ - int ret; - - ret = drm_gem_mmap(filp, vma); - if (ret) - return ret; - - /* Since our objects all come from normal system memory, clear - * PFNMAP that was defaulted by drm_gem_mmap_obj() to indicate - * that they have a "struct page" managing them. - */ - vma->vm_flags &= ~VM_PFNMAP; - - /* Not sure why we need to do this. */ - vma->vm_flags |= VM_MIXEDMAP; - - /* We only do whole-object mappings. */ - vma->vm_pgoff = 0; - - return 0; -} - -/* Removes all user mappings of the object. - * - * This is used to ensure that the user can't modify shaders while the - * GPU is executing them. If the user tries to access these unmapped - * pages, they'll hit a pagefault and end up in vc4_gem_fault(), which - * then can wait for execution to finish. - */ -void -vc4_force_user_unmap(struct drm_gem_object *gem_obj) -{ - struct drm_device *dev = gem_obj->dev; - - drm_vma_node_unmap(&gem_obj->vma_node, dev->anon_inode->i_mapping); -} - int vc4_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 9fbd3b949479..a0fdd1a0d387 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -100,7 +100,7 @@ static const struct file_operations vc4_drm_fops = { .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, - .mmap = vc4_mmap, + .mmap = drm_gem_cma_mmap, .poll = drm_poll, .read = drm_read, #ifdef CONFIG_COMPAT @@ -117,7 +117,6 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(VC4_MMAP_BO, vc4_mmap_bo_ioctl, 0), }; - static struct drm_driver vc4_drm_driver = { .driver_features = (DRIVER_MODESET | DRIVER_GEM | @@ -142,7 +141,7 @@ static struct drm_driver vc4_drm_driver = { #endif .gem_free_object = vc4_free_object, - .gem_vm_ops = &vc4_vm_ops, + .gem_vm_ops = &drm_gem_cma_vm_ops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 3226ef432e32..28f9f58c4dd9 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -352,7 +352,6 @@ void vc4_disable_vblank(struct drm_device *dev, int crtc_id); #define wait_for(COND, MS) _wait_for(COND, MS, 1) /* vc4_bo.c */ -extern const struct vm_operations_struct vc4_vm_ops; void vc4_bo_cache_init(struct drm_device *dev); void vc4_free_object(struct drm_gem_object *gem_obj); struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); @@ -363,8 +362,6 @@ struct drm_gem_object *vc4_prime_import(struct drm_device *dev, struct dma_buf *dma_buf); struct dma_buf *vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags); -int vc4_mmap(struct file *filp, struct vm_area_struct *vma); -void vc4_force_user_unmap(struct drm_gem_object *gem_obj); int vc4_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, @@ -386,8 +383,6 @@ int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, int vc4_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void vc4_submit_next_job(struct drm_device *dev); -int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, - uint64_t timeout_ns); /* vc4_hdmi.c */ void vc4_hdmi_register(void); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 34036c3f0f50..8843c4cf9948 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -117,7 +117,7 @@ submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) barrier(); } -int +static int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns) { struct vc4_dev *vc4 = to_vc4_dev(dev); diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 2b4a4ec64f7a..839d5521cfe5 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -382,8 +382,6 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj) if (shader_bo->validated_shader) return shader_bo->validated_shader; - vc4_force_user_unmap(&shader_obj->base); - /* Our validation relies on nothing modifying the shader * contents after us, so just ban sending us busy BOs. */ -- cgit v1.2.1 From 4f81f084ab1707f64659b8c1f2aecedc9fec5659 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 23 Mar 2015 19:26:42 -0700 Subject: drm/vc4: Make alignment of raster texture widths more consistent. This more like how the simulator writes the aligment, and consistent with the other tiling formats. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 5ecd640f625a..27eabcbb8351 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -164,7 +164,7 @@ check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, switch (tiling_format) { case VC4_TILING_FORMAT_LINEAR: - aligned_width = roundup(width, 16 / cpp); + aligned_width = roundup(width, utile_w); aligned_height = height; break; case VC4_TILING_FORMAT_T: @@ -951,7 +951,7 @@ reloc_tex(struct vc4_exec_info *exec, aligned_height = roundup(level_height, utile_h); break; default: - aligned_width = roundup(level_width, 16 / cpp); + aligned_width = roundup(level_width, utile_w); aligned_height = level_height; break; } -- cgit v1.2.1 From 527817b1970f00b0179b045012b762579b2fc859 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 23 Mar 2015 19:28:07 -0700 Subject: drm/vc4: Drop unnecessary restriction on render w/h vs bin w/h. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 27eabcbb8351..02eac4f0b5e7 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -589,21 +589,6 @@ validate_tile_rendering_mode_config(VALIDATE_ARGS) exec->fb_width = *(uint16_t *)(untrusted + 4); exec->fb_height = *(uint16_t *)(untrusted + 6); - /* Make sure that the fb width/height matches the binning config -- we - * rely on being able to interchange these for various assertions. - * (Within a tile, loads and stores will be clipped to the - * width/height, but we allow load/storing to any binned tile). - */ - if (exec->fb_width <= (exec->bin_tiles_x - 1) * 64 || - exec->fb_width > exec->bin_tiles_x * 64 || - exec->fb_height <= (exec->bin_tiles_y - 1) * 64 || - exec->fb_height > exec->bin_tiles_y * 64) { - DRM_ERROR("bin config %dx%d doesn't match FB %dx%d\n", - exec->bin_tiles_x, exec->bin_tiles_y, - exec->fb_width, exec->fb_height); - return -EINVAL; - } - flags = *(uint16_t *)(untrusted + 8); if ((flags & VC4_RENDER_CONFIG_FORMAT_MASK) == VC4_RENDER_CONFIG_FORMAT_RGBA8888) { @@ -632,13 +617,9 @@ validate_tile_coordinates(VALIDATE_ARGS) uint8_t tile_x = *(uint8_t *)(untrusted + 0); uint8_t tile_y = *(uint8_t *)(untrusted + 1); - if (tile_x >= exec->bin_tiles_x || - tile_y >= exec->bin_tiles_y) { - DRM_ERROR("Tile coordinates %d,%d > bin config %d,%d\n", - tile_x, - tile_y, - exec->bin_tiles_x, - exec->bin_tiles_y); + if (tile_x * 64 >= exec->fb_width || tile_y * 64 >= exec->fb_height) { + DRM_ERROR("Tile coordinates %d,%d > render config %dx%d\n", + tile_x, tile_y, exec->fb_width, exec->fb_height); return -EINVAL; } -- cgit v1.2.1 From b774b5bdd312250829d8cf2b0f0224368a059949 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 9 Apr 2015 17:54:42 -0700 Subject: drm/vc4: Add support for jobs without a bin CL. This is useful for doing blit operations using the render engine, which can perform reads from raster textures into a tiled output, when the texture fetch engine has proved incapable of fetching correctly. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 1 + drivers/gpu/drm/vc4/vc4_gem.c | 5 ++++- drivers/gpu/drm/vc4/vc4_validate.c | 9 ++++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 28f9f58c4dd9..aa6592dd1fc9 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -428,6 +428,7 @@ vc4_validate_cl(struct drm_device *dev, void *unvalidated, uint32_t len, bool is_bin, + bool has_bin, struct vc4_exec_info *exec); int diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 8843c4cf9948..a6e5be04697b 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -210,7 +210,8 @@ vc4_submit_next_job(struct drm_device *dev) V3D_WRITE(V3D_BPOA, 0); V3D_WRITE(V3D_BPOS, 0); - submit_cl(dev, 0, exec->ct0ca, exec->ct0ea); + if (exec->ct0ca != exec->ct0ea) + submit_cl(dev, 0, exec->ct0ca, exec->ct0ea); submit_cl(dev, 1, exec->ct1ca, exec->ct1ea); } @@ -434,6 +435,7 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) bin, args->bin_cl_size, true, + args->bin_cl_size != 0, exec); if (ret) goto fail; @@ -443,6 +445,7 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) render, args->render_cl_size, false, + args->bin_cl_size != 0, exec); if (ret) goto fail; diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 02eac4f0b5e7..79657939dcf2 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -702,6 +702,7 @@ vc4_validate_cl(struct drm_device *dev, void *unvalidated, uint32_t len, bool is_bin, + bool has_bin, struct vc4_exec_info *exec) { uint32_t dst_offset = 0; @@ -772,7 +773,7 @@ vc4_validate_cl(struct drm_device *dev, if (is_bin) { exec->ct0ea = exec->ct0ca + dst_offset; - if (!exec->found_start_tile_binning_packet) { + if (has_bin && !exec->found_start_tile_binning_packet) { DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); return -EINVAL; } @@ -786,8 +787,10 @@ vc4_validate_cl(struct drm_device *dev, * increment from the bin CL. Otherwise a later submit would * have render execute immediately. */ - if (!exec->found_wait_on_semaphore_packet) { - DRM_ERROR("Render CL missing VC4_PACKET_WAIT_ON_SEMAPHORE\n"); + if (exec->found_wait_on_semaphore_packet != has_bin) { + DRM_ERROR("Render CL %s VC4_PACKET_WAIT_ON_SEMAPHORE\n", + exec->found_wait_on_semaphore_packet ? + "has" : "missing"); return -EINVAL; } exec->ct1ea = exec->ct1ca + dst_offset; -- cgit v1.2.1 From 7272bdd11c4887efda4ea291ad37f23bcb491a4a Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 9 Apr 2015 18:00:56 -0700 Subject: drm/vc4: Fix off-by-one in branch target validation. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 79657939dcf2..7c6925cd7929 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -287,7 +287,7 @@ validate_branch_to_sublist(VALIDATE_ARGS) offset = *(uint32_t *)(untrusted + 0); if (offset % exec->tile_alloc_init_block_size || - offset / exec->tile_alloc_init_block_size > + offset / exec->tile_alloc_init_block_size >= exec->bin_tiles_x * exec->bin_tiles_y) { DRM_ERROR("VC4_PACKET_BRANCH_TO_SUB_LIST must jump to initial " "tile allocation space.\n"); -- cgit v1.2.1 From 56102d5e94beca405b551f2f94eb307d0c36c103 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Mon, 23 Feb 2015 04:03:32 +0000 Subject: drm/vc4: Return a correct IRQ status. If there were stray IRQs, the core kernel would never had a chance to shut it down. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_irq.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 66201229801c..f39d80dafdd1 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -106,6 +106,7 @@ vc4_irq(int irq, void *arg) struct drm_device *dev = arg; struct vc4_dev *vc4 = to_vc4_dev(dev); uint32_t intctl; + irqreturn_t status = IRQ_NONE; barrier(); intctl = V3D_READ(V3D_INTCTL); @@ -114,13 +115,15 @@ vc4_irq(int irq, void *arg) if (intctl & V3D_INT_OUTOMEM) { V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); schedule_work(&vc4->overflow_mem_work); + status = IRQ_HANDLED; } if (intctl & V3D_INT_FRDONE) { vc4_irq_finish_job(dev); + status = IRQ_HANDLED; } - return intctl ? IRQ_HANDLED : IRQ_NONE; + return status; } void -- cgit v1.2.1 From 1fafb6cab83be514ca37d8701dc5a789abb3b333 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 21 Apr 2015 10:02:19 -0700 Subject: drm/vc4: Avoid race with IRQs at module load. vc4_gem_init() initializes the BO cache, but v3d probe was setting up the interrupt handler, and potentially taking an interrupt and trying to allocate an overflow BO out of the cache before then. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index a0fdd1a0d387..471f6bcaee95 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -75,12 +75,12 @@ vc4_drm_load(struct drm_device *dev, unsigned long flags) drm_mode_config_init(dev); + vc4_gem_init(dev); + ret = component_bind_all(dev->dev, dev); if (ret) return ret; - vc4_gem_init(dev); - vc4_kms_load(dev); return 0; -- cgit v1.2.1 From eb4fdfd674ab976fed764b6c606c9963c183906a Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 14 May 2015 11:32:24 -0700 Subject: drm/vc4: Don't forget to disable IRQs during the job_done spinlock. The hard IRQ handler also takes this lock, so we need interrupts disabled or we'll deadlock. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 14 ++++++++------ drivers/gpu/drm/vc4/vc4_irq.c | 6 ++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index a6e5be04697b..c2561ea1cdb3 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -245,11 +245,12 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) { struct vc4_dev *vc4 = to_vc4_dev(dev); uint64_t seqno = ++vc4->emit_seqno; + unsigned long irqflags; exec->seqno = seqno; vc4_update_bo_seqnos(exec, seqno); - spin_lock(&vc4->job_lock); + spin_lock_irqsave(&vc4->job_lock, irqflags); list_add_tail(&exec->head, &vc4->job_list); /* If no job was executing, kick ours off. Otherwise, it'll @@ -261,7 +262,7 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec) vc4_queue_hangcheck(dev); } - spin_unlock(&vc4->job_lock); + spin_unlock_irqrestore(&vc4->job_lock, irqflags); } /** @@ -488,22 +489,23 @@ vc4_job_done_work(struct work_struct *work) struct vc4_dev *vc4 = container_of(work, struct vc4_dev, job_done_work); struct drm_device *dev = vc4->dev; + unsigned long irqflags; /* Need the struct lock for drm_gem_object_unreference(). */ mutex_lock(&dev->struct_mutex); - spin_lock(&vc4->job_lock); + spin_lock_irqsave(&vc4->job_lock, irqflags); while (!list_empty(&vc4->job_done_list)) { struct vc4_exec_info *exec = list_first_entry(&vc4->job_done_list, struct vc4_exec_info, head); list_del(&exec->head); - spin_unlock(&vc4->job_lock); + spin_unlock_irqrestore(&vc4->job_lock, irqflags); vc4_complete_exec(exec); - spin_lock(&vc4->job_lock); + spin_lock_irqsave(&vc4->job_lock, irqflags); } - spin_unlock(&vc4->job_lock); + spin_unlock_irqrestore(&vc4->job_lock, irqflags); mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index f39d80dafdd1..5af0e6ff5318 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -55,7 +55,9 @@ vc4_overflow_mem_work(struct work_struct *work) */ if (vc4->overflow_mem) { struct vc4_exec_info *current_exec; - spin_lock(&vc4->job_lock); + unsigned long irqflags; + + spin_lock_irqsave(&vc4->job_lock, irqflags); current_exec = vc4_first_job(vc4); if (current_exec) { vc4->overflow_mem->seqno = vc4->finished_seqno + 1; @@ -63,7 +65,7 @@ vc4_overflow_mem_work(struct work_struct *work) ¤t_exec->unref_list); vc4->overflow_mem = NULL; } - spin_unlock(&vc4->job_lock); + spin_unlock_irqrestore(&vc4->job_lock, irqflags); } if (vc4->overflow_mem) { -- cgit v1.2.1 From 660b630f8de24d4c73f5f2982b65ed2537735c1d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 14 May 2015 11:33:08 -0700 Subject: drm/vc4: Don't forget to lock around BO creation in the overflow work. The overflow handler notably walks through our BO cache structs, which are protected by this lock. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_irq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 5af0e6ff5318..c2f539c3b6c6 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -37,7 +37,9 @@ vc4_overflow_mem_work(struct work_struct *work) struct drm_device *dev = vc4->dev; struct vc4_bo *bo; + mutex_lock(&dev->struct_mutex); bo = vc4_bo_create(dev, 256 * 1024); + mutex_unlock(&dev->struct_mutex); if (!bo) { DRM_ERROR("Couldn't allocate binner overflow mem\n"); return; -- cgit v1.2.1 From 006708c9879d3951edcec2a74021afc41ac65084 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 15 May 2015 12:33:00 -0700 Subject: drm/vc4: Purge BO cache when we've failed an allocation, and retry. This gets us a bit farther when running low on CMA memory. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index c39413f6bcbc..aa063eb4889f 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -63,6 +63,20 @@ vc4_get_cache_list_for_size(struct drm_device *dev, size_t size) return &vc4->bo_cache.size_list[page_index]; } +static void +vc4_bo_cache_purge(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + while (!list_empty(&vc4->bo_cache.time_list)) { + struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, + struct vc4_bo, unref_head); + list_del(&bo->unref_head); + list_del(&bo->size_head); + drm_gem_cma_free_object(&bo->base.base); + } +} + struct vc4_bo * vc4_bo_create(struct drm_device *dev, size_t size) { @@ -87,10 +101,17 @@ vc4_bo_create(struct drm_device *dev, size_t size) /* Otherwise, make a new BO. */ cma_obj = drm_gem_cma_create(dev, size); - if (IS_ERR(cma_obj)) - return NULL; - else - return to_vc4_bo(&cma_obj->base); + if (IS_ERR(cma_obj)) { + /* If we've run out of CMA memory, kill the cache of + * CMA allocations we've got laying around and try again. + */ + vc4_bo_cache_purge(dev); + cma_obj = drm_gem_cma_create(dev, size); + if (IS_ERR(cma_obj)) + return NULL; + } + + return to_vc4_bo(&cma_obj->base); } int -- cgit v1.2.1 From e624e229ca70b4285da4063bd6261639e3df37d6 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 15 May 2015 12:44:55 -0700 Subject: drm/vc4: Add a debugfs node for looking at BO allocation stats. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 77 +++++++++++++++++++++++++++++++++------ drivers/gpu/drm/vc4/vc4_debugfs.c | 1 + drivers/gpu/drm/vc4/vc4_drv.h | 1 + 3 files changed, 68 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index aa063eb4889f..ba91b7c17ab1 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -18,12 +18,37 @@ #include "vc4_drv.h" #include "uapi/drm/vc4_drm.h" +static struct { + u32 num_allocated; + u32 size_allocated; + u32 num_cached; + u32 size_cached; +} bo_stats; + static uint32_t bo_page_index(size_t size) { return (size / PAGE_SIZE) - 1; } +static void +vc4_bo_destroy(struct vc4_bo *bo) +{ + bo_stats.num_allocated--; + bo_stats.size_allocated -= bo->base.base.size; + drm_gem_cma_free_object(&bo->base.base); +} + +static void +vc4_bo_remove_from_cache(struct vc4_bo *bo) +{ + bo_stats.num_cached--; + bo_stats.size_cached -= bo->base.base.size; + + list_del(&bo->unref_head); + list_del(&bo->size_head); +} + static struct list_head * vc4_get_cache_list_for_size(struct drm_device *dev, size_t size) { @@ -71,9 +96,8 @@ vc4_bo_cache_purge(struct drm_device *dev) while (!list_empty(&vc4->bo_cache.time_list)) { struct vc4_bo *bo = list_last_entry(&vc4->bo_cache.time_list, struct vc4_bo, unref_head); - list_del(&bo->unref_head); - list_del(&bo->size_head); - drm_gem_cma_free_object(&bo->base.base); + vc4_bo_remove_from_cache(bo); + vc4_bo_destroy(bo); } } @@ -90,8 +114,7 @@ vc4_bo_create(struct drm_device *dev, size_t size) if (!list_empty(&vc4->bo_cache.size_list[page_index])) { bo = list_first_entry(&vc4->bo_cache.size_list[page_index], struct vc4_bo, size_head); - list_del(&bo->size_head); - list_del(&bo->unref_head); + vc4_bo_remove_from_cache(bo); } } if (bo) { @@ -111,6 +134,9 @@ vc4_bo_create(struct drm_device *dev, size_t size) return NULL; } + bo_stats.num_allocated++; + bo_stats.size_allocated += size; + return to_vc4_bo(&cma_obj->base); } @@ -157,9 +183,8 @@ vc4_bo_cache_free_old(struct drm_device *dev) return; } - list_del(&bo->unref_head); - list_del(&bo->size_head); - drm_gem_cma_free_object(&bo->base.base); + vc4_bo_remove_from_cache(bo); + vc4_bo_destroy(bo); } } @@ -179,19 +204,19 @@ vc4_free_object(struct drm_gem_object *gem_bo) /* If the object references someone else's memory, we can't cache it. */ if (gem_bo->import_attach) { - drm_gem_cma_free_object(gem_bo); + vc4_bo_destroy(bo); return; } /* Don't cache if it was publicly named. */ if (gem_bo->name) { - drm_gem_cma_free_object(gem_bo); + vc4_bo_destroy(bo); return; } cache_list = vc4_get_cache_list_for_size(dev, gem_bo->size); if (!cache_list) { - drm_gem_cma_free_object(gem_bo); + vc4_bo_destroy(bo); return; } @@ -208,6 +233,9 @@ vc4_free_object(struct drm_gem_object *gem_bo) list_add(&bo->size_head, cache_list); list_add(&bo->unref_head, &vc4->bo_cache.time_list); + bo_stats.num_cached++; + bo_stats.size_cached += gem_bo->size; + vc4_bo_cache_free_old(dev); } @@ -318,3 +346,30 @@ vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, drm_gem_object_unreference(gem_obj); return 0; } + +#ifdef CONFIG_DEBUG_FS +int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + + mutex_lock(&dev->struct_mutex); + + seq_printf(m, "num bos allocated: %d\n", + bo_stats.num_allocated); + seq_printf(m, "size bos allocated: %dkb\n", + bo_stats.size_allocated / 1024); + seq_printf(m, "num bos used: %d\n", + bo_stats.num_allocated - bo_stats.num_cached); + seq_printf(m, "size bos used: %dkb\n", + (bo_stats.size_allocated - bo_stats.size_cached) / 1024); + seq_printf(m, "num bos cached: %d\n", + bo_stats.num_cached); + seq_printf(m, "size bos cached: %dkb\n", + bo_stats.size_cached / 1024); + + mutex_unlock(&dev->struct_mutex); + + return 0; +} +#endif diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index abd541f470ca..ca4743126736 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -16,6 +16,7 @@ #include "vc4_regs.h" static const struct drm_info_list vc4_debugfs_list[] = { + {"bo_stats", vc4_bo_stats_debugfs, 0}, {"v3d_ident", vc4_v3d_debugfs_ident, 0}, {"v3d_regs", vc4_v3d_debugfs_regs, 0}, {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index aa6592dd1fc9..fa14c801b1a4 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -366,6 +366,7 @@ int vc4_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int vc4_bo_stats_debugfs(struct seq_file *m, void *unused); /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); -- cgit v1.2.1 From c142c62b580d988adf2baa40e5f09302f3a0f9d9 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 15 May 2015 13:04:46 -0700 Subject: drm/vc4: Make sure we clean up GEM if probe fails. We often return -EPROBE_DEFER from the various components, and we would have left a bunch of GEM stuff running and allocated. Cleans up a boot-time warning about how we leaked the 256k of overflow_mem. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 35 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/vc4/vc4_drv.c | 4 +++- drivers/gpu/drm/vc4/vc4_drv.h | 2 ++ drivers/gpu/drm/vc4/vc4_gem.c | 21 +++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_irq.c | 4 ++-- drivers/gpu/drm/vc4/vc4_v3d.c | 13 +++++++++++++ 6 files changed, 75 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index ba91b7c17ab1..62e8a0c27a39 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -25,6 +25,23 @@ static struct { u32 size_cached; } bo_stats; +static void +vc4_bo_stats_dump(void) +{ + DRM_INFO("num bos allocated: %d\n", + bo_stats.num_allocated); + DRM_INFO("size bos allocated: %dkb\n", + bo_stats.size_allocated / 1024); + DRM_INFO("num bos used: %d\n", + bo_stats.num_allocated - bo_stats.num_cached); + DRM_INFO("size bos used: %dkb\n", + (bo_stats.size_allocated - bo_stats.size_cached) / 1024); + DRM_INFO("num bos cached: %d\n", + bo_stats.num_cached); + DRM_INFO("size bos cached: %dkb\n", + bo_stats.size_cached / 1024); +} + static uint32_t bo_page_index(size_t size) { @@ -88,7 +105,7 @@ vc4_get_cache_list_for_size(struct drm_device *dev, size_t size) return &vc4->bo_cache.size_list[page_index]; } -static void +void vc4_bo_cache_purge(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -273,6 +290,22 @@ vc4_bo_cache_init(struct drm_device *dev) (unsigned long) dev); } +void +vc4_bo_cache_destroy(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + del_timer(&vc4->bo_cache.time_timer); + cancel_work_sync(&vc4->bo_cache.time_work); + + vc4_bo_cache_purge(dev); + + if (bo_stats.num_allocated) { + DRM_ERROR("Destroying BO cache while BOs still allocated:\n"); + vc4_bo_stats_dump(); + } +} + struct drm_gem_object * vc4_prime_import(struct drm_device *dev, struct dma_buf *dma_buf) { diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 471f6bcaee95..8d32dca645ad 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -78,8 +78,10 @@ vc4_drm_load(struct drm_device *dev, unsigned long flags) vc4_gem_init(dev); ret = component_bind_all(dev->dev, dev); - if (ret) + if (ret) { + vc4_gem_destroy(dev); return ret; + } vc4_kms_load(dev); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index fa14c801b1a4..57b889e072ef 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -353,6 +353,7 @@ void vc4_disable_vblank(struct drm_device *dev, int crtc_id); /* vc4_bo.c */ void vc4_bo_cache_init(struct drm_device *dev); +void vc4_bo_cache_destroy(struct drm_device *dev); void vc4_free_object(struct drm_gem_object *gem_obj); struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t size); int vc4_dumb_create(struct drm_file *file_priv, @@ -377,6 +378,7 @@ void __iomem *vc4_ioremap_regs(struct platform_device *dev, int index); /* vc4_gem.c */ void vc4_gem_init(struct drm_device *dev); +void vc4_gem_destroy(struct drm_device *dev); int vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index c2561ea1cdb3..3cec0eb16a12 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -634,3 +634,24 @@ vc4_gem_init(struct drm_device *dev) vc4_bo_cache_init(dev); } + +void +vc4_gem_destroy(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + /* Waiting for exec to finish would need to be done before + * unregistering V3D. + */ + WARN_ON(vc4->emit_seqno != vc4->finished_seqno); + + /* V3D should already have disabled its interrupt and cleared + * the overflow allocation registers. Now free the object. + */ + if (vc4->overflow_mem) { + drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base); + vc4->overflow_mem = NULL; + } + + vc4_bo_cache_destroy(dev); +} diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index c2f539c3b6c6..df9c4c86fa34 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -167,13 +167,13 @@ vc4_irq_uninstall(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - cancel_work_sync(&vc4->overflow_mem_work); - V3D_WRITE(V3D_INTENA, 0); V3D_WRITE(V3D_INTDIS, 0); /* Clear any pending interrupts we might have left. */ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); + + cancel_work_sync(&vc4->overflow_mem_work); } /** Reinitializes interrupt registers when a GPU reset is performed. */ diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index 9321fa2a2f23..2ac27c9944f1 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -200,6 +200,12 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) return -EINVAL; } + /* Reset the binner overflow address/size at setup, to be sure + * we don't reuse an old one. + */ + V3D_WRITE(V3D_BPOA, 0); + V3D_WRITE(V3D_BPOS, 0); + vc4_v3d_init_hw(drm); ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); @@ -219,6 +225,13 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master, drm_irq_uninstall(drm); + /* Disable the binner's overflow memory address, so the next + * driver probe (if any) doesn't try to reuse our old + * allocation. + */ + V3D_WRITE(V3D_BPOA, 0); + V3D_WRITE(V3D_BPOS, 0); + vc4->v3d = NULL; } -- cgit v1.2.1 From 31831905d175291d9c2c504c89ba167b327ee939 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 27 May 2015 16:30:18 -0700 Subject: drm/vc4: Don't forget to irqsave/restore in the reset path. Fixes another deadlock error from lockdep. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_irq.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index df9c4c86fa34..b5a1a273f83f 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -88,18 +88,13 @@ vc4_irq_finish_job(struct drm_device *dev) struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_exec_info *exec = vc4_first_job(vc4); - spin_lock(&vc4->job_lock); - if (!exec) { - spin_unlock(&vc4->job_lock); + if (!exec) return; - } vc4->finished_seqno++; list_move_tail(&exec->head, &vc4->job_done_list); vc4_submit_next_job(dev); - spin_unlock(&vc4->job_lock); - wake_up_all(&vc4->job_wait_queue); schedule_work(&vc4->job_done_work); } @@ -123,7 +118,9 @@ vc4_irq(int irq, void *arg) } if (intctl & V3D_INT_FRDONE) { + spin_lock(&vc4->job_lock); vc4_irq_finish_job(dev); + spin_unlock(&vc4->job_lock); status = IRQ_HANDLED; } @@ -180,10 +177,13 @@ vc4_irq_uninstall(struct drm_device *dev) void vc4_irq_reset(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); + unsigned long irqflags; V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); V3D_WRITE(V3D_INTDIS, 0); V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); + spin_lock_irqsave(&vc4->job_lock, irqflags); vc4_irq_finish_job(dev); + spin_unlock_irqrestore(&vc4->job_lock, irqflags); } -- cgit v1.2.1 From 4deff3653db719f62834ca866e818dbe5d403afe Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 14:37:38 -0700 Subject: drm/vc4: Avoid repeatedly grabbing/dropping the GEM handle spinlock. Improves no-swapbuffers drawarrays-mode isosurf performance by 0.219463% +/- 0.166978% (n=4/5) Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 3cec0eb16a12..17d15df285fd 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -311,18 +311,21 @@ vc4_cl_lookup_bos(struct drm_device *dev, goto fail; } + spin_lock(&file_priv->table_lock); for (i = 0; i < exec->bo_count; i++) { - struct drm_gem_object *bo; - - bo = drm_gem_object_lookup(dev, file_priv, handles[i]); + struct drm_gem_object *bo = idr_find(&file_priv->object_idr, + handles[i]); if (!bo) { DRM_ERROR("Failed to look up GEM BO %d: %d\n", i, handles[i]); ret = -EINVAL; + spin_unlock(&file_priv->table_lock); goto fail; } + drm_gem_object_reference(bo); exec->bo[i].bo = (struct drm_gem_cma_object *)bo; } + spin_unlock(&file_priv->table_lock); fail: kfree(handles); -- cgit v1.2.1 From fa1ad658e659f0a0f1f056d0dd131aad5d0167c6 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 15:05:51 -0700 Subject: drm/vc4: Don't bother flushing the ARM CPU caches on GEM exec. All of our BOs are mapped write-combining both in the kernel and userspace by drm_gem_cma_helper.c, so anything we've written will end up in main memory, anyway. The only coherency trouble we might have would be if we had content in the CPU's write combining buffers that hadn't been flushed, but a CPU cache flush is unlikely to help with that, anyway. Improves no-swapbuffers drawarrays-mode isosurf performance by around 20%. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 17d15df285fd..2aad41118cec 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -180,14 +180,6 @@ vc4_flush_caches(struct drm_device *dev) VC4_SET_FIELD(0xf, V3D_SLCACTL_T0CC) | VC4_SET_FIELD(0xf, V3D_SLCACTL_UCC) | VC4_SET_FIELD(0xf, V3D_SLCACTL_ICC)); - - /* Flush the CPU L1/L2 caches. Since the GPU reads from L3 - * don't snoop up the L1/L2, we have to either do this or - * manually clflush the cachelines we (and userspace) dirtied. - */ - flush_cache_all(); - - barrier(); } /* Sets the registers for the next job to be actually be executed in -- cgit v1.2.1 From 09a4b5d74664220c0f5077590c9e6ab7d288cdff Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 15:48:43 -0700 Subject: drm/vc4: Make userspace's infinite waits actually infinite. We use ~0 (584 years) to indicate "wait forever", so don't actually set up a timer for 584 years from now. Saves about 1% CPU. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 2aad41118cec..7e8070668eb4 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -142,15 +142,18 @@ vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns) break; } - if (time_after_eq(jiffies, timeout_expire)) { - ret = -ETIME; - break; - } - if (vc4->finished_seqno >= seqno) break; - schedule_timeout(timeout_expire - jiffies); + if (timeout_ns != ~0ull) { + if (time_after_eq(jiffies, timeout_expire)) { + ret = -ETIME; + break; + } + schedule_timeout(timeout_expire - jiffies); + } else { + schedule(); + } } finish_wait(&vc4->job_wait_queue, &wait); @@ -513,7 +516,7 @@ vc4_wait_for_seqno_ioctl_helper(struct drm_device *dev, unsigned long start = jiffies; int ret = vc4_wait_for_seqno(dev, seqno, *timeout_ns); - if (ret == -EINTR || ret == -ERESTARTSYS) { + if ((ret == -EINTR || ret == -ERESTARTSYS) && *timeout_ns != ~0ull) { uint64_t delta = jiffies_to_nsecs(jiffies - start); if (*timeout_ns >= delta) *timeout_ns -= delta; -- cgit v1.2.1 From daaefada7fb313811c2c9a781f69a2b40dd3fcb8 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 16:01:29 -0700 Subject: drm/vc4: Optimize BRANCH_TO_SUBLIST validation. We typically get bin_tiles_x * bin_tiles_y of these, so it makes sense to do a tiny bit of up front work to make this easier (particularly, avoid a divide, since those are terrible). Saves about half a percent of the CPU. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 3 ++- drivers/gpu/drm/vc4/vc4_validate.c | 16 ++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 57b889e072ef..6b3e07cf8443 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -234,7 +234,8 @@ struct vc4_exec_info { bool found_wait_on_semaphore_packet; uint8_t bin_tiles_x, bin_tiles_y; uint32_t fb_width, fb_height; - uint32_t tile_alloc_init_block_size; + uint32_t tile_alloc_init_block_mask; + uint32_t tile_alloc_init_block_last; struct drm_gem_cma_object *tile_alloc_bo; /** diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 7c6925cd7929..b1f88bb8ecaa 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -286,9 +286,8 @@ validate_branch_to_sublist(VALIDATE_ARGS) } offset = *(uint32_t *)(untrusted + 0); - if (offset % exec->tile_alloc_init_block_size || - offset / exec->tile_alloc_init_block_size >= - exec->bin_tiles_x * exec->bin_tiles_y) { + if (offset & exec->tile_alloc_init_block_mask || + offset > exec->tile_alloc_init_block_last) { DRM_ERROR("VC4_PACKET_BRANCH_TO_SUB_LIST must jump to initial " "tile allocation space.\n"); return -EINVAL; @@ -496,6 +495,7 @@ validate_tile_binning_config(VALIDATE_ARGS) struct drm_gem_cma_object *tile_state_data_array; uint8_t flags; uint32_t tile_allocation_size; + uint32_t tile_alloc_init_block_size; if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &tile_allocation) || !vc4_use_handle(exec, 1, VC4_MODE_TSDA, &tile_state_data_array)) @@ -547,15 +547,19 @@ validate_tile_binning_config(VALIDATE_ARGS) *(uint32_t *)validated = tile_allocation->paddr; exec->tile_alloc_bo = tile_allocation; - exec->tile_alloc_init_block_size = 1 << (5 + ((flags >> 5) & 3)); + tile_alloc_init_block_size = 1 << (5 + ((flags >> 5) & 3)); if (exec->bin_tiles_x * exec->bin_tiles_y * - exec->tile_alloc_init_block_size > tile_allocation_size) { + tile_alloc_init_block_size > tile_allocation_size) { DRM_ERROR("tile init exceeds tile alloc size (%d vs %d)\n", exec->bin_tiles_x * exec->bin_tiles_y * - exec->tile_alloc_init_block_size, + tile_alloc_init_block_size, tile_allocation_size); return -EINVAL; } + exec->tile_alloc_init_block_mask = tile_alloc_init_block_size - 1; + exec->tile_alloc_init_block_last = tile_alloc_init_block_size * + (exec->bin_tiles_x * exec->bin_tiles_y - 1); + if (*(uint32_t *)(untrusted + 8) != 0) { DRM_ERROR("TSDA offset != 0 unsupported\n"); return -EINVAL; -- cgit v1.2.1 From 83bf3e60e7fe504e82bd030064e3a7c17624ad44 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 16:09:51 -0700 Subject: drm/vc4: Stop using relocations for BRANCH_TO_SUBLIST. If we're going to insist that the relocation target is the tile alloc BO that we saw during binner config, then just ignore the relocations and point at the tile alloc BO. Saves an immediate .6% or so of CPU, and will let us ditch the relocations we've been processing for it, too. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index b1f88bb8ecaa..29fc4476f75e 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -269,14 +269,11 @@ validate_wait_on_semaphore(VALIDATE_ARGS) static int validate_branch_to_sublist(VALIDATE_ARGS) { - struct drm_gem_cma_object *target; uint32_t offset; - if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &target)) - return -EINVAL; - - if (target != exec->tile_alloc_bo) { - DRM_ERROR("Jumping to BOs other than tile alloc unsupported\n"); + if (!exec->tile_alloc_bo) { + DRM_ERROR("VC4_PACKET_BRANCH_TO_SUB_LIST seen before " + "binner setup\n"); return -EINVAL; } @@ -293,7 +290,7 @@ validate_branch_to_sublist(VALIDATE_ARGS) return -EINVAL; } - *(uint32_t *)(validated + 0) = target->paddr + offset; + *(uint32_t *)(validated + 0) = exec->tile_alloc_bo->paddr + offset; return 0; } -- cgit v1.2.1 From ca280a9022d3932fbd301a220e04517024f6af5d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 16:27:47 -0700 Subject: drm/vc4: Restrict texture/fb sizes, to avoid a divide in validation. This avoids generating a uidiv, and saves us about a percent in check_tex_size as a result of loadstore_tile_buffer_general. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 29fc4476f75e..73c4c686498f 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -156,11 +156,17 @@ check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, uint32_t utile_w = utile_width(cpp); uint32_t utile_h = utile_height(cpp); - /* The values are limited by the packet/texture parameter bitfields, - * so we don't need to worry as much about integer overflow. + /* The shaded vertex format stores signed 12.4 fixed point + * (-2048,2047) offsets from the viewport center, so we should + * never have a render target larger than 4096. The texture + * unit can only sample from 2048x2048, so it's even more + * restricted. This lets us avoid worrying about overflow in + * our math. */ - BUG_ON(width > 65535); - BUG_ON(height > 65535); + if (width > 4096 || height > 4096) { + DRM_ERROR("Surface dimesions (%d,%d) too large", width, height); + return false; + } switch (tiling_format) { case VC4_TILING_FORMAT_LINEAR: @@ -181,13 +187,6 @@ check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, } stride = aligned_width * cpp; - - if (INT_MAX / stride < aligned_height) { - DRM_ERROR("Overflow in fbo size (%dx%d -> %dx%d)\n", - width, height, - aligned_width, aligned_height); - return false; - } size = stride * aligned_height; if (size + offset < size || -- cgit v1.2.1 From a7b0de93b1ecec6219e675a2c43cfb70800f2efd Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 16:35:57 -0700 Subject: drm/vc4: Use the power-of-two divider round_up instead of roundup. This avoids generating a uidiv, and saves us another ~.6% in check_tex_size as a result of loadstore_tile_buffer_general. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_validate.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 73c4c686498f..04e7d67f8f27 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -170,16 +170,16 @@ check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, switch (tiling_format) { case VC4_TILING_FORMAT_LINEAR: - aligned_width = roundup(width, utile_w); + aligned_width = round_up(width, utile_w); aligned_height = height; break; case VC4_TILING_FORMAT_T: - aligned_width = roundup(width, utile_w * 8); - aligned_height = roundup(height, utile_h * 8); + aligned_width = round_up(width, utile_w * 8); + aligned_height = round_up(height, utile_h * 8); break; case VC4_TILING_FORMAT_LT: - aligned_width = roundup(width, utile_w); - aligned_height = roundup(height, utile_h); + aligned_width = round_up(width, utile_w); + aligned_height = round_up(height, utile_h); break; default: DRM_ERROR("buffer tiling %d unsupported\n", tiling_format); @@ -927,15 +927,15 @@ reloc_tex(struct vc4_exec_info *exec, switch (tiling_format) { case VC4_TILING_FORMAT_T: - aligned_width = roundup(level_width, utile_w * 8); - aligned_height = roundup(level_height, utile_h * 8); + aligned_width = round_up(level_width, utile_w * 8); + aligned_height = round_up(level_height, utile_h * 8); break; case VC4_TILING_FORMAT_LT: - aligned_width = roundup(level_width, utile_w); - aligned_height = roundup(level_height, utile_h); + aligned_width = round_up(level_width, utile_w); + aligned_height = round_up(level_height, utile_h); break; default: - aligned_width = roundup(level_width, utile_w); + aligned_width = round_up(level_width, utile_w); aligned_height = level_height; break; } -- cgit v1.2.1 From d161078693e34a3315d5fd8bee9916eebfc72df3 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 17:27:18 -0700 Subject: drm/vc4: Return the proper incremented seqno for our job. I was returning the seqno before the increment, so userspace's waits on job completion could be too early. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 7e8070668eb4..abc0684d7e28 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -593,9 +593,6 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, if (ret) goto fail; - /* Return the seqno for our job. */ - args->seqno = vc4->emit_seqno; - /* Clear this out of the struct we'll be putting in the queue, * since it's part of our stack. */ @@ -603,6 +600,9 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, vc4_queue_submit(dev, exec); + /* Return the seqno for our job. */ + args->seqno = vc4->emit_seqno; + mutex_unlock(&dev->struct_mutex); return 0; -- cgit v1.2.1 From 4c5a13277fe32bb9727bd458f540196c379503d9 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 17:50:36 -0700 Subject: drm/vc4: Prevent clients from racing far ahead of execution. This is policy that we've often left up to userland in various DRM drivers. However, given how thoroughly memory-constrained we are on VC4, letting userspace run away with things and over-inflate their caches is definitely not helping. This hasn't reduced maximum throughput in my testing, since we're only waiting until the exec just before the one we queued is finished, so there will be a job to submit the moment the GPU sends us the frame done interrupt. We would only actually idle the GPU if assembling some scenes takes more CPU time than the time it takes to complete some scenes on the GPU. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_gem.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index abc0684d7e28..bedf53e02e8e 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -604,6 +604,13 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, args->seqno = vc4->emit_seqno; mutex_unlock(&dev->struct_mutex); + + /* To keep any client from getting too far ahead (particularly + * a problem when BO caching is involved), we wait on the + * previous rendering before returning to userspace. + */ + vc4_wait_for_seqno(dev, args->seqno - 1, ~0ull); + return 0; fail: -- cgit v1.2.1 From 6ea08947f9caa843e12a4fcf158740c368135885 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 17 Jun 2015 15:25:27 -0700 Subject: drm/vc4: Don't forget to turn the hardware off on unbind. This helps make sure that we don't have any state left around (like the binner overflow allocation). Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_v3d.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index 2ac27c9944f1..8841d8e97695 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -232,6 +232,8 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master, V3D_WRITE(V3D_BPOA, 0); V3D_WRITE(V3D_BPOS, 0); + vc4_v3d_set_power(vc4, false); + vc4->v3d = NULL; } -- cgit v1.2.1 From 71a3914556983a1ee9184c1ebccf062ca9c0c416 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 10 Jun 2015 12:50:14 -0700 Subject: drm/vc4: Sync validation code from userspace. We get validation that direct texturing doesn't address off the start of the array, some more symbolic #defines, and a fix for the dithering bits. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_packet.h | 167 ++++++++++++++++++++++++----- drivers/gpu/drm/vc4/vc4_validate.c | 115 ++++++++++---------- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 116 ++++++++++++++------ 3 files changed, 287 insertions(+), 111 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h index eef5be99a046..ec8586aca71f 100644 --- a/drivers/gpu/drm/vc4/vc4_packet.h +++ b/drivers/gpu/drm/vc4/vc4_packet.h @@ -24,6 +24,8 @@ #ifndef VC4_PACKET_H #define VC4_PACKET_H +#include "vc4_regs.h" /* for VC4_MASK, VC4_GET_FIELD, VC4_SET_FIELD */ + enum vc4_packet { VC4_PACKET_HALT = 0, VC4_PACKET_NOP = 1, @@ -81,6 +83,39 @@ enum vc4_packet { VC4_PACKET_GEM_HANDLES = 254, } __attribute__ ((__packed__)); +#define VC4_PACKET_HALT_SIZE 1 +#define VC4_PACKET_NOP_SIZE 1 +#define VC4_PACKET_FLUSH_SIZE 1 +#define VC4_PACKET_FLUSH_ALL_SIZE 1 +#define VC4_PACKET_START_TILE_BINNING_SIZE 1 +#define VC4_PACKET_INCREMENT_SEMAPHORE_SIZE 1 +#define VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE 1 +#define VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE 5 +#define VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE 1 +#define VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF_SIZE 1 +#define VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE 7 +#define VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE 7 +#define VC4_PACKET_GL_INDEXED_PRIMITIVE_SIZE 14 +#define VC4_PACKET_GL_ARRAY_PRIMITIVE_SIZE 10 +#define VC4_PACKET_PRIMITIVE_LIST_FORMAT_SIZE 2 +#define VC4_PACKET_GL_SHADER_STATE_SIZE 5 +#define VC4_PACKET_NV_SHADER_STATE_SIZE 5 +#define VC4_PACKET_CONFIGURATION_BITS_SIZE 4 +#define VC4_PACKET_FLAT_SHADE_FLAGS_SIZE 5 +#define VC4_PACKET_POINT_SIZE_SIZE 5 +#define VC4_PACKET_LINE_WIDTH_SIZE 5 +#define VC4_PACKET_RHT_X_BOUNDARY_SIZE 3 +#define VC4_PACKET_DEPTH_OFFSET_SIZE 5 +#define VC4_PACKET_CLIP_WINDOW_SIZE 9 +#define VC4_PACKET_VIEWPORT_OFFSET_SIZE 5 +#define VC4_PACKET_CLIPPER_XY_SCALING_SIZE 9 +#define VC4_PACKET_CLIPPER_Z_SCALING_SIZE 9 +#define VC4_PACKET_TILE_BINNING_MODE_CONFIG_SIZE 16 +#define VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE 11 +#define VC4_PACKET_CLEAR_COLORS_SIZE 14 +#define VC4_PACKET_TILE_COORDINATES_SIZE 3 +#define VC4_PACKET_GEM_HANDLES_SIZE 9 + /** @{ * Bits used by packets like VC4_PACKET_STORE_TILE_BUFFER_GENERAL and * VC4_PACKET_TILE_RENDERING_MODE_CONFIG. @@ -96,6 +131,7 @@ enum vc4_packet { * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL (low bits of the address) */ +#define VC4_LOADSTORE_TILE_BUFFER_EOF (1 << 3) #define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_VG_MASK (1 << 2) #define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_ZS (1 << 1) #define VC4_LOADSTORE_TILE_BUFFER_DISABLE_FULL_COLOR (1 << 0) @@ -104,18 +140,19 @@ enum vc4_packet { /** @{ * - * byte 1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and + * byte 0-1 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL */ -#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR (1 << 7) -#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR (1 << 6) -#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR (1 << 5) -#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP (1 << 4) - -#define VC4_LOADSTORE_TILE_BUFFER_RGBA8888 (0 << 0) -#define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER (1 << 0) -#define VC4_LOADSTORE_TILE_BUFFER_BGR565 (2 << 0) -#define VC4_LOADSTORE_TILE_BUFFER_MASK (3 << 0) +#define VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR (1 << 15) +#define VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR (1 << 14) +#define VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR (1 << 13) +#define VC4_STORE_TILE_BUFFER_DISABLE_SWAP (1 << 12) + +#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK VC4_MASK(9, 8) +#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 8 +#define VC4_LOADSTORE_TILE_BUFFER_RGBA8888 0 +#define VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER 1 +#define VC4_LOADSTORE_TILE_BUFFER_BGR565 2 /** @} */ /** @{ @@ -123,21 +160,24 @@ enum vc4_packet { * byte 0 of VC4_PACKET_STORE_TILE_BUFFER_GENERAL and * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL */ +#define VC4_STORE_TILE_BUFFER_MODE_MASK VC4_MASK(7, 6) +#define VC4_STORE_TILE_BUFFER_MODE_SHIFT 6 #define VC4_STORE_TILE_BUFFER_MODE_SAMPLE0 (0 << 6) #define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X4 (1 << 6) #define VC4_STORE_TILE_BUFFER_MODE_DECIMATE_X16 (2 << 6) /** The values of the field are VC4_TILING_FORMAT_* */ -#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK (3 << 4) -#define VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT 4 - - -#define VC4_LOADSTORE_TILE_BUFFER_NONE (0 << 0) -#define VC4_LOADSTORE_TILE_BUFFER_COLOR (1 << 0) -#define VC4_LOADSTORE_TILE_BUFFER_ZS (2 << 0) -#define VC4_LOADSTORE_TILE_BUFFER_Z (3 << 0) -#define VC4_LOADSTORE_TILE_BUFFER_VG_MASK (4 << 0) -#define VC4_LOADSTORE_TILE_BUFFER_FULL (5 << 0) +#define VC4_LOADSTORE_TILE_BUFFER_TILING_MASK VC4_MASK(5, 4) +#define VC4_LOADSTORE_TILE_BUFFER_TILING_SHIFT 4 + +#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK VC4_MASK(2, 0) +#define VC4_LOADSTORE_TILE_BUFFER_BUFFER_SHIFT 0 +#define VC4_LOADSTORE_TILE_BUFFER_NONE 0 +#define VC4_LOADSTORE_TILE_BUFFER_COLOR 1 +#define VC4_LOADSTORE_TILE_BUFFER_ZS 2 +#define VC4_LOADSTORE_TILE_BUFFER_Z 3 +#define VC4_LOADSTORE_TILE_BUFFER_VG_MASK 4 +#define VC4_LOADSTORE_TILE_BUFFER_FULL 5 /** @} */ #define VC4_INDEX_BUFFER_U8 (0 << 4) @@ -206,21 +246,28 @@ enum vc4_packet { #define VC4_RENDER_CONFIG_ENABLE_VG_MASK (1 << 8) /** The values of the field are VC4_TILING_FORMAT_* */ -#define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK (3 << 6) +#define VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK VC4_MASK(7, 6) #define VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT 6 #define VC4_RENDER_CONFIG_DECIMATE_MODE_1X (0 << 4) #define VC4_RENDER_CONFIG_DECIMATE_MODE_4X (1 << 4) #define VC4_RENDER_CONFIG_DECIMATE_MODE_16X (2 << 4) -#define VC4_RENDER_CONFIG_FORMAT_BGR565 (0 << 2) -#define VC4_RENDER_CONFIG_FORMAT_RGBA8888 (1 << 2) -#define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED (2 << 2) -#define VC4_RENDER_CONFIG_FORMAT_MASK (3 << 2) +#define VC4_RENDER_CONFIG_FORMAT_MASK VC4_MASK(3, 2) +#define VC4_RENDER_CONFIG_FORMAT_SHIFT 2 +#define VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED 0 +#define VC4_RENDER_CONFIG_FORMAT_RGBA8888 1 +#define VC4_RENDER_CONFIG_FORMAT_BGR565 2 #define VC4_RENDER_CONFIG_TILE_BUFFER_64BIT (1 << 1) #define VC4_RENDER_CONFIG_MS_MODE_4X (1 << 0) +#define VC4_PRIMITIVE_LIST_FORMAT_16_INDEX (1 << 4) +#define VC4_PRIMITIVE_LIST_FORMAT_32_XY (3 << 4) +#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_POINTS (0 << 0) +#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_LINES (1 << 0) +#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_TRIANGLES (2 << 0) +#define VC4_PRIMITIVE_LIST_FORMAT_TYPE_RHT (3 << 0) enum vc4_texture_data_type { VC4_TEXTURE_TYPE_RGBA8888 = 0, @@ -243,4 +290,74 @@ enum vc4_texture_data_type { VC4_TEXTURE_TYPE_YUV422R = 17, }; +#define VC4_TEX_P0_OFFSET_MASK VC4_MASK(31, 12) +#define VC4_TEX_P0_OFFSET_SHIFT 12 +#define VC4_TEX_P0_CSWIZ_MASK VC4_MASK(11, 10) +#define VC4_TEX_P0_CSWIZ_SHIFT 10 +#define VC4_TEX_P0_CMMODE_MASK VC4_MASK(9, 9) +#define VC4_TEX_P0_CMMODE_SHIFT 9 +#define VC4_TEX_P0_FLIPY_MASK VC4_MASK(8, 8) +#define VC4_TEX_P0_FLIPY_SHIFT 8 +#define VC4_TEX_P0_TYPE_MASK VC4_MASK(7, 4) +#define VC4_TEX_P0_TYPE_SHIFT 4 +#define VC4_TEX_P0_MIPLVLS_MASK VC4_MASK(3, 0) +#define VC4_TEX_P0_MIPLVLS_SHIFT 0 + +#define VC4_TEX_P1_TYPE4_MASK VC4_MASK(31, 31) +#define VC4_TEX_P1_TYPE4_SHIFT 31 +#define VC4_TEX_P1_HEIGHT_MASK VC4_MASK(30, 20) +#define VC4_TEX_P1_HEIGHT_SHIFT 20 +#define VC4_TEX_P1_ETCFLIP_MASK VC4_MASK(19, 19) +#define VC4_TEX_P1_ETCFLIP_SHIFT 19 +#define VC4_TEX_P1_WIDTH_MASK VC4_MASK(18, 8) +#define VC4_TEX_P1_WIDTH_SHIFT 8 + +#define VC4_TEX_P1_MAGFILT_MASK VC4_MASK(7, 7) +#define VC4_TEX_P1_MAGFILT_SHIFT 7 +# define VC4_TEX_P1_MAGFILT_LINEAR 0 +# define VC4_TEX_P1_MAGFILT_NEAREST 1 + +#define VC4_TEX_P1_MINFILT_MASK VC4_MASK(6, 4) +#define VC4_TEX_P1_MINFILT_SHIFT 4 +# define VC4_TEX_P1_MINFILT_LINEAR 0 +# define VC4_TEX_P1_MINFILT_NEAREST 1 +# define VC4_TEX_P1_MINFILT_NEAR_MIP_NEAR 2 +# define VC4_TEX_P1_MINFILT_NEAR_MIP_LIN 3 +# define VC4_TEX_P1_MINFILT_LIN_MIP_NEAR 4 +# define VC4_TEX_P1_MINFILT_LIN_MIP_LIN 5 + +#define VC4_TEX_P1_WRAP_T_MASK VC4_MASK(3, 2) +#define VC4_TEX_P1_WRAP_T_SHIFT 2 +#define VC4_TEX_P1_WRAP_S_MASK VC4_MASK(1, 0) +#define VC4_TEX_P1_WRAP_S_SHIFT 0 +# define VC4_TEX_P1_WRAP_REPEAT 0 +# define VC4_TEX_P1_WRAP_CLAMP 1 +# define VC4_TEX_P1_WRAP_MIRROR 2 +# define VC4_TEX_P1_WRAP_BORDER 3 + +#define VC4_TEX_P2_PTYPE_MASK VC4_MASK(31, 30) +#define VC4_TEX_P2_PTYPE_SHIFT 30 +# define VC4_TEX_P2_PTYPE_IGNORED 0 +# define VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE 1 +# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS 2 +# define VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS 3 + +/* VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE bits */ +#define VC4_TEX_P2_CMST_MASK VC4_MASK(29, 12) +#define VC4_TEX_P2_CMST_SHIFT 12 +#define VC4_TEX_P2_BSLOD_MASK VC4_MASK(0, 0) +#define VC4_TEX_P2_BSLOD_SHIFT 0 + +/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_DIMENSIONS */ +#define VC4_TEX_P2_CHEIGHT_MASK VC4_MASK(22, 12) +#define VC4_TEX_P2_CHEIGHT_SHIFT 12 +#define VC4_TEX_P2_CWIDTH_MASK VC4_MASK(10, 0) +#define VC4_TEX_P2_CWIDTH_SHIFT 0 + +/* VC4_TEX_P2_PTYPE_CHILD_IMAGE_OFFSETS */ +#define VC4_TEX_P2_CYOFF_MASK VC4_MASK(22, 12) +#define VC4_TEX_P2_CYOFF_SHIFT 12 +#define VC4_TEX_P2_CXOFF_MASK VC4_MASK(10, 0) +#define VC4_TEX_P2_CXOFF_SHIFT 0 + #endif /* VC4_PACKET_H */ diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 04e7d67f8f27..abd37867e4d2 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -311,17 +311,18 @@ validate_branch_to_sublist(VALIDATE_ARGS) static int validate_loadstore_tile_buffer_general(VALIDATE_ARGS) { - uint32_t packet_b0 = *(uint8_t *)(untrusted + 0); - uint32_t packet_b1 = *(uint8_t *)(untrusted + 1); + uint16_t packet_b01 = *(uint16_t *)(untrusted + 0); struct drm_gem_cma_object *fbo; - uint32_t buffer_type = packet_b0 & 0xf; + uint32_t buffer_type = VC4_GET_FIELD(packet_b01, + VC4_LOADSTORE_TILE_BUFFER_BUFFER); uint32_t untrusted_address, offset, cpp; switch (buffer_type) { case VC4_LOADSTORE_TILE_BUFFER_NONE: return 0; case VC4_LOADSTORE_TILE_BUFFER_COLOR: - if ((packet_b1 & VC4_LOADSTORE_TILE_BUFFER_MASK) == + if (VC4_GET_FIELD(packet_b01, + VC4_LOADSTORE_TILE_BUFFER_FORMAT) == VC4_LOADSTORE_TILE_BUFFER_RGBA8888) { cpp = 4; } else { @@ -346,9 +347,8 @@ validate_loadstore_tile_buffer_general(VALIDATE_ARGS) offset = untrusted_address & ~0xf; if (!check_tex_size(exec, fbo, offset, - ((packet_b0 & - VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK) >> - VC4_LOADSTORE_TILE_BUFFER_FORMAT_SHIFT), + VC4_GET_FIELD(packet_b01, + VC4_LOADSTORE_TILE_BUFFER_TILING), exec->fb_width, exec->fb_height, cpp)) { return -EINVAL; } @@ -590,7 +590,7 @@ validate_tile_rendering_mode_config(VALIDATE_ARGS) exec->fb_height = *(uint16_t *)(untrusted + 6); flags = *(uint16_t *)(untrusted + 8); - if ((flags & VC4_RENDER_CONFIG_FORMAT_MASK) == + if (VC4_GET_FIELD(flags, VC4_RENDER_CONFIG_FORMAT) == VC4_RENDER_CONFIG_FORMAT_RGBA8888) { cpp = 4; } else { @@ -599,9 +599,8 @@ validate_tile_rendering_mode_config(VALIDATE_ARGS) offset = *(uint32_t *)untrusted; if (!check_tex_size(exec, fbo, offset, - ((flags & - VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK) >> - VC4_RENDER_CONFIG_MEMORY_FORMAT_SHIFT), + VC4_GET_FIELD(flags, + VC4_RENDER_CONFIG_MEMORY_FORMAT), exec->fb_width, exec->fb_height, cpp)) { return -EINVAL; } @@ -633,6 +632,9 @@ validate_gem_handles(VALIDATE_ARGS) return 0; } +#define VC4_DEFINE_PACKET(packet, bin, render, name, func) \ + [packet] = { bin, render, packet ## _SIZE, name, func } + static const struct cmd_info { bool bin; bool render; @@ -641,59 +643,59 @@ static const struct cmd_info { int (*func)(struct vc4_exec_info *exec, void *validated, void *untrusted); } cmd_info[] = { - [VC4_PACKET_HALT] = { 1, 1, 1, "halt", NULL }, - [VC4_PACKET_NOP] = { 1, 1, 1, "nop", NULL }, - [VC4_PACKET_FLUSH] = { 1, 1, 1, "flush", NULL }, - [VC4_PACKET_FLUSH_ALL] = { 1, 0, 1, "flush all state", validate_flush_all }, - [VC4_PACKET_START_TILE_BINNING] = { 1, 0, 1, "start tile binning", validate_start_tile_binning }, - [VC4_PACKET_INCREMENT_SEMAPHORE] = { 1, 0, 1, "increment semaphore", validate_increment_semaphore }, - [VC4_PACKET_WAIT_ON_SEMAPHORE] = { 0, 1, 1, "wait on semaphore", validate_wait_on_semaphore }, + VC4_DEFINE_PACKET(VC4_PACKET_HALT, 1, 1, "halt", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_NOP, 1, 1, "nop", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, 1, 1, "flush", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, 1, 0, "flush all state", validate_flush_all), + VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, 1, 0, "start tile binning", validate_start_tile_binning), + VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, 1, 0, "increment semaphore", validate_increment_semaphore), + VC4_DEFINE_PACKET(VC4_PACKET_WAIT_ON_SEMAPHORE, 0, 1, "wait on semaphore", validate_wait_on_semaphore), /* BRANCH_TO_SUB_LIST is actually supported in the binner as well, but * we only use it from the render CL in order to jump into the tile * allocation BO. */ - [VC4_PACKET_BRANCH_TO_SUB_LIST] = { 0, 1, 5, "branch to sublist", validate_branch_to_sublist }, - [VC4_PACKET_STORE_MS_TILE_BUFFER] = { 0, 1, 1, "store MS resolved tile color buffer", NULL }, - [VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF] = { 0, 1, 1, "store MS resolved tile color buffer and EOF", NULL }, + VC4_DEFINE_PACKET(VC4_PACKET_BRANCH_TO_SUB_LIST, 0, 1, "branch to sublist", validate_branch_to_sublist), + VC4_DEFINE_PACKET(VC4_PACKET_STORE_MS_TILE_BUFFER, 0, 1, "store MS resolved tile color buffer", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF, 0, 1, "store MS resolved tile color buffer and EOF", NULL), - [VC4_PACKET_STORE_TILE_BUFFER_GENERAL] = { 0, 1, 7, "Store Tile Buffer General", validate_loadstore_tile_buffer_general }, - [VC4_PACKET_LOAD_TILE_BUFFER_GENERAL] = { 0, 1, 7, "Load Tile Buffer General", validate_loadstore_tile_buffer_general }, + VC4_DEFINE_PACKET(VC4_PACKET_STORE_TILE_BUFFER_GENERAL, 0, 1, "Store Tile Buffer General", validate_loadstore_tile_buffer_general), + VC4_DEFINE_PACKET(VC4_PACKET_LOAD_TILE_BUFFER_GENERAL, 0, 1, "Load Tile Buffer General", validate_loadstore_tile_buffer_general), - [VC4_PACKET_GL_INDEXED_PRIMITIVE] = { 1, 1, 14, "Indexed Primitive List", validate_indexed_prim_list }, + VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, 1, 1, "Indexed Primitive List", validate_indexed_prim_list), - [VC4_PACKET_GL_ARRAY_PRIMITIVE] = { 1, 1, 10, "Vertex Array Primitives", validate_gl_array_primitive }, + VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, 1, 1, "Vertex Array Primitives", validate_gl_array_primitive), /* This is only used by clipped primitives (packets 48 and 49), which * we don't support parsing yet. */ - [VC4_PACKET_PRIMITIVE_LIST_FORMAT] = { 1, 1, 2, "primitive list format", NULL }, - - [VC4_PACKET_GL_SHADER_STATE] = { 1, 1, 5, "GL Shader State", validate_gl_shader_state }, - [VC4_PACKET_NV_SHADER_STATE] = { 1, 1, 5, "NV Shader State", validate_nv_shader_state }, - - [VC4_PACKET_CONFIGURATION_BITS] = { 1, 1, 4, "configuration bits", NULL }, - [VC4_PACKET_FLAT_SHADE_FLAGS] = { 1, 1, 5, "flat shade flags", NULL }, - [VC4_PACKET_POINT_SIZE] = { 1, 1, 5, "point size", NULL }, - [VC4_PACKET_LINE_WIDTH] = { 1, 1, 5, "line width", NULL }, - [VC4_PACKET_RHT_X_BOUNDARY] = { 1, 1, 3, "RHT X boundary", NULL }, - [VC4_PACKET_DEPTH_OFFSET] = { 1, 1, 5, "Depth Offset", NULL }, - [VC4_PACKET_CLIP_WINDOW] = { 1, 1, 9, "Clip Window", NULL }, - [VC4_PACKET_VIEWPORT_OFFSET] = { 1, 1, 5, "Viewport Offset", NULL }, - [VC4_PACKET_CLIPPER_XY_SCALING] = { 1, 1, 9, "Clipper XY Scaling", NULL }, + VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, 1, 1, "primitive list format", NULL), + + VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, 1, 1, "GL Shader State", validate_gl_shader_state), + VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, 1, 1, "NV Shader State", validate_nv_shader_state), + + VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, 1, 1, "configuration bits", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, 1, 1, "flat shade flags", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, 1, 1, "point size", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, 1, 1, "line width", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, 1, 1, "RHT X boundary", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, 1, 1, "Depth Offset", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, 1, 1, "Clip Window", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, 1, 1, "Viewport Offset", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, 1, 1, "Clipper XY Scaling", NULL), /* Note: The docs say this was also 105, but it was 106 in the * initial userland code drop. */ - [VC4_PACKET_CLIPPER_Z_SCALING] = { 1, 1, 9, "Clipper Z Scale and Offset", NULL }, + VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, 1, 1, "Clipper Z Scale and Offset", NULL), - [VC4_PACKET_TILE_BINNING_MODE_CONFIG] = { 1, 0, 16, "tile binning configuration", validate_tile_binning_config }, + VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, 1, 0, "tile binning configuration", validate_tile_binning_config), - [VC4_PACKET_TILE_RENDERING_MODE_CONFIG] = { 0, 1, 11, "tile rendering mode configuration", validate_tile_rendering_mode_config}, + VC4_DEFINE_PACKET(VC4_PACKET_TILE_RENDERING_MODE_CONFIG, 0, 1, "tile rendering mode configuration", validate_tile_rendering_mode_config), - [VC4_PACKET_CLEAR_COLORS] = { 0, 1, 14, "Clear Colors", NULL }, + VC4_DEFINE_PACKET(VC4_PACKET_CLEAR_COLORS, 0, 1, "Clear Colors", NULL), - [VC4_PACKET_TILE_COORDINATES] = { 0, 1, 3, "Tile Coordinates", validate_tile_coordinates }, + VC4_DEFINE_PACKET(VC4_PACKET_TILE_COORDINATES, 0, 1, "Tile Coordinates", validate_tile_coordinates), - [VC4_PACKET_GEM_HANDLES] = { 1, 1, 9, "GEM handles", validate_gem_handles }, + VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, 1, 1, "GEM handles", validate_gem_handles), }; int @@ -814,10 +816,10 @@ reloc_tex(struct vc4_exec_info *exec, uint32_t p3 = (sample->p_offset[3] != ~0 ? *(uint32_t *)(uniform_data_u + sample->p_offset[3]) : 0); uint32_t *validated_p0 = exec->uniforms_v + sample->p_offset[0]; - uint32_t offset = p0 & ~0xfff; - uint32_t miplevels = (p0 & 15); - uint32_t width = (p1 >> 8) & 2047; - uint32_t height = (p1 >> 20) & 2047; + uint32_t offset = p0 & VC4_TEX_P0_OFFSET_MASK; + uint32_t miplevels = VC4_GET_FIELD(p0, VC4_TEX_P0_MIPLVLS); + uint32_t width = VC4_GET_FIELD(p1, VC4_TEX_P1_WIDTH); + uint32_t height = VC4_GET_FIELD(p1, VC4_TEX_P1_HEIGHT); uint32_t cpp, tiling_format, utile_w, utile_h; uint32_t i; uint32_t cube_map_stride = 0; @@ -845,16 +847,18 @@ reloc_tex(struct vc4_exec_info *exec, if (height == 0) height = 2048; - if (p0 & (1 << 9)) { - if ((p2 & (3 << 30)) == (1 << 30)) - cube_map_stride = p2 & 0x3ffff000; - if ((p3 & (3 << 30)) == (1 << 30)) { + if (p0 & VC4_TEX_P0_CMMODE_MASK) { + if (VC4_GET_FIELD(p2, VC4_TEX_P2_PTYPE) == + VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) + cube_map_stride = p2 & VC4_TEX_P2_CMST_MASK; + if (VC4_GET_FIELD(p3, VC4_TEX_P2_PTYPE) == + VC4_TEX_P2_PTYPE_CUBE_MAP_STRIDE) { if (cube_map_stride) { DRM_ERROR("Cube map stride set twice\n"); return false; } - cube_map_stride = p3 & 0x3ffff000; + cube_map_stride = p3 & VC4_TEX_P2_CMST_MASK; } if (!cube_map_stride) { DRM_ERROR("Cube map stride not set\n"); @@ -862,7 +866,8 @@ reloc_tex(struct vc4_exec_info *exec, } } - type = ((p0 >> 4) & 15) | ((p1 >> 31) << 4); + type = (VC4_GET_FIELD(p0, VC4_TEX_P0_TYPE) | + (VC4_GET_FIELD(p1, VC4_TEX_P1_TYPE4) << 4)); switch (type) { case VC4_TEXTURE_TYPE_RGBA8888: diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c index 839d5521cfe5..01351874ff8c 100644 --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c @@ -57,7 +57,8 @@ struct vc4_shader_validation_state { * * This is used for the validation of direct address memory reads. */ - uint32_t live_clamp_offsets[32 + 32 + 4]; + uint32_t live_min_clamp_offsets[32 + 32 + 4]; + bool live_max_clamp_regs[32 + 32 + 4]; }; static uint32_t @@ -76,6 +77,25 @@ waddr_to_live_reg_index(uint32_t waddr, bool is_b) } } +static uint32_t +raddr_add_a_to_live_reg_index(uint64_t inst) +{ + uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); + uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); + uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); + uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); + + if (add_a == QPU_MUX_A) { + return raddr_a; + } else if (add_a == QPU_MUX_B && sig != QPU_SIG_SMALL_IMM) { + return 32 + raddr_b; + } else if (add_a <= QPU_MUX_R3) { + return 64 + add_a; + } else { + return ~0; + } +} + static bool is_tmu_submit(uint32_t waddr) { @@ -135,9 +155,8 @@ check_tmu_write(uint64_t inst, uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); if (is_direct) { - uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); - uint32_t clamp_offset = ~0; + uint32_t clamp_reg, clamp_offset; if (sig == QPU_SIG_SMALL_IMM) { DRM_ERROR("direct TMU read used small immediate\n"); @@ -158,14 +177,13 @@ check_tmu_write(uint64_t inst, * This is arbitrary, but simpler than supporting flipping the * two either way. */ - if (add_a == QPU_MUX_A) { - clamp_offset = validation_state->live_clamp_offsets[raddr_a]; - } else if (add_a == QPU_MUX_B) { - clamp_offset = validation_state->live_clamp_offsets[32 + raddr_b]; - } else if (add_a <= QPU_MUX_R4) { - clamp_offset = validation_state->live_clamp_offsets[64 + add_a]; + clamp_reg = raddr_add_a_to_live_reg_index(inst); + if (clamp_reg == ~0) { + DRM_ERROR("direct TMU load wasn't clamped\n"); + return false; } + clamp_offset = validation_state->live_min_clamp_offsets[clamp_reg]; if (clamp_offset == ~0) { DRM_ERROR("direct TMU load wasn't clamped\n"); return false; @@ -228,8 +246,6 @@ check_register_write(uint64_t inst, uint32_t waddr = (is_mul ? QPU_GET_FIELD(inst, QPU_WADDR_MUL) : QPU_GET_FIELD(inst, QPU_WADDR_ADD)); - bool is_b = is_mul != ((inst & QPU_WS) != 0); - uint32_t live_reg_index; switch (waddr) { case QPU_W_UNIFORMS_ADDRESS: @@ -284,14 +300,6 @@ check_register_write(uint64_t inst, return true; } - /* Clear out the live offset clamp tracking for the written register. - * If this particular instruction is setting up an offset clamp, it'll - * get tracked immediately after we return. - */ - live_reg_index = waddr_to_live_reg_index(waddr, is_b); - if (live_reg_index != ~0) - validation_state->live_clamp_offsets[live_reg_index] = ~0; - return true; } @@ -300,26 +308,72 @@ track_live_clamps(uint64_t inst, struct vc4_validated_shader_info *validated_shader, struct vc4_shader_validation_state *validation_state) { + uint32_t op_add = QPU_GET_FIELD(inst, QPU_OP_ADD); uint32_t waddr_add = QPU_GET_FIELD(inst, QPU_WADDR_ADD); + uint32_t waddr_mul = QPU_GET_FIELD(inst, QPU_WADDR_MUL); + uint32_t cond_add = QPU_GET_FIELD(inst, QPU_COND_ADD); + uint32_t add_a = QPU_GET_FIELD(inst, QPU_ADD_A); uint32_t add_b = QPU_GET_FIELD(inst, QPU_ADD_B); uint32_t raddr_a = QPU_GET_FIELD(inst, QPU_RADDR_A); uint32_t raddr_b = QPU_GET_FIELD(inst, QPU_RADDR_B); uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG); - bool is_b = inst & QPU_WS; - uint32_t live_reg_index; + bool ws = inst & QPU_WS; + uint32_t lri_add_a, lri_add, lri_mul; + bool add_a_is_min_0; - if (QPU_GET_FIELD(inst, QPU_OP_ADD) != QPU_A_MIN) + /* Check whether OP_ADD's A argumennt comes from a live MAX(x, 0), + * before we clear previous live state. + */ + lri_add_a = raddr_add_a_to_live_reg_index(inst); + add_a_is_min_0 = (lri_add_a != ~0 && + validation_state->live_max_clamp_regs[lri_add_a]); + + /* Clear live state for registers written by our instruction. */ + lri_add = waddr_to_live_reg_index(waddr_add, ws); + lri_mul = waddr_to_live_reg_index(waddr_mul, !ws); + if (lri_mul != ~0) { + validation_state->live_max_clamp_regs[lri_mul] = false; + validation_state->live_min_clamp_offsets[lri_mul] = ~0; + } + if (lri_add != ~0) { + validation_state->live_max_clamp_regs[lri_add] = false; + validation_state->live_min_clamp_offsets[lri_add] = ~0; + } else { + /* Nothing further to do for live tracking, since only ADDs + * generate new live clamp registers. + */ return; + } + + /* Now, handle remaining live clamp tracking for the ADD operation. */ - if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && - !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF && - sig != QPU_SIG_SMALL_IMM)) { + if (cond_add != QPU_COND_ALWAYS) return; - } - live_reg_index = waddr_to_live_reg_index(waddr_add, is_b); - if (live_reg_index != ~0) { - validation_state->live_clamp_offsets[live_reg_index] = + if (op_add == QPU_A_MAX) { + /* Track live clamps of a value to a minimum of 0 (in either + * arg). + */ + if (sig != QPU_SIG_SMALL_IMM || raddr_b != 0 || + (add_a != QPU_MUX_B && add_b != QPU_MUX_B)) { + return; + } + + validation_state->live_max_clamp_regs[lri_add] = true; + } if (op_add == QPU_A_MIN) { + /* Track live clamps of a value clamped to a minimum of 0 and + * a maximum of some uniform's offset. + */ + if (!add_a_is_min_0) + return; + + if (!(add_b == QPU_MUX_A && raddr_a == QPU_R_UNIF) && + !(add_b == QPU_MUX_B && raddr_b == QPU_R_UNIF && + sig != QPU_SIG_SMALL_IMM)) { + return; + } + + validation_state->live_min_clamp_offsets[lri_add] = validated_shader->uniforms_size; } } @@ -399,8 +453,8 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj) for (i = 0; i < 8; i++) validation_state.tmu_setup[i / 4].p_offset[i % 4] = ~0; - for (i = 0; i < ARRAY_SIZE(validation_state.live_clamp_offsets); i++) - validation_state.live_clamp_offsets[i] = ~0; + for (i = 0; i < ARRAY_SIZE(validation_state.live_min_clamp_offsets); i++) + validation_state.live_min_clamp_offsets[i] = ~0; shader = shader_obj->vaddr; max_ip = shader_obj->base.size / sizeof(uint64_t); -- cgit v1.2.1 From 8d102f7a302db39539ce780875f88ea62270818b Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 17 Jun 2015 15:05:57 -0700 Subject: drm/vc4: Allocate the correct size for our bin BO. The 256k was left over from early bringup. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 6 +++++- drivers/gpu/drm/vc4/vc4_gem.c | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 62e8a0c27a39..26817bfe71b4 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -119,13 +119,17 @@ vc4_bo_cache_purge(struct drm_device *dev) } struct vc4_bo * -vc4_bo_create(struct drm_device *dev, size_t size) +vc4_bo_create(struct drm_device *dev, size_t unaligned_size) { struct vc4_dev *vc4 = to_vc4_dev(dev); + uint32_t size = roundup(unaligned_size, PAGE_SIZE); uint32_t page_index = bo_page_index(size); struct vc4_bo *bo = NULL; struct drm_gem_cma_object *cma_obj; + if (size == 0) + return NULL; + /* First, try to get a vc4_bo from the kernel BO cache. */ if (vc4->bo_cache.size_list_size > page_index) { if (!list_empty(&vc4->bo_cache.size_list[page_index])) { diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index bedf53e02e8e..7bb446cc1f40 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -407,7 +407,7 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) goto fail; } - bo = vc4_bo_create(dev, 256 * 1024); + bo = vc4_bo_create(dev, exec_size); if (!bo) { DRM_ERROR("Couldn't allocate BO for exec\n"); ret = PTR_ERR(exec->exec_bo); -- cgit v1.2.1 From fefaa64b6faf8bd7bac610758cefe3c405962fe1 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Tue, 16 Jun 2015 16:18:38 -0700 Subject: drm/vc4: Clean up V3D interrupt handling, and document how it works. My surprise about having to set INTENA for OUTOMEM when I hadn't cleared anything in it now makes sense, having found that there is just one set of underlying enable bits behind the INTENA/INTDIS registers. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_irq.c | 50 ++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index b5a1a273f83f..2d0b91a9bd93 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -21,6 +21,25 @@ * IN THE SOFTWARE. */ +/** DOC: Interrupt management for the V3D engine. + * + * We have an interrupt status register (V3D_INTCTL) which reports + * interrupts, and where writing 1 bits clears those interrupts. + * There are also a pair of interrupt registers + * (V3D_INTENA/V3D_INTDIS) where writing a 1 to their bits enables or + * disables that specific interrupt, and 0s written are ignored + * (reading either one returns the set of enabled interrupts). + * + * When we take a render frame interrupt, we need to wake the + * processes waiting for some frame to be done, and get the next frame + * submitted ASAP (so the hardware doesn't sit idle when there's work + * to do). + * + * When we take the binner out of memory interrupt, we need to + * allocate some new memory and pass it to the binner so that the + * current job can make progress. + */ + #include "vc4_drv.h" #include "vc4_regs.h" @@ -77,9 +96,8 @@ vc4_overflow_mem_work(struct work_struct *work) V3D_WRITE(V3D_BPOA, bo->base.paddr); V3D_WRITE(V3D_BPOS, bo->base.base.size); - V3D_WRITE(V3D_INTDIS, 0); - V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); + V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); } static void @@ -109,9 +127,15 @@ vc4_irq(int irq, void *arg) barrier(); intctl = V3D_READ(V3D_INTCTL); + + /* Acknowledge the interrupts we're handling here. The render + * frame done interrupt will be cleared, while OUTOMEM will + * stay high until the underlying cause is cleared. + */ V3D_WRITE(V3D_INTCTL, intctl); if (intctl & V3D_INT_OUTOMEM) { + /* Disable OUTOMEM until the work is done. */ V3D_WRITE(V3D_INTDIS, V3D_INT_OUTOMEM); schedule_work(&vc4->overflow_mem_work); status = IRQ_HANDLED; @@ -146,16 +170,9 @@ vc4_irq_postinstall(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - /* Enable both the bin and render done interrupts, as well as - * out of memory. Eventually, we'll have the bin use internal - * semaphores with render to sync between the two, but for now - * we're driving that from the ARM. - */ + /* Enable both the render done and out of memory interrupts. */ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); - /* No interrupts disabled. */ - V3D_WRITE(V3D_INTDIS, 0); - return 0; } @@ -164,8 +181,8 @@ vc4_irq_uninstall(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); - V3D_WRITE(V3D_INTENA, 0); - V3D_WRITE(V3D_INTDIS, 0); + /* Disable sending interrupts for our driver's IRQs. */ + V3D_WRITE(V3D_INTDIS, V3D_DRIVER_IRQS); /* Clear any pending interrupts we might have left. */ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); @@ -179,8 +196,15 @@ void vc4_irq_reset(struct drm_device *dev) struct vc4_dev *vc4 = to_vc4_dev(dev); unsigned long irqflags; + /* Acknowledge any stale IRQs. */ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); - V3D_WRITE(V3D_INTDIS, 0); + + /* + * Turn all our interrupts on. Binner out of memory is the + * only one we expect to trigger at this point, since we've + * just come from poweron and haven't supplied any overflow + * memory yet. + */ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); spin_lock_irqsave(&vc4->job_lock, irqflags); -- cgit v1.2.1 From d2240a43f12494484b893eda8d607b036448a3fb Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 17 Jun 2015 15:37:54 -0700 Subject: drm/vc4: Clean up BO allocation from the cache a bit. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 26817bfe71b4..b6169d3a93c1 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -124,7 +124,6 @@ vc4_bo_create(struct drm_device *dev, size_t unaligned_size) struct vc4_dev *vc4 = to_vc4_dev(dev); uint32_t size = roundup(unaligned_size, PAGE_SIZE); uint32_t page_index = bo_page_index(size); - struct vc4_bo *bo = NULL; struct drm_gem_cma_object *cma_obj; if (size == 0) @@ -133,15 +132,14 @@ vc4_bo_create(struct drm_device *dev, size_t unaligned_size) /* First, try to get a vc4_bo from the kernel BO cache. */ if (vc4->bo_cache.size_list_size > page_index) { if (!list_empty(&vc4->bo_cache.size_list[page_index])) { - bo = list_first_entry(&vc4->bo_cache.size_list[page_index], - struct vc4_bo, size_head); + struct vc4_bo *bo = + list_first_entry(&vc4->bo_cache.size_list[page_index], + struct vc4_bo, size_head); vc4_bo_remove_from_cache(bo); + kref_init(&bo->base.base.refcount); + return bo; } } - if (bo) { - kref_init(&bo->base.base.refcount); - return bo; - } /* Otherwise, make a new BO. */ cma_obj = drm_gem_cma_create(dev, size); -- cgit v1.2.1 From 378249d4a8186872d9b43b2d7b2d3a773ea1229e Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 17 Jun 2015 15:38:42 -0700 Subject: drm/vc4: Try even harder to succeed at allocations (wait for GPU). Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_bo.c | 35 ++++++++++++++++++++++++++++------- drivers/gpu/drm/vc4/vc4_drv.h | 3 +++ drivers/gpu/drm/vc4/vc4_gem.c | 32 +++++++++++++++++++------------- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index b6169d3a93c1..e4f14aa8ca9f 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -125,6 +125,7 @@ vc4_bo_create(struct drm_device *dev, size_t unaligned_size) uint32_t size = roundup(unaligned_size, PAGE_SIZE); uint32_t page_index = bo_page_index(size); struct drm_gem_cma_object *cma_obj; + int pass; if (size == 0) return NULL; @@ -142,15 +143,35 @@ vc4_bo_create(struct drm_device *dev, size_t unaligned_size) } /* Otherwise, make a new BO. */ - cma_obj = drm_gem_cma_create(dev, size); - if (IS_ERR(cma_obj)) { - /* If we've run out of CMA memory, kill the cache of - * CMA allocations we've got laying around and try again. - */ - vc4_bo_cache_purge(dev); + for (pass = 0; ; pass++) { cma_obj = drm_gem_cma_create(dev, size); - if (IS_ERR(cma_obj)) + if (!IS_ERR(cma_obj)) + break; + + switch (pass) { + case 0: + /* + * If we've run out of CMA memory, kill the cache of + * CMA allocations we've got laying around and try again. + */ + vc4_bo_cache_purge(dev); + break; + case 1: + /* + * Getting desperate, so try to wait for any + * previous rendering to finish, free its + * unreferenced BOs to the cache, and then + * free the cache. + */ + vc4_wait_for_seqno(dev, vc4->emit_seqno, ~0ull); + vc4_job_handle_completed(vc4); + vc4_bo_cache_purge(dev); + break; + case 3: + DRM_ERROR("Failed to allocate from CMA:\n"); + vc4_bo_stats_dump(); return NULL; + } } bo_stats.num_allocated++; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 6b3e07cf8443..bc4384e3d4cb 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -387,6 +387,9 @@ int vc4_wait_seqno_ioctl(struct drm_device *dev, void *data, int vc4_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void vc4_submit_next_job(struct drm_device *dev); +int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, + uint64_t timeout_ns); +void vc4_job_handle_completed(struct vc4_dev *vc4); /* vc4_hdmi.c */ void vc4_hdmi_register(void); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 7bb446cc1f40..54fe0b83421b 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -117,7 +117,7 @@ submit_cl(struct drm_device *dev, uint32_t thread, uint32_t start, uint32_t end) barrier(); } -static int +int vc4_wait_for_seqno(struct drm_device *dev, uint64_t seqno, uint64_t timeout_ns) { struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -477,21 +477,11 @@ vc4_complete_exec(struct vc4_exec_info *exec) kfree(exec); } -/* Scheduled when any job has been completed, this walks the list of - * jobs that had completed and unrefs their BOs and frees their exec - * structs. - */ -static void -vc4_job_done_work(struct work_struct *work) +void +vc4_job_handle_completed(struct vc4_dev *vc4) { - struct vc4_dev *vc4 = - container_of(work, struct vc4_dev, job_done_work); - struct drm_device *dev = vc4->dev; unsigned long irqflags; - /* Need the struct lock for drm_gem_object_unreference(). */ - mutex_lock(&dev->struct_mutex); - spin_lock_irqsave(&vc4->job_lock, irqflags); while (!list_empty(&vc4->job_done_list)) { struct vc4_exec_info *exec = @@ -505,6 +495,22 @@ vc4_job_done_work(struct work_struct *work) } spin_unlock_irqrestore(&vc4->job_lock, irqflags); +} + +/* Scheduled when any job has been completed, this walks the list of + * jobs that had completed and unrefs their BOs and frees their exec + * structs. + */ +static void +vc4_job_done_work(struct work_struct *work) +{ + struct vc4_dev *vc4 = + container_of(work, struct vc4_dev, job_done_work); + struct drm_device *dev = vc4->dev; + + /* Need the struct lock for drm_gem_object_unreference(). */ + mutex_lock(&dev->struct_mutex); + vc4_job_handle_completed(vc4); mutex_unlock(&dev->struct_mutex); } -- cgit v1.2.1 From 93b2a13c9086924adfff516028486f529f102a38 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 12 Jun 2015 11:54:54 -0700 Subject: drm/vc4: Switch to generating RCLs from the kernel. Validation was expensive, and there aren't enough variations of RCLs to warrant it. This also may let us cache generated RCLs, to reduce overhead even further. Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/Makefile | 1 + drivers/gpu/drm/vc4/vc4_drv.h | 28 ++- drivers/gpu/drm/vc4/vc4_gem.c | 59 ++--- drivers/gpu/drm/vc4/vc4_render_cl.c | 447 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/vc4/vc4_validate.c | 307 ++++--------------------- include/uapi/drm/vc4_drm.h | 40 ++-- 6 files changed, 561 insertions(+), 321 deletions(-) create mode 100644 drivers/gpu/drm/vc4/vc4_render_cl.c diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index 0abf23d62f9e..cf3898da60f5 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -13,6 +13,7 @@ vc4-y := \ vc4_hvs.o \ vc4_irq.o \ vc4_plane.o \ + vc4_render_cl.o \ vc4_v3d.o \ vc4_validate.o \ vc4_validate_shaders.o \ diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index bc4384e3d4cb..e4e2e081628c 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -228,14 +228,9 @@ struct vc4_exec_info { uint32_t shader_state_count; bool found_tile_binning_mode_config_packet; - bool found_tile_rendering_mode_config_packet; bool found_start_tile_binning_packet; bool found_increment_semaphore_packet; - bool found_wait_on_semaphore_packet; uint8_t bin_tiles_x, bin_tiles_y; - uint32_t fb_width, fb_height; - uint32_t tile_alloc_init_block_mask; - uint32_t tile_alloc_init_block_last; struct drm_gem_cma_object *tile_alloc_bo; /** @@ -430,16 +425,25 @@ int vc4_v3d_set_power(struct vc4_dev *vc4, bool on); /* vc4_validate.c */ int -vc4_validate_cl(struct drm_device *dev, - void *validated, - void *unvalidated, - uint32_t len, - bool is_bin, - bool has_bin, - struct vc4_exec_info *exec); +vc4_validate_bin_cl(struct drm_device *dev, + void *validated, + void *unvalidated, + struct vc4_exec_info *exec); int vc4_validate_shader_recs(struct drm_device *dev, struct vc4_exec_info *exec); struct vc4_validated_shader_info * vc4_validate_shader(struct drm_gem_cma_object *shader_obj); + +bool vc4_use_bo(struct vc4_exec_info *exec, + uint32_t hindex, + enum vc4_bo_mode mode, + struct drm_gem_cma_object **obj); + +int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec); + +bool vc4_check_tex_size(struct vc4_exec_info *exec, + struct drm_gem_cma_object *fbo, + uint32_t offset, uint8_t tiling_format, + uint32_t width, uint32_t height, uint8_t cpp); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 54fe0b83421b..28c6a9c80fa0 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -328,24 +328,22 @@ fail: } static int -vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) +vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec) { struct drm_vc4_submit_cl *args = exec->args; void *temp = NULL; - void *bin, *render; + void *bin; int ret = 0; uint32_t bin_offset = 0; - uint32_t render_offset = bin_offset + args->bin_cl_size; - uint32_t shader_rec_offset = roundup(render_offset + - args->render_cl_size, 16); + uint32_t shader_rec_offset = roundup(bin_offset + args->bin_cl_size, + 16); uint32_t uniforms_offset = shader_rec_offset + args->shader_rec_size; uint32_t exec_size = uniforms_offset + args->uniforms_size; uint32_t temp_size = exec_size + (sizeof(struct vc4_shader_state) * args->shader_rec_count); struct vc4_bo *bo; - if (shader_rec_offset < render_offset || - uniforms_offset < shader_rec_offset || + if (uniforms_offset < shader_rec_offset || exec_size < uniforms_offset || args->shader_rec_count >= (UINT_MAX / sizeof(struct vc4_shader_state)) || @@ -369,7 +367,6 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) goto fail; } bin = temp + bin_offset; - render = temp + render_offset; exec->shader_rec_u = temp + shader_rec_offset; exec->uniforms_u = temp + uniforms_offset; exec->shader_state = temp + exec_size; @@ -383,14 +380,6 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) goto fail; } - ret = copy_from_user(render, - (void __user *)(uintptr_t)args->render_cl, - args->render_cl_size); - if (ret) { - DRM_ERROR("Failed to copy in render cl\n"); - goto fail; - } - ret = copy_from_user(exec->shader_rec_u, (void __user *)(uintptr_t)args->shader_rec, args->shader_rec_size); @@ -409,7 +398,7 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) bo = vc4_bo_create(dev, exec_size); if (!bo) { - DRM_ERROR("Couldn't allocate BO for exec\n"); + DRM_ERROR("Couldn't allocate BO for binning\n"); ret = PTR_ERR(exec->exec_bo); goto fail; } @@ -419,7 +408,6 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) &exec->unref_list); exec->ct0ca = exec->exec_bo->paddr + bin_offset; - exec->ct1ca = exec->exec_bo->paddr + render_offset; exec->shader_rec_v = exec->exec_bo->vaddr + shader_rec_offset; exec->shader_rec_p = exec->exec_bo->paddr + shader_rec_offset; @@ -429,23 +417,10 @@ vc4_cl_validate(struct drm_device *dev, struct vc4_exec_info *exec) exec->uniforms_p = exec->exec_bo->paddr + uniforms_offset; exec->uniforms_size = args->uniforms_size; - ret = vc4_validate_cl(dev, - exec->exec_bo->vaddr + bin_offset, - bin, - args->bin_cl_size, - true, - args->bin_cl_size != 0, - exec); - if (ret) - goto fail; - - ret = vc4_validate_cl(dev, - exec->exec_bo->vaddr + render_offset, - render, - args->render_cl_size, - false, - args->bin_cl_size != 0, - exec); + ret = vc4_validate_bin_cl(dev, + exec->exec_bo->vaddr + bin_offset, + bin, + exec); if (ret) goto fail; @@ -577,8 +552,10 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, struct vc4_exec_info *exec; int ret; - if (args->flags != 0) + if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) { + DRM_ERROR("Unknown flags: 0x%02x\n", args->flags); return -EINVAL; + } exec = kcalloc(1, sizeof(*exec), GFP_KERNEL); if (!exec) { @@ -595,7 +572,15 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data, if (ret) goto fail; - ret = vc4_cl_validate(dev, exec); + if (exec->args->bin_cl_size != 0) { + ret = vc4_get_bcl(dev, exec); + if (ret) + goto fail; + } else { + exec->ct0ca = exec->ct0ea = 0; + } + + ret = vc4_get_rcl(dev, exec); if (ret) goto fail; diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c new file mode 100644 index 000000000000..241adbfa84ca --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_render_cl.c @@ -0,0 +1,447 @@ +/* + * Copyright © 2014-2015 Broadcom + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * DOC: Render command list generation + * + * In the VC4 driver, render command list generation is performed by the + * kernel instead of userspace. We do this because validating a + * user-submitted command list is hard to get right and has high CPU overhead, + * while the number of valid configurations for render command lists is + * actually fairly low. + */ + +#include "uapi/drm/vc4_drm.h" +#include "vc4_drv.h" +#include "vc4_packet.h" + +struct vc4_rcl_setup { + struct drm_gem_cma_object *color_read; + struct drm_gem_cma_object *color_ms_write; + struct drm_gem_cma_object *zs_read; + struct drm_gem_cma_object *zs_write; + + struct drm_gem_cma_object *rcl; + u32 next_offset; +}; + +static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val) +{ + *(u8 *)(setup->rcl->vaddr + setup->next_offset) = val; + setup->next_offset += 1; +} + +static inline void rcl_u16(struct vc4_rcl_setup *setup, u16 val) +{ + *(u16 *)(setup->rcl->vaddr + setup->next_offset) = val; + setup->next_offset += 2; +} + +static inline void rcl_u32(struct vc4_rcl_setup *setup, u32 val) +{ + *(u32 *)(setup->rcl->vaddr + setup->next_offset) = val; + setup->next_offset += 4; +} + + +/* + * Emits a no-op STORE_TILE_BUFFER_GENERAL. + * + * If we emit a PACKET_TILE_COORDINATES, it must be followed by a store of + * some sort before another load is triggered. + */ +static void vc4_store_before_load(struct vc4_rcl_setup *setup) +{ + rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); + rcl_u16(setup, + VC4_SET_FIELD(VC4_LOADSTORE_TILE_BUFFER_NONE, + VC4_LOADSTORE_TILE_BUFFER_BUFFER) | + VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR | + VC4_STORE_TILE_BUFFER_DISABLE_ZS_CLEAR | + VC4_STORE_TILE_BUFFER_DISABLE_VG_MASK_CLEAR); + rcl_u32(setup, 0); /* no address, since we're in None mode */ +} + +/* + * Emits a PACKET_TILE_COORDINATES if one isn't already pending. + * + * The tile coordinates packet triggers a pending load if there is one, are + * used for clipping during rendering, and determine where loads/stores happen + * relative to their base address. + */ +static void vc4_tile_coordinates(struct vc4_rcl_setup *setup, + uint32_t x, uint32_t y) +{ + rcl_u8(setup, VC4_PACKET_TILE_COORDINATES); + rcl_u8(setup, x); + rcl_u8(setup, y); +} + +static void emit_tile(struct vc4_exec_info *exec, + struct vc4_rcl_setup *setup, + uint8_t x, uint8_t y, bool first, bool last) +{ + bool has_bin = exec->args->bin_cl_size != 0; + + /* Note that the load doesn't actually occur until the + * tile coords packet is processed, and only one load + * may be outstanding at a time. + */ + if (setup->color_read) { + rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); + rcl_u16(setup, exec->args->color_read.bits); + rcl_u32(setup, + setup->color_read->paddr + + exec->args->color_read.offset); + } + + if (setup->zs_read) { + if (setup->color_read) { + /* Exec previous load. */ + vc4_tile_coordinates(setup, x, y); + vc4_store_before_load(setup); + } + + rcl_u8(setup, VC4_PACKET_LOAD_TILE_BUFFER_GENERAL); + rcl_u16(setup, exec->args->zs_read.bits); + rcl_u32(setup, + setup->zs_read->paddr + exec->args->zs_read.offset); + } + + /* Clipping depends on tile coordinates having been + * emitted, so we always need one here. + */ + vc4_tile_coordinates(setup, x, y); + + /* Wait for the binner before jumping to the first + * tile's lists. + */ + if (first && has_bin) + rcl_u8(setup, VC4_PACKET_WAIT_ON_SEMAPHORE); + + if (has_bin) { + rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST); + rcl_u32(setup, (exec->tile_alloc_bo->paddr + + (y * exec->bin_tiles_x + x) * 32)); + } + + if (setup->zs_write) { + rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); + rcl_u16(setup, exec->args->zs_write.bits | + (setup->color_ms_write ? + VC4_STORE_TILE_BUFFER_DISABLE_COLOR_CLEAR : 0)); + rcl_u32(setup, + (setup->zs_write->paddr + exec->args->zs_write.offset) | + ((last && !setup->color_ms_write) ? + VC4_LOADSTORE_TILE_BUFFER_EOF : 0)); + } + + if (setup->color_ms_write) { + if (setup->zs_write) { + /* Reset after previous store */ + vc4_tile_coordinates(setup, x, y); + } + + if (last) + rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF); + else + rcl_u8(setup, VC4_PACKET_STORE_MS_TILE_BUFFER); + } +} + +static int vc4_create_rcl_bo(struct drm_device *dev, struct vc4_exec_info *exec, + struct vc4_rcl_setup *setup) +{ + bool has_bin = exec->args->bin_cl_size != 0; + uint8_t min_x_tile = exec->args->min_x_tile; + uint8_t min_y_tile = exec->args->min_y_tile; + uint8_t max_x_tile = exec->args->max_x_tile; + uint8_t max_y_tile = exec->args->max_y_tile; + uint8_t xtiles = max_x_tile - min_x_tile + 1; + uint8_t ytiles = max_y_tile - min_y_tile + 1; + uint8_t x, y; + uint32_t size, loop_body_size; + + size = VC4_PACKET_TILE_RENDERING_MODE_CONFIG_SIZE; + loop_body_size = VC4_PACKET_TILE_COORDINATES_SIZE; + + if (exec->args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { + size += VC4_PACKET_CLEAR_COLORS_SIZE + + VC4_PACKET_TILE_COORDINATES_SIZE + + VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; + } + + if (setup->color_read) { + loop_body_size += (VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE); + } + if (setup->zs_read) { + if (setup->color_read) { + loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; + loop_body_size += VC4_PACKET_STORE_TILE_BUFFER_GENERAL_SIZE; + } + loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; + } + + if (has_bin) { + size += VC4_PACKET_WAIT_ON_SEMAPHORE_SIZE; + loop_body_size += VC4_PACKET_BRANCH_TO_SUB_LIST_SIZE; + } + + if (setup->zs_write) + loop_body_size += VC4_PACKET_LOAD_TILE_BUFFER_GENERAL_SIZE; + if (setup->color_ms_write) { + if (setup->zs_write) + loop_body_size += VC4_PACKET_TILE_COORDINATES_SIZE; + loop_body_size += VC4_PACKET_STORE_MS_TILE_BUFFER_SIZE; + } + size += xtiles * ytiles * loop_body_size; + + setup->rcl = &vc4_bo_create(dev, size)->base; + if (!setup->rcl) + return -ENOMEM; + list_add_tail(&to_vc4_bo(&setup->rcl->base)->unref_head, + &exec->unref_list); + + rcl_u8(setup, VC4_PACKET_TILE_RENDERING_MODE_CONFIG); + rcl_u32(setup, + (setup->color_ms_write ? + (setup->color_ms_write->paddr + + exec->args->color_ms_write.offset) : + 0)); + rcl_u16(setup, exec->args->width); + rcl_u16(setup, exec->args->height); + rcl_u16(setup, exec->args->color_ms_write.bits); + + /* The tile buffer gets cleared when the previous tile is stored. If + * the clear values changed between frames, then the tile buffer has + * stale clear values in it, so we have to do a store in None mode (no + * writes) so that we trigger the tile buffer clear. + */ + if (exec->args->flags & VC4_SUBMIT_CL_USE_CLEAR_COLOR) { + rcl_u8(setup, VC4_PACKET_CLEAR_COLORS); + rcl_u32(setup, exec->args->clear_color[0]); + rcl_u32(setup, exec->args->clear_color[1]); + rcl_u32(setup, exec->args->clear_z); + rcl_u8(setup, exec->args->clear_s); + + vc4_tile_coordinates(setup, 0, 0); + + rcl_u8(setup, VC4_PACKET_STORE_TILE_BUFFER_GENERAL); + rcl_u16(setup, VC4_LOADSTORE_TILE_BUFFER_NONE); + rcl_u32(setup, 0); /* no address, since we're in None mode */ + } + + for (y = min_y_tile; y <= max_y_tile; y++) { + for (x = min_x_tile; x <= max_x_tile; x++) { + bool first = (x == min_x_tile && y == min_y_tile); + bool last = (x == max_x_tile && y == max_y_tile); + emit_tile(exec, setup, x, y, first, last); + } + } + + BUG_ON(setup->next_offset != size); + exec->ct1ca = setup->rcl->paddr; + exec->ct1ea = setup->rcl->paddr + setup->next_offset; + + return 0; +} + +static int vc4_rcl_surface_setup(struct vc4_exec_info *exec, + struct drm_gem_cma_object **obj, + struct drm_vc4_submit_rcl_surface *surf) +{ + uint8_t tiling = VC4_GET_FIELD(surf->bits, + VC4_LOADSTORE_TILE_BUFFER_TILING); + uint8_t buffer = VC4_GET_FIELD(surf->bits, + VC4_LOADSTORE_TILE_BUFFER_BUFFER); + uint8_t format = VC4_GET_FIELD(surf->bits, + VC4_LOADSTORE_TILE_BUFFER_FORMAT); + int cpp; + + if (surf->pad != 0) { + DRM_ERROR("Padding unset\n"); + return -EINVAL; + } + + if (surf->hindex == ~0) + return 0; + + if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) + return -EINVAL; + + if (surf->bits & ~(VC4_LOADSTORE_TILE_BUFFER_TILING_MASK | + VC4_LOADSTORE_TILE_BUFFER_BUFFER_MASK | + VC4_LOADSTORE_TILE_BUFFER_FORMAT_MASK)) { + DRM_ERROR("Unknown bits in load/store: 0x%04x\n", + surf->bits); + return -EINVAL; + } + + if (tiling > VC4_TILING_FORMAT_LT) { + DRM_ERROR("Bad tiling format\n"); + return -EINVAL; + } + + if (buffer == VC4_LOADSTORE_TILE_BUFFER_ZS) { + if (format != 0) { + DRM_ERROR("No color format should be set for ZS\n"); + return -EINVAL; + } + cpp = 4; + } else if (buffer == VC4_LOADSTORE_TILE_BUFFER_COLOR) { + switch (format) { + case VC4_LOADSTORE_TILE_BUFFER_BGR565: + case VC4_LOADSTORE_TILE_BUFFER_BGR565_DITHER: + cpp = 2; + break; + case VC4_LOADSTORE_TILE_BUFFER_RGBA8888: + cpp = 4; + break; + default: + DRM_ERROR("Bad tile buffer format\n"); + return -EINVAL; + } + } else { + DRM_ERROR("Bad load/store buffer %d.\n", buffer); + return -EINVAL; + } + + if (surf->offset & 0xf) { + DRM_ERROR("load/store buffer must be 16b aligned.\n"); + return -EINVAL; + } + + if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, + exec->args->width, exec->args->height, cpp)) { + return -EINVAL; + } + + return 0; +} + +static int +vc4_rcl_ms_surface_setup(struct vc4_exec_info *exec, + struct drm_gem_cma_object **obj, + struct drm_vc4_submit_rcl_surface *surf) +{ + uint8_t tiling = VC4_GET_FIELD(surf->bits, + VC4_RENDER_CONFIG_MEMORY_FORMAT); + uint8_t format = VC4_GET_FIELD(surf->bits, + VC4_RENDER_CONFIG_FORMAT); + int cpp; + + if (surf->pad != 0) { + DRM_ERROR("Padding unset\n"); + return -EINVAL; + } + + if (surf->bits & ~(VC4_RENDER_CONFIG_MEMORY_FORMAT_MASK | + VC4_RENDER_CONFIG_FORMAT_MASK)) { + DRM_ERROR("Unknown bits in render config: 0x%04x\n", + surf->bits); + return -EINVAL; + } + + if (surf->hindex == ~0) + return 0; + + if (!vc4_use_bo(exec, surf->hindex, VC4_MODE_RENDER, obj)) + return -EINVAL; + + if (tiling > VC4_TILING_FORMAT_LT) { + DRM_ERROR("Bad tiling format\n"); + return -EINVAL; + } + + switch (format) { + case VC4_RENDER_CONFIG_FORMAT_BGR565_DITHERED: + case VC4_RENDER_CONFIG_FORMAT_BGR565: + cpp = 2; + break; + case VC4_RENDER_CONFIG_FORMAT_RGBA8888: + cpp = 4; + break; + default: + DRM_ERROR("Bad tile buffer format\n"); + return -EINVAL; + } + + if (!vc4_check_tex_size(exec, *obj, surf->offset, tiling, + exec->args->width, exec->args->height, cpp)) { + return -EINVAL; + } + + return 0; +} + +int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec) +{ + struct vc4_rcl_setup setup = {0}; + struct drm_vc4_submit_cl *args = exec->args; + bool has_bin = args->bin_cl_size != 0; + int ret; + + if (args->min_x_tile > args->max_x_tile || + args->min_y_tile > args->max_y_tile) { + DRM_ERROR("Bad render tile set (%d,%d)-(%d,%d)\n", + args->min_x_tile, args->min_y_tile, + args->max_x_tile, args->max_y_tile); + return -EINVAL; + } + + if (has_bin && + (args->max_x_tile > exec->bin_tiles_x || + args->max_y_tile > exec->bin_tiles_y)) { + DRM_ERROR("Render tiles (%d,%d) outside of bin config (%d,%d)\n", + args->max_x_tile, args->max_y_tile, + exec->bin_tiles_x, exec->bin_tiles_y); + return -EINVAL; + } + + ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read); + if (ret) + return ret; + + ret = vc4_rcl_ms_surface_setup(exec, &setup.color_ms_write, + &args->color_ms_write); + if (ret) + return ret; + + ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read); + if (ret) + return ret; + + ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write); + if (ret) + return ret; + + /* We shouldn't even have the job submitted to us if there's no + * surface to write out. + */ + if (!setup.color_ms_write && !setup.zs_write) { + DRM_ERROR("RCL requires color or Z/S write\n"); + return -EINVAL; + } + + return vc4_create_rcl_bo(dev, exec, &setup); +} diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index abd37867e4d2..2a825f0d0aad 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -39,6 +39,7 @@ * is where GEM relocation processing happens. */ +#include "uapi/drm/vc4_drm.h" #include "vc4_drv.h" #include "vc4_packet.h" @@ -94,7 +95,7 @@ size_is_lt(uint32_t width, uint32_t height, int cpp) height <= 4 * utile_height(cpp)); } -static bool +bool vc4_use_bo(struct vc4_exec_info *exec, uint32_t hindex, enum vc4_bo_mode mode, @@ -147,10 +148,10 @@ gl_shader_rec_size(uint32_t pointer_bits) return 36 + attribute_count * 8; } -static bool -check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, - uint32_t offset, uint8_t tiling_format, - uint32_t width, uint32_t height, uint8_t cpp) +bool +vc4_check_tex_size(struct vc4_exec_info *exec, struct drm_gem_cma_object *fbo, + uint32_t offset, uint8_t tiling_format, + uint32_t width, uint32_t height, uint8_t cpp) { uint32_t aligned_width, aligned_height, stride, size; uint32_t utile_w = utile_width(cpp); @@ -247,118 +248,6 @@ validate_increment_semaphore(VALIDATE_ARGS) return 0; } -static int -validate_wait_on_semaphore(VALIDATE_ARGS) -{ - if (exec->found_wait_on_semaphore_packet) { - DRM_ERROR("Duplicate VC4_PACKET_WAIT_ON_SEMAPHORE\n"); - return -EINVAL; - } - exec->found_wait_on_semaphore_packet = true; - - if (!exec->found_increment_semaphore_packet) { - DRM_ERROR("VC4_PACKET_WAIT_ON_SEMAPHORE without " - "VC4_PACKET_INCREMENT_SEMAPHORE\n"); - return -EINVAL; - } - - return 0; -} - -static int -validate_branch_to_sublist(VALIDATE_ARGS) -{ - uint32_t offset; - - if (!exec->tile_alloc_bo) { - DRM_ERROR("VC4_PACKET_BRANCH_TO_SUB_LIST seen before " - "binner setup\n"); - return -EINVAL; - } - - if (!exec->found_wait_on_semaphore_packet) { - DRM_ERROR("Jumping to tile alloc before binning finished.\n"); - return -EINVAL; - } - - offset = *(uint32_t *)(untrusted + 0); - if (offset & exec->tile_alloc_init_block_mask || - offset > exec->tile_alloc_init_block_last) { - DRM_ERROR("VC4_PACKET_BRANCH_TO_SUB_LIST must jump to initial " - "tile allocation space.\n"); - return -EINVAL; - } - - *(uint32_t *)(validated + 0) = exec->tile_alloc_bo->paddr + offset; - - return 0; -} - -/** - * validate_loadstore_tile_buffer_general() - Validation for - * VC4_PACKET_LOAD_TILE_BUFFER_GENERAL and - * VC4_PACKET_STORE_TILE_BUFFER_GENERAL. - * - * The two packets are nearly the same, except for the TLB-clearing management - * bits not being present for loads. Additionally, while stores are executed - * immediately (using the current tile coordinates), loads are queued to be - * executed when the tile coordinates packet occurs. - * - * Note that coordinates packets are validated to be within the declared - * bin_x/y, which themselves are verified to match the rendering-configuration - * FB width and height (which the hardware uses to clip loads and stores). - */ -static int -validate_loadstore_tile_buffer_general(VALIDATE_ARGS) -{ - uint16_t packet_b01 = *(uint16_t *)(untrusted + 0); - struct drm_gem_cma_object *fbo; - uint32_t buffer_type = VC4_GET_FIELD(packet_b01, - VC4_LOADSTORE_TILE_BUFFER_BUFFER); - uint32_t untrusted_address, offset, cpp; - - switch (buffer_type) { - case VC4_LOADSTORE_TILE_BUFFER_NONE: - return 0; - case VC4_LOADSTORE_TILE_BUFFER_COLOR: - if (VC4_GET_FIELD(packet_b01, - VC4_LOADSTORE_TILE_BUFFER_FORMAT) == - VC4_LOADSTORE_TILE_BUFFER_RGBA8888) { - cpp = 4; - } else { - cpp = 2; - } - break; - - case VC4_LOADSTORE_TILE_BUFFER_Z: - case VC4_LOADSTORE_TILE_BUFFER_ZS: - cpp = 4; - break; - - default: - DRM_ERROR("Load/store type %d unsupported\n", buffer_type); - return -EINVAL; - } - - if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo)) - return -EINVAL; - - untrusted_address = *(uint32_t *)(untrusted + 2); - offset = untrusted_address & ~0xf; - - if (!check_tex_size(exec, fbo, offset, - VC4_GET_FIELD(packet_b01, - VC4_LOADSTORE_TILE_BUFFER_TILING), - exec->fb_width, exec->fb_height, cpp)) { - return -EINVAL; - } - - *(uint32_t *)(validated + 2) = (offset + fbo->paddr + - (untrusted_address & 0xf)); - - return 0; -} - static int validate_indexed_prim_list(VALIDATE_ARGS) { @@ -552,9 +441,6 @@ validate_tile_binning_config(VALIDATE_ARGS) tile_allocation_size); return -EINVAL; } - exec->tile_alloc_init_block_mask = tile_alloc_init_block_size - 1; - exec->tile_alloc_init_block_last = tile_alloc_init_block_size * - (exec->bin_tiles_x * exec->bin_tiles_y - 1); if (*(uint32_t *)(untrusted + 8) != 0) { DRM_ERROR("TSDA offset != 0 unsupported\n"); @@ -571,60 +457,6 @@ validate_tile_binning_config(VALIDATE_ARGS) return 0; } -static int -validate_tile_rendering_mode_config(VALIDATE_ARGS) -{ - struct drm_gem_cma_object *fbo; - uint32_t flags, offset, cpp; - - if (exec->found_tile_rendering_mode_config_packet) { - DRM_ERROR("Duplicate VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n"); - return -EINVAL; - } - exec->found_tile_rendering_mode_config_packet = true; - - if (!vc4_use_handle(exec, 0, VC4_MODE_RENDER, &fbo)) - return -EINVAL; - - exec->fb_width = *(uint16_t *)(untrusted + 4); - exec->fb_height = *(uint16_t *)(untrusted + 6); - - flags = *(uint16_t *)(untrusted + 8); - if (VC4_GET_FIELD(flags, VC4_RENDER_CONFIG_FORMAT) == - VC4_RENDER_CONFIG_FORMAT_RGBA8888) { - cpp = 4; - } else { - cpp = 2; - } - - offset = *(uint32_t *)untrusted; - if (!check_tex_size(exec, fbo, offset, - VC4_GET_FIELD(flags, - VC4_RENDER_CONFIG_MEMORY_FORMAT), - exec->fb_width, exec->fb_height, cpp)) { - return -EINVAL; - } - - *(uint32_t *)validated = fbo->paddr + offset; - - return 0; -} - -static int -validate_tile_coordinates(VALIDATE_ARGS) -{ - uint8_t tile_x = *(uint8_t *)(untrusted + 0); - uint8_t tile_y = *(uint8_t *)(untrusted + 1); - - if (tile_x * 64 >= exec->fb_width || tile_y * 64 >= exec->fb_height) { - DRM_ERROR("Tile coordinates %d,%d > render config %dx%d\n", - tile_x, tile_y, exec->fb_width, exec->fb_height); - return -EINVAL; - } - - return 0; -} - static int validate_gem_handles(VALIDATE_ARGS) { @@ -632,81 +464,60 @@ validate_gem_handles(VALIDATE_ARGS) return 0; } -#define VC4_DEFINE_PACKET(packet, bin, render, name, func) \ - [packet] = { bin, render, packet ## _SIZE, name, func } +#define VC4_DEFINE_PACKET(packet, name, func) \ + [packet] = { packet ## _SIZE, name, func } static const struct cmd_info { - bool bin; - bool render; uint16_t len; const char *name; int (*func)(struct vc4_exec_info *exec, void *validated, void *untrusted); } cmd_info[] = { - VC4_DEFINE_PACKET(VC4_PACKET_HALT, 1, 1, "halt", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_NOP, 1, 1, "nop", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, 1, 1, "flush", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, 1, 0, "flush all state", validate_flush_all), - VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, 1, 0, "start tile binning", validate_start_tile_binning), - VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, 1, 0, "increment semaphore", validate_increment_semaphore), - VC4_DEFINE_PACKET(VC4_PACKET_WAIT_ON_SEMAPHORE, 0, 1, "wait on semaphore", validate_wait_on_semaphore), - /* BRANCH_TO_SUB_LIST is actually supported in the binner as well, but - * we only use it from the render CL in order to jump into the tile - * allocation BO. - */ - VC4_DEFINE_PACKET(VC4_PACKET_BRANCH_TO_SUB_LIST, 0, 1, "branch to sublist", validate_branch_to_sublist), - VC4_DEFINE_PACKET(VC4_PACKET_STORE_MS_TILE_BUFFER, 0, 1, "store MS resolved tile color buffer", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF, 0, 1, "store MS resolved tile color buffer and EOF", NULL), - - VC4_DEFINE_PACKET(VC4_PACKET_STORE_TILE_BUFFER_GENERAL, 0, 1, "Store Tile Buffer General", validate_loadstore_tile_buffer_general), - VC4_DEFINE_PACKET(VC4_PACKET_LOAD_TILE_BUFFER_GENERAL, 0, 1, "Load Tile Buffer General", validate_loadstore_tile_buffer_general), + VC4_DEFINE_PACKET(VC4_PACKET_HALT, "halt", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_NOP, "nop", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_FLUSH, "flush", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_FLUSH_ALL, "flush all state", validate_flush_all), + VC4_DEFINE_PACKET(VC4_PACKET_START_TILE_BINNING, "start tile binning", validate_start_tile_binning), + VC4_DEFINE_PACKET(VC4_PACKET_INCREMENT_SEMAPHORE, "increment semaphore", validate_increment_semaphore), - VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, 1, 1, "Indexed Primitive List", validate_indexed_prim_list), + VC4_DEFINE_PACKET(VC4_PACKET_GL_INDEXED_PRIMITIVE, "Indexed Primitive List", validate_indexed_prim_list), - VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, 1, 1, "Vertex Array Primitives", validate_gl_array_primitive), + VC4_DEFINE_PACKET(VC4_PACKET_GL_ARRAY_PRIMITIVE, "Vertex Array Primitives", validate_gl_array_primitive), /* This is only used by clipped primitives (packets 48 and 49), which * we don't support parsing yet. */ - VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, 1, 1, "primitive list format", NULL), - - VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, 1, 1, "GL Shader State", validate_gl_shader_state), - VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, 1, 1, "NV Shader State", validate_nv_shader_state), - - VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, 1, 1, "configuration bits", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, 1, 1, "flat shade flags", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, 1, 1, "point size", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, 1, 1, "line width", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, 1, 1, "RHT X boundary", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, 1, 1, "Depth Offset", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, 1, 1, "Clip Window", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, 1, 1, "Viewport Offset", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, 1, 1, "Clipper XY Scaling", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_PRIMITIVE_LIST_FORMAT, "primitive list format", NULL), + + VC4_DEFINE_PACKET(VC4_PACKET_GL_SHADER_STATE, "GL Shader State", validate_gl_shader_state), + VC4_DEFINE_PACKET(VC4_PACKET_NV_SHADER_STATE, "NV Shader State", validate_nv_shader_state), + + VC4_DEFINE_PACKET(VC4_PACKET_CONFIGURATION_BITS, "configuration bits", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_FLAT_SHADE_FLAGS, "flat shade flags", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_POINT_SIZE, "point size", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_LINE_WIDTH, "line width", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_RHT_X_BOUNDARY, "RHT X boundary", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_DEPTH_OFFSET, "Depth Offset", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_CLIP_WINDOW, "Clip Window", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_VIEWPORT_OFFSET, "Viewport Offset", NULL), + VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_XY_SCALING, "Clipper XY Scaling", NULL), /* Note: The docs say this was also 105, but it was 106 in the * initial userland code drop. */ - VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, 1, 1, "Clipper Z Scale and Offset", NULL), - - VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, 1, 0, "tile binning configuration", validate_tile_binning_config), + VC4_DEFINE_PACKET(VC4_PACKET_CLIPPER_Z_SCALING, "Clipper Z Scale and Offset", NULL), - VC4_DEFINE_PACKET(VC4_PACKET_TILE_RENDERING_MODE_CONFIG, 0, 1, "tile rendering mode configuration", validate_tile_rendering_mode_config), + VC4_DEFINE_PACKET(VC4_PACKET_TILE_BINNING_MODE_CONFIG, "tile binning configuration", validate_tile_binning_config), - VC4_DEFINE_PACKET(VC4_PACKET_CLEAR_COLORS, 0, 1, "Clear Colors", NULL), - - VC4_DEFINE_PACKET(VC4_PACKET_TILE_COORDINATES, 0, 1, "Tile Coordinates", validate_tile_coordinates), - - VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, 1, 1, "GEM handles", validate_gem_handles), + VC4_DEFINE_PACKET(VC4_PACKET_GEM_HANDLES, "GEM handles", validate_gem_handles), }; int -vc4_validate_cl(struct drm_device *dev, - void *validated, - void *unvalidated, - uint32_t len, - bool is_bin, - bool has_bin, - struct vc4_exec_info *exec) +vc4_validate_bin_cl(struct drm_device *dev, + void *validated, + void *unvalidated, + struct vc4_exec_info *exec) { + uint32_t len = exec->args->bin_cl_size; uint32_t dst_offset = 0; uint32_t src_offset = 0; @@ -734,14 +545,6 @@ vc4_validate_cl(struct drm_device *dev, src_offset, cmd, info->name, info->len); #endif - if ((is_bin && !info->bin) || - (!is_bin && !info->render)) { - DRM_ERROR("0x%08x: packet %d (%s) invalid for %s\n", - src_offset, cmd, info->name, - is_bin ? "binner" : "render"); - return -EINVAL; - } - if (src_offset + info->len > len) { DRM_ERROR("0x%08x: packet %d (%s) length 0x%08x " "exceeds bounds (0x%08x)\n", @@ -772,30 +575,16 @@ vc4_validate_cl(struct drm_device *dev, break; } - if (is_bin) { - exec->ct0ea = exec->ct0ca + dst_offset; + exec->ct0ea = exec->ct0ca + dst_offset; - if (has_bin && !exec->found_start_tile_binning_packet) { - DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); - return -EINVAL; - } - } else { - if (!exec->found_tile_rendering_mode_config_packet) { - DRM_ERROR("Render CL missing VC4_PACKET_TILE_RENDERING_MODE_CONFIG\n"); - return -EINVAL; - } + if (!exec->found_start_tile_binning_packet) { + DRM_ERROR("Bin CL missing VC4_PACKET_START_TILE_BINNING\n"); + return -EINVAL; + } - /* Make sure that they actually consumed the semaphore - * increment from the bin CL. Otherwise a later submit would - * have render execute immediately. - */ - if (exec->found_wait_on_semaphore_packet != has_bin) { - DRM_ERROR("Render CL %s VC4_PACKET_WAIT_ON_SEMAPHORE\n", - exec->found_wait_on_semaphore_packet ? - "has" : "missing"); - return -EINVAL; - } - exec->ct1ea = exec->ct1ca + dst_offset; + if (!exec->found_increment_semaphore_packet) { + DRM_ERROR("Bin CL missing VC4_PACKET_INCREMENT_SEMAPHORE\n"); + return -EINVAL; } return 0; @@ -910,8 +699,8 @@ reloc_tex(struct vc4_exec_info *exec, tiling_format = VC4_TILING_FORMAT_T; } - if (!check_tex_size(exec, tex, offset + cube_map_stride * 5, - tiling_format, width, height, cpp)) { + if (!vc4_check_tex_size(exec, tex, offset + cube_map_stride * 5, + tiling_format, width, height, cpp)) { return false; } diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index 9dd54403a5d8..4d55307c5107 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -38,6 +38,15 @@ #define DRM_IOCTL_VC4_CREATE_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_CREATE_BO, struct drm_vc4_create_bo) #define DRM_IOCTL_VC4_MMAP_BO DRM_IOWR( DRM_COMMAND_BASE + DRM_VC4_MMAP_BO, struct drm_vc4_mmap_bo) +struct drm_vc4_submit_rcl_surface { + uint32_t hindex; /* Handle index, or ~0 if not present. */ + uint32_t offset; /* Offset to start of buffer. */ + /* + * Bits for either render config (color_ms_write) or load/store packet. + */ + uint16_t bits; + uint16_t pad; +}; /** * struct drm_vc4_submit_cl - ioctl argument for submitting commands to the 3D @@ -62,16 +71,6 @@ struct drm_vc4_submit_cl { */ uint64_t bin_cl; - /* Pointer to the render command list. - * - * The render command list contains a set of packets to load the - * current tile's state (reading from memory, or just clearing it) - * into the GPU, then call into the tile allocation BO to run the - * stored rendering for that tile, then store the tile's state back to - * memory. - */ - uint64_t render_cl; - /* Pointer to the shader records. * * Shader records are the structures read by the hardware that contain @@ -102,8 +101,6 @@ struct drm_vc4_submit_cl { /* Size in bytes of the binner command list. */ uint32_t bin_cl_size; - /* Size in bytes of the render command list */ - uint32_t render_cl_size; /* Size in bytes of the set of shader records. */ uint32_t shader_rec_size; /* Number of shader records. @@ -119,8 +116,25 @@ struct drm_vc4_submit_cl { /* Number of BO handles passed in (size is that times 4). */ uint32_t bo_handle_count; + /* RCL setup: */ + uint16_t width; + uint16_t height; + uint8_t min_x_tile; + uint8_t min_y_tile; + uint8_t max_x_tile; + uint8_t max_y_tile; + struct drm_vc4_submit_rcl_surface color_read; + struct drm_vc4_submit_rcl_surface color_ms_write; + struct drm_vc4_submit_rcl_surface zs_read; + struct drm_vc4_submit_rcl_surface zs_write; + uint32_t clear_color[2]; + uint32_t clear_z; + uint8_t clear_s; + + uint32_t pad:24; + +#define VC4_SUBMIT_CL_USE_CLEAR_COLOR (1 << 0) uint32_t flags; - uint32_t pad; /* Returned value of the seqno of this render job (for the * wait ioctl). -- cgit v1.2.1 From 56d1015c668327beefaf8142b0f83960437052c7 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 17 Jun 2015 14:52:39 -0700 Subject: drm/vc4: Make the kernel allocate the tile state/alloc buffers. This fixes a security hole of letting userspace map and modify the buffers, keeps each userspace client from needing to hang on to a set of them, reduces kernel validation overhead, and improves stability (possibly due to reducing the severity of an addressing issue in the hardware's tile state buffer). Signed-off-by: Eric Anholt --- drivers/gpu/drm/vc4/vc4_drv.h | 5 +- drivers/gpu/drm/vc4/vc4_packet.h | 22 +++++---- drivers/gpu/drm/vc4/vc4_render_cl.c | 3 +- drivers/gpu/drm/vc4/vc4_validate.c | 99 ++++++++++++++++++------------------- 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index e4e2e081628c..9fdddf823114 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -165,8 +165,6 @@ to_vc4_plane(struct drm_plane *plane) enum vc4_bo_mode { VC4_MODE_UNDECIDED, - VC4_MODE_TILE_ALLOC, - VC4_MODE_TSDA, VC4_MODE_RENDER, VC4_MODE_SHADER, }; @@ -231,7 +229,8 @@ struct vc4_exec_info { bool found_start_tile_binning_packet; bool found_increment_semaphore_packet; uint8_t bin_tiles_x, bin_tiles_y; - struct drm_gem_cma_object *tile_alloc_bo; + struct drm_gem_cma_object *tile_bo; + uint32_t tile_alloc_offset; /** * Computed addresses pointing into exec_bo where we start the diff --git a/drivers/gpu/drm/vc4/vc4_packet.h b/drivers/gpu/drm/vc4/vc4_packet.h index ec8586aca71f..1eb23416311d 100644 --- a/drivers/gpu/drm/vc4/vc4_packet.h +++ b/drivers/gpu/drm/vc4/vc4_packet.h @@ -223,15 +223,19 @@ enum vc4_packet { /** @{ bits in the last u8 of VC4_PACKET_TILE_BINNING_MODE_CONFIG */ #define VC4_BIN_CONFIG_DB_NON_MS (1 << 7) -#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_32 (0 << 5) -#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_64 (1 << 5) -#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128 (2 << 5) -#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_256 (3 << 5) - -#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32 (0 << 3) -#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_64 (1 << 3) -#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 (2 << 3) -#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 (3 << 3) +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK VC4_MASK(6, 5) +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_SHIFT 5 +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_32 0 +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_64 1 +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128 2 +#define VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_256 3 + +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK VC4_MASK(4, 3) +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_SHIFT 3 +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32 0 +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_64 1 +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_128 2 +#define VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_256 3 #define VC4_BIN_CONFIG_AUTO_INIT_TSDA (1 << 2) #define VC4_BIN_CONFIG_TILE_BUFFER_64BIT (1 << 1) diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c index 241adbfa84ca..973ffa20ffb6 100644 --- a/drivers/gpu/drm/vc4/vc4_render_cl.c +++ b/drivers/gpu/drm/vc4/vc4_render_cl.c @@ -141,7 +141,8 @@ static void emit_tile(struct vc4_exec_info *exec, if (has_bin) { rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST); - rcl_u32(setup, (exec->tile_alloc_bo->paddr + + rcl_u32(setup, (exec->tile_bo->paddr + + exec->tile_alloc_offset + (y * exec->bin_tiles_x + x) * 32)); } diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 2a825f0d0aad..9eaa0f9b53f2 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -376,15 +376,10 @@ validate_nv_shader_state(VALIDATE_ARGS) static int validate_tile_binning_config(VALIDATE_ARGS) { - struct drm_gem_cma_object *tile_allocation; - struct drm_gem_cma_object *tile_state_data_array; + struct drm_device *dev = exec->exec_bo->base.dev; uint8_t flags; - uint32_t tile_allocation_size; - uint32_t tile_alloc_init_block_size; - - if (!vc4_use_handle(exec, 0, VC4_MODE_TILE_ALLOC, &tile_allocation) || - !vc4_use_handle(exec, 1, VC4_MODE_TSDA, &tile_state_data_array)) - return -EINVAL; + uint32_t tile_state_size, tile_alloc_size; + uint32_t tile_count; if (exec->found_tile_binning_mode_config_packet) { DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n"); @@ -394,6 +389,7 @@ validate_tile_binning_config(VALIDATE_ARGS) exec->bin_tiles_x = *(uint8_t *)(untrusted + 12); exec->bin_tiles_y = *(uint8_t *)(untrusted + 13); + tile_count = exec->bin_tiles_x * exec->bin_tiles_y; flags = *(uint8_t *)(untrusted + 14); if (exec->bin_tiles_x == 0 || @@ -403,15 +399,6 @@ validate_tile_binning_config(VALIDATE_ARGS) return -EINVAL; } - /* Our validation relies on the user not getting to set up their own - * tile state/tile allocation BO contents. - */ - if (!(flags & VC4_BIN_CONFIG_AUTO_INIT_TSDA)) { - DRM_ERROR("binning config missing " - "VC4_BIN_CONFIG_AUTO_INIT_TSDA\n"); - return -EINVAL; - } - if (flags & (VC4_BIN_CONFIG_DB_NON_MS | VC4_BIN_CONFIG_TILE_BUFFER_64BIT | VC4_BIN_CONFIG_MS_MODE_4X)) { @@ -419,40 +406,52 @@ validate_tile_binning_config(VALIDATE_ARGS) return -EINVAL; } - if (*(uint32_t *)(untrusted + 0) != 0) { - DRM_ERROR("tile allocation offset != 0 unsupported\n"); - return -EINVAL; - } - tile_allocation_size = *(uint32_t *)(untrusted + 4); - if (tile_allocation_size > tile_allocation->base.size) { - DRM_ERROR("tile allocation size %d > BO size %d\n", - tile_allocation_size, tile_allocation->base.size); - return -EINVAL; - } - *(uint32_t *)validated = tile_allocation->paddr; - exec->tile_alloc_bo = tile_allocation; - - tile_alloc_init_block_size = 1 << (5 + ((flags >> 5) & 3)); - if (exec->bin_tiles_x * exec->bin_tiles_y * - tile_alloc_init_block_size > tile_allocation_size) { - DRM_ERROR("tile init exceeds tile alloc size (%d vs %d)\n", - exec->bin_tiles_x * exec->bin_tiles_y * - tile_alloc_init_block_size, - tile_allocation_size); - return -EINVAL; - } + /* The tile state data array is 48 bytes per tile, and we put it at + * the start of a BO containing both it and the tile alloc. + */ + tile_state_size = 48 * tile_count; + + /* Since the tile alloc array will follow us, align. */ + exec->tile_alloc_offset = roundup(tile_state_size, 4096); + + *(uint8_t *)(validated + 14) = + ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK | + VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_MASK)) | + VC4_BIN_CONFIG_AUTO_INIT_TSDA | + VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_32, + VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE) | + VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128, + VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE)); + + /* Initial block size. */ + tile_alloc_size = 32 * tile_count; + + /* + * The initial allocation gets rounded to the next 256 bytes before + * the hardware starts fulfilling further allocations. + */ + tile_alloc_size = roundup(tile_alloc_size, 256); - if (*(uint32_t *)(untrusted + 8) != 0) { - DRM_ERROR("TSDA offset != 0 unsupported\n"); - return -EINVAL; - } - if (exec->bin_tiles_x * exec->bin_tiles_y * 48 > - tile_state_data_array->base.size) { - DRM_ERROR("TSDA of %db too small for %dx%d bin config\n", - tile_state_data_array->base.size, - exec->bin_tiles_x, exec->bin_tiles_y); - } - *(uint32_t *)(validated + 8) = tile_state_data_array->paddr; + /* Add space for the extra allocations. This is what gets used first, + * before overflow memory. It must have at least 4096 bytes, but we + * want to avoid overflow memory usage if possible. + */ + tile_alloc_size += 1024 * 1024; + + exec->tile_bo = &vc4_bo_create(dev, exec->tile_alloc_offset + + tile_alloc_size)->base; + if (!exec->tile_bo) + return -ENOMEM; + list_add_tail(&to_vc4_bo(&exec->tile_bo->base)->unref_head, + &exec->unref_list); + + /* tile alloc address. */ + *(uint32_t *)(validated + 0) = (exec->tile_bo->paddr + + exec->tile_alloc_offset); + /* tile alloc size. */ + *(uint32_t *)(validated + 4) = tile_alloc_size; + /* tile state address. */ + *(uint32_t *)(validated + 8) = exec->tile_bo->paddr; return 0; } -- cgit v1.2.1