audio: hifi4dsp: Add tm2 hifi 4 dsp ctl driver support [1/1]
authorShuyu Li <shuyu.li@amlogic.com>
Wed, 27 Mar 2019 12:55:05 +0000 (20:55 +0800)
committerJianxin Pan <jianxin.pan@amlogic.com>
Thu, 16 May 2019 13:17:23 +0000 (06:17 -0700)
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>
30 files changed:
MAINTAINERS
arch/arm/boot/dts/amlogic/mesontm2.dtsi
arch/arm/boot/dts/amlogic/tm2_pxp.dts
arch/arm/boot/dts/amlogic/tm2_t962e2_ab311.dts
arch/arm/boot/dts/amlogic/tm2_t962e2_ab319.dts
arch/arm/boot/dts/amlogic/tm2_t962x3_ab301.dts
arch/arm/boot/dts/amlogic/tm2_t962x3_ab309.dts
arch/arm/configs/meson64_a32_defconfig
arch/arm64/boot/dts/amlogic/mesontm2.dtsi
arch/arm64/boot/dts/amlogic/tm2_pxp.dts
arch/arm64/boot/dts/amlogic/tm2_t962e2_ab311.dts
arch/arm64/boot/dts/amlogic/tm2_t962e2_ab319.dts
arch/arm64/boot/dts/amlogic/tm2_t962x3_ab301.dts
arch/arm64/boot/dts/amlogic/tm2_t962x3_ab309.dts
arch/arm64/configs/meson64_defconfig
drivers/amlogic/Kconfig
drivers/amlogic/Makefile
drivers/amlogic/hifi4dsp/Kconfig [new file with mode: 0644]
drivers/amlogic/hifi4dsp/Makefile [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_api.h [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_dsp.c [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_dsp.h [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_firmware.c [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_firmware.h [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_ipc.c [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_ipc.h [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_module.c [new file with mode: 0644]
drivers/amlogic/hifi4dsp/hifi4dsp_priv.h [new file with mode: 0644]
drivers/amlogic/hifi4dsp/tm2_dsp_top.c [new file with mode: 0644]
drivers/amlogic/hifi4dsp/tm2_dsp_top.h [new file with mode: 0644]

index 0775fa6..b65fb09 100644 (file)
@@ -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/*
index c15290f..1c1bc90 100644 (file)
        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 {
index bdcede1..34910ea 100644 (file)
                        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 {
                         */
                };
        };
+
 }; /* end of / */
 
 &i2c0 {
index dd6f76d..8d055f7 100644 (file)
                        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";
index c39ae3e..db2014e 100644 (file)
                        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";
index 2e2f38c..c84e551 100644 (file)
                        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";
index dddc68e..a1aa913 100644 (file)
                        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";
index 457821a..365651d 100644 (file)
@@ -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
index bcbb435..190fc6c 100644 (file)
        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 {
index 94efd82..f4f6134 100644 (file)
                        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 {
index f044545..fdf9cc5 100644 (file)
                        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";
index db003a7..78fce0a 100644 (file)
                        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";
index a2576f3..7e38b56 100644 (file)
                        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";
index bd0d17c..50d7b7c 100644 (file)
                        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";
index eefdff9..90cf7fe 100644 (file)
@@ -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
index e12f32c..52c163e 100644 (file)
@@ -140,5 +140,7 @@ source "drivers/amlogic/dolby_fw/Kconfig"
 
 source "drivers/amlogic/ircut/Kconfig"
 
+source "drivers/amlogic/hifi4dsp/Kconfig"
+
 endmenu
 endif
index 0d79615..fec14e2 100644 (file)
@@ -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 (file)
index 0000000..622e835
--- /dev/null
@@ -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 (file)
index 0000000..afac2b1
--- /dev/null
@@ -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 (file)
index 0000000..04c6022
--- /dev/null
@@ -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 (file)
index 0000000..c0edcf9
--- /dev/null
@@ -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 (file)
index 0000000..66c21cc
--- /dev/null
@@ -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 (file)
index 0000000..c7ab5cb
--- /dev/null
@@ -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 (file)
index 0000000..fba82c0
--- /dev/null
@@ -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 (file)
index 0000000..20bfae1
--- /dev/null
@@ -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 (file)
index 0000000..efb239a
--- /dev/null
@@ -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 (file)
index 0000000..6dd3b51
--- /dev/null
@@ -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 (file)
index 0000000..3a39393
--- /dev/null
@@ -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 (file)
index 0000000..b8625ca
--- /dev/null
@@ -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 (file)
index 0000000..7173b28
--- /dev/null
@@ -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*/