Linux-Aspeed Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] misc: aspeed: Add Aspeed UART routing control driver.
From: Oskar Senft @ 2018-09-04 20:20 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20180904202018.198707-1-osk@google.com>

This driver adds sysfs files that allow the BMC userspace to configure
how UARTs and physical serial I/O ports are routed.

Tested: Checked correct behavior (both read & write) on TYAN S7106
board by manually changing routing settings and confirming that bits
flow as expected. Tested for UART1 and UART3 as this board doesn't have
the other UARTs wired up in a testable way.

Signed-off-by: Oskar Senft <osk@google.com>
---
 .../stable/sysfs-driver-aspeed-uart-routing   |  14 +
 .../misc-devices/aspeed-uart-routing.txt      |  49 +++
 drivers/misc/Kconfig                          |  10 +
 drivers/misc/Makefile                         |   1 +
 drivers/misc/aspeed-uart-routing.c            | 384 ++++++++++++++++++
 5 files changed, 458 insertions(+)
 create mode 100644 Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
 create mode 100644 Documentation/misc-devices/aspeed-uart-routing.txt
 create mode 100644 drivers/misc/aspeed-uart-routing.c

diff --git a/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
new file mode 100644
index 000000000000..5068737d9c12
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-driver-aspeed-uart-routing
@@ -0,0 +1,14 @@
+What:		/sys/bus/platform/drivers/aspeed-uart-routing/*/io*
+Date:		August 2018
+Contact:	Oskar Senft <osk@google.com>
+Description:	Configures the input source for the specific physical
+		serial I/O port.
+Users:		OpenBMC.  Proposed changes should be mailed to
+		openbmc at lists.ozlabs.org
+
+What:		/sys/bus/platform/drivers/aspeed-uart-routing/*/uart*
+Date:		August 2018
+Contact:	Oskar Senft <osk@google.com>
+Description:	Configures the input source for the specific UART.
+Users:		OpenBMC.  Proposed changes should be mailed to
+		openbmc at lists.ozlabs.org
diff --git a/Documentation/misc-devices/aspeed-uart-routing.txt b/Documentation/misc-devices/aspeed-uart-routing.txt
new file mode 100644
index 000000000000..afaf17cb7eda
--- /dev/null
+++ b/Documentation/misc-devices/aspeed-uart-routing.txt
@@ -0,0 +1,49 @@
+Kernel driver aspeed-uart-routing
+=================================
+
+Supported chips:
+ASPEED AST2500
+
+Author:
+Google LLC
+
+Description
+-----------
+
+The Aspeed AST2500 allows to dynamically route the inputs for the built-in
+UARTS and physical serial I/O ports.
+
+This allows, for example, to connect the output of UART to another UART.
+This can be used to enable host<->BMC communication via UARTs, e.g. to allow
+access to the host's serial console.
+
+This driver is for the BMC side. The sysfs files allow the BMC userspace
+which owns the system configuration policy, to configure how UARTs and
+physical serial I/O ports are routed.
+
+The driver provides the following files in sysfs:
+uart1		Configure the input signal to UART1.
+uart2		Configure the input signal to UART2.
+uart3		Configure the input signal to UART3.
+uart4		Configure the input signal to UART4.
+uart5		Configure the input signal to UART5.
+io1		Configure the input signal to physical serial port 1.
+io2		Configure the input signal to physical serial port 2.
+io3		Configure the input signal to physical serial port 3.
+io4		Configure the input signal to physical serial port 4.
+io5		Configure the input signal to physical serial port 5.
+
+When read, each file shows the list of available options with the currently
+selected option marked by square brackets "[]". The list of available options
+depends on the selected file.
+
+Example:
+$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
+[io1] io2 io3 io4 uart2 uart3 uart4 io6
+
+In this case, UART1 gets its input signal from IO1 (physical serial port 1).
+
+$ echo -n "uart3" \
+  >/sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
+$ cat /sys/bus/platform/drivers/aspeed-uart-routing/*.uart_routing/uart1
+io1 io2 io3 io4 uart2 [uart3] uart4 io6
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3726eacdf65d..89d42cd6b790 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -501,6 +501,16 @@ config ASPEED_LPC_SNOOP
 	  allows the BMC to listen on and save the data written by
 	  the host to an arbitrary LPC I/O port.
 
+config ASPEED_UART_ROUTING
+	tristate "Aspeed ast2500 UART routing control"
+	depends on (ARCH_ASPEED || COMPILE_TEST)
+	help
+	  Provides a driver to configure UART routing on Aspeed BMC platforms
+	  at runtime through sysfs. This allows, for example, to connect the
+	  output of UART to another UART. This can be used to enable host<->BMC
+	  communication via UARTs, e.g. to allow access to the host's serial
+	  console.
+
 config PCI_ENDPOINT_TEST
 	depends on PCI
 	select CRC32
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index af22bbc3d00c..9705182b317f 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG)	+= vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_ASPEED_LPC_CTRL)	+= aspeed-lpc-ctrl.o
 obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
+obj-$(CONFIG_ASPEED_UART_ROUTING)	+= aspeed-uart-routing.o
 obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
 obj-$(CONFIG_OCXL)		+= ocxl/
 obj-$(CONFIG_MISC_RTSX)		+= cardreader/
diff --git a/drivers/misc/aspeed-uart-routing.c b/drivers/misc/aspeed-uart-routing.c
new file mode 100644
index 000000000000..381bac19e4af
--- /dev/null
+++ b/drivers/misc/aspeed-uart-routing.c
@@ -0,0 +1,384 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UART Routing driver for Aspeed AST2500
+ *
+ * Copyright (c) 2018 Google LLC
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+
+/* The Aspeed AST2500 allows to dynamically route the inputs for the built-in
+ * UARTS and physical serial I/O ports.
+ *
+ * This allows, for example, to connect the output of UART to another UART.
+ * This can be used to enable host<->BMC communication via UARTs, e.g. to allow
+ * access to the host's serial console.
+ *
+ * This driver is for the BMC side. The sysfs files allow the BMC userspace
+ * which owns the system configuration policy, to configure how UARTs and
+ * physical serial I/O ports are routed.
+ */
+
+#define ASPEED_HICRA_IO1	"io1"
+#define ASPEED_HICRA_IO2	"io2"
+#define ASPEED_HICRA_IO3	"io3"
+#define ASPEED_HICRA_IO4	"io4"
+#define ASPEED_HICRA_IO5	"io5"
+#define ASPEED_HICRA_IO6	"io6"
+#define ASPEED_HICRA_UART1	"uart1"
+#define ASPEED_HICRA_UART2	"uart2"
+#define ASPEED_HICRA_UART3	"uart3"
+#define ASPEED_HICRA_UART4	"uart4"
+#define ASPEED_HICRA_UART5	"uart5"
+
+struct aspeed_uart_routing {
+	struct device		*dev;
+	void __iomem		*regs;
+	spinlock_t		lock;
+};
+
+struct aspeed_uart_routing_selector {
+	struct device_attribute	dev_attr;
+	int				shift;
+	int				mask;
+	const char * const options[];
+};
+
+#define to_routing_selector(_dev_attr)					\
+	container_of(_dev_attr, struct aspeed_uart_routing_selector, dev_attr)
+
+
+static ssize_t aspeed_uart_routing_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t aspeed_uart_routing_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+#define ROUTING_ATTR(_name) {						\
+	.attr = {.name = _name,					\
+		 .mode = VERIFY_OCTAL_PERMISSIONS(0644) },		\
+	.show = aspeed_uart_routing_show,				\
+	.store = aspeed_uart_routing_store,				\
+}
+
+static struct aspeed_uart_routing_selector uart5_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART5),
+	.shift = 28,
+	.mask = 0xf,
+	.options = {
+		    ASPEED_HICRA_IO5,   // 0
+		    ASPEED_HICRA_IO1,   // 1
+		    ASPEED_HICRA_IO2,   // 2
+		    ASPEED_HICRA_IO3,   // 3
+		    ASPEED_HICRA_IO4,   // 4
+		    ASPEED_HICRA_UART1, // 5
+		    ASPEED_HICRA_UART2, // 6
+		    ASPEED_HICRA_UART3, // 7
+		    ASPEED_HICRA_UART4, // 8
+		    ASPEED_HICRA_IO6,   // 9
+		    NULL,               // NULL termination
+		    },
+};
+
+static struct aspeed_uart_routing_selector uart4_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART4),
+	.shift = 25,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_IO4,   // 0
+		    ASPEED_HICRA_IO1,   // 1
+		    ASPEED_HICRA_IO2,   // 2
+		    ASPEED_HICRA_IO3,   // 3
+		    ASPEED_HICRA_UART1, // 4
+		    ASPEED_HICRA_UART2, // 5
+		    ASPEED_HICRA_UART3, // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+	},
+};
+
+static struct aspeed_uart_routing_selector uart3_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART3),
+	.shift = 22,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_IO3,   // 0
+		    ASPEED_HICRA_IO4,   // 1
+		    ASPEED_HICRA_IO1,   // 2
+		    ASPEED_HICRA_IO2,   // 3
+		    ASPEED_HICRA_UART4, // 4
+		    ASPEED_HICRA_UART1, // 5
+		    ASPEED_HICRA_UART2, // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+		    },
+};
+
+static struct aspeed_uart_routing_selector uart2_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART2),
+	.shift = 19,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_IO2,   // 0
+		    ASPEED_HICRA_IO3,   // 1
+		    ASPEED_HICRA_IO4,   // 2
+		    ASPEED_HICRA_IO1,   // 3
+		    ASPEED_HICRA_UART3, // 4
+		    ASPEED_HICRA_UART4, // 5
+		    ASPEED_HICRA_UART1, // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+		    },
+};
+
+static struct aspeed_uart_routing_selector uart1_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_UART1),
+	.shift = 16,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_IO1,   // 0
+		    ASPEED_HICRA_IO2,   // 1
+		    ASPEED_HICRA_IO3,   // 2
+		    ASPEED_HICRA_IO4,   // 3
+		    ASPEED_HICRA_UART2, // 4
+		    ASPEED_HICRA_UART3, // 5
+		    ASPEED_HICRA_UART4, // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+		    },
+};
+
+static struct aspeed_uart_routing_selector io5_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO5),
+	.shift = 12,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_UART5, // 0
+		    ASPEED_HICRA_UART1, // 1
+		    ASPEED_HICRA_UART2, // 2
+		    ASPEED_HICRA_UART3, // 3
+		    ASPEED_HICRA_UART4, // 4
+		    ASPEED_HICRA_IO1,   // 5
+		    ASPEED_HICRA_IO3,   // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+		    },
+};
+
+static struct aspeed_uart_routing_selector io4_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO4),
+	.shift = 9,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_UART4, // 0
+		    ASPEED_HICRA_UART5, // 1
+		    ASPEED_HICRA_UART1, // 2
+		    ASPEED_HICRA_UART2, // 3
+		    ASPEED_HICRA_UART3, // 4
+		    ASPEED_HICRA_IO1,   // 5
+		    ASPEED_HICRA_IO2,   // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+		    },
+};
+
+static struct aspeed_uart_routing_selector io3_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO3),
+	.shift = 6,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_UART3, // 0
+		    ASPEED_HICRA_UART4, // 1
+		    ASPEED_HICRA_UART5, // 2
+		    ASPEED_HICRA_UART1, // 3
+		    ASPEED_HICRA_UART2, // 4
+		    ASPEED_HICRA_IO1,   // 5
+		    ASPEED_HICRA_IO2,   // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+		    },
+};
+
+static struct aspeed_uart_routing_selector io2_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO2),
+	.shift = 3,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_UART2, // 0
+		    ASPEED_HICRA_UART3, // 1
+		    ASPEED_HICRA_UART4, // 2
+		    ASPEED_HICRA_UART5, // 3
+		    ASPEED_HICRA_UART1, // 4
+		    ASPEED_HICRA_IO3,   // 5
+		    ASPEED_HICRA_IO4,   // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+		    },
+};
+
+static struct aspeed_uart_routing_selector io1_sel = {
+	.dev_attr = ROUTING_ATTR(ASPEED_HICRA_IO1),
+	.shift = 0,
+	.mask = 0x7,
+	.options = {
+		    ASPEED_HICRA_UART1, // 0
+		    ASPEED_HICRA_UART2, // 1
+		    ASPEED_HICRA_UART3, // 2
+		    ASPEED_HICRA_UART4, // 3
+		    ASPEED_HICRA_UART5, // 4
+		    ASPEED_HICRA_IO3,   // 5
+		    ASPEED_HICRA_IO4,   // 6
+		    ASPEED_HICRA_IO6,   // 7
+		    NULL,               // NULL termination
+		    },
+};
+
+
+static struct attribute *aspeed_uart_routing_attrs[] = {
+	&uart1_sel.dev_attr.attr,
+	&uart2_sel.dev_attr.attr,
+	&uart3_sel.dev_attr.attr,
+	&uart4_sel.dev_attr.attr,
+	&uart5_sel.dev_attr.attr,
+	&io1_sel.dev_attr.attr,
+	&io2_sel.dev_attr.attr,
+	&io3_sel.dev_attr.attr,
+	&io4_sel.dev_attr.attr,
+	&io5_sel.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group aspeed_uart_routing_attr_group = {
+	.attrs = aspeed_uart_routing_attrs,
+};
+
+static ssize_t aspeed_uart_routing_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
+	struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
+	int val, pos, len;
+
+	val = (readl(uart_routing->regs) >> sel->shift) & sel->mask;
+
+	len = 0;
+	for (pos = 0; sel->options[pos] != NULL; ++pos) {
+		if (pos == val) {
+			len += snprintf(buf + len, PAGE_SIZE - 1 - len,
+					"[%s] ", sel->options[pos]);
+		} else {
+			len += snprintf(buf + len, PAGE_SIZE - 1 - len,
+					"%s ", sel->options[pos]);
+		}
+	}
+
+	if (val >= pos) {
+		len += snprintf(buf + len, PAGE_SIZE - 1 - len,
+				"[unknown(%d)]", val);
+	}
+
+	len += snprintf(buf + len, PAGE_SIZE - 1 - len, "\n");
+
+	return len;
+}
+
+static ssize_t aspeed_uart_routing_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct aspeed_uart_routing *uart_routing = dev_get_drvdata(dev);
+	struct aspeed_uart_routing_selector *sel = to_routing_selector(attr);
+	int val;
+	u32 reg;
+
+	val = match_string(sel->options, -1, buf);
+	if (val < 0) {
+		dev_err(dev, "invalid value \"%s\"\n", buf);
+		return -EINVAL;
+	}
+
+	spin_lock(&uart_routing->lock);
+	reg = readl(uart_routing->regs);
+	// Zero out existing value in specified bits.
+	reg &= ~(sel->mask << sel->shift);
+	// Set new value in specified bits.
+	reg |= (val & sel->mask) << sel->shift;
+	writel(reg, uart_routing->regs);
+	spin_unlock(&uart_routing->lock);
+
+	return count;
+}
+
+static int aspeed_uart_routing_probe(struct platform_device *pdev)
+{
+	struct aspeed_uart_routing *uart_routing;
+	struct resource *res;
+	int rc;
+
+	uart_routing = devm_kzalloc(&pdev->dev,
+				    sizeof(*uart_routing),
+				    GFP_KERNEL);
+	if (!uart_routing)
+		return -ENOMEM;
+
+	spin_lock_init(&uart_routing->lock);
+	uart_routing->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	uart_routing->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(uart_routing->regs))
+		return PTR_ERR(uart_routing->regs);
+
+	rc = sysfs_create_group(&uart_routing->dev->kobj,
+				&aspeed_uart_routing_attr_group);
+	if (rc < 0)
+		return rc;
+
+	platform_set_drvdata(pdev, uart_routing);
+
+	return 0;
+}
+
+static int aspeed_uart_routing_remove(struct platform_device *pdev)
+{
+	struct aspeed_uart_routing *uart_routing = platform_get_drvdata(pdev);
+
+	sysfs_remove_group(&uart_routing->dev->kobj,
+			   &aspeed_uart_routing_attr_group);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_uart_routing_table[] = {
+	{ .compatible = "aspeed,ast2500-uart-routing" },
+	{ },
+};
+
+static struct platform_driver aspeed_uart_routing_driver = {
+	.driver = {
+		.name = "aspeed-uart-routing",
+		.of_match_table = aspeed_uart_routing_table,
+	},
+	.probe = aspeed_uart_routing_probe,
+	.remove = aspeed_uart_routing_remove,
+};
+
+module_platform_driver(aspeed_uart_routing_driver);
+
+MODULE_AUTHOR("Oskar Senft <osk@google.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Driver to configure Aspeed UART routing");
-- 
2.19.0.rc1.350.ge57e33dbd1-goog


^ permalink raw reply related

* Aspeed UART routing driver
From: Oskar Senft @ 2018-09-04 20:20 UTC (permalink / raw)
  To: linux-aspeed

Hi

As discussed with Andrew, we have a use case that requires the UART
routing on the AST2500 to be configured at runtime.

After talking with Linus Walleij, we concluded that the pinmux driver is
not a good match for this use case.

I went ahead and implemented a "UART routing control driver" for the
AST2500. It may just work on the AST2400 as is, but I don't have a
system to test on.

Thanks
Oskar.



^ permalink raw reply

* [PATCH] arm: dts: aspeed: quanta-q71l: Enable adc & ibt nodes
From: Patrick Venture @ 2018-09-04 15:53 UTC (permalink / raw)
  To: linux-aspeed

ADC node is disabled by default in 4.13 tree onward and
needs to be enabled explicitly in separate device trees.

ibt(ipmi-bt-host) node is disabled by default in 4.13 tree onward and
needs to be enabled explicitly in separate device trees.

Signed-off-by: Patrick Venture <venture@google.com>
---
 arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
index c655564e6c6d..385c0f4b69ee 100644
--- a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
@@ -112,6 +112,10 @@
 			&pinctrl_ddcclk_default &pinctrl_ddcdat_default>;
 };
 
+&ibt {
+	status = "okay";
+};
+
 &lpc_snoop {
 	status = "okay";
 	snoop-ports = <0x80>;
@@ -384,6 +388,10 @@
 	status = "okay";
 };
 
+&adc {
+	status = "okay";
+};
+
 &pwm_tacho {
 	status = "okay";
 
-- 
2.19.0.rc1.350.ge57e33dbd1-goog


^ permalink raw reply related

* [PATCH 1/4] clock: aspeed: Add VIDEO reset index definition
From: Rob Herring @ 2018-09-04 13:36 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-2-git-send-email-eajames@linux.vnet.ibm.com>

On Wed, 29 Aug 2018 16:09:30 -0500, Eddie James wrote:
> Add an index into the array of resets kept in the Aspeed clock driver.
> This isn't a HW bit definition.
> 
> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
> ---
>  include/dt-bindings/clock/aspeed-clock.h | 1 +
>  1 file changed, 1 insertion(+)
> 

Reviewed-by: Rob Herring <robh@kernel.org>


^ permalink raw reply

* [PATCH 3/4] dt-bindings: media: Add Aspeed Video Engine binding documentation
From: Rob Herring @ 2018-09-04  0:50 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-4-git-send-email-eajames@linux.vnet.ibm.com>

On Wed, Aug 29, 2018 at 04:09:32PM -0500, Eddie James wrote:
> Document the bindings.
> 
> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
> ---
>  .../devicetree/bindings/media/aspeed-video.txt     | 23 ++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt b/Documentation/devicetree/bindings/media/aspeed-video.txt
> new file mode 100644
> index 0000000..58c056a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/aspeed-video.txt
> @@ -0,0 +1,23 @@
> +* Device tree bindings for Aspeed Video Engine
> +
> +The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can
> +capture and compress video data from digital or analog sources.
> +
> +Required properties:
> + - compatible:		"aspeed,ast2400-video" or "aspeed,ast2500-video"

Maybe "video-engine" or "ve" would be more specific than just "video". 
Use what is closest to the name of the block.

> + - reg:			contains the offset and length of the VE memory region
> + - clocks:		pointers to the the "vclk" and "eclk" of the syscon

No pointers in DT. "Clock specifier"

> + - clock-names:		"vclk-gate", "eclk-gate"

Is that the name at or in the module? The fact that it is gated is 
really outside the block and a property of the clock controller.

> + - resets:		pointer to the VE reset of the syscon
> + - interrupts:		the interrupt associated with the VE on this platform
> +
> +Example:
> +
> +video: video at 1e700000 {

video-codec at ...

> +    compatible = "aspeed,ast2500-video";
> +    reg = <0x1e700000 0x20000>;
> +    clocks = <&syscon ASPEED_CLK_GATE_VCLK>, <&syscon ASPEED_CLK_GATE_ECLK>;
> +    clock-names = "vclk-gate", "eclk-gate";
> +    resets = <&syscon ASPEED_RESET_VIDEO>;
> +    interrupts = <7>;
> +};
> -- 
> 1.8.3.1
> 


^ permalink raw reply

* [PATCH 0/4] media: platform: Add Aspeed Video Engine driver
From: Hans Verkuil @ 2018-09-03 11:57 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-1-git-send-email-eajames@linux.vnet.ibm.com>

Hi Eddie,

Thank you for your work on this. Interesting to see support for this SoC :-)

On 08/29/2018 11:09 PM, Eddie James wrote:
> The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
> can capture and compress video data from digital or analog sources. With
> the Aspeed chip acting as a service processor, the Video Engine can
> capture the host processor graphics output.
> 
> This series adds a V4L2 driver for the VE, providing a read() interface
> only. The driver triggers the hardware to capture the host graphics output
> and compress it to JPEG format.
> 
> Testing on an AST2500 determined that the videobuf/streaming/mmap interface
> was significantly slower than the simple read() interface, so I have not
> included the streaming part.

Do you know why? It should be equal or faster, not slower.

I reviewed about half of the driver, but then I stopped since there were too
many things missing.

First of all, you need to test your driver with v4l2-compliance (available here:
https://git.linuxtv.org/v4l-utils.git/). Always compile from the git repo since
the versions from distros tend to be too old.

Just run 'v4l2-compliance -d /dev/videoX' and fix all issues. Then run
'v4l2-compliance -s -d /dev/videoX' to test streaming.

This utility checks if the driver follows the V4L2 API correctly, implements
all ioctls that it should and fills in all the fields that it should.

Please add the output of 'v4l2-compliance -s' to future versions of this patch
series: I don't accept V4L2 drivers without a clean report of this utility.

If you have any questions, then mail me or (usually quicker) ask on the #v4l
freenode irc channel (I'm in the CET timezone).

One thing that needs more explanation: from what I could tell from the driver
the VIDIOC_G_FMT ioctl returns the detected format instead of the current
format. This is wrong. Instead you should implement the VIDIOC_*_DV_TIMINGS
ioctls and the V4L2_EVENT_SOURCE_CHANGE event.

The normal sequence is that userspace queries the current timings with
VIDIOC_QUERY_DV_TIMINGS, if it finds valid timings, then it sets these
timings with _S_DV_TIMINGS. Now it can call G/S_FMT. If the timings
change, then the driver should detect that and send a V4L2_EVENT_SOURCE_CHANGE
event.

When the application receives this event it can take action, such as
increasing the size of the buffer for the jpeg data that it reads into.

The reason for this sequence of events is that you can't just change the
format/resolution mid-stream without giving userspace the chance to
reconfigure.

Regards,

	Hans

> 
> It's also possible to use an automatic mode for the VE such that
> re-triggering the HW every frame isn't necessary. However this wasn't
> reliable on the AST2400, and probably used more CPU anyway due to excessive
> interrupts. It was approximately 15% faster.
> 
> The series also adds the necessary parent clock definitions to the Aspeed
> clock driver, with both a mux and clock divider.
> 
> Eddie James (4):
>   clock: aspeed: Add VIDEO reset index definition
>   clock: aspeed: Setup video engine clocking
>   dt-bindings: media: Add Aspeed Video Engine binding documentation
>   media: platform: Add Aspeed Video Engine driver
> 
>  .../devicetree/bindings/media/aspeed-video.txt     |   23 +
>  drivers/clk/clk-aspeed.c                           |   41 +-
>  drivers/media/platform/Kconfig                     |    8 +
>  drivers/media/platform/Makefile                    |    1 +
>  drivers/media/platform/aspeed-video.c              | 1307 ++++++++++++++++++++
>  include/dt-bindings/clock/aspeed-clock.h           |    1 +
>  6 files changed, 1379 insertions(+), 2 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt
>  create mode 100644 drivers/media/platform/aspeed-video.c
> 


^ permalink raw reply

* [PATCH 4/4] media: platform: Add Aspeed Video Engine driver
From: Hans Verkuil @ 2018-09-03 11:40 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-5-git-send-email-eajames@linux.vnet.ibm.com>

On 08/29/2018 11:09 PM, Eddie James wrote:
> The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
> can capture and compress video data from digital or analog sources. With
> the Aspeed chip acting a service processor, the Video Engine can capture
> the host processor graphics output.
> 
> Add a V4L2 driver to capture video data and compress it to JPEG images,
> making the data available through a standard read interface.
> 
> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
> ---
>  drivers/media/platform/Kconfig        |    8 +
>  drivers/media/platform/Makefile       |    1 +
>  drivers/media/platform/aspeed-video.c | 1307 +++++++++++++++++++++++++++++++++

Missing MAINTAINERS file update.

>  3 files changed, 1316 insertions(+)
>  create mode 100644 drivers/media/platform/aspeed-video.c
> 
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 94c1fe0..e599245 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -32,6 +32,14 @@ source "drivers/media/platform/davinci/Kconfig"
>  
>  source "drivers/media/platform/omap/Kconfig"
>  
> +config VIDEO_ASPEED
> +	tristate "Aspeed AST2400 and AST2500 Video Engine driver"
> +	depends on VIDEO_V4L2
> +	help
> +	  Support for the Aspeed Video Engine (VE) embedded in the Aspeed
> +	  AST2400 and AST2500 SOCs. The VE can capture and compress video data
> +	  from digital or analog sources.
> +
>  config VIDEO_SH_VOU
>  	tristate "SuperH VOU video output driver"
>  	depends on MEDIA_CAMERA_SUPPORT
> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
> index 41322ab..205c33a 100644
> --- a/drivers/media/platform/Makefile
> +++ b/drivers/media/platform/Makefile
> @@ -3,6 +3,7 @@
>  # Makefile for the video capture/playback device drivers.
>  #
>  
> +obj-$(CONFIG_VIDEO_ASPEED)		+= aspeed-video.o
>  obj-$(CONFIG_VIDEO_CADENCE)		+= cadence/
>  obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
>  obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
> diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
> new file mode 100644
> index 0000000..4f36a3f
> --- /dev/null
> +++ b/drivers/media/platform/aspeed-video.c
> @@ -0,0 +1,1307 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * ASPEED Video Engine Driver
> + *
> + * Copyright 2018 IBM Corp.
> + *
> + * Eddie James <eajames@linux.vnet.ibm.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/atomic.h>
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/sched.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +#include <linux/videodev2.h>
> +#include <linux/wait.h>
> +#include <linux/workqueue.h>
> +#include <media/v4l2-dev.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-ioctl.h>
> +
> +#define DEVICE_NAME			"aspeed-video"
> +
> +#define ASPEED_VIDEO_JPEG_NUM_QUALITIES	12
> +#define ASPEED_VIDEO_JPEG_HEADER_SIZE	10
> +#define ASPEED_VIDEO_JPEG_QUANT_SIZE	116
> +#define ASPEED_VIDEO_JPEG_DCT_SIZE	34
> +
> +#define NUM_POLARITY_CHECKS		10
> +#define INVALID_RESOLUTION_RETRIES	1
> +#define INVALID_RESOLUTION_DELAY	msecs_to_jiffies(250)
> +#define RESOLUTION_CHANGE_DELAY		msecs_to_jiffies(500)
> +#define MODE_DETECT_TIMEOUT		msecs_to_jiffies(500)
> +#define DIRECT_FETCH_THRESHOLD		0x0c0000 /* 1024 * 768, 32bpp */
> +
> +#define VE_SRC_BUFFER_SIZE		0x900000 /* 1920 * 1200, 32bpp */
> +#define VE_COMP_BUFFER_SIZE		0x100000 /* 128K packet * 8 packets */
> +#define VE_JPEG_BUFFER_SIZE		0x006000 /* 512 * 12 * 4 */
> +
> +#define VE_PROTECTION_KEY		0x000
> +#define  VE_PROTECTION_KEY_UNLOCK	0x1A038AA8
> +
> +#define VE_SEQ_CTRL			0x004
> +#define  VE_SEQ_CTRL_TRIG_MODE_DET	BIT(0)
> +#define  VE_SEQ_CTRL_TRIG_CAPTURE	BIT(1)
> +#define  VE_SEQ_CTRL_FORCE_IDLE		BIT(2)
> +#define  VE_SEQ_CTRL_MULT_FRAME		BIT(3)
> +#define  VE_SEQ_CTRL_TRIG_COMP		BIT(4)
> +#define  VE_SEQ_CTRL_AUTO_COMP		BIT(5)
> +#define  VE_SEQ_CTRL_EN_WATCHDOG	BIT(7)
> +#define  VE_SEQ_CTRL_YUV420		BIT(10)
> +#define  VE_SEQ_CTRL_COMP_FMT		GENMASK(11, 10)
> +#define  VE_SEQ_CTRL_HALT		BIT(12)
> +#define  VE_SEQ_CTRL_EN_WATCHDOG_COMP	BIT(14)
> +#define  VE_SEQ_CTRL_TRIG_JPG		BIT(15)
> +#define  VE_SEQ_CTRL_CAP_BUSY		BIT(16)
> +#define  VE_SEQ_CTRL_COMP_BUSY		BIT(18)
> +
> +#ifdef CONFIG_MACH_ASPEED_G5
> +#define  VE_SEQ_CTRL_JPEG_MODE		BIT(13)	/* AST2500 */
> +#else
> +#define  VE_SEQ_CTRL_JPEG_MODE		BIT(8)	/* AST2400 */
> +#endif /* CONFIG_MACH_ASPEED_G5 */
> +
> +#define VE_CTRL				0x008
> +#define  VE_CTRL_HSYNC_POL		BIT(0)
> +#define  VE_CTRL_VSYNC_POL		BIT(1)
> +#define  VE_CTRL_SOURCE			BIT(2)
> +#define  VE_CTRL_INT_DE			BIT(4)
> +#define  VE_CTRL_DIRECT_FETCH		BIT(5)
> +#define  VE_CTRL_YUV			BIT(6)
> +#define  VE_CTRL_RGB			BIT(7)
> +#define  VE_CTRL_CAPTURE_FMT		GENMASK(7, 6)
> +#define  VE_CTRL_AUTO_OR_CURSOR		BIT(8)
> +#define  VE_CTRL_CLK_INVERSE		BIT(11)
> +#define  VE_CTRL_CLK_DELAY		GENMASK(11, 9)
> +#define  VE_CTRL_INTERLACE		BIT(14)
> +#define  VE_CTRL_HSYNC_POL_CTRL		BIT(15)
> +#define  VE_CTRL_FRC			GENMASK(23, 16)
> +
> +#define VE_TGS_0			0x00c
> +#define VE_TGS_1			0x010
> +#define  VE_TGS_FIRST			GENMASK(28, 16)
> +#define  VE_TGS_LAST			GENMASK(12, 0)
> +
> +#define VE_SCALING_FACTOR		0x014
> +#define VE_SCALING_FILTER0		0x018
> +#define VE_SCALING_FILTER1		0x01c
> +#define VE_SCALING_FILTER2		0x020
> +#define VE_SCALING_FILTER3		0x024
> +
> +#define VE_CAP_WINDOW			0x030
> +#define VE_COMP_WINDOW			0x034
> +#define VE_COMP_PROC_OFFSET		0x038
> +#define VE_COMP_OFFSET			0x03c
> +#define VE_JPEG_ADDR			0x040
> +#define VE_SRC0_ADDR			0x044
> +#define VE_SRC_SCANLINE_OFFSET		0x048
> +#define VE_SRC1_ADDR			0x04c
> +#define VE_COMP_ADDR			0x054
> +
> +#define VE_STREAM_BUF_SIZE		0x058
> +#define  VE_STREAM_BUF_SIZE_N_PACKETS	GENMASK(5, 3)
> +#define  VE_STREAM_BUF_SIZE_P_SIZE	GENMASK(2, 0)
> +
> +#define VE_COMP_CTRL			0x060
> +#define  VE_COMP_CTRL_VQ_DCT_ONLY	BIT(0)
> +#define  VE_COMP_CTRL_VQ_4COLOR		BIT(1)
> +#define  VE_COMP_CTRL_QUANTIZE		BIT(2)
> +#define  VE_COMP_CTRL_EN_BQ		BIT(4)
> +#define  VE_COMP_CTRL_EN_CRYPTO		BIT(5)
> +#define  VE_COMP_CTRL_DCT_CHR		GENMASK(10, 6)
> +#define  VE_COMP_CTRL_DCT_LUM		GENMASK(15, 11)
> +#define  VE_COMP_CTRL_EN_HQ		BIT(16)
> +#define  VE_COMP_CTRL_RSVD		BIT(19)
> +#define  VE_COMP_CTRL_ENCODE		GENMASK(21, 20)
> +#define  VE_COMP_CTRL_HQ_DCT_CHR	GENMASK(26, 22)
> +#define  VE_COMP_CTRL_HQ_DCT_LUM	GENMASK(31, 27)
> +
> +#define VE_OFFSET_COMP_STREAM		0x078
> +
> +#define VE_SRC_LR_EDGE_DET		0x090
> +#define  VE_SRC_LR_EDGE_DET_LEFT	GENMASK(11, 0)
> +#define  VE_SRC_LR_EDGE_DET_NO_V	BIT(12)
> +#define  VE_SRC_LR_EDGE_DET_NO_H	BIT(13)
> +#define  VE_SRC_LR_EDGE_DET_NO_DISP	BIT(14)
> +#define  VE_SRC_LR_EDGE_DET_NO_CLK	BIT(15)
> +#define  VE_SRC_LR_EDGE_DET_RT_SHF	16
> +#define  VE_SRC_LR_EDGE_DET_RT		GENMASK(27, VE_SRC_LR_EDGE_DET_RT_SHF)
> +#define  VE_SRC_LR_EDGE_DET_INTERLACE	BIT(31)
> +
> +#define VE_SRC_TB_EDGE_DET		0x094
> +#define  VE_SRC_TB_EDGE_DET_TOP		GENMASK(12, 0)
> +#define  VE_SRC_TB_EDGE_DET_BOT_SHF	16
> +#define  VE_SRC_TB_EDGE_DET_BOT		GENMASK(28, VE_SRC_TB_EDGE_DET_BOT_SHF)
> +
> +#define VE_MODE_DETECT_STATUS		0x098
> +#define  VE_MODE_DETECT_STATUS_VSYNC	BIT(28)
> +#define  VE_MODE_DETECT_STATUS_HSYNC	BIT(29)
> +
> +#define VE_INTERRUPT_CTRL		0x304
> +#define VE_INTERRUPT_STATUS		0x308
> +#define  VE_INTERRUPT_MODE_DETECT_WD	BIT(0)
> +#define  VE_INTERRUPT_CAPTURE_COMPLETE	BIT(1)
> +#define  VE_INTERRUPT_COMP_READY	BIT(2)
> +#define  VE_INTERRUPT_COMP_COMPLETE	BIT(3)
> +#define  VE_INTERRUPT_MODE_DETECT	BIT(4)
> +#define  VE_INTERRUPT_FRAME_COMPLETE	BIT(5)
> +#define  VE_INTERRUPT_DECODE_ERR	BIT(6)
> +#define  VE_INTERRUPT_HALT_READY	BIT(8)
> +#define  VE_INTERRUPT_HANG_WD		BIT(9)
> +#define  VE_INTERRUPT_STREAM_DESC	BIT(10)
> +#define  VE_INTERRUPT_VSYNC_DESC	BIT(11)
> +
> +#define VE_MODE_DETECT			0x30c
> +#define VE_MEM_RESTRICT_START		0x310
> +#define VE_MEM_RESTRICT_END		0x314
> +
> +enum {
> +	VIDEO_MODE_DETECT_DONE,
> +	VIDEO_RES_CHANGE,
> +	VIDEO_FRAME_AVAILABLE,
> +	VIDEO_FRAME_TRIGGERED,
> +};
> +
> +struct aspeed_video_addr {
> +	dma_addr_t dma;
> +	void *virt;
> +};
> +
> +struct aspeed_video {
> +	void __iomem *base;
> +	struct clk *eclk;
> +	struct clk *vclk;
> +	struct reset_control *rst;
> +
> +	struct device *dev;
> +	struct v4l2_device v4l2_dev;
> +	struct video_device vdev;
> +	struct mutex video_lock;
> +
> +	atomic_t clients;
> +	wait_queue_head_t wait;
> +	struct delayed_work res_work;
> +	unsigned long flags;
> +
> +	int frame_idx;
> +	u32 frame_size;
> +
> +	dma_addr_t max;
> +	dma_addr_t min;
> +	struct aspeed_video_addr srcs[2];
> +	struct aspeed_video_addr comp[2];
> +	struct aspeed_video_addr jpeg;
> +
> +	int frame_rate;
> +	int jpeg_quality;
> +	struct v4l2_pix_format fmt;
> +};
> +
> +#define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev)
> +
> +static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = {
> +	0xE0FFD8FF, 0x464A1000, 0x01004649, 0x60000101, 0x00006000, 0x0F00FEFF,
> +	0x00002D05, 0x00000000, 0x00000000, 0x00DBFF00
> +};
> +
> +static const u32 aspeed_video_jpeg_quant[ASPEED_VIDEO_JPEG_QUANT_SIZE] = {
> +	0x081100C0, 0x00000000, 0x00110103, 0x03011102, 0xC4FF0111, 0x00001F00,
> +	0x01010501, 0x01010101, 0x00000000, 0x00000000, 0x04030201, 0x08070605,
> +	0xFF0B0A09, 0x10B500C4, 0x03010200, 0x03040203, 0x04040505, 0x7D010000,
> +	0x00030201, 0x12051104, 0x06413121, 0x07615113, 0x32147122, 0x08A19181,
> +	0xC1B14223, 0xF0D15215, 0x72623324, 0x160A0982, 0x1A191817, 0x28272625,
> +	0x35342A29, 0x39383736, 0x4544433A, 0x49484746, 0x5554534A, 0x59585756,
> +	0x6564635A, 0x69686766, 0x7574736A, 0x79787776, 0x8584837A, 0x89888786,
> +	0x9493928A, 0x98979695, 0xA3A29A99, 0xA7A6A5A4, 0xB2AAA9A8, 0xB6B5B4B3,
> +	0xBAB9B8B7, 0xC5C4C3C2, 0xC9C8C7C6, 0xD4D3D2CA, 0xD8D7D6D5, 0xE2E1DAD9,
> +	0xE6E5E4E3, 0xEAE9E8E7, 0xF4F3F2F1, 0xF8F7F6F5, 0xC4FFFAF9, 0x00011F00,
> +	0x01010103, 0x01010101, 0x00000101, 0x00000000, 0x04030201, 0x08070605,
> +	0xFF0B0A09, 0x11B500C4, 0x02010200, 0x04030404, 0x04040507, 0x77020100,
> +	0x03020100, 0x21050411, 0x41120631, 0x71610751, 0x81322213, 0x91421408,
> +	0x09C1B1A1, 0xF0523323, 0xD1726215, 0x3424160A, 0x17F125E1, 0x261A1918,
> +	0x2A292827, 0x38373635, 0x44433A39, 0x48474645, 0x54534A49, 0x58575655,
> +	0x64635A59, 0x68676665, 0x74736A69, 0x78777675, 0x83827A79, 0x87868584,
> +	0x928A8988, 0x96959493, 0x9A999897, 0xA5A4A3A2, 0xA9A8A7A6, 0xB4B3B2AA,
> +	0xB8B7B6B5, 0xC3C2BAB9, 0xC7C6C5C4, 0xD2CAC9C8, 0xD6D5D4D3, 0xDAD9D8D7,
> +	0xE5E4E3E2, 0xE9E8E7E6, 0xF4F3F2EA, 0xF8F7F6F5, 0xDAFFFAF9, 0x01030C00,
> +	0x03110200, 0x003F0011
> +};
> +
> +static const u32 aspeed_video_jpeg_dct[ASPEED_VIDEO_JPEG_NUM_QUALITIES]
> +				      [ASPEED_VIDEO_JPEG_DCT_SIZE] = {
> +	{ 0x0D140043, 0x0C0F110F, 0x11101114, 0x17141516, 0x1E20321E,
> +	  0x3D1E1B1B, 0x32242E2B, 0x4B4C3F48, 0x44463F47, 0x61735A50,
> +	  0x566C5550, 0x88644644, 0x7A766C65, 0x4D808280, 0x8C978D60,
> +	  0x7E73967D, 0xDBFF7B80, 0x1F014300, 0x272D2121, 0x3030582D,
> +	  0x697BB958, 0xB8B9B97B, 0xB9B8A6A6, 0xB9B9B9B9, 0xB9B9B9B9,
> +	  0xB9B9B9B9, 0xB9B9B9B9, 0xB9B9B9B9, 0xB9B9B9B9, 0xB9B9B9B9,
> +	  0xB9B9B9B9, 0xB9B9B9B9, 0xB9B9B9B9, 0xFFB9B9B9 },
> +	{ 0x0C110043, 0x0A0D0F0D, 0x0F0E0F11, 0x14111213, 0x1A1C2B1A,
> +	  0x351A1818, 0x2B1F2826, 0x4142373F, 0x3C3D373E, 0x55644E46,
> +	  0x4B5F4A46, 0x77573D3C, 0x6B675F58, 0x43707170, 0x7A847B54,
> +	  0x6E64836D, 0xDBFF6C70, 0x1B014300, 0x22271D1D, 0x2A2A4C27,
> +	  0x5B6BA04C, 0xA0A0A06B, 0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0,
> +	  0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0,
> +	  0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0, 0xFFA0A0A0 },
> +	{ 0x090E0043, 0x090A0C0A, 0x0C0B0C0E, 0x110E0F10, 0x15172415,
> +	  0x2C151313, 0x241A211F, 0x36372E34, 0x31322E33, 0x4653413A,
> +	  0x3E4E3D3A, 0x62483231, 0x58564E49, 0x385D5E5D, 0x656D6645,
> +	  0x5B536C5A, 0xDBFF595D, 0x16014300, 0x1C201818, 0x22223F20,
> +	  0x4B58853F, 0x85858558, 0x85858585, 0x85858585, 0x85858585,
> +	  0x85858585, 0x85858585, 0x85858585, 0x85858585, 0x85858585,
> +	  0x85858585, 0x85858585, 0x85858585, 0xFF858585 },
> +	{ 0x070B0043, 0x07080A08, 0x0A090A0B, 0x0D0B0C0C, 0x11121C11,
> +	  0x23110F0F, 0x1C141A19, 0x2B2B2429, 0x27282428, 0x3842332E,
> +	  0x313E302E, 0x4E392827, 0x46443E3A, 0x2C4A4A4A, 0x50565137,
> +	  0x48425647, 0xDBFF474A, 0x12014300, 0x161A1313, 0x1C1C331A,
> +	  0x3D486C33, 0x6C6C6C48, 0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C,
> +	  0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C,
> +	  0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C, 0xFF6C6C6C },
> +	{ 0x06090043, 0x05060706, 0x07070709, 0x0A09090A, 0x0D0E160D,
> +	  0x1B0D0C0C, 0x16101413, 0x21221C20, 0x1E1F1C20, 0x2B332824,
> +	  0x26302624, 0x3D2D1F1E, 0x3735302D, 0x22393A39, 0x3F443F2B,
> +	  0x38334338, 0xDBFF3739, 0x0D014300, 0x11130E0E, 0x15152613,
> +	  0x2D355026, 0x50505035, 0x50505050, 0x50505050, 0x50505050,
> +	  0x50505050, 0x50505050, 0x50505050, 0x50505050, 0x50505050,
> +	  0x50505050, 0x50505050, 0x50505050, 0xFF505050 },
> +	{ 0x04060043, 0x03040504, 0x05040506, 0x07060606, 0x09090F09,
> +	  0x12090808, 0x0F0A0D0D, 0x16161315, 0x14151315, 0x1D221B18,
> +	  0x19201918, 0x281E1514, 0x2423201E, 0x17262726, 0x2A2D2A1C,
> +	  0x25222D25, 0xDBFF2526, 0x09014300, 0x0B0D0A0A, 0x0E0E1A0D,
> +	  0x1F25371A, 0x37373725, 0x37373737, 0x37373737, 0x37373737,
> +	  0x37373737, 0x37373737, 0x37373737, 0x37373737, 0x37373737,
> +	  0x37373737, 0x37373737, 0x37373737, 0xFF373737 },
> +	{ 0x02030043, 0x01020202, 0x02020203, 0x03030303, 0x04040704,
> +	  0x09040404, 0x07050606, 0x0B0B090A, 0x0A0A090A, 0x0E110D0C,
> +	  0x0C100C0C, 0x140F0A0A, 0x1211100F, 0x0B131313, 0x1516150E,
> +	  0x12111612, 0xDBFF1213, 0x04014300, 0x05060505, 0x07070D06,
> +	  0x0F121B0D, 0x1B1B1B12, 0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B,
> +	  0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B,
> +	  0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B, 0xFF1B1B1B },
> +	{ 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503,
> +	  0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090B0908,
> +	  0x080A0808, 0x0D0A0706, 0x0C0B0A0A, 0x070C0D0C, 0x0E0F0E09,
> +	  0x0C0B0F0C, 0xDBFF0C0C, 0x03014300, 0x03040303, 0x04040804,
> +	  0x0A0C1208, 0x1212120C, 0x12121212, 0x12121212, 0x12121212,
> +	  0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212,
> +	  0x12121212, 0x12121212, 0x12121212, 0xFF121212 },
> +	{ 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503,
> +	  0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090B0908,
> +	  0x080A0808, 0x0D0A0706, 0x0C0B0A0A, 0x070C0D0C, 0x0E0F0E09,
> +	  0x0C0B0F0C, 0xDBFF0C0C, 0x02014300, 0x03030202, 0x04040703,
> +	  0x080A0F07, 0x0F0F0F0A, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
> +	  0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
> +	  0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0xFF0F0F0F },
> +	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x02020302,
> +	  0x04020202, 0x03020303, 0x05050405, 0x05050405, 0x07080606,
> +	  0x06080606, 0x0A070505, 0x09080807, 0x05090909, 0x0A0B0A07,
> +	  0x09080B09, 0xDBFF0909, 0x02014300, 0x02030202, 0x03030503,
> +	  0x07080C05, 0x0C0C0C08, 0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C,
> +	  0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C,
> +	  0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C, 0xFF0C0C0C },
> +	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010201,
> +	  0x03010101, 0x02010202, 0x03030303, 0x03030303, 0x04050404,
> +	  0x04050404, 0x06050303, 0x06050505, 0x03060606, 0x07070704,
> +	  0x06050706, 0xDBFF0606, 0x01014300, 0x01020101, 0x02020402,
> +	  0x05060904, 0x09090906, 0x09090909, 0x09090909, 0x09090909,
> +	  0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909,
> +	  0x09090909, 0x09090909, 0x09090909, 0xFF090909 },
> +	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010101,
> +	  0x01010101, 0x01010101, 0x01010101, 0x01010101, 0x02020202,
> +	  0x02020202, 0x03020101, 0x03020202, 0x01030303, 0x03030302,
> +	  0x03020303, 0xDBFF0403, 0x01014300, 0x01010101, 0x01010201,
> +	  0x03040602, 0x06060604, 0x06060606, 0x06060606, 0x06060606,
> +	  0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606,
> +	  0x06060606, 0x06060606, 0x06060606, 0xFF060606 }
> +};
> +
> +static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420)
> +{
> +	int i;
> +	unsigned int base;
> +
> +	for (i = 0; i < ASPEED_VIDEO_JPEG_NUM_QUALITIES; i++) {
> +		int j;
> +
> +		base = 256 * i;	/* AST HW requires this header spacing */
> +
> +		for (j = 0; j < ASPEED_VIDEO_JPEG_HEADER_SIZE; j++)
> +			table[base + j] =
> +				le32_to_cpu(aspeed_video_jpeg_header[j]);

This doesn't look right. These tables are in cpu format to begin with,
so le32_to_cpu doesn't make sense.

BTW, what is the endianness of an aspeed SoC?

> +
> +		base += ASPEED_VIDEO_JPEG_HEADER_SIZE;
> +		for (j = 0; j < ASPEED_VIDEO_JPEG_DCT_SIZE; j++)
> +			table[base + j] =
> +				le32_to_cpu(aspeed_video_jpeg_dct[i][j]);
> +
> +		base += ASPEED_VIDEO_JPEG_DCT_SIZE;
> +		for (j = 0; j < ASPEED_VIDEO_JPEG_QUANT_SIZE; j++)
> +			table[base + j] =
> +				le32_to_cpu(aspeed_video_jpeg_quant[j]);
> +
> +		if (yuv420)
> +			table[base + 2] = le32_to_cpu(0x00220103);
> +	}
> +}
> +
> +static void aspeed_video_update(struct aspeed_video *video, u32 reg,
> +				unsigned long mask, u32 bits)
> +{
> +	u32 t = readl(video->base + reg);
> +	u32 before = t;
> +
> +	t &= mask;
> +	t |= bits;
> +	writel(t, video->base + reg);
> +	dev_dbg(video->dev, "update %03x[%08x -> %08x]\n", reg, before,
> +		readl(video->base + reg));
> +}
> +
> +static u32 aspeed_video_read(struct aspeed_video *video, u32 reg)
> +{
> +	u32 t = readl(video->base + reg);
> +
> +	dev_dbg(video->dev, "read %03x[%08x]\n", reg, t);
> +	return t;
> +}
> +
> +static void aspeed_video_write(struct aspeed_video *video, u32 reg, u32 val)
> +{
> +	writel(val, video->base + reg);
> +	dev_dbg(video->dev, "write %03x[%08x]\n", reg,
> +		readl(video->base + reg));
> +}
> +
> +static bool aspeed_video_engine_busy(struct aspeed_video *video)
> +{
> +	u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL);
> +
> +	if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) ||
> +	    !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) {
> +		dev_info(video->dev, "video engine busy\n");
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static int aspeed_video_start_frame(struct aspeed_video *video)
> +{
> +	if (aspeed_video_engine_busy(video))
> +		return -EBUSY;
> +
> +	video->frame_idx = (video->frame_idx + 1) % 2;
> +
> +	aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0);
> +	aspeed_video_write(video, VE_COMP_OFFSET, 0);
> +	aspeed_video_write(video, VE_COMP_ADDR,
> +			   video->comp[video->frame_idx].dma);
> +
> +	set_bit(VIDEO_FRAME_TRIGGERED, &video->flags);
> +
> +	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0xFFFFFFFF,
> +			    VE_INTERRUPT_COMP_COMPLETE |
> +			    VE_INTERRUPT_CAPTURE_COMPLETE);
> +
> +	aspeed_video_update(video, VE_SEQ_CTRL, 0xFFFFFFFF,
> +			    VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP);
> +
> +	return 0;
> +}
> +
> +static void aspeed_video_start_mode_detect(struct aspeed_video *video)
> +{
> +	/* Enable mode detect interrupts */
> +	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0xFFFFFFFF,
> +			    VE_INTERRUPT_MODE_DETECT);
> +
> +	/* Trigger mode detect */
> +	aspeed_video_update(video, VE_SEQ_CTRL, 0xFFFFFFFF,
> +			    VE_SEQ_CTRL_TRIG_MODE_DET);
> +}
> +
> +static void aspeed_video_disable_mode_detect(struct aspeed_video *video)
> +{
> +	/* Disable mode detect interrupts */
> +	aspeed_video_update(video, VE_INTERRUPT_CTRL,
> +			    ~VE_INTERRUPT_MODE_DETECT, 0);
> +
> +	/* Disable mode detect */
> +	aspeed_video_update(video, VE_SEQ_CTRL, ~VE_SEQ_CTRL_TRIG_MODE_DET, 0);
> +}
> +
> +static void aspeed_video_off(struct aspeed_video *video)
> +{
> +	/* Reset the engine */
> +	reset_control_assert(video->rst);
> +	udelay(100);
> +	reset_control_deassert(video->rst);
> +
> +	/* Turn off the relevant clocks */
> +	clk_disable_unprepare(video->vclk);
> +	clk_disable_unprepare(video->eclk);
> +}
> +
> +static void aspeed_video_on(struct aspeed_video *video)
> +{
> +	/* Turn on the relevant clocks */
> +	clk_prepare_enable(video->eclk);
> +	clk_prepare_enable(video->vclk);
> +
> +	/* Reset the engine */
> +	reset_control_assert(video->rst);
> +	udelay(100);
> +	reset_control_deassert(video->rst);
> +}
> +
> +static irqreturn_t aspeed_video_irq(int irq, void *arg)
> +{
> +	struct aspeed_video *video = arg;
> +	u32 sts = aspeed_video_read(video, VE_INTERRUPT_STATUS);
> +
> +	if (atomic_read(&video->clients) == 0) {
> +		dev_info(video->dev, "irq with no client; disabling irqs\n");
> +
> +		aspeed_video_write(video, VE_INTERRUPT_CTRL, 0);
> +		aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xFFFFFFFF);
> +		return IRQ_HANDLED;
> +	}
> +
> +	/* Resolution changed; reset entire engine and reinitialize */
> +	if (sts & VE_INTERRUPT_MODE_DETECT_WD) {
> +		dev_info(video->dev, "resolution changed; resetting\n");
> +		set_bit(VIDEO_RES_CHANGE, &video->flags);
> +
> +		aspeed_video_off(video);
> +
> +		schedule_delayed_work(&video->res_work,
> +				      RESOLUTION_CHANGE_DELAY);
> +		return IRQ_HANDLED;
> +	}
> +
> +	if (sts & VE_INTERRUPT_MODE_DETECT) {
> +		aspeed_video_update(video, VE_INTERRUPT_CTRL,
> +				    ~VE_INTERRUPT_MODE_DETECT, 0);
> +		aspeed_video_write(video, VE_INTERRUPT_STATUS,
> +				   VE_INTERRUPT_MODE_DETECT);
> +
> +		set_bit(VIDEO_MODE_DETECT_DONE, &video->flags);
> +		wake_up_interruptible_all(&video->wait);
> +	}
> +
> +	if ((sts & VE_INTERRUPT_COMP_COMPLETE) &&
> +	    (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) {
> +		video->frame_size = aspeed_video_read(video,
> +						      VE_OFFSET_COMP_STREAM);
> +
> +		aspeed_video_update(video, VE_INTERRUPT_CTRL,
> +				    ~(VE_INTERRUPT_COMP_COMPLETE |
> +				      VE_INTERRUPT_CAPTURE_COMPLETE), 0);
> +		aspeed_video_write(video, VE_INTERRUPT_STATUS,
> +				   VE_INTERRUPT_COMP_COMPLETE |
> +				   VE_INTERRUPT_CAPTURE_COMPLETE);
> +		aspeed_video_update(video, VE_SEQ_CTRL,
> +				    ~(VE_SEQ_CTRL_TRIG_CAPTURE |
> +				      VE_SEQ_CTRL_FORCE_IDLE |
> +				      VE_SEQ_CTRL_TRIG_COMP), 0);
> +
> +		set_bit(VIDEO_FRAME_AVAILABLE, &video->flags);
> +		clear_bit(VIDEO_FRAME_TRIGGERED, &video->flags);
> +		wake_up_interruptible_all(&video->wait);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void aspeed_video_check_polarity(struct aspeed_video *video)
> +{
> +	int i;
> +	int hsync_counter = 0;
> +	int vsync_counter = 0;
> +	u32 sts;
> +
> +	for (i = 0; i < NUM_POLARITY_CHECKS; ++i) {
> +		sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS);
> +		if (sts & VE_MODE_DETECT_STATUS_VSYNC)
> +			vsync_counter--;
> +		else
> +			vsync_counter++;
> +
> +		if (sts & VE_MODE_DETECT_STATUS_HSYNC)
> +			hsync_counter--;
> +		else
> +			hsync_counter++;
> +	}
> +
> +	if (hsync_counter < 0 || vsync_counter < 0) {
> +		u32 ctrl;
> +
> +		if (hsync_counter < 0)
> +			ctrl = VE_CTRL_HSYNC_POL;
> +
> +		if (vsync_counter < 0)
> +			ctrl = VE_CTRL_VSYNC_POL;
> +
> +		aspeed_video_update(video, VE_CTRL, 0xFFFFFFFF, ctrl);
> +	}
> +}
> +
> +#define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags)
> +
> +static int aspeed_video_get_resolution(struct aspeed_video *video)
> +{
> +	bool invalid_resolution = true;
> +	int rc;
> +	int tries = 0;
> +	unsigned int bottom;
> +	unsigned int left;
> +	unsigned int right;
> +	unsigned int top;
> +	u32 src_lr_edge;
> +	u32 src_tb_edge;
> +
> +	video->fmt.width = 0;
> +	video->fmt.height = 0;
> +
> +	do {
> +		if (tries) {
> +			set_current_state(TASK_INTERRUPTIBLE);
> +			if (schedule_timeout(INVALID_RESOLUTION_DELAY))
> +				return -EINTR;
> +		}
> +
> +		aspeed_video_start_mode_detect(video);
> +
> +		rc = wait_event_interruptible_timeout(video->wait,
> +						      res_check(video),
> +						      MODE_DETECT_TIMEOUT);
> +		if (!rc) {
> +			dev_err(video->dev, "timed out on 1st mode detect\n");
> +			aspeed_video_disable_mode_detect(video);
> +			return -ETIME;
> +		}
> +
> +		/* Disable mode detect in order to re-trigger */
> +		aspeed_video_update(video, VE_SEQ_CTRL,
> +				    ~VE_SEQ_CTRL_TRIG_MODE_DET, 0);
> +
> +		aspeed_video_check_polarity(video);
> +
> +		aspeed_video_start_mode_detect(video);
> +
> +		rc = wait_event_interruptible_timeout(video->wait,
> +						      res_check(video),
> +						      MODE_DETECT_TIMEOUT);
> +		if (!rc) {
> +			dev_err(video->dev, "timed out on 2nd mode detect\n");
> +			aspeed_video_disable_mode_detect(video);
> +			return -ETIME;
> +		}
> +
> +		src_lr_edge = aspeed_video_read(video, VE_SRC_LR_EDGE_DET);
> +		src_tb_edge = aspeed_video_read(video, VE_SRC_TB_EDGE_DET);
> +
> +		bottom = (src_tb_edge & VE_SRC_TB_EDGE_DET_BOT) >>
> +			VE_SRC_TB_EDGE_DET_BOT_SHF;
> +		top = src_tb_edge & VE_SRC_TB_EDGE_DET_TOP;
> +		if (top > bottom)
> +			continue;
> +
> +		right = (src_lr_edge & VE_SRC_LR_EDGE_DET_RT) >>
> +			VE_SRC_LR_EDGE_DET_RT_SHF;
> +		left = src_lr_edge & VE_SRC_LR_EDGE_DET_LEFT;
> +		if (left > right)
> +			continue;
> +
> +		invalid_resolution = false;
> +	} while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES));
> +
> +	if (invalid_resolution) {
> +		dev_err(video->dev, "invalid resolution detected\n");
> +		return -EMSGSIZE;
> +	}
> +
> +	video->fmt.height = (bottom - top) + 1;
> +	video->fmt.width = (right - left) + 1;
> +
> +	/* Don't use direct mode below 1024 x 768 (irqs don't fire) */
> +	if (video->fmt.height * video->fmt.width < DIRECT_FETCH_THRESHOLD) {
> +		aspeed_video_write(video, VE_TGS_0,
> +				   FIELD_PREP(VE_TGS_FIRST, left - 1) |
> +				   FIELD_PREP(VE_TGS_LAST, right));
> +		aspeed_video_write(video, VE_TGS_1,
> +				   FIELD_PREP(VE_TGS_FIRST, top) |
> +				   FIELD_PREP(VE_TGS_LAST, bottom + 1));
> +		aspeed_video_update(video, VE_CTRL,
> +				    ~VE_CTRL_DIRECT_FETCH, VE_CTRL_INT_DE);
> +	}
> +
> +	aspeed_video_write(video, VE_CAP_WINDOW,
> +			   video->fmt.width << 16 | video->fmt.height);
> +	aspeed_video_write(video, VE_COMP_WINDOW,
> +			   video->fmt.width << 16 | video->fmt.height);
> +	aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET,
> +			   video->fmt.width * 4);
> +
> +	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0xFFFFFFFF,
> +			    VE_INTERRUPT_MODE_DETECT_WD);
> +	aspeed_video_update(video, VE_SEQ_CTRL, 0xFFFFFFFF,
> +			    VE_SEQ_CTRL_EN_WATCHDOG);
> +
> +	dev_dbg(video->dev, "got resolution[%dx%d]\n", video->fmt.width,
> +		video->fmt.height);
> +
> +	return 0;
> +}
> +
> +static void aspeed_video_init_regs(struct aspeed_video *video)
> +{
> +	u32 comp_ctrl = VE_COMP_CTRL_RSVD |
> +		FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
> +		FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10);
> +	u32 ctrl = VE_CTRL_DIRECT_FETCH | VE_CTRL_AUTO_OR_CURSOR;
> +	u32 seq_ctrl = VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_JPEG_MODE;
> +
> +	if (video->frame_rate)
> +		ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate);
> +
> +	if (video->fmt.pixelformat == V4L2_PIX_FMT_YUV420)
> +		seq_ctrl |= VE_SEQ_CTRL_YUV420;
> +
> +	/* Unlock VE registers */
> +	aspeed_video_write(video, VE_PROTECTION_KEY, VE_PROTECTION_KEY_UNLOCK);
> +
> +	/* Disable interrupts */
> +	aspeed_video_write(video, VE_INTERRUPT_CTRL, 0);
> +	aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xFFFFFFFF);
> +
> +	/* Clear the offset */
> +	aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0);
> +	aspeed_video_write(video, VE_COMP_OFFSET, 0);
> +
> +	/* Set memory restrictions */
> +	aspeed_video_write(video, VE_MEM_RESTRICT_START, video->min);
> +	aspeed_video_write(video, VE_MEM_RESTRICT_END, video->max);
> +
> +	/* Set buffer addresses */
> +	aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma);
> +	aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma);
> +	aspeed_video_write(video, VE_COMP_ADDR, video->comp[0].dma);
> +	aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma);
> +
> +	/* Set control registers */
> +	aspeed_video_write(video, VE_SEQ_CTRL, seq_ctrl);
> +	aspeed_video_write(video, VE_CTRL, ctrl);
> +	aspeed_video_write(video, VE_COMP_CTRL, comp_ctrl);
> +
> +	/* Compression buffer size; 128K packet * 8 packets */
> +	aspeed_video_write(video, VE_STREAM_BUF_SIZE, 0xf);
> +
> +	/* Don't downscale */
> +	aspeed_video_write(video, VE_SCALING_FACTOR, 0x10001000);
> +	aspeed_video_write(video, VE_SCALING_FILTER0, 0x00200000);
> +	aspeed_video_write(video, VE_SCALING_FILTER1, 0x00200000);
> +	aspeed_video_write(video, VE_SCALING_FILTER2, 0x00200000);
> +	aspeed_video_write(video, VE_SCALING_FILTER3, 0x00200000);
> +
> +	/* Set mode detection defaults */
> +	aspeed_video_write(video, VE_MODE_DETECT, 0x22666500);
> +}
> +
> +static int aspeed_video_allocate_cma(struct aspeed_video *video)
> +{
> +	video->srcs[0].virt = dma_alloc_coherent(video->dev,
> +						 VE_SRC_BUFFER_SIZE,
> +						 &video->srcs[0].dma,
> +						 GFP_KERNEL);
> +	if (!video->srcs[0].virt) {
> +		dev_err(video->dev,
> +			"Failed to allocate source buffer 0, size[%x]\n",
> +			VE_SRC_BUFFER_SIZE);
> +		goto err;
> +	}
> +
> +	video->srcs[1].virt = dma_alloc_coherent(video->dev,
> +						 VE_SRC_BUFFER_SIZE,
> +						 &video->srcs[1].dma,
> +						 GFP_KERNEL);
> +	if (!video->srcs[1].virt) {
> +		dev_err(video->dev,
> +			"Failed to allocate source buffer 1, size[%x]\n",
> +			VE_SRC_BUFFER_SIZE);
> +		goto free_src0;
> +	}
> +
> +	video->comp[0].virt = dma_alloc_coherent(video->dev,
> +						 VE_COMP_BUFFER_SIZE,
> +						 &video->comp[0].dma,
> +						 GFP_KERNEL);
> +	if (!video->comp[0].virt) {
> +		dev_err(video->dev,
> +			"Failed to allocate compression buffer 0, size[%x]\n",
> +			VE_COMP_BUFFER_SIZE);
> +		goto free_src1;
> +	}
> +
> +	video->comp[1].virt = dma_alloc_coherent(video->dev,
> +						 VE_COMP_BUFFER_SIZE,
> +						 &video->comp[1].dma,
> +						 GFP_KERNEL);
> +	if (!video->comp[0].virt) {
> +		dev_err(video->dev,
> +			"Failed to allocate compression buffer 1, size[%x]\n",
> +			VE_COMP_BUFFER_SIZE);
> +		goto free_comp0;
> +	}
> +
> +	video->jpeg.virt = dma_alloc_coherent(video->dev, VE_JPEG_BUFFER_SIZE,
> +					      &video->jpeg.dma, GFP_KERNEL);
> +	if (!video->jpeg.virt) {
> +		dev_err(video->dev,
> +			"Failed to allocate JPEG buffer, size[%x]\n",
> +			VE_JPEG_BUFFER_SIZE);
> +		goto free_comp1;
> +	}
> +
> +	if (video->fmt.pixelformat == V4L2_PIX_FMT_YUV420)
> +		aspeed_video_init_jpeg_table(video->jpeg.virt, true);
> +	else
> +		aspeed_video_init_jpeg_table(video->jpeg.virt, false);
> +
> +	/*
> +	 * Calculate the memory restrictions. Don't consider the JPEG header
> +	 * buffer since HW doesn't need to write to it.
> +	 */
> +	video->max = max(video->srcs[0].dma + VE_SRC_BUFFER_SIZE,
> +			 video->srcs[1].dma + VE_SRC_BUFFER_SIZE);
> +	video->max = max(video->max, video->comp[0].dma + VE_COMP_BUFFER_SIZE);
> +	video->max = max(video->max, video->comp[1].dma + VE_COMP_BUFFER_SIZE);
> +
> +	video->min = min(video->srcs[0].dma, video->srcs[1].dma);
> +	video->min = min(video->min, video->comp[0].dma);
> +	video->min = min(video->min, video->comp[1].dma);
> +
> +	return 0;
> +
> +free_comp1:
> +	dma_free_coherent(video->dev, VE_COMP_BUFFER_SIZE, video->comp[1].virt,
> +			  video->comp[1].dma);
> +	video->comp[1].dma = 0ULL;
> +	video->comp[1].virt = NULL;
> +
> +free_comp0:
> +	dma_free_coherent(video->dev, VE_COMP_BUFFER_SIZE, video->comp[0].virt,
> +			  video->comp[0].dma);
> +	video->comp[0].dma = 0ULL;
> +	video->comp[0].virt = NULL;
> +
> +free_src1:
> +	dma_free_coherent(video->dev, VE_SRC_BUFFER_SIZE, video->srcs[1].virt,
> +			  video->srcs[1].dma);
> +	video->srcs[1].dma = 0ULL;
> +	video->srcs[1].virt = NULL;
> +
> +free_src0:
> +	dma_free_coherent(video->dev, VE_SRC_BUFFER_SIZE, video->srcs[0].virt,
> +			  video->srcs[0].dma);
> +	video->srcs[0].dma = 0ULL;
> +	video->srcs[0].virt = NULL;
> +err:
> +	return -ENOMEM;
> +}
> +
> +static void aspeed_video_free_cma(struct aspeed_video *video)
> +{
> +	dma_free_coherent(video->dev, VE_JPEG_BUFFER_SIZE, video->jpeg.virt,
> +			  video->jpeg.dma);
> +	dma_free_coherent(video->dev, VE_COMP_BUFFER_SIZE, video->comp[1].virt,
> +			  video->comp[1].dma);
> +	dma_free_coherent(video->dev, VE_COMP_BUFFER_SIZE, video->comp[0].virt,
> +			  video->comp[0].dma);
> +	dma_free_coherent(video->dev, VE_SRC_BUFFER_SIZE, video->srcs[1].virt,
> +			  video->srcs[1].dma);
> +	dma_free_coherent(video->dev, VE_SRC_BUFFER_SIZE, video->srcs[0].virt,
> +			  video->srcs[0].dma);
> +
> +	video->srcs[0].dma = 0ULL;
> +	video->srcs[0].virt = NULL;
> +	video->srcs[1].dma = 0ULL;
> +	video->srcs[1].virt = NULL;
> +	video->comp[0].dma = 0ULL;
> +	video->comp[0].virt = NULL;
> +	video->comp[1].dma = 0ULL;
> +	video->comp[1].virt = NULL;
> +	video->jpeg.dma = 0ULL;
> +	video->jpeg.virt = NULL;
> +}
> +
> +static int aspeed_video_start(struct aspeed_video *video)
> +{
> +	int rc = aspeed_video_allocate_cma(video);
> +
> +	if (rc)
> +		return rc;
> +
> +	aspeed_video_on(video);
> +
> +	aspeed_video_init_regs(video);
> +
> +	rc = aspeed_video_get_resolution(video);
> +	if (rc)
> +		aspeed_video_free_cma(video);
> +
> +	clear_bit(VIDEO_FRAME_TRIGGERED, &video->flags);
> +
> +	return rc;
> +}
> +
> +static void aspeed_video_stop(struct aspeed_video *video)
> +{
> +	cancel_delayed_work_sync(&video->res_work);
> +
> +	aspeed_video_off(video);
> +
> +	aspeed_video_free_cma(video);
> +
> +	clear_bit(VIDEO_FRAME_AVAILABLE, &video->flags);
> +}
> +
> +static int aspeed_video_querycap(struct file *file, void *fh,
> +				 struct v4l2_capability *cap)
> +{
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	strncpy(cap->driver, DEVICE_NAME, sizeof(cap->driver));

Use strlcpy.

Also fill in bus_info ("platform:<foo>").

> +	cap->capabilities = video->vdev.device_caps | V4L2_CAP_DEVICE_CAPS;

You can drop this, it's filled in for you.

> +
> +	return 0;
> +}
> +
> +static int aspeed_video_get_format(struct file *file, void *fh,
> +				   struct v4l2_format *f)
> +{
> +	int rc;
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	if (test_bit(VIDEO_RES_CHANGE, &video->flags)) {
> +		if (file->f_flags & O_NONBLOCK)
> +			return -EAGAIN;
> +
> +		rc = wait_event_interruptible(video->wait,
> +					      !test_bit(VIDEO_RES_CHANGE,
> +							&video->flags));
> +		if (rc)
> +			return -EINTR;

No, get_format should always just return the currently set format,
not the format that is detected.

Use VIDIOC_QUERY_DV_TIMINGS for that.

> +	}
> +
> +	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

Drop this, not needed.

> +	f->fmt.pix = video->fmt;
> +
> +	return 0;
> +}
> +
> +static int aspeed_video_set_format(struct file *file, void *fh,
> +				   struct v4l2_format *f)
> +{
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	if (f->fmt.pix.pixelformat == video->fmt.pixelformat)
> +		return 0;
> +
> +	if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV444) {
> +		video->fmt.pixelformat = V4L2_PIX_FMT_YUV444;
> +		aspeed_video_init_jpeg_table(video->jpeg.virt, false);
> +		aspeed_video_update(video, VE_SEQ_CTRL, ~VE_SEQ_CTRL_YUV420,
> +				    0);
> +	} else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
> +		video->fmt.pixelformat = V4L2_PIX_FMT_YUV420;
> +		aspeed_video_init_jpeg_table(video->jpeg.virt, true);
> +		aspeed_video_update(video, VE_SEQ_CTRL, 0xFFFFFFFF,
> +				    VE_SEQ_CTRL_YUV420);

This isn't filling in any of the other fields.

> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int aspeed_video_get_jpegcomp(struct file *file, void *fh,
> +				     struct v4l2_jpegcompression *a)
> +{
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	a->quality = video->jpeg_quality;
> +
> +	return 0;
> +}
> +
> +static int aspeed_video_set_jpegcomp(struct file *file, void *fh,
> +				     const struct v4l2_jpegcompression *a)
> +{
> +	u32 comp_ctrl;
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	if (a->quality < 0 || a->quality > 11)
> +		return -EINVAL;
> +
> +	video->jpeg_quality = a->quality;
> +	comp_ctrl = FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
> +		FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10);
> +
> +	aspeed_video_update(video, VE_COMP_CTRL,
> +			    ~(VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR),
> +			    comp_ctrl);
> +
> +	return 0;
> +}

As the spec says, the jpegcomp ioctls are deprecated and you should use
JPEG controls instead.

See: https://hverkuil.home.xs4all.nl/spec/uapi/v4l/vidioc-g-jpegcomp.html

I stop reviewing here, since the first thing you need to do is to run
v4l2-compliance for your device driver.

See my reply to patch 0/4.

Regards,

	Hans

> +
> +static int aspeed_video_get_parm(struct file *file, void *fh,
> +				 struct v4l2_streamparm *a)
> +{
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
> +	a->parm.capture.timeperframe.numerator = 1;
> +	a->parm.capture.timeperframe.denominator = video->frame_rate;
> +
> +	return 0;
> +}
> +
> +static int aspeed_video_set_parm(struct file *file, void *fh,
> +				 struct v4l2_streamparm *a)
> +{
> +	int frame_rate;
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
> +		return -EINVAL;
> +
> +	frame_rate = a->parm.capture.timeperframe.denominator /
> +		a->parm.capture.timeperframe.numerator;
> +
> +	if (frame_rate < 0 || frame_rate > 60)
> +		return -EINVAL;
> +
> +	if (video->frame_rate != frame_rate) {
> +		video->frame_rate = frame_rate;
> +		aspeed_video_update(video, VE_CTRL, ~VE_CTRL_FRC,
> +				    FIELD_PREP(VE_CTRL_FRC, frame_rate));
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_ioctl_ops aspeed_video_ioctls = {
> +	.vidioc_querycap = aspeed_video_querycap,
> +	.vidioc_g_fmt_vid_cap = aspeed_video_get_format,
> +	.vidioc_s_fmt_vid_cap = aspeed_video_set_format,
> +	.vidioc_g_jpegcomp = aspeed_video_get_jpegcomp,
> +	.vidioc_s_jpegcomp = aspeed_video_set_jpegcomp,
> +	.vidioc_g_parm = aspeed_video_get_parm,
> +	.vidioc_s_parm = aspeed_video_set_parm,
> +};
> +
> +static void aspeed_video_resolution_work(struct work_struct *work)
> +{
> +	int rc;
> +	struct delayed_work *dwork = to_delayed_work(work);
> +	struct aspeed_video *video = container_of(dwork, struct aspeed_video,
> +						  res_work);
> +
> +	/* No clients remaining after delay */
> +	if (atomic_read(&video->clients) == 0)
> +		goto done;
> +
> +	aspeed_video_on(video);
> +
> +	aspeed_video_init_regs(video);
> +
> +	rc = aspeed_video_get_resolution(video);
> +	if (rc) {
> +		dev_err(video->dev,
> +			"resolution changed; couldn't get new resolution\n");
> +	} else {
> +		video->frame_idx = 0;
> +		clear_bit(VIDEO_FRAME_TRIGGERED, &video->flags);
> +	}
> +
> +done:
> +	clear_bit(VIDEO_RES_CHANGE, &video->flags);
> +	wake_up_interruptible_all(&video->wait);
> +}
> +
> +static bool aspeed_video_frame_available(struct aspeed_video *video)
> +{
> +	if (!test_and_clear_bit(VIDEO_FRAME_AVAILABLE, &video->flags)) {
> +		if (!test_bit(VIDEO_FRAME_TRIGGERED, &video->flags))
> +			aspeed_video_start_frame(video);
> +
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static ssize_t aspeed_video_file_read(struct file *file, char __user *buf,
> +				      size_t count, loff_t *ppos)
> +{
> +	int rc;
> +	int fidx;
> +	size_t size;
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	if (mutex_lock_interruptible(&video->video_lock))
> +		return -EINTR;
> +
> +	if (file->f_flags & O_NONBLOCK) {
> +		if (!aspeed_video_frame_available(video)) {
> +			rc = -EAGAIN;
> +			goto unlock;
> +		} else {
> +			goto ready;
> +		}
> +	}
> +
> +	rc = wait_event_interruptible(video->wait,
> +				      aspeed_video_frame_available(video));
> +	if (rc) {
> +		rc = -EINTR;
> +		goto unlock;
> +	}
> +
> +ready:
> +	fidx = video->frame_idx;
> +	size = min_t(size_t, video->frame_size, count);
> +	aspeed_video_start_frame(video);
> +
> +	if (copy_to_user(buf, video->comp[fidx].virt, size)) {
> +		rc = -EFAULT;
> +		goto unlock;
> +	}
> +
> +	rc = size;
> +
> +unlock:
> +	mutex_unlock(&video->video_lock);
> +	return rc;
> +}
> +
> +static int aspeed_video_open(struct file *file)
> +{
> +	int rc;
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	if (atomic_inc_return(&video->clients) == 1) {
> +		rc = aspeed_video_start(video);
> +		if (rc) {
> +			dev_err(video->dev, "Failed to start video engine\n");
> +			atomic_dec(&video->clients);
> +			return rc;
> +		}
> +	}
> +
> +	return v4l2_fh_open(file);
> +}
> +
> +static int aspeed_video_release(struct file *file)
> +{
> +	int rc;
> +	struct aspeed_video *video = video_drvdata(file);
> +
> +	rc = v4l2_fh_release(file);
> +
> +	if (atomic_dec_return(&video->clients) == 0)
> +		aspeed_video_stop(video);
> +
> +	return rc;
> +}
> +
> +static const struct v4l2_file_operations aspeed_video_v4l2_fops = {
> +	.owner = THIS_MODULE,
> +	.read = aspeed_video_file_read,
> +	.unlocked_ioctl = video_ioctl2,
> +	.open = aspeed_video_open,
> +	.release = aspeed_video_release,
> +};
> +
> +static void aspeed_video_device_release(struct video_device *vdev)
> +{
> +}
> +
> +static int aspeed_video_setup_video(struct aspeed_video *video)
> +{
> +	int rc;
> +	struct v4l2_device *v4l2_dev = &video->v4l2_dev;
> +	struct video_device *vdev = &video->vdev;
> +
> +	rc = v4l2_device_register(video->dev, v4l2_dev);
> +	if (rc) {
> +		dev_err(video->dev, "Failed to register v4l2 device\n");
> +		return rc;
> +	}
> +
> +	vdev->fops = &aspeed_video_v4l2_fops;
> +	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
> +	vdev->v4l2_dev = v4l2_dev;
> +	strncpy(vdev->name, DEVICE_NAME, sizeof(vdev->name));
> +	vdev->vfl_type = VFL_TYPE_GRABBER;
> +	vdev->vfl_dir = VFL_DIR_RX;
> +	vdev->release = aspeed_video_device_release;
> +	vdev->ioctl_ops = &aspeed_video_ioctls;
> +	vdev->lock = &video->video_lock;
> +
> +	video_set_drvdata(vdev, video);
> +	rc = video_register_device(vdev, VFL_TYPE_GRABBER, 0);
> +	if (rc) {
> +		v4l2_device_unregister(v4l2_dev);
> +		dev_err(video->dev, "Failed to register video device\n");
> +		return rc;
> +	}
> +
> +	/* set pixel format defaults */
> +	video->fmt.pixelformat = V4L2_PIX_FMT_YUV444;
> +	video->fmt.field = V4L2_FIELD_NONE;
> +
> +	return 0;
> +}
> +
> +static int aspeed_video_init(struct aspeed_video *video)
> +{
> +	int irq;
> +	int rc;
> +	struct device *dev = video->dev;
> +
> +	irq = irq_of_parse_and_map(dev->of_node, 0);
> +	if (!irq) {
> +		dev_err(dev, "Unable to find IRQ\n");
> +		return -ENODEV;
> +	}
> +
> +	rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED,
> +			      DEVICE_NAME, video);
> +	if (rc < 0) {
> +		dev_err(dev, "Unable to request IRQ %d\n", irq);
> +		return rc;
> +	}
> +
> +	video->eclk = devm_clk_get(dev, "eclk-gate");
> +	if (IS_ERR(video->eclk)) {
> +		dev_err(dev, "Unable to get ECLK\n");
> +		return PTR_ERR(video->eclk);
> +	}
> +
> +	video->vclk = devm_clk_get(dev, "vclk-gate");
> +	if (IS_ERR(video->vclk)) {
> +		dev_err(dev, "Unable to get VCLK\n");
> +		return PTR_ERR(video->vclk);
> +	}
> +
> +	video->rst = devm_reset_control_get_exclusive(dev, NULL);
> +	if (IS_ERR(video->rst)) {
> +		dev_err(dev, "Unable to get VE reset\n");
> +		return PTR_ERR(video->rst);
> +	}
> +
> +	rc = of_reserved_mem_device_init(dev);
> +	if (rc) {
> +		dev_err(dev, "Unable to reserve memory\n");
> +		return rc;
> +	}
> +
> +	rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> +	if (rc) {
> +		dev_err(dev, "Failed to set DMA mask\n");
> +		of_reserved_mem_device_release(dev);
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +static int aspeed_video_probe(struct platform_device *pdev)
> +{
> +	int rc;
> +	struct resource *res;
> +	struct aspeed_video *video = kzalloc(sizeof(*video), GFP_KERNEL);
> +
> +	if (!video)
> +		return -ENOMEM;
> +
> +	video->frame_rate = 30;
> +	video->dev = &pdev->dev;
> +	mutex_init(&video->video_lock);
> +	init_waitqueue_head(&video->wait);
> +	INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +	video->base = devm_ioremap_resource(video->dev, res);
> +
> +	if (IS_ERR(video->base))
> +		return PTR_ERR(video->base);
> +
> +	rc = aspeed_video_init(video);
> +	if (rc)
> +		return rc;
> +
> +	rc = aspeed_video_setup_video(video);
> +	if (rc)
> +		return rc;
> +
> +	return 0;
> +}
> +
> +static int aspeed_video_remove(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
> +	struct aspeed_video *video = to_aspeed_video(v4l2_dev);
> +
> +	video_unregister_device(&video->vdev);
> +
> +	v4l2_device_unregister(v4l2_dev);
> +
> +	of_reserved_mem_device_release(dev);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id aspeed_video_of_match[] = {
> +	{ .compatible = "aspeed,ast2400-video" },
> +	{ .compatible = "aspeed,ast2500-video" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, aspeed_video_of_match);
> +
> +static struct platform_driver aspeed_video_driver = {
> +	.driver = {
> +		.name = DEVICE_NAME,
> +		.of_match_table = aspeed_video_of_match,
> +	},
> +	.probe = aspeed_video_probe,
> +	.remove = aspeed_video_remove,
> +};
> +
> +module_platform_driver(aspeed_video_driver);
> +
> +MODULE_DESCRIPTION("ASPEED Video Engine Driver");
> +MODULE_AUTHOR("Eddie James");
> +MODULE_LICENSE("GPL v2");
> 


^ permalink raw reply

* [PATCH 0/4] media: platform: Add Aspeed Video Engine driver
From: Stephen Boyd @ 2018-09-01  2:46 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <a959a032-6817-dcb4-2c5f-b4bd17fc1c8b@linux.vnet.ibm.com>

Quoting Eddie James (2018-08-31 12:30:02)
> 
> 
> On 08/31/2018 12:56 PM, Stephen Boyd wrote:
> > Quoting Eddie James (2018-08-29 14:09:29)
> > Please let me know your merge strategy here. I can ack the clk patches
> > because they look fine from high-level clk driver perspective (maybe
> > Joel can take a closer look) or I can merge the patches into clk-next
> > and get them into next release while the video driver gets reviewed.
> 
> Thanks for taking a look! Probably preferable to get the clk patches 
> into clk-next first (though Joel reviewing would be great). I just put 
> everything in the same set for the sake of explaining the necessity of 
> the clk changes.
> 

Ok. If Joel is able to review then I can easily merge it into clk-next.


^ permalink raw reply

* [PATCH 2/4] clock: aspeed: Setup video engine clocking
From: Joel Stanley @ 2018-08-31 21:33 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-3-git-send-email-eajames@linux.vnet.ibm.com>

On Wed, 29 Aug 2018 at 14:09, Eddie James <eajames@linux.vnet.ibm.com> wrote:
>
> Add the video engine reset bit. Add eclk mux and clock divider table.
>
> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>

I'm travelling this week, so I'll be able to take a closer look next week.

At a glance it looks okay.

Cheers,

Joel

> ---
>  drivers/clk/clk-aspeed.c | 41 +++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 39 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
> index 5961367..f16ce7d 100644
> --- a/drivers/clk/clk-aspeed.c
> +++ b/drivers/clk/clk-aspeed.c
> @@ -87,7 +87,7 @@ struct aspeed_clk_gate {
>  /* TODO: ask Aspeed about the actual parent data */
>  static const struct aspeed_gate_data aspeed_gates[] = {
>         /*                               clk rst   name                 parent  flags */
> -       [ASPEED_CLK_GATE_ECLK] =        {  0, -1, "eclk-gate",          "eclk", 0 }, /* Video Engine */
> +       [ASPEED_CLK_GATE_ECLK] =        {  0,  6, "eclk-gate",          "eclk", 0 }, /* Video Engine */
>         [ASPEED_CLK_GATE_GCLK] =        {  1,  7, "gclk-gate",          NULL,   0 }, /* 2D engine */
>         [ASPEED_CLK_GATE_MCLK] =        {  2, -1, "mclk-gate",          "mpll", CLK_IS_CRITICAL }, /* SDRAM */
>         [ASPEED_CLK_GATE_VCLK] =        {  3,  6, "vclk-gate",          NULL,   0 }, /* Video Capture */
> @@ -113,6 +113,24 @@ struct aspeed_clk_gate {
>         [ASPEED_CLK_GATE_LHCCLK] =      { 28, -1, "lhclk-gate",         "lhclk", 0 }, /* LPC master/LPC+ */
>  };
>
> +static const char * const eclk_parent_names[] = {
> +       "mpll",
> +       "hpll",
> +       "dpll",
> +};
> +
> +static const struct clk_div_table ast2500_eclk_div_table[] = {
> +       { 0x0, 2 },
> +       { 0x1, 2 },
> +       { 0x2, 3 },
> +       { 0x3, 4 },
> +       { 0x4, 5 },
> +       { 0x5, 6 },
> +       { 0x6, 7 },
> +       { 0x7, 8 },
> +       { 0 }
> +};
> +
>  static const struct clk_div_table ast2500_mac_div_table[] = {
>         { 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */
>         { 0x1, 4 },
> @@ -192,18 +210,21 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char *name, u32 val)
>
>  struct aspeed_clk_soc_data {
>         const struct clk_div_table *div_table;
> +       const struct clk_div_table *eclk_div_table;
>         const struct clk_div_table *mac_div_table;
>         struct clk_hw *(*calc_pll)(const char *name, u32 val);
>  };
>
>  static const struct aspeed_clk_soc_data ast2500_data = {
>         .div_table = ast2500_div_table,
> +       .eclk_div_table = ast2500_eclk_div_table,
>         .mac_div_table = ast2500_mac_div_table,
>         .calc_pll = aspeed_ast2500_calc_pll,
>  };
>
>  static const struct aspeed_clk_soc_data ast2400_data = {
>         .div_table = ast2400_div_table,
> +       .eclk_div_table = ast2400_div_table,
>         .mac_div_table = ast2400_div_table,
>         .calc_pll = aspeed_ast2400_calc_pll,
>  };
> @@ -317,6 +338,7 @@ struct aspeed_reset {
>         [ASPEED_RESET_PECI]     = 10,
>         [ASPEED_RESET_I2C]      =  2,
>         [ASPEED_RESET_AHB]      =  1,
> +       [ASPEED_RESET_VIDEO]    =  6,
>
>         /*
>          * SCUD4 resets start at an offset to separate them from
> @@ -522,6 +544,22 @@ static int aspeed_clk_probe(struct platform_device *pdev)
>                 return PTR_ERR(hw);
>         aspeed_clk_data->hws[ASPEED_CLK_24M] = hw;
>
> +       hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names,
> +                                ARRAY_SIZE(eclk_parent_names), 0,
> +                                scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0,
> +                                &aspeed_clk_lock);
> +       if (IS_ERR(hw))
> +               return PTR_ERR(hw);
> +       aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw;
> +
> +       hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0,
> +                                          scu_base + ASPEED_CLK_SELECTION, 28,
> +                                          3, 0, soc_data->eclk_div_table,
> +                                          &aspeed_clk_lock);
> +       if (IS_ERR(hw))
> +               return PTR_ERR(hw);
> +       aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw;
> +
>         /*
>          * TODO: There are a number of clocks that not included in this driver
>          * as more information is required:
> @@ -531,7 +569,6 @@ static int aspeed_clk_probe(struct platform_device *pdev)
>          *   RGMII
>          *   RMII
>          *   UART[1..5] clock source mux
> -        *   Video Engine (ECLK) mux and clock divider
>          */
>
>         for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) {
> --
> 1.8.3.1
>

^ permalink raw reply

* [PATCH v2 2/2] ARM: dts: aspeed: quanta-q71l: Add four PSUs
From: Joel Stanley @ 2018-08-31 21:30 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20180831205855.242061-1-venture@google.com>

On Fri, 31 Aug 2018 at 13:59, Patrick Venture <venture@google.com> wrote:
>
> Enable the four PSUs via generic PMBUS.
>
> Signed-off-by: Patrick Venture <venture@google.com>

These two look good. I'm travelling at the moment, but these will be
applied when I'm back at my desk.

Cheers,

Joel

> ---
> Changes for v2:
>  - Fixed the commit header typo s/q7l1/q71l/
> ---
>  arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
>
> diff --git a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
> index 586050e1c0e6..c655564e6c6d 100644
> --- a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
> +++ b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
> @@ -318,24 +318,44 @@
>                         #address-cells = <1>;
>                         #size-cells = <0>;
>                         reg = <0>;
> +
> +                       psu at 59 {
> +                               compatible = "pmbus";
> +                               reg = <0x59>;
> +                       };
>                 };
>
>                 i2c_psu1: i2c at 1 {
>                         #address-cells = <1>;
>                         #size-cells = <0>;
>                         reg = <1>;
> +
> +                       psu at 58 {
> +                               compatible = "pmbus";
> +                               reg = <0x58>;
> +                       };
>                 };
>
>                 i2c_psu3: i2c at 2 {
>                         #address-cells = <1>;
>                         #size-cells = <0>;
>                         reg = <2>;
> +
> +                       psu at 58 {
> +                               compatible = "pmbus";
> +                               reg = <0x58>;
> +                       };
>                 };
>
>                 i2c_psu2: i2c at 3 {
>                         #address-cells = <1>;
>                         #size-cells = <0>;
>                         reg = <3>;
> +
> +                       psu at 59 {
> +                               compatible = "pmbus";
> +                               reg = <0x59>;
> +                       };
>                 };
>         };
>
> --
> 2.19.0.rc1.350.ge57e33dbd1-goog
>

^ permalink raw reply

* [PATCH v2 2/2] ARM: dts: aspeed: quanta-q71l: Add four PSUs
From: Patrick Venture @ 2018-08-31 20:58 UTC (permalink / raw)
  To: linux-aspeed

Enable the four PSUs via generic PMBUS.

Signed-off-by: Patrick Venture <venture@google.com>
---
Changes for v2:
 - Fixed the commit header typo s/q7l1/q71l/
---
 arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
index 586050e1c0e6..c655564e6c6d 100644
--- a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
@@ -318,24 +318,44 @@
 			#address-cells = <1>;
 			#size-cells = <0>;
 			reg = <0>;
+
+			psu at 59 {
+				compatible = "pmbus";
+				reg = <0x59>;
+			};
 		};
 
 		i2c_psu1: i2c at 1 {
 			#address-cells = <1>;
 			#size-cells = <0>;
 			reg = <1>;
+
+			psu at 58 {
+				compatible = "pmbus";
+				reg = <0x58>;
+			};
 		};
 
 		i2c_psu3: i2c at 2 {
 			#address-cells = <1>;
 			#size-cells = <0>;
 			reg = <2>;
+
+			psu at 58 {
+				compatible = "pmbus";
+				reg = <0x58>;
+			};
 		};
 
 		i2c_psu2: i2c at 3 {
 			#address-cells = <1>;
 			#size-cells = <0>;
 			reg = <3>;
+
+			psu at 59 {
+				compatible = "pmbus";
+				reg = <0x59>;
+			};
 		};
 	};
 
-- 
2.19.0.rc1.350.ge57e33dbd1-goog


^ permalink raw reply related

* [PATCH v2 1/2] ARM: dts: aspeed: quanta-q71l: add aliases for i2c
From: Patrick Venture @ 2018-08-31 20:58 UTC (permalink / raw)
  To: linux-aspeed

Provide aliases to each i2c bus per labels added for
each PCIe slot, etc, that are downstream beyond a mux.

Signed-off-by: Patrick Venture <venture@google.com>
---
Changes for v2:
 - No changes.
---
 arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
index 76aa6ea1f988..586050e1c0e6 100644
--- a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
@@ -7,6 +7,25 @@
 	model = "Quanta Q71L BMC";
 	compatible = "quanta,q71l-bmc", "aspeed,ast2400";
 
+	aliases {
+		i2c14 = &i2c_pcie2;
+		i2c15 = &i2c_pcie3;
+		i2c16 = &i2c_pcie6;
+		i2c17 = &i2c_pcie7;
+		i2c18 = &i2c_pcie1;
+		i2c19 = &i2c_pcie4;
+		i2c20 = &i2c_pcie5;
+		i2c21 = &i2c_pcie8;
+		i2c22 = &i2c_pcie9;
+		i2c23 = &i2c_pcie10;
+		i2c24 = &i2c_ssd1;
+		i2c25 = &i2c_ssd2;
+		i2c26 = &i2c_psu4;
+		i2c27 = &i2c_psu1;
+		i2c28 = &i2c_psu3;
+		i2c29 = &i2c_psu2;
+	};
+
 	chosen {
 		stdout-path = &uart5;
 		bootargs = "console=ttyS4,115200 earlyprintk";
-- 
2.18.0.1017.ga543ac7ca45-goog


^ permalink raw reply related

* [PATCH 2/2] ARM: dts: aspeed: quanta-q7l1: Add four PSUs
From: Patrick Venture @ 2018-08-31 20:54 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20180822171238.227972-1-venture@google.com>

On Wed, Aug 22, 2018 at 10:12 AM Patrick Venture <venture@google.com> wrote:
>
> Enable the four PSUs via generic PMBUS.
>
> Signed-off-by: Patrick Venture <venture@google.com>
> ---
>  arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts | 20 ++++++++++++++++++++
>  1 file changed, 20 insertions(+)
>
> diff --git a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
> index 586050e1c0e6..c655564e6c6d 100644
> --- a/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
> +++ b/arch/arm/boot/dts/aspeed-bmc-quanta-q71l.dts
> @@ -318,24 +318,44 @@
>                         #address-cells = <1>;
>                         #size-cells = <0>;
>                         reg = <0>;
> +
> +                       psu at 59 {
> +                               compatible = "pmbus";
> +                               reg = <0x59>;
> +                       };
>                 };
>
>                 i2c_psu1: i2c at 1 {
>                         #address-cells = <1>;
>                         #size-cells = <0>;
>                         reg = <1>;
> +
> +                       psu at 58 {
> +                               compatible = "pmbus";
> +                               reg = <0x58>;
> +                       };
>                 };
>
>                 i2c_psu3: i2c at 2 {
>                         #address-cells = <1>;
>                         #size-cells = <0>;
>                         reg = <2>;
> +
> +                       psu at 58 {
> +                               compatible = "pmbus";
> +                               reg = <0x58>;
> +                       };
>                 };
>
>                 i2c_psu2: i2c at 3 {
>                         #address-cells = <1>;
>                         #size-cells = <0>;
>                         reg = <3>;
> +
> +                       psu at 59 {
> +                               compatible = "pmbus";
> +                               reg = <0x59>;
> +                       };
>                 };
>         };
>
> --
> 2.18.0.1017.ga543ac7ca45-goog
>

Just noticed I typo'd q7l1 and q71l.  I'll send this patch as a new
version with the correction.

^ permalink raw reply

* [PATCH 0/4] media: platform: Add Aspeed Video Engine driver
From: Eddie James @ 2018-08-31 19:30 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <153573819126.93865.1884182656081956202@swboyd.mtv.corp.google.com>



On 08/31/2018 12:56 PM, Stephen Boyd wrote:
> Quoting Eddie James (2018-08-29 14:09:29)
>> The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
>> can capture and compress video data from digital or analog sources. With
>> the Aspeed chip acting as a service processor, the Video Engine can
>> capture the host processor graphics output.
>>
>> This series adds a V4L2 driver for the VE, providing a read() interface
>> only. The driver triggers the hardware to capture the host graphics output
>> and compress it to JPEG format.
>>
>> Testing on an AST2500 determined that the videobuf/streaming/mmap interface
>> was significantly slower than the simple read() interface, so I have not
>> included the streaming part.
>>
>> It's also possible to use an automatic mode for the VE such that
>> re-triggering the HW every frame isn't necessary. However this wasn't
>> reliable on the AST2400, and probably used more CPU anyway due to excessive
>> interrupts. It was approximately 15% faster.
>>
>> The series also adds the necessary parent clock definitions to the Aspeed
>> clock driver, with both a mux and clock divider.
> Please let me know your merge strategy here. I can ack the clk patches
> because they look fine from high-level clk driver perspective (maybe
> Joel can take a closer look) or I can merge the patches into clk-next
> and get them into next release while the video driver gets reviewed.

Thanks for taking a look! Probably preferable to get the clk patches 
into clk-next first (though Joel reviewing would be great). I just put 
everything in the same set for the sake of explaining the necessity of 
the clk changes.

Thanks,
Eddie

>


^ permalink raw reply

* [PATCH 2/4] clock: aspeed: Setup video engine clocking
From: Stephen Boyd @ 2018-08-31 17:56 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-3-git-send-email-eajames@linux.vnet.ibm.com>

Quoting Eddie James (2018-08-29 14:09:31)
> Add the video engine reset bit. Add eclk mux and clock divider table.
> 
> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
> ---

Acked-by: Stephen Boyd <sboyd@kernel.org>


^ permalink raw reply

* [PATCH 1/4] clock: aspeed: Add VIDEO reset index definition
From: Stephen Boyd @ 2018-08-31 17:56 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-2-git-send-email-eajames@linux.vnet.ibm.com>

Quoting Eddie James (2018-08-29 14:09:30)
> Add an index into the array of resets kept in the Aspeed clock driver.
> This isn't a HW bit definition.
> 
> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
> ---

Acked-by: Stephen Boyd <sboyd@kernel.org>


^ permalink raw reply

* [PATCH 0/4] media: platform: Add Aspeed Video Engine driver
From: Stephen Boyd @ 2018-08-31 17:56 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-1-git-send-email-eajames@linux.vnet.ibm.com>

Quoting Eddie James (2018-08-29 14:09:29)
> The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
> can capture and compress video data from digital or analog sources. With
> the Aspeed chip acting as a service processor, the Video Engine can
> capture the host processor graphics output.
> 
> This series adds a V4L2 driver for the VE, providing a read() interface
> only. The driver triggers the hardware to capture the host graphics output
> and compress it to JPEG format.
> 
> Testing on an AST2500 determined that the videobuf/streaming/mmap interface
> was significantly slower than the simple read() interface, so I have not
> included the streaming part.
> 
> It's also possible to use an automatic mode for the VE such that
> re-triggering the HW every frame isn't necessary. However this wasn't
> reliable on the AST2400, and probably used more CPU anyway due to excessive
> interrupts. It was approximately 15% faster.
> 
> The series also adds the necessary parent clock definitions to the Aspeed
> clock driver, with both a mux and clock divider.

Please let me know your merge strategy here. I can ack the clk patches
because they look fine from high-level clk driver perspective (maybe
Joel can take a closer look) or I can merge the patches into clk-next
and get them into next release while the video driver gets reviewed.


^ permalink raw reply

* [PATCH 4/4] media: platform: Add Aspeed Video Engine driver
From: Eddie James @ 2018-08-30 15:40 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <CAAEAJfBpmFPLTMAr+Azc-53JXHPkCU4bjtwqE6nDWUvm=J_x-A@mail.gmail.com>



On 08/29/2018 07:52 PM, Ezequiel Garcia wrote:
> Hi Eddie,
>
> On 29 August 2018 at 18:09, Eddie James <eajames@linux.vnet.ibm.com> wrote:
>> The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
>> can capture and compress video data from digital or analog sources. With
>> the Aspeed chip acting a service processor, the Video Engine can capture
>> the host processor graphics output.
>>
>> Add a V4L2 driver to capture video data and compress it to JPEG images,
>> making the data available through a standard read interface.
>>
>> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
>> ---
>>   drivers/media/platform/Kconfig        |    8 +
>>   drivers/media/platform/Makefile       |    1 +
>>   drivers/media/platform/aspeed-video.c | 1307 +++++++++++++++++++++++++++++++++
>>   3 files changed, 1316 insertions(+)
>>   create mode 100644 drivers/media/platform/aspeed-video.c
>>
>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
>> index 94c1fe0..e599245 100644
>> --- a/drivers/media/platform/Kconfig
>> +++ b/drivers/media/platform/Kconfig
>> @@ -32,6 +32,14 @@ source "drivers/media/platform/davinci/Kconfig"
>>
>>   source "drivers/media/platform/omap/Kconfig"
>>
>> +config VIDEO_ASPEED
>> +       tristate "Aspeed AST2400 and AST2500 Video Engine driver"
>> +       depends on VIDEO_V4L2
> It seems you are not using videobuf2. I think it should simplify the read
> I/O part and at the same time expose the other capture methods.

Hi,

Well I'm not sure it would simplify the read interface; it's quite 
simple as it is, both in the driver and to set up in user-space.

I did get streaming I/O working but found the performance significantly 
worse than simple read calls, and therefore not worth the additional 
complexity.

Is it required that I support streaming?

Thanks,
Eddie

>
> There are plenty of examples to follow.
>
> Regards,
> Eze
>


^ permalink raw reply

* [PATCH 4/4] media: platform: Add Aspeed Video Engine driver
From: Ezequiel Garcia @ 2018-08-30  0:52 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-5-git-send-email-eajames@linux.vnet.ibm.com>

Hi Eddie,

On 29 August 2018 at 18:09, Eddie James <eajames@linux.vnet.ibm.com> wrote:
> The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
> can capture and compress video data from digital or analog sources. With
> the Aspeed chip acting a service processor, the Video Engine can capture
> the host processor graphics output.
>
> Add a V4L2 driver to capture video data and compress it to JPEG images,
> making the data available through a standard read interface.
>
> Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
> ---
>  drivers/media/platform/Kconfig        |    8 +
>  drivers/media/platform/Makefile       |    1 +
>  drivers/media/platform/aspeed-video.c | 1307 +++++++++++++++++++++++++++++++++
>  3 files changed, 1316 insertions(+)
>  create mode 100644 drivers/media/platform/aspeed-video.c
>
> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
> index 94c1fe0..e599245 100644
> --- a/drivers/media/platform/Kconfig
> +++ b/drivers/media/platform/Kconfig
> @@ -32,6 +32,14 @@ source "drivers/media/platform/davinci/Kconfig"
>
>  source "drivers/media/platform/omap/Kconfig"
>
> +config VIDEO_ASPEED
> +       tristate "Aspeed AST2400 and AST2500 Video Engine driver"
> +       depends on VIDEO_V4L2

It seems you are not using videobuf2. I think it should simplify the read
I/O part and at the same time expose the other capture methods.

There are plenty of examples to follow.

Regards,
Eze

^ permalink raw reply

* [PATCH 4/4] media: platform: Add Aspeed Video Engine driver
From: Eddie James @ 2018-08-29 21:09 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-1-git-send-email-eajames@linux.vnet.ibm.com>

The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting a service processor, the Video Engine can capture
the host processor graphics output.

Add a V4L2 driver to capture video data and compress it to JPEG images,
making the data available through a standard read interface.

Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
 drivers/media/platform/Kconfig        |    8 +
 drivers/media/platform/Makefile       |    1 +
 drivers/media/platform/aspeed-video.c | 1307 +++++++++++++++++++++++++++++++++
 3 files changed, 1316 insertions(+)
 create mode 100644 drivers/media/platform/aspeed-video.c

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 94c1fe0..e599245 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -32,6 +32,14 @@ source "drivers/media/platform/davinci/Kconfig"
 
 source "drivers/media/platform/omap/Kconfig"
 
+config VIDEO_ASPEED
+	tristate "Aspeed AST2400 and AST2500 Video Engine driver"
+	depends on VIDEO_V4L2
+	help
+	  Support for the Aspeed Video Engine (VE) embedded in the Aspeed
+	  AST2400 and AST2500 SOCs. The VE can capture and compress video data
+	  from digital or analog sources.
+
 config VIDEO_SH_VOU
 	tristate "SuperH VOU video output driver"
 	depends on MEDIA_CAMERA_SUPPORT
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 41322ab..205c33a 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -3,6 +3,7 @@
 # Makefile for the video capture/playback device drivers.
 #
 
+obj-$(CONFIG_VIDEO_ASPEED)		+= aspeed-video.o
 obj-$(CONFIG_VIDEO_CADENCE)		+= cadence/
 obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
 obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c
new file mode 100644
index 0000000..4f36a3f
--- /dev/null
+++ b/drivers/media/platform/aspeed-video.c
@@ -0,0 +1,1307 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ASPEED Video Engine Driver
+ *
+ * Copyright 2018 IBM Corp.
+ *
+ * Eddie James <eajames@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/atomic.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+
+#define DEVICE_NAME			"aspeed-video"
+
+#define ASPEED_VIDEO_JPEG_NUM_QUALITIES	12
+#define ASPEED_VIDEO_JPEG_HEADER_SIZE	10
+#define ASPEED_VIDEO_JPEG_QUANT_SIZE	116
+#define ASPEED_VIDEO_JPEG_DCT_SIZE	34
+
+#define NUM_POLARITY_CHECKS		10
+#define INVALID_RESOLUTION_RETRIES	1
+#define INVALID_RESOLUTION_DELAY	msecs_to_jiffies(250)
+#define RESOLUTION_CHANGE_DELAY		msecs_to_jiffies(500)
+#define MODE_DETECT_TIMEOUT		msecs_to_jiffies(500)
+#define DIRECT_FETCH_THRESHOLD		0x0c0000 /* 1024 * 768, 32bpp */
+
+#define VE_SRC_BUFFER_SIZE		0x900000 /* 1920 * 1200, 32bpp */
+#define VE_COMP_BUFFER_SIZE		0x100000 /* 128K packet * 8 packets */
+#define VE_JPEG_BUFFER_SIZE		0x006000 /* 512 * 12 * 4 */
+
+#define VE_PROTECTION_KEY		0x000
+#define  VE_PROTECTION_KEY_UNLOCK	0x1A038AA8
+
+#define VE_SEQ_CTRL			0x004
+#define  VE_SEQ_CTRL_TRIG_MODE_DET	BIT(0)
+#define  VE_SEQ_CTRL_TRIG_CAPTURE	BIT(1)
+#define  VE_SEQ_CTRL_FORCE_IDLE		BIT(2)
+#define  VE_SEQ_CTRL_MULT_FRAME		BIT(3)
+#define  VE_SEQ_CTRL_TRIG_COMP		BIT(4)
+#define  VE_SEQ_CTRL_AUTO_COMP		BIT(5)
+#define  VE_SEQ_CTRL_EN_WATCHDOG	BIT(7)
+#define  VE_SEQ_CTRL_YUV420		BIT(10)
+#define  VE_SEQ_CTRL_COMP_FMT		GENMASK(11, 10)
+#define  VE_SEQ_CTRL_HALT		BIT(12)
+#define  VE_SEQ_CTRL_EN_WATCHDOG_COMP	BIT(14)
+#define  VE_SEQ_CTRL_TRIG_JPG		BIT(15)
+#define  VE_SEQ_CTRL_CAP_BUSY		BIT(16)
+#define  VE_SEQ_CTRL_COMP_BUSY		BIT(18)
+
+#ifdef CONFIG_MACH_ASPEED_G5
+#define  VE_SEQ_CTRL_JPEG_MODE		BIT(13)	/* AST2500 */
+#else
+#define  VE_SEQ_CTRL_JPEG_MODE		BIT(8)	/* AST2400 */
+#endif /* CONFIG_MACH_ASPEED_G5 */
+
+#define VE_CTRL				0x008
+#define  VE_CTRL_HSYNC_POL		BIT(0)
+#define  VE_CTRL_VSYNC_POL		BIT(1)
+#define  VE_CTRL_SOURCE			BIT(2)
+#define  VE_CTRL_INT_DE			BIT(4)
+#define  VE_CTRL_DIRECT_FETCH		BIT(5)
+#define  VE_CTRL_YUV			BIT(6)
+#define  VE_CTRL_RGB			BIT(7)
+#define  VE_CTRL_CAPTURE_FMT		GENMASK(7, 6)
+#define  VE_CTRL_AUTO_OR_CURSOR		BIT(8)
+#define  VE_CTRL_CLK_INVERSE		BIT(11)
+#define  VE_CTRL_CLK_DELAY		GENMASK(11, 9)
+#define  VE_CTRL_INTERLACE		BIT(14)
+#define  VE_CTRL_HSYNC_POL_CTRL		BIT(15)
+#define  VE_CTRL_FRC			GENMASK(23, 16)
+
+#define VE_TGS_0			0x00c
+#define VE_TGS_1			0x010
+#define  VE_TGS_FIRST			GENMASK(28, 16)
+#define  VE_TGS_LAST			GENMASK(12, 0)
+
+#define VE_SCALING_FACTOR		0x014
+#define VE_SCALING_FILTER0		0x018
+#define VE_SCALING_FILTER1		0x01c
+#define VE_SCALING_FILTER2		0x020
+#define VE_SCALING_FILTER3		0x024
+
+#define VE_CAP_WINDOW			0x030
+#define VE_COMP_WINDOW			0x034
+#define VE_COMP_PROC_OFFSET		0x038
+#define VE_COMP_OFFSET			0x03c
+#define VE_JPEG_ADDR			0x040
+#define VE_SRC0_ADDR			0x044
+#define VE_SRC_SCANLINE_OFFSET		0x048
+#define VE_SRC1_ADDR			0x04c
+#define VE_COMP_ADDR			0x054
+
+#define VE_STREAM_BUF_SIZE		0x058
+#define  VE_STREAM_BUF_SIZE_N_PACKETS	GENMASK(5, 3)
+#define  VE_STREAM_BUF_SIZE_P_SIZE	GENMASK(2, 0)
+
+#define VE_COMP_CTRL			0x060
+#define  VE_COMP_CTRL_VQ_DCT_ONLY	BIT(0)
+#define  VE_COMP_CTRL_VQ_4COLOR		BIT(1)
+#define  VE_COMP_CTRL_QUANTIZE		BIT(2)
+#define  VE_COMP_CTRL_EN_BQ		BIT(4)
+#define  VE_COMP_CTRL_EN_CRYPTO		BIT(5)
+#define  VE_COMP_CTRL_DCT_CHR		GENMASK(10, 6)
+#define  VE_COMP_CTRL_DCT_LUM		GENMASK(15, 11)
+#define  VE_COMP_CTRL_EN_HQ		BIT(16)
+#define  VE_COMP_CTRL_RSVD		BIT(19)
+#define  VE_COMP_CTRL_ENCODE		GENMASK(21, 20)
+#define  VE_COMP_CTRL_HQ_DCT_CHR	GENMASK(26, 22)
+#define  VE_COMP_CTRL_HQ_DCT_LUM	GENMASK(31, 27)
+
+#define VE_OFFSET_COMP_STREAM		0x078
+
+#define VE_SRC_LR_EDGE_DET		0x090
+#define  VE_SRC_LR_EDGE_DET_LEFT	GENMASK(11, 0)
+#define  VE_SRC_LR_EDGE_DET_NO_V	BIT(12)
+#define  VE_SRC_LR_EDGE_DET_NO_H	BIT(13)
+#define  VE_SRC_LR_EDGE_DET_NO_DISP	BIT(14)
+#define  VE_SRC_LR_EDGE_DET_NO_CLK	BIT(15)
+#define  VE_SRC_LR_EDGE_DET_RT_SHF	16
+#define  VE_SRC_LR_EDGE_DET_RT		GENMASK(27, VE_SRC_LR_EDGE_DET_RT_SHF)
+#define  VE_SRC_LR_EDGE_DET_INTERLACE	BIT(31)
+
+#define VE_SRC_TB_EDGE_DET		0x094
+#define  VE_SRC_TB_EDGE_DET_TOP		GENMASK(12, 0)
+#define  VE_SRC_TB_EDGE_DET_BOT_SHF	16
+#define  VE_SRC_TB_EDGE_DET_BOT		GENMASK(28, VE_SRC_TB_EDGE_DET_BOT_SHF)
+
+#define VE_MODE_DETECT_STATUS		0x098
+#define  VE_MODE_DETECT_STATUS_VSYNC	BIT(28)
+#define  VE_MODE_DETECT_STATUS_HSYNC	BIT(29)
+
+#define VE_INTERRUPT_CTRL		0x304
+#define VE_INTERRUPT_STATUS		0x308
+#define  VE_INTERRUPT_MODE_DETECT_WD	BIT(0)
+#define  VE_INTERRUPT_CAPTURE_COMPLETE	BIT(1)
+#define  VE_INTERRUPT_COMP_READY	BIT(2)
+#define  VE_INTERRUPT_COMP_COMPLETE	BIT(3)
+#define  VE_INTERRUPT_MODE_DETECT	BIT(4)
+#define  VE_INTERRUPT_FRAME_COMPLETE	BIT(5)
+#define  VE_INTERRUPT_DECODE_ERR	BIT(6)
+#define  VE_INTERRUPT_HALT_READY	BIT(8)
+#define  VE_INTERRUPT_HANG_WD		BIT(9)
+#define  VE_INTERRUPT_STREAM_DESC	BIT(10)
+#define  VE_INTERRUPT_VSYNC_DESC	BIT(11)
+
+#define VE_MODE_DETECT			0x30c
+#define VE_MEM_RESTRICT_START		0x310
+#define VE_MEM_RESTRICT_END		0x314
+
+enum {
+	VIDEO_MODE_DETECT_DONE,
+	VIDEO_RES_CHANGE,
+	VIDEO_FRAME_AVAILABLE,
+	VIDEO_FRAME_TRIGGERED,
+};
+
+struct aspeed_video_addr {
+	dma_addr_t dma;
+	void *virt;
+};
+
+struct aspeed_video {
+	void __iomem *base;
+	struct clk *eclk;
+	struct clk *vclk;
+	struct reset_control *rst;
+
+	struct device *dev;
+	struct v4l2_device v4l2_dev;
+	struct video_device vdev;
+	struct mutex video_lock;
+
+	atomic_t clients;
+	wait_queue_head_t wait;
+	struct delayed_work res_work;
+	unsigned long flags;
+
+	int frame_idx;
+	u32 frame_size;
+
+	dma_addr_t max;
+	dma_addr_t min;
+	struct aspeed_video_addr srcs[2];
+	struct aspeed_video_addr comp[2];
+	struct aspeed_video_addr jpeg;
+
+	int frame_rate;
+	int jpeg_quality;
+	struct v4l2_pix_format fmt;
+};
+
+#define to_aspeed_video(x) container_of((x), struct aspeed_video, v4l2_dev)
+
+static const u32 aspeed_video_jpeg_header[ASPEED_VIDEO_JPEG_HEADER_SIZE] = {
+	0xE0FFD8FF, 0x464A1000, 0x01004649, 0x60000101, 0x00006000, 0x0F00FEFF,
+	0x00002D05, 0x00000000, 0x00000000, 0x00DBFF00
+};
+
+static const u32 aspeed_video_jpeg_quant[ASPEED_VIDEO_JPEG_QUANT_SIZE] = {
+	0x081100C0, 0x00000000, 0x00110103, 0x03011102, 0xC4FF0111, 0x00001F00,
+	0x01010501, 0x01010101, 0x00000000, 0x00000000, 0x04030201, 0x08070605,
+	0xFF0B0A09, 0x10B500C4, 0x03010200, 0x03040203, 0x04040505, 0x7D010000,
+	0x00030201, 0x12051104, 0x06413121, 0x07615113, 0x32147122, 0x08A19181,
+	0xC1B14223, 0xF0D15215, 0x72623324, 0x160A0982, 0x1A191817, 0x28272625,
+	0x35342A29, 0x39383736, 0x4544433A, 0x49484746, 0x5554534A, 0x59585756,
+	0x6564635A, 0x69686766, 0x7574736A, 0x79787776, 0x8584837A, 0x89888786,
+	0x9493928A, 0x98979695, 0xA3A29A99, 0xA7A6A5A4, 0xB2AAA9A8, 0xB6B5B4B3,
+	0xBAB9B8B7, 0xC5C4C3C2, 0xC9C8C7C6, 0xD4D3D2CA, 0xD8D7D6D5, 0xE2E1DAD9,
+	0xE6E5E4E3, 0xEAE9E8E7, 0xF4F3F2F1, 0xF8F7F6F5, 0xC4FFFAF9, 0x00011F00,
+	0x01010103, 0x01010101, 0x00000101, 0x00000000, 0x04030201, 0x08070605,
+	0xFF0B0A09, 0x11B500C4, 0x02010200, 0x04030404, 0x04040507, 0x77020100,
+	0x03020100, 0x21050411, 0x41120631, 0x71610751, 0x81322213, 0x91421408,
+	0x09C1B1A1, 0xF0523323, 0xD1726215, 0x3424160A, 0x17F125E1, 0x261A1918,
+	0x2A292827, 0x38373635, 0x44433A39, 0x48474645, 0x54534A49, 0x58575655,
+	0x64635A59, 0x68676665, 0x74736A69, 0x78777675, 0x83827A79, 0x87868584,
+	0x928A8988, 0x96959493, 0x9A999897, 0xA5A4A3A2, 0xA9A8A7A6, 0xB4B3B2AA,
+	0xB8B7B6B5, 0xC3C2BAB9, 0xC7C6C5C4, 0xD2CAC9C8, 0xD6D5D4D3, 0xDAD9D8D7,
+	0xE5E4E3E2, 0xE9E8E7E6, 0xF4F3F2EA, 0xF8F7F6F5, 0xDAFFFAF9, 0x01030C00,
+	0x03110200, 0x003F0011
+};
+
+static const u32 aspeed_video_jpeg_dct[ASPEED_VIDEO_JPEG_NUM_QUALITIES]
+				      [ASPEED_VIDEO_JPEG_DCT_SIZE] = {
+	{ 0x0D140043, 0x0C0F110F, 0x11101114, 0x17141516, 0x1E20321E,
+	  0x3D1E1B1B, 0x32242E2B, 0x4B4C3F48, 0x44463F47, 0x61735A50,
+	  0x566C5550, 0x88644644, 0x7A766C65, 0x4D808280, 0x8C978D60,
+	  0x7E73967D, 0xDBFF7B80, 0x1F014300, 0x272D2121, 0x3030582D,
+	  0x697BB958, 0xB8B9B97B, 0xB9B8A6A6, 0xB9B9B9B9, 0xB9B9B9B9,
+	  0xB9B9B9B9, 0xB9B9B9B9, 0xB9B9B9B9, 0xB9B9B9B9, 0xB9B9B9B9,
+	  0xB9B9B9B9, 0xB9B9B9B9, 0xB9B9B9B9, 0xFFB9B9B9 },
+	{ 0x0C110043, 0x0A0D0F0D, 0x0F0E0F11, 0x14111213, 0x1A1C2B1A,
+	  0x351A1818, 0x2B1F2826, 0x4142373F, 0x3C3D373E, 0x55644E46,
+	  0x4B5F4A46, 0x77573D3C, 0x6B675F58, 0x43707170, 0x7A847B54,
+	  0x6E64836D, 0xDBFF6C70, 0x1B014300, 0x22271D1D, 0x2A2A4C27,
+	  0x5B6BA04C, 0xA0A0A06B, 0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0,
+	  0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0,
+	  0xA0A0A0A0, 0xA0A0A0A0, 0xA0A0A0A0, 0xFFA0A0A0 },
+	{ 0x090E0043, 0x090A0C0A, 0x0C0B0C0E, 0x110E0F10, 0x15172415,
+	  0x2C151313, 0x241A211F, 0x36372E34, 0x31322E33, 0x4653413A,
+	  0x3E4E3D3A, 0x62483231, 0x58564E49, 0x385D5E5D, 0x656D6645,
+	  0x5B536C5A, 0xDBFF595D, 0x16014300, 0x1C201818, 0x22223F20,
+	  0x4B58853F, 0x85858558, 0x85858585, 0x85858585, 0x85858585,
+	  0x85858585, 0x85858585, 0x85858585, 0x85858585, 0x85858585,
+	  0x85858585, 0x85858585, 0x85858585, 0xFF858585 },
+	{ 0x070B0043, 0x07080A08, 0x0A090A0B, 0x0D0B0C0C, 0x11121C11,
+	  0x23110F0F, 0x1C141A19, 0x2B2B2429, 0x27282428, 0x3842332E,
+	  0x313E302E, 0x4E392827, 0x46443E3A, 0x2C4A4A4A, 0x50565137,
+	  0x48425647, 0xDBFF474A, 0x12014300, 0x161A1313, 0x1C1C331A,
+	  0x3D486C33, 0x6C6C6C48, 0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C,
+	  0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C,
+	  0x6C6C6C6C, 0x6C6C6C6C, 0x6C6C6C6C, 0xFF6C6C6C },
+	{ 0x06090043, 0x05060706, 0x07070709, 0x0A09090A, 0x0D0E160D,
+	  0x1B0D0C0C, 0x16101413, 0x21221C20, 0x1E1F1C20, 0x2B332824,
+	  0x26302624, 0x3D2D1F1E, 0x3735302D, 0x22393A39, 0x3F443F2B,
+	  0x38334338, 0xDBFF3739, 0x0D014300, 0x11130E0E, 0x15152613,
+	  0x2D355026, 0x50505035, 0x50505050, 0x50505050, 0x50505050,
+	  0x50505050, 0x50505050, 0x50505050, 0x50505050, 0x50505050,
+	  0x50505050, 0x50505050, 0x50505050, 0xFF505050 },
+	{ 0x04060043, 0x03040504, 0x05040506, 0x07060606, 0x09090F09,
+	  0x12090808, 0x0F0A0D0D, 0x16161315, 0x14151315, 0x1D221B18,
+	  0x19201918, 0x281E1514, 0x2423201E, 0x17262726, 0x2A2D2A1C,
+	  0x25222D25, 0xDBFF2526, 0x09014300, 0x0B0D0A0A, 0x0E0E1A0D,
+	  0x1F25371A, 0x37373725, 0x37373737, 0x37373737, 0x37373737,
+	  0x37373737, 0x37373737, 0x37373737, 0x37373737, 0x37373737,
+	  0x37373737, 0x37373737, 0x37373737, 0xFF373737 },
+	{ 0x02030043, 0x01020202, 0x02020203, 0x03030303, 0x04040704,
+	  0x09040404, 0x07050606, 0x0B0B090A, 0x0A0A090A, 0x0E110D0C,
+	  0x0C100C0C, 0x140F0A0A, 0x1211100F, 0x0B131313, 0x1516150E,
+	  0x12111612, 0xDBFF1213, 0x04014300, 0x05060505, 0x07070D06,
+	  0x0F121B0D, 0x1B1B1B12, 0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B,
+	  0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B,
+	  0x1B1B1B1B, 0x1B1B1B1B, 0x1B1B1B1B, 0xFF1B1B1B },
+	{ 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503,
+	  0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090B0908,
+	  0x080A0808, 0x0D0A0706, 0x0C0B0A0A, 0x070C0D0C, 0x0E0F0E09,
+	  0x0C0B0F0C, 0xDBFF0C0C, 0x03014300, 0x03040303, 0x04040804,
+	  0x0A0C1208, 0x1212120C, 0x12121212, 0x12121212, 0x12121212,
+	  0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212,
+	  0x12121212, 0x12121212, 0x12121212, 0xFF121212 },
+	{ 0x01020043, 0x01010101, 0x01010102, 0x02020202, 0x03030503,
+	  0x06030202, 0x05030404, 0x07070607, 0x06070607, 0x090B0908,
+	  0x080A0808, 0x0D0A0706, 0x0C0B0A0A, 0x070C0D0C, 0x0E0F0E09,
+	  0x0C0B0F0C, 0xDBFF0C0C, 0x02014300, 0x03030202, 0x04040703,
+	  0x080A0F07, 0x0F0F0F0A, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
+	  0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F,
+	  0x0F0F0F0F, 0x0F0F0F0F, 0x0F0F0F0F, 0xFF0F0F0F },
+	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x02020302,
+	  0x04020202, 0x03020303, 0x05050405, 0x05050405, 0x07080606,
+	  0x06080606, 0x0A070505, 0x09080807, 0x05090909, 0x0A0B0A07,
+	  0x09080B09, 0xDBFF0909, 0x02014300, 0x02030202, 0x03030503,
+	  0x07080C05, 0x0C0C0C08, 0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C,
+	  0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C,
+	  0x0C0C0C0C, 0x0C0C0C0C, 0x0C0C0C0C, 0xFF0C0C0C },
+	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010201,
+	  0x03010101, 0x02010202, 0x03030303, 0x03030303, 0x04050404,
+	  0x04050404, 0x06050303, 0x06050505, 0x03060606, 0x07070704,
+	  0x06050706, 0xDBFF0606, 0x01014300, 0x01020101, 0x02020402,
+	  0x05060904, 0x09090906, 0x09090909, 0x09090909, 0x09090909,
+	  0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909,
+	  0x09090909, 0x09090909, 0x09090909, 0xFF090909 },
+	{ 0x01010043, 0x01010101, 0x01010101, 0x01010101, 0x01010101,
+	  0x01010101, 0x01010101, 0x01010101, 0x01010101, 0x02020202,
+	  0x02020202, 0x03020101, 0x03020202, 0x01030303, 0x03030302,
+	  0x03020303, 0xDBFF0403, 0x01014300, 0x01010101, 0x01010201,
+	  0x03040602, 0x06060604, 0x06060606, 0x06060606, 0x06060606,
+	  0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606,
+	  0x06060606, 0x06060606, 0x06060606, 0xFF060606 }
+};
+
+static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420)
+{
+	int i;
+	unsigned int base;
+
+	for (i = 0; i < ASPEED_VIDEO_JPEG_NUM_QUALITIES; i++) {
+		int j;
+
+		base = 256 * i;	/* AST HW requires this header spacing */
+
+		for (j = 0; j < ASPEED_VIDEO_JPEG_HEADER_SIZE; j++)
+			table[base + j] =
+				le32_to_cpu(aspeed_video_jpeg_header[j]);
+
+		base += ASPEED_VIDEO_JPEG_HEADER_SIZE;
+		for (j = 0; j < ASPEED_VIDEO_JPEG_DCT_SIZE; j++)
+			table[base + j] =
+				le32_to_cpu(aspeed_video_jpeg_dct[i][j]);
+
+		base += ASPEED_VIDEO_JPEG_DCT_SIZE;
+		for (j = 0; j < ASPEED_VIDEO_JPEG_QUANT_SIZE; j++)
+			table[base + j] =
+				le32_to_cpu(aspeed_video_jpeg_quant[j]);
+
+		if (yuv420)
+			table[base + 2] = le32_to_cpu(0x00220103);
+	}
+}
+
+static void aspeed_video_update(struct aspeed_video *video, u32 reg,
+				unsigned long mask, u32 bits)
+{
+	u32 t = readl(video->base + reg);
+	u32 before = t;
+
+	t &= mask;
+	t |= bits;
+	writel(t, video->base + reg);
+	dev_dbg(video->dev, "update %03x[%08x -> %08x]\n", reg, before,
+		readl(video->base + reg));
+}
+
+static u32 aspeed_video_read(struct aspeed_video *video, u32 reg)
+{
+	u32 t = readl(video->base + reg);
+
+	dev_dbg(video->dev, "read %03x[%08x]\n", reg, t);
+	return t;
+}
+
+static void aspeed_video_write(struct aspeed_video *video, u32 reg, u32 val)
+{
+	writel(val, video->base + reg);
+	dev_dbg(video->dev, "write %03x[%08x]\n", reg,
+		readl(video->base + reg));
+}
+
+static bool aspeed_video_engine_busy(struct aspeed_video *video)
+{
+	u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL);
+
+	if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) ||
+	    !(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) {
+		dev_info(video->dev, "video engine busy\n");
+		return true;
+	}
+
+	return false;
+}
+
+static int aspeed_video_start_frame(struct aspeed_video *video)
+{
+	if (aspeed_video_engine_busy(video))
+		return -EBUSY;
+
+	video->frame_idx = (video->frame_idx + 1) % 2;
+
+	aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0);
+	aspeed_video_write(video, VE_COMP_OFFSET, 0);
+	aspeed_video_write(video, VE_COMP_ADDR,
+			   video->comp[video->frame_idx].dma);
+
+	set_bit(VIDEO_FRAME_TRIGGERED, &video->flags);
+
+	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0xFFFFFFFF,
+			    VE_INTERRUPT_COMP_COMPLETE |
+			    VE_INTERRUPT_CAPTURE_COMPLETE);
+
+	aspeed_video_update(video, VE_SEQ_CTRL, 0xFFFFFFFF,
+			    VE_SEQ_CTRL_TRIG_CAPTURE | VE_SEQ_CTRL_TRIG_COMP);
+
+	return 0;
+}
+
+static void aspeed_video_start_mode_detect(struct aspeed_video *video)
+{
+	/* Enable mode detect interrupts */
+	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0xFFFFFFFF,
+			    VE_INTERRUPT_MODE_DETECT);
+
+	/* Trigger mode detect */
+	aspeed_video_update(video, VE_SEQ_CTRL, 0xFFFFFFFF,
+			    VE_SEQ_CTRL_TRIG_MODE_DET);
+}
+
+static void aspeed_video_disable_mode_detect(struct aspeed_video *video)
+{
+	/* Disable mode detect interrupts */
+	aspeed_video_update(video, VE_INTERRUPT_CTRL,
+			    ~VE_INTERRUPT_MODE_DETECT, 0);
+
+	/* Disable mode detect */
+	aspeed_video_update(video, VE_SEQ_CTRL, ~VE_SEQ_CTRL_TRIG_MODE_DET, 0);
+}
+
+static void aspeed_video_off(struct aspeed_video *video)
+{
+	/* Reset the engine */
+	reset_control_assert(video->rst);
+	udelay(100);
+	reset_control_deassert(video->rst);
+
+	/* Turn off the relevant clocks */
+	clk_disable_unprepare(video->vclk);
+	clk_disable_unprepare(video->eclk);
+}
+
+static void aspeed_video_on(struct aspeed_video *video)
+{
+	/* Turn on the relevant clocks */
+	clk_prepare_enable(video->eclk);
+	clk_prepare_enable(video->vclk);
+
+	/* Reset the engine */
+	reset_control_assert(video->rst);
+	udelay(100);
+	reset_control_deassert(video->rst);
+}
+
+static irqreturn_t aspeed_video_irq(int irq, void *arg)
+{
+	struct aspeed_video *video = arg;
+	u32 sts = aspeed_video_read(video, VE_INTERRUPT_STATUS);
+
+	if (atomic_read(&video->clients) == 0) {
+		dev_info(video->dev, "irq with no client; disabling irqs\n");
+
+		aspeed_video_write(video, VE_INTERRUPT_CTRL, 0);
+		aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xFFFFFFFF);
+		return IRQ_HANDLED;
+	}
+
+	/* Resolution changed; reset entire engine and reinitialize */
+	if (sts & VE_INTERRUPT_MODE_DETECT_WD) {
+		dev_info(video->dev, "resolution changed; resetting\n");
+		set_bit(VIDEO_RES_CHANGE, &video->flags);
+
+		aspeed_video_off(video);
+
+		schedule_delayed_work(&video->res_work,
+				      RESOLUTION_CHANGE_DELAY);
+		return IRQ_HANDLED;
+	}
+
+	if (sts & VE_INTERRUPT_MODE_DETECT) {
+		aspeed_video_update(video, VE_INTERRUPT_CTRL,
+				    ~VE_INTERRUPT_MODE_DETECT, 0);
+		aspeed_video_write(video, VE_INTERRUPT_STATUS,
+				   VE_INTERRUPT_MODE_DETECT);
+
+		set_bit(VIDEO_MODE_DETECT_DONE, &video->flags);
+		wake_up_interruptible_all(&video->wait);
+	}
+
+	if ((sts & VE_INTERRUPT_COMP_COMPLETE) &&
+	    (sts & VE_INTERRUPT_CAPTURE_COMPLETE)) {
+		video->frame_size = aspeed_video_read(video,
+						      VE_OFFSET_COMP_STREAM);
+
+		aspeed_video_update(video, VE_INTERRUPT_CTRL,
+				    ~(VE_INTERRUPT_COMP_COMPLETE |
+				      VE_INTERRUPT_CAPTURE_COMPLETE), 0);
+		aspeed_video_write(video, VE_INTERRUPT_STATUS,
+				   VE_INTERRUPT_COMP_COMPLETE |
+				   VE_INTERRUPT_CAPTURE_COMPLETE);
+		aspeed_video_update(video, VE_SEQ_CTRL,
+				    ~(VE_SEQ_CTRL_TRIG_CAPTURE |
+				      VE_SEQ_CTRL_FORCE_IDLE |
+				      VE_SEQ_CTRL_TRIG_COMP), 0);
+
+		set_bit(VIDEO_FRAME_AVAILABLE, &video->flags);
+		clear_bit(VIDEO_FRAME_TRIGGERED, &video->flags);
+		wake_up_interruptible_all(&video->wait);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void aspeed_video_check_polarity(struct aspeed_video *video)
+{
+	int i;
+	int hsync_counter = 0;
+	int vsync_counter = 0;
+	u32 sts;
+
+	for (i = 0; i < NUM_POLARITY_CHECKS; ++i) {
+		sts = aspeed_video_read(video, VE_MODE_DETECT_STATUS);
+		if (sts & VE_MODE_DETECT_STATUS_VSYNC)
+			vsync_counter--;
+		else
+			vsync_counter++;
+
+		if (sts & VE_MODE_DETECT_STATUS_HSYNC)
+			hsync_counter--;
+		else
+			hsync_counter++;
+	}
+
+	if (hsync_counter < 0 || vsync_counter < 0) {
+		u32 ctrl;
+
+		if (hsync_counter < 0)
+			ctrl = VE_CTRL_HSYNC_POL;
+
+		if (vsync_counter < 0)
+			ctrl = VE_CTRL_VSYNC_POL;
+
+		aspeed_video_update(video, VE_CTRL, 0xFFFFFFFF, ctrl);
+	}
+}
+
+#define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags)
+
+static int aspeed_video_get_resolution(struct aspeed_video *video)
+{
+	bool invalid_resolution = true;
+	int rc;
+	int tries = 0;
+	unsigned int bottom;
+	unsigned int left;
+	unsigned int right;
+	unsigned int top;
+	u32 src_lr_edge;
+	u32 src_tb_edge;
+
+	video->fmt.width = 0;
+	video->fmt.height = 0;
+
+	do {
+		if (tries) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (schedule_timeout(INVALID_RESOLUTION_DELAY))
+				return -EINTR;
+		}
+
+		aspeed_video_start_mode_detect(video);
+
+		rc = wait_event_interruptible_timeout(video->wait,
+						      res_check(video),
+						      MODE_DETECT_TIMEOUT);
+		if (!rc) {
+			dev_err(video->dev, "timed out on 1st mode detect\n");
+			aspeed_video_disable_mode_detect(video);
+			return -ETIME;
+		}
+
+		/* Disable mode detect in order to re-trigger */
+		aspeed_video_update(video, VE_SEQ_CTRL,
+				    ~VE_SEQ_CTRL_TRIG_MODE_DET, 0);
+
+		aspeed_video_check_polarity(video);
+
+		aspeed_video_start_mode_detect(video);
+
+		rc = wait_event_interruptible_timeout(video->wait,
+						      res_check(video),
+						      MODE_DETECT_TIMEOUT);
+		if (!rc) {
+			dev_err(video->dev, "timed out on 2nd mode detect\n");
+			aspeed_video_disable_mode_detect(video);
+			return -ETIME;
+		}
+
+		src_lr_edge = aspeed_video_read(video, VE_SRC_LR_EDGE_DET);
+		src_tb_edge = aspeed_video_read(video, VE_SRC_TB_EDGE_DET);
+
+		bottom = (src_tb_edge & VE_SRC_TB_EDGE_DET_BOT) >>
+			VE_SRC_TB_EDGE_DET_BOT_SHF;
+		top = src_tb_edge & VE_SRC_TB_EDGE_DET_TOP;
+		if (top > bottom)
+			continue;
+
+		right = (src_lr_edge & VE_SRC_LR_EDGE_DET_RT) >>
+			VE_SRC_LR_EDGE_DET_RT_SHF;
+		left = src_lr_edge & VE_SRC_LR_EDGE_DET_LEFT;
+		if (left > right)
+			continue;
+
+		invalid_resolution = false;
+	} while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES));
+
+	if (invalid_resolution) {
+		dev_err(video->dev, "invalid resolution detected\n");
+		return -EMSGSIZE;
+	}
+
+	video->fmt.height = (bottom - top) + 1;
+	video->fmt.width = (right - left) + 1;
+
+	/* Don't use direct mode below 1024 x 768 (irqs don't fire) */
+	if (video->fmt.height * video->fmt.width < DIRECT_FETCH_THRESHOLD) {
+		aspeed_video_write(video, VE_TGS_0,
+				   FIELD_PREP(VE_TGS_FIRST, left - 1) |
+				   FIELD_PREP(VE_TGS_LAST, right));
+		aspeed_video_write(video, VE_TGS_1,
+				   FIELD_PREP(VE_TGS_FIRST, top) |
+				   FIELD_PREP(VE_TGS_LAST, bottom + 1));
+		aspeed_video_update(video, VE_CTRL,
+				    ~VE_CTRL_DIRECT_FETCH, VE_CTRL_INT_DE);
+	}
+
+	aspeed_video_write(video, VE_CAP_WINDOW,
+			   video->fmt.width << 16 | video->fmt.height);
+	aspeed_video_write(video, VE_COMP_WINDOW,
+			   video->fmt.width << 16 | video->fmt.height);
+	aspeed_video_write(video, VE_SRC_SCANLINE_OFFSET,
+			   video->fmt.width * 4);
+
+	aspeed_video_update(video, VE_INTERRUPT_CTRL, 0xFFFFFFFF,
+			    VE_INTERRUPT_MODE_DETECT_WD);
+	aspeed_video_update(video, VE_SEQ_CTRL, 0xFFFFFFFF,
+			    VE_SEQ_CTRL_EN_WATCHDOG);
+
+	dev_dbg(video->dev, "got resolution[%dx%d]\n", video->fmt.width,
+		video->fmt.height);
+
+	return 0;
+}
+
+static void aspeed_video_init_regs(struct aspeed_video *video)
+{
+	u32 comp_ctrl = VE_COMP_CTRL_RSVD |
+		FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
+		FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10);
+	u32 ctrl = VE_CTRL_DIRECT_FETCH | VE_CTRL_AUTO_OR_CURSOR;
+	u32 seq_ctrl = VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_JPEG_MODE;
+
+	if (video->frame_rate)
+		ctrl |= FIELD_PREP(VE_CTRL_FRC, video->frame_rate);
+
+	if (video->fmt.pixelformat == V4L2_PIX_FMT_YUV420)
+		seq_ctrl |= VE_SEQ_CTRL_YUV420;
+
+	/* Unlock VE registers */
+	aspeed_video_write(video, VE_PROTECTION_KEY, VE_PROTECTION_KEY_UNLOCK);
+
+	/* Disable interrupts */
+	aspeed_video_write(video, VE_INTERRUPT_CTRL, 0);
+	aspeed_video_write(video, VE_INTERRUPT_STATUS, 0xFFFFFFFF);
+
+	/* Clear the offset */
+	aspeed_video_write(video, VE_COMP_PROC_OFFSET, 0);
+	aspeed_video_write(video, VE_COMP_OFFSET, 0);
+
+	/* Set memory restrictions */
+	aspeed_video_write(video, VE_MEM_RESTRICT_START, video->min);
+	aspeed_video_write(video, VE_MEM_RESTRICT_END, video->max);
+
+	/* Set buffer addresses */
+	aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma);
+	aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma);
+	aspeed_video_write(video, VE_COMP_ADDR, video->comp[0].dma);
+	aspeed_video_write(video, VE_JPEG_ADDR, video->jpeg.dma);
+
+	/* Set control registers */
+	aspeed_video_write(video, VE_SEQ_CTRL, seq_ctrl);
+	aspeed_video_write(video, VE_CTRL, ctrl);
+	aspeed_video_write(video, VE_COMP_CTRL, comp_ctrl);
+
+	/* Compression buffer size; 128K packet * 8 packets */
+	aspeed_video_write(video, VE_STREAM_BUF_SIZE, 0xf);
+
+	/* Don't downscale */
+	aspeed_video_write(video, VE_SCALING_FACTOR, 0x10001000);
+	aspeed_video_write(video, VE_SCALING_FILTER0, 0x00200000);
+	aspeed_video_write(video, VE_SCALING_FILTER1, 0x00200000);
+	aspeed_video_write(video, VE_SCALING_FILTER2, 0x00200000);
+	aspeed_video_write(video, VE_SCALING_FILTER3, 0x00200000);
+
+	/* Set mode detection defaults */
+	aspeed_video_write(video, VE_MODE_DETECT, 0x22666500);
+}
+
+static int aspeed_video_allocate_cma(struct aspeed_video *video)
+{
+	video->srcs[0].virt = dma_alloc_coherent(video->dev,
+						 VE_SRC_BUFFER_SIZE,
+						 &video->srcs[0].dma,
+						 GFP_KERNEL);
+	if (!video->srcs[0].virt) {
+		dev_err(video->dev,
+			"Failed to allocate source buffer 0, size[%x]\n",
+			VE_SRC_BUFFER_SIZE);
+		goto err;
+	}
+
+	video->srcs[1].virt = dma_alloc_coherent(video->dev,
+						 VE_SRC_BUFFER_SIZE,
+						 &video->srcs[1].dma,
+						 GFP_KERNEL);
+	if (!video->srcs[1].virt) {
+		dev_err(video->dev,
+			"Failed to allocate source buffer 1, size[%x]\n",
+			VE_SRC_BUFFER_SIZE);
+		goto free_src0;
+	}
+
+	video->comp[0].virt = dma_alloc_coherent(video->dev,
+						 VE_COMP_BUFFER_SIZE,
+						 &video->comp[0].dma,
+						 GFP_KERNEL);
+	if (!video->comp[0].virt) {
+		dev_err(video->dev,
+			"Failed to allocate compression buffer 0, size[%x]\n",
+			VE_COMP_BUFFER_SIZE);
+		goto free_src1;
+	}
+
+	video->comp[1].virt = dma_alloc_coherent(video->dev,
+						 VE_COMP_BUFFER_SIZE,
+						 &video->comp[1].dma,
+						 GFP_KERNEL);
+	if (!video->comp[0].virt) {
+		dev_err(video->dev,
+			"Failed to allocate compression buffer 1, size[%x]\n",
+			VE_COMP_BUFFER_SIZE);
+		goto free_comp0;
+	}
+
+	video->jpeg.virt = dma_alloc_coherent(video->dev, VE_JPEG_BUFFER_SIZE,
+					      &video->jpeg.dma, GFP_KERNEL);
+	if (!video->jpeg.virt) {
+		dev_err(video->dev,
+			"Failed to allocate JPEG buffer, size[%x]\n",
+			VE_JPEG_BUFFER_SIZE);
+		goto free_comp1;
+	}
+
+	if (video->fmt.pixelformat == V4L2_PIX_FMT_YUV420)
+		aspeed_video_init_jpeg_table(video->jpeg.virt, true);
+	else
+		aspeed_video_init_jpeg_table(video->jpeg.virt, false);
+
+	/*
+	 * Calculate the memory restrictions. Don't consider the JPEG header
+	 * buffer since HW doesn't need to write to it.
+	 */
+	video->max = max(video->srcs[0].dma + VE_SRC_BUFFER_SIZE,
+			 video->srcs[1].dma + VE_SRC_BUFFER_SIZE);
+	video->max = max(video->max, video->comp[0].dma + VE_COMP_BUFFER_SIZE);
+	video->max = max(video->max, video->comp[1].dma + VE_COMP_BUFFER_SIZE);
+
+	video->min = min(video->srcs[0].dma, video->srcs[1].dma);
+	video->min = min(video->min, video->comp[0].dma);
+	video->min = min(video->min, video->comp[1].dma);
+
+	return 0;
+
+free_comp1:
+	dma_free_coherent(video->dev, VE_COMP_BUFFER_SIZE, video->comp[1].virt,
+			  video->comp[1].dma);
+	video->comp[1].dma = 0ULL;
+	video->comp[1].virt = NULL;
+
+free_comp0:
+	dma_free_coherent(video->dev, VE_COMP_BUFFER_SIZE, video->comp[0].virt,
+			  video->comp[0].dma);
+	video->comp[0].dma = 0ULL;
+	video->comp[0].virt = NULL;
+
+free_src1:
+	dma_free_coherent(video->dev, VE_SRC_BUFFER_SIZE, video->srcs[1].virt,
+			  video->srcs[1].dma);
+	video->srcs[1].dma = 0ULL;
+	video->srcs[1].virt = NULL;
+
+free_src0:
+	dma_free_coherent(video->dev, VE_SRC_BUFFER_SIZE, video->srcs[0].virt,
+			  video->srcs[0].dma);
+	video->srcs[0].dma = 0ULL;
+	video->srcs[0].virt = NULL;
+err:
+	return -ENOMEM;
+}
+
+static void aspeed_video_free_cma(struct aspeed_video *video)
+{
+	dma_free_coherent(video->dev, VE_JPEG_BUFFER_SIZE, video->jpeg.virt,
+			  video->jpeg.dma);
+	dma_free_coherent(video->dev, VE_COMP_BUFFER_SIZE, video->comp[1].virt,
+			  video->comp[1].dma);
+	dma_free_coherent(video->dev, VE_COMP_BUFFER_SIZE, video->comp[0].virt,
+			  video->comp[0].dma);
+	dma_free_coherent(video->dev, VE_SRC_BUFFER_SIZE, video->srcs[1].virt,
+			  video->srcs[1].dma);
+	dma_free_coherent(video->dev, VE_SRC_BUFFER_SIZE, video->srcs[0].virt,
+			  video->srcs[0].dma);
+
+	video->srcs[0].dma = 0ULL;
+	video->srcs[0].virt = NULL;
+	video->srcs[1].dma = 0ULL;
+	video->srcs[1].virt = NULL;
+	video->comp[0].dma = 0ULL;
+	video->comp[0].virt = NULL;
+	video->comp[1].dma = 0ULL;
+	video->comp[1].virt = NULL;
+	video->jpeg.dma = 0ULL;
+	video->jpeg.virt = NULL;
+}
+
+static int aspeed_video_start(struct aspeed_video *video)
+{
+	int rc = aspeed_video_allocate_cma(video);
+
+	if (rc)
+		return rc;
+
+	aspeed_video_on(video);
+
+	aspeed_video_init_regs(video);
+
+	rc = aspeed_video_get_resolution(video);
+	if (rc)
+		aspeed_video_free_cma(video);
+
+	clear_bit(VIDEO_FRAME_TRIGGERED, &video->flags);
+
+	return rc;
+}
+
+static void aspeed_video_stop(struct aspeed_video *video)
+{
+	cancel_delayed_work_sync(&video->res_work);
+
+	aspeed_video_off(video);
+
+	aspeed_video_free_cma(video);
+
+	clear_bit(VIDEO_FRAME_AVAILABLE, &video->flags);
+}
+
+static int aspeed_video_querycap(struct file *file, void *fh,
+				 struct v4l2_capability *cap)
+{
+	struct aspeed_video *video = video_drvdata(file);
+
+	strncpy(cap->driver, DEVICE_NAME, sizeof(cap->driver));
+	cap->capabilities = video->vdev.device_caps | V4L2_CAP_DEVICE_CAPS;
+
+	return 0;
+}
+
+static int aspeed_video_get_format(struct file *file, void *fh,
+				   struct v4l2_format *f)
+{
+	int rc;
+	struct aspeed_video *video = video_drvdata(file);
+
+	if (test_bit(VIDEO_RES_CHANGE, &video->flags)) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		rc = wait_event_interruptible(video->wait,
+					      !test_bit(VIDEO_RES_CHANGE,
+							&video->flags));
+		if (rc)
+			return -EINTR;
+	}
+
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	f->fmt.pix = video->fmt;
+
+	return 0;
+}
+
+static int aspeed_video_set_format(struct file *file, void *fh,
+				   struct v4l2_format *f)
+{
+	struct aspeed_video *video = video_drvdata(file);
+
+	if (f->fmt.pix.pixelformat == video->fmt.pixelformat)
+		return 0;
+
+	if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV444) {
+		video->fmt.pixelformat = V4L2_PIX_FMT_YUV444;
+		aspeed_video_init_jpeg_table(video->jpeg.virt, false);
+		aspeed_video_update(video, VE_SEQ_CTRL, ~VE_SEQ_CTRL_YUV420,
+				    0);
+	} else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+		video->fmt.pixelformat = V4L2_PIX_FMT_YUV420;
+		aspeed_video_init_jpeg_table(video->jpeg.virt, true);
+		aspeed_video_update(video, VE_SEQ_CTRL, 0xFFFFFFFF,
+				    VE_SEQ_CTRL_YUV420);
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int aspeed_video_get_jpegcomp(struct file *file, void *fh,
+				     struct v4l2_jpegcompression *a)
+{
+	struct aspeed_video *video = video_drvdata(file);
+
+	a->quality = video->jpeg_quality;
+
+	return 0;
+}
+
+static int aspeed_video_set_jpegcomp(struct file *file, void *fh,
+				     const struct v4l2_jpegcompression *a)
+{
+	u32 comp_ctrl;
+	struct aspeed_video *video = video_drvdata(file);
+
+	if (a->quality < 0 || a->quality > 11)
+		return -EINVAL;
+
+	video->jpeg_quality = a->quality;
+	comp_ctrl = FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
+		FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10);
+
+	aspeed_video_update(video, VE_COMP_CTRL,
+			    ~(VE_COMP_CTRL_DCT_LUM | VE_COMP_CTRL_DCT_CHR),
+			    comp_ctrl);
+
+	return 0;
+}
+
+static int aspeed_video_get_parm(struct file *file, void *fh,
+				 struct v4l2_streamparm *a)
+{
+	struct aspeed_video *video = video_drvdata(file);
+
+	a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	a->parm.capture.timeperframe.numerator = 1;
+	a->parm.capture.timeperframe.denominator = video->frame_rate;
+
+	return 0;
+}
+
+static int aspeed_video_set_parm(struct file *file, void *fh,
+				 struct v4l2_streamparm *a)
+{
+	int frame_rate;
+	struct aspeed_video *video = video_drvdata(file);
+
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	frame_rate = a->parm.capture.timeperframe.denominator /
+		a->parm.capture.timeperframe.numerator;
+
+	if (frame_rate < 0 || frame_rate > 60)
+		return -EINVAL;
+
+	if (video->frame_rate != frame_rate) {
+		video->frame_rate = frame_rate;
+		aspeed_video_update(video, VE_CTRL, ~VE_CTRL_FRC,
+				    FIELD_PREP(VE_CTRL_FRC, frame_rate));
+	}
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops aspeed_video_ioctls = {
+	.vidioc_querycap = aspeed_video_querycap,
+	.vidioc_g_fmt_vid_cap = aspeed_video_get_format,
+	.vidioc_s_fmt_vid_cap = aspeed_video_set_format,
+	.vidioc_g_jpegcomp = aspeed_video_get_jpegcomp,
+	.vidioc_s_jpegcomp = aspeed_video_set_jpegcomp,
+	.vidioc_g_parm = aspeed_video_get_parm,
+	.vidioc_s_parm = aspeed_video_set_parm,
+};
+
+static void aspeed_video_resolution_work(struct work_struct *work)
+{
+	int rc;
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct aspeed_video *video = container_of(dwork, struct aspeed_video,
+						  res_work);
+
+	/* No clients remaining after delay */
+	if (atomic_read(&video->clients) == 0)
+		goto done;
+
+	aspeed_video_on(video);
+
+	aspeed_video_init_regs(video);
+
+	rc = aspeed_video_get_resolution(video);
+	if (rc) {
+		dev_err(video->dev,
+			"resolution changed; couldn't get new resolution\n");
+	} else {
+		video->frame_idx = 0;
+		clear_bit(VIDEO_FRAME_TRIGGERED, &video->flags);
+	}
+
+done:
+	clear_bit(VIDEO_RES_CHANGE, &video->flags);
+	wake_up_interruptible_all(&video->wait);
+}
+
+static bool aspeed_video_frame_available(struct aspeed_video *video)
+{
+	if (!test_and_clear_bit(VIDEO_FRAME_AVAILABLE, &video->flags)) {
+		if (!test_bit(VIDEO_FRAME_TRIGGERED, &video->flags))
+			aspeed_video_start_frame(video);
+
+		return false;
+	}
+
+	return true;
+}
+
+static ssize_t aspeed_video_file_read(struct file *file, char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	int rc;
+	int fidx;
+	size_t size;
+	struct aspeed_video *video = video_drvdata(file);
+
+	if (mutex_lock_interruptible(&video->video_lock))
+		return -EINTR;
+
+	if (file->f_flags & O_NONBLOCK) {
+		if (!aspeed_video_frame_available(video)) {
+			rc = -EAGAIN;
+			goto unlock;
+		} else {
+			goto ready;
+		}
+	}
+
+	rc = wait_event_interruptible(video->wait,
+				      aspeed_video_frame_available(video));
+	if (rc) {
+		rc = -EINTR;
+		goto unlock;
+	}
+
+ready:
+	fidx = video->frame_idx;
+	size = min_t(size_t, video->frame_size, count);
+	aspeed_video_start_frame(video);
+
+	if (copy_to_user(buf, video->comp[fidx].virt, size)) {
+		rc = -EFAULT;
+		goto unlock;
+	}
+
+	rc = size;
+
+unlock:
+	mutex_unlock(&video->video_lock);
+	return rc;
+}
+
+static int aspeed_video_open(struct file *file)
+{
+	int rc;
+	struct aspeed_video *video = video_drvdata(file);
+
+	if (atomic_inc_return(&video->clients) == 1) {
+		rc = aspeed_video_start(video);
+		if (rc) {
+			dev_err(video->dev, "Failed to start video engine\n");
+			atomic_dec(&video->clients);
+			return rc;
+		}
+	}
+
+	return v4l2_fh_open(file);
+}
+
+static int aspeed_video_release(struct file *file)
+{
+	int rc;
+	struct aspeed_video *video = video_drvdata(file);
+
+	rc = v4l2_fh_release(file);
+
+	if (atomic_dec_return(&video->clients) == 0)
+		aspeed_video_stop(video);
+
+	return rc;
+}
+
+static const struct v4l2_file_operations aspeed_video_v4l2_fops = {
+	.owner = THIS_MODULE,
+	.read = aspeed_video_file_read,
+	.unlocked_ioctl = video_ioctl2,
+	.open = aspeed_video_open,
+	.release = aspeed_video_release,
+};
+
+static void aspeed_video_device_release(struct video_device *vdev)
+{
+}
+
+static int aspeed_video_setup_video(struct aspeed_video *video)
+{
+	int rc;
+	struct v4l2_device *v4l2_dev = &video->v4l2_dev;
+	struct video_device *vdev = &video->vdev;
+
+	rc = v4l2_device_register(video->dev, v4l2_dev);
+	if (rc) {
+		dev_err(video->dev, "Failed to register v4l2 device\n");
+		return rc;
+	}
+
+	vdev->fops = &aspeed_video_v4l2_fops;
+	vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+	vdev->v4l2_dev = v4l2_dev;
+	strncpy(vdev->name, DEVICE_NAME, sizeof(vdev->name));
+	vdev->vfl_type = VFL_TYPE_GRABBER;
+	vdev->vfl_dir = VFL_DIR_RX;
+	vdev->release = aspeed_video_device_release;
+	vdev->ioctl_ops = &aspeed_video_ioctls;
+	vdev->lock = &video->video_lock;
+
+	video_set_drvdata(vdev, video);
+	rc = video_register_device(vdev, VFL_TYPE_GRABBER, 0);
+	if (rc) {
+		v4l2_device_unregister(v4l2_dev);
+		dev_err(video->dev, "Failed to register video device\n");
+		return rc;
+	}
+
+	/* set pixel format defaults */
+	video->fmt.pixelformat = V4L2_PIX_FMT_YUV444;
+	video->fmt.field = V4L2_FIELD_NONE;
+
+	return 0;
+}
+
+static int aspeed_video_init(struct aspeed_video *video)
+{
+	int irq;
+	int rc;
+	struct device *dev = video->dev;
+
+	irq = irq_of_parse_and_map(dev->of_node, 0);
+	if (!irq) {
+		dev_err(dev, "Unable to find IRQ\n");
+		return -ENODEV;
+	}
+
+	rc = devm_request_irq(dev, irq, aspeed_video_irq, IRQF_SHARED,
+			      DEVICE_NAME, video);
+	if (rc < 0) {
+		dev_err(dev, "Unable to request IRQ %d\n", irq);
+		return rc;
+	}
+
+	video->eclk = devm_clk_get(dev, "eclk-gate");
+	if (IS_ERR(video->eclk)) {
+		dev_err(dev, "Unable to get ECLK\n");
+		return PTR_ERR(video->eclk);
+	}
+
+	video->vclk = devm_clk_get(dev, "vclk-gate");
+	if (IS_ERR(video->vclk)) {
+		dev_err(dev, "Unable to get VCLK\n");
+		return PTR_ERR(video->vclk);
+	}
+
+	video->rst = devm_reset_control_get_exclusive(dev, NULL);
+	if (IS_ERR(video->rst)) {
+		dev_err(dev, "Unable to get VE reset\n");
+		return PTR_ERR(video->rst);
+	}
+
+	rc = of_reserved_mem_device_init(dev);
+	if (rc) {
+		dev_err(dev, "Unable to reserve memory\n");
+		return rc;
+	}
+
+	rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+	if (rc) {
+		dev_err(dev, "Failed to set DMA mask\n");
+		of_reserved_mem_device_release(dev);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int aspeed_video_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct resource *res;
+	struct aspeed_video *video = kzalloc(sizeof(*video), GFP_KERNEL);
+
+	if (!video)
+		return -ENOMEM;
+
+	video->frame_rate = 30;
+	video->dev = &pdev->dev;
+	mutex_init(&video->video_lock);
+	init_waitqueue_head(&video->wait);
+	INIT_DELAYED_WORK(&video->res_work, aspeed_video_resolution_work);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	video->base = devm_ioremap_resource(video->dev, res);
+
+	if (IS_ERR(video->base))
+		return PTR_ERR(video->base);
+
+	rc = aspeed_video_init(video);
+	if (rc)
+		return rc;
+
+	rc = aspeed_video_setup_video(video);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int aspeed_video_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+	struct aspeed_video *video = to_aspeed_video(v4l2_dev);
+
+	video_unregister_device(&video->vdev);
+
+	v4l2_device_unregister(v4l2_dev);
+
+	of_reserved_mem_device_release(dev);
+
+	return 0;
+}
+
+static const struct of_device_id aspeed_video_of_match[] = {
+	{ .compatible = "aspeed,ast2400-video" },
+	{ .compatible = "aspeed,ast2500-video" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, aspeed_video_of_match);
+
+static struct platform_driver aspeed_video_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.of_match_table = aspeed_video_of_match,
+	},
+	.probe = aspeed_video_probe,
+	.remove = aspeed_video_remove,
+};
+
+module_platform_driver(aspeed_video_driver);
+
+MODULE_DESCRIPTION("ASPEED Video Engine Driver");
+MODULE_AUTHOR("Eddie James");
+MODULE_LICENSE("GPL v2");
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 3/4] dt-bindings: media: Add Aspeed Video Engine binding documentation
From: Eddie James @ 2018-08-29 21:09 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-1-git-send-email-eajames@linux.vnet.ibm.com>

Document the bindings.

Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
 .../devicetree/bindings/media/aspeed-video.txt     | 23 ++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt

diff --git a/Documentation/devicetree/bindings/media/aspeed-video.txt b/Documentation/devicetree/bindings/media/aspeed-video.txt
new file mode 100644
index 0000000..58c056a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/aspeed-video.txt
@@ -0,0 +1,23 @@
+* Device tree bindings for Aspeed Video Engine
+
+The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs can
+capture and compress video data from digital or analog sources.
+
+Required properties:
+ - compatible:		"aspeed,ast2400-video" or "aspeed,ast2500-video"
+ - reg:			contains the offset and length of the VE memory region
+ - clocks:		pointers to the the "vclk" and "eclk" of the syscon
+ - clock-names:		"vclk-gate", "eclk-gate"
+ - resets:		pointer to the VE reset of the syscon
+ - interrupts:		the interrupt associated with the VE on this platform
+
+Example:
+
+video: video at 1e700000 {
+    compatible = "aspeed,ast2500-video";
+    reg = <0x1e700000 0x20000>;
+    clocks = <&syscon ASPEED_CLK_GATE_VCLK>, <&syscon ASPEED_CLK_GATE_ECLK>;
+    clock-names = "vclk-gate", "eclk-gate";
+    resets = <&syscon ASPEED_RESET_VIDEO>;
+    interrupts = <7>;
+};
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 2/4] clock: aspeed: Setup video engine clocking
From: Eddie James @ 2018-08-29 21:09 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-1-git-send-email-eajames@linux.vnet.ibm.com>

Add the video engine reset bit. Add eclk mux and clock divider table.

Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
 drivers/clk/clk-aspeed.c | 41 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 39 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk-aspeed.c b/drivers/clk/clk-aspeed.c
index 5961367..f16ce7d 100644
--- a/drivers/clk/clk-aspeed.c
+++ b/drivers/clk/clk-aspeed.c
@@ -87,7 +87,7 @@ struct aspeed_clk_gate {
 /* TODO: ask Aspeed about the actual parent data */
 static const struct aspeed_gate_data aspeed_gates[] = {
 	/*				 clk rst   name			parent	flags */
-	[ASPEED_CLK_GATE_ECLK] =	{  0, -1, "eclk-gate",		"eclk",	0 }, /* Video Engine */
+	[ASPEED_CLK_GATE_ECLK] =	{  0,  6, "eclk-gate",		"eclk",	0 }, /* Video Engine */
 	[ASPEED_CLK_GATE_GCLK] =	{  1,  7, "gclk-gate",		NULL,	0 }, /* 2D engine */
 	[ASPEED_CLK_GATE_MCLK] =	{  2, -1, "mclk-gate",		"mpll",	CLK_IS_CRITICAL }, /* SDRAM */
 	[ASPEED_CLK_GATE_VCLK] =	{  3,  6, "vclk-gate",		NULL,	0 }, /* Video Capture */
@@ -113,6 +113,24 @@ struct aspeed_clk_gate {
 	[ASPEED_CLK_GATE_LHCCLK] =	{ 28, -1, "lhclk-gate",		"lhclk", 0 }, /* LPC master/LPC+ */
 };
 
+static const char * const eclk_parent_names[] = {
+	"mpll",
+	"hpll",
+	"dpll",
+};
+
+static const struct clk_div_table ast2500_eclk_div_table[] = {
+	{ 0x0, 2 },
+	{ 0x1, 2 },
+	{ 0x2, 3 },
+	{ 0x3, 4 },
+	{ 0x4, 5 },
+	{ 0x5, 6 },
+	{ 0x6, 7 },
+	{ 0x7, 8 },
+	{ 0 }
+};
+
 static const struct clk_div_table ast2500_mac_div_table[] = {
 	{ 0x0, 4 }, /* Yep, really. Aspeed confirmed this is correct */
 	{ 0x1, 4 },
@@ -192,18 +210,21 @@ static struct clk_hw *aspeed_ast2500_calc_pll(const char *name, u32 val)
 
 struct aspeed_clk_soc_data {
 	const struct clk_div_table *div_table;
+	const struct clk_div_table *eclk_div_table;
 	const struct clk_div_table *mac_div_table;
 	struct clk_hw *(*calc_pll)(const char *name, u32 val);
 };
 
 static const struct aspeed_clk_soc_data ast2500_data = {
 	.div_table = ast2500_div_table,
+	.eclk_div_table = ast2500_eclk_div_table,
 	.mac_div_table = ast2500_mac_div_table,
 	.calc_pll = aspeed_ast2500_calc_pll,
 };
 
 static const struct aspeed_clk_soc_data ast2400_data = {
 	.div_table = ast2400_div_table,
+	.eclk_div_table = ast2400_div_table,
 	.mac_div_table = ast2400_div_table,
 	.calc_pll = aspeed_ast2400_calc_pll,
 };
@@ -317,6 +338,7 @@ struct aspeed_reset {
 	[ASPEED_RESET_PECI]	= 10,
 	[ASPEED_RESET_I2C]	=  2,
 	[ASPEED_RESET_AHB]	=  1,
+	[ASPEED_RESET_VIDEO]	=  6,
 
 	/*
 	 * SCUD4 resets start at an offset to separate them from
@@ -522,6 +544,22 @@ static int aspeed_clk_probe(struct platform_device *pdev)
 		return PTR_ERR(hw);
 	aspeed_clk_data->hws[ASPEED_CLK_24M] = hw;
 
+	hw = clk_hw_register_mux(dev, "eclk-mux", eclk_parent_names,
+				 ARRAY_SIZE(eclk_parent_names), 0,
+				 scu_base + ASPEED_CLK_SELECTION, 2, 0x3, 0,
+				 &aspeed_clk_lock);
+	if (IS_ERR(hw))
+		return PTR_ERR(hw);
+	aspeed_clk_data->hws[ASPEED_CLK_ECLK_MUX] = hw;
+
+	hw = clk_hw_register_divider_table(dev, "eclk", "eclk-mux", 0,
+					   scu_base + ASPEED_CLK_SELECTION, 28,
+					   3, 0, soc_data->eclk_div_table,
+					   &aspeed_clk_lock);
+	if (IS_ERR(hw))
+		return PTR_ERR(hw);
+	aspeed_clk_data->hws[ASPEED_CLK_ECLK] = hw;
+
 	/*
 	 * TODO: There are a number of clocks that not included in this driver
 	 * as more information is required:
@@ -531,7 +569,6 @@ static int aspeed_clk_probe(struct platform_device *pdev)
 	 *   RGMII
 	 *   RMII
 	 *   UART[1..5] clock source mux
-	 *   Video Engine (ECLK) mux and clock divider
 	 */
 
 	for (i = 0; i < ARRAY_SIZE(aspeed_gates); i++) {
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 1/4] clock: aspeed: Add VIDEO reset index definition
From: Eddie James @ 2018-08-29 21:09 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <1535576973-8067-1-git-send-email-eajames@linux.vnet.ibm.com>

Add an index into the array of resets kept in the Aspeed clock driver.
This isn't a HW bit definition.

Signed-off-by: Eddie James <eajames@linux.vnet.ibm.com>
---
 include/dt-bindings/clock/aspeed-clock.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/aspeed-clock.h b/include/dt-bindings/clock/aspeed-clock.h
index f437386..15a9059 100644
--- a/include/dt-bindings/clock/aspeed-clock.h
+++ b/include/dt-bindings/clock/aspeed-clock.h
@@ -50,5 +50,6 @@
 #define ASPEED_RESET_I2C		7
 #define ASPEED_RESET_AHB		8
 #define ASPEED_RESET_CRT1		9
+#define ASPEED_RESET_VIDEO		10
 
 #endif
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH 0/4] media: platform: Add Aspeed Video Engine driver
From: Eddie James @ 2018-08-29 21:09 UTC (permalink / raw)
  To: linux-aspeed

The Video Engine (VE) embedded in the Aspeed AST2400 and AST2500 SOCs
can capture and compress video data from digital or analog sources. With
the Aspeed chip acting as a service processor, the Video Engine can
capture the host processor graphics output.

This series adds a V4L2 driver for the VE, providing a read() interface
only. The driver triggers the hardware to capture the host graphics output
and compress it to JPEG format.

Testing on an AST2500 determined that the videobuf/streaming/mmap interface
was significantly slower than the simple read() interface, so I have not
included the streaming part.

It's also possible to use an automatic mode for the VE such that
re-triggering the HW every frame isn't necessary. However this wasn't
reliable on the AST2400, and probably used more CPU anyway due to excessive
interrupts. It was approximately 15% faster.

The series also adds the necessary parent clock definitions to the Aspeed
clock driver, with both a mux and clock divider.

Eddie James (4):
  clock: aspeed: Add VIDEO reset index definition
  clock: aspeed: Setup video engine clocking
  dt-bindings: media: Add Aspeed Video Engine binding documentation
  media: platform: Add Aspeed Video Engine driver

 .../devicetree/bindings/media/aspeed-video.txt     |   23 +
 drivers/clk/clk-aspeed.c                           |   41 +-
 drivers/media/platform/Kconfig                     |    8 +
 drivers/media/platform/Makefile                    |    1 +
 drivers/media/platform/aspeed-video.c              | 1307 ++++++++++++++++++++
 include/dt-bindings/clock/aspeed-clock.h           |    1 +
 6 files changed, 1379 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/media/aspeed-video.txt
 create mode 100644 drivers/media/platform/aspeed-video.c

-- 
1.8.3.1


^ permalink raw reply

* [PATCH 3/5] arm: dts: OpenPower Palmetto system can use coprocessor for FSI
From: Rob Herring @ 2018-08-24  0:43 UTC (permalink / raw)
  To: linux-aspeed
In-Reply-To: <20180724042406.15374-4-benh@kernel.crashing.org>

On Mon, Jul 23, 2018 at 11:25 PM Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
>
> This switches away from userspace bitbanging to kernel FSI
> using the coprocessor.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>  arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 28 ++++++++++++++-----
>  1 file changed, 21 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> index c7084a819dc6..e6cfdf3c1a67 100644
> --- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> +++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> @@ -26,6 +26,11 @@
>                         no-map;
>                         reg = <0x5f000000 0x01000000>; /* 16M */
>                 };
> +
> +               coldfire_memory: codefire_memory at 5ee00000 {

typo

> +                       reg = <0x5ee00000 0x00200000>;
> +                       no-map;
> +               };
>         };
>
>         leds {
> @@ -44,6 +49,22 @@
>                 };
>         };
>
> +       fsi: gpio-fsi {
> +               compatible = "aspeed,ast2400-cf-fsi-master", "fsi-master";
> +               #address-cells = <2>;
> +               #size-cells = <0>;
> +
> +               memory-region = <&coldfire_memory>;
> +               aspeed,sram = <&sram>;
> +               aspeed,cvic = <&cvic>;
> +
> +               clock-gpios = <&gpio ASPEED_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
> +               data-gpios = <&gpio ASPEED_GPIO(A, 5) GPIO_ACTIVE_HIGH>;
> +               mux-gpios = <&gpio ASPEED_GPIO(A, 6) GPIO_ACTIVE_HIGH>;
> +               enable-gpios = <&gpio ASPEED_GPIO(D, 0) GPIO_ACTIVE_HIGH>;
> +               trans-gpios = <&gpio ASPEED_GPIO(H, 6) GPIO_ACTIVE_HIGH>;
> +       };
> +
>         gpio-keys {
>                 compatible = "gpio-keys";
>
> @@ -303,13 +324,6 @@
>                 line-name = "SYS_PWROK_BMC";
>         };
>
> -       pin_gpio_h6 {
> -               gpio-hog;
> -               gpios = <ASPEED_GPIO(H, 6) GPIO_ACTIVE_HIGH>;
> -               output-high;
> -               line-name = "SCM1_FSI0_DATA_EN";
> -       };
> -
>         pin_gpio_h7 {
>                 gpio-hog;
>                 gpios = <ASPEED_GPIO(H, 7) GPIO_ACTIVE_HIGH>;
> --
> 2.17.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox