2 * linux/arch/arm/mach-omap/dsp/ipbuf.c
6 * Copyright (C) 2002-2005 Nokia Corporation
8 * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * 2005/06/06: DSP Gateway version 3.3
27 #include <linux/init.h>
28 #include <linux/sched.h>
29 #include <linux/interrupt.h>
30 #include <linux/workqueue.h>
31 #include <linux/device.h>
32 #include <asm/signal.h>
33 #include <asm/arch/dsp.h>
39 struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad;
40 static struct ipblink ipb_free = IPBLINK_INIT;
41 static int ipbuf_sys_hold_mem_active;
47 spin_lock(&ipb_free.lock);
48 INIT_IPBLINK(&ipb_free);
49 spin_unlock(&ipb_free.lock);
56 for (i = 0; i < ipbuf_sys_hold_mem_active; i++) {
57 dsp_mem_disable((void *)daram_base);
59 ipbuf_sys_hold_mem_active = 0;
62 int ipbuf_config(unsigned short ln, unsigned short lsz, void *base)
64 unsigned long lsz_byte = ((unsigned long)lsz) << 1;
72 if (((unsigned long)base) & 0x3) {
74 "omapdsp: global ipbuf address(0x%p) is not "
75 "32-bit aligned!\n", base);
79 if (dsp_address_validate(base, size, "global ipbuf") < 0)
82 ipbuf = kmalloc(sizeof(void *) * ln, GFP_KERNEL);
85 "omapdsp: memory allocation for ipbuf failed.\n");
88 for (i = 0; i < ln; i++) {
91 top = base + (sizeof(struct ipbuf) + lsz_byte) * i;
92 btm = base + (sizeof(struct ipbuf) + lsz_byte) * (i+1) - 1;
93 ipbuf[i] = (struct ipbuf *)top;
94 if (((unsigned long)top & 0xfffe0000) !=
95 ((unsigned long)btm & 0xfffe0000)) {
97 * an ipbuf line should not cross
101 "omapdsp: ipbuf[%d] crosses 64k-word boundary!\n"
102 " @0x%p, size=0x%08lx\n", i, top, lsz_byte);
110 ipbcfg.bsycnt = ln; /* DSP holds all ipbufs initially. */
114 "omapdsp: IPBUF configuration\n"
115 " %d words * %d lines at 0x%p.\n",
116 ipbcfg.lsz, ipbcfg.ln, ipbcfg.base);
126 int ipbuf_sys_config(void *p, enum arm_dsp_dir dir)
128 char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
130 if (((unsigned long)p) & 0x3) {
132 "omapdsp: system ipbuf(%s) address(0x%p) is "
133 "not 32-bit aligned!\n", dir_str, p);
136 if (dsp_address_validate(p, sizeof(struct ipbuf_sys),
137 "system ipbuf(%s)", dir_str) < 0)
139 if (dsp_mem_type(p, sizeof(struct ipbuf_sys)) != MEM_TYPE_EXTERN) {
141 "omapdsp: system ipbuf(%s) is placed in"
142 " DSP internal memory.\n"
143 " It will prevent DSP from idling.\n", dir_str);
144 ipbuf_sys_hold_mem_active++;
146 * dsp_mem_enable() never fails because
147 * it has been already enabled in dspcfg process and
148 * this will just increment the usecount.
150 dsp_mem_enable((void *)daram_base);
161 int ipbuf_p_validate(void *p, enum arm_dsp_dir dir)
163 char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
165 if (((unsigned long)p) & 0x3) {
167 "omapdsp: private ipbuf(%s) address(0x%p) is "
168 "not 32-bit aligned!\n", dir_str, p);
171 return dsp_address_validate(p, sizeof(struct ipbuf_p),
172 "private ipbuf(%s)", dir_str);
176 * Global IPBUF operations
178 unsigned short get_free_ipbuf(unsigned char tid)
182 if (dsp_mem_enable_ipbuf() < 0)
183 return OMAP_DSP_BID_NULL;
185 spin_lock(&ipb_free.lock);
187 if (ipblink_empty(&ipb_free)) {
188 /* FIXME: wait on queue when not available. */
189 bid = OMAP_DSP_BID_NULL;
193 ipbuf[bid]->la = tid; /* lock */
194 ipblink_del_top(&ipb_free, ipbuf);
196 spin_unlock(&ipb_free.lock);
197 dsp_mem_disable_ipbuf();
202 void release_ipbuf(unsigned short bid)
204 if (ipbuf[bid]->la == OMAP_DSP_TID_FREE) {
206 "omapdsp: attempt to release unlocked IPBUF[%d].\n",
209 * FIXME: re-calc bsycnt
213 ipbuf[bid]->la = OMAP_DSP_TID_FREE;
214 ipbuf[bid]->sa = OMAP_DSP_TID_FREE;
215 spin_lock(&ipb_free.lock);
216 ipblink_add_tail(&ipb_free, bid, ipbuf);
217 spin_unlock(&ipb_free.lock);
220 static int try_yld(unsigned short bid)
224 ipbuf[bid]->sa = OMAP_DSP_TID_ANON;
225 status = dsp_mbsend(MBCMD(BKYLD), 0, bid);
227 /* DSP is busy and ARM keeps this line. */
232 ipb_bsycnt_inc(&ipbcfg);
237 * balancing ipbuf lines with DSP
239 static void do_balance_ipbuf(void)
241 while (ipbcfg.bsycnt <= ipbcfg.ln / 4) {
244 bid = get_free_ipbuf(OMAP_DSP_TID_ANON);
245 if (bid == OMAP_DSP_BID_NULL)
247 if (try_yld(bid) < 0)
252 static DECLARE_WORK(balance_ipbuf_work, (void (*)(void *))do_balance_ipbuf,
255 void balance_ipbuf(void)
257 schedule_work(&balance_ipbuf_work);
260 /* for process context */
261 void unuse_ipbuf(unsigned short bid)
263 if (ipbcfg.bsycnt > ipbcfg.ln / 4) {
264 /* we don't have enough IPBUF lines. let's keep it. */
267 /* we have enough IPBUF lines. let's return this line to DSP. */
268 ipbuf[bid]->la = OMAP_DSP_TID_ANON;
274 /* for interrupt context */
275 void unuse_ipbuf_nowait(unsigned short bid)
282 * functions called from mailbox1 interrupt routine
285 void mbx1_err_ipbfull(void)
293 static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr,
299 for (bid = 0; bid < ipbcfg.ln; bid++) {
300 unsigned short la = ipbuf[bid]->la;
301 unsigned short ld = ipbuf[bid]->ld;
302 unsigned short c = ipbuf[bid]->c;
304 if (len > PAGE_SIZE - 100) {
305 len += sprintf(buf + len, "out of buffer.\n");
309 len += sprintf(buf + len, "ipbuf[%d]: adr = 0x%p\n",
311 if (la == OMAP_DSP_TID_FREE) {
312 len += sprintf(buf + len,
313 " DSPtask[%d]->Linux "
314 "(already read and now free for Linux)\n",
316 } else if (ld == OMAP_DSP_TID_FREE) {
317 len += sprintf(buf + len,
318 " Linux->DSPtask[%d] "
319 "(already read and now free for DSP)\n",
321 } else if (ipbuf_is_held(ld, bid)) {
322 len += sprintf(buf + len,
323 " DSPtask[%d]->Linux "
324 "(waiting to be read)\n"
325 " count = %d\n", ld, c);
327 len += sprintf(buf + len,
328 " Linux->DSPtask[%d] "
329 "(waiting to be read)\n"
330 " count = %d\n", la, c);
334 len += sprintf(buf + len, "\nFree IPBUF link: ");
335 spin_lock(&ipb_free.lock);
336 ipblink_for_each(bid, &ipb_free, ipbuf) {
337 len += sprintf(buf + len, "%d ", bid);
339 spin_unlock(&ipb_free.lock);
340 len += sprintf(buf + len, "\n");
341 len += sprintf(buf + len, "IPBFULL error count: %ld\n",
348 struct device_attribute dev_attr_ipbuf = __ATTR_RO(ipbuf);