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