2 * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
4 * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
6 * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
24 #include <linux/sched.h>
25 #include <linux/device.h>
26 #include <linux/interrupt.h>
27 #include <asm/arch/mailbox.h>
28 #include "dsp_mbcmd.h"
32 static struct ipbuf_head *g_ipbuf;
34 struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad;
35 static struct ipblink ipb_free = IPBLINK_INIT;
36 static int ipbuf_sys_hold_mem_active;
38 static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr,
40 static struct device_attribute dev_attr_ipbuf = __ATTR_RO(ipbuf);
46 device_remove_file(omap_dsp->dev, &dev_attr_ipbuf);
48 spin_lock(&ipb_free.lock);
49 RESET_IPBLINK(&ipb_free);
50 spin_unlock(&ipb_free.lock);
57 for (i = 0; i < ipbuf_sys_hold_mem_active; i++) {
58 dsp_mem_disable((void *)daram_base);
60 ipbuf_sys_hold_mem_active = 0;
63 int ipbuf_config(u16 ln, u16 lsz, void *base)
65 size_t lsz_byte = ((size_t)lsz) << 1;
73 if (((unsigned long)base) & 0x3) {
75 "omapdsp: global ipbuf address(0x%p) is not "
76 "32-bit aligned!\n", base);
80 if (dsp_address_validate(base, size, "global ipbuf") < 0)
83 g_ipbuf = kmalloc(sizeof(struct ipbuf_head) * ln, GFP_KERNEL);
84 if (g_ipbuf == NULL) {
86 "omapdsp: memory allocation for ipbuf failed.\n");
89 for (i = 0; i < ln; i++) {
92 top = base + (sizeof(struct ipbuf) + lsz_byte) * i;
93 btm = base + (sizeof(struct ipbuf) + lsz_byte) * (i+1) - 1;
94 g_ipbuf[i].p = (struct ipbuf *)top;
96 if (((unsigned long)top & 0xfffe0000) !=
97 ((unsigned long)btm & 0xfffe0000)) {
99 * an ipbuf line should not cross
103 "omapdsp: ipbuf[%d] crosses 64k-word boundary!\n"
104 " @0x%p, size=0x%08x\n", i, top, lsz_byte);
112 ipbcfg.bsycnt = ln; /* DSP holds all ipbufs initially. */
115 pr_info("omapdsp: IPBUF configuration\n"
116 " %d words * %d lines at 0x%p.\n",
117 ipbcfg.lsz, ipbcfg.ln, ipbcfg.base);
119 ret = device_create_file(omap_dsp->dev, &dev_attr_ipbuf);
121 printk(KERN_ERR "device_create_file failed: %d\n", ret);
131 int ipbuf_sys_config(void *p, arm_dsp_dir_t dir)
133 char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
135 if (((unsigned long)p) & 0x3) {
137 "omapdsp: system ipbuf(%s) address(0x%p) is "
138 "not 32-bit aligned!\n", dir_str, p);
141 if (dsp_address_validate(p, sizeof(struct ipbuf_sys),
142 "system ipbuf(%s)", dir_str) < 0)
144 if (dsp_mem_type(p, sizeof(struct ipbuf_sys)) != MEM_TYPE_EXTERN) {
146 "omapdsp: system ipbuf(%s) is placed in"
147 " DSP internal memory.\n"
148 " It will prevent DSP from idling.\n", dir_str);
149 ipbuf_sys_hold_mem_active++;
151 * dsp_mem_enable() never fails because
152 * it has been already enabled in dspcfg process and
153 * this will just increment the usecount.
155 dsp_mem_enable((void *)daram_base);
166 int ipbuf_p_validate(void *p, arm_dsp_dir_t dir)
168 char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
170 if (((unsigned long)p) & 0x3) {
172 "omapdsp: private ipbuf(%s) address(0x%p) is "
173 "not 32-bit aligned!\n", dir_str, p);
176 return dsp_address_validate(p, sizeof(struct ipbuf_p),
177 "private ipbuf(%s)", dir_str);
181 * Global IPBUF operations
183 struct ipbuf_head *bid_to_ipbuf(u16 bid)
185 return &g_ipbuf[bid];
188 struct ipbuf_head *get_free_ipbuf(u8 tid)
190 struct ipbuf_head *ipb_h;
192 if (dsp_mem_enable_ipbuf() < 0)
195 spin_lock(&ipb_free.lock);
197 if (ipblink_empty(&ipb_free)) {
198 /* FIXME: wait on queue when not available. */
202 ipb_h = &g_ipbuf[ipb_free.top];
203 ipb_h->p->la = tid; /* lock */
204 __ipblink_del_top(&ipb_free);
206 spin_unlock(&ipb_free.lock);
207 dsp_mem_disable_ipbuf();
212 void release_ipbuf(struct ipbuf_head *ipb_h)
214 if (ipb_h->p->la == TID_FREE) {
216 "omapdsp: attempt to release unlocked IPBUF[%d].\n",
219 * FIXME: re-calc bsycnt
223 ipb_h->p->la = TID_FREE;
224 ipb_h->p->sa = TID_FREE;
225 ipblink_add_tail(&ipb_free, ipb_h->bid);
228 static int try_yld(struct ipbuf_head *ipb_h)
232 ipb_h->p->sa = TID_ANON;
233 status = mbcompose_send(BKYLD, 0, ipb_h->bid);
235 /* DSP is busy and ARM keeps this line. */
236 release_ipbuf(ipb_h);
240 ipb_bsycnt_inc(&ipbcfg);
245 * balancing ipbuf lines with DSP
247 static void do_balance_ipbuf(struct work_struct *unused)
249 while (ipbcfg.bsycnt <= ipbcfg.ln / 4) {
250 struct ipbuf_head *ipb_h;
252 if ((ipb_h = get_free_ipbuf(TID_ANON)) == NULL)
254 if (try_yld(ipb_h) < 0)
259 static DECLARE_WORK(balance_ipbuf_work, do_balance_ipbuf);
261 void balance_ipbuf(void)
263 schedule_work(&balance_ipbuf_work);
266 /* for process context */
267 void unuse_ipbuf(struct ipbuf_head *ipb_h)
269 if (ipbcfg.bsycnt > ipbcfg.ln / 4) {
270 /* we don't have enough IPBUF lines. let's keep it. */
271 release_ipbuf(ipb_h);
273 /* we have enough IPBUF lines. let's return this line to DSP. */
274 ipb_h->p->la = TID_ANON;
280 /* for interrupt context */
281 void unuse_ipbuf_nowait(struct ipbuf_head *ipb_h)
283 release_ipbuf(ipb_h);
288 * functions called from mailbox interrupt routine
291 void mbox_err_ipbfull(void)
299 static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr,
305 for (bid = 0; bid < ipbcfg.ln; bid++) {
306 struct ipbuf_head *ipb_h = &g_ipbuf[bid];
307 u16 la = ipb_h->p->la;
308 u16 ld = ipb_h->p->ld;
311 if (len > PAGE_SIZE - 100) {
312 len += sprintf(buf + len, "out of buffer.\n");
316 len += sprintf(buf + len, "ipbuf[%d]: adr = 0x%p\n",
318 if (la == TID_FREE) {
319 len += sprintf(buf + len,
320 " DSPtask[%d]->Linux "
321 "(already read and now free for Linux)\n",
323 } else if (ld == TID_FREE) {
324 len += sprintf(buf + len,
325 " Linux->DSPtask[%d] "
326 "(already read and now free for DSP)\n",
328 } else if (ipbuf_is_held(ld, bid)) {
329 len += sprintf(buf + len,
330 " DSPtask[%d]->Linux "
331 "(waiting to be read)\n"
332 " count = %d\n", ld, c);
334 len += sprintf(buf + len,
335 " Linux->DSPtask[%d] "
336 "(waiting to be read)\n"
337 " count = %d\n", la, c);
341 len += sprintf(buf + len, "\nFree IPBUF link: ");
342 spin_lock(&ipb_free.lock);
343 ipblink_for_each(bid, &ipb_free) {
344 len += sprintf(buf + len, "%d ", bid);
346 spin_unlock(&ipb_free.lock);
347 len += sprintf(buf + len, "\n");
348 len += sprintf(buf + len, "IPBFULL error count: %ld\n",