19e567af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2960366cfSKarsten Keil /*
3960366cfSKarsten Keil * dsp_pipeline.c: pipelined audio processing
4960366cfSKarsten Keil *
5960366cfSKarsten Keil * Copyright (C) 2007, Nadi Sarrar
6960366cfSKarsten Keil *
7960366cfSKarsten Keil * Nadi Sarrar <nadi@beronet.com>
8960366cfSKarsten Keil */
9960366cfSKarsten Keil
10960366cfSKarsten Keil #include <linux/kernel.h>
115a0e3ad6STejun Heo #include <linux/slab.h>
12960366cfSKarsten Keil #include <linux/list.h>
13960366cfSKarsten Keil #include <linux/string.h>
14960366cfSKarsten Keil #include <linux/mISDNif.h>
15960366cfSKarsten Keil #include <linux/mISDNdsp.h>
165d76fc21SPaul Gortmaker #include <linux/export.h>
17960366cfSKarsten Keil #include "dsp.h"
18960366cfSKarsten Keil #include "dsp_hwec.h"
19960366cfSKarsten Keil
20960366cfSKarsten Keil struct dsp_pipeline_entry {
21960366cfSKarsten Keil struct mISDN_dsp_element *elem;
22960366cfSKarsten Keil void *p;
23960366cfSKarsten Keil struct list_head list;
24960366cfSKarsten Keil };
25960366cfSKarsten Keil struct dsp_element_entry {
26960366cfSKarsten Keil struct mISDN_dsp_element *elem;
27960366cfSKarsten Keil struct device dev;
28960366cfSKarsten Keil struct list_head list;
29960366cfSKarsten Keil };
30960366cfSKarsten Keil
31960366cfSKarsten Keil static LIST_HEAD(dsp_elements);
32960366cfSKarsten Keil
33960366cfSKarsten Keil /* sysfs */
34960366cfSKarsten Keil static struct class *elements_class;
35960366cfSKarsten Keil
36960366cfSKarsten Keil static ssize_t
attr_show_args(struct device * dev,struct device_attribute * attr,char * buf)37960366cfSKarsten Keil attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
38960366cfSKarsten Keil {
39960366cfSKarsten Keil struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
401ce1513fSKarsten Keil int i;
411ce1513fSKarsten Keil char *p = buf;
42960366cfSKarsten Keil
43960366cfSKarsten Keil *buf = 0;
441ce1513fSKarsten Keil for (i = 0; i < elem->num_args; i++)
451ce1513fSKarsten Keil p += sprintf(p, "Name: %s\n%s%s%sDescription: %s\n\n",
46960366cfSKarsten Keil elem->args[i].name,
47960366cfSKarsten Keil elem->args[i].def ? "Default: " : "",
48960366cfSKarsten Keil elem->args[i].def ? elem->args[i].def : "",
49960366cfSKarsten Keil elem->args[i].def ? "\n" : "",
50960366cfSKarsten Keil elem->args[i].desc);
51960366cfSKarsten Keil
521ce1513fSKarsten Keil return p - buf;
53960366cfSKarsten Keil }
54960366cfSKarsten Keil
55960366cfSKarsten Keil static struct device_attribute element_attributes[] = {
56960366cfSKarsten Keil __ATTR(args, 0444, attr_show_args, NULL),
57960366cfSKarsten Keil };
58960366cfSKarsten Keil
59808a14a1SAndreas Eversberg static void
mISDN_dsp_dev_release(struct device * dev)60808a14a1SAndreas Eversberg mISDN_dsp_dev_release(struct device *dev)
61808a14a1SAndreas Eversberg {
62808a14a1SAndreas Eversberg struct dsp_element_entry *entry =
63808a14a1SAndreas Eversberg container_of(dev, struct dsp_element_entry, dev);
64808a14a1SAndreas Eversberg list_del(&entry->list);
65808a14a1SAndreas Eversberg kfree(entry);
66808a14a1SAndreas Eversberg }
67808a14a1SAndreas Eversberg
mISDN_dsp_element_register(struct mISDN_dsp_element * elem)68960366cfSKarsten Keil int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
69960366cfSKarsten Keil {
70960366cfSKarsten Keil struct dsp_element_entry *entry;
71960366cfSKarsten Keil int ret, i;
72960366cfSKarsten Keil
73960366cfSKarsten Keil if (!elem)
74960366cfSKarsten Keil return -EINVAL;
75960366cfSKarsten Keil
76400fd978SAndreas Eversberg entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC);
77960366cfSKarsten Keil if (!entry)
78960366cfSKarsten Keil return -ENOMEM;
79960366cfSKarsten Keil
8098a2ac1cSYang Yingliang INIT_LIST_HEAD(&entry->list);
81960366cfSKarsten Keil entry->elem = elem;
82960366cfSKarsten Keil
83960366cfSKarsten Keil entry->dev.class = elements_class;
84808a14a1SAndreas Eversberg entry->dev.release = mISDN_dsp_dev_release;
85960366cfSKarsten Keil dev_set_drvdata(&entry->dev, elem);
8602aa2a37SKees Cook dev_set_name(&entry->dev, "%s", elem->name);
87960366cfSKarsten Keil ret = device_register(&entry->dev);
88960366cfSKarsten Keil if (ret) {
89960366cfSKarsten Keil printk(KERN_ERR "%s: failed to register %s\n",
90960366cfSKarsten Keil __func__, elem->name);
91960366cfSKarsten Keil goto err1;
92960366cfSKarsten Keil }
93808a14a1SAndreas Eversberg list_add_tail(&entry->list, &dsp_elements);
94960366cfSKarsten Keil
9521c150a6SIlpo Järvinen for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) {
96960366cfSKarsten Keil ret = device_create_file(&entry->dev,
97960366cfSKarsten Keil &element_attributes[i]);
98960366cfSKarsten Keil if (ret) {
99960366cfSKarsten Keil printk(KERN_ERR "%s: failed to create device file\n",
100960366cfSKarsten Keil __func__);
101960366cfSKarsten Keil goto err2;
102960366cfSKarsten Keil }
10321c150a6SIlpo Järvinen }
104960366cfSKarsten Keil
105960366cfSKarsten Keil return 0;
106960366cfSKarsten Keil
107960366cfSKarsten Keil err2:
108960366cfSKarsten Keil device_unregister(&entry->dev);
109808a14a1SAndreas Eversberg return ret;
110960366cfSKarsten Keil err1:
11198a2ac1cSYang Yingliang put_device(&entry->dev);
112960366cfSKarsten Keil return ret;
113960366cfSKarsten Keil }
114960366cfSKarsten Keil EXPORT_SYMBOL(mISDN_dsp_element_register);
115960366cfSKarsten Keil
mISDN_dsp_element_unregister(struct mISDN_dsp_element * elem)116960366cfSKarsten Keil void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
117960366cfSKarsten Keil {
118960366cfSKarsten Keil struct dsp_element_entry *entry, *n;
119960366cfSKarsten Keil
120960366cfSKarsten Keil if (!elem)
121960366cfSKarsten Keil return;
122960366cfSKarsten Keil
123960366cfSKarsten Keil list_for_each_entry_safe(entry, n, &dsp_elements, list)
124960366cfSKarsten Keil if (entry->elem == elem) {
125960366cfSKarsten Keil device_unregister(&entry->dev);
126960366cfSKarsten Keil return;
127960366cfSKarsten Keil }
128960366cfSKarsten Keil printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
129960366cfSKarsten Keil }
130960366cfSKarsten Keil EXPORT_SYMBOL(mISDN_dsp_element_unregister);
131960366cfSKarsten Keil
dsp_pipeline_module_init(void)132960366cfSKarsten Keil int dsp_pipeline_module_init(void)
133960366cfSKarsten Keil {
134*1aaba11dSGreg Kroah-Hartman elements_class = class_create("dsp_pipeline");
135960366cfSKarsten Keil if (IS_ERR(elements_class))
136960366cfSKarsten Keil return PTR_ERR(elements_class);
137960366cfSKarsten Keil
138960366cfSKarsten Keil dsp_hwec_init();
139960366cfSKarsten Keil
140960366cfSKarsten Keil return 0;
141960366cfSKarsten Keil }
142960366cfSKarsten Keil
dsp_pipeline_module_exit(void)143960366cfSKarsten Keil void dsp_pipeline_module_exit(void)
144960366cfSKarsten Keil {
145960366cfSKarsten Keil struct dsp_element_entry *entry, *n;
146960366cfSKarsten Keil
147960366cfSKarsten Keil dsp_hwec_exit();
148960366cfSKarsten Keil
149960366cfSKarsten Keil class_destroy(elements_class);
150960366cfSKarsten Keil
151960366cfSKarsten Keil list_for_each_entry_safe(entry, n, &dsp_elements, list) {
152960366cfSKarsten Keil list_del(&entry->list);
153960366cfSKarsten Keil printk(KERN_WARNING "%s: element was still registered: %s\n",
154960366cfSKarsten Keil __func__, entry->elem->name);
155960366cfSKarsten Keil kfree(entry);
156960366cfSKarsten Keil }
157960366cfSKarsten Keil }
158960366cfSKarsten Keil
dsp_pipeline_init(struct dsp_pipeline * pipeline)159960366cfSKarsten Keil int dsp_pipeline_init(struct dsp_pipeline *pipeline)
160960366cfSKarsten Keil {
161960366cfSKarsten Keil if (!pipeline)
162960366cfSKarsten Keil return -EINVAL;
163960366cfSKarsten Keil
164960366cfSKarsten Keil INIT_LIST_HEAD(&pipeline->list);
165960366cfSKarsten Keil
166960366cfSKarsten Keil return 0;
167960366cfSKarsten Keil }
168960366cfSKarsten Keil
_dsp_pipeline_destroy(struct dsp_pipeline * pipeline)169960366cfSKarsten Keil static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
170960366cfSKarsten Keil {
171960366cfSKarsten Keil struct dsp_pipeline_entry *entry, *n;
172960366cfSKarsten Keil
173960366cfSKarsten Keil list_for_each_entry_safe(entry, n, &pipeline->list, list) {
174960366cfSKarsten Keil list_del(&entry->list);
175960366cfSKarsten Keil if (entry->elem == dsp_hwec)
176960366cfSKarsten Keil dsp_hwec_disable(container_of(pipeline, struct dsp,
177960366cfSKarsten Keil pipeline));
178960366cfSKarsten Keil else
179960366cfSKarsten Keil entry->elem->free(entry->p);
180960366cfSKarsten Keil kfree(entry);
181960366cfSKarsten Keil }
182960366cfSKarsten Keil }
183960366cfSKarsten Keil
dsp_pipeline_destroy(struct dsp_pipeline * pipeline)184960366cfSKarsten Keil void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
185960366cfSKarsten Keil {
186960366cfSKarsten Keil
187960366cfSKarsten Keil if (!pipeline)
188960366cfSKarsten Keil return;
189960366cfSKarsten Keil
190960366cfSKarsten Keil _dsp_pipeline_destroy(pipeline);
191960366cfSKarsten Keil }
192960366cfSKarsten Keil
dsp_pipeline_build(struct dsp_pipeline * pipeline,const char * cfg)193960366cfSKarsten Keil int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
194960366cfSKarsten Keil {
1952682ea32SZhen Lei int found = 0;
196c6a502c2SAlexey Khoroshilov char *dup, *next, *tok, *name, *args;
197960366cfSKarsten Keil struct dsp_element_entry *entry, *n;
198960366cfSKarsten Keil struct dsp_pipeline_entry *pipeline_entry;
199960366cfSKarsten Keil struct mISDN_dsp_element *elem;
200960366cfSKarsten Keil
201960366cfSKarsten Keil if (!pipeline)
202960366cfSKarsten Keil return -EINVAL;
203960366cfSKarsten Keil
204960366cfSKarsten Keil if (!list_empty(&pipeline->list))
205960366cfSKarsten Keil _dsp_pipeline_destroy(pipeline);
206960366cfSKarsten Keil
207c6a502c2SAlexey Khoroshilov dup = next = kstrdup(cfg, GFP_ATOMIC);
208960366cfSKarsten Keil if (!dup)
209960366cfSKarsten Keil return 0;
210c6a502c2SAlexey Khoroshilov while ((tok = strsep(&next, "|"))) {
211960366cfSKarsten Keil if (!strlen(tok))
212960366cfSKarsten Keil continue;
213960366cfSKarsten Keil name = strsep(&tok, "(");
214960366cfSKarsten Keil args = strsep(&tok, ")");
215960366cfSKarsten Keil if (args && !*args)
216bcf91745SHannes Eder args = NULL;
217960366cfSKarsten Keil
218960366cfSKarsten Keil list_for_each_entry_safe(entry, n, &dsp_elements, list)
219960366cfSKarsten Keil if (!strcmp(entry->elem->name, name)) {
220960366cfSKarsten Keil elem = entry->elem;
221960366cfSKarsten Keil
222960366cfSKarsten Keil pipeline_entry = kmalloc(sizeof(struct
223400fd978SAndreas Eversberg dsp_pipeline_entry), GFP_ATOMIC);
224960366cfSKarsten Keil if (!pipeline_entry) {
225808a14a1SAndreas Eversberg printk(KERN_ERR "%s: failed to add "
226960366cfSKarsten Keil "entry to pipeline: %s (out of "
227960366cfSKarsten Keil "memory)\n", __func__, elem->name);
228960366cfSKarsten Keil goto _out;
229960366cfSKarsten Keil }
230960366cfSKarsten Keil pipeline_entry->elem = elem;
231960366cfSKarsten Keil
232960366cfSKarsten Keil if (elem == dsp_hwec) {
233960366cfSKarsten Keil /* This is a hack to make the hwec
234960366cfSKarsten Keil available as a pipeline module */
235960366cfSKarsten Keil dsp_hwec_enable(container_of(pipeline,
236960366cfSKarsten Keil struct dsp, pipeline), args);
237960366cfSKarsten Keil list_add_tail(&pipeline_entry->list,
238960366cfSKarsten Keil &pipeline->list);
239960366cfSKarsten Keil } else {
240960366cfSKarsten Keil pipeline_entry->p = elem->new(args);
241960366cfSKarsten Keil if (pipeline_entry->p) {
242960366cfSKarsten Keil list_add_tail(&pipeline_entry->
243960366cfSKarsten Keil list, &pipeline->list);
244960366cfSKarsten Keil } else {
245808a14a1SAndreas Eversberg printk(KERN_ERR "%s: failed "
246960366cfSKarsten Keil "to add entry to pipeline: "
247960366cfSKarsten Keil "%s (new() returned NULL)\n",
248960366cfSKarsten Keil __func__, elem->name);
249960366cfSKarsten Keil kfree(pipeline_entry);
250960366cfSKarsten Keil }
251960366cfSKarsten Keil }
252960366cfSKarsten Keil found = 1;
253960366cfSKarsten Keil break;
254960366cfSKarsten Keil }
255960366cfSKarsten Keil
256960366cfSKarsten Keil if (found)
257960366cfSKarsten Keil found = 0;
2582682ea32SZhen Lei else
259808a14a1SAndreas Eversberg printk(KERN_ERR "%s: element not found, skipping: "
260960366cfSKarsten Keil "%s\n", __func__, name);
261960366cfSKarsten Keil }
262960366cfSKarsten Keil
263960366cfSKarsten Keil _out:
264960366cfSKarsten Keil if (!list_empty(&pipeline->list))
265960366cfSKarsten Keil pipeline->inuse = 1;
266960366cfSKarsten Keil else
267960366cfSKarsten Keil pipeline->inuse = 0;
268960366cfSKarsten Keil
269960366cfSKarsten Keil kfree(dup);
270960366cfSKarsten Keil return 0;
271960366cfSKarsten Keil }
272960366cfSKarsten Keil
dsp_pipeline_process_tx(struct dsp_pipeline * pipeline,u8 * data,int len)273960366cfSKarsten Keil void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
274960366cfSKarsten Keil {
275960366cfSKarsten Keil struct dsp_pipeline_entry *entry;
276960366cfSKarsten Keil
277960366cfSKarsten Keil if (!pipeline)
278960366cfSKarsten Keil return;
279960366cfSKarsten Keil
280960366cfSKarsten Keil list_for_each_entry(entry, &pipeline->list, list)
281960366cfSKarsten Keil if (entry->elem->process_tx)
282960366cfSKarsten Keil entry->elem->process_tx(entry->p, data, len);
283960366cfSKarsten Keil }
284960366cfSKarsten Keil
dsp_pipeline_process_rx(struct dsp_pipeline * pipeline,u8 * data,int len,unsigned int txlen)2857cfa153dSAndreas Eversberg void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len,
2867cfa153dSAndreas Eversberg unsigned int txlen)
287960366cfSKarsten Keil {
288960366cfSKarsten Keil struct dsp_pipeline_entry *entry;
289960366cfSKarsten Keil
290960366cfSKarsten Keil if (!pipeline)
291960366cfSKarsten Keil return;
292960366cfSKarsten Keil
293960366cfSKarsten Keil list_for_each_entry_reverse(entry, &pipeline->list, list)
294960366cfSKarsten Keil if (entry->elem->process_rx)
2957cfa153dSAndreas Eversberg entry->elem->process_rx(entry->p, data, len, txlen);
296960366cfSKarsten Keil }
297