aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/soc/amlogic/amlogic,canvas.txt29
-rw-r--r--drivers/firmware/meson/meson_sm.c56
-rw-r--r--drivers/soc/amlogic/Kconfig7
-rw-r--r--drivers/soc/amlogic/Makefile1
-rw-r--r--drivers/soc/amlogic/meson-canvas.c185
-rw-r--r--include/linux/firmware/meson/meson_sm.h1
-rw-r--r--include/linux/soc/amlogic/meson-canvas.h65
7 files changed, 344 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/soc/amlogic/amlogic,canvas.txt b/Documentation/devicetree/bindings/soc/amlogic/amlogic,canvas.txt
new file mode 100644
index 000000000000..436d2106e80d
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/amlogic/amlogic,canvas.txt
@@ -0,0 +1,29 @@
+Amlogic Canvas
+================================
+
+A canvas is a collection of metadata that describes a pixel buffer.
+Those metadata include: width, height, phyaddr, wrapping, block mode
+and endianness.
+
+Many IPs within Amlogic SoCs rely on canvas indexes to read/write pixel data
+rather than use the phy addresses directly. For instance, this is the case for
+the video decoders and the display.
+
+Amlogic SoCs have 256 canvas.
+
+Device Tree Bindings:
+---------------------
+
+Video Lookup Table
+--------------------------
+
+Required properties:
+- compatible: "amlogic,canvas"
+- reg: Base physical address and size of the canvas registers.
+
+Example:
+
+canvas: video-lut@48 {
+ compatible = "amlogic,canvas";
+ reg = <0x0 0x48 0x0 0x14>;
+};
diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c
index 0ec2ca87318c..29fbc818a573 100644
--- a/drivers/firmware/meson/meson_sm.c
+++ b/drivers/firmware/meson/meson_sm.c
@@ -24,6 +24,7 @@
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/sizes.h>
+ #include <linux/slab.h>
#include <linux/firmware/meson/meson_sm.h>
@@ -48,6 +49,7 @@ struct meson_sm_chip gxbb_chip = {
CMD(SM_EFUSE_READ, 0x82000030),
CMD(SM_EFUSE_WRITE, 0x82000031),
CMD(SM_EFUSE_USER_MAX, 0x82000033),
+ CMD(SM_GET_CHIP_ID, 0x82000044),
{ /* sentinel */ },
},
};
@@ -214,6 +216,57 @@ int meson_sm_call_write(void *buffer, unsigned int size, unsigned int cmd_index,
}
EXPORT_SYMBOL(meson_sm_call_write);
+#define SM_CHIP_ID_LENGTH 119
+#define SM_CHIP_ID_OFFSET 4
+#define SM_CHIP_ID_SIZE 12
+
+static ssize_t serial_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ uint8_t *id_buf;
+ int ret;
+
+ id_buf = kmalloc(SM_CHIP_ID_LENGTH, GFP_KERNEL);
+ if (!id_buf)
+ return -ENOMEM;
+
+ ret = meson_sm_call_read(id_buf, SM_CHIP_ID_LENGTH, SM_GET_CHIP_ID,
+ 0, 0, 0, 0, 0);
+ if (ret < 0) {
+ kfree(id_buf);
+ return ret;
+ }
+
+ ret = sprintf(buf, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ id_buf[SM_CHIP_ID_OFFSET + 0],
+ id_buf[SM_CHIP_ID_OFFSET + 1],
+ id_buf[SM_CHIP_ID_OFFSET + 2],
+ id_buf[SM_CHIP_ID_OFFSET + 3],
+ id_buf[SM_CHIP_ID_OFFSET + 4],
+ id_buf[SM_CHIP_ID_OFFSET + 5],
+ id_buf[SM_CHIP_ID_OFFSET + 6],
+ id_buf[SM_CHIP_ID_OFFSET + 7],
+ id_buf[SM_CHIP_ID_OFFSET + 8],
+ id_buf[SM_CHIP_ID_OFFSET + 9],
+ id_buf[SM_CHIP_ID_OFFSET + 10],
+ id_buf[SM_CHIP_ID_OFFSET + 11]);
+
+ kfree(id_buf);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RO(serial);
+
+static struct attribute *meson_sm_sysfs_attributes[] = {
+ &dev_attr_serial.attr,
+ NULL,
+};
+
+static const struct attribute_group meson_sm_sysfs_attr_group = {
+ .attrs = meson_sm_sysfs_attributes,
+};
+
static const struct of_device_id meson_sm_ids[] = {
{ .compatible = "amlogic,meson-gxbb-sm", .data = &gxbb_chip },
{ /* sentinel */ },
@@ -242,6 +295,9 @@ static int __init meson_sm_probe(struct platform_device *pdev)
fw.chip = chip;
pr_info("secure-monitor enabled\n");
+ if (sysfs_create_group(&pdev->dev.kobj, &meson_sm_sysfs_attr_group))
+ goto out_in_base;
+
return 0;
out_in_base:
diff --git a/drivers/soc/amlogic/Kconfig b/drivers/soc/amlogic/Kconfig
index b04f6e4aedbc..2f282b472912 100644
--- a/drivers/soc/amlogic/Kconfig
+++ b/drivers/soc/amlogic/Kconfig
@@ -1,5 +1,12 @@
menu "Amlogic SoC drivers"
+config MESON_CANVAS
+ tristate "Amlogic Meson Canvas driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ default n
+ help
+ Say yes to support the canvas IP for Amlogic SoCs.
+
config MESON_GX_SOCINFO
bool "Amlogic Meson GX SoC Information driver"
depends on ARCH_MESON || COMPILE_TEST
diff --git a/drivers/soc/amlogic/Makefile b/drivers/soc/amlogic/Makefile
index 8fa321893928..0ab16d35ac36 100644
--- a/drivers/soc/amlogic/Makefile
+++ b/drivers/soc/amlogic/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MESON_CANVAS) += meson-canvas.o
obj-$(CONFIG_MESON_GX_SOCINFO) += meson-gx-socinfo.o
obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_MX_SOCINFO) += meson-mx-socinfo.o
diff --git a/drivers/soc/amlogic/meson-canvas.c b/drivers/soc/amlogic/meson-canvas.c
new file mode 100644
index 000000000000..fce33ca76bb6
--- /dev/null
+++ b/drivers/soc/amlogic/meson-canvas.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 BayLibre, SAS
+ * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
+ * Copyright (C) 2014 Endless Mobile
+ */
+
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/soc/amlogic/meson-canvas.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+
+#define NUM_CANVAS 256
+
+/* DMC Registers */
+#define DMC_CAV_LUT_DATAL 0x00
+ #define CANVAS_WIDTH_LBIT 29
+ #define CANVAS_WIDTH_LWID 3
+#define DMC_CAV_LUT_DATAH 0x04
+ #define CANVAS_WIDTH_HBIT 0
+ #define CANVAS_HEIGHT_BIT 9
+ #define CANVAS_WRAP_BIT 22
+ #define CANVAS_BLKMODE_BIT 24
+ #define CANVAS_ENDIAN_BIT 26
+#define DMC_CAV_LUT_ADDR 0x08
+ #define CANVAS_LUT_WR_EN BIT(9)
+ #define CANVAS_LUT_RD_EN BIT(8)
+
+struct meson_canvas {
+ struct device *dev;
+ void __iomem *reg_base;
+ spinlock_t lock; /* canvas device lock */
+ u8 used[NUM_CANVAS];
+};
+
+static void canvas_write(struct meson_canvas *canvas, u32 reg, u32 val)
+{
+ writel_relaxed(val, canvas->reg_base + reg);
+}
+
+static u32 canvas_read(struct meson_canvas *canvas, u32 reg)
+{
+ return readl_relaxed(canvas->reg_base + reg);
+}
+
+struct meson_canvas *meson_canvas_get(struct device *dev)
+{
+ struct device_node *canvas_node;
+ struct platform_device *canvas_pdev;
+
+ canvas_node = of_parse_phandle(dev->of_node, "amlogic,canvas", 0);
+ if (!canvas_node)
+ return ERR_PTR(-ENODEV);
+
+ canvas_pdev = of_find_device_by_node(canvas_node);
+ if (!canvas_pdev)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return dev_get_drvdata(&canvas_pdev->dev);
+}
+EXPORT_SYMBOL_GPL(meson_canvas_get);
+
+int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
+ u32 addr, u32 stride, u32 height,
+ unsigned int wrap,
+ unsigned int blkmode,
+ unsigned int endian)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&canvas->lock, flags);
+ if (!canvas->used[canvas_index]) {
+ dev_err(canvas->dev,
+ "Trying to setup non allocated canvas %u\n",
+ canvas_index);
+ spin_unlock_irqrestore(&canvas->lock, flags);
+ return -EINVAL;
+ }
+
+ canvas_write(canvas, DMC_CAV_LUT_DATAL,
+ ((addr + 7) >> 3) |
+ (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT));
+
+ canvas_write(canvas, DMC_CAV_LUT_DATAH,
+ ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) <<
+ CANVAS_WIDTH_HBIT) |
+ (height << CANVAS_HEIGHT_BIT) |
+ (wrap << CANVAS_WRAP_BIT) |
+ (blkmode << CANVAS_BLKMODE_BIT) |
+ (endian << CANVAS_ENDIAN_BIT));
+
+ canvas_write(canvas, DMC_CAV_LUT_ADDR,
+ CANVAS_LUT_WR_EN | canvas_index);
+
+ /* Force a read-back to make sure everything is flushed. */
+ canvas_read(canvas, DMC_CAV_LUT_DATAH);
+ spin_unlock_irqrestore(&canvas->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(meson_canvas_config);
+
+int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&canvas->lock, flags);
+ for (i = 0; i < NUM_CANVAS; ++i) {
+ if (!canvas->used[i]) {
+ canvas->used[i] = 1;
+ spin_unlock_irqrestore(&canvas->lock, flags);
+ *canvas_index = i;
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&canvas->lock, flags);
+
+ dev_err(canvas->dev, "No more canvas available\n");
+ return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(meson_canvas_alloc);
+
+int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&canvas->lock, flags);
+ if (!canvas->used[canvas_index]) {
+ dev_err(canvas->dev,
+ "Trying to free unused canvas %u\n", canvas_index);
+ spin_unlock_irqrestore(&canvas->lock, flags);
+ return -EINVAL;
+ }
+ canvas->used[canvas_index] = 0;
+ spin_unlock_irqrestore(&canvas->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(meson_canvas_free);
+
+static int meson_canvas_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct meson_canvas *canvas;
+ struct device *dev = &pdev->dev;
+
+ canvas = devm_kzalloc(dev, sizeof(*canvas), GFP_KERNEL);
+ if (!canvas)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ canvas->reg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(canvas->reg_base))
+ return PTR_ERR(canvas->reg_base);
+
+ canvas->dev = dev;
+ spin_lock_init(&canvas->lock);
+ dev_set_drvdata(dev, canvas);
+
+ return 0;
+}
+
+static const struct of_device_id canvas_dt_match[] = {
+ { .compatible = "amlogic,canvas" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, canvas_dt_match);
+
+static struct platform_driver meson_canvas_driver = {
+ .probe = meson_canvas_probe,
+ .driver = {
+ .name = "amlogic-canvas",
+ .of_match_table = canvas_dt_match,
+ },
+};
+module_platform_driver(meson_canvas_driver);
+
+MODULE_DESCRIPTION("Amlogic Canvas driver");
+MODULE_AUTHOR("Maxime Jourdan <[email protected]>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/firmware/meson/meson_sm.h b/include/linux/firmware/meson/meson_sm.h
index 37a5eaea69dd..f98c20dd266e 100644
--- a/include/linux/firmware/meson/meson_sm.h
+++ b/include/linux/firmware/meson/meson_sm.h
@@ -17,6 +17,7 @@ enum {
SM_EFUSE_READ,
SM_EFUSE_WRITE,
SM_EFUSE_USER_MAX,
+ SM_GET_CHIP_ID,
};
struct meson_sm_firmware;
diff --git a/include/linux/soc/amlogic/meson-canvas.h b/include/linux/soc/amlogic/meson-canvas.h
new file mode 100644
index 000000000000..b4dde2fbeb3f
--- /dev/null
+++ b/include/linux/soc/amlogic/meson-canvas.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2018 BayLibre, SAS
+ */
+#ifndef __SOC_MESON_CANVAS_H
+#define __SOC_MESON_CANVAS_H
+
+#include <linux/kernel.h>
+
+#define MESON_CANVAS_WRAP_NONE 0x00
+#define MESON_CANVAS_WRAP_X 0x01
+#define MESON_CANVAS_WRAP_Y 0x02
+
+#define MESON_CANVAS_BLKMODE_LINEAR 0x00
+#define MESON_CANVAS_BLKMODE_32x32 0x01
+#define MESON_CANVAS_BLKMODE_64x64 0x02
+
+#define MESON_CANVAS_ENDIAN_SWAP16 0x1
+#define MESON_CANVAS_ENDIAN_SWAP32 0x3
+#define MESON_CANVAS_ENDIAN_SWAP64 0x7
+#define MESON_CANVAS_ENDIAN_SWAP128 0xf
+
+struct meson_canvas;
+
+/**
+ * meson_canvas_get() - get a canvas provider instance
+ *
+ * @dev: consumer device pointer
+ */
+struct meson_canvas *meson_canvas_get(struct device *dev);
+
+/**
+ * meson_canvas_alloc() - take ownership of a canvas
+ *
+ * @canvas: canvas provider instance retrieved from meson_canvas_get()
+ * @canvas_index: will be filled with the canvas ID
+ */
+int meson_canvas_alloc(struct meson_canvas *canvas, u8 *canvas_index);
+
+/**
+ * meson_canvas_free() - remove ownership from a canvas
+ *
+ * @canvas: canvas provider instance retrieved from meson_canvas_get()
+ * @canvas_index: canvas ID that was obtained via meson_canvas_alloc()
+ */
+int meson_canvas_free(struct meson_canvas *canvas, u8 canvas_index);
+
+/**
+ * meson_canvas_config() - configure a canvas
+ *
+ * @canvas: canvas provider instance retrieved from meson_canvas_get()
+ * @canvas_index: canvas ID that was obtained via meson_canvas_alloc()
+ * @addr: physical address to the pixel buffer
+ * @stride: width of the buffer
+ * @height: height of the buffer
+ * @wrap: undocumented
+ * @blkmode: block mode (linear, 32x32, 64x64)
+ * @endian: byte swapping (swap16, swap32, swap64, swap128)
+ */
+int meson_canvas_config(struct meson_canvas *canvas, u8 canvas_index,
+ u32 addr, u32 stride, u32 height,
+ unsigned int wrap, unsigned int blkmode,
+ unsigned int endian);
+
+#endif