diff options
author | Wai Yew CHAY <wychay@ctl.creative.com> | 2009-05-14 02:05:58 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-05-14 02:24:10 -0400 |
commit | 8cc72361481f00253f1e468ade5795427386d593 (patch) | |
tree | ec6f3ea304f90fa9c99abb1bf2354fc5d357db27 /sound/pci/ctxfi/ctamixer.c | |
parent | 091bf7624d1c90cec9e578a18529f615213ff847 (diff) |
ALSA: SB X-Fi driver merge
The Sound Blaster X-Fi driver supports Creative solutions based on
20K1 and 20K2 chipsets.
Supported hardware :
Creative Sound Blaster X-Fi Titanium Fatal1ty® Champion Series
Creative Sound Blaster X-Fi Titanium Fatal1ty Professional Series
Creative Sound Blaster X-Fi Titanium Professional Audio
Creative Sound Blaster X-Fi Titanium
Creative Sound Blaster X-Fi Elite Pro
Creative Sound Blaster X-Fi Platinum
Creative Sound Blaster X-Fi Fatal1ty
Creative Sound Blaster X-Fi XtremeGamer
Creative Sound Blaster X-Fi XtremeMusic
Current release features:
* ALSA PCM Playback
* ALSA Record
* ALSA Mixer
Note:
* External I/O modules detection not included.
Signed-off-by: Wai Yew CHAY <wychay@ctl.creative.com>
Singed-off-by: Ryan RICHARDS <ryan_richards@creativelabs.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/ctxfi/ctamixer.c')
-rw-r--r-- | sound/pci/ctxfi/ctamixer.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c new file mode 100644 index 000000000000..119791aa70eb --- /dev/null +++ b/sound/pci/ctxfi/ctamixer.c | |||
@@ -0,0 +1,488 @@ | |||
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 | { | ||
61 | struct hw *hw = NULL; | ||
62 | |||
63 | hw = (struct hw *)amixer->rsc.hw; | ||
64 | hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE); | ||
65 | amixer->input = rsc; | ||
66 | if (NULL == rsc) | ||
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 | { | ||
78 | struct hw *hw = NULL; | ||
79 | |||
80 | hw = (struct hw *)amixer->rsc.hw; | ||
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 | { | ||
88 | struct hw *hw = NULL; | ||
89 | |||
90 | hw = (struct hw *)amixer->rsc.hw; | ||
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 | { | ||
98 | struct hw *hw = NULL; | ||
99 | |||
100 | hw = (struct hw *)amixer->rsc.hw; | ||
101 | amixer->sum = sum; | ||
102 | if (NULL == sum) { | ||
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 | { | ||
115 | struct hw *hw = NULL; | ||
116 | unsigned int index = 0; | ||
117 | int i = 0; | ||
118 | struct rsc *input = NULL; | ||
119 | struct sum *sum = NULL; | ||
120 | |||
121 | hw = (struct hw *)amixer->rsc.hw; | ||
122 | input = amixer->input; | ||
123 | sum = amixer->sum; | ||
124 | |||
125 | /* Program master and conjugate resources */ | ||
126 | amixer->rsc.ops->master(&amixer->rsc); | ||
127 | if (NULL != input) | ||
128 | input->ops->master(input); | ||
129 | |||
130 | if (NULL != sum) | ||
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); | ||
135 | if (NULL != input) { | ||
136 | hw->amixer_set_x(amixer->rsc.ctrl_blk, | ||
137 | input->ops->output_slot(input)); | ||
138 | input->ops->next_conj(input); | ||
139 | } | ||
140 | if (NULL != sum) { | ||
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); | ||
150 | if (NULL != input) | ||
151 | input->ops->master(input); | ||
152 | |||
153 | if (NULL != sum) | ||
154 | sum->rsc.ops->master(&sum->rsc); | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int amixer_commit_raw_write(struct amixer *amixer) | ||
160 | { | ||
161 | struct hw *hw = NULL; | ||
162 | unsigned int index = 0; | ||
163 | |||
164 | hw = (struct hw *)amixer->rsc.hw; | ||
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 | { | ||
173 | struct hw *hw = NULL; | ||
174 | |||
175 | hw = (struct hw *)amixer->rsc.hw; | ||
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 | { | ||
204 | int err = 0; | ||
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 | { | ||
236 | int err = 0, i = 0; | ||
237 | unsigned int idx = 0; | ||
238 | struct amixer *amixer = NULL; | ||
239 | unsigned long flags; | ||
240 | |||
241 | *ramixer = NULL; | ||
242 | |||
243 | /* Allocate mem for amixer resource */ | ||
244 | amixer = kzalloc(sizeof(*amixer), GFP_KERNEL); | ||
245 | if (NULL == amixer) { | ||
246 | err = -ENOMEM; | ||
247 | return err; | ||
248 | } | ||
249 | |||
250 | /* Check whether there are sufficient | ||
251 | * amixer resources to meet request. */ | ||
252 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
253 | for (i = 0; i < desc->msr; i++) { | ||
254 | err = mgr_get_resource(&mgr->mgr, 1, &idx); | ||
255 | if (err) | ||
256 | break; | ||
257 | |||
258 | amixer->idx[i] = idx; | ||
259 | } | ||
260 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
261 | if (err) { | ||
262 | printk(KERN_ERR "Can't meet AMIXER resource request!\n"); | ||
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; | ||
287 | int i = 0; | ||
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 | |||
300 | int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr) | ||
301 | { | ||
302 | int err = 0; | ||
303 | struct amixer_mgr *amixer_mgr; | ||
304 | |||
305 | *ramixer_mgr = NULL; | ||
306 | amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL); | ||
307 | if (NULL == amixer_mgr) | ||
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; | ||
318 | |||
319 | *ramixer_mgr = amixer_mgr; | ||
320 | |||
321 | return 0; | ||
322 | |||
323 | error: | ||
324 | kfree(amixer_mgr); | ||
325 | return err; | ||
326 | } | ||
327 | |||
328 | int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr) | ||
329 | { | ||
330 | rsc_mgr_uninit(&amixer_mgr->mgr); | ||
331 | kfree(amixer_mgr); | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | /* SUM resource management */ | ||
336 | |||
337 | static int sum_master(struct rsc *rsc) | ||
338 | { | ||
339 | rsc->conj = 0; | ||
340 | return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0]; | ||
341 | } | ||
342 | |||
343 | static int sum_next_conj(struct rsc *rsc) | ||
344 | { | ||
345 | rsc->conj++; | ||
346 | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | ||
347 | } | ||
348 | |||
349 | static int sum_index(const struct rsc *rsc) | ||
350 | { | ||
351 | return container_of(rsc, struct sum, rsc)->idx[rsc->conj]; | ||
352 | } | ||
353 | |||
354 | static int sum_output_slot(const struct rsc *rsc) | ||
355 | { | ||
356 | return (sum_index(rsc) << 4) + 0xc; | ||
357 | } | ||
358 | |||
359 | static struct rsc_ops sum_basic_rsc_ops = { | ||
360 | .master = sum_master, | ||
361 | .next_conj = sum_next_conj, | ||
362 | .index = sum_index, | ||
363 | .output_slot = sum_output_slot, | ||
364 | }; | ||
365 | |||
366 | static int sum_rsc_init(struct sum *sum, | ||
367 | const struct sum_desc *desc, | ||
368 | struct sum_mgr *mgr) | ||
369 | { | ||
370 | int err = 0; | ||
371 | |||
372 | err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw); | ||
373 | if (err) | ||
374 | return err; | ||
375 | |||
376 | sum->rsc.ops = &sum_basic_rsc_ops; | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int sum_rsc_uninit(struct sum *sum) | ||
382 | { | ||
383 | rsc_uninit(&sum->rsc); | ||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | static int get_sum_rsc(struct sum_mgr *mgr, | ||
388 | const struct sum_desc *desc, | ||
389 | struct sum **rsum) | ||
390 | { | ||
391 | int err = 0, i = 0; | ||
392 | unsigned int idx = 0; | ||
393 | struct sum *sum = NULL; | ||
394 | unsigned long flags; | ||
395 | |||
396 | *rsum = NULL; | ||
397 | |||
398 | /* Allocate mem for sum resource */ | ||
399 | sum = kzalloc(sizeof(*sum), GFP_KERNEL); | ||
400 | if (NULL == sum) { | ||
401 | err = -ENOMEM; | ||
402 | return err; | ||
403 | } | ||
404 | |||
405 | /* Check whether there are sufficient sum resources to meet request. */ | ||
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) { | ||
416 | printk(KERN_ERR "Can't meet SUM resource request!\n"); | ||
417 | goto error; | ||
418 | } | ||
419 | |||
420 | err = sum_rsc_init(sum, desc, mgr); | ||
421 | if (err) | ||
422 | goto error; | ||
423 | |||
424 | *rsum = sum; | ||
425 | |||
426 | return 0; | ||
427 | |||
428 | error: | ||
429 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
430 | for (i--; i >= 0; i--) | ||
431 | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | ||
432 | |||
433 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
434 | kfree(sum); | ||
435 | return err; | ||
436 | } | ||
437 | |||
438 | static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum) | ||
439 | { | ||
440 | unsigned long flags; | ||
441 | int i = 0; | ||
442 | |||
443 | spin_lock_irqsave(&mgr->mgr_lock, flags); | ||
444 | for (i = 0; i < sum->rsc.msr; i++) | ||
445 | mgr_put_resource(&mgr->mgr, 1, sum->idx[i]); | ||
446 | |||
447 | spin_unlock_irqrestore(&mgr->mgr_lock, flags); | ||
448 | sum_rsc_uninit(sum); | ||
449 | kfree(sum); | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr) | ||
455 | { | ||
456 | int err = 0; | ||
457 | struct sum_mgr *sum_mgr; | ||
458 | |||
459 | *rsum_mgr = NULL; | ||
460 | sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL); | ||
461 | if (NULL == sum_mgr) | ||
462 | return -ENOMEM; | ||
463 | |||
464 | err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw); | ||
465 | if (err) | ||
466 | goto error; | ||
467 | |||
468 | spin_lock_init(&sum_mgr->mgr_lock); | ||
469 | |||
470 | sum_mgr->get_sum = get_sum_rsc; | ||
471 | sum_mgr->put_sum = put_sum_rsc; | ||
472 | |||
473 | *rsum_mgr = sum_mgr; | ||
474 | |||
475 | return 0; | ||
476 | |||
477 | error: | ||
478 | kfree(sum_mgr); | ||
479 | return err; | ||
480 | } | ||
481 | |||
482 | int sum_mgr_destroy(struct sum_mgr *sum_mgr) | ||
483 | { | ||
484 | rsc_mgr_uninit(&sum_mgr->mgr); | ||
485 | kfree(sum_mgr); | ||
486 | return 0; | ||
487 | } | ||
488 | |||