]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/plat-omap/dsp/ipbuf.c
ebb482b05f04d467665952fa0eabbfba44170ee2
[linux-2.6-omap-h63xx.git] / arch / arm / plat-omap / dsp / ipbuf.c
1 /*
2  * linux/arch/arm/mach-omap/dsp/ipbuf.c
3  *
4  * IPBUF handler
5  *
6  * Copyright (C) 2002-2005 Nokia Corporation
7  *
8  * Written by Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
9  *
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.
14  *
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.
19  *
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
23  *
24  * 2005/06/06:  DSP Gateway version 3.3
25  */
26
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>
34 #include "dsp.h"
35 #include "ipbuf.h"
36
37 struct ipbuf **ipbuf;
38 struct ipbcfg ipbcfg;
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;
42
43 void ipbuf_stop(void)
44 {
45         int i;
46
47         spin_lock(&ipb_free.lock);
48         INIT_IPBLINK(&ipb_free);
49         spin_unlock(&ipb_free.lock);
50
51         ipbcfg.ln = 0;
52         if (ipbuf) {
53                 kfree(ipbuf);
54                 ipbuf = NULL;
55         }
56         for (i = 0; i < ipbuf_sys_hold_mem_active; i++) {
57                 dsp_mem_disable((void *)daram_base);
58         }
59         ipbuf_sys_hold_mem_active = 0;
60 }
61
62 int ipbuf_config(unsigned short ln, unsigned short lsz, void *base)
63 {
64         unsigned long lsz_byte = ((unsigned long)lsz) << 1;
65         size_t size;
66         int ret = 0;
67         int i;
68
69         /*
70          * global IPBUF
71          */
72         if (((unsigned long)base) & 0x3) {
73                 printk(KERN_ERR
74                        "omapdsp: global ipbuf address(0x%p) is not "
75                        "32-bit aligned!\n", base);
76                 return -EINVAL;
77         }
78         size = lsz_byte * ln;
79         if (dsp_address_validate(base, size, "global ipbuf") < 0)
80                 return -EINVAL;
81
82         ipbuf = kmalloc(sizeof(void *) * ln, GFP_KERNEL);
83         if (ipbuf == NULL) {
84                 printk(KERN_ERR
85                        "omapdsp: memory allocation for ipbuf failed.\n");
86                 return -ENOMEM;
87         }
88         for (i = 0; i < ln; i++) {
89                 void *top, *btm;
90
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)) {
96                         /*
97                          * an ipbuf line should not cross
98                          * 64k-word boundary.
99                          */
100                         printk(KERN_ERR
101                                "omapdsp: ipbuf[%d] crosses 64k-word boundary!\n"
102                                "  @0x%p, size=0x%08lx\n", i, top, lsz_byte);
103                         ret = -EINVAL;
104                         goto free_out;
105                 }
106         }
107         ipbcfg.ln       = ln;
108         ipbcfg.lsz      = lsz;
109         ipbcfg.base     = base;
110         ipbcfg.bsycnt   = ln;   /* DSP holds all ipbufs initially. */
111         ipbcfg.cnt_full = 0;
112
113         printk(KERN_INFO
114                "omapdsp: IPBUF configuration\n"
115                "           %d words * %d lines at 0x%p.\n",
116                ipbcfg.lsz, ipbcfg.ln, ipbcfg.base);
117
118         return ret;
119
120 free_out:
121         kfree(ipbuf);
122         ipbuf = NULL;
123         return ret;
124 }
125
126 int ipbuf_sys_config(void *p, enum arm_dsp_dir dir)
127 {
128         char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
129
130         if (((unsigned long)p) & 0x3) {
131                 printk(KERN_ERR
132                        "omapdsp: system ipbuf(%s) address(0x%p) is "
133                        "not 32-bit aligned!\n", dir_str, p);
134                 return -1;
135         }
136         if (dsp_address_validate(p, sizeof(struct ipbuf_sys),
137                                  "system ipbuf(%s)", dir_str) < 0)
138                 return -1;
139         if (dsp_mem_type(p, sizeof(struct ipbuf_sys)) != MEM_TYPE_EXTERN) {
140                 printk(KERN_WARNING
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++;
145                 /*
146                  * dsp_mem_enable() never fails because
147                  * it has been already enabled in dspcfg process and
148                  * this will just increment the usecount.
149                  */
150                 dsp_mem_enable((void *)daram_base);
151         }
152
153         if (dir == DIR_D2A)
154                 ipbuf_sys_da = p;
155         else
156                 ipbuf_sys_ad = p;
157         
158         return 0;
159 }
160
161 int ipbuf_p_validate(void *p, enum arm_dsp_dir dir)
162 {
163         char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
164
165         if (((unsigned long)p) & 0x3) {
166                 printk(KERN_ERR
167                        "omapdsp: private ipbuf(%s) address(0x%p) is "
168                        "not 32-bit aligned!\n", dir_str, p);
169                 return -1;
170         }
171         return dsp_address_validate(p, sizeof(struct ipbuf_p),
172                                     "private ipbuf(%s)", dir_str);
173 }
174
175 /*
176  * Global IPBUF operations
177  */
178 unsigned short get_free_ipbuf(unsigned char tid)
179 {
180         unsigned short bid;
181
182         if (dsp_mem_enable_ipbuf() < 0)
183                 return OMAP_DSP_BID_NULL;
184
185         spin_lock(&ipb_free.lock);
186
187         if (ipblink_empty(&ipb_free)) {
188                 /* FIXME: wait on queue when not available.  */
189                 bid = OMAP_DSP_BID_NULL;
190                 goto out;
191         }
192         bid = ipb_free.top;
193         ipbuf[bid]->la = tid;   /* lock */
194         ipblink_del_top(&ipb_free, ipbuf);
195 out:
196         spin_unlock(&ipb_free.lock);
197         dsp_mem_disable_ipbuf();
198
199         return bid;
200 }
201
202 void release_ipbuf(unsigned short bid)
203 {
204         if (ipbuf[bid]->la == OMAP_DSP_TID_FREE) {
205                 printk(KERN_WARNING
206                        "omapdsp: attempt to release unlocked IPBUF[%d].\n",
207                        bid);
208                 /*
209                  * FIXME: re-calc bsycnt
210                  */
211                 return;
212         }
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);
218 }
219
220 static int try_yld(unsigned short bid)
221 {
222         int status;
223
224         ipbuf[bid]->sa = OMAP_DSP_TID_ANON;
225         status = dsp_mbsend(MBCMD(BKYLD), 0, bid);
226         if (status < 0) {
227                 /* DSP is busy and ARM keeps this line. */
228                 release_ipbuf(bid);
229                 return status;
230         }
231
232         ipb_bsycnt_inc(&ipbcfg);
233         return 0;
234 }
235
236 /*
237  * balancing ipbuf lines with DSP
238  */
239 static void do_balance_ipbuf(void)
240 {
241         while (ipbcfg.bsycnt <= ipbcfg.ln / 4) {
242                 unsigned short bid;
243
244                 bid = get_free_ipbuf(OMAP_DSP_TID_ANON);
245                 if (bid == OMAP_DSP_BID_NULL)
246                         return;
247                 if (try_yld(bid) < 0)
248                         return;
249         }
250 }
251
252 static DECLARE_WORK(balance_ipbuf_work, (void (*)(void *))do_balance_ipbuf,
253                     NULL);
254
255 void balance_ipbuf(void)
256 {
257         schedule_work(&balance_ipbuf_work);
258 }
259
260 /* for process context */
261 void unuse_ipbuf(unsigned short bid)
262 {
263         if (ipbcfg.bsycnt > ipbcfg.ln / 4) {
264                 /* we don't have enough IPBUF lines. let's keep it. */
265                 release_ipbuf(bid);
266         } else {
267                 /* we have enough IPBUF lines. let's return this line to DSP. */
268                 ipbuf[bid]->la = OMAP_DSP_TID_ANON;
269                 try_yld(bid);
270                 balance_ipbuf();
271         }
272 }
273
274 /* for interrupt context */
275 void unuse_ipbuf_nowait(unsigned short bid)
276 {
277         release_ipbuf(bid);
278         balance_ipbuf();
279 }
280
281 /*
282  * functions called from mailbox1 interrupt routine
283  */
284
285 void mbx1_err_ipbfull(void)
286 {
287         ipbcfg.cnt_full++;
288 }
289
290 /*
291  * sysfs files
292  */
293 static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr,
294                           char *buf)
295 {
296         int len = 0;
297         unsigned short bid;
298
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;
303
304                 if (len > PAGE_SIZE - 100) {
305                         len += sprintf(buf + len, "out of buffer.\n");
306                         goto finish;
307                 }
308
309                 len += sprintf(buf + len, "ipbuf[%d]: adr = 0x%p\n",
310                                bid, ipbuf[bid]);
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",
315                                        ld);
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",
320                                        la);
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);
326                 } else {
327                         len += sprintf(buf + len,
328                                        "  Linux->DSPtask[%d] "
329                                        "(waiting to be read)\n"
330                                        "  count = %d\n", la, c);
331                 }
332         }
333
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);
338         }
339         spin_unlock(&ipb_free.lock);
340         len += sprintf(buf + len, "\n");
341         len += sprintf(buf + len, "IPBFULL error count: %ld\n",
342                        ipbcfg.cnt_full);
343
344 finish:
345         return len;
346 }
347
348 struct device_attribute dev_attr_ipbuf = __ATTR_RO(ipbuf);