]>
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 | ||
43f2cdeb | 52 | static const struct rsc_ops amixer_basic_rsc_ops = { |
8cc72361 WYC |
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 | ||
43f2cdeb | 189 | static const struct amixer_rsc_ops amixer_ops = { |
8cc72361 WYC |
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) { | |
0cae90a9 SM |
261 | dev_err(mgr->card->dev, |
262 | "Can't meet AMIXER resource request!\n"); | |
8cc72361 WYC |
263 | goto error; |
264 | } | |
265 | ||
266 | err = amixer_rsc_init(amixer, desc, mgr); | |
267 | if (err) | |
268 | goto error; | |
269 | ||
270 | *ramixer = amixer; | |
271 | ||
272 | return 0; | |
273 | ||
274 | error: | |
275 | spin_lock_irqsave(&mgr->mgr_lock, flags); | |
276 | for (i--; i >= 0; i--) | |
277 | mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); | |
278 | ||
279 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
280 | kfree(amixer); | |
281 | return err; | |
282 | } | |
283 | ||
284 | static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer) | |
285 | { | |
286 | unsigned long flags; | |
514eef9c | 287 | int i; |
8cc72361 WYC |
288 | |
289 | spin_lock_irqsave(&mgr->mgr_lock, flags); | |
290 | for (i = 0; i < amixer->rsc.msr; i++) | |
291 | mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]); | |
292 | ||
293 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
294 | amixer_rsc_uninit(amixer); | |
295 | kfree(amixer); | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
66640898 | 300 | int amixer_mgr_create(struct hw *hw, struct amixer_mgr **ramixer_mgr) |
8cc72361 | 301 | { |
514eef9c | 302 | int err; |
8cc72361 WYC |
303 | struct amixer_mgr *amixer_mgr; |
304 | ||
305 | *ramixer_mgr = NULL; | |
306 | amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL); | |
35ebf6e7 | 307 | if (!amixer_mgr) |
8cc72361 WYC |
308 | return -ENOMEM; |
309 | ||
310 | err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw); | |
311 | if (err) | |
312 | goto error; | |
313 | ||
314 | spin_lock_init(&amixer_mgr->mgr_lock); | |
315 | ||
316 | amixer_mgr->get_amixer = get_amixer_rsc; | |
317 | amixer_mgr->put_amixer = put_amixer_rsc; | |
e5347f9a | 318 | amixer_mgr->card = hw->card; |
8cc72361 WYC |
319 | |
320 | *ramixer_mgr = amixer_mgr; | |
321 | ||
322 | return 0; | |
323 | ||
324 | error: | |
325 | kfree(amixer_mgr); | |
326 | return err; | |
327 | } | |
328 | ||
329 | int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) | |
330 | { | |
331 | rsc_mgr_uninit(&amixer_mgr->mgr); | |
332 | kfree(amixer_mgr); | |
333 | return 0; | |
334 | } | |
335 | ||
336 | /* SUM resource management */ | |
337 | ||
338 | static int sum_master(struct rsc *rsc) | |
339 | { | |
340 | rsc->conj = 0; | |
341 | return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0]; | |
342 | } | |
343 | ||
344 | static int sum_next_conj(struct rsc *rsc) | |
345 | { | |
346 | rsc->conj++; | |
347 | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | |
348 | } | |
349 | ||
350 | static int sum_index(const struct rsc *rsc) | |
351 | { | |
352 | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | |
353 | } | |
354 | ||
355 | static int sum_output_slot(const struct rsc *rsc) | |
356 | { | |
357 | return (sum_index(rsc) << 4) + 0xc; | |
358 | } | |
359 | ||
43f2cdeb | 360 | static const struct rsc_ops sum_basic_rsc_ops = { |
8cc72361 WYC |
361 | .master = sum_master, |
362 | .next_conj = sum_next_conj, | |
363 | .index = sum_index, | |
364 | .output_slot = sum_output_slot, | |
365 | }; | |
366 | ||
367 | static int sum_rsc_init(struct sum *sum, | |
368 | const struct sum_desc *desc, | |
369 | struct sum_mgr *mgr) | |
370 | { | |
514eef9c | 371 | int err; |
8cc72361 WYC |
372 | |
373 | err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw); | |
374 | if (err) | |
375 | return err; | |
376 | ||
377 | sum->rsc.ops = &sum_basic_rsc_ops; | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
382 | static int sum_rsc_uninit(struct sum *sum) | |
383 | { | |
384 | rsc_uninit(&sum->rsc); | |
385 | return 0; | |
386 | } | |
387 | ||
388 | static int get_sum_rsc(struct sum_mgr *mgr, | |
389 | const struct sum_desc *desc, | |
390 | struct sum **rsum) | |
391 | { | |
514eef9c TI |
392 | int err, i; |
393 | unsigned int idx; | |
394 | struct sum *sum; | |
8cc72361 WYC |
395 | unsigned long flags; |
396 | ||
397 | *rsum = NULL; | |
398 | ||
399 | /* Allocate mem for sum resource */ | |
400 | sum = kzalloc(sizeof(*sum), GFP_KERNEL); | |
68110661 TI |
401 | if (!sum) |
402 | return -ENOMEM; | |
8cc72361 WYC |
403 | |
404 | /* Check whether there are sufficient sum resources to meet request. */ | |
68110661 | 405 | err = 0; |
8cc72361 WYC |
406 | spin_lock_irqsave(&mgr->mgr_lock, flags); |
407 | for (i = 0; i < desc->msr; i++) { | |
408 | err = mgr_get_resource(&mgr->mgr, 1, &idx); | |
409 | if (err) | |
410 | break; | |
411 | ||
412 | sum->idx[i] = idx; | |
413 | } | |
414 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
415 | if (err) { | |
0cae90a9 SM |
416 | dev_err(mgr->card->dev, |
417 | "Can't meet SUM resource request!\n"); | |
8cc72361 WYC |
418 | goto error; |
419 | } | |
420 | ||
421 | err = sum_rsc_init(sum, desc, mgr); | |
422 | if (err) | |
423 | goto error; | |
424 | ||
425 | *rsum = sum; | |
426 | ||
427 | return 0; | |
428 | ||
429 | error: | |
430 | spin_lock_irqsave(&mgr->mgr_lock, flags); | |
431 | for (i--; i >= 0; i--) | |
432 | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | |
433 | ||
434 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
435 | kfree(sum); | |
436 | return err; | |
437 | } | |
438 | ||
439 | static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) | |
440 | { | |
441 | unsigned long flags; | |
514eef9c | 442 | int i; |
8cc72361 WYC |
443 | |
444 | spin_lock_irqsave(&mgr->mgr_lock, flags); | |
445 | for (i = 0; i < sum->rsc.msr; i++) | |
446 | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | |
447 | ||
448 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | |
449 | sum_rsc_uninit(sum); | |
450 | kfree(sum); | |
451 | ||
452 | return 0; | |
453 | } | |
454 | ||
66640898 | 455 | int sum_mgr_create(struct hw *hw, struct sum_mgr **rsum_mgr) |
8cc72361 | 456 | { |
514eef9c | 457 | int err; |
8cc72361 WYC |
458 | struct sum_mgr *sum_mgr; |
459 | ||
460 | *rsum_mgr = NULL; | |
461 | sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL); | |
35ebf6e7 | 462 | if (!sum_mgr) |
8cc72361 WYC |
463 | return -ENOMEM; |
464 | ||
465 | err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw); | |
466 | if (err) | |
467 | goto error; | |
468 | ||
469 | spin_lock_init(&sum_mgr->mgr_lock); | |
470 | ||
471 | sum_mgr->get_sum = get_sum_rsc; | |
472 | sum_mgr->put_sum = put_sum_rsc; | |
e5347f9a | 473 | sum_mgr->card = hw->card; |
8cc72361 WYC |
474 | |
475 | *rsum_mgr = sum_mgr; | |
476 | ||
477 | return 0; | |
478 | ||
479 | error: | |
480 | kfree(sum_mgr); | |
481 | return err; | |
482 | } | |
483 | ||
484 | int sum_mgr_destroy(struct sum_mgr *sum_mgr) | |
485 | { | |
486 | rsc_mgr_uninit(&sum_mgr->mgr); | |
487 | kfree(sum_mgr); | |
488 | return 0; | |
489 | } | |
490 |