new file mode 100644
@@ -0,0 +1,52 @@
+# SPDX-LIcense-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/soc/visconti/toshiba,visconti-pyramid.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Toshiba Visconti PYRAMID image processing accelerator
+
+maintainers:
+ - Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
+
+description: |
+ Toshiba Visconti PYRAMID image processing accelerator accepts a picture with up to 3 planes
+ and yields up to 4 pictures with different scaling parameters.
+
+properties:
+ compatible:
+ items:
+ - const: toshiba,visconti-pyramid
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ index:
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - index
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ pyramid0: pyramid@14160000 {
+ compatible = "toshiba,visconti-pyramid";
+ reg = <0 0x14160000 0 0x2000>;
+ interrupts = <GIC_SPI 196 IRQ_TYPE_LEVEL_HIGH>;
+ index = <0>;
+ };
+ };
@@ -487,4 +487,5 @@ source "drivers/misc/cardreader/Kconfig"
source "drivers/misc/habanalabs/Kconfig"
source "drivers/misc/uacce/Kconfig"
source "drivers/misc/pvpanic/Kconfig"
+source "drivers/misc/visconti/Kconfig"
endmenu
@@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE) += uacce/
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
+obj-$(CONFIG_ARCH_VISCONTI) += visconti/
new file mode 100644
@@ -0,0 +1,8 @@
+if ARCH_VISCONTI
+
+config VISCONTI_PYRAMID
+ tristate "Visconti Pyramid driver"
+ help
+ This option enable support for the Toshiba Visconti Pyramid accelerator driver.
+
+endif
new file mode 100644
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Visconti specific device drivers.
+#
+
+obj-$(CONFIG_VISCONTI_PYRAMID) += pyramid/
new file mode 100644
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Visconti PYRAMID driver
+#
+
+visconti-pyramid-objs = pyramid.o hwd_pyramid.o
+
+obj-$(CONFIG_VISCONTI_PYRAMID) += visconti-pyramid.o
new file mode 100644
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Toshiba Visconti Affine Accelerator Support
+ *
+ * (C) Copyright 2022 TOSHIBA CORPORATION
+ * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/types.h>
+
+#include "hwd_pyramid.h"
+
+/* REGISTER DEFINITIONS */
+#define PYM_CFG 0x001C
+#define PYM_INT_MASK 0x0020
+#define PYM_R_ENABLE 0x0030
+#define PYM_W_ENABLE 0x0034
+#define PYM_T_ENABLE 0x0038
+#define PYM_T00_CFG 0x0040
+#define PYM_T00_SRAM_BASE 0x0044
+#define PYM_T00_SRAM_SIZE 0x0048
+/* table transfer: tt 00 - 15 */
+#define PYM_DMAC_TTT_BASE(tt) (0x0100 + 0x08 * (tt))
+#define PYM_DMAC_TTT_STADR(tt) (PYM_DMAC_TTT_BASE(tt))
+#define PYM_DMAC_TTT_SIZE(tt) (PYM_DMAC_TTT_BASE(tt) + 0x04)
+/* read image: rr 00 - 02 */
+#define PYM_DMAC_RRR_BASE(rr) (0x200 + 0x40 * (rr))
+#define PYM_DMAC_RRR_STADR(rr) (PYM_DMAC_RRR_BASE(rr))
+#define PYM_DMAC_RRR_ENDADR(rr) (PYM_DMAC_RRR_BASE(rr) + 0x04)
+#define PYM_DMAC_RRR_HEIGHT(rr) (PYM_DMAC_RRR_BASE(rr) + 0x08)
+#define PYM_DMAC_RRR_PITCH(rr) (PYM_DMAC_RRR_BASE(rr) + 0x0C)
+#define PYM_DMAC_RRR_CFG0(rr) (PYM_DMAC_RRR_BASE(rr) + 0x10)
+#define PYM_DMAC_RRR_SRAM_BASE(rr) (PYM_DMAC_RRR_BASE(rr) + 0x18)
+#define PYM_DMAC_RRR_SRAM_SIZE(rr) (PYM_DMAC_RRR_BASE(rr) + 0x1C)
+/* write image: ww 00 - 11 */
+#define PYM_DMAC_WWW_BASE(ww) (0x300 + 0x40 * (ww))
+#define PYM_DMAC_WWW_STADR(ww) (PYM_DMAC_WWW_BASE(ww))
+#define PYM_DMAC_WWW_ENDADR(ww) (PYM_DMAC_WWW_BASE(ww) + 0x04)
+#define PYM_DMAC_WWW_HEIGHT(ww) (PYM_DMAC_WWW_BASE(ww) + 0x08)
+#define PYM_DMAC_WWW_PITCH(ww) (PYM_DMAC_WWW_BASE(ww) + 0x0C)
+#define PYM_DMAC_WWW_CFG0(ww) (PYM_DMAC_WWW_BASE(ww) + 0x10)
+#define PYM_DMAC_WWW_SRAM_BASE(ww) (PYM_DMAC_WWW_BASE(ww) + 0x18)
+#define PYM_DMAC_WWW_SRAM_SIZE(ww) (PYM_DMAC_WWW_BASE(ww) + 0x1C)
+#define PYM_INT_STATUS 0x0E04
+
+#define PYM_RSCALER_CS_START 0x1000
+#define PYM_RSCALER_CS_MODE1 0x1008
+#define PYM_RSCALER_CS_MODE2 0x100C
+#define PYM_RSCALER_SIZE_IN_Y 0x1010
+#define PYM_RSCALER_SIZE_IN_C 0x1014
+#define PYM_RSCALER_SIZE_OUT_Y(id) (0x1018 + 0x08 * (id))
+#define PYM_RSCALER_SIZE_OUT_C(id) (0x101C + 0x08 * (id))
+#define PYM_RSCALER_OAINSET 0x1040
+#define PYM_RSCALER_OAINSET_M 0x1044
+#define PYM_RSCALER_OAOUTSET(id) (0x1050 + 0x04 * (id))
+#define PYM_RSCALER_OAOUTSET_M(id) (0x1060 + 0x04 * (id))
+/* scaler route registers id 0-3, ch YH=0, YV, CH, CV=3 */
+#define PYM_RSCALER_XX_BASE(id, ch) (0x1080 + 0x80 * (id) + 0x20 * (ch))
+#define PYM_RSCALER_XX_FILMODE(ch) (PYM_RSCALER_XX_BASE((0), (ch)))
+#define PYM_RSCALER_XX_FILPSMODE(ch) (PYM_RSCALER_XX_BASE((0), (ch)) + 0x04)
+#define PYM_RSCALER_XX_POS(ch) (PYM_RSCALER_XX_BASE((0), (ch)) + 0x08)
+#define PYM_RSCALER_XX_FILBASE(id, ch) (PYM_RSCALER_XX_BASE((id), (ch)) + 0x0C)
+#define PYM_RSCALER_XX_OAOUTSET(id, ch) (PYM_RSCALER_XX_BASE((id), (ch)) + 0x10)
+
+#define HWD_PYRAMID_Y (0U)
+#define HWD_PYRAMID_CB (1U)
+#define HWD_PYRAMID_DMAC_T00_SRAM_BASE (0U)
+#define HWD_PYRAMID_COEF_SIZE (0x40U)
+#define HWD_PYRAMID_REGISTER_START (1U)
+#define HWD_PYRAMID_REGISTER_IN_C_SIZE_NULL (0U)
+#define HWD_PYRAMID_REGISTER_OUT_C_SIZE_NULL (0U)
+#define HWD_PYRAMID_MAX_SCALER_NUM (4U)
+#define HWD_PYRAMID_REGISTER_DUMMY_PITCH (2U)
+#define HWD_PYRAMID_REGISTER_INT_DONE BIT(0)
+#define HWD_PYRAMID_REG_CLEAR_INT BIT(0)
+#define HWD_PYRAMID_FIXED_R_CFG_VALUE (0x00070701U)
+#define HWD_PYRAMID_FIXED_T00_CFG_VALUE (0x00000210U)
+
+/**
+ * struct hwd_pyramid_resources - HWD driver internal resource structure
+ *
+ * @base: hardware register base address
+ * @clk_id: clock_id
+ */
+struct hwd_pyramid_resources {
+ void __iomem *base;
+};
+
+/* HWD driver internal resource */
+static struct hwd_pyramid_resources hwd_pyramid_resources[HWD_PYRAMID_DEVICE_MAX] = {};
+
+/**
+ * hwd_pyramid_reset() - Reset Pyramid accelerator
+ *
+ * @index: Index of Pyramid accelerator
+ * Reset the specified pyramid module
+ */
+static void hwd_pyramid_reset(u32 index)
+{
+ const struct hwd_pyramid_resources *res = &hwd_pyramid_resources[index];
+ void __iomem *base = res->base;
+
+ /* setting fixed value */
+ writel(HWD_PYRAMID_FIXED_R_CFG_VALUE, base + PYM_CFG);
+ writel(HWD_PYRAMID_FIXED_T00_CFG_VALUE, base + PYM_T00_CFG);
+ writel(HWD_PYRAMID_DMAC_T00_SRAM_BASE, base + PYM_T00_SRAM_BASE);
+}
+
+/**
+ * hwd_pyramid_config_rvdmac() - Configure RVDMAC settings of PYRAMID to the registers
+ *
+ * @base: hardware register base address
+ * @desc: Pointer to descriptor structure
+ */
+static void hwd_pyramid_config_rvdmac(void __iomem *base, const struct hwd_pyramid_descriptor *desc)
+{
+ u32 r_enable, t_enable, w_enable;
+ u32 scaler;
+ u32 color;
+ u32 i;
+
+ /* loading TAP settings */
+ t_enable = FIELD_GET(BIT(16), desc->port_enable);
+ writel(t_enable, base + PYM_T_ENABLE);
+ writel(desc->t00_sram_size, base + PYM_T00_SRAM_SIZE);
+ for (i = 0; i < HWD_PYRAMID_TAP_ELEMENT; i++) {
+ if (desc->tap_addr[i] != 0U)
+ writel(HWD_PYRAMID_COEF_SIZE, base + PYM_DMAC_TTT_SIZE(i));
+ else
+ writel(0U, base + PYM_DMAC_TTT_SIZE(i));
+ writel(desc->tap_addr[i] & GENMASK(31, 0), base + PYM_DMAC_TTT_STADR(i));
+ }
+
+ /* loading read port settings */
+ r_enable = FIELD_GET(GENMASK(15, 13), desc->port_enable);
+ writel(r_enable, base + PYM_R_ENABLE);
+ for (i = 0; i < HWD_PYRAMID_DMA_READ_PORT; i++) {
+ writel((u32)(desc->r_stadr[i] & GENMASK(31, 0)), base + PYM_DMAC_RRR_STADR(i));
+ writel((u32)(desc->r_endadr[i] & GENMASK(31, 0)), base + PYM_DMAC_RRR_ENDADR(i));
+ writel(desc->r_dmac_height[i], base + PYM_DMAC_RRR_HEIGHT(i));
+ if (desc->r_stadr[i] != 0U) {
+ if (i == HWD_PYRAMID_Y)
+ writel(desc->r_y_pitch, base + PYM_DMAC_RRR_PITCH(i));
+ else
+ writel(desc->r_c_pitch, base + PYM_DMAC_RRR_PITCH(i));
+ } else {
+ writel(HWD_PYRAMID_REGISTER_DUMMY_PITCH, base + PYM_DMAC_RRR_PITCH(i));
+ }
+
+ writel(desc->r_sram_base[i], base + PYM_DMAC_RRR_SRAM_BASE(i));
+ writel(desc->r_sram_size[i], base + PYM_DMAC_RRR_SRAM_SIZE(i));
+ }
+ writel(desc->r_cfg0, base + PYM_DMAC_RRR_CFG0(0));
+
+ /* loading write port settings */
+ w_enable = desc->port_enable & GENMASK(11, 0);
+ writel(w_enable, base + PYM_W_ENABLE);
+ for (i = 0; i < HWD_PYRAMID_DMA_WRITE_PORT; i++) {
+ scaler = i % HWD_PYRAMID_MAX_SCALER_NUM;
+ color = i / HWD_PYRAMID_MAX_SCALER_NUM;
+ writel((u32)(desc->w_stadr[i] & GENMASK(31, 0)), base + PYM_DMAC_WWW_STADR(i));
+ writel((u32)(desc->w_endadr[i] & GENMASK(31, 0)), base + PYM_DMAC_WWW_ENDADR(i));
+ writel(desc->w_dmac_height[i], base + PYM_DMAC_WWW_HEIGHT(i));
+ if (desc->w_stadr[i] != 0U) {
+ if (color == HWD_PYRAMID_Y) {
+ writel(desc->w_cfg0[i], base + PYM_DMAC_WWW_CFG0(i));
+ writel(desc->w_y_pitch[scaler], base + PYM_DMAC_WWW_PITCH(i));
+ } else {
+ writel(desc->w_c_pitch[scaler], base + PYM_DMAC_WWW_PITCH(i));
+ }
+ } else {
+ writel(HWD_PYRAMID_REGISTER_DUMMY_PITCH, base + PYM_DMAC_WWW_PITCH(i));
+ }
+ writel(desc->w_sram_base[i], base + PYM_DMAC_WWW_SRAM_BASE(i));
+ writel(desc->w_sram_size[i], base + PYM_DMAC_WWW_SRAM_SIZE(i));
+ }
+}
+
+/**
+ * hwd_pyramid_config_scaler() - Configure SCALER settings of PYRAMID to the registers
+ *
+ * @base: hardware register base address
+ * @desc: Pointer to descriptor structure
+ */
+static void hwd_pyramid_config_scaler(void __iomem *base, const struct hwd_pyramid_descriptor *desc)
+{
+ u32 scaler_sel;
+ u32 i;
+
+ writel(desc->mode1, base + PYM_RSCALER_CS_MODE1);
+ writel(desc->mode2, base + PYM_RSCALER_CS_MODE2);
+
+ /* set r_size */
+ writel(desc->r_size, base + PYM_RSCALER_SIZE_IN_Y);
+ if (desc->r_stadr[HWD_PYRAMID_CB] == 0U)
+ writel(HWD_PYRAMID_REGISTER_IN_C_SIZE_NULL, base + PYM_RSCALER_SIZE_IN_C);
+ else
+ writel(desc->r_size, base + PYM_RSCALER_SIZE_IN_C);
+
+ /* set out_size */
+ for (scaler_sel = 0; scaler_sel < HWD_PYRAMID_SCALER_ROUTE; scaler_sel++) {
+ writel(desc->w_size[scaler_sel], base + PYM_RSCALER_SIZE_OUT_Y(scaler_sel));
+ if (desc->w_stadr[scaler_sel + HWD_PYRAMID_SCALER_ROUTE] == 0U) {
+ writel(HWD_PYRAMID_REGISTER_OUT_C_SIZE_NULL,
+ base + PYM_RSCALER_SIZE_OUT_C(scaler_sel));
+ } else {
+ writel(desc->w_size[scaler_sel], base + PYM_RSCALER_SIZE_OUT_C(scaler_sel));
+ }
+ }
+
+ /* set boundary processing register */
+ writel(desc->oainset, base + PYM_RSCALER_OAINSET);
+ writel(desc->oainset_m, base + PYM_RSCALER_OAINSET_M);
+
+ /* set filter output fixed value embedded register */
+ for (scaler_sel = 0; scaler_sel < HWD_PYRAMID_SCALER_ROUTE; scaler_sel++) {
+ writel(desc->oaoutset[scaler_sel], base + PYM_RSCALER_OAOUTSET(scaler_sel));
+ writel(desc->oaoutset_m[scaler_sel], base + PYM_RSCALER_OAOUTSET_M(scaler_sel));
+ writel(desc->h_oaoutset[scaler_sel],
+ base + PYM_RSCALER_XX_OAOUTSET(scaler_sel, HWD_PYRAMID_YH));
+ writel(desc->v_oaoutset[scaler_sel],
+ base + PYM_RSCALER_XX_OAOUTSET(scaler_sel, HWD_PYRAMID_YV));
+ }
+
+ /* set common register */
+ for (i = HWD_PYRAMID_YH; i <= HWD_PYRAMID_CV; i++) {
+ writel(desc->scl_filmode[i], base + PYM_RSCALER_XX_FILMODE(i));
+ if (i == HWD_PYRAMID_YH || i == HWD_PYRAMID_CH) {
+ /* Y and C Horizontal Components */
+ writel(desc->scl_h_crop_mode, base + PYM_RSCALER_XX_FILPSMODE(i));
+ writel(desc->scl_h_crop_pos, base + PYM_RSCALER_XX_POS(i));
+ } else {
+ /* Y and C Vertical Components */
+ writel(desc->scl_v_crop_mode, base + PYM_RSCALER_XX_FILPSMODE(i));
+ writel(desc->scl_v_crop_pos, base + PYM_RSCALER_XX_POS(i));
+ }
+ }
+
+ for (scaler_sel = 0; scaler_sel < HWD_PYRAMID_SCALER_ROUTE; scaler_sel++) {
+ writel(desc->width_ratio[scaler_sel],
+ base + PYM_RSCALER_XX_FILBASE(scaler_sel, HWD_PYRAMID_YH));
+ writel(desc->width_ratio[scaler_sel],
+ base + PYM_RSCALER_XX_FILBASE(scaler_sel, HWD_PYRAMID_CH));
+ writel(desc->height_ratio[scaler_sel],
+ base + PYM_RSCALER_XX_FILBASE(scaler_sel, HWD_PYRAMID_YV));
+ writel(desc->height_ratio[scaler_sel],
+ base + PYM_RSCALER_XX_FILBASE(scaler_sel, HWD_PYRAMID_CV));
+ }
+}
+
+/**
+ * hwd_pyramid_initialize() - Initialize PYRAMID device
+ *
+ * @module_id: @ref hwd_pyramid_device "id" of the h/w module
+ * @vaddr: register base virtual address
+ * Return: 0 operation completed successfully
+ */
+void hwd_pyramid_initialize(u32 module_id, void __iomem *vaddr)
+{
+ struct hwd_pyramid_resources *res = &hwd_pyramid_resources[module_id];
+
+ res->base = vaddr;
+}
+
+/**
+ * hwd_pyramid_uninitialize() - Uninitialize PYRAMID device
+ *
+ * @module_id: @ref hwd_pyramid_device "id" of the h/w module
+ */
+void hwd_pyramid_uninitialize(u32 module_id)
+{
+ struct hwd_pyramid_resources *res = &hwd_pyramid_resources[module_id];
+
+ res->base = NULL;
+}
+
+/**
+ * hwd_pyramid_start() - Start PYRAMID device
+ *
+ * @module_id: @ref hwd_pyramid_device "id" of the h/w module
+ * @desc: Pointer to descriptor structure
+ */
+void hwd_pyramid_start(u32 module_id, const struct hwd_pyramid_descriptor *desc)
+{
+ const struct hwd_pyramid_resources *res = &hwd_pyramid_resources[module_id];
+ void __iomem *base = res->base;
+
+ hwd_pyramid_reset(module_id);
+
+ /* Clear Interrupts */
+ writel(HWD_PYRAMID_REG_CLEAR_INT, base + PYM_INT_STATUS);
+
+ hwd_pyramid_config_rvdmac(base, desc);
+ hwd_pyramid_config_scaler(base, desc);
+ dsb(st);
+
+ /* Kick Pyramid */
+ writel(HWD_PYRAMID_REGISTER_START, base + PYM_RSCALER_CS_START);
+}
+
+/**
+ * hwd_pyramid_stop() - Stop PYRAMID device.
+ *
+ * @module_id: @ref hwd_pyramid_device "id" of the h/w module
+ *
+ */
+void hwd_pyramid_stop(u32 module_id)
+{
+}
+
+/**
+ * hwd_pyramid_irq_handler() - HWD PYRAMID interrupt handler
+ *
+ * @@param[in]: module_id @ref hwd_pyramid_device "id" of the h/w module
+ * Return: event @ref hwd_pyramid_events "event" returned by HWD irq handler
+ */
+u32 hwd_pyramid_irq_handler(u32 module_id)
+{
+ const struct hwd_pyramid_resources *res = &hwd_pyramid_resources[module_id];
+ void __iomem *base = res->base;
+ u32 event = 0;
+ u32 cause;
+
+ /* Read and Clear the interrupt causes */
+ cause = readl(base + PYM_INT_STATUS);
+ writel(cause, base + PYM_INT_STATUS);
+
+ if (FIELD_GET(HWD_PYRAMID_REGISTER_INT_DONE, cause))
+ event |= HWD_PYRAMID_EVENT_DONE;
+
+ return event;
+}
new file mode 100644
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */
+/* Toshiba Visconti Affine Accelerator Support
+ *
+ * (C) Copyright 2022 TOSHIBA CORPORATION
+ * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#ifndef HWD_PYRAMID_H
+#define HWD_PYRAMID_H
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+/**
+ * enum hwd_pyramid_module_id - ID for each PYRAMID device
+ */
+enum hwd_pyramid_module_id { HWD_PYRAMID_DEVICE_0 = 0, HWD_PYRAMID_DEVICE_MAX = 1 };
+
+/**
+ * enum hwd_pyramid_component
+ * @HWD_PYRAMID_YH: Horizontal Y Component
+ * @HWD_PYRAMID_YV: Vertical Y Component
+ * @HWD_PYRAMID_CH: Horizontal C Component
+ * @HWD_PYRAMID_CV: Vertical C Component
+ * @HWD_PYRAMID_YCHV: Total [YC][HV] Combinations
+ */
+enum hwd_pyramid_component {
+ HWD_PYRAMID_YH = 0,
+ HWD_PYRAMID_YV = 1,
+ HWD_PYRAMID_CH = 2,
+ HWD_PYRAMID_CV = 3,
+ HWD_PYRAMID_YCHV = 4,
+};
+
+/* Process completed successfully */
+#define HWD_PYRAMID_EVENT_DONE (0x01U)
+
+#define HWD_PYRAMID_TAP_ELEMENT (16U) /**< YH1, YV1, CH1, CV1, YH2, ... , CV4 */
+#define HWD_PYRAMID_DMA_READ_PORT (3U) /**< Y, Cb, Cr */
+#define HWD_PYRAMID_DMA_WRITE_PORT (12U) /**< Y1, Y2, Y3, Y4, Cb1, Cb2, ... , Cr4 */
+#define HWD_PYRAMID_SCALER_ROUTE (4U) /**< Scaler 0-3 */
+
+/**
+ * struct hwd_pyramid_descriptor - HWD PYRAMID Descriptor
+ * @port_enable: Read, Write and TAP Port enable/disable
+ * @t00_sram_size: Table Port 00 SRAM FIFO size
+ * @config_done: Status of the config APIs called
+ * @tap_addr: Table ID00 to 15 Transfer Start Address Register
+ * @r_stadr: Y, Cb, Cr data start address of input data
+ * @r_endadr: Y, Cb, Cr data end address of input data
+ * @r_size: Input width and height
+ * @mode2: Scaler mode setting register 2
+ * @r_y_pitch: The lines pitch of y on input image
+ * @r_c_pitch: The lines pitch of y on input image
+ * @r_dmac_height: Number of line rectangle data
+ * @r_cfg0: Read port configuration register 0
+ * @r_sram_base: Internal read port sram base address
+ * @r_sram_size: Internal read port sram size
+ * @w_stadr: Y, Cb, and Cr data address of Scaler0-3
+ * @w_endadr: Y, Cb, and Cr data address of Scaler0-3
+ * @w_size: Output width and height of Scaler0-3
+ * @w_cfg0: Write port configuration register 0
+ * @w_y_pitch: The lines pitch of y output image from Scaler0-3
+ * @w_c_pitch: The lines pitch of c output image from Scaler0-3
+ * @w_dmac_height: Number of line rectangle data
+ * @w_sram_base: Internal write port sram base address
+ * @w_sram_size: Internal write port sram size
+ * @mode1: Scaler mode setting register 1
+ * @scl_filmode: Filter mode setting of Scaler (common to Scaler0-3)
+ * @oainset: Filter input effective pixel outside boundary processing
+ * @oainset_m: Filter input effective pixel outside boundary processing
+ * @oaoutset: Fixed data setting of filter output
+ * @oaoutset_m: Fixed data setting of filter output
+ * @scl_h_crop_mode: Cropping setting of horizontal (common to Scaler0-3)
+ * @scl_v_crop_mode: Cropping setting of vertical (common to Scaler0-3)
+ * @scl_h_crop_pos: Starting position of horizontal cropping(common to Scaler0-3)
+ * @scl_v_crop_pos: Starting position of vertical cropping(common to Scaler0-3)
+ * @width_ratio: Width reduction/expansion rate of Scaler0 to 3
+ * @height_ratio: Height reduction/expansion rate of Scaler0 to 3
+ * @h_oaoutset: Left and right position for fixed data setting of filter output
+ * @v_oaoutset: Upper and lower position for fixed data setting of filter output
+ */
+struct hwd_pyramid_descriptor {
+ u32 port_enable;
+ u16 t00_sram_size;
+ u16 config_done;
+ u32 tap_addr[HWD_PYRAMID_TAP_ELEMENT];
+ u32 r_stadr[HWD_PYRAMID_DMA_READ_PORT];
+ u32 r_endadr[HWD_PYRAMID_DMA_READ_PORT];
+ u32 r_size;
+ u32 mode2;
+ u32 r_y_pitch;
+ u32 r_c_pitch;
+ u16 r_dmac_height[HWD_PYRAMID_DMA_READ_PORT];
+ u16 r_cfg0;
+ u16 r_sram_base[HWD_PYRAMID_DMA_READ_PORT];
+ u16 r_sram_size[HWD_PYRAMID_DMA_READ_PORT];
+ u32 w_stadr[HWD_PYRAMID_DMA_WRITE_PORT];
+ u32 w_endadr[HWD_PYRAMID_DMA_WRITE_PORT];
+ u32 w_size[HWD_PYRAMID_SCALER_ROUTE];
+ u16 w_cfg0[HWD_PYRAMID_DMA_WRITE_PORT];
+ u32 w_y_pitch[HWD_PYRAMID_SCALER_ROUTE];
+ u32 w_c_pitch[HWD_PYRAMID_SCALER_ROUTE];
+ u16 w_dmac_height[HWD_PYRAMID_DMA_WRITE_PORT];
+ u16 w_sram_base[HWD_PYRAMID_DMA_WRITE_PORT];
+ u16 w_sram_size[HWD_PYRAMID_DMA_WRITE_PORT];
+ u32 mode1;
+ u8 scl_filmode[HWD_PYRAMID_YCHV];
+ u32 oainset;
+ u32 oainset_m;
+ u32 oaoutset[HWD_PYRAMID_SCALER_ROUTE];
+ u32 oaoutset_m[HWD_PYRAMID_SCALER_ROUTE];
+ u32 scl_h_crop_mode;
+ u32 scl_v_crop_mode;
+ u32 scl_h_crop_pos;
+ u32 scl_v_crop_pos;
+ u32 width_ratio[HWD_PYRAMID_SCALER_ROUTE];
+ u32 height_ratio[HWD_PYRAMID_SCALER_ROUTE];
+ u32 h_oaoutset[HWD_PYRAMID_SCALER_ROUTE];
+ u32 v_oaoutset[HWD_PYRAMID_SCALER_ROUTE];
+};
+
+void hwd_pyramid_initialize(u32 module_id, void __iomem *vaddr);
+void hwd_pyramid_uninitialize(u32 module_id);
+void hwd_pyramid_start(u32 module_id, const struct hwd_pyramid_descriptor *desc);
+void hwd_pyramid_stop(u32 module_id);
+u32 hwd_pyramid_irq_handler(u32 module_id);
+
+#endif /* HWD_PYRAMID_H */
new file mode 100644
@@ -0,0 +1,532 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Toshiba Visconti Affine Accelerator Support
+ *
+ * (C) Copyright 2022 TOSHIBA CORPORATION
+ * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-buf.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/visconti-pyramid.h>
+#include <linux/wait.h>
+
+#include "hwd_pyramid.h"
+
+#define DRV_PYRAMID_DUMMY_END_ADDR (0xFFFFFFFFU)
+
+#define IPA_POLL_EVENT_NONE (0)
+#define IPA_POLL_EVENT_DONE (1)
+#define IPA_POLL_EVENT_ERROR (2)
+#define IPA_WAKEUP_RETRY_DELAY (300 * 1000) /*usec*/
+
+int ipa_attach_dmabuf(struct device *dev, int fd, struct dma_buf_attachment **a,
+ struct sg_table **s, dma_addr_t *addr, enum dma_data_direction dma_dir)
+{
+ struct dma_buf_attachment *attachment;
+ struct dma_buf *dmabuf;
+ struct sg_table *sgt;
+ int ret;
+
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR(dmabuf)) {
+ dev_err(dev, "Invalid dmabuf FD\n");
+ return PTR_ERR(dmabuf);
+ }
+ attachment = dma_buf_attach(dmabuf, dev);
+
+ if (IS_ERR(attachment)) {
+ dev_err(dev, "Failed to attach dmabuf\n");
+ ret = PTR_ERR(attachment);
+ goto err_put;
+ }
+ sgt = dma_buf_map_attachment(attachment, dma_dir);
+ if (IS_ERR(sgt)) {
+ dev_err(dev, "Failed to get dmabufs sg_table\n");
+ ret = PTR_ERR(sgt);
+ goto err_detach;
+ }
+ if (sgt->nents != 1) {
+ dev_err(dev, "Sparse DMA region is unsupported\n");
+ ret = -EINVAL;
+ goto err_unmap;
+ }
+
+ *addr = sg_dma_address(sgt->sgl);
+ *a = attachment;
+ *s = sgt;
+
+ return 0;
+
+err_unmap:
+ dma_buf_unmap_attachment(attachment, sgt, dma_dir);
+err_detach:
+ dma_buf_detach(dmabuf, attachment);
+err_put:
+ dma_buf_put(dmabuf);
+ return ret;
+}
+
+struct pyramid_priv {
+ struct device *dev;
+ struct miscdevice miscdev;
+ struct mutex lock; /* only 1 user context execute an API at a time */
+ void __iomem *regs;
+ int irq;
+ wait_queue_head_t waitq;
+ enum drv_ipa_state status;
+ unsigned int hwd_event;
+ unsigned int poll_event;
+ int id;
+ char name[16];
+
+ struct dma_buf_attachment *dba[IPA_BUFFER_INDEX_MAX];
+ struct sg_table *sgt[IPA_BUFFER_INDEX_MAX];
+ enum dma_data_direction dma_dir[IPA_BUFFER_INDEX_MAX];
+ unsigned int dma_count;
+
+ dma_addr_t buffer_iova[IPA_BUFFER_INDEX_MAX];
+};
+
+static u32 pyramid_ipa_addr_to_iova(struct pyramid_priv *priv, struct drv_ipa_addr addr)
+{
+ u32 iova = 0;
+
+ if (addr.buffer_index < priv->dma_count &&
+ addr.offset < priv->dba[addr.buffer_index]->dmabuf->size)
+ iova = priv->buffer_iova[addr.buffer_index] + addr.offset;
+ return iova;
+}
+
+static int pyramid_attach_dma_buf(struct pyramid_priv *priv, unsigned int buffer_index,
+ struct drv_ipa_buffer_info *buffer_info)
+{
+ dma_addr_t addr;
+ int ret = 0;
+
+ if (buffer_index >= IPA_BUFFER_INDEX_MAX) {
+ dev_err(priv->dev, "Buffer index invalid: index=%d\n", buffer_index);
+ return -EINVAL;
+ }
+
+ switch (buffer_info[buffer_index].direction) {
+ case DRV_IPA_DIR_NONE:
+ priv->dma_dir[priv->dma_count] = DMA_NONE;
+ break;
+ case DRV_IPA_DIR_TO_DEVICE:
+ priv->dma_dir[priv->dma_count] = DMA_TO_DEVICE;
+ break;
+ case DRV_IPA_DIR_FROM_DEVICE:
+ priv->dma_dir[priv->dma_count] = DMA_FROM_DEVICE;
+ break;
+ case DRV_IPA_DIR_BIDIRECTION:
+ priv->dma_dir[priv->dma_count] = DMA_BIDIRECTIONAL;
+ break;
+ default:
+ dev_err(priv->dev, "DMA direction invalid: index=%d dir=%d\n", buffer_index,
+ buffer_info[buffer_index].direction);
+ return -EINVAL;
+ }
+
+ ret = ipa_attach_dmabuf(priv->dev, buffer_info[buffer_index].fd,
+ &priv->dba[priv->dma_count], &priv->sgt[priv->dma_count], &addr,
+ priv->dma_dir[priv->dma_count]);
+ if (ret == 0) {
+ priv->dma_count++;
+ priv->buffer_iova[buffer_index] = addr;
+ }
+
+ return ret;
+}
+
+static void pyramid_detach_dma_buf(struct pyramid_priv *priv)
+{
+ struct dma_buf *dmabuf;
+ int i;
+
+ for (i = 0; i < priv->dma_count; i++) {
+ dmabuf = priv->dba[i]->dmabuf;
+ dma_buf_unmap_attachment(priv->dba[i], priv->sgt[i], priv->dma_dir[i]);
+ dma_buf_detach(dmabuf, priv->dba[i]);
+ dma_buf_put(dmabuf);
+ }
+}
+
+static irqreturn_t pyramid_irq(int irq, void *dev_id)
+{
+ struct pyramid_priv *priv = dev_id;
+
+ priv->hwd_event = hwd_pyramid_irq_handler(priv->id);
+
+ disable_irq_nosync(priv->irq);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t pyramid_irq_thread(int irq, void *dev_id)
+{
+ struct pyramid_priv *priv = dev_id;
+ unsigned long delay = 1;
+
+ mutex_lock(&priv->lock);
+ pyramid_detach_dma_buf(priv);
+
+ priv->status = DRV_IPA_STATE_IDLE;
+
+ /* status should be updated before poll_event so that
+ * when poll() returns, user context must observe state as idle
+ */
+ smp_wmb();
+
+ WARN_ON(priv->hwd_event != HWD_PYRAMID_EVENT_DONE);
+ priv->poll_event = IPA_POLL_EVENT_DONE;
+
+ /* General barrier to avoid re-ordering of priv->poll_event=N and
+ * waitqueue_active()
+ */
+ smp_mb();
+
+ /* Threads going to sleep in poll() can miss wakeup, when wakeup is done
+ * between event check in ipa_poll() and sleeping. Wakeup repeatedly.
+ */
+ while (waitqueue_active(&priv->waitq)) {
+ wake_up_interruptible(&priv->waitq);
+
+ WARN_ON(delay > IPA_WAKEUP_RETRY_DELAY);
+ usleep_range(delay, delay + 1);
+ delay += delay;
+ }
+
+ mutex_unlock(&priv->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void pyramid_start(struct pyramid_priv *priv, struct hwd_pyramid_descriptor *desc)
+{
+ hwd_pyramid_start(priv->id, desc);
+}
+
+static int pyramid_ioctl_start(struct pyramid_priv *priv, unsigned long arg)
+{
+ struct hwd_pyramid_descriptor hwd_desc;
+ struct drv_pyramid_descriptor desc;
+ int ret = 0;
+ int i;
+
+ ret = mutex_lock_interruptible(&priv->lock);
+ if (ret)
+ return ret;
+
+ if (priv->status == DRV_IPA_STATE_BUSY) {
+ dev_dbg(priv->dev, "busy: %d\n", priv->status);
+ mutex_unlock(&priv->lock);
+ return -EBUSY;
+ }
+
+ if (copy_from_user(&desc, (void __user *)arg, sizeof(struct drv_pyramid_descriptor))) {
+ dev_err(priv->dev, "Descriptor memory access error\n");
+ ret = -EFAULT;
+ goto err1;
+ }
+
+ if (!FIELD_GET(DRV_PYRAMID_BIT_CONFIG_DESC_FINAL, desc.config_done)) {
+ dev_err(priv->dev, "Descriptor configuration not complete\n");
+ ret = -EINVAL;
+ goto err1;
+ }
+
+ priv->dma_count = 0;
+
+ /* setup buffer */
+ for (i = 0; i < desc.buffer_info_num; i++) {
+ ret = pyramid_attach_dma_buf(priv, i, desc.buffer_info);
+ if (ret) {
+ dev_err(priv->dev, "dma buf attach error: index=%d\n", i);
+ goto err2;
+ }
+ }
+
+ hwd_desc.port_enable = desc.port_enable;
+ hwd_desc.t00_sram_size = desc.t00_sram_size;
+ hwd_desc.config_done = desc.config_done;
+ hwd_desc.r_size = desc.r_size;
+ hwd_desc.mode2 = desc.mode2;
+ hwd_desc.r_y_pitch = desc.r_y_pitch;
+ hwd_desc.r_c_pitch = desc.r_c_pitch;
+ memcpy(hwd_desc.r_dmac_height, desc.r_dmac_height, sizeof(hwd_desc.r_dmac_height));
+ hwd_desc.r_cfg0 = desc.r_cfg0;
+ memcpy(hwd_desc.r_sram_base, desc.r_sram_base, sizeof(hwd_desc.r_sram_base));
+ memcpy(hwd_desc.r_sram_size, desc.r_sram_size, sizeof(hwd_desc.r_sram_size));
+ memcpy(hwd_desc.w_size, desc.w_size, sizeof(hwd_desc.w_size));
+ memcpy(hwd_desc.w_cfg0, desc.w_cfg0, sizeof(hwd_desc.w_cfg0));
+ memcpy(hwd_desc.w_y_pitch, desc.w_y_pitch, sizeof(hwd_desc.w_y_pitch));
+ memcpy(hwd_desc.w_c_pitch, desc.w_c_pitch, sizeof(hwd_desc.w_c_pitch));
+ memcpy(hwd_desc.w_dmac_height, desc.w_dmac_height, sizeof(hwd_desc.w_dmac_height));
+ memcpy(hwd_desc.w_sram_base, desc.w_sram_base, sizeof(hwd_desc.w_sram_base));
+ memcpy(hwd_desc.w_sram_size, desc.w_sram_size, sizeof(hwd_desc.w_sram_size));
+ hwd_desc.mode1 = desc.mode1;
+ memcpy(hwd_desc.scl_filmode, desc.scl_filmode, sizeof(hwd_desc.scl_filmode));
+ hwd_desc.oainset = desc.oainset;
+ hwd_desc.oainset_m = desc.oainset_m;
+ memcpy(hwd_desc.oaoutset, desc.oaoutset, sizeof(hwd_desc.oaoutset));
+ memcpy(hwd_desc.oaoutset_m, desc.oaoutset_m, sizeof(hwd_desc.oaoutset));
+ hwd_desc.scl_h_crop_mode = desc.scl_h_crop_mode;
+ hwd_desc.scl_v_crop_mode = desc.scl_v_crop_mode;
+ hwd_desc.scl_h_crop_pos = desc.scl_h_crop_pos;
+ hwd_desc.scl_v_crop_pos = desc.scl_v_crop_pos;
+ memcpy(hwd_desc.width_ratio, desc.width_ratio, sizeof(hwd_desc.width_ratio));
+ memcpy(hwd_desc.height_ratio, desc.height_ratio, sizeof(hwd_desc.height_ratio));
+ memcpy(hwd_desc.h_oaoutset, desc.h_oaoutset, sizeof(hwd_desc.h_oaoutset));
+ memcpy(hwd_desc.v_oaoutset, desc.v_oaoutset, sizeof(hwd_desc.v_oaoutset));
+
+ for (i = 0; i < HWD_PYRAMID_TAP_ELEMENT; i++) {
+ hwd_desc.tap_addr[i] = 0;
+ if (!drv_ipa_is_invalid_addr(desc.tap_addr[i])) {
+ hwd_desc.tap_addr[i] = pyramid_ipa_addr_to_iova(priv, desc.tap_addr[i]);
+ if (hwd_desc.tap_addr[i] == 0) {
+ dev_err(priv->dev,
+ "IPA address to iova conversion error: tap_addr %s: %d\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto err2;
+ }
+ }
+ }
+
+ for (i = 0; i < HWD_PYRAMID_DMA_READ_PORT; i++) {
+ hwd_desc.r_stadr[i] = 0;
+ if (!drv_ipa_is_invalid_addr(desc.r_stadr[i])) {
+ hwd_desc.r_stadr[i] = pyramid_ipa_addr_to_iova(priv, desc.r_stadr[i]);
+ if (hwd_desc.r_stadr[i] == 0) {
+ dev_err(priv->dev,
+ "IPA address to iova conversion error: r_stadr %s: %d\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto err2;
+ }
+ }
+ }
+
+ for (i = 0; i < HWD_PYRAMID_DMA_READ_PORT; i++) {
+ hwd_desc.r_endadr[i] = DRV_PYRAMID_DUMMY_END_ADDR;
+ if (!drv_ipa_is_invalid_addr(desc.r_endadr[i])) {
+ hwd_desc.r_endadr[i] = pyramid_ipa_addr_to_iova(priv, desc.r_endadr[i]);
+ if (hwd_desc.r_endadr[i] == 0) {
+ dev_err(priv->dev,
+ "IPA address to iova conversion error: r_endadr %s: %d\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto err2;
+ }
+ }
+ }
+
+ for (i = 0; i < HWD_PYRAMID_DMA_WRITE_PORT; i++) {
+ hwd_desc.w_stadr[i] = 0;
+ if (!drv_ipa_is_invalid_addr(desc.w_stadr[i])) {
+ hwd_desc.w_stadr[i] = pyramid_ipa_addr_to_iova(priv, desc.w_stadr[i]);
+ if (hwd_desc.w_stadr[i] == 0) {
+ dev_err(priv->dev,
+ "IPA address to iova conversion error: w_stadr %s: %d\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto err2;
+ }
+ }
+ }
+
+ for (i = 0; i < HWD_PYRAMID_DMA_WRITE_PORT; i++) {
+ hwd_desc.w_endadr[i] = DRV_PYRAMID_DUMMY_END_ADDR;
+ if (!drv_ipa_is_invalid_addr(desc.w_endadr[i])) {
+ hwd_desc.w_endadr[i] = pyramid_ipa_addr_to_iova(priv, desc.w_endadr[i]);
+ if (hwd_desc.w_endadr[i] == 0) {
+ dev_err(priv->dev,
+ "IPA address to iova conversion error: w_endadr %s: %d\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto err2;
+ }
+ }
+ }
+
+ pyramid_start(priv, &hwd_desc);
+
+ priv->poll_event = IPA_POLL_EVENT_NONE;
+ priv->hwd_event = 0;
+ priv->status = DRV_IPA_STATE_BUSY;
+ /* Barrier to prevent pyramid_irq() from setting priv->hwd_event
+ * before it is reset above
+ */
+ smp_wmb();
+ enable_irq(priv->irq);
+
+ mutex_unlock(&priv->lock);
+
+ return ret;
+
+err2:
+ pyramid_detach_dma_buf(priv);
+err1:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int pyramid_ioctl_get_status(struct pyramid_priv *priv, unsigned long arg)
+{
+ struct drv_pyramid_status status;
+ int ret = 0;
+
+ status.state = priv->status;
+ if (copy_to_user((void __user *)arg, &status, sizeof(struct drv_pyramid_status))) {
+ dev_err(priv->dev, "status memory access error\n");
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static long pyramid_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ struct pyramid_priv *priv = container_of(fp->private_data, struct pyramid_priv, miscdev);
+ int ret = 0;
+
+ switch (cmd) {
+ case IOC_IPA_START:
+ ret = pyramid_ioctl_start(priv, arg);
+ break;
+ case IOC_IPA_GET_STATUS:
+ ret = pyramid_ioctl_get_status(priv, arg);
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+
+ return ret;
+}
+
+static __poll_t pyramid_poll(struct file *fp, poll_table *wait)
+{
+ struct pyramid_priv *priv = container_of(fp->private_data, struct pyramid_priv, miscdev);
+ __poll_t mask = 0;
+
+ poll_wait(fp, &priv->waitq, wait);
+
+ /* Barrier to avoid re-ordering of poll_wait() and event load
+ * Read barrier here and release barrier in poll_wait() together will
+ * prevent re-ordering
+ */
+ smp_rmb();
+ if (priv->poll_event != IPA_POLL_EVENT_NONE)
+ mask = EPOLLIN | EPOLLRDNORM;
+ return mask;
+}
+
+static const struct file_operations pyramid_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = pyramid_ioctl,
+ .poll = pyramid_poll,
+};
+
+static int pyramid_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pyramid_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->lock);
+
+ /* update DMA mask */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
+ if (ret)
+ return ret;
+
+ priv->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
+ priv->irq = platform_get_irq(pdev, 0);
+ if (priv->irq < 0)
+ return priv->irq;
+
+ ret = devm_request_threaded_irq(dev, priv->irq, pyramid_irq, pyramid_irq_thread, 0,
+ "pyramid", priv);
+ if (ret) {
+ dev_err(dev, "irq request failed\n");
+ return ret;
+ }
+ disable_irq(priv->irq);
+
+ ret = of_property_read_u32(dev->of_node, "index", &priv->id);
+ if (ret) {
+ dev_err(dev, "failed to acquire irq resource\n");
+ return ret;
+ }
+
+ hwd_pyramid_initialize(priv->id, priv->regs);
+
+ snprintf(priv->name, sizeof(priv->name), "pyramid%d", priv->id);
+ priv->miscdev.minor = MISC_DYNAMIC_MINOR;
+ priv->miscdev.name = priv->name;
+ priv->miscdev.fops = &pyramid_fops;
+ ret = misc_register(&priv->miscdev);
+ if (ret) {
+ dev_err(dev, "misc registration failed\n");
+ hwd_pyramid_uninitialize(priv->id);
+ return ret;
+ }
+
+ priv->dev = dev;
+ platform_set_drvdata(pdev, priv);
+
+ init_waitqueue_head(&priv->waitq);
+
+ priv->status = DRV_IPA_STATE_IDLE;
+ return 0;
+}
+
+static int pyramid_remove(struct platform_device *pdev)
+{
+ struct pyramid_priv *priv = platform_get_drvdata(pdev);
+
+ misc_deregister(&priv->miscdev);
+ hwd_pyramid_uninitialize(priv->id);
+ return 0;
+}
+
+static const struct of_device_id pyramid_of_match[] = {
+ {
+ .compatible = "toshiba,visconti-pyramid",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pyramid_of_match);
+
+static struct platform_driver pyramid_driver = {
+ .probe = pyramid_probe,
+ .remove = pyramid_remove,
+ .driver = {
+ .name = "visconti_pyramid",
+ .of_match_table = of_match_ptr(pyramid_of_match),
+ },
+};
+module_platform_driver(pyramid_driver);
+
+MODULE_AUTHOR("Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>");
+MODULE_DESCRIPTION("Toshiba Visconti pyramid driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS(DMA_BUF);
new file mode 100644
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Toshiba Visconti Image Processing Accelerator Support
+ *
+ * (C) Copyright 2022 TOSHIBA CORPORATION
+ * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#ifndef _UAPI_LINUX_IPA_H
+#define _UAPI_LINUX_IPA_H
+
+#include <linux/types.h>
+
+/**
+ * enum drv_ipa_state - state of Visconti IPA driver
+ *
+ * @DRV_IPA_STATE_IDLE: driver is idle
+ * @DRV_IPA_STATE_BUSY: device is busy
+ */
+enum drv_ipa_state { DRV_IPA_STATE_IDLE = 0, DRV_IPA_STATE_BUSY };
+
+/**
+ * enum drv_ipa_buffer_direction - usage of buffer
+ *
+ * @DRV_IPA_DIR_BIDIRECTION: cpu writes/reads data
+ * @DRV_IPA_DIR_TO_DEVICE: cpu writes data
+ * @DRV_IPA_DIR_FROM_DEVICE: cpu reads data
+ * @DRV_IPA_DIR_NONE: cpu not access or non-coherent
+ */
+enum drv_ipa_buffer_direction {
+ DRV_IPA_DIR_BIDIRECTION = 0,
+ DRV_IPA_DIR_TO_DEVICE,
+ DRV_IPA_DIR_FROM_DEVICE,
+ DRV_IPA_DIR_NONE
+};
+
+/**
+ * struct drv_ipa_buffer_info - buffer information for Visconti IPA drivers
+ *
+ * @fd: file descriptor of a dma-buf heap instance
+ * @flags: optional flags for attributes of the buffer
+ * @direction: A &enum drv_ipa_buffer_direction value
+ * for buffer direction (to/from device)
+ */
+struct drv_ipa_buffer_info {
+ __u32 fd;
+ __u32 flags;
+ __u32 direction;
+};
+
+/**
+ * struct drv_ipa_addr - address structure for Visconti IPA drivers
+ *
+ * @buffer_index: array index of &struct drv_ipa_buffer_info
+ * @offset: offset from the top of the heap instance
+ */
+struct drv_ipa_addr {
+ __u32 buffer_index;
+ __u32 offset;
+};
+
+#define IPA_BUFFER_INDEX_MAX (16)
+#define IPA_INVALID_ADDR \
+ { \
+ .buffer_index = 0xffffffffu, .offset = 0xffffffffu \
+ }
+
+static inline struct drv_ipa_addr drv_ipa_invalid_addr(void)
+{
+ struct drv_ipa_addr invalid_addr = IPA_INVALID_ADDR;
+ return invalid_addr;
+}
+
+static inline bool drv_ipa_is_invalid_addr(struct drv_ipa_addr addr)
+{
+ struct drv_ipa_addr invalid_addr = IPA_INVALID_ADDR;
+
+ if (addr.buffer_index == invalid_addr.buffer_index && addr.offset == invalid_addr.offset)
+ return true;
+
+ return false;
+}
+
+#define IOC_IPA_MAGIC 'I'
+#define IOC_IPA_START _IO(IOC_IPA_MAGIC, 0)
+#define IOC_IPA_GET_STATUS _IO(IOC_IPA_MAGIC, 1)
+
+#endif /* _UAPI_LINUX_IPA_H */
new file mode 100644
@@ -0,0 +1,120 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Toshiba Visconti Affine Accelerator Support
+ *
+ * (C) Copyright 2022 TOSHIBA CORPORATION
+ * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#ifndef _UAPI_LINUX_PYRAMID_H
+#define _UAPI_LINUX_PYRAMID_H
+
+#include <linux/const.h>
+#include <linux/ioctl.h>
+#include <linux/types.h>
+#include <linux/visconti-ipa.h>
+
+#define DRV_PYRAMID_BIT_CONFIG_DESC_FINAL _BITUL(15)
+
+#define DRV_PYRAMID_YH (0U) /* Horizontal Y Component */
+#define DRV_PYRAMID_YV (1U) /* Vertical Y Component */
+#define DRV_PYRAMID_CH (2U) /* Horizontal C Component */
+#define DRV_PYRAMID_CV (3U) /* Vertical C Component */
+#define DRV_PYRAMID_YCHV (4U) /* Total [YC][HV] Combinations */
+#define DRV_PYRAMID_TAP_ELEMENT (16U) /* YH1, YV1, CH1, CV1, YH2, ... , CV4 */
+#define DRV_PYRAMID_DMA_READ_PORT (3U) /* Y, Cb, Cr */
+#define DRV_PYRAMID_DMA_WRITE_PORT (12U) /* Y1, Y2, Y3, Y4, Cb1, Cb2, ... , Cr4 */
+#define DRV_PYRAMID_SCALER_ROUTE (4U) /* Scaler 0-3 */
+
+/**
+ * struct drv_pyramid_status - PYRAMID IPA status for IOC_IPA_GET_STATUS
+ * @state: A &enum drv_ipa_state value for the state of the driver
+ */
+struct drv_pyramid_status {
+ __u32 state;
+};
+
+/**
+ * struct drv_pyramid_descriptor - PYRAMID IPA descriptor for IOC_IPA_START
+ * @port_enable: Read, Write and TAP Port enable/disable
+ * @t00_sram_size: Table Port 00 SRAM FIFO size
+ * @config_done: Status of the config APIs called
+ * @tap_addr: Table ID00 to 15 Transfer Start Address Register
+ * @r_stadr: Y, Cb, Cr data start address of input data
+ * @r_endadr: Y, Cb, Cr data end address of input data
+ * @r_size: Input width and height
+ * @mode2: Scaler mode setting register 2
+ * @r_y_pitch: The lines pitch of y on input image
+ * @r_c_pitch: The lines pitch of y on input image
+ * @r_dmac_height: Read port configuration register 0
+ * @r_cfg0: Line pitch of the table used in the table conversion mode
+ * @r_sram_base: Internal read port sram base address
+ * @r_sram_size: Internal read port sram size
+ * @w_stadr: Y, Cb, and Cr data address of Scaler0-3
+ * @w_endadr: Y, Cb, and Cr data address of Scaler0-3
+ * @w_size: Output width and height of Scaler0-3
+ * @w_cfg0: Write port configuration register 0
+ * @w_y_pitch: The lines pitch of y output image from Scaler0-3
+ * @w_c_pitch: The lines pitch of c output image from Scaler0-3
+ * @w_dmac_height: Number of line rectangle data
+ * @w_sram_base: Internal write port sram base address
+ * @w_sram_size: Internal write port sram size
+ * @mode1: Scaler mode setting register 1
+ * @scl_filmode: Filter mode setting of Scaler (common to Scaler0-3)
+ * @oainset: Filter input effective pixel outside boundary processing
+ * @oainset_m: Filter input effective pixel outside boundary processing
+ * @oaoutset: Fixed data setting of filter output
+ * @oaoutset_m: Fixed data setting of filter output
+ * @scl_h_crop_mode: Cropping setting of horizontal (common to Scaler0-3)
+ * @scl_v_crop_mode: Cropping setting of vertical (common to Scaler0-3)
+ * @scl_h_crop_pos: Starting position of horizontal cropping(common to Scaler0-3)
+ * @scl_v_crop_pos: Starting position of vertical cropping(common to Scaler0-3)
+ * @width_ratio: Width reduction/expansion rate of Scaler0 to 3
+ * @height_ratio: Height reduction/expansion rate of Scaler0 to 3
+ * @h_oaoutset: Left and right position for fixed data setting of filter output
+ * @v_oaoutset: Upper and lower position for fixed data setting of filter output
+ * @buffer_info: Table of buffer information
+ * @buffer_info_num: Number of buffer_info
+ */
+struct drv_pyramid_descriptor {
+ __u32 port_enable;
+ __u16 t00_sram_size;
+ __u16 config_done;
+ struct drv_ipa_addr tap_addr[DRV_PYRAMID_TAP_ELEMENT];
+ struct drv_ipa_addr r_stadr[DRV_PYRAMID_DMA_READ_PORT];
+ struct drv_ipa_addr r_endadr[DRV_PYRAMID_DMA_READ_PORT];
+ __u32 r_size;
+ __u32 mode2;
+ __u32 r_y_pitch;
+ __u32 r_c_pitch;
+ __u16 r_dmac_height[DRV_PYRAMID_DMA_READ_PORT];
+ __u16 r_cfg0;
+ __u16 r_sram_base[DRV_PYRAMID_DMA_READ_PORT];
+ __u16 r_sram_size[DRV_PYRAMID_DMA_READ_PORT];
+ struct drv_ipa_addr w_stadr[DRV_PYRAMID_DMA_WRITE_PORT];
+ struct drv_ipa_addr w_endadr[DRV_PYRAMID_DMA_WRITE_PORT];
+ __u32 w_size[DRV_PYRAMID_SCALER_ROUTE];
+ __u16 w_cfg0[DRV_PYRAMID_DMA_WRITE_PORT];
+ __u32 w_y_pitch[DRV_PYRAMID_SCALER_ROUTE];
+ __u32 w_c_pitch[DRV_PYRAMID_SCALER_ROUTE];
+ __u16 w_dmac_height[DRV_PYRAMID_DMA_WRITE_PORT];
+ __u16 w_sram_base[DRV_PYRAMID_DMA_WRITE_PORT];
+ __u16 w_sram_size[DRV_PYRAMID_DMA_WRITE_PORT];
+ __u32 mode1;
+ __u8 scl_filmode[DRV_PYRAMID_YCHV];
+ __u32 oainset;
+ __u32 oainset_m;
+ __u32 oaoutset[DRV_PYRAMID_SCALER_ROUTE];
+ __u32 oaoutset_m[DRV_PYRAMID_SCALER_ROUTE];
+ __u32 scl_h_crop_mode;
+ __u32 scl_v_crop_mode;
+ __u32 scl_h_crop_pos;
+ __u32 scl_v_crop_pos;
+ __u32 width_ratio[DRV_PYRAMID_SCALER_ROUTE];
+ __u32 height_ratio[DRV_PYRAMID_SCALER_ROUTE];
+ __u32 h_oaoutset[DRV_PYRAMID_SCALER_ROUTE];
+ __u32 v_oaoutset[DRV_PYRAMID_SCALER_ROUTE];
+ struct drv_ipa_buffer_info buffer_info[IPA_BUFFER_INDEX_MAX];
+ __s32 buffer_info_num;
+};
+
+#endif /* _UAPI_LINUX_PYRAMID_H */