blob: 985463b0b0de7eeb1c04ba7d00872b22fae08d9a [file] [log] [blame]
/*
* drivers/amlogic/hifi4dsp/hifi4dsp_module.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
//#define DEBUG
#define pr_fmt(fmt) "hifi4dsp: " fmt
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/reset.h>
#include <linux/dma-mapping.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_reserved_mem.h>
#include <linux/cma.h>
#include <linux/dma-contiguous.h>
#include <linux/vmalloc.h>
#include <linux/clk.h>
#include <asm/cacheflush.h>
#include <linux/amlogic/major.h>
#include "hifi4dsp_api.h"
#include "hifi4dsp_priv.h"
#include "hifi4dsp_firmware.h"
#include "hifi4dsp_dsp.h"
#include "dsp_top.h"
struct reg_iomem_t g_regbases;
unsigned int boot_sram_addr;
unsigned int boot_sram_size;
static unsigned int bootlocation;
unsigned int dspcount;
static struct reserved_mem hifi4_rmem = {.base = 0, .size = 0};
#define HIFI4DSP_MAX_CNT 2
struct hifi4dsp_priv *hifi4dsp_p[HIFI4DSP_MAX_CNT];
static int hifi4dsp_driver_load_fw(struct hifi4dsp_dsp *dsp);
static int hifi4dsp_driver_load_2fw(struct hifi4dsp_dsp *dsp);
static int hifi4dsp_driver_reset(struct hifi4dsp_dsp *dsp);
static int hifi4dsp_driver_dsp_start(struct hifi4dsp_dsp *dsp);
static int hifi4dsp_driver_dsp_stop(struct hifi4dsp_dsp *dsp);
static int hifi4dsp_driver_dsp_sleep(struct hifi4dsp_dsp *dsp);
static int hifi4dsp_driver_dsp_wake(struct hifi4dsp_dsp *dsp);
static int hifi4dsp_miscdev_open(struct inode *inode, struct file *fp)
{
int minor = iminor(inode);
int major = imajor(inode);
struct hifi4dsp_priv *priv;
struct miscdevice *c;
struct hifi4dsp_miscdev_t *pmscdev_t;
c = fp->private_data;
pr_debug("%s,%s,major=%d,minor=%d\n", __func__,
c->name,
major,
minor);
if (strcmp(c->name, "hifi4dsp0") == 0)
priv = hifi4dsp_p[0];
else if (strcmp(c->name, "hifi4dsp1") == 0)
priv = hifi4dsp_p[1];
else
priv = NULL;
pmscdev_t = container_of(c, struct hifi4dsp_miscdev_t, dsp_miscdev);
if (!pmscdev_t) {
pr_err("hifi4dsp_miscdev_t == NULL\n");
return -ENOMEM;
}
pmscdev_t->priv = priv;
if (!pmscdev_t->priv) {
pr_err("hifi4dsp_miscdev_t pointer *pmscdev_t==NULL\n");
return -ENOMEM;
}
fp->private_data = priv;
pr_debug("%s,%s,major=%d,minor=%d\n", __func__,
priv->dev->kobj.name,
major,
minor);
return 0;
}
static int hifi4dsp_miscdev_release(struct inode *inode, struct file *fp)
{
return 0;
}
static long hifi4dsp_miscdev_unlocked_ioctl(struct file *fp, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
struct hifi4dsp_priv *priv;
struct hifi4dsp_dsp *dsp;
struct hifi4dsp_info_t *info;
struct hifi4_shm_info_t shminfo;
void __user *argp = (void __user *)arg;
unsigned int hifimemend;
pr_debug("%s\n", __func__);
if (!fp->private_data) {
pr_err("%s error:fp->private_data is null", __func__);
return -ENOMEM;
}
priv = (struct hifi4dsp_priv *)fp->private_data;
if (!priv) {
pr_err("%s error: hifi4dsp_priv is null", __func__);
return -ENOMEM;
}
dsp = priv->dsp;
if (!dsp) {
pr_err("%s hifi4dsp_dsp is null:\n", __func__);
return -ENOMEM;
}
if (!priv->dsp->dsp_fw) {
pr_err("%s hifi4dsp_firmware is null:\n", __func__);
return -ENOMEM;
}
if (!priv->dsp->fw) {
pr_err("%s firmware is null:\n", __func__);
return -ENOMEM;
}
pr_debug("%s %s\n", __func__, priv->dev->kobj.name);
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
hifimemend = hifi4_rmem.base + hifi4_rmem.size;
switch (cmd) {
case HIFI4DSP_TEST:
pr_debug("%s HIFI4DSP_TEST\n", __func__);
break;
case HIFI4DSP_LOAD:
pr_debug("%s HIFI4DSP_LOAD\n", __func__);
ret = copy_from_user(info, argp,
sizeof(struct hifi4dsp_info_t));
pr_debug("\ninfo->fw_name : %s\n", info->fw_name);
priv->dsp->info = info;
hifi4dsp_driver_load_fw(priv->dsp);
break;
case HIFI4DSP_2LOAD:
pr_debug("%s HIFI4DSP_2LOAD\n", __func__);
ret = copy_from_user(info, argp,
sizeof(struct hifi4dsp_info_t));
priv->dsp->info = info;
hifi4dsp_driver_load_2fw(priv->dsp);
break;
case HIFI4DSP_RESET:
pr_debug("%s HIFI4DSP_RESET\n", __func__);
ret = copy_from_user(info, argp,
sizeof(struct hifi4dsp_info_t));
priv->dsp->info = info;
hifi4dsp_driver_reset(priv->dsp);
break;
case HIFI4DSP_START:
pr_debug("%s HIFI4DSP_START\n", __func__);
ret = copy_from_user(info, argp,
sizeof(struct hifi4dsp_info_t));
priv->dsp->info = info;
hifi4dsp_driver_dsp_start(priv->dsp);
break;
case HIFI4DSP_STOP:
pr_debug("%s HIFI4DSP_STOP\n", __func__);
ret = copy_from_user(info, argp,
sizeof(struct hifi4dsp_info_t));
priv->dsp->info = info;
hifi4dsp_driver_dsp_stop(priv->dsp);
break;
case HIFI4DSP_SLEEP:
pr_debug("%s HIFI4DSP_SLEEP\n", __func__);
ret = copy_from_user(info, argp,
sizeof(struct hifi4dsp_info_t));
priv->dsp->info = info;
hifi4dsp_driver_dsp_sleep(priv->dsp);
break;
case HIFI4DSP_WAKE:
pr_debug("%s HIFI4DSP_WAKE\n", __func__);
ret = copy_from_user(info, argp,
sizeof(struct hifi4dsp_info_t));
priv->dsp->info = info;
hifi4dsp_driver_dsp_wake(priv->dsp);
break;
case HIFI4DSP_GET_INFO:
pr_debug("%s HIFI4DSP_GET_INFO\n", __func__);
ret = copy_from_user(info, argp,
sizeof(struct hifi4dsp_info_t));
pr_debug("%s HIFI4DSP_GET_INFO %s\n", __func__,
info->fw_name);
strcpy(info->fw_name, "1234abcdef");
info->phy_addr = priv->pdata->fw_paddr;
info->size = priv->pdata->fw_max_size;
ret = copy_to_user(argp, info,
sizeof(struct hifi4dsp_info_t));
pr_debug("%s HIFI4DSP_GET_INFO %s\n", __func__,
info->fw_name);
break;
case HIFI4DSP_SHM_CLEAN:
ret = copy_from_user(&shminfo, argp, sizeof(shminfo));
pr_debug("%s clean cache, addr:%lu, size:%zu\n",
__func__, shminfo.addr, shminfo.size);
if ((shminfo.addr < hifi4_rmem.base) ||
(shminfo.addr + shminfo.size > hifimemend)) {
kfree(info);
return -EINVAL;
}
dma_sync_single_for_device
(priv->dev,
shminfo.addr,
shminfo.size,
DMA_TO_DEVICE);
break;
case HIFI4DSP_SHM_INV:
ret = copy_from_user(&shminfo, argp, sizeof(shminfo));
pr_debug("%s invalidate cache, addr:%lu, size:%zu\n",
__func__, shminfo.addr, shminfo.size);
if ((shminfo.addr < hifi4_rmem.base) ||
(shminfo.addr + shminfo.size > hifimemend)) {
kfree(info);
return -EINVAL;
}
dma_sync_single_for_device
(priv->dev,
shminfo.addr,
shminfo.size,
DMA_FROM_DEVICE);
break;
default:
pr_err("%s ioctl CMD error\n", __func__);
break;
}
kfree(info);
priv->dsp->info = NULL;
return ret;
}
#ifdef CONFIG_COMPAT
static long hifi4dsp_miscdev_compat_ioctl(struct file *filp,
unsigned int cmd, unsigned long args)
{
long ret;
args = (unsigned long)compat_ptr(args);
ret = hifi4dsp_miscdev_unlocked_ioctl(filp, cmd, args);
return ret;
}
#endif
static int hifi4dsp_miscdev_mmap(struct file *fp, struct vm_area_struct *vma)
{
int ret = 0;
unsigned long phys_page_addr = 0;
unsigned long size = 0;
struct hifi4dsp_priv *priv = NULL;
if (NULL == (void *)vma) {
pr_err("input error: vma is NULL\n");
return -1;
}
pr_debug("%s\n", __func__);
if (!fp->private_data) {
pr_err("%s error:fp->private_data is null", __func__);
return -1;
}
priv = (struct hifi4dsp_priv *)fp->private_data;
phys_page_addr = (u64)priv->pdata->fw_paddr >> PAGE_SHIFT;
size = ((unsigned long)vma->vm_end - (unsigned long)vma->vm_start);
pr_debug("vma=0x%pK.\n", vma);
pr_debug("size=%ld, vma->vm_start=%ld, end=%ld.\n",
((unsigned long)vma->vm_end - (unsigned long)vma->vm_start),
(unsigned long)vma->vm_start, (unsigned long)vma->vm_end);
pr_debug("phys_page_addr=%ld.\n", (unsigned long)phys_page_addr);
vma->vm_page_prot = PAGE_SHARED;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
if (size > priv->pdata->fw_max_size)
size = priv->pdata->fw_max_size;
ret = remap_pfn_range(vma,
vma->vm_start,
phys_page_addr, size, vma->vm_page_prot);
if (ret != 0) {
pr_err("remap_pfn_range ret=%d\n", ret);
return -1;
}
return ret;
}
static int hifi4dsp_driver_load_fw(struct hifi4dsp_dsp *dsp)
{
int err = 0;
struct hifi4dsp_firmware *new_dsp_fw;
struct hifi4dsp_info_t *info = dsp->info;
if (strlen(info->fw_name) == 0)
return -1;
pr_debug("hifi4dsp_info_t, name=%s, paddr=0x%llx\n",
info->fw_name,
(unsigned long long)info->phy_addr);
new_dsp_fw = hifi4dsp_fw_register(dsp, info->fw_name);
if (!new_dsp_fw) {
pr_err("register firmware:%s error\n", info->fw_name);
return -1;
}
dsp->dsp_fw = new_dsp_fw; /*set newest fw as def fw of dsp*/
strcpy(new_dsp_fw->name, info->fw_name);
if (info->phy_addr != 0) { /*to be improved*/
//info->phy_addr may !=0, but illegal
new_dsp_fw->paddr = info->phy_addr;
/*TODO*/
/*new_dsp_fw->buf = phys_to_virt(new_dsp_fw->paddr);*/
new_dsp_fw->buf = dsp->pdata->fw_buf;
} else {
new_dsp_fw->paddr = dsp->pdata->fw_paddr;
new_dsp_fw->buf = dsp->pdata->fw_buf;
}
pr_debug("new hifi4dsp_firmware, name=%s, paddr=0x%llx\n",
new_dsp_fw->name,
(unsigned long long)new_dsp_fw->paddr);
hifi4dsp_fw_load(new_dsp_fw);
return err;
}
static int hifi4dsp_driver_load_2fw(struct hifi4dsp_dsp *dsp)
{
int err = 0;
struct hifi4dsp_firmware *new_dsp_sram_fw;
struct hifi4dsp_firmware *new_dsp_ddr_fw;
struct hifi4dsp_info_t *info = dsp->info;
if (strlen(info->fw1_name) == 0)
return -1;
/*load dspboot_sdram.bin to ddr*/
pr_debug("info->fw1_name : %s\n", info->fw1_name);
new_dsp_ddr_fw = hifi4dsp_fw_register(dsp, info->fw1_name);
if (!new_dsp_ddr_fw) {
pr_err("register firmware:%s error\n", info->fw1_name);
return -1;
}
dsp->dsp_fw = new_dsp_ddr_fw; /*set newest fw as def fw of dsp*/
strcpy(new_dsp_ddr_fw->name, info->fw1_name);
new_dsp_ddr_fw->paddr = dsp->pdata->fw_paddr;
new_dsp_ddr_fw->buf = dsp->pdata->fw_buf;
pr_debug("new_dsp_ddr_fw, name=%s, paddr=0x%llx, virtual addr:0x%lx\n",
new_dsp_ddr_fw->name,
(unsigned long long)new_dsp_ddr_fw->paddr,
(unsigned long)new_dsp_ddr_fw->buf);
hifi4dsp_fw_load(new_dsp_ddr_fw);
/*load dspboot_sram.bin to sram*/
if (strlen(info->fw2_name) == 0)
return -1;
pr_debug("info->fw2_name : %s\n", info->fw2_name);
new_dsp_sram_fw = hifi4dsp_fw_register(dsp, info->fw2_name);
if (!new_dsp_sram_fw) {
pr_err("register firmware:%s error\n", info->fw2_name);
return -1;
}
dsp->dsp_fw = new_dsp_sram_fw;
strcpy(new_dsp_sram_fw->name, info->fw2_name);
new_dsp_sram_fw->paddr = boot_sram_addr;
new_dsp_sram_fw->buf = g_regbases.sram_base;
hifi4dsp_fw_sram_load(new_dsp_sram_fw);
return err;
}
static int hifi4dsp_driver_reset(struct hifi4dsp_dsp *dsp)
{
struct hifi4dsp_info_t *info;
pr_debug("%s\n", __func__);
if (!dsp->info)
info = (struct hifi4dsp_info_t *)dsp->info;
dsp->info = NULL;
return 0;
}
static int hifi4dsp_driver_dsp_boot(struct hifi4dsp_dsp *dsp)
{
struct hifi4dsp_info_t *info;
info = (struct hifi4dsp_info_t *)dsp->info;
pr_debug("%s\n", __func__);
dsp->info = NULL;
return 0;
}
static int hifi4dsp_driver_dsp_start(struct hifi4dsp_dsp *dsp)
{
struct hifi4dsp_info_t *info;
pr_debug("%s\n", __func__);
info = (struct hifi4dsp_info_t *)dsp->info;
pr_debug("dsp_id: %d\n", dsp->id);
pr_debug("dsp_freqence: %d Hz\n", dsp->freq);
pr_debug("dsp_start_addr: 0x%llx\n",
(unsigned long long)dsp->dsp_fw->paddr);
if (!dsp->dsp_clk) {
pr_err("dsp_clk=NULL\n");
return -EINVAL;
}
clk_set_rate(dsp->dsp_clk, dsp->freq);
clk_prepare_enable(dsp->dsp_clk);
if (bootlocation == 1)
soc_dsp_bootup(dsp->id, dsp->dsp_fw->paddr, dsp->freq);
else if (bootlocation == 2)
soc_dsp_bootup(dsp->id, boot_sram_addr, dsp->freq);
dsp->info = NULL;
return 0;
}
static int hifi4dsp_driver_dsp_clk_off(struct hifi4dsp_dsp *dsp)
{
if (!dsp->dsp_clk) {
pr_err("dsp_clk=NULL\n");
return -EINVAL;
}
clk_disable_unprepare(dsp->dsp_clk);
return 0;
}
static int hifi4dsp_driver_dsp_stop(struct hifi4dsp_dsp *dsp)
{
struct hifi4dsp_info_t *info;
info = (struct hifi4dsp_info_t *)dsp->info;
pr_debug("%s\n", __func__);
soc_dsp_poweroff(info->id);
hifi4dsp_driver_dsp_clk_off(dsp);
dsp->info = NULL;
return 0;
}
static int hifi4dsp_driver_dsp_sleep(struct hifi4dsp_dsp *dsp)
{
struct hifi4dsp_info_t *info;
info = (struct hifi4dsp_info_t *)dsp->info;
pr_debug("%s\n", __func__);
dsp->info = NULL;
return 0;
}
static int hifi4dsp_driver_dsp_wake(struct hifi4dsp_dsp *dsp)
{
struct hifi4dsp_info_t *info;
info = (struct hifi4dsp_info_t *)dsp->info;
pr_debug("%s\n", __func__);
dsp->info = NULL;
return 0;
}
/*transfer param from pdata to dsp*/
static int hifi4dsp_driver_resource_map(struct hifi4dsp_dsp *dsp,
struct hifi4dsp_pdata *pdata)
{
int ret = 0;
if (!pdata) {
pr_err("%s error\n", __func__);
ret = -1;
}
return ret;
}
static int hifi4dsp_driver_init(struct hifi4dsp_dsp *dsp,
struct hifi4dsp_pdata *pdata)
{
struct device *dev;
int ret = -ENODEV;
dev = dsp->dev;
pr_debug("%s\n", __func__);
ret = hifi4dsp_driver_resource_map(dsp, pdata);
if (ret < 0) {
dev_err(dev, "failed to map resources\n");
return ret;
}
//ret = dma_coerce_mask_and_coherent(dsp->dma_dev, DMA_BIT_MASK(32));
if (ret)
return ret;
/* enable interrupt from both arm sides and dsp side */
/* todo */
return 0;
}
static int hifi4dsp_load_and_parse_fw(struct hifi4dsp_firmware *dsp_fw,
void *pinfo)
{
struct hifi4dsp_info_t *info;
info = (struct hifi4dsp_info_t *)pinfo;
pr_debug("%s\n", __func__);
hifi4dsp_driver_load_fw(dsp_fw->dsp);
return 0;
}
struct hifi4dsp_ops;
struct hifi4dsp_ops hifi4dsp_driver_dsp_ops = {
.boot = hifi4dsp_driver_dsp_boot,
.reset = hifi4dsp_driver_reset,
.sleep = hifi4dsp_driver_dsp_sleep,
.wake = hifi4dsp_driver_dsp_wake,
.init = hifi4dsp_driver_init,
.parse_fw = hifi4dsp_load_and_parse_fw,
};
static struct hifi4dsp_dsp_device hifi4dsp_dev = {
.ops = &hifi4dsp_driver_dsp_ops,
};
struct hifi4dsp_priv *hifi4dsp_privdata()
{
return hifi4dsp_p[0];
}
static int hifi4dsp_platform_remove(struct platform_device *pdev)
{
struct hifi4dsp_priv *priv;
int id = 0, dsp_cnt = 0;
int ret = 0;
ret = of_property_read_u32(pdev->dev.of_node, "dsp-cnt", &dsp_cnt);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve dsp-cnt\n");
ret = -EINVAL;
goto dsp_cnt_error;
}
priv = hifi4dsp_privdata();
for (id = 0; id < dsp_cnt; id++) {
if (!priv)
continue;
if (priv->dev)
device_destroy(priv->class, priv->dev->devt);
priv += 1;
}
priv = NULL;
for (id = 0; id < dsp_cnt; id++)
hifi4dsp_p[id] = NULL;
return 0;
dsp_cnt_error:
return ret;
}
static const struct file_operations hifi4dsp_miscdev_fops = {
.owner = THIS_MODULE,
.open = hifi4dsp_miscdev_open,
.read = NULL,
.write = NULL,
.release = hifi4dsp_miscdev_release,
.unlocked_ioctl = hifi4dsp_miscdev_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = hifi4dsp_miscdev_compat_ioctl,
#endif
.mmap = hifi4dsp_miscdev_mmap,
};
static struct miscdevice hifi4dsp_miscdev[] = {
{
.minor = MISC_DYNAMIC_MINOR,
.name = "hifi4dsp0",
.fops = &hifi4dsp_miscdev_fops,
},
{
.minor = MISC_DYNAMIC_MINOR,
.name = "hifi4dsp1",
.fops = &hifi4dsp_miscdev_fops,
}
};
static void *mm_vmap(phys_addr_t phys, unsigned long size)
{
u32 offset, npages;
struct page **pages = NULL;
pgprot_t pgprot = PAGE_KERNEL;
void *vaddr;
int i;
offset = offset_in_page(phys);
npages = DIV_ROUND_UP(size + offset, PAGE_SIZE);
pages = vmalloc(sizeof(struct page *) * npages);
if (!pages)
return NULL;
for (i = 0; i < npages; i++) {
pages[i] = phys_to_page(phys);
phys += PAGE_SIZE;
}
vaddr = vmap(pages, npages, VM_MAP, pgprot);
if (!vaddr) {
pr_err("vmaped fail, size: %d\n",
npages << PAGE_SHIFT);
vfree(pages);
return NULL;
}
vfree(pages);
pr_debug("[HIGH-MEM-MAP] pa(%lx) to va(%p), size: %d\n",
(unsigned long)phys, vaddr, npages << PAGE_SHIFT);
return vaddr;
}
/*of read clk_gate, clk*/
static inline int of_read_dsp_irq(struct platform_device *pdev, int dsp_id)
{
int irq = -1;
if (dsp_id == 0)
irq = of_irq_get_byname(pdev->dev.of_node, "irq_frm_dspa");
else if (dsp_id == 1)
irq = of_irq_get_byname(pdev->dev.of_node, "irq_frm_dspb");
pr_debug("%s %s irq=%d\n", __func__,
(irq < 0) ? "error" : "successful", irq);
return irq;
}
/*of read clk_gate, clk*/
static inline struct clk *of_read_dsp_clk(struct platform_device *pdev,
int dsp_id)
{
struct clk *p_clk = NULL;
char clk_name[20];
if (dsp_id == 0) {
strcpy(clk_name, "dspa_clk");
p_clk = devm_clk_get(&pdev->dev, clk_name);
} else if (dsp_id == 1) {
strcpy(clk_name, "dspb_clk");
p_clk = devm_clk_get(&pdev->dev, clk_name);
}
if (!p_clk)
pr_err("%s %s error\n", __func__, clk_name);
return p_clk;
}
void get_dsp_baseaddr(struct platform_device *pdev)
{
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get dspa base address\n");
goto error;
}
g_regbases.dspa_addr = devm_ioremap_resource(&pdev->dev, res);
g_regbases.rega_size = resource_size(res);
if (dspcount == 2) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
dev_err(&pdev->dev, "failed to get dspb base address\n");
goto error;
}
g_regbases.dspb_addr = devm_ioremap_resource(&pdev->dev, res);
g_regbases.regb_size = resource_size(res);
}
error:
return;
}
int of_read_dsp_cnt(struct platform_device *pdev)
{
int ret;
int dsp_cnt;
ret = of_property_read_u32(pdev->dev.of_node, "dsp-cnt", &dsp_cnt);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve dsp-cnt\n");
return -EINVAL;
}
pr_debug("%s of read dsp-cnt=%d\n", __func__, dsp_cnt);
return dsp_cnt;
}
static int hifi4dsp_platform_probe(struct platform_device *pdev)
{
int ret = 0;
int i = 0, id = 0;
int dsp_cnt = 0;
unsigned int dspoffset[2];
unsigned int dsp_clkfreq[2];
struct hifi4dsp_priv *priv = NULL;
struct hifi4dsp_dsp *dsp = NULL;
struct hifi4dsp_pdata *pdata = NULL;
struct hifi4dsp_miscdev_t *p_dsp_miscdev = NULL;
struct miscdevice *pmscdev = NULL;
phys_addr_t hifi4base;
int hifi4size;
void *fw_addr = NULL;
struct hifi4dsp_firmware *dsp_firmware = NULL;
struct firmware *fw = NULL;
struct device_node *np = NULL;
struct clk *dsp_clk = NULL;
struct hifi4dsp_info_t *hifi_info = NULL;
struct page *cma_pages = NULL;
unsigned int reservememsize;
np = pdev->dev.of_node;
/*dsp boot offset */
dsp_cnt = of_read_dsp_cnt(pdev);
if (dsp_cnt > 0) {
dspcount = dsp_cnt;
ret = of_property_read_u32(np, "dspaoffset", &dspoffset[0]);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve dspaoffset\n");
goto err1;
}
pr_debug("of read dspaoffset=0x%08x\n", dspoffset[0]);
ret = of_property_read_u32(np, "dspa_clkfreq", &dsp_clkfreq[0]);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve dspaclkval\n");
goto err1;
}
pr_debug("of read dspaclkval=0x%08x\n", dsp_clkfreq[0]);
if (dsp_cnt == 2) {
ret = of_property_read_u32(np, "dspboffset",
&dspoffset[1]);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve dspboffset\n");
goto err1;
}
pr_debug("of read dspboffset=0x%08x\n", dspoffset[1]);
ret = of_property_read_u32(np, "dspb_clkfreq",
&dsp_clkfreq[1]);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve dspbclkval\n");
goto err1;
}
pr_debug("of read dspbclkval=0x%08x\n", dsp_clkfreq[1]);
}
} else {
goto err1;
}
/*boot from DDR or SRAM or ...*/
ret = of_property_read_u32(np, "bootlocation", &bootlocation);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve bootlocation\n");
goto err1;
}
pr_debug("%s of read dsp bootlocation=%x\n", __func__, bootlocation);
if (bootlocation == 1) {
pr_info("Dsp boot from DDR !\n");
} else if (bootlocation == 2) {
ret = of_property_read_u32(np, "boot_sram_addr",
&boot_sram_addr);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve boot_sram_addr\n");
goto err1;
}
ret = of_property_read_u32(np, "boot_sram_size",
&boot_sram_size);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve boot_sram_size\n");
goto err1;
}
pr_info("Dsp boot from SRAM !boot addr : 0x%08x, allocate size: 0x%08x\n",
boot_sram_addr, boot_sram_size);
g_regbases.sram_base = ioremap_nocache(boot_sram_addr,
boot_sram_size);
} else {
pr_err("get wrong bootlocation....\n");
}
/*init hifi4dsp_dsp*/
dsp = devm_kcalloc(&pdev->dev, dsp_cnt, sizeof(*dsp), GFP_KERNEL);
if (!dsp)
goto err2;
/*init hifi4dsp_info_t*/
hifi_info = devm_kzalloc(&pdev->dev, sizeof(*hifi_info), GFP_KERNEL);
if (!hifi_info)
goto hifi_info_malloc_error;
/*init miscdev_t, miscdevice*/
p_dsp_miscdev = devm_kcalloc(&pdev->dev, dsp_cnt,
sizeof(struct hifi4dsp_miscdev_t),
GFP_KERNEL);
if (!p_dsp_miscdev) {
HIFI4DSP_PRNT("kzalloc for p_dsp_miscdev error\n");
goto miscdev_malloc_error;
}
if (!&p_dsp_miscdev->dsp_miscdev)
pr_info("register dsp _p_dsp_miscdev->dsp_miscdev alloc error\n");
else
pr_info("register dsp _p_dsp_miscdev->dsp_miscdev alloc success");
/*init hifi4dsp_priv*/
priv = devm_kcalloc(&pdev->dev, dsp_cnt, sizeof(struct hifi4dsp_priv),
GFP_KERNEL);
if (!priv) {
HIFI4DSP_PRNT("kzalloc for hifi4dsp_priv error\n");
goto priv_malloc_error;
}
/*init hifi4dsp_pdata*/
pdata = devm_kcalloc(&pdev->dev, dsp_cnt,
sizeof(struct hifi4dsp_pdata), GFP_KERNEL);
if (!pdata) {
HIFI4DSP_PRNT("kzalloc for hifi4dsp_pdata error\n");
goto pdata_malloc_error;
}
/*init dsp firmware*/
dsp_firmware = devm_kzalloc(&pdev->dev,
sizeof(*dsp_firmware), GFP_KERNEL);
if (!dsp_firmware) {
HIFI4DSP_PRNT("kzalloc for firmware error\n");
goto dsp_firmware_malloc_error;
}
/*init real dsp firmware*/
fw = devm_kzalloc(&pdev->dev, sizeof(*fw), GFP_KERNEL);
if (!fw)
goto real_fw_malloc_error;
/*get regbase*/
get_dsp_baseaddr(pdev);
/*get reserve memory*/
ret = of_reserved_mem_device_init(&pdev->dev);
if (ret) {
pr_err("reserved memory init fail:%d\n", ret);
ret = -ENOMEM;
goto err3;
}
ret = of_property_read_u32(np, "reservesize", &reservememsize);
if (ret < 0) {
dev_err(&pdev->dev, "Can't retrieve reservesize\n");
goto err1;
}
pr_debug("of read reservememsize=0x%08x\n", reservememsize);
hifi4_rmem.size = reservememsize;
cma_pages =
dma_alloc_from_contiguous(&pdev->dev,
PAGE_ALIGN(reservememsize) >> PAGE_SHIFT, 0);
pr_info("cma alloc hifi4 mem region success!\n");
for (i = 0; i < dsp_cnt; i++) {
id = i;
p_dsp_miscdev += i;
priv += i;
pdata += i;
dsp += i;
pr_info("\nregister dsp-%d start\n", id);
/*get boot address*/
if (cma_pages) {
hifi4_rmem.base = page_to_phys(cma_pages);
pr_debug("reserved_mem :base:0x%llx, size:0x%lx\n",
(unsigned long long)hifi4_rmem.base,
(unsigned long)hifi4_rmem.size);
hifi4base = hifi4_rmem.base + (id == 0 ?
dspoffset[0] :
dspoffset[1]);
if (dsp_cnt == 2) {
hifi4size =
(id == 0 ?
(dspoffset[1] - dspoffset[0]) :
(hifi4_rmem.size - dspoffset[1]));
} else {
hifi4size = hifi4_rmem.size;
}
if (!PageHighMem(cma_pages)) {
fw_addr = phys_to_virt(hifi4base);
pr_info("kernel addr map1 phys:0x%lx->virt:0x%lx\n",
(unsigned long)hifi4base,
(unsigned long)fw_addr);
} else {
fw_addr = mm_vmap(hifi4base, hifi4size);
pr_info("kernel addr map2 phys:0x%lx->virt:0x%lx\n",
(unsigned long)hifi4base,
(unsigned long)fw_addr);
}
} else {
goto err3;
}
pr_debug("hifi4dsp%d, firmware :base:0x%llx, size:0x%x, virt:%lx\n",
id,
(unsigned long long)hifi4base,
hifi4size,
(unsigned long)fw_addr);
/*init miscdevice pmscdev and register*/
memcpy(&p_dsp_miscdev->dsp_miscdev, &hifi4dsp_miscdev[id],
sizeof(struct miscdevice));
pmscdev = &p_dsp_miscdev->dsp_miscdev;
//pmscdev = &hifi4dsp_miscdev[id];
ret = misc_register(pmscdev);
if (ret) {
pr_err("register vad_miscdev error\n");
goto err3;
}
/*add miscdevice to priv*/
priv->dev = pmscdev->this_device;
/*init hifi4dsp_firmware and add to hifi4dsp_dsp */
dsp_firmware->paddr = hifi4base;
dsp_firmware->size = hifi4size;
dsp_firmware->id = id;
strcpy(dsp_firmware->name, "amlogic_firmware");
/*init hifi4dsp_miscdev_t ->priv*/
p_dsp_miscdev->priv = priv;
if (!(p_dsp_miscdev->priv))
pr_info("register dsp _p_dsp_miscdev->priv alloc error");
else
pr_info("register dsp _p_dsp_miscdev->priv alloc success");
/*of read clk and add to priv*/
dsp_clk = of_read_dsp_clk(pdev, id);
priv->p_clk = dsp_clk;
/*init hifidsp_pdata save in *hifi4dsp_data and add to priv*/
pdata->clk_freq = dsp_clkfreq[i];
pdata->name = (id == 0 ? "hifi4dsp0" : "hifi4dsp1");
pdata->fw_paddr = hifi4base;
pdata->fw_buf = fw_addr;
pdata->fw_max_size = hifi4size;
pdata->reg_size = (id == 0 ?
g_regbases.rega_size : g_regbases.regb_size);
pdata->reg = (id == 0 ?
g_regbases.dspa_addr : g_regbases.dspb_addr);
pdata->id = id;
priv->pdata = pdata;
/*add hifi4dsp_dsp_device to priv*/
priv->dsp_dev = &hifi4dsp_dev;
/*initial hifi4dsp_dsp and add to priv*/
mutex_init(&dsp->mutex);
spin_lock_init(&dsp->spinlock);
spin_lock_init(&dsp->fw_spinlock);
INIT_LIST_HEAD(&dsp->fw_list);
dsp->dsp_clk = dsp_clk;
dsp->fw = fw;
dsp->dsp_fw = dsp_firmware;
dsp->id = id;
dsp->freq = pdata->clk_freq;
dsp->regionsize = hifi4size;
dsp->irq = pdata->irq;
dsp->major_id = MAJOR(priv->dev->devt);
dsp->dev = priv->dev;
dsp->pdata = pdata;
dsp->info = hifi_info;
dsp->dsp_dev = priv->dsp_dev;
dsp->ops = priv->dsp_dev->ops;
priv->dsp = dsp;
hifi4dsp_p[i] = priv;
//p_dsp_miscdev->priv = priv;
dev_set_drvdata(priv->dev, priv);
pr_info("register dsp-%d done\n", id);
}
ret = 0;
pr_info("%s done\n", __func__);
goto done;
err3:
real_fw_malloc_error:
dsp_firmware_malloc_error:
pdata_malloc_error:
priv_malloc_error:
miscdev_malloc_error:
hifi_info_malloc_error:
err2:
return -ENOMEM;
err1:
return -EINVAL;
done:
return ret;
}
static const struct of_device_id hifi4dsp_device_id[] = {
{
.compatible = "amlogic, hifi4dsp",
},
{},
};
MODULE_DEVICE_TABLE(of, hifi4dsp_device_id);
static struct platform_driver hifi4dsp_platform_driver = {
.driver = {
.name = "hifi4dsp",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(hifi4dsp_device_id),
},
.probe = hifi4dsp_platform_probe,
.remove = hifi4dsp_platform_remove,
};
module_platform_driver(hifi4dsp_platform_driver);
MODULE_DESCRIPTION("HiFi DSP Module Driver");
MODULE_LICENSE("GPL v2");