]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/dsp/dspgateway/dsp_mem.c
e21bd069619038b40b109edf4288d4578e0f6952
[linux-2.6-omap-h63xx.git] / drivers / dsp / dspgateway / dsp_mem.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  * Conversion to mempool API and ARM MMU section mapping
9  * by Paul Mundt <paul.mundt@nokia.com>
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * version 2 as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * 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., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  *
25  */
26
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/fs.h>
30 #include <linux/fb.h>
31 #include <linux/interrupt.h>
32 #include <linux/delay.h>
33 #include <linux/mempool.h>
34 #include <linux/clk.h>
35 #include <asm/uaccess.h>
36 #include <asm/io.h>
37 #include <asm/irq.h>
38 #include <asm/pgalloc.h>
39 #include <asm/pgtable.h>
40 #include <mach/tc.h>
41 #include <mach/omapfb.h>
42 #include <mach/dsp.h>
43 #include <mach/mailbox.h>
44 #include <mach/mmu.h>
45 #include "dsp_mbcmd.h"
46 #include "dsp.h"
47 #include "ipbuf.h"
48
49 #if 0
50 #if defined(CONFIG_ARCH_OMAP1)
51 #include "../../mach-omap1/mmu.h"
52 #elif defined(CONFIG_ARCH_OMAP2)
53 #include "../../mach-omap2/mmu.h"
54 #endif
55 #endif
56
57 #include "mmu.h"
58
59 static struct mem_sync_struct mem_sync;
60
61 int dsp_mem_sync_inc(void)
62 {
63         if (dsp_mem_enable((void *)dspmem_base) < 0)
64                 return -1;
65         if (mem_sync.DARAM)
66                 mem_sync.DARAM->ad_arm++;
67         if (mem_sync.SARAM)
68                 mem_sync.SARAM->ad_arm++;
69         if (mem_sync.SDRAM)
70                 mem_sync.SDRAM->ad_arm++;
71         dsp_mem_disable((void *)dspmem_base);
72
73         return 0;
74 }
75
76 /*
77  * dsp_mem_sync_config() is called from mbox1 workqueue
78  */
79 int dsp_mem_sync_config(struct mem_sync_struct *sync)
80 {
81         size_t sync_seq_sz = sizeof(struct sync_seq);
82
83 #ifdef OLD_BINARY_SUPPORT
84         if (sync == NULL) {
85                 memset(&mem_sync, 0, sizeof(struct mem_sync_struct));
86                 return 0;
87         }
88 #endif
89         if ((dsp_mem_type(sync->DARAM, sync_seq_sz) != MEM_TYPE_DARAM) ||
90             (dsp_mem_type(sync->SARAM, sync_seq_sz) != MEM_TYPE_SARAM) ||
91             (dsp_mem_type(sync->SDRAM, sync_seq_sz) != MEM_TYPE_EXTERN)) {
92                 printk(KERN_ERR
93                        "omapdsp: mem_sync address validation failure!\n"
94                        "  mem_sync.DARAM = 0x%p,\n"
95                        "  mem_sync.SARAM = 0x%p,\n"
96                        "  mem_sync.SDRAM = 0x%p,\n",
97                        sync->DARAM, sync->SARAM, sync->SDRAM);
98                 return -1;
99         }
100
101         memcpy(&mem_sync, sync, sizeof(struct mem_sync_struct));
102
103         return 0;
104 }
105
106
107 enum dsp_mem_type_e dsp_mem_type(void *vadr, size_t len)
108 {
109         void *ds = (void *)daram_base;
110         void *de = (void *)daram_base + daram_size;
111         void *ss = (void *)saram_base;
112         void *se = (void *)saram_base + saram_size;
113         int ret;
114
115         if ((vadr >= ds) && (vadr < de)) {
116                 if (vadr + len > de)
117                         return MEM_TYPE_CROSSING;
118                 else
119                         return MEM_TYPE_DARAM;
120         } else if ((vadr >= ss) && (vadr < se)) {
121                 if (vadr + len > se)
122                         return MEM_TYPE_CROSSING;
123                 else
124                         return MEM_TYPE_SARAM;
125         } else {
126                 down_read(&dsp_mmu.exmap_sem);
127                 if (exmap_valid(&dsp_mmu, vadr, len))
128                         ret = MEM_TYPE_EXTERN;
129                 else
130                         ret = MEM_TYPE_NONE;
131                 up_read(&dsp_mmu.exmap_sem);
132                 return ret;
133         }
134 }
135
136 int dsp_address_validate(void *p, size_t len, char *fmt, ...)
137 {
138         char s[64];
139         va_list args;
140
141         if (dsp_mem_type(p, len) > 0)
142                 return 0;
143
144         if (fmt == NULL)
145                 goto out;
146
147         va_start(args, fmt);
148         vsprintf(s, fmt, args);
149         va_end(args);
150         printk(KERN_ERR
151                "omapdsp: %s address(0x%p) and size(0x%x) is not valid!\n"
152                "(crossing different type of memories, or external memory\n"
153                "space where no actual memory is mapped)\n", s, p, len);
154  out:
155         return -1;
156 }
157
158 #ifdef CONFIG_OMAP_DSP_FBEXPORT
159
160 static inline unsigned long lineup_offset(unsigned long adr,
161                                           unsigned long ref,
162                                           unsigned long mask)
163 {
164         unsigned long newadr;
165
166         newadr = (adr & ~mask) | (ref & mask);
167         if (newadr < adr)
168                 newadr += mask + 1;
169         return newadr;
170 }
171
172 /*
173  * fb update functions:
174  * fbupd_response() is executed by the workqueue.
175  * fbupd_cb() is called when fb update is done, in interrupt context.
176  * mbox_fbupd() is called when KFUNC:FBCTL:UPD is received from DSP.
177  */
178 static void fbupd_response(struct work_struct *unused)
179 {
180         int status;
181
182         status = mbcompose_send(KFUNC, KFUNC_FBCTL, FBCTL_UPD);
183         if (status == 0)
184                 return;
185
186         /* FIXME: DSP is busy !! */
187         printk(KERN_ERR
188                "omapdsp:"
189                "DSP is busy when trying to send FBCTL:UPD response!\n");
190 }
191
192 static DECLARE_WORK(fbupd_response_work, fbupd_response);
193
194 static void fbupd_cb(void *arg)
195 {
196         schedule_work(&fbupd_response_work);
197 }
198
199 void mbox_fbctl_upd(void)
200 {
201         struct omapfb_update_window win;
202         volatile unsigned short *buf = ipbuf_sys_da->d;
203
204         if (sync_with_dsp(&ipbuf_sys_da->s, TID_ANON, 5000) < 0) {
205                 printk(KERN_ERR "mbox: FBCTL:UPD - IPBUF sync failed!\n");
206                 return;
207         }
208         win.x = buf[0];
209         win.y = buf[1];
210         win.width = buf[2];
211         win.height = buf[3];
212         win.format = buf[4];
213         release_ipbuf_pvt(ipbuf_sys_da);
214
215 #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
216         if (!omapfb_ready) {
217                 printk(KERN_WARNING
218                        "omapdsp: fbupd() called while HWA742 is not ready!\n");
219                 return;
220         }
221 #endif
222         omapfb_update_window_async(registered_fb[0], &win, fbupd_cb, NULL);
223 }
224
225 #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
226 static int omapfb_notifier_cb(struct notifier_block *omapfb_nb,
227                               unsigned long event, void *fbi)
228 {
229         pr_info("omapfb_notifier_cb(): event = %s\n",
230                 (event == OMAPFB_EVENT_READY)    ? "READY" :
231                 (event == OMAPFB_EVENT_DISABLED) ? "DISABLED" : "Unknown");
232         if (event == OMAPFB_EVENT_READY)
233                 omapfb_ready = 1;
234         else if (event == OMAPFB_EVENT_DISABLED)
235                 omapfb_ready = 0;
236         return 0;
237 }
238 #endif
239
240 static int dsp_fbexport(dsp_long_t *dspadr)
241 {
242         dsp_long_t dspadr_actual;
243         unsigned long padr_sys, padr, fbsz_sys, fbsz;
244         int cnt;
245 #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
246         int status;
247 #endif
248
249         pr_debug( "omapdsp: frame buffer export\n");
250
251 #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
252         if (omapfb_nb) {
253                 printk(KERN_WARNING
254                        "omapdsp: frame buffer has been exported already!\n");
255                 return -EBUSY;
256         }
257 #endif
258
259         if (num_registered_fb == 0) {
260                 pr_info("omapdsp: frame buffer not registered.\n");
261                 return -EINVAL;
262         }
263         if (num_registered_fb != 1) {
264                 pr_info("omapdsp: %d frame buffers found. we use first one.\n",
265                         num_registered_fb);
266         }
267         padr_sys = registered_fb[0]->fix.smem_start;
268         fbsz_sys = registered_fb[0]->fix.smem_len;
269         if (fbsz_sys == 0) {
270                 printk(KERN_ERR
271                        "omapdsp: framebuffer doesn't seem to be configured "
272                        "correctly! (size=0)\n");
273                 return -EINVAL;
274         }
275
276         /*
277          * align padr and fbsz to 4kB boundary
278          * (should be noted to the user afterwards!)
279          */
280         padr = padr_sys & ~(SZ_4K-1);
281         fbsz = (fbsz_sys + padr_sys - padr + SZ_4K-1) & ~(SZ_4K-1);
282
283         /* line up dspadr offset with padr */
284         dspadr_actual =
285                 (fbsz > SZ_1M) ?  lineup_offset(*dspadr, padr, SZ_1M-1) :
286                 (fbsz > SZ_64K) ? lineup_offset(*dspadr, padr, SZ_64K-1) :
287                 /* (fbsz > SZ_4KB) ? */ *dspadr;
288         if (dspadr_actual != *dspadr)
289                 pr_debug(
290                         "omapdsp: actual dspadr for FBEXPORT = %08x\n",
291                         dspadr_actual);
292         *dspadr = dspadr_actual;
293
294         cnt = omap_mmu_exmap(&dsp_mmu, dspadr_actual, padr, fbsz,
295                              EXMAP_TYPE_FB);
296         if (cnt < 0) {
297                 printk(KERN_ERR "omapdsp: exmap failure.\n");
298                 return cnt;
299         }
300
301         if ((padr != padr_sys) || (fbsz != fbsz_sys)) {
302                 printk(KERN_WARNING
303 "  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
304 "  !!  screen base address or size is not aligned in 4kB:           !!\n"
305 "  !!    actual screen  adr = %08lx, size = %08lx                   !!\n"
306 "  !!    exporting      adr = %08lx, size = %08lx                   !!\n"
307 "  !!  Make sure that the framebuffer is allocated with 4kB-order!  !!\n"
308 "  !!  Otherwise DSP can corrupt the kernel memory.                 !!\n"
309 "  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",
310                        padr_sys, fbsz_sys, padr, fbsz);
311         }
312
313         /* increase the DMA priority */
314         set_emiff_dma_prio(15);
315
316 #ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
317         omapfb_nb = kzalloc(sizeof(struct omapfb_notifier_block), GFP_KERNEL);
318         if (omapfb_nb == NULL) {
319                 printk(KERN_ERR
320                        "omapdsp: failed to allocate memory for omapfb_nb!\n");
321                 omap_mmu_exunmap(&dsp_mmu, (unsigned long)dspadr);
322                 return -ENOMEM;
323         }
324
325         status = omapfb_register_client(omapfb_nb, omapfb_notifier_cb, NULL);
326         if (status)
327                 pr_info("omapfb_register_client(): failure(%d)\n", status);
328 #endif
329
330         return cnt;
331 }
332 #else
333 void mbox_fbctl_upd(void) { }
334 #endif
335
336 /* dsp/mem fops: backward compatibility */
337 static ssize_t dsp_mem_read(struct file *file, char __user *buf, size_t count,
338                             loff_t *ppos)
339 {
340         struct bin_attribute attr;
341
342         return __omap_mmu_mem_read(&dsp_mmu, &attr,
343                                    (char __user *)buf, *ppos, count);
344 }
345
346 static ssize_t dsp_mem_write(struct file *file, const char __user *buf,
347                              size_t count, loff_t *ppos)
348 {
349         struct bin_attribute attr;
350
351         return __omap_mmu_mem_write(&dsp_mmu, &attr,
352                                     (char __user *)buf, *ppos, count);
353 }
354
355 static int dsp_mem_ioctl(struct inode *inode, struct file *file,
356                          unsigned int cmd, unsigned long arg)
357 {
358         struct omap_dsp_mapinfo mapinfo;
359         __u32 size;
360
361         switch (cmd) {
362         case MEM_IOCTL_MMUINIT:
363                 if (dsp_mmu.exmap_tbl)
364                         omap_mmu_unregister(&dsp_mmu);
365                 dsp_mem_ipi_init();
366                 return omap_mmu_register(&dsp_mmu);
367
368         case MEM_IOCTL_EXMAP:
369                 if (copy_from_user(&mapinfo, (void __user *)arg,
370                                    sizeof(mapinfo)))
371                         return -EFAULT;
372                 return omap_mmu_exmap(&dsp_mmu, mapinfo.dspadr,
373                                       0, mapinfo.size, EXMAP_TYPE_MEM);
374
375         case MEM_IOCTL_EXUNMAP:
376                 return omap_mmu_exunmap(&dsp_mmu, (unsigned long)arg);
377
378         case MEM_IOCTL_EXMAP_FLUSH:
379                 omap_mmu_exmap_flush(&dsp_mmu);
380                 return 0;
381 #ifdef CONFIG_OMAP_DSP_FBEXPORT
382         case MEM_IOCTL_FBEXPORT:
383         {
384                 dsp_long_t dspadr;
385                 int ret;
386                 if (copy_from_user(&dspadr, (void __user *)arg,
387                                    sizeof(dsp_long_t)))
388                         return -EFAULT;
389                 ret = dsp_fbexport(&dspadr);
390                 if (copy_to_user((void __user *)arg, &dspadr,
391                                  sizeof(dsp_long_t)))
392                         return -EFAULT;
393                 return ret;
394         }
395 #endif
396         case MEM_IOCTL_MMUITACK:
397                 return dsp_mmu_itack();
398
399         case MEM_IOCTL_KMEM_RESERVE:
400
401                 if (copy_from_user(&size, (void __user *)arg,
402                                    sizeof(__u32)))
403                         return -EFAULT;
404                 return omap_mmu_kmem_reserve(&dsp_mmu, size);
405
406
407         case MEM_IOCTL_KMEM_RELEASE:
408                 omap_mmu_kmem_release();
409                 return 0;
410
411         default:
412                 return -ENOIOCTLCMD;
413         }
414 }
415
416 struct file_operations dsp_mem_fops = {
417         .owner   = THIS_MODULE,
418         .read    = dsp_mem_read,
419         .write   = dsp_mem_write,
420         .ioctl   = dsp_mem_ioctl,
421 };
422
423 void dsp_mem_start(void)
424 {
425         dsp_register_mem_cb(intmem_enable, intmem_disable);
426 }
427
428 void dsp_mem_stop(void)
429 {
430         memset(&mem_sync, 0, sizeof(struct mem_sync_struct));
431         dsp_unregister_mem_cb();
432 }
433
434 static void dsp_mmu_irq_work(struct work_struct *work)
435 {
436         struct omap_mmu *mmu = container_of(work, struct omap_mmu, irq_work);
437
438         if (dsp_cfgstat_get_stat() == CFGSTAT_READY) {
439                 dsp_err_set(ERRCODE_MMU, mmu->fault_address);
440                 return;
441         }
442         omap_mmu_itack(mmu);
443         pr_info("Resetting DSP...\n");
444         dsp_cpustat_request(CPUSTAT_RESET);
445         omap_mmu_enable(mmu, 0);
446 }
447
448 /*
449  * later half of dsp memory initialization
450  */
451 int dsp_mem_late_init(void)
452 {
453         int ret;
454
455         dsp_mem_ipi_init();
456
457         INIT_WORK(&dsp_mmu.irq_work, dsp_mmu_irq_work);
458         ret = omap_mmu_register(&dsp_mmu);
459         if (ret) {
460                 dsp_reset_idle_boot_base();
461                 goto out;
462         }
463         omap_dsp->mmu = &dsp_mmu;
464  out:
465         return ret;
466 }
467
468 int __init dsp_mem_init(void)
469 {
470 #ifdef CONFIG_ARCH_OMAP2
471         dsp_mmu.clk    = dsp_fck_handle;
472         dsp_mmu.memclk = dsp_ick_handle;
473 #elif defined(CONFIG_ARCH_OMAP1)
474         dsp_mmu.clk    = dsp_ck_handle;
475         dsp_mmu.memclk = api_ck_handle;
476 #endif
477         return 0;
478 }
479
480 void dsp_mem_exit(void)
481 {
482         dsp_reset_idle_boot_base();
483         omap_mmu_unregister(&dsp_mmu);
484 }