Commit | Line | Data |
---|---|---|
8cc72361 WYC |
1 | /** |
2 | * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved. | |
3 | * | |
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. | |
7 | * | |
8 | * @File ctamixer.c | |
9 | * | |
10 | * @Brief | |
11 | * This file contains the implementation of the Audio Mixer | |
12 | * resource management object. | |
13 | * | |
14 | * @Author Liu Chun | |
15 | * @Date May 21 2008 | |
16 | * | |
17 | */ | |
18 | ||
19 | #include "ctamixer.h" | |
20 | #include "cthardware.h" | |
21 | #include <linux/slab.h> | |
22 | ||
23 | #define AMIXER_RESOURCE_NUM 256 | |
24 | #define SUM_RESOURCE_NUM 256 | |
25 | ||
26 | #define AMIXER_Y_IMMEDIATE 1 | |
27 | ||
28 | #define BLANK_SLOT 4094 | |
29 | ||
30 | static int amixer_master(struct rsc *rsc) | |
31 | { | |
32 | rsc->conj = 0; | |
33 | return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0]; | |
34 | } | |
35 | ||
36 | static int amixer_next_conj(struct rsc *rsc) | |
37 | { | |
38 | rsc->conj++; | |
39 | return container_of(rsc, struct amixer, rsc)->idx[rsc->conj]; | |
40 | } | |
41 | ||
42 | static int amixer_index(const struct rsc *rsc) | |
43 | { | |
44 | return container_of(rsc, struct amixer, rsc)->idx[rsc->conj]; | |
45 | } | |
46 | ||
47 | static int amixer_output_slot(const struct rsc *rsc) | |
48 | { | |
49 | return (amixer_index(rsc) << 4) + 0x4; | |
50 | } | |
51 | ||
52 | static struct rsc_ops amixer_basic_rsc_ops = { | |
53 | .master = amixer_master, | |
54 | .next_conj = amixer_next_conj, | |
55 | .index = amixer_index, | |
56 | .output_slot = amixer_output_slot, | |
57 | }; | |
58 | ||
59 | static int amixer_set_input(struct amixer *amixer, struct rsc *rsc) | |
60 | { | |
514eef9c | 61 | struct hw *hw; |
8cc72361 | 62 | |
514eef9c | 63 | hw = amixer->rsc.hw; |
8cc72361 WYC |
64 | hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); |
65 | amixer->input = rsc; | |
35ebf6e7 | 66 | if (!rsc) |
8cc72361 WYC |
67 | hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT); |
68 | else | |
69 | hw->amixer_set_x(amixer->rsc.ctrl_blk, | |
70 | rsc->ops->output_slot(rsc)); | |
71 | ||
72 | return 0; | |
73 | } | |
74 | ||
75 | /* y is a 14-bit immediate constant */ | |
76 | static int amixer_set_y(struct amixer *amixer, unsigned int y) | |
77 | { | |
514eef9c | 78 | struct hw *hw; |
8cc72361 | 79 | |
514eef9c | 80 | hw = amixer->rsc.hw; |
8cc72361 WYC |
81 | hw->amixer_set_y(amixer->rsc.ctrl_blk, y); |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv) | |
87 | { | |
514eef9c | 88 | struct hw *hw; |
8cc72361 | 89 | |
514eef9c | 90 | hw = amixer->rsc.hw; |
8cc72361 WYC |
91 | hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv); |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | static int amixer_set_sum(struct amixer *amixer, struct sum *sum) | |
97 | { | |
514eef9c | 98 | struct hw *hw; |
8cc72361 | 99 | |
514eef9c | 100 | hw = amixer->rsc.hw; |
8cc72361 | 101 | amixer->sum = sum; |
35ebf6e7 | 102 | if (!sum) { |
8cc72361 WYC |
103 | hw->amixer_set_se(amixer->rsc.ctrl_blk, 0); |
104 | } else { | |
105 | hw->amixer_set_se(amixer->rsc.ctrl_blk, 1); | |
106 | hw->amixer_set_sadr(amixer->rsc.ctrl_blk, | |
107 | sum->rsc.ops->index(&sum->rsc)); | |
108 | } | |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
113 | static int amixer_commit_write(struct amixer *amixer) | |
114 | { | |
514eef9c TI |
115 | struct hw *hw; |
116 | unsigned int index; | |
117 | int i; | |
118 | struct rsc *input; | |
119 | struct sum *sum; | |
8cc72361 | 120 | |
514eef9c | 121 | hw = amixer->rsc.hw; |
8cc72361 WYC |
122 | input = amixer->input; |
123 | sum = amixer->sum; | |
124 | ||
125 | /* Program master and conjugate resources */ | |
126 | amixer->rsc.ops->master(&amixer->rsc); | |
35ebf6e7 | 127 | if (input) |
8cc72361 WYC |
128 | input->ops->master(input); |
129 | ||
35ebf6e7 | 130 | if (sum) |
8cc72361 WYC |
131 | sum->rsc.ops->master(&sum->rsc); |
132 | ||
133 | for (i = 0; i < amixer->rsc.msr; i++) { | |
134 | hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk); | |
35ebf6e7 | 135 | if (input) { |
8cc72361 WYC |
136 | hw->amixer_set_x(amixer->rsc.ctrl_blk, |
137 | input->ops->output_slot(input)); | |
138 | input->ops->next_conj(input); | |
139 | } | |
35ebf6e7 | 140 | if (sum) { |
8cc72361 WYC |
141 | hw->amixer_set_sadr(amixer->rsc.ctrl_blk, |
142 | sum->rsc.ops->index(&sum->rsc)); | |
143 | sum->rsc.ops->next_conj(&sum->rsc); | |
144 | } | |
145 | index = amixer->rsc.ops->output_slot(&amixer->rsc); | |
146 | hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); | |
147 | amixer->rsc.ops->next_conj(&amixer->rsc); | |
148 | } | |
149 | amixer->rsc.ops->master(&amixer->rsc); | |
35ebf6e7 | 150 | if (input) |
8cc72361 WYC |
151 | input->ops->master(input); |
152 | ||
35ebf6e7 | 153 | if (sum) |
8cc72361 WYC |
154 | sum->rsc.ops->master(&sum->rsc); |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
159 | static int amixer_commit_raw_write(struct amixer *amixer) | |
160 | { | |
514eef9c TI |
161 | struct hw *hw; |
162 | unsigned int index; | |
8cc72361 | 163 | |
514eef9c | 164 | hw = amixer->rsc.hw; |
8cc72361 WYC |
165 | index = amixer->rsc.ops->output_slot(&amixer->rsc); |
166 | hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk); | |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
171 | static int amixer_get_y(struct amixer *amixer) | |
172 | { | |
514eef9c | 173 | struct hw *hw; |
8cc72361 | 174 | |
514eef9c | 175 | hw = amixer->rsc.hw; |
8cc72361 WYC |
176 | return hw->amixer_get_y(amixer->rsc.ctrl_blk); |
177 | } | |
178 | ||
179 | static int amixer_setup(struct amixer *amixer, struct rsc *input, | |
180 | unsigned int scale, struct sum *sum) | |
181 | { | |
182 | amixer_set_input(amixer, input); | |
183 | amixer_set_y(amixer, scale); | |
184 | amixer_set_sum(amixer, sum); | |
185 | amixer_commit_write(amixer); | |
186 | return 0; | |
187 | } | |
188 | ||
189 | static struct amixer_rsc_ops amixer_ops = { | |
190 | .set_input = amixer_set_input, | |
191 | .set_invalid_squash = amixer_set_invalid_squash, | |
192 | .set_scale = amixer_set_y, | |
193 | .set_sum = amixer_set_sum, | |
194 | .commit_write = amixer_commit_write, | |
195 | .commit_raw_write = amixer_commit_raw_write, | |
196 | .setup = amixer_setup, | |
197 | .get_scale = amixer_get_y, | |
198 | }; | |
199 | ||
200 | static int amixer_rsc_init(struct amixer *amixer, | |
201 | const struct amixer_desc *desc, | |
202 | struct amixer_mgr *mgr) | |
203 | { | |
514eef9c | 204 | int err; |
8cc72361 WYC |
205 | |
206 | err = rsc_init(&amixer->rsc, amixer->idx[0], | |
207 | AMIXER, desc->msr, mgr->mgr.hw); | |
208 | if (err) | |
209 | return err; | |
210 | ||
211 | /* Set amixer specific operations */ | |
212 | amixer->rsc.ops = &amixer_basic_rsc_ops; | |
213 | amixer->ops = &amixer_ops; | |
214 | amixer->input = NULL; | |
215 | amixer->sum = NULL; | |
216 | ||
217 | amixer_setup(amixer, NULL, 0, NULL); | |
218 | ||
219 | return 0; | |
220 | } | |
221 | ||
222 | static int amixer_rsc_uninit(struct amixer *amixer) | |
223 | { | |
224 | amixer_setup(amixer, NULL, 0, NULL); | |
225 | rsc_uninit(&amixer->rsc); | |
226 | amixer->ops = NULL; | |
227 | amixer->input = NULL; | |
228 | amixer->sum = NULL; | |
229 | return 0; | |
230 | } | |
231 | ||
232 | static int get_amixer_rsc(struct amixer_mgr *mgr, | |
233 | const struct amixer_desc *desc, | |
234 | struct amixer **ramixer) | |
235 | { | |
514eef9c TI |
236 | int err, i; |
237 | unsigned int idx; | |
238 | struct amixer *amixer; | |
8cc72361 WYC |
239 | unsigned long flags; |
240 | ||
241 | *ramixer = NULL; | |
242 | ||
243 | /* Allocate mem for amixer resource */ | |
244 | amixer = kzalloc(sizeof(*amixer), GFP_KERNEL); | |
68110661 TI |
245 | if (!amixer) |
246 | return -ENOMEM; | |
8cc72361 WYC |
247 | |
248 | /* Check whether there are sufficient | |
249 | * amixer resources to meet request. */ | |
68110661 | 250 | err = 0; |
8cc72361 WYC |
251 | spin_lock_irqsave(&mgr->mgr_lock, flags); |
252 | for (i = 0; i < desc->msr; i++) { | |
253 | err = mgr_get_resource(&mgr->mgr, 1, &idx); | |
254 | if (err) | |
255 | break; | |
256 | ||
257 | amixer->idx[i] = idx; | |
258 | } | |
259 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
260 | if (err) { | |
b3e0afe6 | 261 | printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n"); |
8cc72361 WYC |
262 | goto error; |
263 | } | |
264 | ||
265 | err = amixer_rsc_init(amixer, desc, mgr); | |
266 | if (err) | |
267 | goto error; | |
268 | ||
269 | *ramixer = amixer; | |
270 | ||
271 | return 0; | |
272 | ||
273 | error: | |
274 | spin_lock_irqsave(&mgr->mgr_lock, flags); | |
275 | for (i--; i >= 0; i--) | |
276 | mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); | |
277 | ||
278 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
279 | kfree(amixer); | |
280 | return err; | |
281 | } | |
282 | ||
283 | static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) | |
284 | { | |
285 | unsigned long flags; | |
514eef9c | 286 | int i; |
8cc72361 WYC |
287 | |
288 | spin_lock_irqsave(&mgr->mgr_lock, flags); | |
289 | for (i = 0; i < amixer->rsc.msr; i++) | |
290 | mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); | |
291 | ||
292 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
293 | amixer_rsc_uninit(amixer); | |
294 | kfree(amixer); | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) | |
300 | { | |
514eef9c | 301 | int err; |
8cc72361 WYC |
302 | struct amixer_mgr *amixer_mgr; |
303 | ||
304 | *ramixer_mgr = NULL; | |
305 | amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL); | |
35ebf6e7 | 306 | if (!amixer_mgr) |
8cc72361 WYC |
307 | return -ENOMEM; |
308 | ||
309 | err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw); | |
310 | if (err) | |
311 | goto error; | |
312 | ||
313 | spin_lock_init(&amixer_mgr->mgr_lock); | |
314 | ||
315 | amixer_mgr->get_amixer = get_amixer_rsc; | |
316 | amixer_mgr->put_amixer = put_amixer_rsc; | |
317 | ||
318 | *ramixer_mgr = amixer_mgr; | |
319 | ||
320 | return 0; | |
321 | ||
322 | error: | |
323 | kfree(amixer_mgr); | |
324 | return err; | |
325 | } | |
326 | ||
327 | int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) | |
328 | { | |
329 | rsc_mgr_uninit(&amixer_mgr->mgr); | |
330 | kfree(amixer_mgr); | |
331 | return 0; | |
332 | } | |
333 | ||
334 | /* SUM resource management */ | |
335 | ||
336 | static int sum_master(struct rsc *rsc) | |
337 | { | |
338 | rsc->conj = 0; | |
339 | return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0]; | |
340 | } | |
341 | ||
342 | static int sum_next_conj(struct rsc *rsc) | |
343 | { | |
344 | rsc->conj++; | |
345 | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | |
346 | } | |
347 | ||
348 | static int sum_index(const struct rsc *rsc) | |
349 | { | |
350 | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | |
351 | } | |
352 | ||
353 | static int sum_output_slot(const struct rsc *rsc) | |
354 | { | |
355 | return (sum_index(rsc) << 4) + 0xc; | |
356 | } | |
357 | ||
358 | static struct rsc_ops sum_basic_rsc_ops = { | |
359 | .master = sum_master, | |
360 | .next_conj = sum_next_conj, | |
361 | .index = sum_index, | |
362 | .output_slot = sum_output_slot, | |
363 | }; | |
364 | ||
365 | static int sum_rsc_init(struct sum *sum, | |
366 | const struct sum_desc *desc, | |
367 | struct sum_mgr *mgr) | |
368 | { | |
514eef9c | 369 | int err; |
8cc72361 WYC |
370 | |
371 | err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw); | |
372 | if (err) | |
373 | return err; | |
374 | ||
375 | sum->rsc.ops = &sum_basic_rsc_ops; | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
380 | static int sum_rsc_uninit(struct sum *sum) | |
381 | { | |
382 | rsc_uninit(&sum->rsc); | |
383 | return 0; | |
384 | } | |
385 | ||
386 | static int get_sum_rsc(struct sum_mgr *mgr, | |
387 | const struct sum_desc *desc, | |
388 | struct sum **rsum) | |
389 | { | |
514eef9c TI |
390 | int err, i; |
391 | unsigned int idx; | |
392 | struct sum *sum; | |
8cc72361 WYC |
393 | unsigned long flags; |
394 | ||
395 | *rsum = NULL; | |
396 | ||
397 | /* Allocate mem for sum resource */ | |
398 | sum = kzalloc(sizeof(*sum), GFP_KERNEL); | |
68110661 TI |
399 | if (!sum) |
400 | return -ENOMEM; | |
8cc72361 WYC |
401 | |
402 | /* Check whether there are sufficient sum resources to meet request. */ | |
68110661 | 403 | err = 0; |
8cc72361 WYC |
404 | spin_lock_irqsave(&mgr->mgr_lock, flags); |
405 | for (i = 0; i < desc->msr; i++) { | |
406 | err = mgr_get_resource(&mgr->mgr, 1, &idx); | |
407 | if (err) | |
408 | break; | |
409 | ||
410 | sum->idx[i] = idx; | |
411 | } | |
412 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
413 | if (err) { | |
b3e0afe6 | 414 | printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n"); |
8cc72361 WYC |
415 | goto error; |
416 | } | |
417 | ||
418 | err = sum_rsc_init(sum, desc, mgr); | |
419 | if (err) | |
420 | goto error; | |
421 | ||
422 | *rsum = sum; | |
423 | ||
424 | return 0; | |
425 | ||
426 | error: | |
427 | spin_lock_irqsave(&mgr->mgr_lock, flags); | |
428 | for (i--; i >= 0; i--) | |
429 | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | |
430 | ||
431 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
432 | kfree(sum); | |
433 | return err; | |
434 | } | |
435 | ||
436 | static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) | |
437 | { | |
438 | unsigned long flags; | |
514eef9c | 439 | int i; |
8cc72361 WYC |
440 | |
441 | spin_lock_irqsave(&mgr->mgr_lock, flags); | |
442 | for (i = 0; i < sum->rsc.msr; i++) | |
443 | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | |
444 | ||
445 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
446 | sum_rsc_uninit(sum); | |
447 | kfree(sum); | |
448 | ||
449 | return 0; | |
450 | } | |
451 | ||
452 | int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) | |
453 | { | |
514eef9c | 454 | int err; |
8cc72361 WYC |
455 | struct sum_mgr *sum_mgr; |
456 | ||
457 | *rsum_mgr = NULL; | |
458 | sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL); | |
35ebf6e7 | 459 | if (!sum_mgr) |
8cc72361 WYC |
460 | return -ENOMEM; |
461 | ||
462 | err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw); | |
463 | if (err) | |
464 | goto error; | |
465 | ||
466 | spin_lock_init(&sum_mgr->mgr_lock); | |
467 | ||
468 | sum_mgr->get_sum = get_sum_rsc; | |
469 | sum_mgr->put_sum = put_sum_rsc; | |
470 | ||
471 | *rsum_mgr = sum_mgr; | |
472 | ||
473 | return 0; | |
474 | ||
475 | error: | |
476 | kfree(sum_mgr); | |
477 | return err; | |
478 | } | |
479 | ||
480 | int sum_mgr_destroy(struct sum_mgr *sum_mgr) | |
481 | { | |
482 | rsc_mgr_uninit(&sum_mgr->mgr); | |
483 | kfree(sum_mgr); | |
484 | return 0; | |
485 | } | |
486 |