From c7074860b54ad51a5f3c62bba70401211316ba95 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E6=9D=9C=E5=9D=A4=E6=98=8E?= Date: Wed, 19 May 2010 02:38:46 +0000 Subject: [PATCH] add dsp dir --- drivers/staging/rk2818/rk2818_dsp/Kconfig | 7 + drivers/staging/rk2818/rk2818_dsp/Makefile | 6 + drivers/staging/rk2818/rk2818_dsp/queue.c | 180 ++++ drivers/staging/rk2818/rk2818_dsp/queue.h | 62 ++ .../staging/rk2818/rk2818_dsp/rk2818_dsp.c | 949 ++++++++++++++++++ .../staging/rk2818/rk2818_dsp/rk2818_dsp.h | 56 ++ 6 files changed, 1260 insertions(+) create mode 100644 drivers/staging/rk2818/rk2818_dsp/Kconfig create mode 100644 drivers/staging/rk2818/rk2818_dsp/Makefile create mode 100644 drivers/staging/rk2818/rk2818_dsp/queue.c create mode 100644 drivers/staging/rk2818/rk2818_dsp/queue.h create mode 100644 drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.c create mode 100644 drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.h diff --git a/drivers/staging/rk2818/rk2818_dsp/Kconfig b/drivers/staging/rk2818/rk2818_dsp/Kconfig new file mode 100644 index 000000000000..fe5d6843ebc2 --- /dev/null +++ b/drivers/staging/rk2818/rk2818_dsp/Kconfig @@ -0,0 +1,7 @@ +menu "DSP" +config RK2818_DSP + tristate "ROCKCHIP RK2818 DSP" + default y + help + rk28 dsp module. +endmenu diff --git a/drivers/staging/rk2818/rk2818_dsp/Makefile b/drivers/staging/rk2818/rk2818_dsp/Makefile new file mode 100644 index 000000000000..efd55bf38793 --- /dev/null +++ b/drivers/staging/rk2818/rk2818_dsp/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the dsp core. +# + +obj-$(CONFIG_RK2818_DSP) += rk2818dsp.o +rk2818dsp-objs := rk2818_dsp.o queue.o \ No newline at end of file diff --git a/drivers/staging/rk2818/rk2818_dsp/queue.c b/drivers/staging/rk2818/rk2818_dsp/queue.c new file mode 100644 index 000000000000..533b40904cd3 --- /dev/null +++ b/drivers/staging/rk2818/rk2818_dsp/queue.c @@ -0,0 +1,180 @@ +/****************************************************************************** + * queue.c + * + * Copyright (c) 2009 Fuzhou Rockchip Co.,Ltd. + * + * DESCRIPTION: - + * Functions used to manage a FIFO queue . + * + * modification history + * -------------------- + * Keith Lin, Feb 27, 2009, Initial version + * -------------------- + ******************************************************************************/ + +#include +#include +#include +#include +#include "queue.h" + +DEFINE_SPINLOCK(lock); +unsigned long flag; + +/** + * queue initialize, + * return Queue pointer on success and NULL on error. + */ +Queue *queue_init(int32_t max_nb_item, void (*destruct)(void *, void *), void *param) +{ + Queue *q = NULL; + spin_lock_irqsave(&lock, flag); + + q = kmalloc(sizeof(*q), GFP_DMA); + if(NULL == q) { + spin_unlock_irqrestore(&lock, flag); + return NULL; + } + + q->items = kmalloc(max_nb_item * sizeof(*q->items), GFP_DMA); + + if(NULL == q->items) + { + kfree(q); + spin_unlock_irqrestore(&lock, flag); + return NULL; + } + + q->first = q->last = 0; + q->nb_item = 0; + q->max_nb_item = max_nb_item; + q->size = 0; + q->destruct = (void (*)(void *, void *))destruct; + q->param = param; + spin_unlock_irqrestore(&lock, flag); + return q; +} + +/** + * put an element to the queue, + * return 0 on success and -1 on error. + */ +int32_t queue_put(Queue *q, void *elem, int32_t size) +{ + spin_lock_irqsave(&lock, flag); + if(q->nb_item >= q->max_nb_item) { + spin_unlock_irqrestore(&lock, flag); + return -1; + } + + q->items[q->last].elem = elem; + q->items[q->last].size = size; + q->last = (q->last + 1) % q->max_nb_item; + q->size += size; + q->nb_item++; + + spin_unlock_irqrestore(&lock, flag); + return 0; +} + +/** + * get an element from the queue. + * return element pointer on success and NULL on error. + */ +void *queue_get(Queue *q) +{ + Item *item; + + spin_lock_irqsave(&lock, flag); + if(q->nb_item <= 0) { + spin_unlock_irqrestore(&lock, flag); + return NULL; + } + + item = &(q->items[q->first]); + q->first = (q->first + 1) % q->max_nb_item; + q->size -= item->size; + q->nb_item--; + + spin_unlock_irqrestore(&lock, flag); + return item->elem; +} + +/** + * Flush the queue. + */ +void queue_flush(Queue *q) +{ + spin_lock_irqsave(&lock, flag); + /* destruct all elements in the queue */ + if (NULL != q->destruct) + { + for(; q->nb_item > 0; q->nb_item --) + { + q->destruct(q->param, q->items[q->first].elem); + q->first = (q->first + 1) % q->max_nb_item; + } + } + + q->first = q->last = 0; + q->nb_item = 0; + q->size = 0; + spin_unlock_irqrestore(&lock, flag); +} + +/** + * Destroy the queue. + */ +void queue_destroy(Queue *q) +{ + spin_lock_irqsave(&lock, flag); + /* destruct all elements first */ + if (NULL != q->destruct) + { + for(; q->nb_item > 0; q->nb_item--) + { + q->destruct(q->param, q->items[q->first].elem); + q->first = (q->first + 1) % q->max_nb_item; + } + } + + if(NULL != q) + { + if(NULL != q->items) + { + kfree(q->items); + q->items = NULL; + } + kfree(q); + q = NULL; + } + spin_unlock_irqrestore(&lock, flag); +} + +int32_t queue_is_full(Queue *q) +{ + int32_t ret = 0; + spin_lock_irqsave(&lock, flag); + ret = (q->nb_item >= q->max_nb_item) ? 1 : 0; + spin_unlock_irqrestore(&lock, flag); + return ret; +} + +int32_t queue_is_empty(Queue *q) +{ + int32_t ret = 0; + spin_lock_irqsave(&lock, flag); + ret = (q->nb_item == 0); + spin_unlock_irqrestore(&lock, flag); + return ret; +} + +int32_t queue_size(Queue *q) +{ + int32_t ret = 0; + spin_lock_irqsave(&lock, flag); + ret = q->size; + spin_unlock_irqrestore(&lock, flag); + return ret; +} + diff --git a/drivers/staging/rk2818/rk2818_dsp/queue.h b/drivers/staging/rk2818/rk2818_dsp/queue.h new file mode 100644 index 000000000000..33d691c8b341 --- /dev/null +++ b/drivers/staging/rk2818/rk2818_dsp/queue.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * queue.h + * + * Copyright (c) 2009 Fuzhou Rockchip Co.,Ltd. + * + * DESCRIPTION: - + * Functions used to manage a FIFO queue. + * + * modification history + * -------------------- + * Keith Lin, Feb 27, 2009, Initial version + * -------------------- + ******************************************************************************/ + +#ifndef QUEUE_H +#define QUEUE_H + + +typedef struct Item +{ + void *elem; + + /* data size of the element */ + int32_t size; +}Item; + +typedef struct Queue +{ + Item *items; + + /** + * first - the first item of the queue, + * last - the last item of the queue. + */ + int32_t first, last; + + /* the number of items in the queue */ + int32_t nb_item; + + /* max item number */ + int32_t max_nb_item; + + /* total data size in the queue */ + int32_t size; + + /* parameter for destruct function */ + void *param; + + /* call this call back to destruct elements before flush */ + void (*destruct)(void *, void *); +} Queue; + +Queue *queue_init(int32_t max_nb_item, void (*destruct)(void *, void *), void *param); +int32_t queue_put(Queue *q, void *elem, int32_t size); +void *queue_get(Queue *q); +void queue_flush(Queue *q); +void queue_destroy(Queue *q); +int32_t queue_is_full(Queue *q); +int32_t queue_is_empty(Queue *q); +int32_t queue_size(Queue *q); + +#endif /* QUEUE_H */ diff --git a/drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.c b/drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.c new file mode 100644 index 000000000000..1fb8bf4a8ddb --- /dev/null +++ b/drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.c @@ -0,0 +1,949 @@ +/* drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.c + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk2818_dsp.h" +#include "queue.h" + + +#if 0 + #define dspprintk(msg...) printk(msg); +#else + #define dspprintk(msg...) +#endif + +struct rk28dsp_inf { + struct miscdevice miscdev; + struct device dev; + + void *pmu_base; + void *l1_dbase; + void *l2_idbase; + + int irq0; + int irq1; + + void *bootaddress; + void *codeprogramaddress; + void *codetableaddress; + void *codedataaddress; + + struct rk28dsp_msg rcvmsg; + Queue *rcvmsg_q; + + int cur_req; + pid_t cur_pid; + int cur_freq; + int req_waited; + + char req1fwname[20]; + + struct timer_list dsp_timer; + int dsp_status; + + struct clk *clk; +}; + + +#define SET_BOOT_VECTOR(v) __raw_writel(v,RK2818_REGFILE_BASE + 0x18); +#define DSP_BOOT_CTRL() __raw_writel(__raw_readl(RK2818_REGFILE_BASE + 0x14) | (1<<4),RK2818_REGFILE_BASE + 0x14); +#define DSP_BOOT_CLR() __raw_writel(__raw_readl(RK2818_REGFILE_BASE + 0x14) & (~(1<<4)),RK2818_REGFILE_BASE + 0x14); + + +#define CODEC_SECTION_NO 0x5 +#define DSP_MAJOR 232 + + +/* CEVA memory map base address for ARM */ +#define DSP_L2_IMEM_BASE (RK2818_DSP_PHYS + 0x200000) +#define DSP_L2_DMEM_BASE (RK2818_DSP_PHYS + 0x400000) +#define PIU_BASE_ADDR (RK2818_DSP_PHYS + 0x132000) +#define PMU_BASE_ADDR (RK2818_DSP_PHYS + 0x130000) + + + +#define PIU_PUT_IMASK(port,v) __raw_writel(v,inf->pmu_base+PIU_IMASK_OFFSET); + +#define PIU_GET_STATUS_REPX(channel) \ + (__raw_readl(inf->pmu_base + PIU_STATUS_OFFSET) & (1 << ((channel) + PIU_STATUS_R0WRS))) + +#define PIU_READ_REPX_VAL(channel) \ + __raw_readl(inf->pmu_base + PIU_REPLY0_OFFSET + ((channel) << 2)) + +#define PIU_CLR_STATUS_REPX(channel) \ + __raw_writel(__raw_readl(inf->pmu_base + PIU_STATUS_OFFSET) | (1 << ((channel) + PIU_STATUS_R0WRS)), \ + inf->pmu_base+PIU_STATUS_OFFSET) + +#define PIU_SEND_CMD(channel, cmd) \ + __raw_writel(cmd,inf->pmu_base+PIU_CMD0_OFFSET + (channel << 2)) + + +typedef enum _DSP_STATUS { + DS_NORMAL = 0, + DS_TOSLOW, + DS_SLOW, + DS_SLEEP, +} DSP_STATUS; + +typedef enum _DSP_PWR_CTL { + DPC_NORMAL = 0, + DPC_SLOW, + DPC_SLEEP, +} DSP_PWR_CTL; + + +static struct rk28dsp_inf *g_inf = NULL; + +static DECLARE_WAIT_QUEUE_HEAD(wq); +static int wq_condition = 1; + +static DECLARE_WAIT_QUEUE_HEAD(wq2); +static int wq2_condition = 0; + +static DECLARE_MUTEX(sem); + +static int rcv_quit = 0; + +static int CheckDSPLIBHead(char *buff) +{ + if ((buff[0] != 'r') + || (buff[1] != 'k') + || (buff[2] != 'd') + || (buff[3] != 's') + || (buff[4] != 'p') + || (buff[5] != ' ') + || (buff[6] != 'X') + || (buff[7] != 'X')) + { + return -1; + } + else + { + return 0; + } +} + +void dsp_set_clk(int clkrate) +{ +#if 1 + struct rk28dsp_inf *inf = g_inf; + if(!inf) return; + if(clkrate > 24 && clkrate < 600) { + if(inf->clk) clk_set_rate(inf->clk, clkrate*1000000); + } else { + if(inf->clk) clk_set_rate(inf->clk, 300*1000000); + } +#else + unsigned int freqreg = 0; + switch(clkrate) { + case 300: freqreg = 0x01830310; break; + case 400: freqreg = 0x01820310; break; + case 560: freqreg = 0x01982300; break; + case 600: freqreg = 0x01982580; break; + case 500: + default: freqreg = 0x01810290; break; + } + __raw_writel(freqreg, RK2818_SCU_BASE+0x04); //0x01830310 300mhz 0x01820310 400mhz 0x01810290 500mhz 0x01982300 560mhz 0x01982580 600mhz + mdelay(15); +#endif +} + + +void dsp_powerctl(int ctl, int arg) +{ + struct rk28dsp_inf *inf = g_inf; + if(!inf) return; + + switch(ctl) + { + case DPC_SLOW: + { + /* dsp work mode :slow mode*/ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x0c) & (~0x03)) , RK2818_SCU_BASE+0x0c); + } + break; + case DPC_NORMAL: + { + /* dsp clock enable 0x12*/ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x1c) & (~0x02)) , RK2818_SCU_BASE+0x1c); + /* dsp ahb bus clock enable*/ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x24) & (~0x04)) , RK2818_SCU_BASE+0x24); + /* sram arm clock enable */ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x1c) & (~0x08)) , RK2818_SCU_BASE+0x1c); + /* sram dsp clock enable */ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x1c) & (~0x10)) , RK2818_SCU_BASE+0x1c); + /* dsp core peripheral rest then not rest */ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x28) | 0x02000030) , RK2818_SCU_BASE+0x28); + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x28) & (~0x02000020)) , RK2818_SCU_BASE+0x28); + mdelay(15); + /* dsp work mode :slow mode*/ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x0c) & (~0x03)) , RK2818_SCU_BASE+0x0c); + mdelay(15); + + /* dsp set clk */ + dsp_set_clk(arg); + + /* dsp subsys power on 0x21*/ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x10) & (~0x21)) , RK2818_SCU_BASE+0x10); + mdelay(15); + /* change dsp & arm to normal mode */ + __raw_writel(0x5, RK2818_SCU_BASE+0x0c); + } + break; + case DPC_SLEEP: + { + /* dsp work mode :slow mode*/ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x0c) & (~0x03)) , RK2818_SCU_BASE+0x0c); + /* dsp core/peripheral rest*/ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x28) | 0x02000030) , RK2818_SCU_BASE+0x28); + /* dsp clock disable */ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x1c) | (0x02)) , RK2818_SCU_BASE+0x1c); + /* dsp ahb bus clock disable */ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x24) | (0x04)) , RK2818_SCU_BASE+0x24); + /* sram arm clock disable */ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x1c) | (0x08)) , RK2818_SCU_BASE+0x1c); + /* sram dsp clock disable */ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x1c) | (0x10)) , RK2818_SCU_BASE+0x1c); + udelay(10); + /* dsp pll disable */ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x04) | (0x01u<<22)) , RK2818_SCU_BASE+0x04); + udelay(10); + /* dsp subsys power off 0x21*/ + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x10) | (0x21)) , RK2818_SCU_BASE+0x10); + } + break; + default: + break; + } +} + + +static int _down_firmware(char *fwname, struct rk28dsp_inf *inf) +{ + static char lst_fwname[20] = {0}; + int ret = 0; + + if(NULL==fwname) return -EINVAL; + if(0==strcmp(fwname, "")) return -EINVAL; + if((0==strcmp(lst_fwname, fwname)) && (DS_SLEEP!=inf->dsp_status)) { + if(1==inf->cur_req) { + dspprintk("%s already down, not redown! \n", fwname); + + /* change dsp & arm to normal mode */ + if(DS_SLOW==inf->dsp_status) printk("dsp work mode : slow -> normal mode \n"); + inf->dsp_status = DS_NORMAL; + __raw_writel(0x5, RK2818_SCU_BASE+0x0c); + return 0; + } + } + + if(DS_SLEEP==inf->dsp_status) printk("dsp work mode : sleep -> normal mode \n"); + inf->dsp_status = DS_NORMAL; + + dspprintk("down firmware (%s) ... \n", fwname); + { + const struct firmware *fw; + char *buf,*code_buf; + int *pfile,*pboot; + int indexoffset,dataOffset; + int *pcodeprograml1,*pcodedatal1,*pcodetable; + int address,length,indexNo,loadNo,i,j; + int *fpIndexFile,*fpDataFile; + + __raw_writel((__raw_readl(RK2818_REGFILE_BASE + 0x10) | (0x6d8)), RK2818_REGFILE_BASE + 0x10); // 0x6d8 + dsp_powerctl(DPC_NORMAL, inf->cur_freq); + dspprintk("\nrequest_firmware ... \n"); + + /* down dsp boot */ + ret = request_firmware(&fw, "DspBoot.rkl", &inf->dev); + if (ret) { + printk(KERN_ERR "Failed to load boot image \"DspBoot.rkl\" err %d\n",ret); + return -ENOMEM; + } + buf = (char*)fw->data; + if(CheckDSPLIBHead(buf) != 0){ + printk("dsp boot head failed ! \n"); + return -ENOMEM; + } + pboot = (int*)inf->bootaddress; + indexoffset = *(buf+8+32); + dataOffset = *(buf+8+32+4); + pfile = (int *)(buf+dataOffset); + memcpy((char *)(pboot+16),(char *)(pfile),2000); /*copy boot to *pboot point*/ + SET_BOOT_VECTOR(__pa(pboot+16)); /*set dsp boot program address*/ + dspprintk("%s [%d]--%x\n",__FUNCTION__,__LINE__,__raw_readl(RK2818_REGFILE_BASE + 0x18)); + release_firmware(fw); + + /* down dsp codec */ + pcodeprograml1 = (int*)inf->codeprogramaddress; + pcodedatal1 = (int*)inf->codedataaddress; + pcodetable = (int*)inf->codetableaddress; + *(pboot+13) = __pa(pcodetable); /*set decode table address */ + *(pboot+14) = __pa(pcodeprograml1); /*set decode program address */ + *(pboot+15) = __pa(pcodedatal1); /*set decode data address */ + ret = request_firmware(&fw, fwname, &inf->dev); + if (ret) { + printk(KERN_ERR "Failed to load image \"%s\" err %d\n",fwname, ret); + return ret; + } + code_buf = (char*)fw->data; + if(CheckDSPLIBHead(code_buf) != 0){ + printk("dsp code head failed ! \n"); + return -ENOMEM; + } + + loadNo = CODEC_SECTION_NO; + i=8+16; + while ((loadNo--) != 0x0) + { + indexoffset = *((int *)(code_buf + i)); + dataOffset = *((int *)(code_buf + i + 4)); + //dspprintk("%s [%d]-- indexoffset=0x%x dataOffset=0x%x loadNo=%d \n",__FUNCTION__,__LINE__,indexoffset,dataOffset,loadNo); + i = i + 8; + if ((indexoffset != 0x0) && (dataOffset != 0x0)) + { + fpIndexFile = (int *)(code_buf + indexoffset); + fpDataFile = (int *)(code_buf + dataOffset); + indexNo = *(fpIndexFile + 1); + j = 0; + while ((indexNo--) != 0x0) + { + if(indexNo<0) break; + address = *(fpIndexFile + 2 + j*2); + length = *(fpIndexFile + 2 + 1 + j*2); + j = j + 1; + //dspprintk("%s [%d]-- address=0x%x length =0x%x indexNo=0x%x\n",__FUNCTION__,__LINE__,address,length,indexNo); + if(loadNo == (CODEC_SECTION_NO - 0x1)){ + /* Èç¹ûΪL1 data MEMµÄ´úÂë¶Î£¬¿½±´µ½¹Ì¼þµÄλÖà */ + memcpy((char *)(pcodeprograml1),(char *)(fpDataFile),length); + } else { + if (loadNo == (CODEC_SECTION_NO - 0x2)) { + /* Èç¹ûΪL1 data MEMµÄÊý¾Ý¶Î£¬¿½±´µ½¹Ì¼þµÄλÖà */ + memcpy((char *)(pcodedatal1),(char *)(fpDataFile),length); + } else { + /* Èç¹ûΪCEVAµÄÄÚ´æÇøÓò£¬ÐèÒª½øÐеØÖ·×ª»» */ + if (address < RK2818_SDRAM_PHYS) { +#if 1 + int k; + int *buffL2; + address = (unsigned int)inf->l2_idbase + (address - 0x200000); + buffL2 = (int *)address; + for (k=0; k<(length >> 2); k++) + { + buffL2[k] = fpDataFile[k]; + } + fpDataFile = (int *)((int)fpDataFile + length); +#else + address = (unsigned int)inf->l2_idbase + (address - 0x200000); + memcpy((char *)(address), (char *)fpDataFile, length); +#endif + } else { + /* Èç¹ûΪsdramÖУ¬ÔòÖ»ÄÜÂë±í£¬ÇÒµØÖ·Î޹أ¬ + ²¢ÇÒÖ»ÄÜÓÐÒ»¶Î(¼´µØÖ·Á¬Ðø), ·ñÔòÒýÆðϵͳ±ÀÀ£ + ÔÚÍ˳öÊÓÆµ»òÕß¼ÓÔØ²»³É¹¦ºó£¬¼ÇµÃ°Ñ´ËBuffÊͷŵô */ + memcpy((char *)pcodetable, (char *)fpDataFile, length); + } + } + } + + } + } + } + release_firmware(fw); + } + + DSP_BOOT_CTRL(); + mdelay(10); + __raw_writel((__raw_readl(RK2818_SCU_BASE+0x28) & (~0x00000010)) , RK2818_SCU_BASE+0x28); /* dsp core peripheral not rest*/ + + strcpy(lst_fwname, fwname); + + return 0; +} + + +void dsptimer_callback(unsigned long arg) +{ + struct rk28dsp_inf *inf = g_inf; + static int sec_cnt = 0; + if(!inf) return; + + mod_timer(&inf->dsp_timer, jiffies + HZ); + + switch(inf->dsp_status) + { + case DS_TOSLOW: + dsp_powerctl(DPC_SLOW, 0); + inf->dsp_status = DS_SLOW; + printk("dsp work mode : slow mode \n"); + sec_cnt = 0; + break; + case DS_SLOW: + if(++sec_cnt>=5) { + dsp_powerctl(DPC_SLEEP, 0); + inf->dsp_status = DS_SLEEP; + printk("dsp work mode : sleep mode \n"); + } + break; + case DS_SLEEP: + break; + case DS_NORMAL: + default: + inf->dsp_status = DS_NORMAL; + sec_cnt = 0; + break; + } + + +} + + +static int dsp_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct rk28dsp_inf *inf; + unsigned long vma_size = vma->vm_end - vma->vm_start; + unsigned long pageFrameNo = 0; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + + if(!g_inf) return -EAGAIN; + inf = g_inf; + + switch(vma_size) + { + case 0x10000: //l1_dbase + offset += RK2818_DSP_PHYS; + pageFrameNo = (offset >> PAGE_SHIFT); + //dspprintk("dsp_mmap l1_dbase \n"); + break; + case 0x400000: //l2_idbase + offset += DSP_L2_IMEM_BASE; + pageFrameNo = (offset >> PAGE_SHIFT); + //dspprintk("dsp_mmap l2_idbase \n"); + break; + case 0x600000: //dsp_base + offset += RK2818_DSP_PHYS; + pageFrameNo = (offset >> PAGE_SHIFT); + //dspprintk("dsp_mmap dsp_base \n"); + break; + default: //pmu_base (0x3000) + offset += PMU_BASE_ADDR; + pageFrameNo = (offset >> PAGE_SHIFT); + //dspprintk("dsp_mmap pmu_base \n"); + break; + } + vma->vm_pgoff = pageFrameNo; + vma->vm_flags |= VM_IO|VM_RESERVED|VM_LOCKED; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vma->vm_start, vma->vm_page_prot)) { + printk("remap_pfn_range fail(vma_size=0x%08x)\n", (unsigned int)vma_size); + return -EAGAIN; + } + return 0; +} + + +static long dsp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct rk28dsp_req req; + struct rk28dsp_inf *inf; + + if(!g_inf) return -EAGAIN; + inf = g_inf; + + if(DSP_IOCTL_RES_REQUEST!=cmd && DSP_IOCTL_GET_TABLE_PHY!=cmd) { + if(inf->cur_pid!=current->tgid) { + dspprintk("res is obtain by pid %d, refuse this req(pid=%d cmd=0x%08x) \n", + inf->cur_pid, current->tgid, cmd); + return -EBUSY; + } + } + + switch(cmd) + { + case DSP_IOCTL_RES_REQUEST: + if(copy_from_user(&req, (void *)arg, sizeof(struct rk28dsp_req))) + return -EFAULT; + dspprintk("DSP_IOCTL_RES_REQUEST reqno=%d(%d->%d) cur_req=%d(%d) \n", + req.reqno, current->tgid, current->pid, inf->cur_req, inf->cur_pid); + + if(0==req.reqno) return -EINVAL; + if(0==strcmp(req.fwname, "")) return -EINVAL; + + down(&sem); + if(0==inf->cur_req && !inf->req_waited) + { + inf->cur_req = req.reqno; + inf->cur_pid = current->tgid; + inf->cur_freq = req.freq; + if(1==req.reqno) strcpy(inf->req1fwname, req.fwname); + } + else if(1==inf->cur_req && !inf->req_waited && inf->cur_req!=req.reqno) + { + inf->req_waited = 1; + wq_condition = 0; + up(&sem); + dspprintk("wait_event \n"); + wait_event(wq, wq_condition); + down(&sem); + inf->req_waited = 0; + inf->cur_req = req.reqno; + inf->cur_pid = current->tgid; + inf->cur_freq = req.freq; + } else { + ret = -EBUSY; + } + + if(0==ret) { + _down_firmware(req.fwname, inf); + queue_flush(inf->rcvmsg_q); + rcv_quit = 0; + wq2_condition = 0; + } + up(&sem); + break; + + case DSP_IOCTL_RES_RELEASE: + dspprintk("DSP_IOCTL_RES_RELEASE cur_req = %d \n",inf->cur_req); + down(&sem); + if(inf->cur_req) { + if(1==inf->cur_req) { + if(1==arg) strcpy(inf->req1fwname, ""); + wq_condition = 1; + wake_up(&wq); + } else { + if(strcmp(inf->req1fwname, "")) { + _down_firmware(inf->req1fwname, inf); + } + } + inf->cur_req = 0; + inf->cur_pid = 0; + + mod_timer(&inf->dsp_timer, jiffies + HZ); + inf->dsp_status = DS_TOSLOW; + } + + /* force DSP_IOCTL_RECV_MSG return */ + rcv_quit = 1; + wq2_condition = 1; + wake_up(&wq2); + + up(&sem); + break; + + case DSP_IOCTL_SEND_MSG: + //dspprintk("DSP_IOCTL_SEND_MSG \n"); + { + struct rk28dsp_msg msg; + if(copy_from_user(&msg, (void *)arg, sizeof(struct rk28dsp_msg))) + return -EFAULT; + + if(CODEC_MSG_ICU_CHANNEL==msg.channel) { + __raw_writel((__raw_readl(inf->pmu_base+0x043C) | 0x100), inf->pmu_base+0x043C); + } else { + PIU_SEND_CMD(msg.channel, msg.cmd); + } + } + break; + + case DSP_IOCTL_RECV_MSG: + { + struct rk28dsp_msg *pmsg = NULL; + struct rk28dsp_msg msg; + if(NULL==inf->rcvmsg_q) return -EFAULT; + + while(NULL==pmsg) + { + pmsg = queue_get(inf->rcvmsg_q); + if(pmsg) break; + + if(copy_from_user(&msg, (void *)arg, sizeof(struct rk28dsp_msg))) return -EFAULT; + if(0==msg.rcv_timeout) return -EAGAIN; + + if(-1==msg.rcv_timeout) { + if(rcv_quit) return -EAGAIN; + wait_event(wq2, wq2_condition); + wq2_condition = 0; + if(rcv_quit) return -EAGAIN; + } else { + if(rcv_quit) return -EAGAIN; + if(!wait_event_timeout(wq2, wq2_condition, msg.rcv_timeout)) { wq2_condition = 0; return -EAGAIN; } + wq2_condition = 0; + if(rcv_quit) return -EAGAIN; + } + } + + if(pmsg) { + if(copy_to_user((void __user *)arg, pmsg, sizeof(struct rk28dsp_msg))) ret = -EFAULT; + kfree(pmsg); + } else { + ret = -EFAULT; + } + } + break; + + case DSP_IOCTL_SET_FREQ: + dspprintk("DSP_IOCTL_SET_FREQ: %dM!\n", (int)arg); + { + dsp_set_clk((int)arg); + inf->cur_freq = (int)arg; + } + break; + + case DSP_IOCTL_GET_TABLE_PHY: + { + unsigned int table_phy = __pa(inf->codetableaddress); + if(copy_to_user((void __user *)arg, (void*)&table_phy, 4)) ret = -EFAULT; + } + break; + + default: + break; + } + + return ret; +} + + +static int dsp_open(struct inode *inode, struct file *file) +{ + return 0; +} + + +static int dsp_release(struct inode *inode, struct file *file) +{ + return 0; + //return dsp_ioctl(file, DSP_IOCTL_RES_RELEASE, 1); +} + +static irqreturn_t rk28_dsp_irq(int irq, void *dev_id) +{ + struct platform_device *pdev = (struct platform_device*)dev_id; + struct rk28dsp_inf *inf = platform_get_drvdata(pdev); + struct rk28dsp_msg *pmsg = NULL; + unsigned int cmd = 0; + + if(NULL==inf || NULL==inf->rcvmsg_q) return IRQ_HANDLED; + + if(IRQ_NR_PIUCMD == irq) + { + if (PIU_GET_STATUS_REPX(CODEC_OUTPUT_PIU_CHANNEL)) { + cmd = PIU_READ_REPX_VAL(CODEC_OUTPUT_PIU_CHANNEL); + if(inf->cur_pid && !queue_is_full(inf->rcvmsg_q)) { + pmsg = kmalloc(sizeof(struct rk28dsp_msg), GFP_ATOMIC); + if(pmsg) { + pmsg->channel = CODEC_OUTPUT_PIU_CHANNEL; + pmsg->cmd = cmd; + pmsg->rcv_timeout = 0; + if(!queue_put(inf->rcvmsg_q, pmsg, sizeof(struct rk28dsp_msg))) { + wq2_condition = 1; + wake_up(&wq2); + } else { + printk("queue_put fail! \n"); + } + } else { + printk("kmalloc fail! \n"); + } + } + PIU_CLR_STATUS_REPX(CODEC_OUTPUT_PIU_CHANNEL); // clear status. + } + + if (PIU_GET_STATUS_REPX(CODEC_MSG_PIU_CHANNEL)) { + cmd = PIU_READ_REPX_VAL(CODEC_MSG_PIU_CHANNEL); + if(inf->cur_pid && !queue_is_full(inf->rcvmsg_q)) { + pmsg = kmalloc(sizeof(struct rk28dsp_msg), GFP_ATOMIC); + if(pmsg) { + pmsg->channel = CODEC_MSG_PIU_CHANNEL; + pmsg->cmd = cmd; + pmsg->rcv_timeout = 0; + if(!queue_put(inf->rcvmsg_q, pmsg, sizeof(struct rk28dsp_msg))) { + wq2_condition = 1; + wake_up(&wq2); + } else { + printk("queue_put fail! \n"); + } + } else { + printk("kmalloc fail! \n"); + } + } + PIU_CLR_STATUS_REPX(CODEC_MSG_PIU_CHANNEL); // clear status. + } + + if (PIU_GET_STATUS_REPX(CODEC_MSG_PIU_NEXT_CHANNEL)) { + cmd = PIU_READ_REPX_VAL(CODEC_MSG_PIU_NEXT_CHANNEL); + if(inf->cur_pid && !queue_is_full(inf->rcvmsg_q)) { + pmsg = kmalloc(sizeof(struct rk28dsp_msg), GFP_ATOMIC); + if(pmsg) { + pmsg->channel = CODEC_MSG_PIU_NEXT_CHANNEL; + pmsg->cmd = cmd; + pmsg->rcv_timeout = 0; + if(!queue_put(inf->rcvmsg_q, pmsg, sizeof(struct rk28dsp_msg))) { + wq2_condition = 1; + wake_up(&wq2); + } else { + printk("queue_put fail! \n"); + } + } else { + printk("kmalloc fail! \n"); + } + } + PIU_CLR_STATUS_REPX(CODEC_MSG_PIU_NEXT_CHANNEL); // clear status. + } + } + + + if(IRQ_NR_DSPSWI == irq) + { + __raw_writel((__raw_readl(inf->pmu_base+0x2c10) & (~0x40)), inf->pmu_base+0x2c10); // clear status. + if(inf->cur_pid && !queue_is_full(inf->rcvmsg_q)) { + pmsg = kmalloc(sizeof(struct rk28dsp_msg), GFP_ATOMIC); + if(pmsg) { + pmsg->channel = CODEC_MSG_ICU_CHANNEL; + pmsg->cmd = 0; + pmsg->rcv_timeout = 0; + if(!queue_put(inf->rcvmsg_q, pmsg, sizeof(struct rk28dsp_msg))) { + wq2_condition = 1; + wake_up(&wq2); + } else { + printk("queue_put fail! \n"); + } + } else { + printk("kmalloc fail! \n"); + } + } + } + + return IRQ_HANDLED; +} + + +struct file_operations dsp_fops = { + .open = dsp_open, + .release = dsp_release, + .mmap = dsp_mmap, + .unlocked_ioctl = dsp_ioctl, +}; + +static struct miscdevice dsp_dev ={ + .minor = DSP_MAJOR, + .name = "rk28-dsp", + .fops = &dsp_fops, +}; + +void destruct(void *param, void *elem) +{ + if(elem) kfree(elem); +} + + +static int __init dsp_drv_probe(struct platform_device *pdev) +{ + struct rk28dsp_inf *inf; + int ret = 0; + + inf = kmalloc(sizeof(struct rk28dsp_inf), GFP_KERNEL); + if(NULL==inf) { ret = -ENOMEM; goto alloc_fail; } + memset(inf, 0, sizeof(struct rk28dsp_inf)); + + inf->clk = clk_get(NULL, "dsp"); + if(inf->clk) clk_enable(inf->clk); + + inf->pmu_base = (void*)ioremap(PMU_BASE_ADDR, 0x3000); + inf->l1_dbase = (void*)ioremap(RK2818_DSP_PHYS, 0x10000); + inf->l2_idbase = (void*)ioremap(DSP_L2_IMEM_BASE, 0x400000); + + inf->irq0 = pdev->resource[1].start; + inf->irq1 = pdev->resource[2].start; + + inf->bootaddress = (void*)__get_free_page(GFP_DMA); + inf->codeprogramaddress = (void*)__get_free_pages(GFP_DMA,4); + inf->codedataaddress = (void*)__get_free_pages(GFP_DMA,4); + inf->codetableaddress = (void*)__get_free_pages(GFP_DMA,6); + if(0==inf->bootaddress) { ret = -ENOMEM; printk("alloc bootaddress fail!\n"); goto alloc2_fail; } + if(0==inf->codeprogramaddress) { ret = -ENOMEM; printk("alloc codeprogramaddress fail!\n"); goto alloc2_fail; } + if(0==inf->codedataaddress) { ret = -ENOMEM; printk("alloc codedataaddress fail!\n"); goto alloc2_fail; } + if(0==inf->codetableaddress) { ret = -ENOMEM; printk("alloc codetableaddress fail!\n"); goto alloc2_fail; } + + platform_set_drvdata(pdev, inf); + inf->dev = pdev->dev; + + inf->rcvmsg_q = queue_init(256, destruct, NULL); + + ret = request_irq(inf->irq0, rk28_dsp_irq, IRQF_SHARED, "rk28-dsp", pdev); + if (ret < 0) { + goto irq0_fail; + } + + ret = request_irq(inf->irq1, rk28_dsp_irq, IRQF_SHARED, "rk28-dsp", pdev); + if (ret < 0) { + goto irq1_fail; + } + + init_MUTEX(&sem); + + init_timer(&inf->dsp_timer); + inf->dsp_timer.function = dsptimer_callback; + inf->dsp_timer.expires = jiffies + HZ; + add_timer(&inf->dsp_timer); + + inf->miscdev = dsp_dev; + ret = misc_register(&(inf->miscdev)); + if(ret) { + goto reg_fail; + } + + g_inf = inf; + + return 0; + +reg_fail: + if(inf->irq1) free_irq(inf->irq1, &inf->miscdev); +irq1_fail: + if(inf->irq0) free_irq(inf->irq0, &inf->miscdev); +irq0_fail: +alloc2_fail: + if(inf->bootaddress) free_page((unsigned int)inf->bootaddress); + if(inf->codeprogramaddress) free_pages((unsigned int)inf->codeprogramaddress,4); + if(inf->codedataaddress) free_pages((unsigned int)inf->codedataaddress,4); + if(inf->codetableaddress) free_pages((unsigned int)inf->codetableaddress,6); + if(inf) kfree(inf); +alloc_fail: + return ret; +} + +static int dsp_drv_remove(struct platform_device *pdev) +{ + struct rk28dsp_inf *inf = platform_get_drvdata(pdev); + dspprintk("%s [%d]\n",__FUNCTION__,__LINE__); + + misc_deregister(&(inf->miscdev)); + if(inf->irq0) free_irq(inf->irq0, &inf->miscdev); + if(inf->irq1) free_irq(inf->irq1, &inf->miscdev); + + if(inf->bootaddress) free_page((unsigned int)inf->bootaddress); + if(inf->codeprogramaddress) free_pages((unsigned int)inf->codeprogramaddress,4); + if(inf->codedataaddress) free_pages((unsigned int)inf->codedataaddress,4); + if(inf->codetableaddress) free_pages((unsigned int)inf->codetableaddress,6); + + iounmap((void __iomem *)(inf->pmu_base)); + iounmap((void __iomem *)(inf->l1_dbase)); + iounmap((void __iomem *)(inf->l2_idbase)); + + if(inf->clk) { + clk_disable(inf->clk); + clk_put(inf->clk); + } + + kfree(inf); + return 0; +} + + +#if defined(CONFIG_PM) +static int dsp_drv_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rk28dsp_inf *inf = platform_get_drvdata(pdev); + + dspprintk(">>>>>> %s : %s\n", __FILE__, __FUNCTION__); + + if(!inf) { + printk("inf==0, dsp_drv_suspend fail! \n"); + return -EINVAL; + } + + if(DS_NORMAL==inf->dsp_status) return -EPERM; + + dsp_powerctl(DPC_SLEEP, 0); + inf->dsp_status = DS_SLEEP; + printk("dsp work mode : sleep mode \n"); + return 0; +} + +static int dsp_drv_resume(struct platform_device *pdev) +{ + struct rk28dsp_inf *inf = platform_get_drvdata(pdev); + + dspprintk(">>>>>> %s : %s\n", __FILE__, __FUNCTION__); + + if(!inf) { + printk("inf==0, dsp_drv_resume fail! \n"); + return -EINVAL; + } + + return 0; +} +#else +#define dsp_drv_suspend NULL +#define dsp_drv_resume NULL +#endif /* CONFIG_PM */ + + +static struct platform_driver dsp_driver = { + .probe = dsp_drv_probe, + .remove = dsp_drv_remove, + .suspend = dsp_drv_suspend, + .resume = dsp_drv_resume, + .driver = { + .name = "rk28-dsp", + }, +}; + +static int __init rk2818_dsp_init(void) +{ + return platform_driver_register(&dsp_driver); +} + +static void __exit rk2818_dsp_exit(void) +{ + platform_driver_unregister(&dsp_driver); +} + + +//module_init(rk2818_dsp_init); +subsys_initcall(rk2818_dsp_init); +module_exit(rk2818_dsp_exit); + +MODULE_AUTHOR(" dukunming dkm@rock-chips.com"); +MODULE_DESCRIPTION("Driver for rk2818 dsp device"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.h b/drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.h new file mode 100644 index 000000000000..4f54ccf5451a --- /dev/null +++ b/drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.h @@ -0,0 +1,56 @@ +/* + * drivers/staging/rk2818/rk2818_dsp/rk2818_dsp.h + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DRIVERS_STAGING_RK2818_DSP_H +#define __DRIVERS_STAGING_RK2818_DSP_H + + +#define PIU_CMD0_OFFSET (0x2030) +#define PIU_REPLY0_OFFSET (0x203c) +#define PIU_STATUS_OFFSET (0x204c) +#define PIU_IMASK_OFFSET (0x2048) +#define PIU_STATUS_R0WRS 3 + + +#define CODEC_OUTPUT_PIU_CHANNEL 0 +#define CODEC_MSG_PIU_CHANNEL 1 +#define CODEC_MSG_PIU_NEXT_CHANNEL 2 +#define CODEC_MSG_ICU_CHANNEL 3 + + +#define DSP_IOCTL_RES_REQUEST (0x00800000) +#define DSP_IOCTL_RES_RELEASE (0x00800001) +#define DSP_IOCTL_SEND_MSG (0x00800002) +#define DSP_IOCTL_RECV_MSG (0x00800003) +#define DSP_IOCTL_SET_FREQ (0x00800004) +#define DSP_IOCTL_GET_TABLE_PHY (0x00800005) + + +struct rk28dsp_req { + int reqno; + char fwname[20]; + int freq; +}; + +struct rk28dsp_msg { + int channel; + unsigned int cmd; + int rcv_timeout; // 0:no block -1:block >0:block with timeout +}; + +extern void rockchip_add_device_dsp(void); + +#endif /* __DRIVERS_STAGING_RK2818_DSP_H */ + -- 2.34.1