]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/tidspbridge/pmgr/cmm.c
Merge remote-tracking branch 'asoc/fix/omap' into asoc-linus
[karo-tx-linux.git] / drivers / staging / tidspbridge / pmgr / cmm.c
1 /*
2  * cmm.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * The Communication(Shared) Memory Management(CMM) module provides
7  * shared memory management services for DSP/BIOS Bridge data streaming
8  * and messaging.
9  *
10  * Multiple shared memory segments can be registered with CMM.
11  * Each registered SM segment is represented by a SM "allocator" that
12  * describes a block of physically contiguous shared memory used for
13  * future allocations by CMM.
14  *
15  * Memory is coalesced back to the appropriate heap when a buffer is
16  * freed.
17  *
18  * Notes:
19  *   Va: Virtual address.
20  *   Pa: Physical or kernel system address.
21  *
22  * Copyright (C) 2005-2006 Texas Instruments, Inc.
23  *
24  * This package is free software; you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License version 2 as
26  * published by the Free Software Foundation.
27  *
28  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
29  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
30  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
31  */
32 #include <linux/types.h>
33 #include <linux/list.h>
34
35 /*  ----------------------------------- DSP/BIOS Bridge */
36 #include <dspbridge/dbdefs.h>
37
38 /*  ----------------------------------- OS Adaptation Layer */
39 #include <dspbridge/sync.h>
40
41 /*  ----------------------------------- Platform Manager */
42 #include <dspbridge/dev.h>
43 #include <dspbridge/proc.h>
44
45 /*  ----------------------------------- This */
46 #include <dspbridge/cmm.h>
47
48 /*  ----------------------------------- Defines, Data Structures, Typedefs */
49 #define NEXT_PA(pnode)   (pnode->pa + pnode->size)
50
51 /* Other bus/platform translations */
52 #define DSPPA2GPPPA(base, x, y)  ((x)+(y))
53 #define GPPPA2DSPPA(base, x, y)  ((x)-(y))
54
55 /*
56  *  Allocators define a block of contiguous memory used for future allocations.
57  *
58  *      sma - shared memory allocator.
59  *      vma - virtual memory allocator.(not used).
60  */
61 struct cmm_allocator {          /* sma */
62         unsigned int shm_base;  /* Start of physical SM block */
63         u32 sm_size;            /* Size of SM block in bytes */
64         unsigned int vm_base;   /* Start of VM block. (Dev driver
65                                          * context for 'sma') */
66         u32 dsp_phys_addr_offset;       /* DSP PA to GPP PA offset for this
67                                          * SM space */
68         s8 c_factor;            /* DSPPa to GPPPa Conversion Factor */
69         unsigned int dsp_base;  /* DSP virt base byte address */
70         u32 dsp_size;   /* DSP seg size in bytes */
71         struct cmm_object *cmm_mgr;     /* back ref to parent mgr */
72         /* node list of available memory */
73         struct list_head free_list;
74         /* node list of memory in use */
75         struct list_head in_use_list;
76 };
77
78 struct cmm_xlator {             /* Pa<->Va translator object */
79         /* CMM object this translator associated */
80         struct cmm_object *cmm_mgr;
81         /*
82          *  Client process virtual base address that corresponds to phys SM
83          *  base address for translator's seg_id.
84          *  Only 1 segment ID currently supported.
85          */
86         unsigned int virt_base; /* virtual base address */
87         u32 virt_size;          /* size of virt space in bytes */
88         u32 seg_id;             /* Segment Id */
89 };
90
91 /* CMM Mgr */
92 struct cmm_object {
93         /*
94          * Cmm Lock is used to serialize access mem manager for multi-threads.
95          */
96         struct mutex cmm_lock;  /* Lock to access cmm mgr */
97         struct list_head node_free_list;        /* Free list of memory nodes */
98         u32 min_block_size;     /* Min SM block; default 16 bytes */
99         u32 page_size;  /* Memory Page size (1k/4k) */
100         /* GPP SM segment ptrs */
101         struct cmm_allocator *pa_gppsm_seg_tab[CMM_MAXGPPSEGS];
102 };
103
104 /* Default CMM Mgr attributes */
105 static struct cmm_mgrattrs cmm_dfltmgrattrs = {
106         /* min_block_size, min block size(bytes) allocated by cmm mgr */
107         16
108 };
109
110 /* Default allocation attributes */
111 static struct cmm_attrs cmm_dfltalctattrs = {
112         1               /* seg_id, default segment Id for allocator */
113 };
114
115 /* Address translator default attrs */
116 static struct cmm_xlatorattrs cmm_dfltxlatorattrs = {
117         /* seg_id, does not have to match cmm_dfltalctattrs ul_seg_id */
118         1,
119         0,                      /* dsp_bufs */
120         0,                      /* dsp_buf_size */
121         NULL,                   /* vm_base */
122         0,                      /* vm_size */
123 };
124
125 /* SM node representing a block of memory. */
126 struct cmm_mnode {
127         struct list_head link;  /* must be 1st element */
128         u32 pa;         /* Phys addr */
129         u32 va;                 /* Virtual address in device process context */
130         u32 size;               /* SM block size in bytes */
131         u32 client_proc;        /* Process that allocated this mem block */
132 };
133
134 /*  ----------------------------------- Function Prototypes */
135 static void add_to_free_list(struct cmm_allocator *allocator,
136                              struct cmm_mnode *pnode);
137 static struct cmm_allocator *get_allocator(struct cmm_object *cmm_mgr_obj,
138                                            u32 ul_seg_id);
139 static struct cmm_mnode *get_free_block(struct cmm_allocator *allocator,
140                                         u32 usize);
141 static struct cmm_mnode *get_node(struct cmm_object *cmm_mgr_obj, u32 dw_pa,
142                                   u32 dw_va, u32 ul_size);
143 /* get available slot for new allocator */
144 static s32 get_slot(struct cmm_object *cmm_mgr_obj);
145 static void un_register_gppsm_seg(struct cmm_allocator *psma);
146
147 /*
148  *  ======== cmm_calloc_buf ========
149  *  Purpose:
150  *      Allocate a SM buffer, zero contents, and return the physical address
151  *      and optional driver context virtual address(pp_buf_va).
152  *
153  *      The freelist is sorted in increasing size order. Get the first
154  *      block that satifies the request and sort the remaining back on
155  *      the freelist; if large enough. The kept block is placed on the
156  *      inUseList.
157  */
158 void *cmm_calloc_buf(struct cmm_object *hcmm_mgr, u32 usize,
159                      struct cmm_attrs *pattrs, void **pp_buf_va)
160 {
161         struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
162         void *buf_pa = NULL;
163         struct cmm_mnode *pnode = NULL;
164         struct cmm_mnode *new_node = NULL;
165         struct cmm_allocator *allocator = NULL;
166         u32 delta_size;
167         u8 *pbyte = NULL;
168         s32 cnt;
169
170         if (pattrs == NULL)
171                 pattrs = &cmm_dfltalctattrs;
172
173         if (pp_buf_va != NULL)
174                 *pp_buf_va = NULL;
175
176         if (cmm_mgr_obj && (usize != 0)) {
177                 if (pattrs->seg_id > 0) {
178                         /* SegId > 0 is SM */
179                         /* get the allocator object for this segment id */
180                         allocator =
181                             get_allocator(cmm_mgr_obj, pattrs->seg_id);
182                         /* keep block size a multiple of min_block_size */
183                         usize =
184                             ((usize - 1) & ~(cmm_mgr_obj->min_block_size -
185                                              1))
186                             + cmm_mgr_obj->min_block_size;
187                         mutex_lock(&cmm_mgr_obj->cmm_lock);
188                         pnode = get_free_block(allocator, usize);
189                 }
190                 if (pnode) {
191                         delta_size = (pnode->size - usize);
192                         if (delta_size >= cmm_mgr_obj->min_block_size) {
193                                 /* create a new block with the leftovers and
194                                  * add to freelist */
195                                 new_node =
196                                     get_node(cmm_mgr_obj, pnode->pa + usize,
197                                              pnode->va + usize,
198                                              (u32) delta_size);
199                                 /* leftovers go free */
200                                 add_to_free_list(allocator, new_node);
201                                 /* adjust our node's size */
202                                 pnode->size = usize;
203                         }
204                         /* Tag node with client process requesting allocation
205                          * We'll need to free up a process's alloc'd SM if the
206                          * client process goes away.
207                          */
208                         /* Return TGID instead of process handle */
209                         pnode->client_proc = current->tgid;
210
211                         /* put our node on InUse list */
212                         list_add_tail(&pnode->link, &allocator->in_use_list);
213                         buf_pa = (void *)pnode->pa;     /* physical address */
214                         /* clear mem */
215                         pbyte = (u8 *) pnode->va;
216                         for (cnt = 0; cnt < (s32) usize; cnt++, pbyte++)
217                                 *pbyte = 0;
218
219                         if (pp_buf_va != NULL) {
220                                 /* Virtual address */
221                                 *pp_buf_va = (void *)pnode->va;
222                         }
223                 }
224                 mutex_unlock(&cmm_mgr_obj->cmm_lock);
225         }
226         return buf_pa;
227 }
228
229 /*
230  *  ======== cmm_create ========
231  *  Purpose:
232  *      Create a communication memory manager object.
233  */
234 int cmm_create(struct cmm_object **ph_cmm_mgr,
235                       struct dev_object *hdev_obj,
236                       const struct cmm_mgrattrs *mgr_attrts)
237 {
238         struct cmm_object *cmm_obj = NULL;
239         int status = 0;
240
241         *ph_cmm_mgr = NULL;
242         /* create, zero, and tag a cmm mgr object */
243         cmm_obj = kzalloc(sizeof(struct cmm_object), GFP_KERNEL);
244         if (!cmm_obj)
245                 return -ENOMEM;
246
247         if (mgr_attrts == NULL)
248                 mgr_attrts = &cmm_dfltmgrattrs; /* set defaults */
249
250         /* save away smallest block allocation for this cmm mgr */
251         cmm_obj->min_block_size = mgr_attrts->min_block_size;
252         cmm_obj->page_size = PAGE_SIZE;
253
254         /* create node free list */
255         INIT_LIST_HEAD(&cmm_obj->node_free_list);
256         mutex_init(&cmm_obj->cmm_lock);
257         *ph_cmm_mgr = cmm_obj;
258
259         return status;
260 }
261
262 /*
263  *  ======== cmm_destroy ========
264  *  Purpose:
265  *      Release the communication memory manager resources.
266  */
267 int cmm_destroy(struct cmm_object *hcmm_mgr, bool force)
268 {
269         struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
270         struct cmm_info temp_info;
271         int status = 0;
272         s32 slot_seg;
273         struct cmm_mnode *node, *tmp;
274
275         if (!hcmm_mgr) {
276                 status = -EFAULT;
277                 return status;
278         }
279         mutex_lock(&cmm_mgr_obj->cmm_lock);
280         /* If not force then fail if outstanding allocations exist */
281         if (!force) {
282                 /* Check for outstanding memory allocations */
283                 status = cmm_get_info(hcmm_mgr, &temp_info);
284                 if (!status) {
285                         if (temp_info.total_in_use_cnt > 0) {
286                                 /* outstanding allocations */
287                                 status = -EPERM;
288                         }
289                 }
290         }
291         if (!status) {
292                 /* UnRegister SM allocator */
293                 for (slot_seg = 0; slot_seg < CMM_MAXGPPSEGS; slot_seg++) {
294                         if (cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg] != NULL) {
295                                 un_register_gppsm_seg
296                                     (cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg]);
297                                 /* Set slot to NULL for future reuse */
298                                 cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg] = NULL;
299                         }
300                 }
301         }
302         list_for_each_entry_safe(node, tmp, &cmm_mgr_obj->node_free_list,
303                         link) {
304                 list_del(&node->link);
305                 kfree(node);
306         }
307         mutex_unlock(&cmm_mgr_obj->cmm_lock);
308         if (!status) {
309                 /* delete CS & cmm mgr object */
310                 mutex_destroy(&cmm_mgr_obj->cmm_lock);
311                 kfree(cmm_mgr_obj);
312         }
313         return status;
314 }
315
316 /*
317  *  ======== cmm_free_buf ========
318  *  Purpose:
319  *      Free the given buffer.
320  */
321 int cmm_free_buf(struct cmm_object *hcmm_mgr, void *buf_pa, u32 ul_seg_id)
322 {
323         struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
324         int status = -EFAULT;
325         struct cmm_mnode *curr, *tmp;
326         struct cmm_allocator *allocator;
327         struct cmm_attrs *pattrs;
328
329         if (ul_seg_id == 0) {
330                 pattrs = &cmm_dfltalctattrs;
331                 ul_seg_id = pattrs->seg_id;
332         }
333         if (!hcmm_mgr || !(ul_seg_id > 0)) {
334                 status = -EFAULT;
335                 return status;
336         }
337
338         allocator = get_allocator(cmm_mgr_obj, ul_seg_id);
339         if (!allocator)
340                 return status;
341
342         mutex_lock(&cmm_mgr_obj->cmm_lock);
343         list_for_each_entry_safe(curr, tmp, &allocator->in_use_list, link) {
344                 if (curr->pa == (u32) buf_pa) {
345                         list_del(&curr->link);
346                         add_to_free_list(allocator, curr);
347                         status = 0;
348                         break;
349                 }
350         }
351         mutex_unlock(&cmm_mgr_obj->cmm_lock);
352
353         return status;
354 }
355
356 /*
357  *  ======== cmm_get_handle ========
358  *  Purpose:
359  *      Return the communication memory manager object for this device.
360  *      This is typically called from the client process.
361  */
362 int cmm_get_handle(void *hprocessor, struct cmm_object ** ph_cmm_mgr)
363 {
364         int status = 0;
365         struct dev_object *hdev_obj;
366
367         if (hprocessor != NULL)
368                 status = proc_get_dev_object(hprocessor, &hdev_obj);
369         else
370                 hdev_obj = dev_get_first();     /* default */
371
372         if (!status)
373                 status = dev_get_cmm_mgr(hdev_obj, ph_cmm_mgr);
374
375         return status;
376 }
377
378 /*
379  *  ======== cmm_get_info ========
380  *  Purpose:
381  *      Return the current memory utilization information.
382  */
383 int cmm_get_info(struct cmm_object *hcmm_mgr,
384                         struct cmm_info *cmm_info_obj)
385 {
386         struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
387         u32 ul_seg;
388         int status = 0;
389         struct cmm_allocator *altr;
390         struct cmm_mnode *curr;
391
392         if (!hcmm_mgr) {
393                 status = -EFAULT;
394                 return status;
395         }
396         mutex_lock(&cmm_mgr_obj->cmm_lock);
397         cmm_info_obj->num_gppsm_segs = 0;       /* # of SM segments */
398         /* Total # of outstanding alloc */
399         cmm_info_obj->total_in_use_cnt = 0;
400         /* min block size */
401         cmm_info_obj->min_block_size = cmm_mgr_obj->min_block_size;
402         /* check SM memory segments */
403         for (ul_seg = 1; ul_seg <= CMM_MAXGPPSEGS; ul_seg++) {
404                 /* get the allocator object for this segment id */
405                 altr = get_allocator(cmm_mgr_obj, ul_seg);
406                 if (!altr)
407                         continue;
408                 cmm_info_obj->num_gppsm_segs++;
409                 cmm_info_obj->seg_info[ul_seg - 1].seg_base_pa =
410                         altr->shm_base - altr->dsp_size;
411                 cmm_info_obj->seg_info[ul_seg - 1].total_seg_size =
412                         altr->dsp_size + altr->sm_size;
413                 cmm_info_obj->seg_info[ul_seg - 1].gpp_base_pa =
414                         altr->shm_base;
415                 cmm_info_obj->seg_info[ul_seg - 1].gpp_size =
416                         altr->sm_size;
417                 cmm_info_obj->seg_info[ul_seg - 1].dsp_base_va =
418                         altr->dsp_base;
419                 cmm_info_obj->seg_info[ul_seg - 1].dsp_size =
420                         altr->dsp_size;
421                 cmm_info_obj->seg_info[ul_seg - 1].seg_base_va =
422                         altr->vm_base - altr->dsp_size;
423                 cmm_info_obj->seg_info[ul_seg - 1].in_use_cnt = 0;
424
425                 list_for_each_entry(curr, &altr->in_use_list, link) {
426                         cmm_info_obj->total_in_use_cnt++;
427                         cmm_info_obj->seg_info[ul_seg - 1].in_use_cnt++;
428                 }
429         }
430         mutex_unlock(&cmm_mgr_obj->cmm_lock);
431         return status;
432 }
433
434 /*
435  *  ======== cmm_register_gppsm_seg ========
436  *  Purpose:
437  *      Register a block of SM with the CMM to be used for later GPP SM
438  *      allocations.
439  */
440 int cmm_register_gppsm_seg(struct cmm_object *hcmm_mgr,
441                                   u32 dw_gpp_base_pa, u32 ul_size,
442                                   u32 dsp_addr_offset, s8 c_factor,
443                                   u32 dw_dsp_base, u32 ul_dsp_size,
444                                   u32 *sgmt_id, u32 gpp_base_va)
445 {
446         struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
447         struct cmm_allocator *psma = NULL;
448         int status = 0;
449         struct cmm_mnode *new_node;
450         s32 slot_seg;
451
452         dev_dbg(bridge, "%s: dw_gpp_base_pa %x ul_size %x dsp_addr_offset %x "
453                         "dw_dsp_base %x ul_dsp_size %x gpp_base_va %x\n",
454                         __func__, dw_gpp_base_pa, ul_size, dsp_addr_offset,
455                         dw_dsp_base, ul_dsp_size, gpp_base_va);
456
457         if (!hcmm_mgr)
458                 return -EFAULT;
459
460         /* make sure we have room for another allocator */
461         mutex_lock(&cmm_mgr_obj->cmm_lock);
462
463         slot_seg = get_slot(cmm_mgr_obj);
464         if (slot_seg < 0) {
465                 status = -EPERM;
466                 goto func_end;
467         }
468
469         /* Check if input ul_size is big enough to alloc at least one block */
470         if (ul_size < cmm_mgr_obj->min_block_size) {
471                 status = -EINVAL;
472                 goto func_end;
473         }
474
475         /* create, zero, and tag an SM allocator object */
476         psma = kzalloc(sizeof(struct cmm_allocator), GFP_KERNEL);
477         if (!psma) {
478                 status = -ENOMEM;
479                 goto func_end;
480         }
481
482         psma->cmm_mgr = hcmm_mgr;       /* ref to parent */
483         psma->shm_base = dw_gpp_base_pa;        /* SM Base phys */
484         psma->sm_size = ul_size;        /* SM segment size in bytes */
485         psma->vm_base = gpp_base_va;
486         psma->dsp_phys_addr_offset = dsp_addr_offset;
487         psma->c_factor = c_factor;
488         psma->dsp_base = dw_dsp_base;
489         psma->dsp_size = ul_dsp_size;
490         if (psma->vm_base == 0) {
491                 status = -EPERM;
492                 goto func_end;
493         }
494         /* return the actual segment identifier */
495         *sgmt_id = (u32) slot_seg + 1;
496
497         INIT_LIST_HEAD(&psma->free_list);
498         INIT_LIST_HEAD(&psma->in_use_list);
499
500         /* Get a mem node for this hunk-o-memory */
501         new_node = get_node(cmm_mgr_obj, dw_gpp_base_pa,
502                         psma->vm_base, ul_size);
503         /* Place node on the SM allocator's free list */
504         if (new_node) {
505                 list_add_tail(&new_node->link, &psma->free_list);
506         } else {
507                 status = -ENOMEM;
508                 goto func_end;
509         }
510         /* make entry */
511         cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg] = psma;
512
513 func_end:
514         /* Cleanup allocator */
515         if (status && psma)
516                 un_register_gppsm_seg(psma);
517         mutex_unlock(&cmm_mgr_obj->cmm_lock);
518
519         return status;
520 }
521
522 /*
523  *  ======== cmm_un_register_gppsm_seg ========
524  *  Purpose:
525  *      UnRegister GPP SM segments with the CMM.
526  */
527 int cmm_un_register_gppsm_seg(struct cmm_object *hcmm_mgr,
528                                      u32 ul_seg_id)
529 {
530         struct cmm_object *cmm_mgr_obj = (struct cmm_object *)hcmm_mgr;
531         int status = 0;
532         struct cmm_allocator *psma;
533         u32 ul_id = ul_seg_id;
534
535         if (!hcmm_mgr)
536                 return -EFAULT;
537
538         if (ul_seg_id == CMM_ALLSEGMENTS)
539                 ul_id = 1;
540
541         if ((ul_id <= 0) || (ul_id > CMM_MAXGPPSEGS))
542                 return -EINVAL;
543
544         /*
545          * FIXME: CMM_MAXGPPSEGS == 1. why use a while cycle? Seems to me like
546          * the ul_seg_id is not needed here. It must be always 1.
547          */
548         while (ul_id <= CMM_MAXGPPSEGS) {
549                 mutex_lock(&cmm_mgr_obj->cmm_lock);
550                 /* slot = seg_id-1 */
551                 psma = cmm_mgr_obj->pa_gppsm_seg_tab[ul_id - 1];
552                 if (psma != NULL) {
553                         un_register_gppsm_seg(psma);
554                         /* Set alctr ptr to NULL for future reuse */
555                         cmm_mgr_obj->pa_gppsm_seg_tab[ul_id - 1] = NULL;
556                 } else if (ul_seg_id != CMM_ALLSEGMENTS) {
557                         status = -EPERM;
558                 }
559                 mutex_unlock(&cmm_mgr_obj->cmm_lock);
560                 if (ul_seg_id != CMM_ALLSEGMENTS)
561                         break;
562
563                 ul_id++;
564         }       /* end while */
565         return status;
566 }
567
568 /*
569  *  ======== un_register_gppsm_seg ========
570  *  Purpose:
571  *      UnRegister the SM allocator by freeing all its resources and
572  *      nulling cmm mgr table entry.
573  *  Note:
574  *      This routine is always called within cmm lock crit sect.
575  */
576 static void un_register_gppsm_seg(struct cmm_allocator *psma)
577 {
578         struct cmm_mnode *curr, *tmp;
579
580         /* free nodes on free list */
581         list_for_each_entry_safe(curr, tmp, &psma->free_list, link) {
582                 list_del(&curr->link);
583                 kfree(curr);
584         }
585
586         /* free nodes on InUse list */
587         list_for_each_entry_safe(curr, tmp, &psma->in_use_list, link) {
588                 list_del(&curr->link);
589                 kfree(curr);
590         }
591
592         if ((void *)psma->vm_base != NULL)
593                 MEM_UNMAP_LINEAR_ADDRESS((void *)psma->vm_base);
594
595         /* Free allocator itself */
596         kfree(psma);
597 }
598
599 /*
600  *  ======== get_slot ========
601  *  Purpose:
602  *      An available slot # is returned. Returns negative on failure.
603  */
604 static s32 get_slot(struct cmm_object *cmm_mgr_obj)
605 {
606         s32 slot_seg = -1;      /* neg on failure */
607         /* get first available slot in cmm mgr SMSegTab[] */
608         for (slot_seg = 0; slot_seg < CMM_MAXGPPSEGS; slot_seg++) {
609                 if (cmm_mgr_obj->pa_gppsm_seg_tab[slot_seg] == NULL)
610                         break;
611
612         }
613         if (slot_seg == CMM_MAXGPPSEGS)
614                 slot_seg = -1;  /* failed */
615
616         return slot_seg;
617 }
618
619 /*
620  *  ======== get_node ========
621  *  Purpose:
622  *      Get a memory node from freelist or create a new one.
623  */
624 static struct cmm_mnode *get_node(struct cmm_object *cmm_mgr_obj, u32 dw_pa,
625                                   u32 dw_va, u32 ul_size)
626 {
627         struct cmm_mnode *pnode;
628
629         /* Check cmm mgr's node freelist */
630         if (list_empty(&cmm_mgr_obj->node_free_list)) {
631                 pnode = kzalloc(sizeof(struct cmm_mnode), GFP_KERNEL);
632                 if (!pnode)
633                         return NULL;
634         } else {
635                 /* surely a valid element */
636                 pnode = list_first_entry(&cmm_mgr_obj->node_free_list,
637                                 struct cmm_mnode, link);
638                 list_del_init(&pnode->link);
639         }
640
641         pnode->pa = dw_pa;
642         pnode->va = dw_va;
643         pnode->size = ul_size;
644
645         return pnode;
646 }
647
648 /*
649  *  ======== delete_node ========
650  *  Purpose:
651  *      Put a memory node on the cmm nodelist for later use.
652  *      Doesn't actually delete the node. Heap thrashing friendly.
653  */
654 static void delete_node(struct cmm_object *cmm_mgr_obj, struct cmm_mnode *pnode)
655 {
656         list_add_tail(&pnode->link, &cmm_mgr_obj->node_free_list);
657 }
658
659 /*
660  * ====== get_free_block ========
661  *  Purpose:
662  *      Scan the free block list and return the first block that satisfies
663  *      the size.
664  */
665 static struct cmm_mnode *get_free_block(struct cmm_allocator *allocator,
666                                         u32 usize)
667 {
668         struct cmm_mnode *node, *tmp;
669
670         if (!allocator)
671                 return NULL;
672
673         list_for_each_entry_safe(node, tmp, &allocator->free_list, link) {
674                 if (usize <= node->size) {
675                         list_del(&node->link);
676                         return node;
677                 }
678         }
679
680         return NULL;
681 }
682
683 /*
684  *  ======== add_to_free_list ========
685  *  Purpose:
686  *      Coalesce node into the freelist in ascending size order.
687  */
688 static void add_to_free_list(struct cmm_allocator *allocator,
689                              struct cmm_mnode *node)
690 {
691         struct cmm_mnode *curr;
692
693         if (!node) {
694                 pr_err("%s: failed - node is NULL\n", __func__);
695                 return;
696         }
697
698         list_for_each_entry(curr, &allocator->free_list, link) {
699                 if (NEXT_PA(curr) == node->pa) {
700                         curr->size += node->size;
701                         delete_node(allocator->cmm_mgr, node);
702                         return;
703                 }
704                 if (curr->pa == NEXT_PA(node)) {
705                         curr->pa = node->pa;
706                         curr->va = node->va;
707                         curr->size += node->size;
708                         delete_node(allocator->cmm_mgr, node);
709                         return;
710                 }
711         }
712         list_for_each_entry(curr, &allocator->free_list, link) {
713                 if (curr->size >= node->size) {
714                         list_add_tail(&node->link, &curr->link);
715                         return;
716                 }
717         }
718         list_add_tail(&node->link, &allocator->free_list);
719 }
720
721 /*
722  * ======== get_allocator ========
723  *  Purpose:
724  *      Return the allocator for the given SM Segid.
725  *      SegIds:  1,2,3..max.
726  */
727 static struct cmm_allocator *get_allocator(struct cmm_object *cmm_mgr_obj,
728                                            u32 ul_seg_id)
729 {
730         return cmm_mgr_obj->pa_gppsm_seg_tab[ul_seg_id - 1];
731 }
732
733 /*
734  *  The CMM_Xlator[xxx] routines below are used by Node and Stream
735  *  to perform SM address translation to the client process address space.
736  *  A "translator" object is created by a node/stream for each SM seg used.
737  */
738
739 /*
740  *  ======== cmm_xlator_create ========
741  *  Purpose:
742  *      Create an address translator object.
743  */
744 int cmm_xlator_create(struct cmm_xlatorobject **xlator,
745                              struct cmm_object *hcmm_mgr,
746                              struct cmm_xlatorattrs *xlator_attrs)
747 {
748         struct cmm_xlator *xlator_object = NULL;
749         int status = 0;
750
751         *xlator = NULL;
752         if (xlator_attrs == NULL)
753                 xlator_attrs = &cmm_dfltxlatorattrs;    /* set defaults */
754
755         xlator_object = kzalloc(sizeof(struct cmm_xlator), GFP_KERNEL);
756         if (xlator_object != NULL) {
757                 xlator_object->cmm_mgr = hcmm_mgr;      /* ref back to CMM */
758                 /* SM seg_id */
759                 xlator_object->seg_id = xlator_attrs->seg_id;
760         } else {
761                 status = -ENOMEM;
762         }
763         if (!status)
764                 *xlator = (struct cmm_xlatorobject *)xlator_object;
765
766         return status;
767 }
768
769 /*
770  *  ======== cmm_xlator_alloc_buf ========
771  */
772 void *cmm_xlator_alloc_buf(struct cmm_xlatorobject *xlator, void *va_buf,
773                            u32 pa_size)
774 {
775         struct cmm_xlator *xlator_obj = (struct cmm_xlator *)xlator;
776         void *pbuf = NULL;
777         void *tmp_va_buff;
778         struct cmm_attrs attrs;
779
780         if (xlator_obj) {
781                 attrs.seg_id = xlator_obj->seg_id;
782                 __raw_writel(0, va_buf);
783                 /* Alloc SM */
784                 pbuf =
785                     cmm_calloc_buf(xlator_obj->cmm_mgr, pa_size, &attrs, NULL);
786                 if (pbuf) {
787                         /* convert to translator(node/strm) process Virtual
788                          * address */
789                          tmp_va_buff = cmm_xlator_translate(xlator,
790                                                          pbuf, CMM_PA2VA);
791                         __raw_writel((u32)tmp_va_buff, va_buf);
792                 }
793         }
794         return pbuf;
795 }
796
797 /*
798  *  ======== cmm_xlator_free_buf ========
799  *  Purpose:
800  *      Free the given SM buffer and descriptor.
801  *      Does not free virtual memory.
802  */
803 int cmm_xlator_free_buf(struct cmm_xlatorobject *xlator, void *buf_va)
804 {
805         struct cmm_xlator *xlator_obj = (struct cmm_xlator *)xlator;
806         int status = -EPERM;
807         void *buf_pa = NULL;
808
809         if (xlator_obj) {
810                 /* convert Va to Pa so we can free it. */
811                 buf_pa = cmm_xlator_translate(xlator, buf_va, CMM_VA2PA);
812                 if (buf_pa) {
813                         status = cmm_free_buf(xlator_obj->cmm_mgr, buf_pa,
814                                               xlator_obj->seg_id);
815                         if (status) {
816                                 /* Uh oh, this shouldn't happen. Descriptor
817                                  * gone! */
818                                 pr_err("%s, line %d: Assertion failed\n",
819                                        __FILE__, __LINE__);
820                         }
821                 }
822         }
823         return status;
824 }
825
826 /*
827  *  ======== cmm_xlator_info ========
828  *  Purpose:
829  *      Set/Get translator info.
830  */
831 int cmm_xlator_info(struct cmm_xlatorobject *xlator, u8 ** paddr,
832                            u32 ul_size, u32 segm_id, bool set_info)
833 {
834         struct cmm_xlator *xlator_obj = (struct cmm_xlator *)xlator;
835         int status = 0;
836
837         if (xlator_obj) {
838                 if (set_info) {
839                         /* set translators virtual address range */
840                         xlator_obj->virt_base = (u32) *paddr;
841                         xlator_obj->virt_size = ul_size;
842                 } else {        /* return virt base address */
843                         *paddr = (u8 *) xlator_obj->virt_base;
844                 }
845         } else {
846                 status = -EFAULT;
847         }
848         return status;
849 }
850
851 /*
852  *  ======== cmm_xlator_translate ========
853  */
854 void *cmm_xlator_translate(struct cmm_xlatorobject *xlator, void *paddr,
855                            enum cmm_xlatetype xtype)
856 {
857         u32 dw_addr_xlate = 0;
858         struct cmm_xlator *xlator_obj = (struct cmm_xlator *)xlator;
859         struct cmm_object *cmm_mgr_obj = NULL;
860         struct cmm_allocator *allocator = NULL;
861         u32 dw_offset = 0;
862
863         if (!xlator_obj)
864                 goto loop_cont;
865
866         cmm_mgr_obj = (struct cmm_object *)xlator_obj->cmm_mgr;
867         /* get this translator's default SM allocator */
868         allocator = cmm_mgr_obj->pa_gppsm_seg_tab[xlator_obj->seg_id - 1];
869         if (!allocator)
870                 goto loop_cont;
871
872         if ((xtype == CMM_VA2DSPPA) || (xtype == CMM_VA2PA) ||
873             (xtype == CMM_PA2VA)) {
874                 if (xtype == CMM_PA2VA) {
875                         /* Gpp Va = Va Base + offset */
876                         dw_offset = (u8 *) paddr - (u8 *) (allocator->shm_base -
877                                                            allocator->
878                                                            dsp_size);
879                         dw_addr_xlate = xlator_obj->virt_base + dw_offset;
880                         /* Check if translated Va base is in range */
881                         if ((dw_addr_xlate < xlator_obj->virt_base) ||
882                             (dw_addr_xlate >=
883                              (xlator_obj->virt_base +
884                               xlator_obj->virt_size))) {
885                                 dw_addr_xlate = 0;      /* bad address */
886                         }
887                 } else {
888                         /* Gpp PA =  Gpp Base + offset */
889                         dw_offset =
890                             (u8 *) paddr - (u8 *) xlator_obj->virt_base;
891                         dw_addr_xlate =
892                             allocator->shm_base - allocator->dsp_size +
893                             dw_offset;
894                 }
895         } else {
896                 dw_addr_xlate = (u32) paddr;
897         }
898         /*Now convert address to proper target physical address if needed */
899         if ((xtype == CMM_VA2DSPPA) || (xtype == CMM_PA2DSPPA)) {
900                 /* Got Gpp Pa now, convert to DSP Pa */
901                 dw_addr_xlate =
902                     GPPPA2DSPPA((allocator->shm_base - allocator->dsp_size),
903                                 dw_addr_xlate,
904                                 allocator->dsp_phys_addr_offset *
905                                 allocator->c_factor);
906         } else if (xtype == CMM_DSPPA2PA) {
907                 /* Got DSP Pa, convert to GPP Pa */
908                 dw_addr_xlate =
909                     DSPPA2GPPPA(allocator->shm_base - allocator->dsp_size,
910                                 dw_addr_xlate,
911                                 allocator->dsp_phys_addr_offset *
912                                 allocator->c_factor);
913         }
914 loop_cont:
915         return (void *)dw_addr_xlate;
916 }