summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShuyu Li <shuyu.li@amlogic.com>2019-03-27 20:55:05 +0800
committerJianxin Pan <jianxin.pan@amlogic.com>2019-05-16 06:17:23 -0700
commit9eafec254a22cce33f846b0398ec7ea0074ab1b0 (patch)
tree65e8c172e3689d8a950e93aec76ca7e72f5e2145
parent703c39d2c2d207c035125ccfb0f9f61ca3e00499 (diff)
downloadarm64-9eafec254a22cce33f846b0398ec7ea0074ab1b0.tar.gz
audio: hifi4dsp: Add tm2 hifi 4 dsp ctl driver support [1/1]
PD#SWPL-7574 Problem: Add hifi 4 dsp ctl driver support on tm2 Solution: Add hifi 4 dsp ctl driver support on tm2 Verify: Verified on T962E2_ab311 board Change-Id: Ifd07ec8b8e1c3aeaa1a446a2a0eb9c00618e4ba5 Signed-off-by: Shuyu Li <shuyu.li@amlogic.com>
-rw-r--r--MAINTAINERS4
-rw-r--r--arch/arm/boot/dts/amlogic/mesontm2.dtsi21
-rw-r--r--arch/arm/boot/dts/amlogic/tm2_pxp.dts9
-rw-r--r--arch/arm/boot/dts/amlogic/tm2_t962e2_ab311.dts8
-rw-r--r--arch/arm/boot/dts/amlogic/tm2_t962e2_ab319.dts7
-rw-r--r--arch/arm/boot/dts/amlogic/tm2_t962x3_ab301.dts7
-rw-r--r--arch/arm/boot/dts/amlogic/tm2_t962x3_ab309.dts7
-rw-r--r--arch/arm/configs/meson64_a32_defconfig1
-rw-r--r--arch/arm64/boot/dts/amlogic/mesontm2.dtsi21
-rw-r--r--arch/arm64/boot/dts/amlogic/tm2_pxp.dts8
-rw-r--r--arch/arm64/boot/dts/amlogic/tm2_t962e2_ab311.dts8
-rw-r--r--arch/arm64/boot/dts/amlogic/tm2_t962e2_ab319.dts8
-rw-r--r--arch/arm64/boot/dts/amlogic/tm2_t962x3_ab301.dts8
-rw-r--r--arch/arm64/boot/dts/amlogic/tm2_t962x3_ab309.dts8
-rw-r--r--arch/arm64/configs/meson64_defconfig1
-rw-r--r--drivers/amlogic/Kconfig2
-rw-r--r--drivers/amlogic/Makefile2
-rw-r--r--drivers/amlogic/hifi4dsp/Kconfig11
-rw-r--r--drivers/amlogic/hifi4dsp/Makefile19
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_api.h43
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c397
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h228
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c259
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h58
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c323
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h97
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_module.c1141
-rw-r--r--drivers/amlogic/hifi4dsp/hifi4dsp_priv.h78
-rw-r--r--drivers/amlogic/hifi4dsp/tm2_dsp_top.c525
-rw-r--r--drivers/amlogic/hifi4dsp/tm2_dsp_top.h36
30 files changed, 3345 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 0775fa6bfa3a..b65fb09f6266 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14924,3 +14924,7 @@ F: drivers/amlogic/input/touchscreen/hyn_cst2xx/*
AMLOGIC WEEKLY CHANGE GENERATOR
M: JIAMIN MA <jiamin.ma@amlogic.com>
F: scripts/amlogic/weekly_change.py
+
+ANLOGIC HIFI4DSP
+M: Shuyu Li <Shuyu.Li@amlogic.com>
+F: drivers/amlogic/hifi4dsp/*
diff --git a/arch/arm/boot/dts/amlogic/mesontm2.dtsi b/arch/arm/boot/dts/amlogic/mesontm2.dtsi
index c15290f3ef34..1c1bc9029863 100644
--- a/arch/arm/boot/dts/amlogic/mesontm2.dtsi
+++ b/arch/arm/boot/dts/amlogic/mesontm2.dtsi
@@ -1619,6 +1619,27 @@
cpu_ver_name {
compatible = "amlogic, cpu-major-id-tm2";
};
+
+ hifi4dsp: hifi4dsp {
+ compatible = "amlogic, hifi4dsp";
+ memory-region = <&dsp_fw_reserved>;
+ reserved_mem_size = <0x00400000>;
+ reg = <0xff680000 0x10000
+ 0xff690000 0x10000>;
+ reg-names = "dspa_top_reg","dspb_top_reg";
+ interrupts = <0 242 1
+ 0 246 1>;
+ interrupt-names = "irq_frm_dspa","irq_frm_dspb";
+ clocks = <&clkc CLKID_DSPA
+ &clkc CLKID_DSPA_MUX
+ &clkc CLKID_DSPB
+ &clkc CLKID_DSPB_MUX>;
+ clock-names = "dspa_gate", "dspa_clk",
+ "dspb_gate", "dspb_gate";
+ dsp-cnt = <2>;
+ status = "okay";
+ };
+
}; /* end of / */
&pinctrl_aobus {
diff --git a/arch/arm/boot/dts/amlogic/tm2_pxp.dts b/arch/arm/boot/dts/amlogic/tm2_pxp.dts
index bdcede1c29ab..34910ea376bd 100644
--- a/arch/arm/boot/dts/amlogic/tm2_pxp.dts
+++ b/arch/arm/boot/dts/amlogic/tm2_pxp.dts
@@ -144,6 +144,14 @@
alignment = <0x0>;
linux,contiguous-region;
};
+
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x01000000>;
+ alignment = <0x00400000>;
+ alloc-ranges = <0x30000000 0x01000000>;
+ };
}; /* end of reserved-memory */
codec_mm {
@@ -780,6 +788,7 @@
*/
};
};
+
}; /* end of / */
&i2c0 {
diff --git a/arch/arm/boot/dts/amlogic/tm2_t962e2_ab311.dts b/arch/arm/boot/dts/amlogic/tm2_t962e2_ab311.dts
index dd6f76dd8aa6..8d055f7b062b 100644
--- a/arch/arm/boot/dts/amlogic/tm2_t962e2_ab311.dts
+++ b/arch/arm/boot/dts/amlogic/tm2_t962e2_ab311.dts
@@ -185,6 +185,14 @@
alignment = <0x0>;
linux,contiguous-region;
};
+
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x01000000>;
+ alignment = <0x00400000>;
+ alloc-ranges = <0x30000000 0x01000000>;
+ };
}; /* end of reserved-memory */
galcore {
status = "okay";
diff --git a/arch/arm/boot/dts/amlogic/tm2_t962e2_ab319.dts b/arch/arm/boot/dts/amlogic/tm2_t962e2_ab319.dts
index c39ae3e2518b..db2014ed3adf 100644
--- a/arch/arm/boot/dts/amlogic/tm2_t962e2_ab319.dts
+++ b/arch/arm/boot/dts/amlogic/tm2_t962e2_ab319.dts
@@ -182,6 +182,13 @@
alignment = <0x0>;
linux,contiguous-region;
};
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x01000000>;
+ alignment = <0x00400000>;
+ alloc-ranges = <0x30000000 0x01000000>;
+ };
}; /* end of reserved-memory */
galcore {
status = "okay";
diff --git a/arch/arm/boot/dts/amlogic/tm2_t962x3_ab301.dts b/arch/arm/boot/dts/amlogic/tm2_t962x3_ab301.dts
index 2e2f38c7e768..c84e55110888 100644
--- a/arch/arm/boot/dts/amlogic/tm2_t962x3_ab301.dts
+++ b/arch/arm/boot/dts/amlogic/tm2_t962x3_ab301.dts
@@ -186,6 +186,13 @@
alignment = <0x0>;
linux,contiguous-region;
};
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x01000000>;
+ alignment = <0x00400000>;
+ alloc-ranges = <0x30000000 0x01000000>;
+ };
}; /* end of reserved-memory */
galcore {
status = "okay";
diff --git a/arch/arm/boot/dts/amlogic/tm2_t962x3_ab309.dts b/arch/arm/boot/dts/amlogic/tm2_t962x3_ab309.dts
index dddc68e4f6a8..a1aa9135f823 100644
--- a/arch/arm/boot/dts/amlogic/tm2_t962x3_ab309.dts
+++ b/arch/arm/boot/dts/amlogic/tm2_t962x3_ab309.dts
@@ -183,6 +183,13 @@
alignment = <0x0>;
linux,contiguous-region;
};
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x01000000>;
+ alignment = <0x00400000>;
+ alloc-ranges = <0x30000000 0x01000000>;
+ };
}; /* end of reserved-memory */
galcore {
status = "okay";
diff --git a/arch/arm/configs/meson64_a32_defconfig b/arch/arm/configs/meson64_a32_defconfig
index 457821aeb0ab..365651d521cc 100644
--- a/arch/arm/configs/meson64_a32_defconfig
+++ b/arch/arm/configs/meson64_a32_defconfig
@@ -385,6 +385,7 @@ CONFIG_AMLOGIC_DEBUG_LOCKUP=y
CONFIG_AMLOGIC_DEFENDKEY=y
CONFIG_AMLOGIC_BATTERY_DUMMY=y
CONFIG_AMLOGIC_CHARGER_DUMMY=y
+CONFIG_AMLOGIC_HIFI4DSP=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
diff --git a/arch/arm64/boot/dts/amlogic/mesontm2.dtsi b/arch/arm64/boot/dts/amlogic/mesontm2.dtsi
index bcbb4356a670..190fc6c48255 100644
--- a/arch/arm64/boot/dts/amlogic/mesontm2.dtsi
+++ b/arch/arm64/boot/dts/amlogic/mesontm2.dtsi
@@ -1608,6 +1608,27 @@
cpu_ver_name {
compatible = "amlogic, cpu-major-id-tm2";
};
+
+ hifi4dsp: hifi4dsp {
+ compatible = "amlogic, hifi4dsp";
+ memory-region = <&dsp_fw_reserved>;
+ reserved_mem_size = <0x00400000>;
+ reg = <0x0 0xff680000 0x0 0x10000
+ 0x0 0xff690000 0x0 0x10000>;
+ reg-names = "dspa_top_reg","dspb_top_reg";
+ interrupts = <0 242 1
+ 0 246 1>;
+ interrupt-names = "irq_frm_dspa","irq_frm_dspb";
+ clocks = <&clkc CLKID_DSPA
+ &clkc CLKID_DSPA_MUX
+ &clkc CLKID_DSPB
+ &clkc CLKID_DSPB_MUX>;
+ clock-names = "dspa_gate", "dspa_clk",
+ "dspb_gate", "dspb_gate";
+ dsp-cnt = <2>;
+ status = "okay";
+ };
+
}; /* end of / */
&pinctrl_aobus {
diff --git a/arch/arm64/boot/dts/amlogic/tm2_pxp.dts b/arch/arm64/boot/dts/amlogic/tm2_pxp.dts
index 94efd821f41f..f4f613490d41 100644
--- a/arch/arm64/boot/dts/amlogic/tm2_pxp.dts
+++ b/arch/arm64/boot/dts/amlogic/tm2_pxp.dts
@@ -143,6 +143,14 @@
alignment = <0x0 0x0>;
linux,contiguous-region;
};
+
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x0 0x01000000>;
+ alignment = <0x0 0x00400000>;
+ alloc-ranges = <0x0 0x30000000 0x0 0x01000000>;
+ };
}; /* end of reserved-memory */
codec_mm {
diff --git a/arch/arm64/boot/dts/amlogic/tm2_t962e2_ab311.dts b/arch/arm64/boot/dts/amlogic/tm2_t962e2_ab311.dts
index f04454547bb2..fdf9cc58663a 100644
--- a/arch/arm64/boot/dts/amlogic/tm2_t962e2_ab311.dts
+++ b/arch/arm64/boot/dts/amlogic/tm2_t962e2_ab311.dts
@@ -181,6 +181,14 @@
alignment = <0x0 0x0>;
linux,contiguous-region;
};
+
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x0 0x01000000>;
+ alignment = <0x0 0x00400000>;
+ alloc-ranges = <0x0 0x30000000 0x0 0x01000000>;
+ };
}; /* end of reserved-memory */
galcore {
status = "okay";
diff --git a/arch/arm64/boot/dts/amlogic/tm2_t962e2_ab319.dts b/arch/arm64/boot/dts/amlogic/tm2_t962e2_ab319.dts
index db003a7045c9..78fce0a4503e 100644
--- a/arch/arm64/boot/dts/amlogic/tm2_t962e2_ab319.dts
+++ b/arch/arm64/boot/dts/amlogic/tm2_t962e2_ab319.dts
@@ -182,6 +182,14 @@
alignment = <0x0 0x0>;
linux,contiguous-region;
};
+
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x0 0x01000000>;
+ alignment = <0x0 0x00400000>;
+ alloc-ranges = <0x0 0x30000000 0x0 0x01000000>;
+ };
}; /* end of reserved-memory */
galcore {
status = "okay";
diff --git a/arch/arm64/boot/dts/amlogic/tm2_t962x3_ab301.dts b/arch/arm64/boot/dts/amlogic/tm2_t962x3_ab301.dts
index a2576f3a14ad..7e38b567a38c 100644
--- a/arch/arm64/boot/dts/amlogic/tm2_t962x3_ab301.dts
+++ b/arch/arm64/boot/dts/amlogic/tm2_t962x3_ab301.dts
@@ -183,6 +183,14 @@
alignment = <0x0 0x0>;
linux,contiguous-region;
};
+
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x0 0x01000000>;
+ alignment = <0x0 0x00400000>;
+ alloc-ranges = <0x0 0x30000000 0x0 0x01000000>;
+ };
}; /* end of reserved-memory */
galcore {
status = "okay";
diff --git a/arch/arm64/boot/dts/amlogic/tm2_t962x3_ab309.dts b/arch/arm64/boot/dts/amlogic/tm2_t962x3_ab309.dts
index bd0d17c74b03..50d7b7c13522 100644
--- a/arch/arm64/boot/dts/amlogic/tm2_t962x3_ab309.dts
+++ b/arch/arm64/boot/dts/amlogic/tm2_t962x3_ab309.dts
@@ -183,6 +183,14 @@
alignment = <0x0 0x0>;
linux,contiguous-region;
};
+
+ dsp_fw_reserved:linux,dsp_fw {
+ compatible = "shared-dma-pool";
+ reusable;
+ size = <0x0 0x01000000>;
+ alignment = <0x0 0x00400000>;
+ alloc-ranges = <0x0 0x30000000 0x0 0x01000000>;
+ };
}; /* end of reserved-memory */
galcore {
status = "okay";
diff --git a/arch/arm64/configs/meson64_defconfig b/arch/arm64/configs/meson64_defconfig
index eefdff94ea2a..90cf7fe0e148 100644
--- a/arch/arm64/configs/meson64_defconfig
+++ b/arch/arm64/configs/meson64_defconfig
@@ -380,6 +380,7 @@ CONFIG_AMLOGIC_DEFENDKEY=y
CONFIG_AMLOGIC_BATTERY_DUMMY=y
CONFIG_AMLOGIC_CHARGER_DUMMY=y
CONFIG_DOLBY_FW=y
+CONFIG_AMLOGIC_HIFI4DSP=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
diff --git a/drivers/amlogic/Kconfig b/drivers/amlogic/Kconfig
index e12f32c74e7c..52c163ea84be 100644
--- a/drivers/amlogic/Kconfig
+++ b/drivers/amlogic/Kconfig
@@ -140,5 +140,7 @@ source "drivers/amlogic/dolby_fw/Kconfig"
source "drivers/amlogic/ircut/Kconfig"
+source "drivers/amlogic/hifi4dsp/Kconfig"
+
endmenu
endif
diff --git a/drivers/amlogic/Makefile b/drivers/amlogic/Makefile
index 0d79615bda7b..fec14e294b5e 100644
--- a/drivers/amlogic/Makefile
+++ b/drivers/amlogic/Makefile
@@ -104,6 +104,8 @@ obj-$(CONFIG_AMLOGIC_BT_DEVICE) += bluetooth/
obj-$(CONFIG_AMLOGIC_WIFI) += wifi/
+obj-$(CONFIG_AMLOGIC_HIFI4DSP) += hifi4dsp/
+
obj-$(CONFIG_AMLOGIC_POWER) += power/
obj-$(CONFIG_AMLOGIC_PCIE) += pci/
diff --git a/drivers/amlogic/hifi4dsp/Kconfig b/drivers/amlogic/hifi4dsp/Kconfig
new file mode 100644
index 000000000000..622e835e1e07
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/Kconfig
@@ -0,0 +1,11 @@
+# audio hifi4dsp configuration
+#
+menu "AMLOGIC HiFi4DSP process"
+
+config AMLOGIC_HIFI4DSP
+ tristate "hifi4dsp control support"
+ default n
+ help
+ support the amlogic hifi4dsp;
+
+endmenu
diff --git a/drivers/amlogic/hifi4dsp/Makefile b/drivers/amlogic/hifi4dsp/Makefile
new file mode 100644
index 000000000000..afac2b187f4a
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/Makefile
@@ -0,0 +1,19 @@
+
+# Makefile for tm2 hifi4dsp
+
+hifi4dsp-objs = hifi4dsp_module.o \
+ hifi4dsp_firmware.o \
+ hifi4dsp_dsp.o \
+ hifi4dsp_ipc.o \
+ tm2_dsp_top.o
+
+#audiodsp-objs += pcmenc_module.o pcmenc_stream.o
+#audiodsp-objs += spdif_module.o
+
+ifneq ($(KBUILD_SRC),)
+TOP_KBUILD_SRC := $(KBUILD_SRC)/
+endif
+
+obj-$(CONFIG_AMLOGIC_HIFI4DSP) +=hifi4dsp.o
+
+#EXTRA_CFLAGS = -DENABLE_WAIT_FORMAT
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_api.h b/drivers/amlogic/hifi4dsp/hifi4dsp_api.h
new file mode 100644
index 000000000000..04c60226a01a
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_api.h
@@ -0,0 +1,43 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_api.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __AML_HIFI4DSP_API_H__
+#define __AML_HIFI4DSP_API_H__
+
+struct hifi4dsp_info_t {
+ char id; /*dsp_id 0,1,2...*/
+ char fw_id;
+ char fw_name[32]; /*name of firmware which used for dsp*/
+ long phy_addr; /*phy address of firmware wille be loaded on*/
+};
+
+#define HIFI4DSP_IOC_MAGIC 'H'
+
+#define HIFI4DSP_LOAD _IOWR(HIFI4DSP_IOC_MAGIC, 1, struct hifi4dsp_info_t)
+#define HIFI4DSP_RESET _IOWR(HIFI4DSP_IOC_MAGIC, 2, struct hifi4dsp_info_t)
+#define HIFI4DSP_START _IOWR(HIFI4DSP_IOC_MAGIC, 3, struct hifi4dsp_info_t)
+#define HIFI4DSP_STOP _IOWR(HIFI4DSP_IOC_MAGIC, 4, struct hifi4dsp_info_t)
+#define HIFI4DSP_SLEEP _IOWR(HIFI4DSP_IOC_MAGIC, 5, struct hifi4dsp_info_t)
+#define HIFI4DSP_WAKE _IOWR(HIFI4DSP_IOC_MAGIC, 6, struct hifi4dsp_info_t)
+
+#define HIFI4DSP_GET_INFO _IOWR((HIFI4DSP_IOC_MAGIC), (18), \
+ struct hifi4dsp_info_t)
+
+#define HIFI4DSP_TEST _IO(HIFI4DSP_IOC_MAGIC, 255)
+
+
+#endif /*__AML_AUDIO_HIFI4DSP_API_H__ */
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c b/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c
new file mode 100644
index 000000000000..c0edcf96a145
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c
@@ -0,0 +1,397 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+#define DEBUG
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/firmware.h>
+#include <linux/amlogic/major.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include "hifi4dsp_priv.h"
+#include "hifi4dsp_firmware.h"
+#include "hifi4dsp_dsp.h"
+
+//static struct hifi4dsp_dsp *dsp_g;
+
+/* Internal generic low-level hifi4dsp share memory write/read functions*/
+void hifi4dsp_smem_write(void __iomem *addr, u32 offset, u32 value)
+{
+ writel(value, addr + offset);
+}
+EXPORT_SYMBOL(hifi4dsp_smem_write);
+
+u32 hifi4dsp_smem_read(void __iomem *addr, u32 offset)
+{
+ return readl(addr + offset);
+}
+EXPORT_SYMBOL(hifi4dsp_smem_read);
+
+void hifi4dsp_smem_write64(void __iomem *addr, u32 offset, u64 value)
+{
+ memcpy_toio(addr + offset, &value, sizeof(value));
+}
+EXPORT_SYMBOL(hifi4dsp_smem_write64);
+
+u64 hifi4dsp_smem_read64(void __iomem *addr, u32 offset)
+{
+ u64 val;
+
+ memcpy_fromio(&val, addr + offset, sizeof(val));
+ return val;
+}
+EXPORT_SYMBOL(hifi4dsp_smem_read64);
+
+static inline void _hifi4dsp_memcpy_toio_32(u32 __iomem *dest,
+ u32 *src, size_t bytes)
+{
+ int i, words = bytes >> 2;
+
+ for (i = 0; i < words; i++)
+ writel(src[i], dest + i);
+}
+
+static inline void _hifi4dsp_memcpy_fromio_32(u32 *dest,
+ const __iomem u32 *src, size_t bytes)
+{
+ int i, words = bytes >> 2;
+
+ for (i = 0; i < words; i++)
+ dest[i] = readl(src + i);
+}
+
+void hifi4dsp_memcpy_toio_32(struct hifi4dsp_dsp *dsp,
+ void __iomem *dest, void *src, size_t bytes)
+{
+ _hifi4dsp_memcpy_toio_32(dest, src, bytes);
+}
+EXPORT_SYMBOL(hifi4dsp_memcpy_toio_32);
+
+void hifi4dsp_memcpy_fromio_32(struct hifi4dsp_dsp *dsp, void *dest,
+ void __iomem *src, size_t bytes)
+{
+ _hifi4dsp_memcpy_fromio_32(dest, src, bytes);
+}
+EXPORT_SYMBOL(hifi4dsp_memcpy_fromio_32);
+
+/* Public API */
+void hifi4dsp_dsp_smem_write(struct hifi4dsp_dsp *dsp, u32 offset, u32 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dsp->spinlock, flags);
+ dsp->ops->write(dsp->addr.smem, offset, value);
+ spin_unlock_irqrestore(&dsp->spinlock, flags);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_write);
+
+u32 hifi4dsp_dsp_smem_read(struct hifi4dsp_dsp *dsp, u32 offset)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&dsp->spinlock, flags);
+ val = dsp->ops->read(dsp->addr.smem, offset);
+ spin_unlock_irqrestore(&dsp->spinlock, flags);
+
+ return val;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_read);
+
+void hifi4dsp_dsp_smem_write64(struct hifi4dsp_dsp *dsp, u32 offset, u64 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dsp->spinlock, flags);
+ dsp->ops->write64(dsp->addr.smem, offset, value);
+ spin_unlock_irqrestore(&dsp->spinlock, flags);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_write64);
+
+u64 hifi4dsp_dsp_smem_read64(struct hifi4dsp_dsp *dsp, u32 offset)
+{
+ unsigned long flags;
+ u64 val;
+
+ spin_lock_irqsave(&dsp->spinlock, flags);
+ val = dsp->ops->read64(dsp->addr.smem, offset);
+ spin_unlock_irqrestore(&dsp->spinlock, flags);
+
+ return val;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_read64);
+
+void hifi4dsp_dsp_smem_write_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset, u32 value)
+{
+ dsp->ops->write(dsp->addr.smem, offset, value);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_write_unlocked);
+
+u32 hifi4dsp_dsp_smem_read_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset)
+{
+ return dsp->ops->read(dsp->addr.smem, offset);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_read_unlocked);
+
+void hifi4dsp_dsp_smem_write64_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset, u64 value)
+{
+ dsp->ops->write64(dsp->addr.smem, offset, value);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_write64_unlocked);
+
+u64 hifi4dsp_dsp_smem_read64_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset)
+{
+ return dsp->ops->read64(dsp->addr.smem, offset);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_read64_unlocked);
+
+int hifi4dsp_dsp_smem_update_bits_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset, u32 mask, u32 value)
+{
+ bool change;
+ unsigned int old, new;
+ u32 ret;
+
+ ret = hifi4dsp_dsp_smem_read_unlocked(dsp, offset);
+
+ old = ret;
+ new = (old & (~mask)) | (value & mask);
+
+ change = (old != new);
+ if (change)
+ hifi4dsp_dsp_smem_write_unlocked(dsp, offset, new);
+
+ return change;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_unlocked);
+
+void hifi4dsp_dsp_smem_update_bits_forced_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset, u32 mask, u32 value)
+{
+ unsigned int old, new;
+ u32 ret;
+
+ ret = hifi4dsp_dsp_smem_read_unlocked(dsp, offset);
+
+ old = ret;
+ new = (old & (~mask)) | (value & mask);
+
+ hifi4dsp_dsp_smem_write_unlocked(dsp, offset, new);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_forced_unlocked);
+
+int hifi4dsp_dsp_smem_update_bits64_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset, u64 mask, u64 value)
+{
+ bool change;
+ u64 old, new;
+
+ old = hifi4dsp_dsp_smem_read64_unlocked(dsp, offset);
+
+ new = (old & (~mask)) | (value & mask);
+
+ change = (old != new);
+ if (change)
+ hifi4dsp_dsp_smem_write64_unlocked(dsp, offset, new);
+
+ return change;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits64_unlocked);
+
+int hifi4dsp_dsp_smem_update_bits(struct hifi4dsp_dsp *dsp, u32 offset,
+ u32 mask, u32 value)
+{
+ unsigned long flags;
+ bool change;
+
+ spin_lock_irqsave(&dsp->spinlock, flags);
+ change = hifi4dsp_dsp_smem_update_bits_unlocked(dsp,
+ offset, mask, value);
+ spin_unlock_irqrestore(&dsp->spinlock, flags);
+ return change;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits);
+
+void hifi4dsp_dsp_smem_update_bits_forced(struct hifi4dsp_dsp *dsp,
+ u32 offset, u32 mask, u32 value)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dsp->spinlock, flags);
+ hifi4dsp_dsp_smem_update_bits_forced_unlocked(dsp, offset, mask, value);
+ spin_unlock_irqrestore(&dsp->spinlock, flags);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits_forced);
+
+int hifi4dsp_dsp_smem_update_bits64(struct hifi4dsp_dsp *dsp,
+ u32 offset, u64 mask, u64 value)
+{
+ unsigned long flags;
+ bool change;
+
+ spin_lock_irqsave(&dsp->spinlock, flags);
+ change = hifi4dsp_dsp_smem_update_bits64_unlocked(dsp,
+ offset, mask, value);
+ spin_unlock_irqrestore(&dsp->spinlock, flags);
+ return change;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_smem_update_bits64);
+
+int hifi4dsp_dsp_mailbox_init(struct hifi4dsp_dsp *dsp,
+ u32 outbox_offset, size_t outbox_size,
+ u32 inbox_offset, size_t inbox_size)
+{
+ dsp->mailbox.out_base = dsp->addr.smem + outbox_offset;
+ dsp->mailbox.in_base = dsp->addr.smem + inbox_offset;
+ dsp->mailbox.out_size = outbox_size;
+ dsp->mailbox.in_size = inbox_size;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_init);
+
+void hifi4dsp_dsp_mailbox_outbox_write(struct hifi4dsp_dsp *dsp,
+ void *message, size_t bytes)
+{
+ memcpy_toio(dsp->mailbox.out_base, message, bytes);
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_outbox_write);
+
+void hifi4dsp_dsp_mailbox_outbox_read(struct hifi4dsp_dsp *dsp,
+ void *message, size_t bytes)
+{
+ memcpy_fromio(message, dsp->mailbox.out_base, bytes);
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_outbox_read);
+
+void hifi4dsp_dsp_mailbox_inbox_write(struct hifi4dsp_dsp *dsp,
+ void *message, size_t bytes)
+{
+ memcpy_toio(dsp->mailbox.in_base, message, bytes);
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_inbox_write);
+
+void hifi4dsp_dsp_mailbox_inbox_read(struct hifi4dsp_dsp *dsp,
+ void *message, size_t bytes)
+{
+ memcpy_fromio(message, dsp->mailbox.in_base, bytes);
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_dsp_mailbox_inbox_read);
+
+int hifi4dsp_dsp_boot(struct hifi4dsp_dsp *dsp)
+{
+ if (dsp->ops->boot)
+ dsp->ops->boot(dsp);
+ pr_debug("%s done\n", __func__);
+ return 0;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_boot);
+
+void hifi4dsp_dsp_reset(struct hifi4dsp_dsp *dsp)
+{
+ if (dsp->ops->reset)
+ dsp->ops->reset(dsp);
+ pr_debug("%s done\n", __func__);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_reset);
+
+void hifi4dsp_dsp_sleep(struct hifi4dsp_dsp *dsp)
+{
+ if (dsp->ops->sleep)
+ dsp->ops->sleep(dsp);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_sleep);
+
+int hifi4dsp_dsp_wake(struct hifi4dsp_dsp *dsp)
+{
+ if (dsp->ops->wake)
+ return dsp->ops->wake(dsp);
+ return 0;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_wake);
+
+void hifi4dsp_dsp_dump(struct hifi4dsp_dsp *dsp)
+{
+ if (dsp->ops->dump)
+ dsp->ops->dump(dsp);
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_dump);
+
+struct hifi4dsp_dsp *hifi4dsp_dsp_new(struct hifi4dsp_priv *priv,
+ struct hifi4dsp_pdata *pdata,
+ struct hifi4dsp_dsp_device *dsp_dev)
+{
+ int err = 0;
+ struct hifi4dsp_dsp *dsp;
+
+ dsp = kzalloc(sizeof(struct hifi4dsp_dsp), GFP_KERNEL);
+ if (dsp == NULL)
+ goto dsp_malloc_error;
+
+ mutex_init(&dsp->mutex);
+ spin_lock_init(&dsp->spinlock);
+ spin_lock_init(&dsp->fw_spinlock);
+ INIT_LIST_HEAD(&dsp->fw_list);
+
+ dsp->id = pdata->id;
+ dsp->irq = pdata->irq;
+ dsp->major_id = MAJOR(priv->dev->devt);
+ dsp->dev = priv->dev;
+ dsp->pdata = pdata;
+ dsp->priv = priv;
+ dsp->ops = dsp_dev->ops;
+
+ /* Initialise Audio DSP */
+ if (dsp->ops->init) {
+ err = dsp->ops->init(dsp, pdata);
+ if (err < 0)
+ return NULL;
+ }
+
+ /*Register the ISR here if necessary*/
+ /*
+ * err = request_threaded_irq(dsp->irq, dsp->ops->irq_handler,
+ * dsp_dev->thread, IRQF_SHARED, "HIFI4DSP", dsp);
+ * if (err)
+ * goto irq_err;
+ */
+ goto dsp_new_done;
+ /*
+ * irq_err:
+ * if (dsp->ops->free)
+ * dsp->ops->free(dsp);
+ */
+dsp_malloc_error:
+dsp_new_done:
+ return dsp;
+}
+EXPORT_SYMBOL(hifi4dsp_dsp_new);
+
+MODULE_AUTHOR("Shuyu Li");
+MODULE_DESCRIPTION("HiFi DSP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h b/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h
new file mode 100644
index 000000000000..66c21ccb2f3f
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h
@@ -0,0 +1,228 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _HIFI4DSP_DSP_H
+#define _HIFI4DSP_DSP_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/irqreturn.h>
+
+#include "hifi4dsp_firmware.h"
+
+struct firmware;
+struct hifi4dsp_pdata;
+struct hifi4dsp_dsp;
+
+/*
+ * DSP memory offsets and addresses.
+ */
+struct hifi4dsp_addr {
+ u32 smem_paddr;
+ u32 smem_size;
+ u32 reg_paddr;
+ u32 reg_size;
+ u32 fw_paddr;
+ u32 fw_size;
+ void __iomem *smem;
+ void __iomem *reg;
+ void __iomem *fw;
+};
+/*
+ * DSP Mailbox configuration.
+ */
+struct hifi4dsp_mailbox {
+ void __iomem *in_base;
+ void __iomem *out_base;
+ size_t in_size;
+ size_t out_size;
+};
+
+/*
+ * DSP Operations exported by platform Audio DSP driver.
+ */
+struct hifi4dsp_ops {
+ int (*boot)(struct hifi4dsp_dsp *);
+ int (*reset)(struct hifi4dsp_dsp *);
+ int (*wake)(struct hifi4dsp_dsp *);
+ int (*sleep)(struct hifi4dsp_dsp *);
+ /* Shim IO */
+ void (*write)(void __iomem *addr, u32 offset, u32 value);
+ u32 (*read)(void __iomem *addr, u32 offset);
+
+ /* DSP I/DRAM IO */
+ void (*ram_read)(struct hifi4dsp_dsp *dsp,
+ void *dest, void __iomem *src, size_t bytes);
+ void (*ram_write)(struct hifi4dsp_dsp *dsp,
+ void __iomem *dest, void *src, size_t bytes);
+ void (*write64)(void __iomem *addr, u32 offset, u64 value);
+ u64 (*read64)(void __iomem *addr, u32 offset);
+
+ void (*dump)(struct hifi4dsp_dsp *);
+
+ /* IRQ handlers */
+ irqreturn_t (*irq_handler)(int irq, void *context);
+
+ /* hifi4dsp init and free */
+ int (*init)(struct hifi4dsp_dsp *dsp, struct hifi4dsp_pdata *pdata);
+ void (*free)(struct hifi4dsp_dsp *dsp);
+
+ /* FW module parser/loader */
+ int (*parse_fw)(struct hifi4dsp_firmware *dsp_fw, void *pinfo);
+};
+
+/*
+ * hifi4dsp dsp device.
+ */
+struct hifi4dsp_dsp_device {
+ struct hifi4dsp_ops *ops;
+ irqreturn_t (*thread)(int irq, void *thread_context);
+ void *thread_context;
+};
+
+/*
+ * hifi4dsp Platform Data.
+ */
+struct hifi4dsp_pdata {
+ int id;
+ const char *name;
+ int irq;
+ unsigned int clk_freq;
+ phys_addr_t reg_paddr;
+ unsigned int reg_size;
+ void *reg;
+ /* Share memory */
+ phys_addr_t smem_paddr;
+ unsigned int smem_size;
+
+ /* Firmware */
+ char fw_name[32];
+ phys_addr_t fw_paddr; /*physical address of fw data*/
+ void *fw_buf;
+ int fw_size;
+ int fw_fmt;
+ int fw_max_size;
+
+ void *ops;
+ void *dsp; /*pointer to dsp*/
+ void *priv; /*pointer to priv*/
+};
+
+struct hifi4dsp_dsp {
+ u32 id;
+ int irq;
+ int freq;
+ /* runtime */
+ spinlock_t spinlock; /* used for IPC */
+ struct mutex mutex; /* used for fw */
+ struct device *dev;
+ struct device *dma_dev;
+ u32 major_id;
+ void *thread_context;
+
+ struct hifi4dsp_dsp_device *dsp_dev;
+ /* operations */
+ struct hifi4dsp_ops *ops;
+
+ /* base addresses */
+ struct hifi4dsp_addr addr;
+
+ /* mailbox */
+ struct hifi4dsp_mailbox mailbox;
+
+ /* platform data */
+ struct hifi4dsp_pdata *pdata;
+
+ /*fw support*/
+ struct hifi4dsp_firmware *dsp_fw;/*def fw*/
+ u32 fw_cnt;
+ spinlock_t fw_spinlock;
+ struct list_head fw_list;
+
+ u32 intr_status;
+ struct firmware *fw;
+
+ struct clk *dsp_clk;
+ struct clk *dsp_gate;
+
+ void *info;
+ void *priv;
+};
+
+/* Internal generic low-level hifi4dsp share memory write/read functions*/
+extern void hifi4dsp_smem_write(void __iomem *addr, u32 offset, u32 value);
+extern u32 hifi4dsp_smem_read(void __iomem *addr, u32 offset);
+extern void hifi4dsp_smem_write64(void __iomem *addr, u32 offset, u64 value);
+extern u64 hifi4dsp_smem_read64(void __iomem *addr, u32 offset);
+extern void hifi4dsp_memcpy_toio_32(struct hifi4dsp_dsp *dsp,
+ void __iomem *dest, void *src, size_t bytes);
+extern void hifi4dsp_memcpy_fromio_32(struct hifi4dsp_dsp *dsp,
+ void *dest, void __iomem *src, size_t bytes);
+
+extern void hifi4dsp_dsp_smem_write(struct hifi4dsp_dsp *dsp,
+ u32 offset, u32 value);
+extern u32 hifi4dsp_dsp_smem_read(struct hifi4dsp_dsp *dsp,
+ u32 offset);
+extern void hifi4dsp_dsp_smem_write64(struct hifi4dsp_dsp *dsp,
+ u32 offset, u64 value);
+extern u64 hifi4dsp_dsp_smem_read64(struct hifi4dsp_dsp *dsp,
+ u32 offset);
+extern void hifi4dsp_dsp_smem_write_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset, u32 value);
+extern u32 hifi4dsp_dsp_smem_read_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset);
+extern void hifi4dsp_dsp_smem_write64_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset, u64 value);
+extern u64 hifi4dsp_dsp_smem_read64_unlocked(struct hifi4dsp_dsp *dsp,
+ u32 offset);
+extern int hifi4dsp_dsp_smem_update_bits_unlocked(
+ struct hifi4dsp_dsp *dsp, u32 offset, u32 mask, u32 value);
+extern void hifi4dsp_dsp_smem_update_bits_forced_unlocked(
+ struct hifi4dsp_dsp *dsp, u32 offset, u32 mask, u32 value);
+extern int hifi4dsp_dsp_smem_update_bits64_unlocked(
+ struct hifi4dsp_dsp *dsp, u32 offset, u64 mask, u64 value);
+extern int hifi4dsp_dsp_smem_update_bits(struct hifi4dsp_dsp *dsp,
+ u32 offset, u32 mask, u32 value);
+extern void hifi4dsp_dsp_smem_update_bits_forced(struct hifi4dsp_dsp *dsp,
+ u32 offset, u32 mask, u32 value);
+extern int hifi4dsp_dsp_smem_update_bits64(struct hifi4dsp_dsp *dsp,
+ u32 offset, u64 mask, u64 value);
+
+extern int hifi4dsp_dsp_mailbox_init(struct hifi4dsp_dsp *dsp,
+ u32 outbox_offset, size_t outbox_size,
+ u32 inbox_offset, size_t inbox_size);
+extern void hifi4dsp_dsp_mailbox_outbox_write(struct hifi4dsp_dsp *dsp,
+ void *message, size_t bytes);
+extern void hifi4dsp_dsp_mailbox_outbox_read(struct hifi4dsp_dsp *dsp,
+ void *message, size_t bytes);
+extern void hifi4dsp_dsp_mailbox_inbox_write(struct hifi4dsp_dsp *dsp,
+ void *message, size_t bytes);
+extern void hifi4dsp_dsp_mailbox_inbox_read(struct hifi4dsp_dsp *dsp,
+ void *message, size_t bytes);
+extern int hifi4dsp_dsp_boot(struct hifi4dsp_dsp *dsp);
+extern void hifi4dsp_dsp_reset(struct hifi4dsp_dsp *dsp);
+extern void hifi4dsp_dsp_sleep(struct hifi4dsp_dsp *dsp);
+extern int hifi4dsp_dsp_wake(struct hifi4dsp_dsp *dsp);
+extern void hifi4dsp_dsp_dump(struct hifi4dsp_dsp *dsp);
+//extern struct hifi4dsp_dsp * hifi4dsp_dsp_new(struct hifi4dsp_priv *priv,
+// struct hifi4dsp_pdata *pdata, struct hifi4dsp_ops *ops);
+extern struct hifi4dsp_dsp *hifi4dsp_dsp_new(struct hifi4dsp_priv *priv,
+ struct hifi4dsp_pdata *pdata, struct hifi4dsp_dsp_device *dsp_dev);
+
+#endif /*_HIFI4DSP_DSP_H*/
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c b/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c
new file mode 100644
index 000000000000..c7ab5cbed5fe
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c
@@ -0,0 +1,259 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/firmware.h>
+#include <linux/amlogic/major.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+
+#include "hifi4dsp_priv.h"
+#include "hifi4dsp_firmware.h"
+#include "hifi4dsp_dsp.h"
+
+static inline void hifi4dsp_fw_memcpy(void __iomem *dest,
+ void *src, u32 bytes)
+{
+ memcpy_toio(dest, src, bytes);
+}
+
+/* general a new hifi4dsp_firmware object, called by hi-level functions */
+struct hifi4dsp_firmware *hifi4dsp_fw_new(struct hifi4dsp_dsp *dsp,
+ const struct firmware *fw, void *private)
+{
+ struct hifi4dsp_firmware *dsp_fw;
+
+ dsp_fw = kzalloc(sizeof(struct hifi4dsp_firmware), GFP_KERNEL);
+ if (dsp_fw == NULL)
+ goto dsp_fw_malloc_err;
+
+ dsp_fw->dsp = dsp;
+ dsp_fw->priv = dsp->priv;
+ if (private != NULL)
+ dsp_fw->private = private;
+ if (fw != NULL)
+ dsp_fw->size = fw->size;
+ pr_debug("%s done\n", __func__);
+
+dsp_fw_malloc_err:
+ return dsp_fw;
+}
+/*dsp_fw must be initied before this operation,
+ *special the *dsp pointer must not be null
+ */
+int hifi4dsp_fw_add(struct hifi4dsp_firmware *dsp_fw)
+{
+ unsigned long flags;
+ struct hifi4dsp_dsp *dsp;
+
+ if ((dsp_fw == NULL) || (dsp_fw->dsp == NULL))
+ return -1;
+ dsp = dsp_fw->dsp;
+ spin_lock_irqsave(&dsp->fw_spinlock, flags);
+ list_add_tail(&dsp_fw->list, &dsp->fw_list);
+ dsp->fw_cnt++;
+ dsp_fw->id = dsp->fw_cnt;
+ spin_unlock_irqrestore(&dsp->fw_spinlock, flags);
+ pr_debug("%s done\n", __func__);
+ return 0;
+}
+
+static struct hifi4dsp_firmware *hifi4dsp_fw_search_by_name(
+ struct hifi4dsp_dsp *dsp, char *name)
+{
+ struct hifi4dsp_firmware *dsp_fw = NULL;
+ struct hifi4dsp_firmware *pfw = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dsp->fw_spinlock, flags);
+ if (list_empty(&dsp->fw_list)) {
+ pfw = NULL;
+ pr_err("Firmware list is empty\n");
+ }
+
+ list_for_each_entry(dsp_fw, &dsp->fw_list, list) {
+ if (memcmp(dsp_fw->name, name, strlen(name)) == 0) {
+ pfw = dsp_fw;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&dsp->fw_spinlock, flags);
+ return pfw;
+}
+
+int hifi4dsp_dump_memory(const void *buf, unsigned int bytes, int col)
+{
+ int i = 0, n = 0, size = 0;
+ const u8 *pdata;
+ char str[1024];
+ char a_str[24];
+
+ pdata = (u8 *)buf;
+ size = bytes;
+ memset(str, '\0', sizeof(a_str));
+ memset(str, '\0', sizeof(str));
+ while (n < size) {
+ sprintf(a_str, "%p: ", pdata);
+ strcat(str, a_str);
+ col = ((size-n) > col)?col:(size-n);
+ for (i = 0; i < col; i++) {
+ sprintf(a_str, "%02x ", *(pdata+i));
+ strcat(str, a_str);
+ }
+ pr_info("%s\n", str);
+ memset(str, '\0', sizeof(str));/*re-init buf*/
+ pdata += col;
+ n += col;
+ }
+
+ return 0;
+}
+
+
+//resource res;
+int hifi4dsp_fw_copy_to_ddr(const struct firmware *fw,
+ struct hifi4dsp_firmware *dsp_fw)
+{
+ int fw_bytes = 0;
+ const u8 *fw_src;
+ void *fw_dst;
+
+ fw_src = fw->data;
+ fw_bytes = fw->size;
+ fw_dst = dsp_fw->buf;
+ hifi4dsp_dump_memory(fw_src, 32, 16);
+ hifi4dsp_dump_memory(fw_src+fw_bytes-32, 32, 16);
+ pr_debug("%s fw_src:0x%p, pdata_dst=0x%p ,szie=%d bytes\n",
+ __func__, fw_src, fw_dst, fw_bytes);
+ //memcpy(fw_dst, fw_src, fw_bytes);
+ memcpy_toio(fw_dst, fw_src, fw_bytes);
+ //do memory barrier
+ //mb();
+ /*TODO, if need add membarrier code*/
+ hifi4dsp_dump_memory(fw_dst, 32, 16);
+ hifi4dsp_dump_memory(fw_dst+fw_bytes-32, 32, 16);
+
+ return 0;
+}
+
+int hifi4dsp_fw_load(struct hifi4dsp_firmware *dsp_fw)
+{
+ const struct firmware *fw;
+ struct hifi4dsp_priv *priv;
+ struct hifi4dsp_dsp *dsp;
+ int err = 0;
+
+ pr_info("%s loading firmware %s\n", __func__, dsp_fw->name);
+
+ if ((dsp_fw == NULL) || (dsp_fw->dsp == NULL))
+ return -1;
+ priv = dsp_fw->priv;
+ dsp = dsp_fw->dsp;
+ err = request_firmware(&fw, dsp_fw->name, priv->dev);
+ if (err < 0) {
+ HIFI4DSP_PRNT("can't load the %s,err=%d\n", dsp_fw->name, err);
+ goto done;
+ }
+ if (fw == NULL) {
+ HIFI4DSP_PRNT("firmware pointer==NULL\n");
+ goto done;
+ }
+ if (dsp_fw == NULL) {
+ HIFI4DSP_PRNT("hifi4dsp_firmware pointer==NULL\n");
+ err = ENOMEM;
+ goto release;
+ }
+ dsp_fw->size = fw->size;
+ hifi4dsp_fw_copy_to_ddr(fw, dsp_fw);
+release:
+ release_firmware(fw);
+done:
+ return err;
+}
+
+int hifi4dsp_fw_reload(struct hifi4dsp_dsp *dsp,
+ struct hifi4dsp_firmware *dsp_fw)
+{
+ int err = 0;
+
+ return err;
+}
+
+int hifi4dsp_fw_unload(struct hifi4dsp_firmware *dsp_fw)
+{
+ int err = 0;
+
+ return err;
+}
+
+/* create a hifi4dsp_firmware object and
+ * add it to the fw_list of hifi4dsp_dsp
+ */
+struct hifi4dsp_firmware *hifi4dsp_fw_register(struct hifi4dsp_dsp *dsp,
+ char *name)
+{
+ struct hifi4dsp_firmware *dsp_fw;
+ int str_len = 0;
+
+ dsp_fw = hifi4dsp_fw_search_by_name(dsp, name);
+ if (dsp_fw != NULL) {
+ pr_info("%s firmware( %s ) has been registered\n",
+ __func__, name);
+ return dsp_fw;
+ }
+ dsp_fw = hifi4dsp_fw_new(dsp, NULL, NULL);
+ if (dsp_fw != NULL) {
+ str_len = sizeof(dsp_fw->name) - 1;
+ strncpy(dsp_fw->name, name, str_len);
+ hifi4dsp_fw_add(dsp_fw);
+ pr_info("%s %s done\n", __func__, dsp_fw->name);
+ }
+ return dsp_fw;
+}
+
+/* free single firmware object */
+void hifi4dsp_fw_free(struct hifi4dsp_firmware *dsp_fw)
+{
+ struct hifi4dsp_dsp *dsp = dsp_fw->dsp;
+
+ mutex_lock(&dsp->mutex);
+ list_del(&dsp_fw->list);
+ kfree(dsp_fw);
+ mutex_unlock(&dsp->mutex);
+}
+
+/* free all firmware objects */
+void hifi4dsp_fw_free_all(struct hifi4dsp_dsp *dsp)
+{
+ struct hifi4dsp_firmware *dsp_fw, *t;
+
+ mutex_lock(&dsp->mutex);
+ list_for_each_entry_safe(dsp_fw, t, &dsp->fw_list, list) {
+ list_del(&dsp_fw->list);
+ kfree(dsp_fw);
+ }
+ mutex_unlock(&dsp->mutex);
+}
+
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h b/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h
new file mode 100644
index 000000000000..fba82c01eca6
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h
@@ -0,0 +1,58 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _HIFI4DSP_FIRMWARE_H
+#define _HIFI4DSP_FIRMWARE_H
+
+struct firmware;
+struct hifi4dsp_priv;
+struct hifi4dsp_dsp;
+
+struct hifi4dsp_firmware_block {
+ int id;
+ struct list_head list;
+ phys_addr_t paddr;
+};
+
+struct hifi4dsp_firmware {
+ int id;
+ char name[32];
+ int size;
+ int fmt;
+ struct list_head list; /*fw list of DSP*/
+ phys_addr_t paddr; /*physical address of fw data*/
+ void *buf; /*virtual address of fw data*/
+
+ struct hifi4dsp_priv *priv;
+ struct hifi4dsp_dsp *dsp;
+
+ void *private;
+};
+
+extern int hifi4dsp_fw_load(struct hifi4dsp_firmware *dsp_fw);
+extern int hifi4dsp_fw_unload(struct hifi4dsp_firmware *dsp_fw);
+extern void hifi4dsp_fw_free(struct hifi4dsp_firmware *dsp_fw);
+extern int hifi4dsp_fw_add(struct hifi4dsp_firmware *dsp_fw);
+extern void hifi4dsp_fw_free_all(struct hifi4dsp_dsp *dsp);
+extern struct hifi4dsp_firmware *hifi4dsp_fw_new(struct hifi4dsp_dsp *dsp,
+ const struct firmware *fw, void *private);
+extern struct hifi4dsp_firmware *hifi4dsp_fw_register(struct hifi4dsp_dsp *dsp,
+ char *fw_name);
+extern int hifi4dsp_dump_memory(const void *buf, unsigned int bytes, int col);
+
+
+#endif /*_HIFI4DSP_FIRMWARE_H*/
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c b/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c
new file mode 100644
index 000000000000..20bfae1c415d
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c
@@ -0,0 +1,323 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/amlogic/major.h>
+
+#include "hifi4dsp_priv.h"
+#include "hifi4dsp_firmware.h"
+#include "hifi4dsp_dsp.h"
+#include "hifi4dsp_ipc.h"
+
+/* locks held by caller */
+static struct hifi4dsp_ipc_message *ipc_get_empty_msg(
+ struct hifi4dsp_ipc *ipc)
+{
+ struct hifi4dsp_ipc_message *msg = NULL;
+
+ if (!list_empty(&ipc->empty_list)) {
+ msg = list_first_entry(&ipc->empty_list,
+ struct hifi4dsp_ipc_message, list);
+ list_del(&msg->list);
+ }
+
+ return msg;
+}
+
+static int tx_wait_done(struct hifi4dsp_ipc *ipc,
+ struct hifi4dsp_ipc_message *msg, void *rx_data)
+{
+ unsigned long flags;
+ int ret;
+
+ /* wait for DSP completion (in all cases atm inc pending) */
+ ret = wait_event_timeout(msg->waitq, msg->complete,
+ msecs_to_jiffies(IPC_MSG_TIMEOUT_MSECS));
+
+ spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+ if (ret == 0) {
+ if (ipc->ops.debug_info != NULL)
+ ipc->ops.debug_info(ipc, "message timeout");
+
+ list_del(&msg->list);
+ ret = -ETIMEDOUT;
+ } else {
+
+ /* copy the data returned from DSP */
+ if (msg->rx_size)
+ memcpy(rx_data, msg->rx_data, msg->rx_size);
+ ret = msg->errno;
+ }
+
+ list_add_tail(&msg->list, &ipc->empty_list);
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ return ret;
+}
+
+static int ipc_tx_message(struct hifi4dsp_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes, void *rx_data,
+ size_t rx_bytes, int wait)
+{
+ struct hifi4dsp_ipc_message *msg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+ msg = ipc_get_empty_msg(ipc);
+ if (msg == NULL) {
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ return -EBUSY;
+ }
+
+ msg->header = header;
+ msg->tx_size = tx_bytes;
+ msg->rx_size = rx_bytes;
+ msg->wait = wait;
+ msg->errno = 0;
+ msg->pending = false;
+ msg->complete = false;
+
+ if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL))
+ ipc->ops.tx_data_copy(msg, tx_data, tx_bytes);
+
+ list_add_tail(&msg->list, &ipc->tx_list);
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+
+ kthread_queue_work(&ipc->kworker, &ipc->kwork);
+
+ if (wait)
+ return tx_wait_done(ipc, msg, rx_data);
+ else
+ return 0;
+}
+
+static int ipc_msg_empty_list_init(struct hifi4dsp_ipc *ipc)
+{
+ int i;
+
+ ipc->msg = kzalloc(sizeof(struct hifi4dsp_ipc_message) *
+ IPC_EMPTY_LIST_SIZE, GFP_KERNEL);
+ if (ipc->msg == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+ ipc->msg[i].tx_data = kzalloc
+ (ipc->tx_data_max_size, GFP_KERNEL);
+ if (ipc->msg[i].tx_data == NULL)
+ goto free_mem;
+
+ ipc->msg[i].rx_data = kzalloc
+ (ipc->rx_data_max_size, GFP_KERNEL);
+ if (ipc->msg[i].rx_data == NULL) {
+ kfree(ipc->msg[i].tx_data);
+ goto free_mem;
+ }
+
+ init_waitqueue_head(&ipc->msg[i].waitq);
+ list_add(&ipc->msg[i].list, &ipc->empty_list);
+ }
+
+ return 0;
+
+free_mem:
+ while (i > 0) {
+ kfree(ipc->msg[i-1].tx_data);
+ kfree(ipc->msg[i-1].rx_data);
+ --i;
+ }
+ kfree(ipc->msg);
+
+ return -ENOMEM;
+}
+
+static void ipc_tx_msgs(struct kthread_work *work)
+{
+ struct hifi4dsp_ipc *ipc =
+ container_of(work, struct hifi4dsp_ipc, kwork);
+ struct hifi4dsp_ipc_message *msg;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+ if (list_empty(&ipc->tx_list) || ipc->pending) {
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ return;
+ }
+
+ /* if the DSP is busy, we will TX messages after IRQ.
+ * also postpone if we are in the middle of procesing completion irq
+ */
+ if (ipc->ops.is_dsp_busy && ipc->ops.is_dsp_busy(ipc->dsp)) {
+ pr_err("ipc_tx_msgs dsp busy\n");
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+ return;
+ }
+
+ msg = list_first_entry(&ipc->tx_list,
+ struct hifi4dsp_ipc_message, list);
+ list_move(&msg->list, &ipc->rx_list);
+
+ if (ipc->ops.tx_msg != NULL)
+ ipc->ops.tx_msg(ipc, msg);
+
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+}
+
+int hifi4dsp_ipc_tx_message_wait(struct hifi4dsp_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes)
+{
+ return ipc_tx_message(ipc, header, tx_data, tx_bytes,
+ rx_data, rx_bytes, 1);
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_message_wait);
+
+int hifi4dsp_ipc_tx_message_nowait(struct hifi4dsp_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes)
+{
+ return ipc_tx_message(ipc, header, tx_data, tx_bytes,
+ NULL, 0, 0);
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_message_nowait);
+
+struct hifi4dsp_ipc_message *hifi4dsp_ipc_reply_find_msg(
+ struct hifi4dsp_ipc *ipc, u64 header)
+{
+ struct hifi4dsp_ipc_message *msg;
+ u64 mask = 0;
+
+ if (ipc->ops.reply_msg_match != NULL)
+ header = ipc->ops.reply_msg_match(header, &mask);
+
+ if (list_empty(&ipc->rx_list)) {
+ pr_err("error: rx list empty but received 0x%llx\n",
+ header);
+ return NULL;
+ }
+
+ list_for_each_entry(msg, &ipc->rx_list, list) {
+ if ((msg->header & mask) == header)
+ return msg;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_ipc_reply_find_msg);
+
+/* locks held by caller */
+void hifi4dsp_ipc_tx_msg_reply_complete(struct hifi4dsp_ipc *ipc,
+ struct hifi4dsp_ipc_message *msg)
+{
+ msg->complete = true;
+
+ if (!msg->wait)
+ list_add_tail(&msg->list, &ipc->empty_list);
+ else
+ wake_up(&msg->waitq);
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_ipc_tx_msg_reply_complete);
+
+void hifi4dsp_ipc_drop_all(struct hifi4dsp_ipc *ipc)
+{
+ struct hifi4dsp_ipc_message *msg, *tmp;
+ unsigned long flags;
+ int tx_drop_cnt = 0, rx_drop_cnt = 0;
+
+ /* drop all TX and Rx messages before we stall + reset DSP */
+ spin_lock_irqsave(&ipc->dsp->spinlock, flags);
+
+ list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) {
+ list_move(&msg->list, &ipc->empty_list);
+ tx_drop_cnt++;
+ }
+
+ list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) {
+ list_move(&msg->list, &ipc->empty_list);
+ rx_drop_cnt++;
+ }
+
+ spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
+
+ if (tx_drop_cnt || rx_drop_cnt)
+ pr_err("dropped IPC tx_msg cnt %d, rx_msg=%d\n",
+ tx_drop_cnt, rx_drop_cnt);
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_ipc_drop_all);
+
+int hifi4dsp_ipc_init(struct hifi4dsp_ipc *ipc)
+{
+ int ret;
+
+ INIT_LIST_HEAD(&ipc->tx_list);
+ INIT_LIST_HEAD(&ipc->rx_list);
+ INIT_LIST_HEAD(&ipc->empty_list);
+ init_waitqueue_head(&ipc->wait_txq);
+
+ ret = ipc_msg_empty_list_init(ipc);
+ if (ret < 0)
+ return -ENOMEM;
+
+ /* start the IPC message thread */
+ kthread_init_worker(&ipc->kworker);
+ ipc->tx_thread = kthread_run(kthread_worker_fn,
+ &ipc->kworker, "%s",
+ dev_name(ipc->dev));
+ if (IS_ERR(ipc->tx_thread)) {
+ pr_err("error: failed to create message TX task\n");
+ ret = PTR_ERR(ipc->tx_thread);
+ kfree(ipc->msg);
+ return ret;
+ }
+
+ kthread_init_work(&ipc->kwork, ipc_tx_msgs);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_ipc_init);
+
+void hifi4dsp_ipc_finish(struct hifi4dsp_ipc *ipc)
+{
+ int i;
+
+ if (ipc->tx_thread)
+ kthread_stop(ipc->tx_thread);
+
+ if (ipc->msg) {
+ for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) {
+ kfree(ipc->msg[i].tx_data);
+ kfree(ipc->msg[i].rx_data);
+ }
+ kfree(ipc->msg);
+ }
+}
+EXPORT_SYMBOL_GPL(hifi4dsp_ipc_finish);
+
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h b/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h
new file mode 100644
index 000000000000..efb239ab4f61
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h
@@ -0,0 +1,97 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _HIFI4DSP_IPC_H
+#define _HIFI4DSP_IPC_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+
+#include "hifi4dsp_dsp.h"
+
+struct hifi4dsp_ipc;
+
+#define IPC_MAILBOX_MAX_BYTES 256
+#define IPC_EMPTY_LIST_SIZE 8
+#define IPC_MSG_TIMEOUT_MSECS 300 /* IPC message timeout (msecs) */
+
+struct hifi4dsp_ipc_message {
+ struct list_head list;
+ u64 header;
+
+ char *tx_data;
+ size_t tx_size;
+ char *rx_data;
+ size_t rx_size;
+
+ wait_queue_head_t waitq;
+ bool pending;
+ bool complete;
+ bool wait;
+ int errno;
+};
+
+struct hifi4dsp_ipc_plat_ops {
+ void (*tx_msg)(struct hifi4dsp_ipc *, struct hifi4dsp_ipc_message *);
+ void (*tx_data_copy)(struct hifi4dsp_ipc_message *, char *, size_t);
+ bool (*is_dsp_busy)(struct hifi4dsp_dsp *dsp);
+ void (*debug_info)(struct hifi4dsp_ipc *, const char *);
+ u64 (*reply_msg_match)(u64 header, u64 *mask);
+};
+
+struct hifi4dsp_ipc {
+ struct device *dev;
+ struct hifi4dsp_dsp *dsp;
+
+ /* IPC messaging */
+ struct list_head tx_list;
+ struct list_head rx_list;
+ struct list_head empty_list;
+ wait_queue_head_t wait_txq;
+ struct task_struct *tx_thread;
+ struct kthread_worker kworker;
+ struct kthread_work kwork;
+ bool pending;
+ struct hifi4dsp_ipc_message *msg;
+ int tx_data_max_size;
+ int rx_data_max_size;
+
+ struct hifi4dsp_ipc_plat_ops ops;
+};
+
+int hifi4dsp_ipc_tx_message_wait(struct hifi4dsp_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes);
+
+int hifi4dsp_ipc_tx_message_nowait(struct hifi4dsp_ipc *ipc, u64 header,
+ void *tx_data, size_t tx_bytes);
+
+struct hifi4dsp_ipc_message *hifi4dsp_ipc_reply_find_msg(
+ struct hifi4dsp_ipc *ipc, u64 header);
+
+void hifi4dsp_ipc_tx_msg_reply_complete(struct hifi4dsp_ipc *ipc,
+ struct hifi4dsp_ipc_message *msg);
+
+void hifi4dsp_ipc_drop_all(struct hifi4dsp_ipc *ipc);
+int hifi4dsp_ipc_init(struct hifi4dsp_ipc *ipc);
+void hifi4dsp_ipc_fini(struct hifi4dsp_ipc *ipc);
+
+#endif /*_HIFI4DSP_IPC_H*/
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_module.c b/drivers/amlogic/hifi4dsp/hifi4dsp_module.c
new file mode 100644
index 000000000000..6dd3b51c40e4
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_module.c
@@ -0,0 +1,1141 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_module.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+#define DEBUG
+
+#define pr_fmt(fmt) "hifi4dsp: " fmt
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/reset.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/cma.h>
+#include <linux/dma-contiguous.h>
+#include <linux/vmalloc.h>
+#include <linux/clk.h>
+#include <asm/cacheflush.h>
+
+#include <linux/amlogic/major.h>
+#include <linux/amlogic/media/utils/amstream.h>
+#include <linux/amlogic/media/sound/aiu_regs.h>
+#include <linux/amlogic/media/frame_sync/ptsserv.h>
+#include <linux/amlogic/media/frame_sync/timestamp.h>
+#include <linux/amlogic/media/frame_sync/tsync.h>
+
+#include "hifi4dsp_api.h"
+#include "hifi4dsp_priv.h"
+#include "hifi4dsp_firmware.h"
+#include "hifi4dsp_dsp.h"
+#include "hifi4dsp_ipc.h"
+
+#include "tm2_dsp_top.h"
+
+#define HIFI4DSP_NUM 2
+#define HIFI4DSP_MAX_CNT 2
+#define DSP_PHY_ADDR_0 0x30000000
+#define DSP_PHY_ADDR_1 0x30800000
+
+struct hifi4dsp_priv *hifi4dsp_p[HIFI4DSP_MAX_CNT];
+unsigned int hifi4dsp_debug_flag = 1;
+
+#define DEF_RESERVE_MEM_SIZE 0x800000
+
+#define HIFI4DSP_TM2_ALL_SMEM_SIZE (0x00300000) /* 3 MBytes */
+#define HIFI4DSP_TM2_BASE_PADDR DSP_PHY_ADDR_0
+#define HIFI4DSP_TM2_IRAM_OFFSET (0x00000000)
+#define HIFI4DSP_TM2_DRAM_OFFSET (0x00100000 + \
+ HIFI4DSP_TM2_IRAM_OFFSET)
+#define HIFI4DSP_TM2_SMEM_OFFSET (0x00040000 + \
+ HIFI4DSP_TM2_DRAM_OFFSET)
+#define HIFI4DSP_TM2_MAILBOX_OFFSET (0x00004000 + \
+ HIFI4DSP_TM2_SMEM_OFFSET)
+#define HIFI4DSP_TM2_TIMESTAMP_OFFSET (0x00000800 +\
+ HIFI4DSP_TM2_MAILBOX_OFFSET)
+#define HIFI4DSP_TM2_IPC_MAX_SIZE 256
+
+#define IPC_MSG_HEADER 0x10
+
+#define MASK_BF(x, mask, shift) (((x&mask)<<shift))
+ /* msg header bf */
+#define IPC_MSG_HEADER_ID_SHIFT 0
+#define IPC_MSG_HEADER_ID_MASK (0xFF)
+#define IPC_MSG_HEADER_ID(x) MASK_BF(x, 0xFF, 0)
+
+#define IPC_MSG_HEADER_SENDERID_SHIFT 8
+#define IPC_MSG_HEADER_SENDERID_MASK (0xFF)
+#define IPC_MSG_HEADER_SENDERID(x) MASK_BF(x, 0xFF, 8)
+
+#define IPC_MSG_HEADER_NOTIFY_SHIFT 16
+#define IPC_MSG_HEADER_NOTIFY_MASK (0x1)
+#define IPC_MSG_HEADER_NOTIFY(x) MASK_BF(x, 0x1, 16)
+
+#define IPC_MSG_HEADER_WITH_DATA_SHIFT 17
+#define IPC_MSG_HEADER_WITH_DATA_MASK (0x1)
+#define IPC_MSG_HEADER_WITH_DATA(x) MASK_BF(x, 0x1, 17)
+
+#define IPC_MSG_HEADER_DATA_BYTES_SHIFT 18
+#define IPC_MSG_HEADER_DATA_BYTES_MASK (0xFFFC)
+#define IPC_MSG_HEADER_DATA_BYTES(x) MASK_BF(x, 0xFFFC, 18)
+
+/* MSG_ID/CMD_ID, header's HEADER_ID */
+#define IPC_MSG_AP_2_DSP_0 0x01
+#define IPC_MSG_AP_2_DSP_1 0x02
+
+#define IPC_MSG_DSP_2_AP_0 0x20
+#define IPC_MSG_DSP_2_AP_1 0x21
+
+/* share info, if not have registers, can store info in iram or dram */
+#define IPC_STS_DSP 0x00 /* IPC DSP -> AP */
+#define IPC_STS_AP 0x08 /* IPC AP -> DSP */
+/*IPC_STS_DSP bf*/
+#define IPC_STS_DSP_DONE (0x1 << 30)
+#define IPC_STS_DSP_BUSY (0x1 << 31)
+#define IPC_STS_DSP_DONE_MASK (0x1 << 30)
+#define IPC_STS_DSP_BUSY_MASK (0x1 << 31)
+
+/*IPC_STS_AP bf*/
+#define IPC_STS_AP_DONE (0x1 << 30)
+#define IPC_STS_AP_BUSY (0x1 << 31)
+#define IPC_STS_AP_DONE_MASK (0x1 << 30)
+#define IPC_STS_AP_BUSY_MASK (0x1 << 31)
+
+#define INT_VEC_DSPB_MBOX7 (249) // sec tx
+#define INT_VEC_DSPB_MBOX5 (248) // tx
+#define INT_VEC_DSPB_MBOX6 (247) // sec rx
+#define INT_VEC_DSPB_MBOX4 (246) // rx
+
+#define INT_VEC_DSPA_MBOX7 (245) // sec tx
+#define INT_VEC_DSPA_MBOX5 (244) // tx
+#define INT_VEC_DSPA_MBOX6 (243) // sec rx
+#define INT_VEC_DSPA_MBOX4 (242) // rx
+
+static int hifi4dsp_tm2_dsp_load_fw(struct hifi4dsp_dsp *dsp);
+static int hifi4dsp_tm2_dsp_reset(struct hifi4dsp_dsp *dsp);
+static int hifi4dsp_tm2_dsp_start(struct hifi4dsp_dsp *dsp);
+static int hifi4dsp_tm2_dsp_stop(struct hifi4dsp_dsp *dsp);
+static int hifi4dsp_tm2_dsp_sleep(struct hifi4dsp_dsp *dsp);
+static int hifi4dsp_tm2_dsp_wake(struct hifi4dsp_dsp *dsp);
+
+static int hifi4dsp_miscdev_open(struct inode *inode, struct file *fp)
+{
+ /*
+ *struct hifi4dsp_priv *priv;
+ *dev = conainer_of(inode->i_cdev,struct globalmem_dev,cdev);
+ *flip->private_data = dev;
+ */
+ int minor = iminor(inode);
+ int major = imajor(inode);
+ struct hifi4dsp_priv *priv;
+ struct miscdevice *c;
+ struct hifi4dsp_miscdev_t *pmscdev_t;
+
+ c = fp->private_data;
+ priv = hifi4dsp_p[0];
+ pmscdev_t = container_of(c, struct hifi4dsp_miscdev_t, dsp_miscdev);
+ if (pmscdev_t == NULL) {
+ pr_err("hifi4dsp_miscdev_t == NULL\n");
+ return -1;
+ }
+ if (pmscdev_t->priv == NULL) {
+ pr_err("hifi4dsp_miscdev_t -> priv==NULL\n");
+ return -1;
+ }
+ priv = pmscdev_t->priv;
+ if (priv == NULL) {
+ pr_err("hifi4dsp_miscdev_t pointer *pmscdev_t==NULL\n");
+ return -1;
+ }
+ fp->private_data = priv;
+
+ pr_debug("%s,%s,major=%d,minor=%d\n", __func__,
+ priv->dev->kobj.name,
+ major,
+ minor);
+ return 0;
+}
+
+static int hifi4dsp_miscdev_release(struct inode *inode, struct file *fp)
+{
+ return 0;
+}
+
+static long hifi4dsp_miscdev_ioctl(struct file *fp, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = 0;
+ struct hifi4dsp_priv *priv;
+ struct hifi4dsp_dsp *dsp;
+ struct hifi4dsp_info_t *info;
+ void __user *argp = (void __user *)arg;
+
+ pr_debug("%s\n", __func__);
+ if (fp->private_data == NULL) {
+ pr_err("%s error: fp->private_data is null", __func__);
+ return -1;
+ }
+ priv = (struct hifi4dsp_priv *)fp->private_data;
+ if (priv == NULL) {
+ pr_err("%s error: hifi4dsp_priv is null", __func__);
+ return -1;
+ }
+ dsp = priv->dsp;
+ if (dsp == NULL) {
+ pr_err("%s hifi4dsp_dsp is null:\n", __func__);
+ return -1;
+ }
+ if (priv->dsp->dsp_fw == NULL) {
+ pr_err("%s hifi4dsp_firmware is null:\n", __func__);
+ return -1;
+ }
+ if (priv->dsp->fw == NULL) {
+ pr_err("%s firmware is null:\n", __func__);
+ return -1;
+ }
+ pr_debug("%s %s\n", __func__, priv->dev->kobj.name);
+ info = kmalloc(sizeof(struct hifi4dsp_info_t), GFP_KERNEL);
+ if (!info)
+ return -1;
+
+ switch (cmd) {
+ case HIFI4DSP_TEST:
+ pr_debug("%s HIFI4DSP_TEST\n", __func__);
+ break;
+ case HIFI4DSP_LOAD:
+ pr_debug("%s HIFI4DSP_LOAD\n", __func__);
+ ret = copy_from_user(info, argp,
+ sizeof(struct hifi4dsp_info_t));
+ priv->dsp->info = info;
+ hifi4dsp_tm2_dsp_load_fw(priv->dsp);
+ break;
+ case HIFI4DSP_RESET:
+ pr_debug("%s HIFI4DSP_RESET\n", __func__);
+ ret = copy_from_user(info, argp,
+ sizeof(struct hifi4dsp_info_t));
+ priv->dsp->info = info;
+ hifi4dsp_tm2_dsp_reset(priv->dsp);
+ break;
+ case HIFI4DSP_START:
+ pr_debug("%s HIFI4DSP_START\n", __func__);
+ ret = copy_from_user(info, argp,
+ sizeof(struct hifi4dsp_info_t));
+ priv->dsp->info = info;
+ hifi4dsp_tm2_dsp_start(priv->dsp);
+ break;
+ case HIFI4DSP_STOP:
+ pr_debug("%s HIFI4DSP_STOP\n", __func__);
+ ret = copy_from_user(info, argp,
+ sizeof(struct hifi4dsp_info_t));
+ priv->dsp->info = info;
+ hifi4dsp_tm2_dsp_stop(priv->dsp);
+ break;
+ case HIFI4DSP_SLEEP:
+ pr_debug("%s HIFI4DSP_SLEEP\n", __func__);
+ ret = copy_from_user(info, argp,
+ sizeof(struct hifi4dsp_info_t));
+ priv->dsp->info = info;
+ hifi4dsp_tm2_dsp_sleep(priv->dsp);
+ break;
+ case HIFI4DSP_WAKE:
+ pr_debug("%s HIFI4DSP_WAKE\n", __func__);
+ ret = copy_from_user(info, argp,
+ sizeof(struct hifi4dsp_info_t));
+ priv->dsp->info = info;
+ hifi4dsp_tm2_dsp_wake(priv->dsp);
+ break;
+ case HIFI4DSP_GET_INFO:
+ pr_debug("%s HIFI4DSP_GET_INFO\n", __func__);
+ ret = copy_from_user(info, argp,
+ sizeof(struct hifi4dsp_info_t));
+ pr_debug("%s HIFI4DSP_GET_INFO %s\n", __func__,
+ info->fw_name);
+ strcpy(info->fw_name, "1234abcdef");
+ ret = copy_to_user(argp, info,
+ sizeof(struct hifi4dsp_info_t));
+ pr_debug("%s HIFI4DSP_GET_INFO %s\n", __func__,
+ info->fw_name);
+ break;
+ default:
+ pr_err("%s ioctl CMD error\n", __func__);
+ break;
+ }
+ kfree(info);
+ priv->dsp->info = NULL;
+
+ return ret;
+}
+
+static int hifi4dsp_tm2_dsp_load_fw(struct hifi4dsp_dsp *dsp)
+{
+ int err = 0;
+ struct hifi4dsp_firmware *new_dsp_fw;
+ struct hifi4dsp_info_t *info = dsp->info;
+
+ if (strlen(info->fw_name) == 0)
+ return -1;
+ pr_debug("info->fw_name:%s\n", info->fw_name);
+ new_dsp_fw = hifi4dsp_fw_register(dsp, info->fw_name);
+ if (new_dsp_fw == NULL) {
+ pr_err("register firmware:%s error\n", info->fw_name);
+ return -1;
+ }
+ dsp->dsp_fw = new_dsp_fw; /*set newest fw as def fw of dsp*/
+ strcpy(new_dsp_fw->name, info->fw_name);
+ if (info->phy_addr != 0) { /*to be improved*/
+ //info->phy_addr may !=0, but illegal
+ new_dsp_fw->paddr = info->phy_addr;
+ /*TODO*/
+ /*new_dsp_fw->buf = phys_to_virt(new_dsp_fw->paddr);*/
+ new_dsp_fw->buf = dsp->pdata->fw_buf;
+ } else {
+ new_dsp_fw->paddr = dsp->pdata->fw_paddr;
+ new_dsp_fw->buf = dsp->pdata->fw_buf;
+ }
+ pr_debug("new hifi4dsp_firmware, name=%s, paddr=0x%llx, buf=0x%p\n",
+ new_dsp_fw->name,
+ (unsigned long long)new_dsp_fw->paddr,
+ new_dsp_fw->buf);
+ hifi4dsp_fw_load(new_dsp_fw);
+ return err;
+}
+
+static int hifi4dsp_tm2_dsp_init(struct hifi4dsp_priv *priv,
+ struct device *dev, struct hifi4dsp_pdata *pdata,
+ struct hifi4dsp_dsp_device *dsp_dev)
+{
+ int err = 0;
+ struct hifi4dsp_dsp *new_dsp;
+ struct hifi4dsp_firmware *new_dsp_fw;
+ struct firmware *fw;
+
+ pr_debug("%s\n", __func__);
+ /* init dsp */
+ new_dsp = hifi4dsp_dsp_new(priv, pdata, dsp_dev);
+ if (new_dsp == NULL) {
+ pr_err("%s get new hifi4dsp_dsp error\n", __func__);
+ err = -ENODEV;
+ goto dsp_new_err;
+ }
+
+ /*malloc firmware*/
+ fw = kzalloc(sizeof(struct firmware), GFP_KERNEL);
+ if (new_dsp == NULL) {
+ pr_err("kzalloc new firmware error\n");
+ return -ENOMEM;
+ }
+ new_dsp->fw = fw;
+ /*init id, irq, clk*/
+ new_dsp->id = pdata->id;
+ new_dsp->irq = pdata->irq;
+ new_dsp->freq = pdata->clk_freq;
+
+ /*init addr*/
+ new_dsp->addr.fw_paddr = pdata->fw_paddr;
+ new_dsp->addr.fw = pdata->fw_buf;
+ new_dsp->addr.reg = pdata->reg;
+ new_dsp->addr.reg_size = pdata->reg_size;
+ /*init mailbox*/
+
+ /* reset */
+ hifi4dsp_dsp_reset(new_dsp);
+ /* malloc hifi4dsp_firmware & init */
+ new_dsp_fw = hifi4dsp_fw_new(new_dsp, new_dsp->fw, priv);
+ new_dsp->dsp_fw = new_dsp_fw;
+
+ /* add to priv*/
+ priv->dsp_fw = new_dsp_fw;
+ priv->dsp = new_dsp;
+
+ hifi4dsp_dsp_boot(priv->dsp);
+
+ pr_info("%s done\n", __func__);
+dsp_new_err:
+ return err;
+}
+
+static int hifi4dsp_tm2_dsp_reset(struct hifi4dsp_dsp *dsp)
+{
+ struct hifi4dsp_info_t *info;
+
+ pr_debug("%s\n", __func__);
+ if (dsp->info != NULL)
+ info = (struct hifi4dsp_info_t *)dsp->info;
+
+ dsp->info = NULL;
+ return 0;
+}
+
+static int hifi4dsp_tm2_dsp_boot(struct hifi4dsp_dsp *dsp)
+{
+ struct hifi4dsp_info_t *info;
+
+ info = (struct hifi4dsp_info_t *)dsp->info;
+ pr_debug("%s\n", __func__);
+
+
+ dsp->info = NULL;
+ return 0;
+}
+
+static int hifi4dsp_tm2_dsp_start(struct hifi4dsp_dsp *dsp)
+{
+ struct hifi4dsp_info_t *info;
+
+ pr_debug("%s\n", __func__);
+ info = (struct hifi4dsp_info_t *)dsp->info;
+ pr_debug("dsp_id: %d\n", dsp->id);
+ pr_debug("dsp_freqence: %d Hz\n", dsp->freq);
+ pr_debug("dsp_start_addr: 0x%llx\n",
+ (unsigned long long)dsp->dsp_fw->paddr);
+
+ if (dsp->dsp_clk == NULL)
+ pr_err("dsp_clk=NULL\n");
+ if (dsp->dsp_gate == NULL)
+ pr_err("dsp_gate=NULL\n");
+
+ //clk_set_rate(dsp->dsp_clk, dsp->freq);
+ //clk_prepare_enable(dsp->dsp_clk);
+ //clk_prepare_enable(dsp->dsp_gate);
+
+ tm2_dsp_bootup(dsp->id, dsp->dsp_fw->paddr, dsp->freq);
+ dsp->info = NULL;
+
+ return 0;
+}
+
+static int hifi4dsp_tm2_dsp_stop(struct hifi4dsp_dsp *dsp)
+{
+ struct hifi4dsp_info_t *info;
+
+ info = (struct hifi4dsp_info_t *)dsp->info;
+ pr_debug("%s\n", __func__);
+
+
+ dsp->info = NULL;
+ return 0;
+}
+
+static int hifi4dsp_tm2_dsp_sleep(struct hifi4dsp_dsp *dsp)
+{
+ struct hifi4dsp_info_t *info;
+
+ info = (struct hifi4dsp_info_t *)dsp->info;
+ pr_debug("%s\n", __func__);
+
+
+ dsp->info = NULL;
+ return 0;
+}
+
+static int hifi4dsp_tm2_dsp_wake(struct hifi4dsp_dsp *dsp)
+{
+ struct hifi4dsp_info_t *info;
+
+ info = (struct hifi4dsp_info_t *)dsp->info;
+ pr_debug("%s\n", __func__);
+
+
+ dsp->info = NULL;
+ return 0;
+}
+
+static void hifi4dsp_tm2_dsp_free(struct hifi4dsp_dsp *dsp)
+{
+ pr_debug("%s\n", __func__);
+
+ hifi4dsp_fw_free_all(dsp);
+ //kfree(NULL) is safe and check is probably not required
+
+ kfree(dsp->dsp_fw);
+ dsp->dsp_fw = NULL;
+
+ kfree(dsp->fw);
+ dsp->fw = NULL;
+
+ kfree(dsp->ops);
+ dsp->ops = NULL;
+
+ kfree(dsp->pdata);
+ dsp->pdata = NULL;
+
+ kfree(dsp);
+}
+
+static irqreturn_t hifi4dsp_tm2_dsp_irq(int irq, void *p)
+{
+ irqreturn_t ret = IRQ_NONE;
+ u32 isrx;
+ struct hifi4dsp_dsp *dsp = (struct hifi4dsp_dsp *) p;
+
+ spin_lock(&dsp->spinlock);
+ isrx = hifi4dsp_dsp_smem_read64_unlocked(dsp, IPC_STS_AP);
+ //need improvement, process for rx_request rx_done from dsp
+ spin_unlock(&dsp->spinlock);
+ ret = IRQ_WAKE_THREAD;
+ return ret;
+}
+
+static inline u32 hifi4dsp_tm2_ipc_header_msg_id(u64 header)
+{
+ header &= IPC_MSG_HEADER_DATA_BYTES(IPC_MSG_HEADER_ID_MASK);
+ return header >> IPC_MSG_HEADER_ID_SHIFT;
+}
+
+static inline u32 hifi4dsp_tm2_ipc_header_msg_data_bytes(u64 header)
+{
+ header &= IPC_MSG_HEADER_DATA_BYTES(IPC_MSG_HEADER_DATA_BYTES_MASK);
+ return header >> IPC_MSG_HEADER_DATA_BYTES_SHIFT;
+}
+
+static int hifi4dsp_tm2_process_notification(struct hifi4dsp_priv *priv,
+ unsigned long *flags)
+{
+ struct hifi4dsp_dsp *dsp = priv->dsp;
+ u32 header;
+ u32 msg_id;
+ int handled = 1;
+
+ /*todo, read msg header, get MSG_ID*/
+ header = hifi4dsp_dsp_smem_read_unlocked(dsp, 0);
+ msg_id = hifi4dsp_tm2_ipc_header_msg_id(header);
+
+ switch (msg_id) {
+ case IPC_MSG_DSP_2_AP_0:
+ break;
+ case IPC_MSG_DSP_2_AP_1:
+ break;
+ }
+
+ return handled;
+}
+
+static int hifi4dsp_tm2_process_reply(struct hifi4dsp_priv *priv, u64 header)
+{
+ struct hifi4dsp_ipc_message *msg;
+
+ msg = hifi4dsp_ipc_reply_find_msg(&priv->ipc, header);
+ if (msg == NULL)
+ return 1;
+
+ if (header & IPC_MSG_HEADER_WITH_DATA(header)) {
+ msg->rx_size = hifi4dsp_tm2_ipc_header_msg_data_bytes(header);
+ hifi4dsp_dsp_mailbox_inbox_read(priv->dsp,
+ msg->rx_data, msg->rx_size);
+ }
+
+ list_del(&msg->list);
+ /* wake up */
+ hifi4dsp_ipc_tx_msg_reply_complete(&priv->ipc, msg);
+
+ return 1;
+}
+
+static irqreturn_t hifi4dsp_tm2_dsp_irq_thread(int irq, void *context)
+{
+ struct hifi4dsp_dsp *dsp = (struct hifi4dsp_dsp *) context;
+ struct hifi4dsp_priv *priv = dsp->priv;
+ struct hifi4dsp_ipc *ipc = &priv->ipc;
+ u64 header;
+ u32 ap_sts;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dsp->spinlock, flags);
+
+ ap_sts = hifi4dsp_dsp_smem_read_unlocked(dsp, IPC_STS_AP);
+ header = hifi4dsp_dsp_smem_read64_unlocked(dsp, IPC_MSG_HEADER);
+ if (header & (IPC_STS_AP_BUSY&ap_sts)) {
+ if (header&IPC_MSG_HEADER_NOTIFY_MASK) {
+ /* msg */
+ hifi4dsp_tm2_process_notification(priv, &flags);
+ } else {
+ /* reply msg */
+ hifi4dsp_tm2_process_reply(priv, header);
+ }
+ /*
+ * clear DSP BUSY bit and set DONE bit. Tell DSP we have
+ * processed the message and can accept new. Clear data part
+ * of the header
+ */
+ hifi4dsp_dsp_smem_update_bits64_unlocked(dsp, IPC_STS_AP,
+ IPC_STS_AP_BUSY|IPC_STS_AP_DONE, IPC_STS_AP_DONE);
+ /* unmask msg request interrupts */
+ //hifi4dsp_dsp_smem_update_bits_unlocked(dsp, 0);
+ }
+
+ spin_unlock_irqrestore(&dsp->spinlock, flags);
+
+ /* continue to send any remaining messages... */
+ kthread_queue_work(&ipc->kworker, &ipc->kwork);
+
+ return IRQ_HANDLED;
+}
+
+/*transfer param from pdata to dsp*/
+static int hifi4dsp_tm2_resource_map(struct hifi4dsp_dsp *dsp,
+ struct hifi4dsp_pdata *pdata)
+{
+ int ret = 0;
+
+ if (!pdata) {
+ pr_err("%s error\n", __func__);
+ ret = -1;
+ }
+ return ret;
+}
+
+static int hifi4dsp_tm2_init(struct hifi4dsp_dsp *dsp,
+ struct hifi4dsp_pdata *pdata)
+{
+ struct device *dev;
+ int ret = -ENODEV;
+
+ dev = dsp->dev;
+ pr_debug("%s\n", __func__);
+ ret = hifi4dsp_tm2_resource_map(dsp, pdata);
+ if (ret < 0) {
+ dev_err(dev, "failed to map resources\n");
+ return ret;
+ }
+
+ //ret = dma_coerce_mask_and_coherent(dsp->dma_dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ /* enable interrupt from both arm sides and dsp side */
+ /* todo */
+ return 0;
+}
+
+static void hifi4dsp_tm2_free(struct hifi4dsp_priv *priv)
+{
+ pr_debug("%s\n", __func__);
+ if (priv->dsp) {
+ hifi4dsp_tm2_dsp_free(priv->dsp);
+ priv->dsp = NULL;
+ }
+ if (priv->dsp_fw) {
+ priv->dsp_fw = NULL;
+ return;
+ }
+}
+
+static int hifi4dsp_tm2_load_and_parse_fw(struct hifi4dsp_firmware *dsp_fw,
+ void *pinfo)
+{
+ struct hifi4dsp_info_t *info;
+
+ info = (struct hifi4dsp_info_t *)pinfo;
+ pr_debug("%s\n", __func__);
+ hifi4dsp_tm2_dsp_load_fw(dsp_fw->dsp);
+ return 0;
+}
+
+struct hifi4dsp_ops;
+struct hifi4dsp_ops hifi4dsp_tm2_dsp_ops = {
+ .boot = hifi4dsp_tm2_dsp_boot,
+ .reset = hifi4dsp_tm2_dsp_reset,
+ .sleep = hifi4dsp_tm2_dsp_sleep,
+ .wake = hifi4dsp_tm2_dsp_wake,
+
+ .write = hifi4dsp_smem_write,
+ .read = hifi4dsp_smem_read,
+ .write64 = hifi4dsp_smem_write64,
+ .read64 = hifi4dsp_smem_read64,
+ .ram_read = hifi4dsp_memcpy_fromio_32,
+ .ram_write = hifi4dsp_memcpy_toio_32,
+
+ .irq_handler = hifi4dsp_tm2_dsp_irq,
+ .init = hifi4dsp_tm2_init,
+ .free = hifi4dsp_tm2_dsp_free,
+ .parse_fw = hifi4dsp_tm2_load_and_parse_fw,
+};
+
+static struct hifi4dsp_pdata dsp_pdatas[] = {/*ARRAY_SIZE(dsp_pdatas)*/
+ {
+ .id = 0,
+ .name = "hifi4dsp0",
+ .reg_paddr = 0xFF680000,
+ .reg_size = 0x00100000,
+ .clk_freq = 400*1000*1000,
+ .smem_paddr = DSP_PHY_ADDR_0,
+ .smem_size = HIFI4DSP_TM2_ALL_SMEM_SIZE,
+ .irq = 242,
+ .fw_paddr = DSP_PHY_ADDR_0,
+ },
+ {
+ .id = 1,
+ .name = "hifi4dsp1",
+ .reg_paddr = 0xFF690000,
+ .reg_size = 0x00100000,
+ .clk_freq = 400*1000*1000,
+ .irq = 246,
+ .fw_paddr = DSP_PHY_ADDR_1,
+ },
+};
+
+static struct hifi4dsp_dsp_device hifi4dsp_dev = {
+ .thread = hifi4dsp_tm2_dsp_irq_thread,
+ .ops = &hifi4dsp_tm2_dsp_ops,
+};
+
+static void hifi4dsp_tm2_ipc_dbg(struct hifi4dsp_ipc *ipc,
+ const char *dbg_info)
+{
+ struct hifi4dsp_dsp *dsp = ipc->dsp;
+
+ if (dsp == NULL) {
+ pr_err("%s ipc->dsp is null\n", __func__);
+ return;
+ }
+ /*dump mailbox register info*/
+ pr_debug("%s %s\n", __func__, dbg_info);
+}
+
+/*
+ * call mailbox function to send the msg or
+ * directlly to write the msg to share register/memory
+ * then send notification msg though mailbox to DSP
+ */
+static void hifi4dsp_tm2_ipc_tx_msg(struct hifi4dsp_ipc *ipc,
+ struct hifi4dsp_ipc_message *msg)
+{
+ if (msg->header)
+ hifi4dsp_dsp_mailbox_outbox_write(ipc->dsp,
+ msg->tx_data,
+ msg->tx_size);
+ /*hifi4dsp_dsp_smem_write_unlocked(ipc->dsp,
+ * MBOX_TX_SET,
+ * (u32)(msg->header);
+ */
+}
+
+/*
+ * msg content = lower 32-bit of the header + data
+ *
+ */
+static void hifi4dsp_tm2_ipc_data_copy(struct hifi4dsp_ipc_message *msg,
+ char *tx_data, size_t tx_bytes)
+{
+ *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1);
+ memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes);
+ msg->tx_size += sizeof(u32);
+}
+
+/*
+ * read from mailbox register or
+ * share register between dsp and AP or
+ * share memory eara
+ * to judge if dsp is busy
+ */
+static u64 hifi4dsp_tm2_ipc_reply_msg_match(u64 header, u64 *mask)
+{
+ /* match reply to message sent based on msg and stream IDs */
+ *mask = IPC_MSG_HEADER_ID_MASK;
+ header &= *mask;
+ return header;
+}
+
+/*
+ * read from mailbox register or
+ * share register between dsp and AP or
+ * share memory eara
+ * to judge if dsp is busy
+ */
+static bool hifi4dsp_tm2_is_dsp_busy(struct hifi4dsp_dsp *dsp)
+{
+ u32 is_busy = 0;
+
+ pr_debug("%s %s busy\n", __func__, is_busy?"":"not");
+ return 0;
+}
+
+struct hifi4dsp_ipc_plat_ops hifi4dsp_tm2_ipc_ops = {
+ .tx_msg = hifi4dsp_tm2_ipc_tx_msg,
+ .tx_data_copy = hifi4dsp_tm2_ipc_data_copy,
+ .reply_msg_match = hifi4dsp_tm2_ipc_reply_msg_match,
+ .is_dsp_busy = hifi4dsp_tm2_is_dsp_busy,
+ .debug_info = hifi4dsp_tm2_ipc_dbg,
+};
+
+struct hifi4dsp_priv *hifi4dsp_privdata()
+{
+ return hifi4dsp_p[0];
+}
+
+static int hifi4dsp_platform_remove(struct platform_device *pdev)
+{
+ struct hifi4dsp_priv *priv;
+ int id = 0, dsp_cnt = 0;
+
+ dsp_cnt = ARRAY_SIZE(dsp_pdatas);
+ priv = hifi4dsp_privdata();
+ for (id = 0; id < dsp_cnt; id++) {
+ if (!priv)
+ continue;
+ hifi4dsp_tm2_free(priv);
+ if (priv->dev)
+ device_destroy(priv->class, priv->dev->devt);
+ priv += 1;
+ }
+ kfree(priv);
+ priv = NULL;
+ for (id = 0; id < dsp_cnt; id++)
+ hifi4dsp_p[id] = NULL;
+
+ return 0;
+}
+
+static const struct file_operations hifi4dsp_miscdev_fops = {
+ .owner = THIS_MODULE,
+ .open = hifi4dsp_miscdev_open,
+ .read = NULL,
+ .write = NULL,
+ .release = hifi4dsp_miscdev_release,
+ .unlocked_ioctl = hifi4dsp_miscdev_ioctl,
+ .compat_ioctl = hifi4dsp_miscdev_ioctl,
+};
+
+static struct miscdevice hifi4dsp_miscdev[] = {
+ {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "hifi4dsp0",
+ .fops = &hifi4dsp_miscdev_fops,
+ },
+ {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "hifi4dsp1",
+ .fops = &hifi4dsp_miscdev_fops,
+ }
+};
+
+static int hifi4dsp_priv_data_init(struct hifi4dsp_priv *priv,
+ struct hifi4dsp_pdata *pdata)
+{
+ //priv->fw_id = 0;
+ priv->dsp_is_started = false;
+ priv->dsp_dev = &hifi4dsp_dev;
+ priv->pdata = pdata;
+ HIFI4DSP_PRNT("%s done\n", __func__);
+ return 0;
+}
+
+static void *hifi4dsp_mm_vmap(phys_addr_t phys, unsigned long size)
+{
+ u32 offset, npages;
+ struct page **pages = NULL;
+ pgprot_t pgprot = PAGE_KERNEL;
+ void *vaddr;
+ int i;
+
+ offset = offset_in_page(phys);
+ npages = DIV_ROUND_UP(size + offset, PAGE_SIZE);
+
+ pages = vmalloc(sizeof(struct page *) * npages);
+ if (!pages)
+ return NULL;
+ for (i = 0; i < npages; i++) {
+ pages[i] = phys_to_page(phys);
+ phys += PAGE_SIZE;
+ }
+ /* pgprot = pgprot_writecombine(PAGE_KERNEL); */
+
+ vaddr = vmap(pages, npages, VM_MAP, pgprot);
+ if (!vaddr) {
+ pr_err("vmaped fail, size: %d\n",
+ npages << PAGE_SHIFT);
+ vfree(pages);
+ return NULL;
+ }
+ vfree(pages);
+ pr_debug("[HIGH-MEM-MAP] pa(%lx) to va(%p), size: %d\n",
+ (unsigned long)phys, vaddr, npages << PAGE_SHIFT);
+
+ return vaddr;
+}
+
+/*of read clk_gate, clk*/
+static inline int of_read_dsp_irq(
+ struct platform_device *pdev, int dsp_id)
+{
+ int irq = -1;
+
+ if (dsp_id == 0)
+ irq = of_irq_get_byname(pdev->dev.of_node, "irq_frm_dspa");
+ else if (dsp_id == 1)
+ irq = of_irq_get_byname(pdev->dev.of_node, "irq_frm_dspb");
+
+ pr_debug("%s %s irq=%d\n", __func__,
+ (irq < 0)?"error":"successful", irq);
+
+ return irq;
+}
+
+/*of read clk_gate, clk*/
+static inline struct clk *of_read_dsp_clk(
+ struct platform_device *pdev, int dsp_id)
+{
+ struct clk *p_clk = NULL;
+ char clk_name[20];
+
+ if (dsp_id == 0) {
+ strcpy(clk_name, "dspa_clk");
+ p_clk = devm_clk_get(&pdev->dev, clk_name);
+ } else if (dsp_id == 1) {
+ strcpy(clk_name, "dspb_clk");
+ p_clk = devm_clk_get(&pdev->dev, clk_name);
+ }
+ if (!p_clk)
+ pr_err("%s %s error\n", __func__, clk_name);
+
+ return p_clk;
+}
+
+/*of read clk_gate, clk*/
+static inline struct clk *of_read_dsp_clk_gate(
+ struct platform_device *pdev, int dsp_id)
+{
+ struct clk *p_clk_gate = NULL;
+ char clk_name[20];
+
+ if (dsp_id == 0) {
+ strcpy(clk_name, "dspa_gate");
+ p_clk_gate = devm_clk_get(&pdev->dev, clk_name);
+ } else if (dsp_id == 1) {
+ strcpy(clk_name, "dspb_gate");
+ p_clk_gate = devm_clk_get(&pdev->dev, clk_name);
+ }
+ if (!p_clk_gate)
+ pr_err("%s %s error\n", __func__, clk_name);
+
+ return p_clk_gate;
+}
+
+static int hifi4dsp_platform_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ int i = 0, id = 0;
+ unsigned int dsp_cnt = 0;
+ struct hifi4dsp_priv *priv;
+ struct hifi4dsp_pdata *pdata;
+ struct hifi4dsp_miscdev_t *p_dsp_miscdev;
+ struct miscdevice *pmscdev;
+ struct page *page;
+ struct resource res_mem;
+ int mem_bytes;
+ void *fw_addr = NULL;
+ void __iomem *dsp_regbase;
+ int irq;
+ struct device_node *np;
+ struct clk *dsp_clk = NULL;
+ struct clk *dsp_gate = NULL;
+
+ np = pdev->dev.of_node;
+ dsp_cnt = ARRAY_SIZE(dsp_pdatas);
+ pr_debug("%s pdatas: dsp_cnt=%d\n", __func__, dsp_cnt);
+ ret = of_property_read_u32(np, "dsp-cnt", &dsp_cnt);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Can't retrieve dsp-cnt\n");
+ ret = -EINVAL;
+ goto dsp_cnt_error;
+ }
+ pr_debug("%s of read dsp-cnt=%d\n", __func__, dsp_cnt);
+
+ /*init miscdev_t, miscdevice*/
+ p_dsp_miscdev = NULL;
+ p_dsp_miscdev = kcalloc(dsp_cnt, sizeof(struct hifi4dsp_miscdev_t),
+ GFP_KERNEL);
+ if (p_dsp_miscdev == NULL) {
+ HIFI4DSP_PRNT("kzalloc for p_dsp_miscdev error\n");
+ ret = -ENOMEM;
+ goto miscdev_malloc_error;
+ }
+
+ /*init hifi4dsp_priv*/
+ priv = NULL;
+ priv = kcalloc(dsp_cnt, sizeof(struct hifi4dsp_priv),
+ GFP_KERNEL);
+ if (priv == NULL) {
+ HIFI4DSP_PRNT("kzalloc for hifi4dsp_priv error\n");
+ ret = -ENOMEM;
+ goto priv_malloc_error;
+ }
+ /*of read reserved memory*/
+ ret = of_reserved_mem_device_init(&pdev->dev);
+ if (ret) {
+ pr_err("reserved memory init fail:%d\n", ret);
+ ret = -ENOMEM;
+ goto reserved_mem_alloc_error;
+ }
+
+ for (i = 0; i < dsp_cnt; i++) {
+ id = i;
+ p_dsp_miscdev += i;
+ priv += i;
+ pr_info("register dsp-%d start\n", id);
+
+ memcpy(&(p_dsp_miscdev->dsp_miscdev),
+ &hifi4dsp_miscdev[id], sizeof(struct miscdevice));
+ pmscdev = &p_dsp_miscdev->dsp_miscdev;
+
+ p_dsp_miscdev->priv = priv;
+
+ /*of read reg base, ioremap it*/
+ //res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (of_address_to_resource(np, id, &res_mem)) {
+ ret = -EINVAL;
+ pr_err("%s didn't get iomem from dts\n", __func__);
+ goto error2;
+ }
+ dsp_regbase = ioremap_nocache(res_mem.start,
+ resource_size(&res_mem));
+ pr_debug("dsp_regbase ioremap, reg:%x->%p, size:%x, %s\n",
+ (u32)res_mem.start,
+ dsp_regbase,
+ (u32)resource_size(&res_mem),
+ (dsp_regbase != NULL)?"successful":"error");
+
+ /*of read irq num*/
+ irq = of_read_dsp_irq(pdev, id);
+ if (irq < 0) {
+ ret = -EINVAL;
+ goto error2;
+ }
+
+ /*cma alloc mem for firmware*/
+ if (of_property_read_u32(np, "reserved_mem_size", &mem_bytes)) {
+ mem_bytes = DEF_RESERVE_MEM_SIZE;
+ pr_debug("of read reserved_mem_size error, def value:0x%x\n",
+ mem_bytes);
+ } else
+ pr_info("reserved_mem_size:0x%x\n", mem_bytes);
+
+ page = dma_alloc_from_contiguous(&pdev->dev,
+ mem_bytes >> PAGE_SHIFT, 0);
+ if (!page) {
+ pr_err("alloc page failed, ret:%p\n", page);
+ return -ENOMEM;
+ }
+ if (!PageHighMem(page)) {
+ //fw_addr = page_address(page);
+ fw_addr = phys_to_virt(page_to_phys(page));
+ } else {
+ fw_addr = hifi4dsp_mm_vmap(page_to_phys(page),
+ mem_bytes);
+ pr_info("kernel addr map phys:0x%llx->virt:0x%p\n",
+ (unsigned long long)page_to_phys(page),
+ fw_addr);
+ }
+ pr_debug("get page:%p, 0x%lx, phys:0x%llx, virt:0x%p\n",
+ page,
+ page_to_pfn(page),
+ (unsigned long long)page_to_phys(page),
+ page_address(page));
+
+ /*of read clk_gate, clk*/
+ dsp_gate = of_read_dsp_clk_gate(pdev, id);
+ dsp_clk = of_read_dsp_clk(pdev, id);
+ priv->p_clk = dsp_clk;
+ priv->p_clk_gate = dsp_gate;
+
+ /*register dsp device*/
+ //pmscdev = &hifi4dsp_miscdev;
+ ret = misc_register(pmscdev);
+ if (ret) {
+ pr_err("register vad_miscdev error\n");
+ goto error2;
+ }
+ priv->dev = pmscdev->this_device;
+
+ pdata = &dsp_pdatas[i];
+ pdata->fw_paddr = page_to_phys(page);
+ pdata->fw_buf = fw_addr;
+ pdata->fw_max_size = mem_bytes;
+ pdata->reg_size = (u32)resource_size(&res_mem);
+ pdata->reg = dsp_regbase;
+ id = pdata->id;
+ pdata->irq = irq;
+ hifi4dsp_priv_data_init(priv, pdata);
+ hifi4dsp_tm2_dsp_init(priv, priv->dev,
+ priv->pdata, priv->dsp_dev);
+ priv->dsp->dsp_clk = priv->p_clk;
+ priv->dsp->dsp_gate = priv->p_clk_gate;
+
+ hifi4dsp_p[i] = priv;
+ //priv += 1;
+ priv->dev = pmscdev->this_device;
+ dev_set_drvdata(priv->dev, priv);
+
+ pr_info("register dsp-%d done\n", id);
+ }
+ ret = 0;
+ //tm2_dsp_hw_init(priv->dsp->id, priv->dsp->freq);
+ tm2_dsp_regs_iomem_init();
+
+ pr_info("%s done\n", __func__);
+
+ goto done;
+
+error2:
+reserved_mem_alloc_error:
+ kfree(priv);
+priv_malloc_error:
+ kfree(p_dsp_miscdev);
+miscdev_malloc_error:
+dsp_cnt_error:
+done:
+ return ret;
+}
+
+static const struct of_device_id hifi4dsp_device_id[] = {
+ {
+ .compatible = "amlogic, hifi4dsp",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hifi4dsp_device_id);
+
+static struct platform_driver hifi4dsp_platform_driver = {
+ .driver = {
+ .name = "hifi4dsp",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(hifi4dsp_device_id),
+ },
+ .probe = hifi4dsp_platform_probe,
+ .remove = hifi4dsp_platform_remove,
+};
+module_platform_driver(hifi4dsp_platform_driver);
+
+MODULE_AUTHOR("Shuyu Li");
+MODULE_DESCRIPTION("HiFi DSP Module Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/amlogic/hifi4dsp/hifi4dsp_priv.h b/drivers/amlogic/hifi4dsp/hifi4dsp_priv.h
new file mode 100644
index 000000000000..3a3939372a3f
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/hifi4dsp_priv.h
@@ -0,0 +1,78 @@
+/*
+ * drivers/amlogic/hifi4dsp/hifi4dsp_priv.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _HIFI4DSP_PRIV_H
+#define _HIFI4DSP_PRIV_H
+#include <linux/device.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+
+/*
+ * #include <asm/dsp/hifi4dsp_control.h>
+ * #include <asm/dsp/dsp_register.h>
+ */
+//#include "hifi4dsp_control.h"
+#include <linux/amlogic/media/sound/dsp_register.h>
+
+#include <linux/dma-mapping.h>
+
+#include "hifi4dsp_api.h"
+#include "hifi4dsp_dsp.h"
+#include "hifi4dsp_firmware.h"
+#include "hifi4dsp_ipc.h"
+
+struct class;
+
+struct hifi4dsp_priv {
+ char name[12];
+ struct class *class;
+ struct device *dev;
+ struct device *dma_dev;
+
+ u32 dsp_freq;
+ bool dsp_is_started;
+
+ struct hifi4dsp_dsp *dsp;
+ struct hifi4dsp_dsp_device *dsp_dev;
+ struct hifi4dsp_firmware *dsp_fw;
+ struct hifi4dsp_mailbox *mailbox;
+ struct hifi4dsp_pdata *pdata;
+ struct hifi4dsp_ipc ipc;
+
+ struct clk *p_clk;
+ struct clk *p_clk_gate;
+};
+
+struct hifi4dsp_miscdev_t {
+ struct miscdevice dsp_miscdev;
+ struct hifi4dsp_priv *priv;
+};
+
+struct hifi4dsp_resource_t {
+ struct resource res_iomem;
+ struct clk *p_clk_gate;
+ struct clk *p_clk;
+ int irq;
+};
+
+struct hifi4dsp_priv *hifi4dsp_privdata(void);
+
+#ifndef HIFI4DSP_PRNT
+#define HIFI4DSP_PRNT(...) pr_info(__VA_ARGS__)
+#endif
+
+#endif /*_HIFI4DSP_PRIV_H*/
diff --git a/drivers/amlogic/hifi4dsp/tm2_dsp_top.c b/drivers/amlogic/hifi4dsp/tm2_dsp_top.c
new file mode 100644
index 000000000000..b8625ca6eb63
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/tm2_dsp_top.c
@@ -0,0 +1,525 @@
+/*
+ * drivers/amlogic/hifi4dsp/tm2_dsp_top.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/firmware.h>
+#include <linux/amlogic/major.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/amlogic/scpi_protocol.h>
+
+#include "hifi4dsp_priv.h"
+#include "hifi4dsp_firmware.h"
+#include "hifi4dsp_dsp.h"
+
+/*AO_RTI*/
+#define AO_RTI_ADDR_BASE (0xff800000)
+#define REG_AO_RTI_GEN_PWR_SLEEP0 (0x03a << 2)
+#define REG_AO_RTI_GEN_PWR_ISO0 (0x03b << 2)
+
+/*HIU*/
+#define HUI_ADDR_BASE (0xff63c000)
+#define REG_HHI_DSP_CLK_CNTL (0x0fc << 2)
+#define REG_HHI_DSP_MEM_PD_REG0 (0x044 << 2)
+
+/*reset*/
+#define RESET_ADDR_BASE (0xffd01000)
+#define REG_RESET1_LEVEL (0x84)
+#define REG_RESET4_LEVEL (0x90)
+
+/*DSP TOP*/
+#define DSPA_REG_BASE (0xff680000)
+#define DSPB_REG_BASE (0xff690000)
+
+#define REG_DSP_CFG0 (0x0)
+#define REG_DSP_CFG1 (0x4)
+#define REG_DSP_CFG2 (0x8)
+#define REG_DSP_RESET_VEC (0x004 << 2)
+
+struct reg_iomem_t {
+ void __iomem *dsp_addr;
+ void __iomem *dspa_addr;
+ void __iomem *dspb_addr;
+ void __iomem *hiu_addr; /*HIU*/
+ void __iomem *ao_rti_addr; /*AO_RTI*/
+ void __iomem *reset_addr;
+ void __iomem *sleep_addr;
+};
+
+static struct reg_iomem_t g_regbases;
+static bool regs_iomem_is_inited;
+
+static void __iomem *reg_iomem_init(phys_addr_t paddr, u32 size)
+{
+ void __iomem *vaddr = NULL;
+
+ vaddr = ioremap_nocache(paddr, size);
+ pr_debug("%s phys: %llx, virt: %p, size:%x Bytes\n",
+ __func__,
+ (unsigned long long)paddr,
+ vaddr,
+ size);
+ return vaddr;
+}
+
+static inline void tm2_dsp_top_reg_dump(char *name,
+ void __iomem *reg_base, u32 reg_offset)
+{
+ pr_info("%s (%p) = 0x%x\n", name,
+ (reg_base + reg_offset),
+ readl(reg_base + reg_offset));
+}
+
+void tm2_dsp_regs_iomem_init(void)
+{
+ if (regs_iomem_is_inited == true) {
+ pr_info("%s has been done\n", __func__);
+ return;
+ }
+
+ g_regbases.dspa_addr = reg_iomem_init(DSPA_REG_BASE, 0x8B*4);
+ g_regbases.dspb_addr = reg_iomem_init(DSPB_REG_BASE, 0x8B*4);
+
+ g_regbases.hiu_addr = reg_iomem_init(HUI_ADDR_BASE, 0xFF*4);
+ g_regbases.ao_rti_addr = reg_iomem_init(AO_RTI_ADDR_BASE, 0xFF*4);
+ g_regbases.reset_addr = reg_iomem_init(RESET_ADDR_BASE, 0x40*4);
+
+ regs_iomem_is_inited = true;
+
+ pr_debug("%s done\n", __func__);
+}
+
+void tm2_dsp_regs_iounmem(void)
+{
+ iounmap(g_regbases.dspa_addr);
+ iounmap(g_regbases.dspb_addr);
+
+ iounmap(g_regbases.hiu_addr);
+ iounmap(g_regbases.ao_rti_addr);
+ iounmap(g_regbases.reset_addr);
+
+ regs_iomem_is_inited = false;
+
+ pr_debug("%s done\n", __func__);
+}
+
+static inline void __iomem *get_hiu_addr(void)
+{
+ return g_regbases.hiu_addr;
+}
+
+static inline void __iomem *get_ao_rti_addr(void)
+{
+ return g_regbases.ao_rti_addr;
+}
+
+static inline void __iomem *get_reset_addr(void)
+{
+ return g_regbases.reset_addr;
+}
+
+static inline void __iomem *get_dsp_addr(int dsp_id)
+{
+ if (dsp_id == 1)
+ return g_regbases.dspb_addr;
+ else
+ return g_regbases.dspa_addr;
+}
+
+/*
+ * clk_util_set_dsp_clk
+ * freq_sel: 0:286MHz fclk_7
+ * 1:400MHz fclk_5
+ * 2:500MHz fclk_2/2
+ * 3:667MHz fclk_3
+ * 4:1000MHz fclk_2
+ * others:286MHz fclk/7
+ */
+//crt_clk_div_mux4_ns #(8)
+// u_crt_dspa_clk_mux_div(
+// .clk0 (fclk_div2 ),
+// .clk1 (fclk_div3 ),
+// .clk2 (fclk_div5 ),
+// .clk3 (fclk_div7 ),
+// .reset_n (crt_reset_n ),
+// .force_oscin_clk (1'b0 ),
+// .cts_oscin_clk (1'b0 ),
+//
+// .clk_div (hi_dsp_clk_cntl[7:0] ),
+// .clk_en (hi_dsp_clk_cntl[15] ),
+// .clk_sel (hi_dsp_clk_cntl[9:8] ),
+// .clk_out (cts_dspa_clk ));
+
+static void clk_util_set_dsp_clk(uint32_t id, uint32_t freq_sel)
+{
+ uint32_t clk_sel = 0;
+ uint32_t clk_div = 0;
+ uint32_t read;
+ void __iomem *reg;
+
+ reg = get_hiu_addr() + REG_HHI_DSP_CLK_CNTL;
+ switch (freq_sel) {
+ case 1:
+ clk_sel = 2;
+ clk_div = 0;
+ pr_debug("CLK_UTIL:dsp:fclk/5:400MHz\n");
+ break;
+ case 2:
+ clk_sel = 0;
+ clk_div = 1;
+ pr_debug("CLK_UTIL:dsp:fclk/4:500MHz\n");
+ break;
+ case 3:
+ //clk_sel = 1;
+ //clk_div = 0;
+ //pr_debug("CLK_UTIL:dsp:fclk/3:667MHz\n");
+ break;
+ case 4:
+ clk_sel = 1;
+ clk_div = 1;
+ pr_debug("CLK_UTIL:dsp:fclk/3/2:333MHz\n");
+ break;
+ case 5:
+ clk_sel = 0;
+ clk_div = 3;
+ pr_debug("CLK_UTIL:dsp:fclk/2:250MHz\n");
+ break;
+ case 6:
+ clk_sel = 2;
+ clk_div = 1;
+ pr_debug("CLK_UTIL:dsp:fclk/4/2:200MHz\n");
+ break;
+ case 7:
+ clk_sel = 2;
+ clk_div = 3;
+ pr_debug("CLK_UTIL:dsp:fclk/4/4:100MHz\n");
+ break;
+ case 8:
+ clk_sel = 4;
+ clk_div = 0;
+ pr_debug("CLK_UTIL:dsp:oscin:24MHz\n");
+ break;
+ case 10:
+ //clk_sel = 0;
+ //clk_div = 0;
+ //pr_debug("CLK_UTIL:dsp:fclk/2:1000MHz\n");
+ break;
+ default:
+ clk_sel = 3;
+ clk_div = 0;
+ pr_debug("CLK_UTIL:dsp:fclk/7:286MHz\n");
+ break;
+ }
+
+ read = readl(reg);
+ if (id == 0) {
+ //read = (read & ~((0x3<<8) | (0xff<<0)));
+ //read = read | ((1<<15) | (clk_sel<<8) | (clk_div<<0));
+ if (read & (1 << 15)) { //if sync_mux ==1, sel mux 0
+ read &= (~((1 << 15) | (0xf << 0) | (0x7 << 4)));
+ read |= (1 << 7) | (clk_div << 0) | (clk_sel << 4);
+ } else {
+ read &= (~((1 << 15) | (0xf << 8) | (0x7 << 12)));
+ read |= (1 << 7) | (clk_div << 8);
+ read |= (clk_sel << 12) | (1 << 15);
+ }
+ } else {
+ //read = (read & ~((0x3<<24) | (0xff<<16)));
+ //read = read | ((1<<31) | (clk_sel<<24) | (clk_div<<16));
+ if (read & (1 << 31)) { //if sync_mux ==1, sel mux 0
+ read &= (~((1 << 31) | (0xf << 16) | (0x7 << 20)));
+ read |= (1 << 23) | (clk_div << 16) | (clk_sel << 20);
+ } else {
+ read &= (~((1 << 31) | (0xf << 24) | (0x7 << 28)));
+ read |= (1 << 23) | (clk_div << 24);
+ read |= (clk_sel << 28) | (1 << 31);
+ }
+ }
+ writel(read, reg);
+
+ pr_debug("%s\n", __func__);
+
+}
+
+static void start_dsp(uint32_t dsp_id, uint32_t reset_addr)
+{
+ uint32_t StatVectorSel;
+ uint32_t strobe = 1;
+ uint32_t tmp;
+ uint32_t read;
+ void __iomem *reg;
+
+ reg = get_dsp_addr(dsp_id);
+ StatVectorSel = (reset_addr != 0xfffa0000);
+
+ // the bit 0 is no use, dsp in tm2 is non-secure
+ tmp = 0x1 | StatVectorSel<<1 | strobe<<2;
+ scpi_init_dsp_cfg0(dsp_id, reset_addr, tmp);
+
+
+ read = readl(reg+REG_DSP_CFG0);
+ pr_debug("REG_DSP_CFG0 read=0x%x\n", read);
+ if (dsp_id == 0) {
+ read = read & (~((1 << 31) | (1 << 30) | (0xffff << 0)));
+ read = read | (1 << 29) | (0 << 0); // 29 irq_clk_en
+ } else {
+ read = read & (~((1 << 31) | (1 << 30) | (0xffff << 0)));
+ read = read | (1 << 29) | (1 << 0);
+ }
+ writel(read, reg+REG_DSP_CFG0);
+ tm2_dsp_top_reg_dump("REG_DSP_CFG0", reg, REG_DSP_CFG0);
+
+ pr_debug("%s\n", __func__);
+
+}
+
+static void power_switch_to_dsp_a(int pwr_cntl)
+{
+ uint32_t tmp;
+ void __iomem *reg;
+
+ pr_info("[PWR]: Power %s DSP A\n", pwr_cntl?"On":"Off");
+ if (pwr_cntl == 1) {
+ // Powerup dsp a
+ reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0;
+ tmp = readl(reg) & (~(0x1<<21));
+ writel(tmp, reg);// power on
+ udelay(5);
+
+ // Power up memory
+ reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0;
+ tmp = readl(reg) & (~(0xffff<<0));
+ writel(tmp, reg);
+ udelay(5);
+
+ // reset
+ reg = get_reset_addr() + REG_RESET4_LEVEL;
+ tmp = readl(reg) & (~(0x1<<0));
+ writel(tmp, reg);
+
+ reg = get_reset_addr() + REG_RESET1_LEVEL;
+ tmp = readl(reg) & (~(0x1<<20));
+ writel(tmp, reg);
+
+ // remove isolation
+ reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0;
+ tmp = readl(reg) & (~(0x1<<21));
+ writel(tmp, reg);
+
+ // pull up reset
+ reg = get_reset_addr() + REG_RESET4_LEVEL;
+ tmp = readl(reg) | (0x1<<0);
+ writel(tmp, reg);
+
+ reg = get_reset_addr() + REG_RESET1_LEVEL;
+ tmp = readl(reg) | (0x1<<20);
+ writel(tmp, reg);
+ } else {
+ // reset
+ reg = get_reset_addr() + REG_RESET4_LEVEL;
+ tmp = readl(reg) & (~(0x1<<0));
+ writel(tmp, reg);
+
+ reg = get_reset_addr() + REG_RESET1_LEVEL;
+ tmp = readl(reg) & (~(0x1<<20));
+ writel(tmp, reg);
+
+ // add isolation
+ reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0;
+ tmp = readl(reg) | (0x1<<21);
+ writel(tmp, reg);
+ udelay(5);
+
+ // power down memory
+ reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0;
+ tmp = readl(reg) | (0xffff<<0);
+ writel(tmp, reg);
+ udelay(5);
+
+ // power down dsp a
+ reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0;
+ tmp = readl(reg) | (0x1<<21);
+ writel(tmp, reg);
+ udelay(5);
+ }
+
+}
+
+static void power_switch_to_dsp_b(int pwr_cntl)
+{
+ uint32_t tmp;
+ void __iomem *reg;
+
+ if (pwr_cntl == 1) {
+ pr_info("[PWR]: Power on DSP B\n");
+ // Powerup dsp a
+ reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0;
+ tmp = readl(reg) & (~(0x1<<22));
+ writel(tmp, reg);// power on
+ udelay(5);
+
+ // Power up memory
+ reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0;
+ tmp = readl(reg) & (~(0xffff<<16));
+ writel(tmp, reg);
+ udelay(5);
+
+ // reset
+ reg = get_reset_addr() + REG_RESET4_LEVEL;
+ tmp = readl(reg) & (~(0x1<<1));
+ writel(tmp, reg);
+
+ reg = get_reset_addr() + REG_RESET1_LEVEL;
+ tmp = readl(reg) & (~(0x1<<21));
+ writel(tmp, reg);
+
+ // remove isolation
+ reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0;
+ tmp = readl(reg) & (~(0x1<<22));
+ writel(tmp, reg);
+
+ // pull up reset
+ reg = get_reset_addr() + REG_RESET4_LEVEL;
+ tmp = readl(reg) | (0x1<<1);
+ writel(tmp, reg);
+
+ reg = get_reset_addr() + REG_RESET1_LEVEL;
+ tmp = readl(reg) | (0x1<<21);
+ writel(tmp, reg);
+ } else {
+ pr_info("[PWR]: Power off DSP B\n");
+ // reset
+ reg = get_reset_addr() + REG_RESET4_LEVEL;
+ tmp = readl(reg) & (~(0x1<<1));
+ writel(tmp, reg);
+
+ reg = get_reset_addr() + REG_RESET1_LEVEL;
+ tmp = readl(reg) & (~(0x1<<21));
+ writel(tmp, reg);
+
+ // add isolation
+ reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_ISO0;
+ tmp = readl(reg) | (0x1<<22);
+ writel(tmp, reg);
+ udelay(5);
+
+ // power down memory
+ reg = get_hiu_addr() + REG_HHI_DSP_MEM_PD_REG0;
+ tmp = readl(reg) | (0xffff<<16);
+ writel(tmp, reg);
+ udelay(5);
+
+ // power down dsp a
+ reg = get_ao_rti_addr() + REG_AO_RTI_GEN_PWR_SLEEP0;
+ tmp = readl(reg) | (0x1<<22);
+ writel(tmp, reg);
+ udelay(5);
+ }
+
+}
+
+static void tm2_dsp_power_switch(int dsp_id, int pwr_cntl)
+{
+ if (dsp_id == 0)
+ power_switch_to_dsp_a(pwr_cntl);
+ else if (dsp_id == 1)
+ power_switch_to_dsp_b(pwr_cntl);
+ else
+ pr_err("%s param: dsp_id=%d error\n", __func__, dsp_id);
+}
+
+void tm2_dsp_top_regs_dump(int dsp_id)
+{
+ void __iomem *reg;
+
+ pr_debug("%s\n", __func__);
+
+ reg = get_dsp_addr(dsp_id);
+ pr_debug("%s base=%p\n", __func__, reg);
+
+ tm2_dsp_top_reg_dump("REG_DSP_CFG0", reg, REG_DSP_CFG0);
+ tm2_dsp_top_reg_dump("REG_DSP_CFG1", reg, REG_DSP_CFG1);
+ tm2_dsp_top_reg_dump("REG_DSP_CFG2", reg, REG_DSP_CFG2);
+ tm2_dsp_top_reg_dump("REG_DSP_RESET_VEC", reg, REG_DSP_RESET_VEC);
+
+ reg = get_hiu_addr();
+ tm2_dsp_top_reg_dump("REG_HHI_DSP_CLK_CNTL", reg,
+ REG_HHI_DSP_CLK_CNTL);
+ tm2_dsp_top_reg_dump("REG_HHI_DSP_MEM_PD_REG0", reg,
+ REG_HHI_DSP_MEM_PD_REG0);
+
+ reg = get_ao_rti_addr();
+ tm2_dsp_top_reg_dump("REG_AO_RTI_GEN_PWR_SLEEP0", reg,
+ REG_AO_RTI_GEN_PWR_SLEEP0);
+ tm2_dsp_top_reg_dump("REG_AO_RTI_GEN_PWR_ISO0", reg,
+ REG_AO_RTI_GEN_PWR_ISO0);
+
+ reg = get_reset_addr();
+ tm2_dsp_top_reg_dump("REG_RESET1_LEVEL", reg, REG_RESET1_LEVEL);
+ tm2_dsp_top_reg_dump("REG_RESET4_LEVEL", reg, REG_RESET4_LEVEL);
+
+}
+
+static void tm2_dsp_set_clk(int dsp_id, int freq_sel)
+{
+ clk_util_set_dsp_clk(dsp_id, freq_sel);
+}
+
+void tm2_dsp_hw_init(int dsp_id, int freq_sel)
+{
+ int pwr_cntl = 1;
+
+ tm2_dsp_regs_iomem_init();
+ tm2_dsp_set_clk(dsp_id, freq_sel);
+ tm2_dsp_power_switch(dsp_id, pwr_cntl);
+
+ pr_debug("%s done\n", __func__);
+}
+
+void tm2_dsp_start(int dsp_id, int freq_sel)
+{
+ start_dsp(dsp_id, freq_sel);
+}
+
+void tm2_dsp_bootup(int dsp_id, uint32_t reset_addr, int freq_sel)
+{
+ int pwr_cntl = 1;
+
+ //reset_addr = 0x30000000;
+ //dsp_id = 0;
+ freq_sel = 1;
+
+ pr_debug("%s dsp_id=%d, address=0x%x\n",
+ __func__, dsp_id, reset_addr);
+
+ tm2_dsp_set_clk(dsp_id, freq_sel);
+ tm2_dsp_power_switch(dsp_id, pwr_cntl);
+ tm2_dsp_start(dsp_id, reset_addr);
+
+ msleep(5*1000);
+ tm2_dsp_top_regs_dump(dsp_id);
+}
diff --git a/drivers/amlogic/hifi4dsp/tm2_dsp_top.h b/drivers/amlogic/hifi4dsp/tm2_dsp_top.h
new file mode 100644
index 000000000000..7173b28beddd
--- /dev/null
+++ b/drivers/amlogic/hifi4dsp/tm2_dsp_top.h
@@ -0,0 +1,36 @@
+/*
+ * drivers/amlogic/hifi4dsp/tm2_dsp_top.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _TM2_DSP_TOP_H
+#define _TM2_DSP_TOP_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/irqreturn.h>
+
+#include "hifi4dsp_priv.h"
+#include "hifi4dsp_firmware.h"
+#include "hifi4dsp_dsp.h"
+
+extern void tm2_dsp_bootup(int dsp_id, uint32_t reset_addr, int freq_sel);
+extern void tm2_dsp_regs_iomem_init(void);
+extern void tm2_dsp_hw_init(int dsp_id, int freq_sel);
+extern void tm2_dsp_top_regs_dump(int dsp_id);
+
+#endif /*_TM2_DSP_TOP_H*/