2 * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
4 * This source file is released under GPL v2 license (no other versions).
5 * See the COPYING file included in the main directory of this source
6 * distribution for the license terms and conditions.
11 * This file contains the implementation of virtual memory management object
19 #include <linux/slab.h>
22 #include <sound/pcm.h>
24 #define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *))
25 #define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE)
28 * Find or create vm block based on requested @size.
29 * @size must be page aligned.
31 static struct ct_vm_block
*
32 get_vm_block(struct ct_vm
*vm
, unsigned int size
)
34 struct ct_vm_block
*block
= NULL
, *entry
;
35 struct list_head
*pos
;
37 size
= CT_PAGE_ALIGN(size
);
38 if (size
> vm
->size
) {
39 printk(KERN_ERR
"ctxfi: Fail! No sufficient device virtural "
40 "memory space available!\n");
44 mutex_lock(&vm
->lock
);
45 list_for_each(pos
, &vm
->unused
) {
46 entry
= list_entry(pos
, struct ct_vm_block
, list
);
47 if (entry
->size
>= size
)
48 break; /* found a block that is big enough */
50 if (pos
== &vm
->unused
)
53 if (entry
->size
== size
) {
54 /* Move the vm node from unused list to used list directly */
55 list_del(&entry
->list
);
56 list_add(&entry
->list
, &vm
->used
);
62 block
= kzalloc(sizeof(*block
), GFP_KERNEL
);
66 block
->addr
= entry
->addr
;
68 list_add(&block
->list
, &vm
->used
);
74 mutex_unlock(&vm
->lock
);
78 static void put_vm_block(struct ct_vm
*vm
, struct ct_vm_block
*block
)
80 struct ct_vm_block
*entry
, *pre_ent
;
81 struct list_head
*pos
, *pre
;
83 block
->size
= CT_PAGE_ALIGN(block
->size
);
85 mutex_lock(&vm
->lock
);
86 list_del(&block
->list
);
87 vm
->size
+= block
->size
;
89 list_for_each(pos
, &vm
->unused
) {
90 entry
= list_entry(pos
, struct ct_vm_block
, list
);
91 if (entry
->addr
>= (block
->addr
+ block
->size
))
92 break; /* found a position */
94 if (pos
== &vm
->unused
) {
95 list_add_tail(&block
->list
, &vm
->unused
);
98 if ((block
->addr
+ block
->size
) == entry
->addr
) {
99 entry
->addr
= block
->addr
;
100 entry
->size
+= block
->size
;
103 __list_add(&block
->list
, pos
->prev
, pos
);
110 while (pre
!= &vm
->unused
) {
111 entry
= list_entry(pos
, struct ct_vm_block
, list
);
112 pre_ent
= list_entry(pre
, struct ct_vm_block
, list
);
113 if ((pre_ent
->addr
+ pre_ent
->size
) > entry
->addr
)
116 pre_ent
->size
+= entry
->size
;
122 mutex_unlock(&vm
->lock
);
125 /* Map host addr (kmalloced/vmalloced) to device logical addr. */
126 static struct ct_vm_block
*
127 ct_vm_map(struct ct_vm
*vm
, struct snd_pcm_substream
*substream
, int size
)
129 struct ct_vm_block
*block
;
130 unsigned int pte_start
;
134 block
= get_vm_block(vm
, size
);
136 printk(KERN_ERR
"ctxfi: No virtual memory block that is big "
137 "enough to allocate!\n");
142 pte_start
= (block
->addr
>> CT_PAGE_SHIFT
);
143 pages
= block
->size
>> CT_PAGE_SHIFT
;
144 for (i
= 0; i
< pages
; i
++) {
146 addr
= snd_pcm_sgbuf_get_addr(substream
, i
<< CT_PAGE_SHIFT
);
147 ptp
[pte_start
+ i
] = addr
;
154 static void ct_vm_unmap(struct ct_vm
*vm
, struct ct_vm_block
*block
)
157 put_vm_block(vm
, block
);
161 * return the host (kmalloced) addr of the @index-th device
162 * page talbe page on success, or NULL on failure.
163 * The first returned NULL indicates the termination.
166 ct_get_ptp_virt(struct ct_vm
*vm
, int index
)
170 addr
= (index
>= CT_PTP_NUM
) ? NULL
: vm
->ptp
[index
];
175 int ct_vm_create(struct ct_vm
**rvm
)
178 struct ct_vm_block
*block
;
183 vm
= kzalloc(sizeof(*vm
), GFP_KERNEL
);
187 mutex_init(&vm
->lock
);
189 /* Allocate page table pages */
190 for (i
= 0; i
< CT_PTP_NUM
; i
++) {
191 vm
->ptp
[i
] = kmalloc(PAGE_SIZE
, GFP_KERNEL
);
192 if (NULL
== vm
->ptp
[i
])
196 /* no page table pages are allocated */
200 vm
->size
= CT_ADDRS_PER_PAGE
* i
;
201 /* Initialise remaining ptps */
202 for (; i
< CT_PTP_NUM
; i
++)
206 vm
->unmap
= ct_vm_unmap
;
207 vm
->get_ptp_virt
= ct_get_ptp_virt
;
208 INIT_LIST_HEAD(&vm
->unused
);
209 INIT_LIST_HEAD(&vm
->used
);
210 block
= kzalloc(sizeof(*block
), GFP_KERNEL
);
213 block
->size
= vm
->size
;
214 list_add(&block
->list
, &vm
->unused
);
221 /* The caller must ensure no mapping pages are being used
222 * by hardware before calling this function */
223 void ct_vm_destroy(struct ct_vm
*vm
)
226 struct list_head
*pos
;
227 struct ct_vm_block
*entry
;
229 /* free used and unused list nodes */
230 while (!list_empty(&vm
->used
)) {
233 entry
= list_entry(pos
, struct ct_vm_block
, list
);
236 while (!list_empty(&vm
->unused
)) {
237 pos
= vm
->unused
.next
;
239 entry
= list_entry(pos
, struct ct_vm_block
, list
);
243 /* free allocated page table pages */
244 for (i
= 0; i
< CT_PTP_NUM
; i
++)