blob: c1859eb60a468cf807caba98d10d76bb9b8beeac [file] [log] [blame]
// 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");