| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Driver for Audio DSP. |
| * |
| * Copyright (C) 2023 ASRMicro |
| * |
| * Author: Wang Yan <yjgwang@asrmicro.com> |
| */ |
| |
| |
| /****************************************************************************** |
| * MODULE IMPLEMENTATION FILE |
| ******************************************************************************* |
| * Title: Driver for ADSP in AP subsystem |
| * |
| * Filename: adsp.c |
| * |
| * Authors: Wang Yan |
| * |
| * Description: Driver file for ADSP. |
| * |
| * Last Updated: |
| * |
| * Notes: |
| ******************************************************************************/ |
| |
| |
| /****************************************************************************** |
| * Include files |
| ******************************************************************************/ |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/delay.h> |
| #include <linux/of_device.h> |
| #include <linux/uaccess.h> |
| #include <linux/proc_fs.h> |
| #include <linux/fs.h> |
| #include <linux/debugfs.h> |
| #include <linux/gpio.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/pinctrl/consumer.h> |
| #include <linux/platform_device.h> |
| #include <linux/mutex.h> |
| #include <linux/of.h> |
| #include <linux/of_gpio.h> |
| #include <asm/io.h> |
| #include <linux/interrupt.h> |
| #include <linux/cputype.h> |
| #include <linux/cdev.h> |
| #include <linux/device.h> |
| #include <linux/ioctl.h> |
| #include <linux/vmalloc.h> |
| #include <linux/irq.h> |
| #include <linux/of_irq.h> |
| #include <linux/of_platform.h> |
| #include <linux/kthread.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/bug.h> |
| #include <asm/cacheflush.h> |
| #include "adsp.h" |
| #include "adsp_ve.h" |
| |
| |
| /****************************************************************************** |
| * Macro definitions |
| ******************************************************************************/ |
| #define ADSP_PRINT_IPC 0 |
| |
| #define IPC_MAX_CMD_LENGTH 128 |
| |
| #define MIN(x, y) ((x < y) ? x : y) |
| |
| #define IPC_SUB_OPCODE( OPCODE ) ((OPCODE) & 0x3F) |
| #define IPC_SET_ID( OPCODE ) ((UINT16)(((OPCODE) >> IPC_HEADER_SETID_BITNUM) & 0x3)) |
| #define IPC_DATA_BIT( OPCODE ) ((UINT16)(((OPCODE) >> IPC_HEADER_DATA_BITNUM) & 0x1)) |
| #define IPC_OPCODE(SET_ID, SUB_OPCODE) ((SET_ID << IPC_HEADER_SETID_BITNUM) | SUB_OPCODE) |
| |
| #define IPC_COMPOSE_HEADER( SetID, SubSet, Urgent, Data, Long ) ((UINT16)( \ |
| (((UINT16)(SetID)) << IPC_HEADER_SETID_BITNUM) | \ |
| (((UINT16)(SubSet)) << IPC_HEADER_SUBSET_BITNUM) | \ |
| (((UINT16)(Data)) << IPC_HEADER_DATA_BITNUM))) |
| |
| #define IPC_COMPOSE_DATA_HEADER( ChID, CopyFlag, DataLen ) (((UINT32)(DataLen)) << 16) |
| |
| |
| typedef struct AdspRingCtl { |
| /* TX ring buffer for CP , CP write , AP read */ |
| unsigned int cp_wptr; |
| unsigned int ap_rptr; |
| |
| /* RX ring buffer for CP , AP write , CP read */ |
| unsigned int ap_wptr; |
| unsigned int cp_rptr; |
| |
| /* physical address from AP's pespective*/ |
| unsigned int rxring_base; |
| unsigned int txring_base; |
| |
| unsigned int unitsz; |
| unsigned int unitnum; |
| |
| }AdspRingCtl; |
| |
| typedef struct AudioIpcHandle { |
| AdspRingCtl *ring_ctl; |
| unsigned int *tx_ring_buffer; |
| unsigned int *rx_ring_buffer; |
| |
| unsigned int iir; |
| }AudioIpcHandle; |
| |
| //#define ADSP_VE_ADDR (0xD6A01880) |
| static void __iomem *rVe = NULL; |
| |
| //#define ADSP_RING_CTL_ADDR (0xD6A01000) |
| static void __iomem *rRingCtrl = NULL; |
| |
| #define ADSP_RING_BUFFER_SIZE (1024) |
| |
| static unsigned int adsp_irq = 0; |
| |
| //platform_get_resource 0 |
| #define ADSP_DUMP_SQU_ADDR (0xd101c000) |
| #define ADSP_DUMP_SQU_LENGTH (0x4000) |
| static void __iomem *rDumpSQU = NULL; |
| |
| //platform_get_resource 1 |
| #define ADSP_DUMP_DTCM_ADDR (0xd6000000) |
| #define ADSP_DUMP_DTCM_LENGTH (0x40000) |
| static void __iomem *rDumpDTCM = NULL; |
| |
| //platform_get_resource 2 |
| #define ADSP_DUMP_SHM_ADDR (0xd6a00000) |
| #define ADSP_DUMP_SHM_LENGTH (0x4000) |
| static void __iomem *rDumpSHM = NULL; |
| |
| //platform_get_resource 3 |
| #define ITCM_ADDR (0xd6200000) |
| #define ITCM_LENGTH (0x00010000) |
| static void __iomem *rItcm = NULL; |
| |
| //platform_get_resource 4 |
| //#define IPC_AP2AUD_BASE 0xd401d100 |
| //#define APB_H2_IPC_CPU_DWR_ADDR (IPC_AP2AUD_BASE + 0x0) |
| //#define APB_H2_IPC_CPU_WDR_ADDR (IPC_AP2AUD_BASE + 0x4) |
| //#define APB_H2_IPC_CPU_ISRW_ADDR (IPC_AP2AUD_BASE + 0x8) |
| //#define APB_H2_IPC_CPU_ICR_ADDR (IPC_AP2AUD_BASE + 0xc) |
| //#define APB_H2_IPC_CPU_IIR_ADDR (IPC_AP2AUD_BASE + 0x10) |
| //#define APB_H2_IPC_CPU_RDR_ADDR (IPC_AP2AUD_BASE + 0x14) |
| static void __iomem *rIpcBASE = NULL; |
| static void __iomem *rIpcDWR = NULL; |
| static void __iomem *rIpcWDR = NULL; |
| static void __iomem *rIpcISRW = NULL; |
| static void __iomem *rIpcICR = NULL; |
| static void __iomem *rIpcIIR = NULL; |
| static void __iomem *rIpcRDR = NULL; |
| |
| //platform_get_resource 5 |
| static void __iomem *rSspaBase = NULL; |
| //0xd401e178 |
| static void __iomem *rSspaClk = NULL; |
| //0xd401e17c |
| static void __iomem *rSspaFrm = NULL; |
| //0xd401e180 |
| static void __iomem *rSspaTxd = NULL; |
| //0xd401e184 |
| static void __iomem *rSspaRxd = NULL; |
| |
| //platform_get_resource 6 |
| //0xd6800014 |
| static void __iomem *rPc = NULL; |
| |
| //platform_get_resource 7 |
| //0xd6800048 |
| static void __iomem *rRls = NULL; |
| |
| //platform_get_resource 8 |
| //Can not platform_get_resource from dts |
| //#define AP_IPC_CLK_RST (0xd4015090) |
| static void __iomem *rClkRst = NULL; |
| |
| //platform_get_resource 9 |
| //0xd42828f0 |
| static void __iomem *rPwr = NULL; |
| |
| //should be ioremap earlier before adsp driver init |
| #define REG_PMU_AUDIO_CLK_RES_CTRL (0xd428294c) |
| static void __iomem *rClk = NULL; |
| |
| #define ADSP_DDR_LOAD_ADDR (0x8d80000) |
| #define ADSP_DDR_LOAD_LENGTH (0x280000) |
| |
| #define ADSP_DDR_LOAD_ADDR_S (0x5f80000) |
| #define ADSP_DDR_LOAD_LENGTH_S (0x80000) |
| |
| /* macro for register operation */ |
| #define reg_read(reg) *(volatile unsigned int *)(reg) |
| #define reg_write(reg, val) *(volatile unsigned int *)(reg) = (val) |
| #define reg_bit_set(reg, val) *(volatile unsigned int *)(reg) |= (1 << (val)) |
| #define reg_bit_clr(reg, val) *(volatile unsigned int *)(reg) &= ~(1 << (val)) |
| |
| |
| /****************************************************************************** |
| * Local variable definitions |
| ******************************************************************************/ |
| static AudioIpcHandle g_audio_ipc_handle = { |
| .ring_ctl = (AdspRingCtl *) 0, |
| .tx_ring_buffer = (unsigned int *)0, |
| .rx_ring_buffer = (unsigned int *)0, |
| .iir = 0 |
| }; |
| |
| static struct mutex g_audio_ipc_sema; |
| |
| /****************************************************************************** |
| * function definitions |
| ******************************************************************************/ |
| static void adsp_ipc_clk_enable(void) |
| { |
| //unsigned int val = 0; |
| |
| //val = reg_read(rClkRst); |
| //val |= (0x3); |
| reg_write(rClkRst, 0x3); |
| return; |
| } |
| |
| static void print_adsp_ipc_clk(void) |
| { |
| unsigned int val = 0; |
| |
| val = reg_read(rClkRst); |
| |
| printk(KERN_INFO "reg[0x%x] = 0x%x", (unsigned int)rClkRst, val); |
| } |
| |
| static unsigned int adsp_ipc_read_iir(void) |
| { |
| reg_write(rIpcDWR, 0);//dump write on demand |
| return reg_read(rIpcIIR); |
| } |
| |
| static void adsp_ipc_set_wdr(void) |
| { |
| reg_write(rIpcWDR, 0x55AA6688); |
| return; |
| } |
| |
| static void adsp_ipc_set_isrw(void) |
| { |
| reg_bit_set(rIpcISRW, 8); |
| //reg_write(rIpcISRW, ADSP_IPC_INT_CHANNEL); |
| return; |
| } |
| |
| static void adsp_ipc_clear(unsigned int cls) |
| { |
| reg_write(rIpcICR, cls); |
| return; |
| } |
| |
| #if 0 //keep this for test |
| static unsigned int adsp_ipc_tx_available(void) |
| { |
| unsigned int delta = g_audio_ipc_handle.ring_ctl->cp_wptr - g_audio_ipc_handle.ring_ctl->ap_rptr; |
| |
| return g_audio_ipc_handle.ring_ctl->unitnum - delta; |
| } |
| #endif |
| |
| static unsigned int adsp_ipc_rx_available(void) |
| { |
| return g_audio_ipc_handle.ring_ctl->ap_wptr - g_audio_ipc_handle.ring_ctl->cp_rptr; |
| } |
| |
| #if 0 //keep this for test |
| static void print_AdspRingCtl(void) |
| { |
| if (NULL == g_audio_ipc_handle.ring_ctl) { |
| printk("g_audio_ipc_handle.ring_ctl:0x%x\n", (unsigned int)g_audio_ipc_handle.ring_ctl); |
| |
| return; |
| } |
| |
| printk(KERN_INFO "p_adsp_ring_ctl(0x%x)," \ |
| "tx_ring_buffer:(0x%x)," \ |
| "rx_ring_buffer:(0x%x)," \ |
| "ap_wptr:(0x%x)," \ |
| "cp_rptr:(0x%x)," \ |
| "cp_wptr:(0x%x)," \ |
| "ap_rptr:(0x%x)," \ |
| "unitsz:(0x%x)," \ |
| "unitnum:(0x%x)\n", \ |
| (unsigned int)g_audio_ipc_handle.ring_ctl, \ |
| (unsigned int)g_audio_ipc_handle.tx_ring_buffer, \ |
| (unsigned int)g_audio_ipc_handle.rx_ring_buffer, \ |
| g_audio_ipc_handle.ring_ctl->ap_wptr, \ |
| g_audio_ipc_handle.ring_ctl->cp_rptr, \ |
| g_audio_ipc_handle.ring_ctl->cp_wptr, \ |
| g_audio_ipc_handle.ring_ctl->ap_rptr, \ |
| g_audio_ipc_handle.ring_ctl->unitsz, \ |
| g_audio_ipc_handle.ring_ctl->unitnum); |
| |
| printk(KERN_INFO "rxring_base:(0x%x),txring_base:(0x%x)\n", \ |
| g_audio_ipc_handle.ring_ctl->rxring_base, \ |
| g_audio_ipc_handle.ring_ctl->txring_base); |
| |
| return; |
| } |
| #endif |
| |
| extern void handle_audio_playback_data(u16 len, u32 *addr, u16 id); |
| extern void handle_audio_record_data(u16 len, u32 *addr, u16 id); |
| extern void handle_adsp_exception(void); |
| extern int audio_report_adsp_pwr_to_cp(u16 onoff); |
| static int b_evs_set = 0; |
| static int b_adsp_pwr_on = 0; |
| static int b_adsp_pm_cp = 0; |
| static unsigned char sAdspVer[42]; |
| |
| #define ENABLE_ADSP_PM 0 |
| #if ENABLE_ADSP_PM |
| static void pwr_off_hw(void); |
| static struct task_struct *adsp_pwr_off_taskref = NULL; |
| static DEFINE_MUTEX(pwr_lock); |
| |
| static int power_off_adsp_task(void *data) |
| { |
| msleep(1000); |
| pwr_off_hw(); |
| if (b_adsp_pm_cp) { |
| audio_report_adsp_pwr_to_cp(0); |
| b_adsp_pm_cp = 0; |
| } |
| adsp_pwr_off_taskref = NULL; |
| return 0; |
| } |
| |
| void start_power_off_adsp(void) |
| { |
| printk(KERN_INFO "%s/%d\n", __FUNCTION__, __LINE__); |
| if (adsp_pwr_off_taskref == NULL) { |
| adsp_pwr_off_taskref = (struct task_struct *)kthread_run(power_off_adsp_task, NULL, "pwr_off_adsp"); |
| } |
| } |
| #endif |
| |
| static void pwr_on_hw(void); |
| static struct task_struct *adsp_pwr_on_taskref = NULL; |
| |
| static int power_on_adsp_task(void *data) |
| { |
| pwr_on_hw(); |
| audio_report_adsp_pwr_to_cp(1); |
| b_adsp_pm_cp = 1; |
| adsp_pwr_on_taskref = NULL; |
| return 0; |
| } |
| |
| void start_power_on_adsp_for_cp(void) |
| { |
| printk(KERN_INFO "%s/%d: onoff=%d\n", __FUNCTION__, __LINE__, b_adsp_pwr_on); |
| if (b_adsp_pwr_on) { |
| audio_report_adsp_pwr_to_cp(1); |
| b_adsp_pm_cp = 1; |
| return; |
| } |
| if (adsp_pwr_on_taskref == NULL) { |
| adsp_pwr_on_taskref = (struct task_struct *)kthread_run(power_on_adsp_task, NULL, "pwr_on_adsp"); |
| } |
| } |
| |
| EXPORT_SYMBOL_GPL(start_power_on_adsp_for_cp); |
| |
| |
| void AdspIPCCmdProcess(void) |
| { |
| UINT32 LocalBuf[3 + IPC_MAX_CMD_LENGTH / 4]; |
| IpcSetParamsType CmdParams; |
| //IpcDataParamsType DataParams; |
| IpcDataParamsType *DataParamsPtr = 0; |
| AdspRingCtl *IpcBuf = g_audio_ipc_handle.ring_ctl; |
| UINT32 IPCsizeMask = IpcBuf->unitnum - 1; |
| //UINT32 *IPCCmdBuf = (UINT32 *) IpcBuf->rxring_base; |
| UINT32 *IPCCmdBuf = g_audio_ipc_handle.rx_ring_buffer; |
| void __iomem *rData = NULL; |
| |
| //printk("rxring_base= 0x%x, cp_rptr 0x%x\n", IpcBuf->rxring_base, IpcBuf->cp_rptr); |
| |
| while (IpcBuf->ap_wptr != IpcBuf->cp_rptr) { |
| UINT32 CopyLen; |
| int i, SetID; |
| UINT32 LocalOut = IpcBuf->cp_rptr; |
| union { |
| IpcCmdMsgHeaderType Cmd; |
| UINT32 Value; |
| } CmdHeader; |
| |
| CmdHeader.Value = IPCCmdBuf[IPCsizeMask & LocalOut]; |
| |
| CopyLen = (sizeof(CmdHeader) + CmdHeader.Cmd.UserLength + 3) >> 2; |
| for (i = 0; i < MIN(CopyLen, sizeof(LocalBuf) / 4); i++) { |
| LocalBuf[i] = IPCCmdBuf[IPCsizeMask & LocalOut]; |
| LocalOut++; |
| } |
| IpcBuf->cp_rptr = LocalOut; |
| |
| //Check Msg header |
| if (!CmdHeader.Cmd.Header) { |
| printk(KERN_INFO "%s/%d: ADSP IPC CMD Header is NULL\n", __FUNCTION__, __LINE__); |
| handle_adsp_exception(); |
| return; |
| } |
| |
| //Parser Msg header & DataParamsPtr |
| CmdParams.SubOpcode = CmdHeader.Cmd.Header; |
| CmdParams.CmdMsgLength = CmdHeader.Cmd.UserLength; |
| CmdParams.CmdMsgPtr = &LocalBuf[1]; |
| DataParamsPtr = 0; |
| if (IPC_DATA_BIT( CmdParams.SubOpcode )) { |
| DataParamsPtr = (IpcDataParamsType *)&LocalBuf[1]; |
| |
| CmdParams.CmdMsgPtr += sizeof(IpcDataParamsType) / sizeof(UINT32); //Msg skip IpcDataParamsType |
| CmdParams.CmdMsgLength -= sizeof(IpcDataParamsType); |
| } |
| |
| //get SetID & subopcode |
| SetID = IPC_SET_ID( CmdParams.SubOpcode ); |
| //CmdParams.SubOpcode = IPC_SUB_OPCODE( CmdParams.SubOpcode ); |
| |
| #if ADSP_PRINT_IPC |
| printk("Receive: SetId:0x%x, SubOpcode:0x%x, CmdMsgLength:0x%x, CmdMsg:|%.8x|%.8x|%.8x|%.8x|\n", |
| SetID, CmdParams.SubOpcode, CmdParams.CmdMsgLength, |
| LocalBuf[1], LocalBuf[2], LocalBuf[3], LocalBuf[4]); |
| #endif |
| |
| #if 0 |
| //Process subopcode |
| if (DataParamsPtr) { //for data channel |
| UINT8 *p = NULL; |
| //printk(KERN_INFO "%s/%d: 0x%x|%d\n", __FUNCTION__, __LINE__, (unsigned int)DataParamsPtr->Ptr, DataParamsPtr->Length); |
| rData = ioremap_nocache((unsigned int)DataParamsPtr->Ptr, DataParamsPtr->Length); |
| if (!rData) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| return; |
| } |
| p = (UINT8 *)rData; |
| printk(KERN_INFO "Receive: SubOpcode:0x%.4x len:%d data:|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|\n", \ |
| CmdParams.SubOpcode, DataParamsPtr->Length, \ |
| *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, \ |
| *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++); |
| |
| //Process data |
| } |
| #endif |
| |
| switch (CmdParams.SubOpcode) { |
| #if ENABLE_ADSP_PM |
| case POWER_OFF_REQ: |
| { |
| UINT16 *p = (UINT16 *)&LocalBuf[1]; |
| printk("%s/%d: POWER_OFF_REQ: SubOpcode:0x%x\n", __FUNCTION__, __LINE__, CmdParams.SubOpcode); |
| if (b_adsp_pwr_on) { |
| start_power_off_adsp(); |
| } |
| break; |
| } |
| #endif |
| case DATA_REQ: |
| { |
| UINT16 *p = (UINT16 *)&LocalBuf[1]; |
| //printk("%s/%d: DATA_REQ: StreamID:%d, DataLen:0x%x, DataAddr:0x%x\n", __FUNCTION__, __LINE__, p[0], p[1], LocalBuf[2]); |
| handle_audio_playback_data(p[1], (u32 *)LocalBuf[2], p[0]); |
| break; |
| } |
| |
| case RECORD_DATA: |
| { |
| UINT16 *p = (UINT16 *)&LocalBuf[1]; |
| //printk("%s/%d: RECORD_DATA: Length:0x%x, Ptr:0x%x, StreamID:%d\n", __FUNCTION__, __LINE__, p[1], LocalBuf[2], p[4]); |
| handle_audio_record_data(p[1], (u32 *)LocalBuf[2], p[4]); |
| break; |
| } |
| |
| case VOICE_DEBUG_CONTROL: |
| { |
| if (!b_evs_set) { |
| memcpy(sAdspVer, CmdParams.CmdMsgPtr, 40); |
| sAdspVer[40] = '\0'; |
| printk(KERN_INFO "ADSP Version: %s\n", sAdspVer); |
| b_evs_set = 1; |
| } |
| break; |
| } |
| |
| case ADSP_EXCEPTION: |
| { |
| UINT8 *p = NULL; |
| printk("%s/%d: ADSP report exception\n", __FUNCTION__, __LINE__); |
| if (DataParamsPtr) { //for data channel |
| rData = ioremap_nocache((unsigned int)DataParamsPtr->Ptr, DataParamsPtr->Length); |
| if (!rData) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| return; |
| } |
| p = (UINT8 *)rData; |
| for (i = 0; i < (DataParamsPtr->Length + 15) / 16; i++) { |
| #if 0 |
| printk(KERN_INFO "|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|\n", \ |
| *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++, \ |
| *p++, *p++, *p++, *p++, *p++, *p++, *p++, *p++); |
| #else |
| printk(KERN_INFO "|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|\n", \ |
| *p, *(p+1), *(p+2), *(p+3), *(p+4), *(p+5), *(p+6), *(p+7), \ |
| *(p+8), *(p+9), *(p+10), *(p+11), *(p+12), *(p+13), *(p+14), *(p+15)); |
| p += 16; |
| #endif |
| } |
| handle_adsp_exception(); |
| } |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| if (DataParamsPtr) { //for data channel |
| if (rData) { |
| iounmap(rData); |
| } |
| } |
| } |
| } |
| |
| //int gIpcIntDebug = 0; |
| static irqreturn_t adsp_ipc_int_handler(int data, void *dev_id) |
| { |
| //printk(KERN_INFO "%s:%d: \n", __FUNCTION__, __LINE__); |
| g_audio_ipc_handle.iir = adsp_ipc_read_iir(); |
| adsp_ipc_clear(g_audio_ipc_handle.iir); |
| return IRQ_WAKE_THREAD; |
| } |
| |
| static int b_phase2_inited = 0; |
| static int adsp_ipc_init_phase2(void); |
| irqreturn_t adsp_ipc_hisr(int irq, void *dev_id) |
| { |
| unsigned int avail; |
| |
| //print_AdspRingCtl(); |
| //printk("adsp_ipc_hisr, g_audio_ipc_handle.iir:0x%x\n", g_audio_ipc_handle.iir); |
| |
| if (!b_phase2_inited) { |
| b_phase2_inited = adsp_ipc_init_phase2(); |
| |
| if (!b_phase2_inited) { |
| return IRQ_HANDLED; |
| } |
| } |
| |
| //unsigned int ipc_rdr = adsp_ipc_read_rdr(); |
| |
| avail = adsp_ipc_rx_available(); |
| |
| if (avail) { |
| AdspIPCCmdProcess(); |
| } |
| |
| //printk(KERN_INFO "%s/%d\n", __FUNCTION__, __LINE__); |
| |
| //adsp_ipc_set_isrw(); |
| |
| return IRQ_HANDLED; |
| } |
| |
| int adsp_ipc_init_phase1(void) |
| { |
| int ret; |
| |
| //AP ring_ctl addr is agreed in advance |
| if (!rRingCtrl) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| return 1; |
| } |
| g_audio_ipc_handle.ring_ctl = (AdspRingCtl *) rRingCtrl; |
| |
| /*==================================================================*/ |
| /* in case that CP reset,rx clear buffer and INT */ |
| /*==================================================================*/ |
| //adsp_ipc_clear(0xFFFFFFFF); |
| |
| /*==================================================================*/ |
| /* create semaphore for ring buffer */ |
| /*==================================================================*/ |
| mutex_init(&g_audio_ipc_sema); |
| ret = request_threaded_irq(adsp_irq, adsp_ipc_int_handler, adsp_ipc_hisr, \ |
| IRQF_TRIGGER_HIGH | IRQF_ONESHOT, \ |
| "ipc_adsp2ap_int", NULL); |
| if (ret < 0) { |
| printk(KERN_INFO "%s/%d: request irq fail\n", __FUNCTION__, __LINE__); |
| return 3; |
| } |
| printk(KERN_INFO "%s/%d: ret=%d\n", __FUNCTION__, __LINE__, ret); |
| |
| disable_irq(adsp_irq); |
| enable_irq(adsp_irq); |
| |
| /*==================================================================*/ |
| /* enable ipc clock in */ |
| /*==================================================================*/ |
| print_adsp_ipc_clk(); |
| adsp_ipc_clk_enable(); |
| print_adsp_ipc_clk(); |
| |
| mutex_lock(&g_audio_ipc_sema); |
| |
| printk(KERN_INFO "%s/%d Done.\n", __FUNCTION__, __LINE__); |
| |
| return 0; |
| } |
| |
| static int adsp_ipc_init_phase2(void) |
| { |
| //AP ring_ctl addr is from IPC reg |
| //g_audio_ipc_handle.ring_ctl = (AdspRingCtl *)adsp_ipc_read_rdr(); |
| printk("%s/%d g_audio_ipc_handle.ring_ctl 0x%x\n", __FUNCTION__, __LINE__, (unsigned int)g_audio_ipc_handle.ring_ctl); |
| |
| if (g_audio_ipc_handle.ring_ctl |
| && g_audio_ipc_handle.ring_ctl->rxring_base |
| && g_audio_ipc_handle.ring_ctl->txring_base) { |
| |
| g_audio_ipc_handle.rx_ring_buffer = ioremap_nocache(g_audio_ipc_handle.ring_ctl->rxring_base, ADSP_RING_BUFFER_SIZE); |
| if (NULL == g_audio_ipc_handle.rx_ring_buffer) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| return 0; |
| } |
| |
| g_audio_ipc_handle.tx_ring_buffer = ioremap_nocache(g_audio_ipc_handle.ring_ctl->txring_base, ADSP_RING_BUFFER_SIZE); |
| if (NULL == g_audio_ipc_handle.tx_ring_buffer) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| return 0; |
| } |
| |
| mutex_unlock(&g_audio_ipc_sema); // after Adsp report the Version report, CP can send the IPC to Adsp |
| printk(KERN_INFO "%s/%d Done.\n", __FUNCTION__, __LINE__); |
| return 1; |
| } |
| else { |
| printk("adsp_ipc_init_phase1 Do Not Excute\n"); |
| } |
| |
| return 0; |
| } |
| |
| static int PackIpcMsg( |
| UINT32 *Buf, |
| IpcSetIDType SetId, |
| IpcSetParamsType *IpcMsgParams, |
| IpcDataParamsType *IpcDataParams) |
| { |
| Word32 CopyLen = 1; |
| Word32 MsgLen = (IpcMsgParams->CmdMsgLength + 1) >> 1; |
| { |
| union { |
| IpcCmdMsgHeaderType Msg; |
| UINT32 Value; |
| } IpcHeader; |
| |
| IpcHeader.Msg.Header = IPC_COMPOSE_HEADER( SetId, IpcMsgParams->SubOpcode, 0, (IpcDataParams ? 1 : 0), 0 ); |
| IpcHeader.Msg.UserLength = IpcMsgParams->CmdMsgLength * 2 + (IpcDataParams ? sizeof(IpcDataParamsType) : 0); //UserLength in bytes, without header size |
| *Buf++ = IpcHeader.Value; //32bit operation |
| } |
| |
| if (IpcDataParams) { |
| *Buf++ = *((UINT32 *)(IpcDataParams)); // copy data header : ChId, CopyFlag and Length |
| *Buf++ = (UINT32)(IpcDataParams->Ptr); // Copy Ptr |
| CopyLen += 2; |
| } |
| |
| CopyLen += MsgLen; |
| memcpy((short *)Buf, (short *)IpcMsgParams->CmdMsgPtr, MsgLen * 4); |
| return CopyLen; |
| } |
| |
| IpcReturnCodeType IpcSendAdspMessageData( |
| IpcSetIDType SetId, |
| IpcSetParamsType *IpcMsgParams, |
| IpcDataParamsType *IpcDataParams) |
| { |
| AdspRingCtl *IpcBuf = g_audio_ipc_handle.ring_ctl; |
| // IPCRegType *IpcReg = (IPCRegType *)(BX2_CP_IPC_BASE) ; |
| UINT32 LocalBuf[3 + IPC_MAX_CMD_LENGTH / 4]; |
| //UINT32 currIMASK; |
| UINT32 CopyLen = PackIpcMsg(LocalBuf, SetId, IpcMsgParams, IpcDataParams); |
| IpcReturnCodeType ret = IPC_OK; |
| |
| //UINT16 *p = (UINT16 *)LocalBuf; |
| |
| //printk(KERN_INFO "%s/%d\n", __FUNCTION__, __LINE__); |
| |
| if (!b_phase2_inited) { |
| printk(KERN_INFO "%s/%d: Adsp IPC is not ready\n", __FUNCTION__, __LINE__); |
| return IPC_ERROR; |
| } |
| |
| //printk(KERN_INFO "CopyLen:%d, data:|%.4x %.4x|%.4x %.4x|%.4x %.4x|%.4x %.4x|%.4x %.4x|\n", |
| //CopyLen, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9]); |
| |
| //atom operation |
| mutex_lock(&g_audio_ipc_sema); |
| |
| if ((IpcBuf->cp_wptr - IpcBuf->ap_rptr + CopyLen) <= IpcBuf->unitnum) { //enough space to fill |
| UINT32 IPCsizeMask = IpcBuf->unitnum - 1; |
| UINT32 Windex = IpcBuf->cp_wptr & IPCsizeMask; |
| //UINT32 *IPCMsgMEM = (UINT32 *)IpcBuf->txring_base; |
| UINT32 *IPCMsgMEM = g_audio_ipc_handle.tx_ring_buffer; |
| int i; |
| for (i = 0; i < CopyLen; i++) { |
| IPCMsgMEM[Windex++] = LocalBuf[i]; |
| Windex &= IPCsizeMask; |
| } |
| IpcBuf->cp_wptr += CopyLen; |
| //for ARM use DSB, ISB before trigger int to DSP !!!!!!!!!!!!. |
| //issue_sync_barrier |
| //NOP |
| //DSB |
| //ISB |
| //NOP |
| //BX LR |
| __asm__ __volatile__ ("ISB"); |
| __asm__ __volatile__ ("DSB"); //make sure the data is ready before DSP is informed. |
| |
| //send IPC INT to Adsp |
| adsp_ipc_set_wdr(); |
| adsp_ipc_set_isrw(); |
| } |
| else { |
| printk("Send Adsp IPC CMD Overflow\n"); |
| ret = IPC_MSG_QUEUE_OVERFLOW; |
| } |
| |
| mutex_unlock(&g_audio_ipc_sema); |
| //printk(KERN_INFO "%s/%d Done.\n\n", __FUNCTION__, __LINE__); |
| return ret; |
| } |
| |
| |
| /****************************************************************************** |
| * Macro definitions |
| ******************************************************************************/ |
| //in short |
| #define VOICE_START_COMMAND_LENGTH 7 |
| #define ADSP_SET_COMMAND_LENGTH 32 |
| |
| |
| /****************************************************************************** |
| * Local variable definitions |
| ******************************************************************************/ |
| static int is_adsp_inited = 0; |
| static int is_dump_full_ddr = 0; |
| |
| int ap_send_adsp(u16 *msg, int msglen, int sub_cmd ) |
| { |
| IpcSetIDType SetId = 0; |
| IpcSetParamsType CmdParams; |
| IpcDataParamsType DataParams; |
| UINT8 CmdMsg[128] = { 0 }; |
| |
| //printk(KERN_INFO "send cmd to adsp\n"); |
| |
| memset(&CmdParams, 0, sizeof(IpcSetParamsType)); |
| memset(&DataParams, 0, sizeof(IpcDataParamsType)); |
| |
| if (!is_adsp_inited) { |
| printk(KERN_INFO "%s/%d: adsp is not ready\n", __FUNCTION__, __LINE__); |
| return -EFAULT; |
| } |
| |
| #if ENABLE_ADSP_PM |
| mutex_lock(&pwr_lock); |
| |
| if (!b_adsp_pwr_on) { |
| pwr_on_hw(); |
| } |
| |
| mutex_unlock(&pwr_lock); |
| #endif |
| |
| CmdParams.SubOpcode = sub_cmd; |
| CmdParams.CmdMsgLength = msglen; |
| if (CmdParams.CmdMsgLength < ADSP_SET_COMMAND_LENGTH * 2 - 3) { |
| if (DATA_INDICATION == sub_cmd) { |
| memcpy(CmdMsg, msg + sizeof(IpcDataParamsType) / 2, 2); |
| CmdParams.CmdMsgPtr = (UINT32 *)CmdMsg; |
| CmdParams.SubOpcode |= (0x1 << IPC_HEADER_DATA_BITNUM); |
| memcpy(&DataParams, msg, sizeof(IpcDataParamsType)); |
| #if ADSP_PRINT_IPC |
| printk(KERN_INFO "SetId:0x%x, SubOpcode:0x%x, CmdMsgLength:%d, DataParams.Length:0x%.4x, DataParams.Ptr:0x%.8x, CmdMsgPtr:0x%x\n", |
| SetId, CmdParams.SubOpcode, CmdParams.CmdMsgLength, |
| DataParams.Length, DataParams.Ptr, ((UINT16 *)CmdParams.CmdMsgPtr)[0]); |
| #endif |
| IpcSendAdspMessageData(SetId, &CmdParams, &DataParams); |
| } |
| else { |
| memcpy(CmdMsg, msg, msglen * 2); |
| CmdParams.CmdMsgPtr = (UINT32 *)CmdMsg; |
| #if ADSP_PRINT_IPC |
| printk(KERN_INFO "SetId:0x%x, SubOpcode:0x%x, CmdMsgLength:0x%x, CmdMsg:|%.8x|%.8x|%.8x|%.8x|\n", |
| SetId, CmdParams.SubOpcode, CmdParams.CmdMsgLength, |
| CmdParams.CmdMsgPtr[0], CmdParams.CmdMsgPtr[1], CmdParams.CmdMsgPtr[2], CmdParams.CmdMsgPtr[3]); |
| #endif |
| IpcSendAdspMessageData(SetId, &CmdParams, NULL); |
| } |
| |
| return 0; |
| } |
| return -EFAULT; |
| |
| } |
| |
| EXPORT_SYMBOL_GPL(ap_send_adsp); |
| |
| |
| /****************************************************************************** |
| * Macro definitions |
| ******************************************************************************/ |
| //#define AUDIODEBUG_LOG |
| |
| typedef unsigned short ApplicationID; |
| typedef unsigned int IPC_DataChannelNumber; |
| |
| typedef struct { |
| unsigned short opCode; |
| unsigned short length; |
| unsigned char *pipe; |
| }IPC_CmdMsgParams; |
| |
| typedef struct { |
| unsigned short setID; |
| unsigned short subOpCode; |
| bool allSubOpCodes; |
| }OpCodeParams; |
| |
| typedef struct { |
| unsigned short opCode; |
| unsigned short cmdLength; |
| unsigned short *cmdData; |
| }IPC_Command; |
| |
| typedef struct { |
| unsigned int dataChannelID; |
| unsigned int copyMode; |
| unsigned short dataSize; |
| unsigned int *dataSrcPtr; |
| unsigned int *dataDestPtr; |
| }IPC_DataStructToSend; |
| |
| typedef struct { |
| unsigned int *dataPtr; |
| unsigned short dataSize; |
| unsigned int dataChannelID; |
| }IPC_DataStructReceived; |
| |
| |
| /****************************************************************************** |
| * Local variable definitions |
| ******************************************************************************/ |
| static int is_1901_E = 0; |
| static int is_pmu_clk_inited = 0; |
| |
| |
| /****************************************************************************** |
| * functions for load adsp |
| ******************************************************************************/ |
| #define ADSP_BIN_NAME_1 "/data/adsp.bin" |
| #define ADSP_BIN_NAME_E "/lib/firmware/adsp.bin" |
| #define ADSP_BIN_NAME_S "/lib/firmware/adsp_s.bin" |
| #define ADSP_READ_SIZE (0x10000) |
| |
| #define ADSP_CHECK_IMAGE 1 |
| #if ADSP_CHECK_IMAGE |
| #define ADSP_CHECK_SIZE (0x1000) |
| #define ADSP_CHECK_COUNT 5 |
| #endif |
| |
| int enable_pmu_audio_clk(void) |
| { |
| if (is_pmu_clk_inited) { |
| return 0; |
| } |
| |
| if (!rClk) { |
| rClk = ioremap_nocache(REG_PMU_AUDIO_CLK_RES_CTRL, 4); |
| if (!rClk) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| return 1; |
| } |
| } |
| |
| reg_write(rClk, reg_read(rClk) & ~8); //put bx2 in reset |
| reg_bit_set(rClk, 19); //bypass repaire |
| reg_bit_set(rClk, 27); //enable hw mode |
| reg_bit_set(rClk, 29); //trigger hw pwr on |
| printk(KERN_INFO "%s/%d: Done.\n", __FUNCTION__, __LINE__); |
| |
| is_pmu_clk_inited = 1; |
| |
| return 0; |
| } |
| |
| static void pwr_on_hw(void) |
| { |
| volatile unsigned int rdata; |
| int i; |
| |
| if (b_adsp_pwr_on) { |
| return; |
| } |
| |
| //Step 1 : Reset DSP |
| enable_pmu_audio_clk(); |
| |
| for (i = 0; i < 50; i++) { |
| printk(KERN_INFO "wait for audio power state (on) ...\n"); |
| rdata = reg_read(rPwr); |
| //printk(KERN_INFO "pwr=0x%x\n", rdata); |
| if ((rdata & 0x800) != 0) { |
| break; |
| } |
| msleep(300); |
| } |
| printk(KERN_INFO "%s/%d: power on audio (hw mode) ok\n", __FUNCTION__, __LINE__); |
| |
| // Step2 : AP Copy ITCM to (0xD6200000) |
| if (is_1901_E) { |
| memcpy(rItcm, __va(ADSP_DDR_LOAD_ADDR), ITCM_LENGTH); |
| } |
| else { |
| memcpy(rItcm, __va(ADSP_DDR_LOAD_ADDR_S), ITCM_LENGTH); |
| } |
| |
| // Step3 : AP Set DSP PC= 0x10 |
| reg_write(rPc, 0x10); |
| |
| // Step4 : AP Release DSP |
| reg_bit_set(rClk, 3); //enable release audio aon reset |
| printk(KERN_INFO "%s/%d: release audio bx2 core\n", __FUNCTION__, __LINE__); |
| reg_write(rRls, 0xF); // enable axi dyn ckg |
| |
| printk(KERN_INFO "%s/%d Done.\n", __FUNCTION__, __LINE__); |
| b_adsp_pwr_on = 1; |
| } |
| |
| static void pwr_off_hw(void) |
| { |
| volatile unsigned int rdata; |
| int i; |
| |
| if (!b_adsp_pwr_on) { |
| return; |
| } |
| |
| reg_bit_set(rClk, 27); //enable hw mode |
| printk(KERN_INFO "%s/%d: trigger power off audio (hw mode)\n", __FUNCTION__, __LINE__); |
| reg_bit_clr(rClk, 29); //trigger hw pwr off |
| |
| for (i = 0; i < 50; i++) { |
| printk(KERN_INFO "wait for audio power state (off) ...\n"); |
| rdata = reg_read(rPwr); |
| //printk(KERN_INFO "pwr=0x%x\n", rdata); |
| if ((rdata & 0x40) == 0) { |
| break; |
| } |
| msleep(300); |
| } |
| |
| printk(KERN_INFO "%s/%d Done.\n", __FUNCTION__, __LINE__); |
| is_pmu_clk_inited = 0; |
| b_adsp_pwr_on = 0; |
| } |
| |
| extern int audio_request_cp_down(void); |
| |
| static int ap_boot_adsp(void) |
| { |
| struct file *fp; |
| mm_segment_t fs; |
| loff_t pos; |
| ssize_t readret; |
| unsigned char *pread; |
| void *ptr; |
| |
| #if ADSP_CHECK_IMAGE |
| unsigned char *pddr; |
| int b_check_fail = 0; |
| int i = 0; |
| #endif |
| |
| //Step 0 : Copy bin from Flash to DDR |
| if (!rPc || !rRls || !rItcm) { |
| printk(KERN_INFO "%s/%d: registers not ready for boot bx2\n", __FUNCTION__, __LINE__); |
| return -1; |
| } |
| |
| #if ADSP_CHECK_IMAGE |
| for (i = 0; i < ADSP_CHECK_COUNT; i++) { |
| pread = is_1901_E ? __va(ADSP_DDR_LOAD_ADDR) : __va(ADSP_DDR_LOAD_ADDR_S); |
| printk(KERN_INFO "%s/%d: 0x%x\n", __FUNCTION__, __LINE__, (unsigned int)pread); |
| b_check_fail = 0; |
| #endif |
| fp = filp_open(ADSP_BIN_NAME_1, O_RDONLY, 0); |
| if (IS_ERR(fp)) { |
| if (is_1901_E) { |
| fp = filp_open(ADSP_BIN_NAME_E, O_RDONLY, 0); |
| } |
| else { |
| fp = filp_open(ADSP_BIN_NAME_S, O_RDONLY, 0); |
| } |
| if (IS_ERR(fp)) { |
| printk(KERN_INFO "%s/%d: warning: open adsp bin fail, but can be ignored.\n", __FUNCTION__, __LINE__); |
| return -2; |
| } |
| printk(KERN_INFO "%s/%d: load %s\n", __FUNCTION__, __LINE__, is_1901_E ? ADSP_BIN_NAME_E : ADSP_BIN_NAME_S); |
| } |
| else { |
| printk(KERN_INFO "%s/%d: load %s\n", __FUNCTION__, __LINE__, ADSP_BIN_NAME_1); |
| } |
| |
| fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| pos = 0; |
| do { |
| readret = vfs_read(fp, pread, ADSP_READ_SIZE, &pos); |
| //printk(KERN_INFO "read: 0x%x|%x|%x|%x| read=0x%x pos=0x%llx \n", pread[0], pread[1], pread[2], pread[3], readret, pos); |
| |
| for (ptr = pread; |
| (unsigned int)ptr < (unsigned int)(pread + readret); |
| ptr += PAGE_SIZE) { |
| flush_dcache_page(virt_to_page(ptr)); |
| } |
| |
| pread += readret; |
| }while (readret == ADSP_READ_SIZE); |
| |
| printk(KERN_INFO "load adsp bin size: 0x%llx\n", pos); |
| if (is_1901_E) { |
| dma_sync_single_for_device(NULL, ADSP_DDR_LOAD_ADDR, ADSP_DDR_LOAD_LENGTH, DMA_FROM_DEVICE); |
| } |
| else { |
| dma_sync_single_for_device(NULL, ADSP_DDR_LOAD_ADDR_S, ADSP_DDR_LOAD_LENGTH_S, DMA_FROM_DEVICE); |
| } |
| |
| #if ADSP_CHECK_IMAGE |
| pddr = is_1901_E ? __va(ADSP_DDR_LOAD_ADDR) : __va(ADSP_DDR_LOAD_ADDR_S); |
| |
| pread = (char *)vmalloc(ADSP_CHECK_SIZE); |
| if (!pread) { |
| printk(KERN_INFO "%s/%d: vmalloc for adsp check ddr fail\n", __FUNCTION__, __LINE__); |
| filp_close(fp, NULL); |
| set_fs(fs); |
| return -3; |
| } |
| |
| pos = 0; |
| do { |
| readret = vfs_read(fp, pread, ADSP_CHECK_SIZE, &pos); |
| |
| if (memcmp(pread, pddr, readret)) { |
| printk(KERN_INFO "adsp check ddr fail in 0x%llx, try %d more times\n", pos, ADSP_CHECK_COUNT - i - 1); |
| b_check_fail = 1; |
| break; |
| } |
| |
| pddr += readret; |
| }while (readret == ADSP_CHECK_SIZE); |
| |
| if (pread) { |
| vfree(pread); |
| } |
| #endif |
| |
| filp_close(fp, NULL); |
| set_fs(fs); |
| |
| #if ADSP_CHECK_IMAGE |
| if (!b_check_fail) { |
| printk(KERN_INFO "adsp check ddr: pass\n"); |
| break; |
| } |
| |
| msleep(3000); |
| } |
| |
| if (b_check_fail) { |
| printk(KERN_INFO "adsp check ddr fail for %d times, the config of is_dump_full_ddr is %d, now forced to 1\n", i, is_dump_full_ddr); |
| is_dump_full_ddr = 1; |
| audio_request_cp_down(); |
| return -4; |
| } |
| #endif |
| |
| pwr_on_hw(); |
| is_adsp_inited = 1; |
| |
| #if ENABLE_ADSP_PM |
| msleep(1000); |
| pwr_off_hw(); |
| #endif |
| |
| return 0; |
| } |
| |
| static int check_adsp_ddr(void) |
| { |
| struct file *fp; |
| mm_segment_t fs; |
| loff_t pos; |
| ssize_t readret; |
| unsigned char *pread; |
| unsigned char *pddr; |
| |
| pread = is_1901_E ? __va(ADSP_DDR_LOAD_ADDR) : __va(ADSP_DDR_LOAD_ADDR_S); |
| printk(KERN_INFO "%s/%d: 0x%x\n", __FUNCTION__, __LINE__, (unsigned int)pread); |
| |
| fp = filp_open(ADSP_BIN_NAME_1, O_RDONLY, 0); |
| if (IS_ERR(fp)) { |
| if (is_1901_E) { |
| fp = filp_open(ADSP_BIN_NAME_E, O_RDONLY, 0); |
| } |
| else { |
| fp = filp_open(ADSP_BIN_NAME_S, O_RDONLY, 0); |
| } |
| if (IS_ERR(fp)) { |
| printk(KERN_INFO "%s/%d: warning: open adsp bin fail, but can be ignored.\n", __FUNCTION__, __LINE__); |
| return -2; |
| } |
| printk(KERN_INFO "%s/%d: check %s\n", __FUNCTION__, __LINE__, is_1901_E ? ADSP_BIN_NAME_E : ADSP_BIN_NAME_S); |
| } |
| else { |
| printk(KERN_INFO "%s/%d: check %s\n", __FUNCTION__, __LINE__, ADSP_BIN_NAME_1); |
| } |
| |
| pddr = is_1901_E ? __va(ADSP_DDR_LOAD_ADDR) : __va(ADSP_DDR_LOAD_ADDR_S); |
| |
| pread = (char *)vmalloc(ADSP_CHECK_SIZE); |
| if (!pread) { |
| printk(KERN_INFO "%s/%d: vmalloc for adsp check ddr fail\n", __FUNCTION__, __LINE__); |
| filp_close(fp, NULL); |
| return -3; |
| } |
| |
| fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| pos = 0; |
| do { |
| readret = vfs_read(fp, pread, ADSP_CHECK_SIZE, &pos); |
| |
| if (memcmp(pread, pddr, readret)) { |
| printk(KERN_INFO "adsp check ddr fail in 0x%llx, the config of is_dump_full_ddr is %d, now forced to 1\n", pos, is_dump_full_ddr); |
| is_dump_full_ddr = 1; |
| //audio_request_cp_down(); //here cp already to be down |
| if (pread) { |
| vfree(pread); |
| } |
| filp_close(fp, NULL); |
| set_fs(fs); |
| return -4; |
| } |
| |
| pddr += readret; |
| }while (readret == ADSP_CHECK_SIZE); |
| |
| if (pread) { |
| vfree(pread); |
| } |
| |
| filp_close(fp, NULL); |
| set_fs(fs); |
| |
| printk(KERN_INFO "%s/%d: pass, the config of is_dump_full_ddr is %d\n", __FUNCTION__, __LINE__, is_dump_full_ddr); |
| |
| return 0; |
| } |
| |
| static void ap_reset_adsp(void) |
| { |
| reg_write(rClk, reg_read(rClk) & ~8); //put bx2 in reset |
| |
| //is_adsp_inited = 1; |
| printk(KERN_INFO "%s/%d Done.\n", __FUNCTION__, __LINE__); |
| } |
| |
| |
| /****************************************************************************** |
| * functions for task |
| ******************************************************************************/ |
| #define CONFIG_ASR1901S_MEM_SIZE (256 * 1024 * 1024) |
| static struct task_struct *adsp_taskref = NULL; |
| extern bool cpu_is_asr190Xe(void); |
| |
| static int boot_adsp_task(void *data) |
| { |
| //msleep(10000); //delay 10 seconds |
| printk(KERN_INFO "%s/%d: platform is 1901%c\n", __FUNCTION__, __LINE__, is_1901_E ? 'E' : 'S'); |
| |
| if (ap_boot_adsp()) { |
| //enable_pmu_audio_clk(); |
| return 1; |
| } |
| return 0; |
| } |
| |
| void start_boot_adsp(void) |
| { |
| if (cpu_is_asr190Xe()) { |
| is_1901_E = 1; |
| } |
| if ((cpu_is_asr1901_a0_plus() || cpu_is_asr1906()) && adsp_taskref == NULL) { |
| adsp_taskref = (struct task_struct *)kthread_run(boot_adsp_task, NULL, "boot_adsp"); |
| } |
| } |
| |
| EXPORT_SYMBOL_GPL(start_boot_adsp); |
| EXPORT_SYMBOL_GPL(enable_pmu_audio_clk); |
| |
| |
| /****************************************************************************** |
| * functions for task |
| ******************************************************************************/ |
| #define MFPR_SSPA_VALUE 0x1042 |
| #define MFPR_SSPA_SET_COUNT 10 |
| |
| void audio_enable_mfpr_sspa(void) |
| { |
| volatile unsigned int rdata; |
| int i; |
| |
| for (i = 0; i < MFPR_SSPA_SET_COUNT; i++) { |
| reg_write(rSspaClk, MFPR_SSPA_VALUE); |
| rdata = reg_read(rSspaClk); |
| if (MFPR_SSPA_VALUE == rdata) { |
| break; |
| } |
| msleep(300); |
| } |
| |
| for (i = 0; i < MFPR_SSPA_SET_COUNT; i++) { |
| reg_write(rSspaFrm, MFPR_SSPA_VALUE); |
| rdata = reg_read(rSspaFrm); |
| if (MFPR_SSPA_VALUE == rdata) { |
| break; |
| } |
| msleep(300); |
| } |
| |
| for (i = 0; i < MFPR_SSPA_SET_COUNT; i++) { |
| reg_write(rSspaTxd, MFPR_SSPA_VALUE); |
| rdata = reg_read(rSspaTxd); |
| if (MFPR_SSPA_VALUE == rdata) { |
| break; |
| } |
| msleep(300); |
| } |
| |
| for (i = 0; i < MFPR_SSPA_SET_COUNT; i++) { |
| reg_write(rSspaRxd, MFPR_SSPA_VALUE); |
| rdata = reg_read(rSspaRxd); |
| if (MFPR_SSPA_VALUE == rdata) { |
| break; |
| } |
| msleep(300); |
| } |
| } |
| |
| EXPORT_SYMBOL_GPL(audio_enable_mfpr_sspa); |
| |
| |
| /****************************************************************************** |
| * functions for dump |
| ******************************************************************************/ |
| #define ADSP_DUMP_DDR_NAME "/data/adsp_ddr.dat" |
| #define ADSP_DUMP_SQU_NAME "/data/adsp_squ.dat" |
| #define ADSP_DUMP_DTCM_NAME "/data/adsp_dtcm.dat" |
| #define ADSP_DUMP_SHM_NAME "/data/adsp_shm.dat" |
| #define ADSP_WRITE_SIZE (0x4000) |
| |
| static void dump_memory(unsigned int type) |
| { |
| struct file *fp; |
| mm_segment_t fs; |
| loff_t pos; |
| ssize_t ret; |
| unsigned char *pWrite; |
| unsigned int length; |
| int i; |
| |
| switch (type) { |
| case 1: |
| printk(KERN_INFO "dump adsp ddr memory to %s\n", ADSP_DUMP_DDR_NAME); |
| if (is_1901_E) { |
| dma_sync_single_for_device(NULL, ADSP_DDR_LOAD_ADDR, ADSP_DDR_LOAD_LENGTH, DMA_FROM_DEVICE); |
| pWrite = (unsigned char *)__va(ADSP_DDR_LOAD_ADDR); |
| length = ADSP_DDR_LOAD_LENGTH; |
| } |
| else { |
| dma_sync_single_for_device(NULL, ADSP_DDR_LOAD_ADDR_S, ADSP_DDR_LOAD_LENGTH_S, DMA_FROM_DEVICE); |
| pWrite = (unsigned char *)__va(ADSP_DDR_LOAD_ADDR_S); |
| length = ADSP_DDR_LOAD_LENGTH_S; |
| } |
| fp = filp_open(ADSP_DUMP_DDR_NAME, O_RDWR | O_CREAT | O_SYNC, 0644); |
| break; |
| |
| case 2: |
| printk(KERN_INFO "dump adsp squ memory to %s\n", ADSP_DUMP_SQU_NAME); |
| fp = filp_open(ADSP_DUMP_SQU_NAME, O_RDWR | O_CREAT | O_SYNC, 0644); |
| pWrite = (unsigned char *)rDumpSQU; |
| length = ADSP_DUMP_SQU_LENGTH; |
| break; |
| |
| case 3: |
| printk(KERN_INFO "dump adsp dtcm memory to %s\n", ADSP_DUMP_DTCM_NAME); |
| #if ENABLE_ADSP_PM |
| pwr_on_hw(); |
| #endif |
| fp = filp_open(ADSP_DUMP_DTCM_NAME, O_RDWR | O_CREAT | O_SYNC, 0644); |
| pWrite = (unsigned char *)rDumpDTCM; |
| length = ADSP_DUMP_DTCM_LENGTH; |
| break; |
| |
| case 4: |
| printk(KERN_INFO "dump adsp shm memory to %s\n", ADSP_DUMP_SHM_NAME); |
| #if ENABLE_ADSP_PM |
| pwr_on_hw(); |
| #endif |
| fp = filp_open(ADSP_DUMP_SHM_NAME, O_RDWR | O_CREAT | O_SYNC, 0644); |
| pWrite = (unsigned char *)rDumpSHM; |
| length = ADSP_DUMP_SHM_LENGTH; |
| break; |
| |
| default: |
| printk(KERN_INFO "%s/%d: type invalid\n", __FUNCTION__, __LINE__); |
| break; |
| } |
| |
| if (IS_ERR(fp)) { |
| printk(KERN_INFO "%s/%d: open adsp dump file fail\n", __FUNCTION__, __LINE__); |
| return; |
| } |
| |
| if (!pWrite) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| filp_close(fp, NULL); |
| return; |
| } |
| |
| fs = get_fs(); |
| set_fs(KERNEL_DS); |
| |
| //pos = 0; |
| for (i = 0; i < length / ADSP_WRITE_SIZE; i++) { |
| pos = fp->f_pos; |
| ret = vfs_write(fp, pWrite, ADSP_WRITE_SIZE, &pos); |
| //printk(KERN_INFO "write: 0x%x|%x|%x|%x| write=0x%x pos=0x%llx \n", pWrite[0], pWrite[1], pWrite[2], pWrite[3], ret, pos); |
| fp->f_pos = pos; |
| pWrite += ret; |
| } |
| |
| printk(KERN_INFO "dump adsp size: 0x%llx\n", pos); |
| |
| set_fs(fs); |
| filp_close(fp, NULL); |
| } |
| |
| static struct task_struct *adsp_exception_taskref = NULL; |
| |
| static int adsp_exception_task(void *data) |
| { |
| msleep(300); //300 ms, in case CP does not receive the ADSP exception report, which can trigger CP assert |
| ap_reset_adsp(); |
| check_adsp_ddr(); |
| |
| if (!is_dump_full_ddr) { |
| if (is_1901_E) { |
| memcpy(__va(ADSP_DDR_LOAD_ADDR), rDumpDTCM, ADSP_DUMP_DTCM_LENGTH); |
| memcpy(__va(ADSP_DDR_LOAD_ADDR) + ADSP_DUMP_DTCM_LENGTH, rDumpSHM, ADSP_DUMP_SHM_LENGTH); |
| } |
| else { |
| memcpy(__va(ADSP_DDR_LOAD_ADDR_S), rDumpDTCM, ADSP_DUMP_DTCM_LENGTH); |
| memcpy(__va(ADSP_DDR_LOAD_ADDR_S) + ADSP_DUMP_DTCM_LENGTH, rDumpSHM, ADSP_DUMP_SHM_LENGTH); |
| } |
| } |
| |
| #if 0 //use ramp dump instead of write dump files to flash |
| dump_memory(1); |
| dump_memory(2); |
| dump_memory(3); |
| dump_memory(4); |
| #endif |
| |
| printk(KERN_INFO "%s/%d: ADSP Version: %s\n", __FUNCTION__, __LINE__, sAdspVer); |
| |
| return 0; |
| } |
| |
| void handle_adsp_exception(void) |
| { |
| if ((cpu_is_asr1901_a0_plus() || cpu_is_asr1906()) && adsp_exception_taskref == NULL) { |
| adsp_exception_taskref = (struct task_struct *)kthread_run(adsp_exception_task, NULL, "adsp_exception"); |
| } |
| } |
| |
| EXPORT_SYMBOL_GPL(handle_adsp_exception); |
| |
| |
| /****************************************************************************** |
| * functions for debugfs |
| ******************************************************************************/ |
| #ifdef CONFIG_DEBUG_FS |
| static void *adsp_map_ddr; |
| static u32 adsp_map_len; |
| |
| static ssize_t adsp_rdp_squ_read(struct file *file, char __user *user_buf, |
| size_t size, loff_t *ppos) |
| { |
| int ret; |
| |
| ret = simple_read_from_buffer(user_buf, size, ppos, |
| rDumpSQU, |
| ADSP_DUMP_SQU_LENGTH); |
| return ret; |
| } |
| static const struct file_operations adsp_rdp_squ_fops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = adsp_rdp_squ_read, |
| }; |
| |
| static ssize_t adsp_rdp_dtcm_read(struct file *file, char __user *user_buf, |
| size_t size, loff_t *ppos) |
| { |
| int ret; |
| |
| ret = simple_read_from_buffer(user_buf, size, ppos, |
| adsp_map_ddr, |
| ADSP_DUMP_DTCM_LENGTH); |
| return ret; |
| } |
| static const struct file_operations adsp_rdp_dtcm_fops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = adsp_rdp_dtcm_read, |
| }; |
| |
| static ssize_t adsp_rdp_shm_read(struct file *file, char __user *user_buf, |
| size_t size, loff_t *ppos) |
| { |
| int ret; |
| |
| ret = simple_read_from_buffer(user_buf, size, ppos, |
| adsp_map_ddr + ADSP_DUMP_DTCM_LENGTH, |
| ADSP_DUMP_SHM_LENGTH); |
| return ret; |
| } |
| |
| static const struct file_operations adsp_rdp_shm_fops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = adsp_rdp_shm_read, |
| }; |
| |
| static ssize_t adsp_rdp_ddr_read(struct file *file, char __user *user_buf, |
| size_t size, loff_t *ppos) |
| { |
| int ret; |
| |
| ret = simple_read_from_buffer(user_buf, size, ppos, |
| adsp_map_ddr, |
| adsp_map_len); |
| return ret; |
| } |
| static const struct file_operations adsp_rdp_ddr_fops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = adsp_rdp_ddr_read, |
| }; |
| |
| #endif |
| |
| static struct dentry *adsp_control = NULL; |
| |
| static ssize_t debugfs_read(struct file *file, char __user *user_buf, |
| size_t count, loff_t *ppos) |
| { |
| printk(KERN_INFO "%s/%d.\n", __FUNCTION__, __LINE__); |
| return 0; |
| } |
| |
| static ssize_t debugfs_write(struct file *file, |
| const char __user *user_buf, |
| size_t count, loff_t *ppos) |
| { |
| char msg[10]; |
| int ret = 0; |
| size_t tmp_count = 0; |
| |
| memset(msg, 0x00, sizeof(msg)); |
| tmp_count = count; |
| |
| if (tmp_count >= sizeof(msg)) { |
| tmp_count = sizeof(msg) - 1; |
| } |
| |
| /* copy the content from user space to kernel space */ |
| ret = copy_from_user(msg, user_buf, tmp_count); |
| if (ret) { |
| printk(KERN_ALERT "copy from user fail \n"); |
| return -EFAULT; |
| } |
| |
| switch (msg[0]) { |
| case '1': /* input command# echo 1 > /sys/kernel/debug/adsp */ |
| printk(KERN_INFO "input %c to dump adsp DDR memory to %s\n", msg[0], ADSP_DUMP_DDR_NAME); |
| dump_memory(1); |
| break; |
| |
| case '2': /* input command# echo 2 > /sys/kernel/debug/adsp */ |
| printk(KERN_INFO "input %c to dump adsp SQU memory to %s\n", msg[0], ADSP_DUMP_SQU_NAME); |
| dump_memory(2); |
| break; |
| |
| case '3': /* input command# echo 3 > /sys/kernel/debug/adsp */ |
| printk(KERN_INFO "input %c to dump adsp DTCM memory to %s\n", msg[0], ADSP_DUMP_DTCM_NAME); |
| dump_memory(3); |
| break; |
| |
| case '4': /* input command# echo 4 > /sys/kernel/debug/adsp */ |
| printk(KERN_INFO "input %c to dump adsp share memory to %s\n", msg[0], ADSP_DUMP_SHM_NAME); |
| dump_memory(4); |
| break; |
| |
| case '5': /* input command# echo 5 > /sys/kernel/debug/adsp */ |
| printk(KERN_INFO "input %c to dump all the adsp memory\n", msg[0]); |
| dump_memory(1); |
| dump_memory(2); |
| dump_memory(3); |
| dump_memory(4); |
| break; |
| |
| case '9': /* input command# echo 9 > /sys/kernel/debug/adsp */ |
| printk(KERN_INFO "input %c to power on adsp\n", msg[0]); |
| pwr_on_hw(); |
| break; |
| |
| case '0': /* input command# echo 0 > /sys/kernel/debug/adsp */ |
| printk(KERN_INFO "input %c to power off adsp\n", msg[0]); |
| pwr_off_hw(); |
| break; |
| |
| case 'h': /* input command# echo ? > /sys/kernel/debug/adsp */ |
| printk(KERN_INFO "\nUsage: echo <option> > /sys/kernel/debug/adsp\n"); |
| printk(KERN_INFO " 1 dump adsp DDR memory to %s\n", ADSP_DUMP_DDR_NAME); |
| printk(KERN_INFO " 2 dump adsp SQU memory to %s\n", ADSP_DUMP_SHM_NAME); |
| printk(KERN_INFO " 3 dump adsp DTCM memory to %s\n", ADSP_DUMP_DTCM_NAME); |
| printk(KERN_INFO " 4 dump adsp share memory to %s\n", ADSP_DUMP_SHM_NAME); |
| printk(KERN_INFO " 5 dump all the adsp memory\n"); |
| printk(KERN_INFO " 9 power on adsp\n"); |
| printk(KERN_INFO " 0 power off adsp\n"); |
| printk(KERN_INFO " h show help\n"); |
| break; |
| |
| default: /* input command# */ |
| printk(KERN_INFO "input invalid.\n"); |
| break; |
| } |
| |
| return tmp_count; |
| } |
| |
| static const struct file_operations debugfs_ops = { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .read = debugfs_read, |
| .write = debugfs_write, |
| }; |
| |
| extern bool system_is_rdp_mode(void); |
| extern void *shm_map(phys_addr_t start, size_t size); |
| static inline int adsp_debugfs_init(void) |
| { |
| if (cpu_is_asr190Xe()) { |
| is_1901_E = 1; |
| } |
| printk(KERN_INFO "%s/%d: platform is 1901%c\n", __FUNCTION__, __LINE__, is_1901_E ? 'E' : 'S'); |
| |
| if (system_is_rdp_mode()) { |
| #ifdef CONFIG_DEBUG_FS |
| struct dentry *adsp_dir = NULL; |
| struct dentry *ads_buffer; |
| |
| if (is_1901_E) { |
| adsp_map_ddr = shm_map(ADSP_DDR_LOAD_ADDR, ADSP_DDR_LOAD_LENGTH); |
| adsp_map_len = ADSP_DDR_LOAD_LENGTH; |
| } |
| else { |
| adsp_map_ddr = shm_map(ADSP_DDR_LOAD_ADDR_S, ADSP_DDR_LOAD_LENGTH_S); |
| adsp_map_len = ADSP_DDR_LOAD_LENGTH_S; |
| } |
| |
| adsp_dir = debugfs_create_dir("adsp", NULL); |
| if (!adsp_dir || IS_ERR(adsp_dir)) { |
| pr_err("adsp debugfs create directory failed\n"); |
| return -ENOENT; |
| } |
| |
| ads_buffer = debugfs_create_file("adsp_squ", 0664, adsp_dir, NULL, &adsp_rdp_squ_fops); |
| if (!ads_buffer) { |
| pr_err("create adsp_squ debugfs error!\n"); |
| return -ENOENT; |
| } |
| |
| ads_buffer = debugfs_create_file("adsp_dtcm", 0664, adsp_dir, NULL, &adsp_rdp_dtcm_fops); |
| if (!ads_buffer) { |
| pr_err("create adsp_dtcm debugfs error!\n"); |
| return -ENOENT; |
| } |
| |
| ads_buffer = debugfs_create_file("adsp_shm", 0664, adsp_dir, NULL, &adsp_rdp_shm_fops); |
| if (!ads_buffer) { |
| pr_err("create adsp_shm debugfs error!\n"); |
| return -ENOENT; |
| } |
| |
| ads_buffer = debugfs_create_file("adsp_ddr", 0664, adsp_dir, NULL, &adsp_rdp_ddr_fops); |
| if (!ads_buffer) { |
| pr_err("create adsp_ddr debugfs error!\n"); |
| return -ENOENT; |
| } |
| #endif |
| return 0; |
| } |
| |
| adsp_control = debugfs_create_file("adsp", S_IRUGO | S_IFREG, NULL, NULL, &debugfs_ops); |
| if (adsp_control == NULL) { |
| pr_err("create adsp debugfs error!\n"); |
| return -ENOENT; |
| } |
| else if (adsp_control == ERR_PTR(-ENODEV)) { |
| pr_err("CONFIG_DEBUG_FS is not enabled!\n"); |
| return -ENOENT; |
| } |
| |
| return 0; |
| } |
| |
| static void adsp_debugfs_remove(void) |
| { |
| if (NULL != adsp_control) { |
| debugfs_remove_recursive(adsp_control); |
| } |
| |
| return; |
| } |
| |
| |
| /****************************************************************************** |
| * functions for cdev |
| ******************************************************************************/ |
| #define AUDIO_DSP_MAGIC 'y' |
| #define ADSP_ENABLE _IO(AUDIO_DSP_MAGIC, 0x00) |
| #define ADSP_DISABLE _IO(AUDIO_DSP_MAGIC, 0x01) |
| #define ADSP_SET _IOW(AUDIO_DSP_MAGIC, 0x02, uint16_t[ADSP_SET_COMMAND_LENGTH]) |
| #define ADSP_SEND_VE _IOW(AUDIO_DSP_MAGIC, 0x03, EnhanceParmsT) |
| #define ADSP_DUMP_FULL_DDR _IOR(AUDIO_DSP_MAGIC, 0x4, uint32_t) |
| #define ADSP_START_AUDIO_PATH _IOW(AUDIO_DSP_MAGIC, 0x05, uint16_t[VOICE_START_COMMAND_LENGTH + 2]) |
| #define ADSP_END_AUDIO_PATH _IOW(AUDIO_DSP_MAGIC, 0x06, uint16_t[4]) |
| |
| #define CTRL_BUFF_MAX_LEN 100 |
| |
| static dev_t adsp_dev = 0; |
| static struct class *adsp_class; |
| static struct cdev adsp_cdev; |
| |
| |
| static long cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| { |
| u16 msg[CTRL_BUFF_MAX_LEN]; |
| EnhanceParmsT *pVe; |
| IpcSetIDType SetId = 0; |
| IpcSetParamsType CmdParams; |
| IpcDataParamsType DataParams; |
| UINT8 CmdMsg[128]; |
| |
| //printk(KERN_INFO "%s/%d, cmd = %d", __FUNCTION__, __LINE__, cmd); |
| |
| memset(&CmdParams, 0, sizeof(IpcSetParamsType)); |
| memset(&DataParams, 0, sizeof(IpcDataParamsType)); |
| |
| switch (cmd) { |
| case ADSP_ENABLE: |
| printk(KERN_INFO "%s/%d: enable adsp\n", __FUNCTION__, __LINE__); |
| break; |
| |
| case ADSP_DISABLE: |
| printk(KERN_INFO "%s/%d: disable adsp\n", __FUNCTION__, __LINE__); |
| break; |
| |
| case ADSP_SET: |
| //printk(KERN_INFO "send cmd to adsp\n"); |
| if (!is_adsp_inited) { |
| printk(KERN_INFO "%s/%d: adsp is not ready\n", __FUNCTION__, __LINE__); |
| return -EFAULT; |
| } |
| if (copy_from_user(msg, (void __user *)arg, ADSP_SET_COMMAND_LENGTH * 2)) { |
| return -EFAULT; |
| } |
| printk(KERN_INFO "|%.2x %.2x %.2x|%.4x %.4x|%.4x %.4x|%.4x %.4x|%.4x %.4x|\n", |
| msg[0], msg[1], msg[2], |
| msg[3], msg[4], msg[5], msg[6], |
| msg[7], msg[8], msg[9], msg[10]); |
| SetId = msg[0]; |
| CmdParams.SubOpcode = msg[1]; |
| CmdParams.CmdMsgLength = msg[2]; |
| if (CmdParams.CmdMsgLength < ADSP_SET_COMMAND_LENGTH * 2 - 3) { |
| memcpy(CmdMsg, &msg[3], CmdParams.CmdMsgLength * 2); |
| CmdParams.CmdMsgPtr = (UINT32 *)CmdMsg; |
| printk(KERN_INFO "SetId:0x%x, SubOpcode:0x%x, CmdMsgLength:0x%x, CmdMsg:|%.8x|%.8x|%.8x|%.8x|\n", |
| SetId, CmdParams.SubOpcode, CmdParams.CmdMsgLength, |
| CmdParams.CmdMsgPtr[0], CmdParams.CmdMsgPtr[1], CmdParams.CmdMsgPtr[2], CmdParams.CmdMsgPtr[3]); |
| IpcSendAdspMessageData(SetId, &CmdParams, NULL); |
| } |
| break; |
| |
| case ADSP_SEND_VE: |
| printk(KERN_INFO "prepare ve for adsp\n"); |
| //AP ring_ctl addr is agreed in advance, and ve should be prepared before release adsp |
| if (!rVe) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| return -EFAULT; |
| } |
| if (copy_from_user(rVe, (void __user *)arg, sizeof(EnhanceParmsT))) { |
| return -EFAULT; |
| } |
| pVe = rVe; |
| printk(KERN_INFO "%x|%x\n", |
| pVe->VoiceParms.VoiceParmRX[1].NSRxConfig.NsParams.NoiseMatrix, |
| pVe->VoiceParms.VoiceParmTX[0].NsTxConfig.NsParamsTx.NsParams.NoiseMatrix); |
| break; |
| |
| case ADSP_DUMP_FULL_DDR: |
| printk(KERN_INFO "configure whether to dump full ddr\n"); |
| if (!is_adsp_inited) { |
| printk(KERN_INFO "%s/%d: adsp is not ready\n", __FUNCTION__, __LINE__); |
| return -EFAULT; |
| } |
| if (copy_from_user(&is_dump_full_ddr, (void __user *)arg, sizeof(int))) { |
| return -EFAULT; |
| } |
| printk(KERN_INFO "is_dump_full_ddr = %x\n", is_dump_full_ddr); |
| break; |
| |
| case ADSP_START_AUDIO_PATH: |
| printk(KERN_INFO "ADSP_START_VOICE_PATH\n"); |
| if (!is_adsp_inited) { |
| printk(KERN_INFO "%s/%d: adsp is not ready\n", __FUNCTION__, __LINE__); |
| return -EFAULT; |
| } |
| if (copy_from_user(msg, (void __user *)arg, (2 + VOICE_START_COMMAND_LENGTH) * 2)) { |
| return -EFAULT; |
| } |
| //printk(KERN_INFO "0x%x|%x|%x|%x|%x|%x|%x|%x|%x|\n", msg[0], msg[1], msg[2], msg[3], msg[4], msg[5], msg[6], msg[7], msg[8]); |
| SetId = 1; |
| |
| CmdParams.SubOpcode = 0x05; |
| CmdParams.CmdMsgLength = 4; |
| memset(CmdMsg, 0, sizeof(CmdMsg)); |
| CmdMsg[0] = 0x06; |
| CmdParams.CmdMsgPtr = (UINT32 *)CmdMsg; |
| printk(KERN_INFO "SubOpcode:0x%x, CmdMsgLength:0x%x, CmdMsg:|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|\n", \ |
| CmdParams.SubOpcode, CmdParams.CmdMsgLength, \ |
| CmdMsg[0], CmdMsg[1], CmdMsg[2], CmdMsg[3], CmdMsg[4], CmdMsg[5], CmdMsg[6], CmdMsg[7]); |
| IpcSendAdspMessageData(SetId, &CmdParams, NULL); |
| |
| CmdParams.SubOpcode = 0x09; |
| CmdParams.CmdMsgLength = 2; |
| memset(CmdMsg, 0, sizeof(CmdMsg)); |
| CmdMsg[0] = 0x01; |
| CmdParams.CmdMsgPtr = (UINT32 *)CmdMsg; |
| printk(KERN_INFO "SubOpcode:0x%x, CmdMsgLength:0x%x, CmdMsg:|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|\n", \ |
| CmdParams.SubOpcode, CmdParams.CmdMsgLength, \ |
| CmdMsg[0], CmdMsg[1], CmdMsg[2], CmdMsg[3], CmdMsg[4], CmdMsg[5], CmdMsg[6], CmdMsg[7]); |
| IpcSendAdspMessageData(SetId, &CmdParams, NULL); |
| |
| CmdParams.SubOpcode = 0x06; |
| CmdParams.CmdMsgLength = 2; |
| memset(CmdMsg, 0, sizeof(CmdMsg)); |
| CmdMsg[0] = 0x01; |
| CmdMsg[1] = 0x01; |
| CmdParams.CmdMsgPtr = (UINT32 *)CmdMsg; |
| printk(KERN_INFO "SubOpcode:0x%x, CmdMsgLength:0x%x, CmdMsg:|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|\n", \ |
| CmdParams.SubOpcode, CmdParams.CmdMsgLength, \ |
| CmdMsg[0], CmdMsg[1], CmdMsg[2], CmdMsg[3], CmdMsg[4], CmdMsg[5], CmdMsg[6], CmdMsg[7]); |
| IpcSendAdspMessageData(SetId, &CmdParams, NULL); |
| |
| CmdParams.SubOpcode = 0x00; |
| CmdParams.CmdMsgLength = 0x0C; |
| memset(CmdMsg, 0, sizeof(CmdMsg)); |
| CmdMsg[0] = 0x06; |
| CmdMsg[2] = 0x06; |
| CmdParams.CmdMsgPtr = (UINT32 *)CmdMsg; |
| printk(KERN_INFO "SubOpcode:0x%x, CmdMsgLength:0x%x, CmdMsg:|%.2x %.2x %.2x %.2x|%.2x %.2x %.2x %.2x|\n", \ |
| CmdParams.SubOpcode, CmdParams.CmdMsgLength, \ |
| CmdMsg[0], CmdMsg[1], CmdMsg[2], CmdMsg[3], CmdMsg[4], CmdMsg[5], CmdMsg[6], CmdMsg[7]); |
| IpcSendAdspMessageData(SetId, &CmdParams, NULL); |
| break; |
| |
| case ADSP_END_AUDIO_PATH: |
| if (!is_adsp_inited) { |
| printk(KERN_INFO "%s/%d: adsp is not ready\n", __FUNCTION__, __LINE__); |
| return -EFAULT; |
| } |
| printk(KERN_INFO "ADSP_END_AUDIO_PATH\n"); |
| if (copy_from_user(msg, (void __user *)arg, 4 * 2)) { |
| return -EFAULT; |
| } |
| printk(KERN_INFO "0x%x|%x|%x|%x|\n", msg[0], msg[1], msg[2], msg[3]); |
| CmdParams.SubOpcode = msg[0]; |
| CmdParams.CmdMsgLength = msg[1]; |
| memcpy(CmdMsg, &msg[2], CmdParams.CmdMsgLength * 2); |
| CmdParams.CmdMsgPtr = (UINT32 *)CmdMsg; |
| printk(KERN_INFO "SubOpcode:0x%x, CmdMsgLength:0x%x, CmdMsg:|%.2x %.2x|\n", CmdParams.SubOpcode, CmdParams.CmdMsgLength, CmdParams.CmdMsgPtr[0], CmdParams.CmdMsgPtr[1]); |
| IpcSendAdspMessageData(SetId, &CmdParams, NULL); |
| break; |
| |
| default: |
| pr_info("Default\n"); |
| break; |
| } |
| return 0; |
| } |
| |
| static struct file_operations cdev_fops = |
| { |
| .owner = THIS_MODULE, |
| .open = simple_open, |
| .unlocked_ioctl = cdev_ioctl, |
| }; |
| |
| static int adsp_cdev_init(void) |
| { |
| /*Allocating Major number*/ |
| if ((alloc_chrdev_region(&adsp_dev, 0, 1, "adsp_Dev")) < 0) { |
| pr_err("Cannot allocate major number\n"); |
| return -1; |
| } |
| printk(KERN_INFO "%s/%d, Major = %d, Minor = %d\n", __FUNCTION__, __LINE__, MAJOR(adsp_dev), MINOR(adsp_dev)); |
| |
| /*Creating cdev structure*/ |
| cdev_init(&adsp_cdev, &cdev_fops); |
| |
| /*Adding character device to the system*/ |
| if ((cdev_add(&adsp_cdev, adsp_dev, 1)) < 0) { |
| pr_err("Cannot add the device to the system\n"); |
| unregister_chrdev_region(adsp_dev, 1); |
| return -1; |
| } |
| |
| /*Creating struct class*/ |
| if ((adsp_class = class_create(THIS_MODULE, "adsp_class")) == NULL) { |
| pr_err("Cannot create the struct class\n"); |
| unregister_chrdev_region(adsp_dev, 1); |
| return -2; |
| } |
| |
| /*Creating device*/ |
| if ((device_create(adsp_class, NULL, adsp_dev, NULL, "audio_dsp")) == NULL) { |
| pr_err("Cannot create the Device 1\n"); |
| class_destroy(adsp_class); |
| unregister_chrdev_region(adsp_dev, 1); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static void adsp_cdev_exit(void) |
| { |
| device_destroy(adsp_class, adsp_dev); |
| class_destroy(adsp_class); |
| cdev_del(&adsp_cdev); |
| unregister_chrdev_region(adsp_dev, 1); |
| printk(KERN_INFO "%s/%d Done.\n", __FUNCTION__, __LINE__); |
| } |
| |
| |
| /****************************************************************************** |
| * functions for platform_device |
| ******************************************************************************/ |
| static int adsp_plat_probe(struct platform_device *pdev) |
| { |
| struct resource *res = NULL; |
| |
| if (NULL == pdev) { |
| printk(KERN_INFO "%s/%d: pdev is NULL.\n", __FUNCTION__, __LINE__); |
| return -ENOENT; |
| } |
| |
| //MEM for dump SQU |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rDumpSQU = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rDumpSQU) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| |
| //MEM for dump DTCM |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rDumpDTCM = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rDumpDTCM) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| |
| //MEM for share memory |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 2); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rDumpSHM = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rDumpSHM) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| rRingCtrl = rDumpSHM + 0x1000; |
| rVe = rDumpSHM + 0x1880; |
| |
| if (!system_is_rdp_mode()) { |
| //IRQ for IPC |
| adsp_irq = platform_get_irq(pdev, 0); |
| if (!adsp_irq) { |
| printk(KERN_INFO "%s/%d: platform_get_irq fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| printk(KERN_INFO "%s/%d irq=0x%x.\n", __FUNCTION__, __LINE__, adsp_irq); |
| |
| //MEM for ITCM |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 3); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rItcm = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rItcm) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| |
| //Register for IPC |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 4); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rIpcBASE = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rIpcBASE) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| rIpcDWR = rIpcBASE; |
| rIpcWDR = rIpcBASE + 0x4; |
| rIpcISRW = rIpcBASE + 0x8; |
| rIpcICR = rIpcBASE + 0xc; |
| rIpcIIR = rIpcBASE + 0x10; |
| rIpcRDR = rIpcBASE + 0x14; |
| |
| //Register for SSPA |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 5); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rSspaBase = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rSspaBase) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| rSspaClk = rSspaBase; |
| rSspaFrm = rSspaBase + 0x4; |
| rSspaTxd = rSspaBase + 0x8; |
| rSspaRxd = rSspaBase + 0xc; |
| |
| //Register for BX2 |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 6); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rPc = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rPc) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 7); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rRls = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rRls) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 8); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rClkRst = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rClkRst) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 9); |
| if (!res) { |
| printk(KERN_INFO "%s/%d: platform_get_resource fail\n", __FUNCTION__, __LINE__); |
| return -ENXIO; |
| } |
| rPwr = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); |
| if (!rPwr) { |
| printk(KERN_INFO "%s/%d: devm_ioremap_resource fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| |
| if (!rClk) { |
| rClk = ioremap_nocache(REG_PMU_AUDIO_CLK_RES_CTRL, 4); |
| if (!rClk) { |
| printk(KERN_INFO "%s/%d: ioremap fail\n", __FUNCTION__, __LINE__); |
| return -EIO; |
| } |
| } |
| } |
| |
| printk(KERN_INFO "squ :[%x-%x]-->[%x-%x]\ndtcm:[%x-%x]-->[%x-%x]\nitcm:[%x-%x]-->[%x-%x]\nshm :[%x-%x]-->[%x-%x]\n", |
| ADSP_DUMP_SQU_ADDR, ADSP_DUMP_SQU_ADDR + ADSP_DUMP_SQU_LENGTH, (unsigned int)rDumpSQU, (unsigned int)rDumpSQU + ADSP_DUMP_SQU_LENGTH, |
| ADSP_DUMP_DTCM_ADDR, ADSP_DUMP_DTCM_ADDR + ADSP_DUMP_DTCM_LENGTH, (unsigned int)rDumpDTCM, (unsigned int)rDumpDTCM + ADSP_DUMP_DTCM_LENGTH, |
| ITCM_ADDR, ITCM_ADDR + ITCM_LENGTH, (unsigned int)rItcm, (unsigned int)rItcm + ITCM_LENGTH, |
| ADSP_DUMP_SHM_ADDR, ADSP_DUMP_SHM_ADDR + ADSP_DUMP_SHM_LENGTH, (unsigned int)rDumpSHM, (unsigned int)rDumpSHM + ADSP_DUMP_SHM_LENGTH); |
| |
| printk(KERN_INFO "%s/%d Done.\n", __FUNCTION__, __LINE__); |
| return 0; |
| } |
| |
| static int adsp_plat_remove(struct platform_device *pdev) |
| { |
| iounmap(rClk); |
| |
| printk(KERN_INFO "%s/%d.\n", __FUNCTION__, __LINE__); |
| return 0; |
| } |
| |
| static const struct of_device_id adsp_dt_ids[] = { |
| { .compatible = "asr,adsp", }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, adsp_dt_ids); |
| |
| static struct platform_driver adsp_driver = { |
| .driver = { |
| .name = "adsp", |
| .owner = THIS_MODULE, |
| .of_match_table = of_match_ptr(adsp_dt_ids), |
| }, |
| .probe = adsp_plat_probe, |
| .remove = adsp_plat_remove, |
| }; |
| |
| |
| /****************************************************************************** |
| * functions for module |
| ******************************************************************************/ |
| static int adspInit(void) |
| { |
| printk(KERN_INFO "%s/%d.\n", __FUNCTION__, __LINE__); |
| |
| if (cpu_is_asr1901_a0_plus() || cpu_is_asr1906()) { |
| platform_driver_register(&adsp_driver); |
| if (!system_is_rdp_mode()) { |
| adsp_cdev_init(); |
| } |
| adsp_debugfs_init(); |
| if (!system_is_rdp_mode()) { |
| adsp_ipc_init_phase1(); |
| enable_pmu_audio_clk(); |
| } |
| printk(KERN_INFO "%s/%d Done.\n", __FUNCTION__, __LINE__); |
| } |
| |
| return 0; |
| } |
| |
| static void adspUninit(void) |
| { |
| printk(KERN_INFO "%s/%d.\n", __FUNCTION__, __LINE__); |
| |
| if (cpu_is_asr1901_a0_plus() || cpu_is_asr1906()) { |
| if (g_audio_ipc_handle.rx_ring_buffer) { |
| iounmap(g_audio_ipc_handle.rx_ring_buffer); |
| } |
| |
| if (g_audio_ipc_handle.tx_ring_buffer) { |
| iounmap(g_audio_ipc_handle.tx_ring_buffer); |
| } |
| |
| if (adsp_irq) { |
| free_irq(adsp_irq, NULL); |
| } |
| |
| adsp_cdev_exit(); |
| adsp_debugfs_remove(); |
| } |
| } |
| |
| module_init(adspInit); |
| module_exit(adspUninit); |
| |
| MODULE_DESCRIPTION("Driver for ADSP"); |
| MODULE_AUTHOR("yjgwang@asrmicro.com"); |
| MODULE_LICENSE("GPL v2"); |