]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/plat-omap/dsp/ipbuf.c
ARM: OMAP: DSPGW: Peripheral (kfunc) control
[linux-2.6-omap-h63xx.git] / arch / arm / plat-omap / dsp / ipbuf.c
1 /*
2  * This file is part of OMAP DSP driver (DSP Gateway version 3.3.1)
3  *
4  * Copyright (C) 2002-2006 Nokia Corporation. All rights reserved.
5  *
6  * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
7  *
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.
11  *
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.
16  *
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
20  * 02110-1301 USA
21  *
22  */
23
24 #include <linux/sched.h>
25 #include <linux/device.h>
26 #include <asm/arch/mailbox.h>
27 #include "dsp_mbcmd.h"
28 #include "dsp.h"
29 #include "ipbuf.h"
30
31 static struct ipbuf_head *g_ipbuf;
32 struct ipbcfg ipbcfg;
33 struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad;
34 static struct ipblink ipb_free = IPBLINK_INIT;
35 static int ipbuf_sys_hold_mem_active;
36
37 static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr,
38                           char *buf);
39 static struct device_attribute dev_attr_ipbuf = __ATTR_RO(ipbuf);
40
41 void ipbuf_stop(void)
42 {
43         int i;
44
45         device_remove_file(omap_dsp->dev, &dev_attr_ipbuf);
46
47         spin_lock(&ipb_free.lock);
48         RESET_IPBLINK(&ipb_free);
49         spin_unlock(&ipb_free.lock);
50
51         ipbcfg.ln = 0;
52         if (g_ipbuf) {
53                 kfree(g_ipbuf);
54                 g_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(u16 ln, u16 lsz, void *base)
63 {
64         size_t lsz_byte = ((size_t)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         g_ipbuf = kmalloc(sizeof(struct ipbuf_head) * ln, GFP_KERNEL);
83         if (g_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                 g_ipbuf[i].p = (struct ipbuf *)top;
94                 g_ipbuf[i].bid = i;
95                 if (((unsigned long)top & 0xfffe0000) !=
96                     ((unsigned long)btm & 0xfffe0000)) {
97                         /*
98                          * an ipbuf line should not cross
99                          * 64k-word boundary.
100                          */
101                         printk(KERN_ERR
102                                "omapdsp: ipbuf[%d] crosses 64k-word boundary!\n"
103                                "  @0x%p, size=0x%08x\n", i, top, lsz_byte);
104                         ret = -EINVAL;
105                         goto free_out;
106                 }
107         }
108         ipbcfg.ln       = ln;
109         ipbcfg.lsz      = lsz;
110         ipbcfg.base     = base;
111         ipbcfg.bsycnt   = ln;   /* DSP holds all ipbufs initially. */
112         ipbcfg.cnt_full = 0;
113
114         printk(KERN_INFO
115                "omapdsp: IPBUF configuration\n"
116                "           %d words * %d lines at 0x%p.\n",
117                ipbcfg.lsz, ipbcfg.ln, ipbcfg.base);
118
119         device_create_file(omap_dsp->dev, &dev_attr_ipbuf);
120
121         return ret;
122
123 free_out:
124         kfree(g_ipbuf);
125         g_ipbuf = NULL;
126         return ret;
127 }
128
129 int ipbuf_sys_config(void *p, arm_dsp_dir_t dir)
130 {
131         char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
132
133         if (((unsigned long)p) & 0x3) {
134                 printk(KERN_ERR
135                        "omapdsp: system ipbuf(%s) address(0x%p) is "
136                        "not 32-bit aligned!\n", dir_str, p);
137                 return -1;
138         }
139         if (dsp_address_validate(p, sizeof(struct ipbuf_sys),
140                                  "system ipbuf(%s)", dir_str) < 0)
141                 return -1;
142         if (dsp_mem_type(p, sizeof(struct ipbuf_sys)) != MEM_TYPE_EXTERN) {
143                 printk(KERN_WARNING
144                        "omapdsp: system ipbuf(%s) is placed in"
145                        " DSP internal memory.\n"
146                        "         It will prevent DSP from idling.\n", dir_str);
147                 ipbuf_sys_hold_mem_active++;
148                 /*
149                  * dsp_mem_enable() never fails because
150                  * it has been already enabled in dspcfg process and
151                  * this will just increment the usecount.
152                  */
153                 dsp_mem_enable((void *)daram_base);
154         }
155
156         if (dir == DIR_D2A)
157                 ipbuf_sys_da = p;
158         else
159                 ipbuf_sys_ad = p;
160
161         return 0;
162 }
163
164 int ipbuf_p_validate(void *p, arm_dsp_dir_t dir)
165 {
166         char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D";
167
168         if (((unsigned long)p) & 0x3) {
169                 printk(KERN_ERR
170                        "omapdsp: private ipbuf(%s) address(0x%p) is "
171                        "not 32-bit aligned!\n", dir_str, p);
172                 return -1;
173         }
174         return dsp_address_validate(p, sizeof(struct ipbuf_p),
175                                     "private ipbuf(%s)", dir_str);
176 }
177
178 /*
179  * Global IPBUF operations
180  */
181 struct ipbuf_head *bid_to_ipbuf(u16 bid)
182 {
183         return &g_ipbuf[bid];
184 }
185
186 struct ipbuf_head *get_free_ipbuf(u8 tid)
187 {
188         struct ipbuf_head *ipb_h;
189
190         if (dsp_mem_enable_ipbuf() < 0)
191                 return NULL;
192
193         spin_lock(&ipb_free.lock);
194
195         if (ipblink_empty(&ipb_free)) {
196                 /* FIXME: wait on queue when not available.  */
197                 ipb_h = NULL;
198                 goto out;
199         }
200         ipb_h = &g_ipbuf[ipb_free.top];
201         ipb_h->p->la = tid;     /* lock */
202         __ipblink_del_top(&ipb_free);
203 out:
204         spin_unlock(&ipb_free.lock);
205         dsp_mem_disable_ipbuf();
206
207         return ipb_h;
208 }
209
210 void release_ipbuf(struct ipbuf_head *ipb_h)
211 {
212         if (ipb_h->p->la == TID_FREE) {
213                 printk(KERN_WARNING
214                        "omapdsp: attempt to release unlocked IPBUF[%d].\n",
215                        ipb_h->bid);
216                 /*
217                  * FIXME: re-calc bsycnt
218                  */
219                 return;
220         }
221         ipb_h->p->la = TID_FREE;
222         ipb_h->p->sa = TID_FREE;
223         ipblink_add_tail(&ipb_free, ipb_h->bid);
224 }
225
226 static int try_yld(struct ipbuf_head *ipb_h)
227 {
228         int status;
229
230         ipb_h->p->sa = TID_ANON;
231         status = mbcompose_send(BKYLD, 0, ipb_h->bid);
232         if (status < 0) {
233                 /* DSP is busy and ARM keeps this line. */
234                 release_ipbuf(ipb_h);
235                 return status;
236         }
237
238         ipb_bsycnt_inc(&ipbcfg);
239         return 0;
240 }
241
242 /*
243  * balancing ipbuf lines with DSP
244  */
245 static void do_balance_ipbuf(void)
246 {
247         while (ipbcfg.bsycnt <= ipbcfg.ln / 4) {
248                 struct ipbuf_head *ipb_h;
249
250                 if ((ipb_h = get_free_ipbuf(TID_ANON)) == NULL)
251                         return;
252                 if (try_yld(ipb_h) < 0)
253                         return;
254         }
255 }
256
257 static DECLARE_WORK(balance_ipbuf_work, (void (*)(void *))do_balance_ipbuf,
258                     NULL);
259
260 void balance_ipbuf(void)
261 {
262         schedule_work(&balance_ipbuf_work);
263 }
264
265 /* for process context */
266 void unuse_ipbuf(struct ipbuf_head *ipb_h)
267 {
268         if (ipbcfg.bsycnt > ipbcfg.ln / 4) {
269                 /* we don't have enough IPBUF lines. let's keep it. */
270                 release_ipbuf(ipb_h);
271         } else {
272                 /* we have enough IPBUF lines. let's return this line to DSP. */
273                 ipb_h->p->la = TID_ANON;
274                 try_yld(ipb_h);
275                 balance_ipbuf();
276         }
277 }
278
279 /* for interrupt context */
280 void unuse_ipbuf_nowait(struct ipbuf_head *ipb_h)
281 {
282         release_ipbuf(ipb_h);
283         balance_ipbuf();
284 }
285
286 /*
287  * functions called from mailbox interrupt routine
288  */
289
290 void mbox_err_ipbfull(void)
291 {
292         ipbcfg.cnt_full++;
293 }
294
295 /*
296  * sysfs files
297  */
298 static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr,
299                           char *buf)
300 {
301         int len = 0;
302         u16 bid;
303
304         for (bid = 0; bid < ipbcfg.ln; bid++) {
305                 struct ipbuf_head *ipb_h = &g_ipbuf[bid];
306                 u16 la = ipb_h->p->la;
307                 u16 ld = ipb_h->p->ld;
308                 u16 c  = ipb_h->p->c;
309
310                 if (len > PAGE_SIZE - 100) {
311                         len += sprintf(buf + len, "out of buffer.\n");
312                         goto finish;
313                 }
314
315                 len += sprintf(buf + len, "ipbuf[%d]: adr = 0x%p\n",
316                                bid, ipb_h->p);
317                 if (la == TID_FREE) {
318                         len += sprintf(buf + len,
319                                        "  DSPtask[%d]->Linux "
320                                        "(already read and now free for Linux)\n",
321                                        ld);
322                 } else if (ld == TID_FREE) {
323                         len += sprintf(buf + len,
324                                        "  Linux->DSPtask[%d] "
325                                        "(already read and now free for DSP)\n",
326                                        la);
327                 } else if (ipbuf_is_held(ld, bid)) {
328                         len += sprintf(buf + len,
329                                        "  DSPtask[%d]->Linux "
330                                        "(waiting to be read)\n"
331                                        "  count = %d\n", ld, c);
332                 } else {
333                         len += sprintf(buf + len,
334                                        "  Linux->DSPtask[%d] "
335                                        "(waiting to be read)\n"
336                                        "  count = %d\n", la, c);
337                 }
338         }
339
340         len += sprintf(buf + len, "\nFree IPBUF link: ");
341         spin_lock(&ipb_free.lock);
342         ipblink_for_each(bid, &ipb_free) {
343                 len += sprintf(buf + len, "%d ", bid);
344         }
345         spin_unlock(&ipb_free.lock);
346         len += sprintf(buf + len, "\n");
347         len += sprintf(buf + len, "IPBFULL error count: %ld\n",
348                        ipbcfg.cnt_full);
349
350 finish:
351         return len;
352 }