mirror of
https://github.com/hardkernel/kernel_common_drivers.git
synced 2026-06-25 12:03:48 +09:00
397d2d6f23
PD#SWPL-121622 Problem: t3x bring up. Solution: t3x bring up. Verify: BC302/T968D4 Change-Id: I97c871f7b09d32061d41949ae286de5957802ae9 Signed-off-by: hongyu.chen <hongyu.chen@amlogic.com>
1455 lines
40 KiB
C
1455 lines
40 KiB
C
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
|
/*
|
|
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/err.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/module.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/list.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_fdt.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/of_reserved_mem.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/kfifo.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/sysfs.h>
|
|
|
|
#include <linux/amlogic/secmon.h>
|
|
|
|
#include "aml_aucpu.h"
|
|
|
|
#define AUCPU_PLATFORM_DEVICE_NAME "aml_aucpu"
|
|
#define AUCPU_DEV_NAME "aml_aucpu"
|
|
#define AUCPU_CLASS_NAME "aml_aucpu"
|
|
|
|
#ifndef VM_RESERVED /*for kernel up to 3.7.0 version*/
|
|
#define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP)
|
|
#endif
|
|
|
|
#define MHz (1000000)
|
|
|
|
#define AUCPU_MEMORY_SIZE_IN_BYTE (64 * SZ_1K)
|
|
#define AUCPU_UPD_SZ (32)
|
|
#define AUCPU_DBG_BUF_SZ (32 * SZ_1K)
|
|
#define AUCPU_DBG_PAD_SZ (256)
|
|
|
|
#define LOG_ALL 0
|
|
#define LOG_INFO 1
|
|
#define LOG_DEBUG 2
|
|
#define LOG_ERROR 3
|
|
|
|
#define AUCPU_LOG_EMERG 0 /* ERROR */
|
|
#define AUCPU_LOG_ALERT 1 /* ERROR */
|
|
#define AUCPU_LOG_CRIT 2 /* ERROR */
|
|
#define AUCPU_LOG_ERR 3 /* ERROR */
|
|
#define AUCPU_LOG_WARNING 4 /* WARN */
|
|
#define AUCPU_LOG_NOTICE 5 /* INFO */
|
|
#define AUCPU_LOG_INFO 6 /* INFO */
|
|
#define AUCPU_LOG_DEBUG 7 /* DEBUG */
|
|
|
|
#define DBG_POLL_TIME (HZ / 100)
|
|
|
|
#define aucpu_pr(level, x...) \
|
|
do { \
|
|
if ((level) >= print_level) \
|
|
printk(x); \
|
|
} while (0)
|
|
|
|
static s32 print_level = LOG_ERROR; //LOG_ALL;
|
|
static s32 dbg_level = AUCPU_LOG_ERR;
|
|
|
|
/* back up value for dts config failure */
|
|
#define AUCPU_REG_BASE_ADDR (0xFE09E080)
|
|
#define AUCPU_REG_SIZE (0x100)
|
|
|
|
#define CMD_SEQ_ADDR (0x0)
|
|
#define CMD_TYPE_ADDR (0x4)
|
|
#define CMD_OPT_ADDR (0x8)
|
|
#define CMD_SRC_ADDR (0xC)
|
|
#define CMD_SRC_SZ (0x10)
|
|
#define CMD_DST_ADDR (0x14)
|
|
#define CMD_DST_SZ (0x18)
|
|
#define CMD_RPT_ADDR (0x1C)
|
|
#define CMD_RPT_SZ (0x20)
|
|
#define CMD_UPD_ADDR (0x24)
|
|
|
|
#define RESP_SEQ_ADDR (0x80)
|
|
#define RESP_TYPE_ADDR (0x84)
|
|
#define RESP_RST_ADDR (0x88)
|
|
|
|
#define VERSION_ADDR (0xA8)
|
|
|
|
#define DBG_REG_WRPTR (0xC0)
|
|
#define DBG_REG_STAGE (0xE8)
|
|
#define DBG_REG_ERROR (0xEC)
|
|
|
|
#define ReadAucpuRegister(addr) \
|
|
readl((void __iomem *)(s_aucpu_register_base + (addr)))
|
|
|
|
#define WriteAucpuRegister(addr, val) \
|
|
writel((u32)val, (void __iomem *)(s_aucpu_register_base + (addr)))
|
|
|
|
enum aml_aucpu_cmd_type {
|
|
CMD_NONE = 0,
|
|
CMD_SET_DBG_BUF_LEVEL,
|
|
CMD_RESET_DBG_BUF,
|
|
CMD_SET_DBG_LEVEL,
|
|
CMD_INST_ADD,
|
|
CMD_INST_START,
|
|
CMD_INST_STOP,
|
|
CMD_INST_FLUSH,
|
|
CMD_INST_REMOVE,
|
|
CMD_RESET_ALL,
|
|
CMD_MAX,
|
|
};
|
|
|
|
static bool use_reserve;
|
|
static s32 s_aucpu_irq;
|
|
static bool s_aucpu_irq_requested;
|
|
static bool s_aucpu_delaywork_requested;
|
|
|
|
static s32 s_aucpu_major;
|
|
static s32 s_register_flag;
|
|
static struct device *aucpu_dev;
|
|
static ulong s_aucpu_register_base;
|
|
static s32 load_firmware_status = AUCPU_DRVERR_NO_INIT;
|
|
|
|
struct aucpu_buffer_t {
|
|
u32 size;
|
|
ulong phys_addr; /* physical address */
|
|
ulong base; /* kernel logical address */
|
|
};
|
|
|
|
struct aml_cmd_set {
|
|
u32 seq; /* command sequence number */
|
|
u32 cmd_type; //command type + instance index
|
|
u32 media_options; // media type + options
|
|
u32 src_buf_start; // source es/desc buffer address
|
|
u32 src_buf_sz; //source es buffer size or desc buffer size
|
|
u32 dst_buf_start; // destination es/desc buffer address
|
|
u32 dst_buf_sz; //destination es buffer size or desc buffer size
|
|
u32 dst_report_start; //report structure start address
|
|
u32 dst_report_sz; //report structure max sz
|
|
u32 src_update_start; //input source update structure start address
|
|
};
|
|
|
|
struct aml_cmd_resp {
|
|
u32 resp_seq; /* resp sequence number */
|
|
u32 cmd_type; /* original cmd + instance index */
|
|
u32 result_status; /* 0 OK, others error code */
|
|
};
|
|
|
|
struct aucpu_report_t {
|
|
u32 r_status; /* execut status */
|
|
u32 r_dst_byte_cnt; /* destination total bytes */
|
|
u32 r_dst_wrptr; /* destination write position phy address*/
|
|
u32 r_src_wrptr; /* origin src write position phy address*/
|
|
u32 r_src_rdptr; /* origin src write position phy address*/
|
|
u32 r_src_byte_cnt; /* origin src total bytes */
|
|
|
|
u32 total_bytes; /* total bytes on current round */
|
|
u32 buffed_bytes; /* buffered bytes on current round */
|
|
u32 processed_bytes; /* processed bytes on current round */
|
|
u32 dbg_buf_wrptr; /* print log position in ddr buffer */
|
|
u32 run_cnt;
|
|
};
|
|
|
|
struct aucpu_inst_t {
|
|
u32 inst_status; // current instant status
|
|
u32 media_type;
|
|
u32 config_options;// bit 0: src update by DDR (1) or dmx (0)
|
|
// bit 1: source is linklist 1, or ringbuffer 0
|
|
// bit 2: output is linklist 1, or ringbuffer 0
|
|
struct aucpu_buffer_t src_buf; // source es buffer
|
|
struct aucpu_buffer_t dst_buf; // destination es buffer
|
|
struct aucpu_buffer_t reprt_buf; //report result buffer
|
|
struct aucpu_buffer_t in_upd_buf; //report result buffer
|
|
u32 src_update_index; //source pointer update demux channel index
|
|
s32 inst_handle; //handle from aucpu returned.
|
|
// below are runtime status
|
|
u32 work_state; // the working status of the instance
|
|
u32 src_wrptr; //source buffer wrptr
|
|
u32 src_rdptr; //source rdprt
|
|
u32 src_byte_cnt; // source total processed bytes shadow
|
|
u32 dst_wrptr; // dst wrptr shadow
|
|
u32 dst_byte_cnt; // dst total processed bytes shadow
|
|
|
|
u32 total_bytes; /* total bytes on current round */
|
|
u32 buffed_bytes; /* buffered bytes on current round */
|
|
u32 processed_bytes; /* processed bytes on current round */
|
|
u32 run_cnt; //running count of rounds
|
|
};
|
|
|
|
struct aucpu_ctx_t {
|
|
u32 activated_inst_num; /* active instances number */
|
|
struct aucpu_inst_t strm_inst[AUCPU_MAX_INTST_NUM];
|
|
struct aml_cmd_set last_cmd;
|
|
struct aucpu_buffer_t aucpu_reg; /* register assignment */
|
|
struct aucpu_buffer_t aucpu_mem; /* local total buffer pool */
|
|
|
|
struct aucpu_buffer_t t_upd_buf; /* total update input buf */
|
|
struct aucpu_buffer_t t_rpt_buf; /* total report out buf */
|
|
struct aucpu_buffer_t dbg_buf; /* debug buf */
|
|
u32 dbg_buf_size;
|
|
u32 dbg_pad_size;
|
|
struct mutex mutex; /* command muxtex */
|
|
struct delayed_work dbg_work; /* aucpu debug log */
|
|
struct device *aucpu_device;
|
|
u32 debug_buf_rdptr;
|
|
u32 debug_buf_wrptr;
|
|
u32 aucpu_print_level;
|
|
u32 aucpu_trace_debug; /* error trace line */
|
|
u32 aucpu_trace_error; /* debug trace error line */
|
|
};
|
|
|
|
static struct platform_device *aucpu_pdev;
|
|
static struct aucpu_ctx_t *s_aucpu_ctx;
|
|
static s32 aucpu_init_try(struct platform_device *pdev);
|
|
|
|
static void dma_flush(u32 buf_start, u32 buf_size)
|
|
{
|
|
if (aucpu_pdev && load_firmware_status == 0)
|
|
dma_sync_single_for_device(&aucpu_pdev->dev, buf_start,
|
|
buf_size, DMA_TO_DEVICE);
|
|
}
|
|
|
|
static void cache_flush(u32 buf_start, u32 buf_size)
|
|
{
|
|
if (aucpu_pdev && load_firmware_status == 0)
|
|
dma_sync_single_for_cpu(&aucpu_pdev->dev, buf_start,
|
|
buf_size, DMA_FROM_DEVICE);
|
|
}
|
|
|
|
s32 aucpu_hw_reset(void)
|
|
{
|
|
aucpu_pr(LOG_DEBUG, "request aucpu reset from application\n");
|
|
return 0;
|
|
}
|
|
|
|
static irqreturn_t aucpu_irq_handler(s32 irq, void *dev_id)
|
|
{
|
|
struct aucpu_ctx_t *dev = (struct aucpu_ctx_t *)dev_id;
|
|
|
|
aucpu_pr(LOG_ALL, "Receive an AUCPU interrupt\n");
|
|
|
|
schedule_delayed_work(&dev->dbg_work, 0);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static s32 alloc_local_dma_buf(struct device *dev,
|
|
struct aucpu_buffer_t *buf,
|
|
u32 size, u32 dir)
|
|
{
|
|
u32 required;
|
|
dma_addr_t dma_addr = 0;
|
|
|
|
required = roundup(size, PAGE_SIZE);
|
|
buf->base = (ulong)kzalloc(required, GFP_KERNEL | GFP_DMA);
|
|
if (buf->base == 0) {
|
|
aucpu_pr(LOG_ERROR,
|
|
"failed to allocate aucpu local memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
buf->size = required;
|
|
dma_addr = dma_map_single(dev, (void *)buf->base, buf->size, dir);
|
|
if (dma_mapping_error(dev, dma_addr)) {
|
|
aucpu_pr(LOG_ERROR, "error dma mapping addr 0x%lx\n",
|
|
(ulong)dma_addr);
|
|
kfree((void *)buf->base);
|
|
buf->size = 0;
|
|
return -EINVAL;
|
|
}
|
|
buf->phys_addr = (ulong)dma_addr;
|
|
return 0;
|
|
}
|
|
|
|
static s32 init_Aucpu_local_buf(struct device *dev)
|
|
{
|
|
u32 index, update_size, err;
|
|
ulong phy, base_addr;
|
|
struct aucpu_buffer_t *pbuf;
|
|
struct aucpu_ctx_t *pctx = s_aucpu_ctx;
|
|
/* update buffers */
|
|
update_size = roundup(sizeof(struct aml_aucpu_buf_upd), AUCPU_UPD_SZ);
|
|
if (use_reserve) {
|
|
phy = pctx->aucpu_mem.phys_addr;
|
|
base_addr = pctx->aucpu_mem.base;
|
|
} else {
|
|
err = alloc_local_dma_buf(dev, &pctx->t_upd_buf,
|
|
update_size * AUCPU_MAX_INTST_NUM,
|
|
DMA_TO_DEVICE);
|
|
if (err) {
|
|
aucpu_pr(LOG_ERROR, "error alloc update buffer\n");
|
|
return err;
|
|
}
|
|
phy = pctx->t_upd_buf.phys_addr;
|
|
base_addr = pctx->t_upd_buf.base;
|
|
}
|
|
|
|
aucpu_pr(LOG_DEBUG, "local upd buf start from 0x%lx item size 0x%x\n",
|
|
phy, update_size);
|
|
for (index = 0; index < AUCPU_MAX_INTST_NUM; index++) {
|
|
pbuf = &pctx->strm_inst[index].in_upd_buf;
|
|
pbuf->phys_addr = phy;
|
|
pbuf->base = base_addr;
|
|
pbuf->size = update_size;
|
|
phy += update_size;
|
|
base_addr += update_size;
|
|
aucpu_pr(LOG_INFO, "upd buf %d @0x%lx size 0x%x, phy 0x%lx\n",
|
|
index, pbuf->base, pbuf->size, pbuf->phys_addr);
|
|
}
|
|
|
|
/* report buffers */
|
|
update_size = roundup(sizeof(struct aucpu_report_t), AUCPU_UPD_SZ);
|
|
if (!use_reserve) {
|
|
err = alloc_local_dma_buf(dev, &pctx->t_rpt_buf,
|
|
update_size * AUCPU_MAX_INTST_NUM,
|
|
DMA_FROM_DEVICE);
|
|
if (err) {
|
|
aucpu_pr(LOG_ERROR, "error alloc report buffer\n");
|
|
return err;
|
|
}
|
|
phy = pctx->t_rpt_buf.phys_addr;
|
|
base_addr = pctx->t_rpt_buf.base;
|
|
}
|
|
aucpu_pr(LOG_DEBUG, "local rpt buf start from 0x%lx item size 0x%x\n",
|
|
phy, update_size);
|
|
for (index = 0; index < AUCPU_MAX_INTST_NUM; index++) {
|
|
pbuf = &pctx->strm_inst[index].reprt_buf;
|
|
pbuf->phys_addr = phy;
|
|
pbuf->base = base_addr;
|
|
pbuf->size = update_size;
|
|
phy += update_size;
|
|
base_addr += update_size;
|
|
aucpu_pr(LOG_INFO, "rpt buf %d @0x%lx size 0x%x, phy 0x%lx\n",
|
|
index, pbuf->base, pbuf->size, pbuf->phys_addr);
|
|
}
|
|
/* debug buffer */
|
|
update_size = AUCPU_DBG_BUF_SZ + AUCPU_DBG_PAD_SZ;
|
|
pbuf = &pctx->dbg_buf;
|
|
if (!use_reserve) {
|
|
err = alloc_local_dma_buf(dev, pbuf, update_size,
|
|
DMA_FROM_DEVICE);
|
|
if (err) {
|
|
aucpu_pr(LOG_ERROR, "error alloc debug buffer\n");
|
|
return err;
|
|
}
|
|
} else {
|
|
pbuf->phys_addr = phy;
|
|
pbuf->base = base_addr;
|
|
pbuf->size = AUCPU_DBG_BUF_SZ + AUCPU_DBG_PAD_SZ;
|
|
}
|
|
|
|
pctx->dbg_buf_size = AUCPU_DBG_BUF_SZ;
|
|
pctx->dbg_pad_size = AUCPU_DBG_PAD_SZ;
|
|
aucpu_pr(LOG_DEBUG, "dbg buf @0x%lx size 0x%x pad 0x%x phy 0x%lx\n",
|
|
pbuf->base, pctx->dbg_buf_size, pctx->dbg_pad_size,
|
|
pbuf->phys_addr);
|
|
phy += pbuf->size;
|
|
|
|
if (use_reserve &&
|
|
(phy - pctx->aucpu_mem.phys_addr) > pctx->aucpu_mem.size) {
|
|
aucpu_pr(LOG_ERROR, "Allocated too less mem size 0x%x\n",
|
|
pctx->aucpu_mem.size);
|
|
return -ENOMEM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void uninit_Aucpu_local_buf(struct device *dev)
|
|
{
|
|
struct aucpu_ctx_t *pctx = s_aucpu_ctx;
|
|
|
|
if (use_reserve)
|
|
return;
|
|
if (pctx->dbg_buf.base) {
|
|
dma_unmap_single(dev, pctx->dbg_buf.phys_addr,
|
|
pctx->dbg_buf.size, DMA_FROM_DEVICE);
|
|
kfree((void *)pctx->dbg_buf.base);
|
|
pctx->dbg_buf.base = 0;
|
|
}
|
|
if (pctx->t_rpt_buf.base) {
|
|
dma_unmap_single(dev, pctx->t_rpt_buf.phys_addr,
|
|
pctx->t_rpt_buf.size, DMA_FROM_DEVICE);
|
|
kfree((void *)pctx->t_rpt_buf.base);
|
|
pctx->t_rpt_buf.base = 0;
|
|
}
|
|
if (pctx->t_upd_buf.base) {
|
|
dma_unmap_single(dev, pctx->t_upd_buf.phys_addr,
|
|
pctx->t_upd_buf.size, DMA_TO_DEVICE);
|
|
kfree((void *)pctx->t_upd_buf.base);
|
|
pctx->t_upd_buf.base = 0;
|
|
}
|
|
}
|
|
|
|
static s32 send_aucpu_cmd(struct aml_cmd_set *cmd)
|
|
{
|
|
u32 seq; /* command sequence number */
|
|
u32 resp_seq;
|
|
u32 cmd_type; //command type + instance index
|
|
s32 cmd_code;
|
|
s32 inst_idx;
|
|
struct aucpu_ctx_t *pctx;
|
|
|
|
ulong expires;
|
|
s32 result = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
WriteAucpuRegister(CMD_TYPE_ADDR, cmd->cmd_type);
|
|
WriteAucpuRegister(CMD_OPT_ADDR, cmd->media_options);
|
|
WriteAucpuRegister(CMD_SRC_ADDR, cmd->src_buf_start);
|
|
WriteAucpuRegister(CMD_SRC_SZ, cmd->src_buf_sz);
|
|
WriteAucpuRegister(CMD_DST_ADDR, cmd->dst_buf_start);
|
|
WriteAucpuRegister(CMD_DST_SZ, cmd->dst_buf_sz);
|
|
WriteAucpuRegister(CMD_RPT_ADDR, cmd->dst_report_start);
|
|
WriteAucpuRegister(CMD_RPT_SZ, cmd->dst_report_sz);
|
|
WriteAucpuRegister(CMD_UPD_ADDR, cmd->src_update_start);
|
|
|
|
seq = ReadAucpuRegister(RESP_SEQ_ADDR);
|
|
if (seq == cmd->seq) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU sequence no updated\n");
|
|
seq++;
|
|
} else {
|
|
seq = cmd->seq;
|
|
}
|
|
WriteAucpuRegister(CMD_SEQ_ADDR, seq); // trigger the seq
|
|
// wait for response
|
|
expires = jiffies + HZ / 10;
|
|
do {
|
|
resp_seq = ReadAucpuRegister(RESP_SEQ_ADDR);
|
|
if (resp_seq == seq)
|
|
break;
|
|
if (time_after(jiffies, expires)) {
|
|
aucpu_pr(LOG_ERROR,
|
|
"AUCPU Command %x timeout seq %d tobe %d\n",
|
|
cmd->cmd_type, resp_seq, seq);
|
|
result = AUCPU_DRVERR_CMD_TIMEOUT;
|
|
break;
|
|
}
|
|
usleep_range(20, 2000);
|
|
} while (1);
|
|
if (resp_seq == seq) {
|
|
cmd_type = ReadAucpuRegister(RESP_TYPE_ADDR);
|
|
result = ReadAucpuRegister(RESP_RST_ADDR);
|
|
if (result == AUCPU_NO_ERROR) {
|
|
cmd_code = cmd_type & 0xff;
|
|
inst_idx = (cmd_type >> 8) & 0xff;
|
|
if (cmd_code != (cmd->cmd_type & 0xff)) {
|
|
aucpu_pr(LOG_ERROR,
|
|
"Command no match exp %d now %d\n",
|
|
cmd->cmd_type & 0xff, cmd_code);
|
|
result = AUCPU_DRVERR_CMD_NOMATCH;
|
|
}
|
|
if (cmd_code == CMD_INST_ADD)
|
|
result = inst_idx;
|
|
}
|
|
cmd->seq = resp_seq + 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void change_debug_level(void)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aml_cmd_set *cmd;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
mutex_lock(&pctx->mutex);
|
|
cmd = &pctx->last_cmd;
|
|
|
|
cmd->cmd_type = CMD_SET_DBG_LEVEL;
|
|
cmd->media_options = dbg_level;
|
|
pctx->aucpu_print_level = dbg_level;
|
|
res = send_aucpu_cmd(cmd);
|
|
if (res < 0) { // failed
|
|
aucpu_pr(LOG_ERROR, "aucpu set dbg buf failed\n");
|
|
}
|
|
mutex_unlock(&pctx->mutex);
|
|
}
|
|
|
|
void setup_debug_polling(void)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aml_cmd_set *cmd;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
mutex_lock(&pctx->mutex);
|
|
cmd = &pctx->last_cmd;
|
|
cmd->cmd_type = CMD_SET_DBG_BUF_LEVEL;
|
|
cmd->dst_buf_start = pctx->dbg_buf.phys_addr;
|
|
cmd->dst_buf_sz = pctx->dbg_buf_size; //exclude the padding
|
|
cmd->media_options = dbg_level;
|
|
pctx->aucpu_print_level = dbg_level;
|
|
res = send_aucpu_cmd(cmd);
|
|
if (res < 0) // failed
|
|
aucpu_pr(LOG_ERROR, "aucpu set dbg buf failed\n");
|
|
mutex_unlock(&pctx->mutex);
|
|
pctx->debug_buf_rdptr = pctx->dbg_buf.phys_addr;
|
|
pctx->debug_buf_wrptr = pctx->dbg_buf.phys_addr;
|
|
}
|
|
|
|
static bool do_debug_work_in(struct aucpu_ctx_t *pctx)
|
|
{
|
|
u32 now_wrptr, len, trace, count = 0;
|
|
int fullness;
|
|
unsigned char *cur_ptr;
|
|
|
|
trace = ReadAucpuRegister(DBG_REG_STAGE);
|
|
if (trace != pctx->aucpu_trace_debug) {
|
|
aucpu_pr(LOG_ERROR, "aucpu trace orig 0x%x now 0x%x",
|
|
pctx->aucpu_trace_debug, trace);
|
|
aucpu_pr(LOG_ERROR, "--stage value %d line %d\n",
|
|
trace >> 16, trace & 0xffff);
|
|
pctx->aucpu_trace_debug = trace;
|
|
}
|
|
|
|
trace = ReadAucpuRegister(DBG_REG_ERROR);
|
|
if (trace != pctx->aucpu_trace_error) {
|
|
aucpu_pr(LOG_ERROR, "aucpu trace error orig 0x%x now 0x%x",
|
|
pctx->aucpu_trace_error, trace);
|
|
aucpu_pr(LOG_ERROR, "--Err value %d line %d\n",
|
|
trace >> 16, trace & 0xffff);
|
|
pctx->aucpu_trace_error = trace;
|
|
}
|
|
// get latest debuf buffer wrptr
|
|
now_wrptr = ReadAucpuRegister(DBG_REG_WRPTR);
|
|
if (now_wrptr == pctx->debug_buf_wrptr) // no new update return
|
|
return 0;
|
|
if (now_wrptr < pctx->dbg_buf.phys_addr ||
|
|
now_wrptr >= (pctx->dbg_buf.phys_addr + pctx->dbg_buf.size)) {
|
|
now_wrptr = pctx->debug_buf_wrptr;
|
|
return 0;
|
|
}
|
|
pctx->debug_buf_wrptr = now_wrptr;
|
|
if (pctx->debug_buf_wrptr >=
|
|
(pctx->dbg_buf.phys_addr + pctx->dbg_buf_size))
|
|
pctx->debug_buf_wrptr -= pctx->dbg_buf_size;
|
|
|
|
fullness = now_wrptr - pctx->debug_buf_rdptr;
|
|
if (fullness < 0)
|
|
fullness += pctx->dbg_buf_size;
|
|
//invalidate the whole DBG _buffer as it is small
|
|
cache_flush(pctx->dbg_buf.phys_addr, pctx->dbg_buf.size);
|
|
cur_ptr = (unsigned char *)(pctx->dbg_buf.base +
|
|
(pctx->debug_buf_rdptr - pctx->dbg_buf.phys_addr));
|
|
if (fullness > 1024)
|
|
aucpu_pr(LOG_ERROR, "Aucpu:Too many logs, fullness %d\n",
|
|
fullness);
|
|
while (fullness > 0) {
|
|
len = strlen(cur_ptr); // count the null
|
|
if (print_level <= LOG_DEBUG && len &&
|
|
(fullness <= 1024 || count < 20))
|
|
aucpu_pr(LOG_ERROR, "Aucpu:%s", cur_ptr);
|
|
len += 1; // add the null at the end
|
|
cur_ptr += len;
|
|
fullness -= len;
|
|
count++;
|
|
if (cur_ptr > (unsigned char *)(pctx->dbg_buf.base
|
|
+ pctx->dbg_buf.size)) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU log over boundary\n");
|
|
break;
|
|
}
|
|
if (cur_ptr > (unsigned char *)(pctx->dbg_buf.base
|
|
+ pctx->dbg_buf_size))
|
|
cur_ptr -= pctx->dbg_buf_size; // wrap around
|
|
if (fullness < 0) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU log no align\n");
|
|
break;
|
|
}
|
|
}
|
|
if (fullness) // something wrong
|
|
aucpu_pr(LOG_ERROR, "AUCPU wrong log full%d wrpt %x rdptr %x\n",
|
|
fullness, now_wrptr, pctx->debug_buf_rdptr);
|
|
// check again for new update`
|
|
pctx->debug_buf_rdptr = now_wrptr;
|
|
now_wrptr = ReadAucpuRegister(DBG_REG_WRPTR);
|
|
return (now_wrptr == pctx->debug_buf_wrptr) ? 0 : 1;
|
|
}
|
|
|
|
static void do_polling_work(struct work_struct *work)
|
|
{
|
|
bool ret;
|
|
|
|
struct aucpu_ctx_t *pctx = s_aucpu_ctx;
|
|
int need_retry = 1;
|
|
unsigned long delay = DBG_POLL_TIME;
|
|
|
|
while (need_retry)
|
|
need_retry = do_debug_work_in(pctx);
|
|
|
|
if (pctx->aucpu_print_level != dbg_level)
|
|
change_debug_level();
|
|
|
|
ret = schedule_delayed_work(&pctx->dbg_work, delay);
|
|
if (!ret) {
|
|
aucpu_pr(LOG_ERROR, "debug task delay failed!\n");
|
|
cancel_delayed_work(&pctx->dbg_work);
|
|
}
|
|
}
|
|
|
|
#define AUCPU_FW_PATH "/lib/firmware/aucpu_fw.bin"
|
|
#define AUCPU_MAX_FW_SZ (48 * SZ_1K)
|
|
#include <linux/fs.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/arm-smccc.h>
|
|
|
|
#define FID_FW_LOAD (0x82000077)
|
|
#define FW_TYPE_AUCPU (0)
|
|
#define FW_TYPE_AUCPU_STOP (1)
|
|
|
|
unsigned long sec_fw_cmd(u32 type, void *fw, size_t size)
|
|
{
|
|
struct arm_smccc_res smccc;
|
|
void __iomem *sharemem_input_base;
|
|
long sharemem_phy_input_base;
|
|
|
|
sharemem_input_base = get_meson_sm_input_base();
|
|
sharemem_phy_input_base = get_secmon_phy_input_base();
|
|
if (!sharemem_input_base || !sharemem_phy_input_base)
|
|
return -1;
|
|
|
|
meson_sm_mutex_lock();
|
|
if (size)
|
|
memcpy(sharemem_input_base,
|
|
(const void *)fw, size);
|
|
|
|
//asm __volatile__("" : : : "memory");
|
|
|
|
arm_smccc_smc(FID_FW_LOAD, type, size, 0, 0, 0, 0, 0, &smccc);
|
|
meson_sm_mutex_unlock();
|
|
|
|
return smccc.a0;
|
|
}
|
|
|
|
static int load_start_aucpu_fw(struct device *device)
|
|
{
|
|
const struct firmware *my_fw = NULL;
|
|
char *fw;
|
|
|
|
int result = 0;
|
|
unsigned int length = 0;
|
|
|
|
aucpu_pr(LOG_DEBUG, "FW\n");
|
|
|
|
result = request_firmware(&my_fw, "aucpu_fw.bin", device);
|
|
if (!my_fw || result < 0) {
|
|
aucpu_pr(LOG_ERROR, "load aucpu_fw.bin fail, result=%d\n", result);
|
|
result = AUCPU_ERROR_NOT_IMPLEMENTED;
|
|
return result;
|
|
}
|
|
|
|
fw = (char *)my_fw->data;
|
|
length = my_fw->size;
|
|
|
|
aucpu_pr(LOG_INFO, "FW read size %d data 0x%x-%x-%x-%x\n", length,
|
|
fw[0], fw[1], fw[2], fw[3]);
|
|
|
|
result = sec_fw_cmd(FW_TYPE_AUCPU, fw, length);
|
|
|
|
release_firmware(my_fw);
|
|
|
|
aucpu_pr(LOG_INFO, "download fw success reset start AUCPU\n");
|
|
aucpu_pr(LOG_INFO, "[%s] done result %d\n", __func__, result);
|
|
return result;
|
|
}
|
|
|
|
static void stop_aucpu_fw(struct device *device)
|
|
{
|
|
int result = 0;
|
|
|
|
result = sec_fw_cmd(FW_TYPE_AUCPU_STOP, NULL, 0);
|
|
aucpu_pr(LOG_INFO, "[%s] done result %d\n", __func__, result);
|
|
}
|
|
|
|
s32 aml_aucpu_strm_create(struct aml_aucpu_strm_buf *src,
|
|
struct aml_aucpu_strm_buf *dst,
|
|
struct aml_aucpu_inst_config *cfg)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
struct aml_cmd_set *cmd;
|
|
s32 handle_idx; // handle index AUCPU_STA_NONE
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
if (!aucpu_dev) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU driver did not init yet\n");
|
|
return AUCPU_DRVERR_NO_INIT;
|
|
}
|
|
if (pctx->activated_inst_num >= AUCPU_MAX_INTST_NUM) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU too many stream instances\n");
|
|
return AUCPU_DRVERR_TOO_MANY_STRM;
|
|
}
|
|
/* check parmeters */
|
|
aucpu_pr(LOG_INFO, "%s src 0x%lx size 0x%x dst 0x%lx size 0x%x\n",
|
|
__func__, src->phy_start, src->buf_size,
|
|
dst->phy_start, dst->buf_size);
|
|
aucpu_pr(LOG_INFO, "config media_type %d chn %d flags 0x%x-0x%x-0x%x\n",
|
|
cfg->media_type, cfg->dma_chn_id, cfg->config_flags,
|
|
src->buf_flags, dst->buf_flags);
|
|
|
|
if (cfg->media_type <= MEDIA_UNKNOWN || cfg->media_type >= MEDIA_MAX) {
|
|
aucpu_pr(LOG_ERROR, "invalid media format %d\n",
|
|
cfg->media_type);
|
|
return AUCPU_DRVERR_INVALID_TYPE;
|
|
}
|
|
|
|
mutex_lock(&pctx->mutex);
|
|
for (handle_idx = 0; handle_idx < AUCPU_MAX_INTST_NUM; handle_idx++) {
|
|
pinst = &pctx->strm_inst[handle_idx];
|
|
if (pinst->inst_status == AUCPU_STA_NONE)
|
|
break; // found one empty slot
|
|
}
|
|
if (handle_idx >= AUCPU_MAX_INTST_NUM) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU can not find empty slot\n");
|
|
mutex_unlock(&pctx->mutex);
|
|
return AUCPU_DRVERR_NO_SLOT;
|
|
}
|
|
|
|
pinst->media_type = cfg->media_type;
|
|
pinst->config_options = cfg->config_flags & 0x1;
|
|
pinst->src_update_index = cfg->dma_chn_id;
|
|
if (src->buf_flags & 0x1)
|
|
pinst->config_options |= 0x2;
|
|
if (dst->buf_flags & 0x1)
|
|
pinst->config_options |= 0x4;
|
|
|
|
pinst->src_buf.phys_addr = src->phy_start;
|
|
pinst->src_buf.size = src->buf_size;
|
|
pinst->dst_buf.phys_addr = dst->phy_start;
|
|
pinst->dst_buf.size = dst->buf_size;
|
|
pinst->src_wrptr = pinst->src_buf.phys_addr;
|
|
pinst->src_rdptr = pinst->src_buf.phys_addr;
|
|
pinst->src_byte_cnt = 0;
|
|
pinst->dst_wrptr = pinst->dst_buf.phys_addr;
|
|
pinst->dst_byte_cnt = 0;
|
|
cmd = &pctx->last_cmd;
|
|
cmd->cmd_type = CMD_INST_ADD | ((pinst->inst_handle & 0xff) << 8);
|
|
cmd->media_options = (pinst->config_options << 8) | pinst->media_type;
|
|
cmd->src_buf_start = pinst->src_buf.phys_addr;
|
|
cmd->src_buf_sz = pinst->src_buf.size;
|
|
cmd->dst_buf_start = pinst->dst_buf.phys_addr;
|
|
cmd->dst_buf_sz = pinst->dst_buf.size;
|
|
cmd->dst_report_start = pinst->reprt_buf.phys_addr;
|
|
cmd->dst_report_sz = pinst->reprt_buf.size;
|
|
if (pinst->config_options & 0x1)
|
|
cmd->src_update_start = pinst->in_upd_buf.phys_addr;
|
|
else
|
|
cmd->src_update_start = pinst->src_update_index;
|
|
res = send_aucpu_cmd(cmd);
|
|
if (res >= 0) { // success
|
|
pinst->inst_handle = res;
|
|
pinst->inst_status = AUCPU_STA_IDLE; // change status
|
|
pctx->activated_inst_num++;
|
|
res = handle_idx;
|
|
aucpu_pr(LOG_INFO, "New strm %d %d created\n",
|
|
pinst->inst_handle, res);
|
|
}
|
|
mutex_unlock(&pctx->mutex);
|
|
return res;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_create);
|
|
|
|
static s32 valid_handle2Inst(s32 handle, struct aucpu_ctx_t *pctx,
|
|
struct aucpu_inst_t **inst)
|
|
{
|
|
struct aucpu_inst_t *pinst;
|
|
|
|
if (!aucpu_dev) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU driver did not init yet\n");
|
|
return AUCPU_DRVERR_NO_INIT;
|
|
}
|
|
if (pctx->activated_inst_num == 0) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU No strm active\n");
|
|
return AUCPU_DRVERR_NO_ACTIVE;
|
|
}
|
|
if (handle < 0 || handle >= AUCPU_MAX_INTST_NUM) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU invalid strm handle\n");
|
|
return AUCPU_DRVERR_INVALID_HANDLE;
|
|
}
|
|
pinst = &pctx->strm_inst[handle];
|
|
if (pinst->inst_handle < 0 ||
|
|
pinst->inst_handle >= AUCPU_MAX_INTST_NUM) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU handle is invalid\n");
|
|
return AUCPU_DRVERR_INVALID_HANDLE;
|
|
}
|
|
if (pinst->inst_status == AUCPU_STA_NONE) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU strm handle is empty\n");
|
|
return AUCPU_DRVERR_INVALID_HANDLE;
|
|
}
|
|
*inst = pinst;
|
|
return AUCPU_NO_ERROR;
|
|
}
|
|
|
|
s32 aml_aucpu_strm_start(s32 handle)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
struct aml_cmd_set *cmd;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
res = valid_handle2Inst(handle, pctx, &pinst);
|
|
if (res < 0) {
|
|
aucpu_pr(LOG_ERROR, "%s invalid_handle %d\n", __func__, handle);
|
|
return res;
|
|
}
|
|
if (pinst->inst_status != AUCPU_STA_IDLE) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU strm %d is not idle\n", handle);
|
|
return AUCPU_DRVERR_WRONG_STATE;
|
|
}
|
|
mutex_lock(&pctx->mutex);
|
|
cmd = &pctx->last_cmd;
|
|
cmd->cmd_type = CMD_INST_START | ((pinst->inst_handle & 0xff) << 8);
|
|
res = send_aucpu_cmd(cmd);
|
|
if (res >= 0) { // success
|
|
pinst->inst_status = AUCPU_STA_RUNNING; // change status
|
|
aucpu_pr(LOG_INFO, "Strm %d Started\n", pinst->inst_handle);
|
|
}
|
|
mutex_unlock(&pctx->mutex);
|
|
return res;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_start);
|
|
|
|
s32 aml_aucpu_strm_stop(s32 handle)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
struct aml_cmd_set *cmd;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
res = valid_handle2Inst(handle, pctx, &pinst);
|
|
if (res < 0) {
|
|
aucpu_pr(LOG_ERROR, "%s invalid_handle %d\n", __func__, handle);
|
|
return res;
|
|
}
|
|
if (pinst->inst_status != AUCPU_STA_RUNNING &&
|
|
pinst->inst_status != AUCPU_STA_FLUSHING) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU strm %d is not running\n", handle);
|
|
return AUCPU_DRVERR_WRONG_STATE;
|
|
}
|
|
|
|
mutex_lock(&pctx->mutex);
|
|
cmd = &pctx->last_cmd;
|
|
cmd->cmd_type = CMD_INST_STOP | ((pinst->inst_handle & 0xff) << 8);
|
|
res = send_aucpu_cmd(cmd);
|
|
if (res >= 0) { // success
|
|
pinst->inst_status = AUCPU_STA_IDLE; // change status
|
|
aucpu_pr(LOG_INFO, "Strm %d Stopped\n", pinst->inst_handle);
|
|
}
|
|
mutex_unlock(&pctx->mutex);
|
|
return res;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_stop);
|
|
|
|
s32 aml_aucpu_strm_flush(s32 handle)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
struct aml_cmd_set *cmd;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
res = valid_handle2Inst(handle, pctx, &pinst);
|
|
if (res < 0) {
|
|
aucpu_pr(LOG_ERROR, "%s invalid_handle %d\n", __func__, handle);
|
|
return res;
|
|
}
|
|
|
|
if (pinst->inst_status != AUCPU_STA_RUNNING) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU strm %d is not running\n", handle);
|
|
return AUCPU_DRVERR_WRONG_STATE;
|
|
}
|
|
mutex_lock(&pctx->mutex);
|
|
cmd = &pctx->last_cmd;
|
|
cmd->cmd_type = CMD_INST_FLUSH | ((pinst->inst_handle & 0xff) << 8);
|
|
res = send_aucpu_cmd(cmd);
|
|
if (res >= 0) { // success
|
|
pinst->inst_status = AUCPU_STA_FLUSHING; // change status
|
|
aucpu_pr(LOG_INFO, "Strm %d flushing\n", pinst->inst_handle);
|
|
}
|
|
mutex_unlock(&pctx->mutex);
|
|
return res;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_flush);
|
|
|
|
s32 aml_aucpu_strm_remove(s32 handle)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
struct aml_cmd_set *cmd;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
res = valid_handle2Inst(handle, pctx, &pinst);
|
|
if (res < 0) {
|
|
aucpu_pr(LOG_ERROR, "%s invalid_handle %d\n", __func__, handle);
|
|
return res;
|
|
}
|
|
|
|
if (pinst->inst_status != AUCPU_STA_IDLE) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU strm %d is not running\n", handle);
|
|
return AUCPU_DRVERR_WRONG_STATE;
|
|
}
|
|
mutex_lock(&pctx->mutex);
|
|
cmd = &pctx->last_cmd;
|
|
cmd->cmd_type = CMD_INST_REMOVE | ((pinst->inst_handle & 0xff) << 8);
|
|
res = send_aucpu_cmd(cmd);
|
|
if (res >= 0) { // success
|
|
pinst->inst_status = AUCPU_STA_NONE; // change status
|
|
pctx->activated_inst_num--;
|
|
aucpu_pr(LOG_INFO, "Strm %d removed\n", pinst->inst_handle);
|
|
}
|
|
mutex_unlock(&pctx->mutex);
|
|
return res;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_remove);
|
|
|
|
s32 aml_aucpu_strm_update_src(s32 handle, struct aml_aucpu_buf_upd *upd)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
struct aml_aucpu_buf_upd *pdest;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
res = valid_handle2Inst(handle, pctx, &pinst);
|
|
if (res < 0) {
|
|
aucpu_pr(LOG_ERROR, "%s invalid_handle %d\n", __func__, handle);
|
|
return res;
|
|
}
|
|
if (pinst->inst_status != AUCPU_STA_RUNNING) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU strm %d is not running\n", handle);
|
|
return AUCPU_DRVERR_WRONG_STATE;
|
|
}
|
|
if ((pinst->config_options & 0x1) == 0) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU strm %d wrong config\n", handle);
|
|
return AUCPU_DRVERR_VALID_CONTEXT;
|
|
}
|
|
pdest = (struct aml_aucpu_buf_upd *)pinst->in_upd_buf.base;
|
|
pdest->phy_cur_ptr = upd->phy_cur_ptr;
|
|
pdest->byte_cnt = upd->byte_cnt;
|
|
aucpu_pr(LOG_INFO, "AUCPU strm %d update ptr 0x%lx byte_cnt %d\n",
|
|
handle, upd->phy_cur_ptr, upd->byte_cnt);
|
|
/* need clean the cache here to make sure data in DDR */
|
|
dma_flush(pinst->in_upd_buf.phys_addr, pinst->in_upd_buf.size);
|
|
return AUCPU_NO_ERROR;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_update_src);
|
|
|
|
s32 aml_aucpu_strm_get_dst(s32 handle, struct aml_aucpu_buf_upd *upd)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
struct aucpu_report_t *rpt;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
res = valid_handle2Inst(handle, pctx, &pinst);
|
|
if (res < 0) {
|
|
aucpu_pr(LOG_ERROR, "%s invalid_handle %d\n", __func__, handle);
|
|
return res;
|
|
}
|
|
if (pinst->inst_status == AUCPU_STS_INST_IDLE) {
|
|
upd->phy_cur_ptr = pinst->dst_wrptr;
|
|
upd->byte_cnt = pinst->dst_byte_cnt;
|
|
aucpu_pr(LOG_INFO, "AUCPU strm %d is not running\n", handle);
|
|
return res;
|
|
} else if (pinst->inst_status != AUCPU_STA_RUNNING &&
|
|
pinst->inst_status != AUCPU_STA_FLUSHING) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU strm %d is not running\n", handle);
|
|
return AUCPU_DRVERR_WRONG_STATE;
|
|
}
|
|
|
|
cache_flush(pinst->reprt_buf.phys_addr, pinst->reprt_buf.size);
|
|
rpt = (struct aucpu_report_t *)pinst->reprt_buf.base;
|
|
pinst->work_state = rpt->r_status;
|
|
pinst->src_wrptr = rpt->r_src_wrptr;
|
|
pinst->src_rdptr = rpt->r_src_rdptr;
|
|
pinst->src_byte_cnt = rpt->r_src_byte_cnt;
|
|
pinst->dst_wrptr = rpt->r_dst_wrptr;
|
|
pinst->dst_byte_cnt = rpt->r_dst_byte_cnt;
|
|
upd->phy_cur_ptr = pinst->dst_wrptr;
|
|
upd->byte_cnt = pinst->dst_byte_cnt;
|
|
aucpu_pr(LOG_INFO, "AUCPU strm %d cur in @0x%x %d, out @0x%x %d\n",
|
|
handle, pinst->src_rdptr, pinst->src_byte_cnt,
|
|
rpt->r_dst_wrptr, rpt->r_dst_byte_cnt);
|
|
if (pinst->inst_status == AUCPU_STA_FLUSHING) {
|
|
if (pinst->work_state == AUCPU_STS_INST_IDLE) {
|
|
aucpu_pr(LOG_DEBUG, "AUCPU strm %d flush done\n",
|
|
handle);
|
|
pinst->inst_status = AUCPU_STA_IDLE;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_get_dst);
|
|
|
|
s32 aml_aucpu_strm_get_status(s32 handle, s32 *state, s32 *report)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
res = valid_handle2Inst(handle, pctx, &pinst);
|
|
if (res < 0) {
|
|
aucpu_pr(LOG_ERROR, "%s invalid_handle %d\n", __func__, handle);
|
|
return AUCPU_DRVERR_VALID_CONTEXT;
|
|
}
|
|
*state = pinst->inst_status;
|
|
*report = pinst->work_state;
|
|
return res;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_get_status);
|
|
|
|
s32 aml_aucpu_strm_get_load_firmware_status(void)
|
|
{
|
|
static int retry_times;
|
|
|
|
//load firmware fail, it will retry it 3 times.
|
|
if (load_firmware_status == AUCPU_ERROR_NOT_IMPLEMENTED) {
|
|
if (aucpu_pdev && retry_times < 3) {
|
|
aucpu_init_try(aucpu_pdev);
|
|
retry_times++;
|
|
}
|
|
}
|
|
return load_firmware_status;
|
|
}
|
|
EXPORT_SYMBOL(aml_aucpu_strm_get_load_firmware_status);
|
|
|
|
static void clear_aucpu_all_instances(void)
|
|
{
|
|
struct aucpu_ctx_t *pctx;
|
|
struct aucpu_inst_t *pinst;
|
|
s32 res = AUCPU_NO_ERROR;
|
|
struct aml_cmd_set *cmd;
|
|
u32 handle_idx;
|
|
|
|
pctx = s_aucpu_ctx;
|
|
if (!pctx) {
|
|
aucpu_pr(LOG_ERROR, "AUCPU driver did not init yet\n");
|
|
return;
|
|
}
|
|
if (pctx->activated_inst_num == 0)
|
|
aucpu_pr(LOG_ERROR, "AUCPU no active stream instances\n");
|
|
|
|
mutex_lock(&pctx->mutex);
|
|
cmd = &pctx->last_cmd;
|
|
cmd->cmd_type = CMD_RESET_ALL;
|
|
res = send_aucpu_cmd(cmd);
|
|
if (res < 0) // failed
|
|
aucpu_pr(LOG_ERROR, "aucpu reset all scmd failed\n");
|
|
|
|
// clear the instance now
|
|
for (handle_idx = 0; handle_idx < AUCPU_MAX_INTST_NUM; handle_idx++) {
|
|
pinst = &pctx->strm_inst[handle_idx];
|
|
if (pinst->inst_status != AUCPU_STA_NONE) {
|
|
pinst->inst_status = AUCPU_STA_NONE;
|
|
pctx->activated_inst_num--;
|
|
}
|
|
if (pctx->activated_inst_num == 0)
|
|
break;
|
|
}
|
|
mutex_unlock(&pctx->mutex);
|
|
}
|
|
|
|
static s32 aucpu_open(struct inode *inode, struct file *filp)
|
|
{
|
|
s32 r = 0;
|
|
|
|
aucpu_pr(LOG_DEBUG, "[+] %s\n", __func__);
|
|
filp->private_data = (void *)(s_aucpu_ctx);
|
|
aucpu_pr(LOG_ERROR, "Do nothing for AUCPU driver open\n");
|
|
aucpu_pr(LOG_DEBUG, "[-] %s, ret: %d\n", __func__, r);
|
|
return r;
|
|
}
|
|
|
|
static long aucpu_ioctl(struct file *filp, u32 cmd, ulong arg)
|
|
{
|
|
s32 ret = 0;
|
|
struct aucpu_ctx_t *dev = (struct aucpu_ctx_t *)filp->private_data;
|
|
|
|
aucpu_pr(LOG_ERROR, "IOCTL NO supported, dev %p ret: %d\n", dev, ret);
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long aucpu_compat_ioctl(struct file *filp, u32 cmd, ulong arg)
|
|
{
|
|
long ret;
|
|
|
|
arg = (ulong)compat_ptr(arg);
|
|
ret = aucpu_ioctl(filp, cmd, arg);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static s32 aucpu_release(struct inode *inode, struct file *filp)
|
|
{
|
|
s32 ret = 0;
|
|
|
|
aucpu_pr(LOG_DEBUG, "%s\n", __func__);
|
|
aucpu_pr(LOG_ERROR, "NO thing to do now, ret: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static const struct file_operations aucpu_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = aucpu_open,
|
|
.release = aucpu_release,
|
|
.unlocked_ioctl = aucpu_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = aucpu_compat_ioctl,
|
|
#endif
|
|
};
|
|
|
|
static ssize_t aucpu_status_show(struct class *cla,
|
|
struct class_attribute *attr, char *buf)
|
|
{
|
|
char *pbuf = buf;
|
|
|
|
pbuf += snprintf(buf, 40, "\n Opened Aucpu instances:\n");
|
|
pbuf += snprintf(buf, 80, "Total active %d\n",
|
|
s_aucpu_ctx->activated_inst_num);
|
|
return pbuf - buf;
|
|
}
|
|
|
|
static CLASS_ATTR_RO(aucpu_status);
|
|
|
|
static struct attribute *aucpu_class_attrs[] = {
|
|
&class_attr_aucpu_status.attr,
|
|
NULL
|
|
};
|
|
|
|
ATTRIBUTE_GROUPS(aucpu_class);
|
|
|
|
static struct class aucpu_class = {
|
|
.name = AUCPU_CLASS_NAME,
|
|
.class_groups = aucpu_class_groups,
|
|
};
|
|
|
|
s32 init_Aucpu_device(void)
|
|
{
|
|
s32 r = 0;
|
|
|
|
r = register_chrdev(0, AUCPU_DEV_NAME, &aucpu_fops);
|
|
if (r <= 0) {
|
|
aucpu_pr(LOG_ERROR, "register aucpu device error.\n");
|
|
return -1;
|
|
}
|
|
s_aucpu_major = r;
|
|
|
|
r = class_register(&aucpu_class);
|
|
s_register_flag = 1;
|
|
aucpu_dev = device_create(&aucpu_class, NULL, MKDEV(s_aucpu_major, 0),
|
|
NULL, AUCPU_DEV_NAME);
|
|
|
|
if (IS_ERR(aucpu_dev)) {
|
|
aucpu_pr(LOG_ERROR, "create aucpu device error.\n");
|
|
class_unregister(&aucpu_class);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
s32 uninit_Aucpu_device(void)
|
|
{
|
|
if (aucpu_dev)
|
|
device_destroy(&aucpu_class, MKDEV(s_aucpu_major, 0));
|
|
|
|
if (s_register_flag)
|
|
class_destroy(&aucpu_class);
|
|
s_register_flag = 0;
|
|
|
|
if (s_aucpu_major)
|
|
unregister_chrdev(s_aucpu_major, AUCPU_DEV_NAME);
|
|
s_aucpu_major = 0;
|
|
return 0;
|
|
}
|
|
|
|
static s32 aucpu_mem_device_init(struct reserved_mem *rmem,
|
|
struct device *dev)
|
|
{
|
|
s32 r;
|
|
struct aucpu_ctx_t *pctx;
|
|
|
|
if (!rmem) {
|
|
aucpu_pr(LOG_ERROR, "Can not obtain I/O memory, ");
|
|
aucpu_pr(LOG_ERROR, "will allocate multienc buffer!\n");
|
|
r = -EFAULT;
|
|
return r;
|
|
}
|
|
|
|
if (!rmem->base) {
|
|
aucpu_pr(LOG_ERROR, " Aucpu NO reserved mem assigned\n");
|
|
r = -EFAULT;
|
|
return r;
|
|
}
|
|
if (!s_aucpu_ctx) {
|
|
aucpu_pr(LOG_ERROR, "Aucpu did not allocate yet\n");
|
|
r = -EFAULT;
|
|
return r;
|
|
}
|
|
r = 0;
|
|
pctx = s_aucpu_ctx;
|
|
pctx->aucpu_mem.size = rmem->size;
|
|
pctx->aucpu_mem.phys_addr = (ulong)rmem->base;
|
|
pctx->aucpu_mem.base =
|
|
(ulong)phys_to_virt(pctx->aucpu_mem.phys_addr);
|
|
aucpu_pr(LOG_DEBUG, "aml_mem_device_init %d, 0x%lx\n",
|
|
pctx->aucpu_mem.size, pctx->aucpu_mem.phys_addr);
|
|
|
|
return r;
|
|
}
|
|
|
|
static s32 aucpu_init_try(struct platform_device *pdev)
|
|
{
|
|
s32 err = 0, irq, idx;
|
|
struct aucpu_ctx_t *pctx;
|
|
struct resource *res;
|
|
aucpu_pr(LOG_DEBUG, "%s\n", __func__);
|
|
|
|
s_aucpu_major = 0;
|
|
use_reserve = false;
|
|
s_aucpu_irq = -1;
|
|
s_aucpu_irq_requested = false;
|
|
s_aucpu_delaywork_requested = false;
|
|
|
|
aucpu_dev = NULL;
|
|
if (!aucpu_pdev)
|
|
aucpu_pdev = pdev;
|
|
s_register_flag = 0;
|
|
s_aucpu_register_base = 0;
|
|
s_aucpu_ctx = kzalloc(sizeof(*s_aucpu_ctx), GFP_KERNEL);
|
|
if (!s_aucpu_ctx) {
|
|
aucpu_pr(LOG_ERROR,
|
|
"failed to allocate aucpu memory context\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pctx = s_aucpu_ctx;
|
|
|
|
idx = of_reserved_mem_device_init(&pdev->dev);
|
|
if (idx != 0) {
|
|
aucpu_pr(LOG_DEBUG,
|
|
"Aucpu reserved memory config fail.\n");
|
|
} else if (pctx->aucpu_mem.phys_addr) {
|
|
use_reserve = true;
|
|
}
|
|
/* get interrupt resource */
|
|
irq = platform_get_irq_byname(pdev, "aucpu_irq");
|
|
if (irq < 0) {
|
|
aucpu_pr(LOG_ERROR, "get Aucpu irq resource error\n");
|
|
err = -EFAULT;
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
s_aucpu_irq = irq;
|
|
aucpu_pr(LOG_DEBUG, "Aucpu ->irq: %d\n", s_aucpu_irq);
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
/* if platform driver is implemented */
|
|
if (res && res->start != 0) {
|
|
pctx->aucpu_reg.phys_addr = res->start;
|
|
pctx->aucpu_reg.base =
|
|
(ulong)ioremap(res->start,
|
|
resource_size(res));
|
|
pctx->aucpu_reg.size = res->end - res->start;
|
|
|
|
aucpu_pr(LOG_DEBUG,
|
|
"aucpu base address get from platfor");
|
|
aucpu_pr(LOG_DEBUG,
|
|
" physical addr=0x%lx, base addr=0x%lx\n",
|
|
pctx->aucpu_reg.phys_addr,
|
|
pctx->aucpu_reg.base);
|
|
} else {
|
|
pctx->aucpu_reg.phys_addr = AUCPU_REG_BASE_ADDR;
|
|
pctx->aucpu_reg.base =
|
|
(ulong)ioremap(pctx->aucpu_reg.phys_addr,
|
|
AUCPU_REG_SIZE);
|
|
|
|
pctx->aucpu_reg.size = AUCPU_REG_SIZE;
|
|
aucpu_pr(LOG_DEBUG,
|
|
"aucpu base address get from defined value ");
|
|
aucpu_pr(LOG_DEBUG,
|
|
"physical addr=0x%lx, base addr=0x%lx\n",
|
|
pctx->aucpu_reg.phys_addr,
|
|
pctx->aucpu_reg.base);
|
|
}
|
|
|
|
s_aucpu_register_base = pctx->aucpu_reg.base;
|
|
|
|
/* get the major number of the character device */
|
|
if (init_Aucpu_device()) {
|
|
err = -EBUSY;
|
|
aucpu_pr(LOG_ERROR, "could not allocate major number\n");
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
aucpu_pr(LOG_INFO, "SUCCESS alloc_chrdev_region\n");
|
|
pctx->aucpu_device = aucpu_dev;
|
|
|
|
/* init the local buffers */
|
|
if (init_Aucpu_local_buf(&pdev->dev /*aucpu_dev */)) {
|
|
err = -EBUSY;
|
|
aucpu_pr(LOG_ERROR, "did not allocate enough memory\n");
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
/* load FW and start AUCPU */
|
|
load_firmware_status = load_start_aucpu_fw(&pdev->dev /*aucpu_dev */);
|
|
if (load_firmware_status) {
|
|
aucpu_pr(LOG_ERROR, "load start_aucpu_fw fail\n");
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
aucpu_pr(LOG_INFO, "aucpu fw version: 0x%x\n",
|
|
ReadAucpuRegister(VERSION_ADDR));
|
|
|
|
mutex_init(&pctx->mutex);
|
|
/*setup the DEBUG buffer */
|
|
setup_debug_polling();
|
|
|
|
INIT_DELAYED_WORK(&pctx->dbg_work, do_polling_work);
|
|
s_aucpu_delaywork_requested = true;
|
|
|
|
err = request_irq(s_aucpu_irq, aucpu_irq_handler, 0,
|
|
"Aucpu-irq", (void *)s_aucpu_ctx);
|
|
if (err) {
|
|
aucpu_pr(LOG_ERROR, "Failed to register irq handler\n");
|
|
goto ERROR_PROVE_DEVICE;
|
|
}
|
|
s_aucpu_irq_requested = true;
|
|
schedule_delayed_work(&pctx->dbg_work, DBG_POLL_TIME);
|
|
// aucpu_pdev = pdev;
|
|
aucpu_pr(LOG_ERROR, "Amlogic Aucpu driver installed\n");
|
|
return 0;
|
|
|
|
ERROR_PROVE_DEVICE:
|
|
if (s_aucpu_irq_requested) {
|
|
free_irq(s_aucpu_irq, s_aucpu_ctx);
|
|
s_aucpu_irq = -1;
|
|
s_aucpu_irq_requested = false;
|
|
}
|
|
if (s_aucpu_delaywork_requested) {
|
|
cancel_delayed_work_sync(&pctx->dbg_work);
|
|
flush_scheduled_work();
|
|
s_aucpu_delaywork_requested = false;
|
|
}
|
|
if (s_aucpu_register_base)
|
|
iounmap((void *)s_aucpu_register_base);
|
|
|
|
uninit_Aucpu_device();
|
|
aucpu_dev = NULL;
|
|
kfree(s_aucpu_ctx);
|
|
s_aucpu_ctx = NULL;
|
|
return err;
|
|
}
|
|
|
|
static s32 aucpu_probe(struct platform_device *pdev)
|
|
{
|
|
return aucpu_init_try(pdev);
|
|
}
|
|
|
|
static s32 aucpu_remove(struct platform_device *pdev)
|
|
{
|
|
aucpu_pr(LOG_DEBUG, "aupu_remove\n");
|
|
|
|
clear_aucpu_all_instances(); //make sure aucpu is idle
|
|
stop_aucpu_fw(&pdev->dev /*aucpu_dev */);
|
|
|
|
if (s_aucpu_irq_requested) {
|
|
free_irq(s_aucpu_irq, s_aucpu_ctx);
|
|
s_aucpu_irq = -1;
|
|
s_aucpu_irq_requested = false;
|
|
}
|
|
if (s_aucpu_delaywork_requested) {
|
|
cancel_delayed_work_sync(&s_aucpu_ctx->dbg_work);
|
|
flush_scheduled_work();
|
|
s_aucpu_delaywork_requested = false;
|
|
}
|
|
|
|
uninit_Aucpu_local_buf(&pdev->dev);
|
|
|
|
if (s_aucpu_register_base)
|
|
iounmap((void *)s_aucpu_register_base);
|
|
|
|
uninit_Aucpu_device();
|
|
kfree(s_aucpu_ctx);
|
|
s_aucpu_ctx = NULL;
|
|
aucpu_dev = NULL;
|
|
aucpu_pdev = NULL;
|
|
aucpu_pr(LOG_DEBUG, "aupu_remove done\n");
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id aml_aucpu_dt_match[] = {
|
|
{
|
|
.compatible = "amlogic, aucpu",
|
|
},
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver aucpu_driver = {
|
|
.driver = {
|
|
.name = AUCPU_PLATFORM_DEVICE_NAME,
|
|
.of_match_table = aml_aucpu_dt_match,
|
|
},
|
|
.probe = aucpu_probe,
|
|
.remove = aucpu_remove,
|
|
};
|
|
|
|
s32 __init aucpu_init(void)
|
|
{
|
|
s32 res;
|
|
|
|
aucpu_pr(LOG_DEBUG, "%s\n", __func__);
|
|
|
|
res = platform_driver_register(&aucpu_driver);
|
|
aucpu_pr(LOG_INFO, "end %s result=0x%x\n", __func__, res);
|
|
return res;
|
|
}
|
|
|
|
void __exit aucpu_exit(void)
|
|
{
|
|
aucpu_pr(LOG_DEBUG, "%s\n", __func__);
|
|
platform_driver_unregister(&aucpu_driver);
|
|
}
|
|
|
|
static const struct reserved_mem_ops rmem_aucpu_ops = {
|
|
.device_init = aucpu_mem_device_init,
|
|
};
|
|
|
|
static s32 __init aucpu_mem_setup(struct reserved_mem *rmem)
|
|
{
|
|
rmem->ops = &rmem_aucpu_ops;
|
|
aucpu_pr(LOG_DEBUG, "Aucpu reserved mem setup.\n");
|
|
return 0;
|
|
}
|
|
|
|
module_param(print_level, uint, 0664);
|
|
MODULE_PARM_DESC(print_level, "\n print_level\n");
|
|
module_param(dbg_level, uint, 0664);
|
|
MODULE_PARM_DESC(dbg_level, "\n dbg_level\n");
|
|
|
|
RESERVEDMEM_OF_DECLARE(aml_aucpu, "aml, Aucpu-mem", aucpu_mem_setup);
|