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