18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * dsp_pipeline.c: pipelined audio processing 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007, Nadi Sarrar 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Nadi Sarrar <nadi@beronet.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/list.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/mISDNif.h> 158c2ecf20Sopenharmony_ci#include <linux/mISDNdsp.h> 168c2ecf20Sopenharmony_ci#include <linux/export.h> 178c2ecf20Sopenharmony_ci#include "dsp.h" 188c2ecf20Sopenharmony_ci#include "dsp_hwec.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct dsp_pipeline_entry { 218c2ecf20Sopenharmony_ci struct mISDN_dsp_element *elem; 228c2ecf20Sopenharmony_ci void *p; 238c2ecf20Sopenharmony_ci struct list_head list; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_cistruct dsp_element_entry { 268c2ecf20Sopenharmony_ci struct mISDN_dsp_element *elem; 278c2ecf20Sopenharmony_ci struct device dev; 288c2ecf20Sopenharmony_ci struct list_head list; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic LIST_HEAD(dsp_elements); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* sysfs */ 348c2ecf20Sopenharmony_cistatic struct class *elements_class; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic ssize_t 378c2ecf20Sopenharmony_ciattr_show_args(struct device *dev, struct device_attribute *attr, char *buf) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct mISDN_dsp_element *elem = dev_get_drvdata(dev); 408c2ecf20Sopenharmony_ci int i; 418c2ecf20Sopenharmony_ci char *p = buf; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci *buf = 0; 448c2ecf20Sopenharmony_ci for (i = 0; i < elem->num_args; i++) 458c2ecf20Sopenharmony_ci p += sprintf(p, "Name: %s\n%s%s%sDescription: %s\n\n", 468c2ecf20Sopenharmony_ci elem->args[i].name, 478c2ecf20Sopenharmony_ci elem->args[i].def ? "Default: " : "", 488c2ecf20Sopenharmony_ci elem->args[i].def ? elem->args[i].def : "", 498c2ecf20Sopenharmony_ci elem->args[i].def ? "\n" : "", 508c2ecf20Sopenharmony_ci elem->args[i].desc); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return p - buf; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic struct device_attribute element_attributes[] = { 568c2ecf20Sopenharmony_ci __ATTR(args, 0444, attr_show_args, NULL), 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic void 608c2ecf20Sopenharmony_cimISDN_dsp_dev_release(struct device *dev) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct dsp_element_entry *entry = 638c2ecf20Sopenharmony_ci container_of(dev, struct dsp_element_entry, dev); 648c2ecf20Sopenharmony_ci list_del(&entry->list); 658c2ecf20Sopenharmony_ci kfree(entry); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciint mISDN_dsp_element_register(struct mISDN_dsp_element *elem) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct dsp_element_entry *entry; 718c2ecf20Sopenharmony_ci int ret, i; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!elem) 748c2ecf20Sopenharmony_ci return -EINVAL; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC); 778c2ecf20Sopenharmony_ci if (!entry) 788c2ecf20Sopenharmony_ci return -ENOMEM; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&entry->list); 818c2ecf20Sopenharmony_ci entry->elem = elem; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci entry->dev.class = elements_class; 848c2ecf20Sopenharmony_ci entry->dev.release = mISDN_dsp_dev_release; 858c2ecf20Sopenharmony_ci dev_set_drvdata(&entry->dev, elem); 868c2ecf20Sopenharmony_ci dev_set_name(&entry->dev, "%s", elem->name); 878c2ecf20Sopenharmony_ci ret = device_register(&entry->dev); 888c2ecf20Sopenharmony_ci if (ret) { 898c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: failed to register %s\n", 908c2ecf20Sopenharmony_ci __func__, elem->name); 918c2ecf20Sopenharmony_ci goto err1; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci list_add_tail(&entry->list, &dsp_elements); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(element_attributes); ++i) { 968c2ecf20Sopenharmony_ci ret = device_create_file(&entry->dev, 978c2ecf20Sopenharmony_ci &element_attributes[i]); 988c2ecf20Sopenharmony_ci if (ret) { 998c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: failed to create device file\n", 1008c2ecf20Sopenharmony_ci __func__); 1018c2ecf20Sopenharmony_ci goto err2; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cierr2: 1088c2ecf20Sopenharmony_ci device_unregister(&entry->dev); 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_cierr1: 1118c2ecf20Sopenharmony_ci put_device(&entry->dev); 1128c2ecf20Sopenharmony_ci return ret; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_dsp_element_register); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_civoid mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct dsp_element_entry *entry, *n; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!elem) 1218c2ecf20Sopenharmony_ci return; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, n, &dsp_elements, list) 1248c2ecf20Sopenharmony_ci if (entry->elem == elem) { 1258c2ecf20Sopenharmony_ci device_unregister(&entry->dev); 1268c2ecf20Sopenharmony_ci return; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mISDN_dsp_element_unregister); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ciint dsp_pipeline_module_init(void) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci elements_class = class_create(THIS_MODULE, "dsp_pipeline"); 1358c2ecf20Sopenharmony_ci if (IS_ERR(elements_class)) 1368c2ecf20Sopenharmony_ci return PTR_ERR(elements_class); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci dsp_hwec_init(); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_civoid dsp_pipeline_module_exit(void) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct dsp_element_entry *entry, *n; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci dsp_hwec_exit(); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci class_destroy(elements_class); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, n, &dsp_elements, list) { 1528c2ecf20Sopenharmony_ci list_del(&entry->list); 1538c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: element was still registered: %s\n", 1548c2ecf20Sopenharmony_ci __func__, entry->elem->name); 1558c2ecf20Sopenharmony_ci kfree(entry); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ciint dsp_pipeline_init(struct dsp_pipeline *pipeline) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci if (!pipeline) 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&pipeline->list); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct dsp_pipeline_entry *entry, *n; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, n, &pipeline->list, list) { 1748c2ecf20Sopenharmony_ci list_del(&entry->list); 1758c2ecf20Sopenharmony_ci if (entry->elem == dsp_hwec) 1768c2ecf20Sopenharmony_ci dsp_hwec_disable(container_of(pipeline, struct dsp, 1778c2ecf20Sopenharmony_ci pipeline)); 1788c2ecf20Sopenharmony_ci else 1798c2ecf20Sopenharmony_ci entry->elem->free(entry->p); 1808c2ecf20Sopenharmony_ci kfree(entry); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_civoid dsp_pipeline_destroy(struct dsp_pipeline *pipeline) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (!pipeline) 1888c2ecf20Sopenharmony_ci return; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci _dsp_pipeline_destroy(pipeline); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciint dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int found = 0; 1968c2ecf20Sopenharmony_ci char *dup, *next, *tok, *name, *args; 1978c2ecf20Sopenharmony_ci struct dsp_element_entry *entry, *n; 1988c2ecf20Sopenharmony_ci struct dsp_pipeline_entry *pipeline_entry; 1998c2ecf20Sopenharmony_ci struct mISDN_dsp_element *elem; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!pipeline) 2028c2ecf20Sopenharmony_ci return -EINVAL; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!list_empty(&pipeline->list)) 2058c2ecf20Sopenharmony_ci _dsp_pipeline_destroy(pipeline); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci dup = next = kstrdup(cfg, GFP_ATOMIC); 2088c2ecf20Sopenharmony_ci if (!dup) 2098c2ecf20Sopenharmony_ci return 0; 2108c2ecf20Sopenharmony_ci while ((tok = strsep(&next, "|"))) { 2118c2ecf20Sopenharmony_ci if (!strlen(tok)) 2128c2ecf20Sopenharmony_ci continue; 2138c2ecf20Sopenharmony_ci name = strsep(&tok, "("); 2148c2ecf20Sopenharmony_ci args = strsep(&tok, ")"); 2158c2ecf20Sopenharmony_ci if (args && !*args) 2168c2ecf20Sopenharmony_ci args = NULL; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, n, &dsp_elements, list) 2198c2ecf20Sopenharmony_ci if (!strcmp(entry->elem->name, name)) { 2208c2ecf20Sopenharmony_ci elem = entry->elem; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pipeline_entry = kmalloc(sizeof(struct 2238c2ecf20Sopenharmony_ci dsp_pipeline_entry), GFP_ATOMIC); 2248c2ecf20Sopenharmony_ci if (!pipeline_entry) { 2258c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: failed to add " 2268c2ecf20Sopenharmony_ci "entry to pipeline: %s (out of " 2278c2ecf20Sopenharmony_ci "memory)\n", __func__, elem->name); 2288c2ecf20Sopenharmony_ci goto _out; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci pipeline_entry->elem = elem; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (elem == dsp_hwec) { 2338c2ecf20Sopenharmony_ci /* This is a hack to make the hwec 2348c2ecf20Sopenharmony_ci available as a pipeline module */ 2358c2ecf20Sopenharmony_ci dsp_hwec_enable(container_of(pipeline, 2368c2ecf20Sopenharmony_ci struct dsp, pipeline), args); 2378c2ecf20Sopenharmony_ci list_add_tail(&pipeline_entry->list, 2388c2ecf20Sopenharmony_ci &pipeline->list); 2398c2ecf20Sopenharmony_ci } else { 2408c2ecf20Sopenharmony_ci pipeline_entry->p = elem->new(args); 2418c2ecf20Sopenharmony_ci if (pipeline_entry->p) { 2428c2ecf20Sopenharmony_ci list_add_tail(&pipeline_entry-> 2438c2ecf20Sopenharmony_ci list, &pipeline->list); 2448c2ecf20Sopenharmony_ci } else { 2458c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: failed " 2468c2ecf20Sopenharmony_ci "to add entry to pipeline: " 2478c2ecf20Sopenharmony_ci "%s (new() returned NULL)\n", 2488c2ecf20Sopenharmony_ci __func__, elem->name); 2498c2ecf20Sopenharmony_ci kfree(pipeline_entry); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci found = 1; 2538c2ecf20Sopenharmony_ci break; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (found) 2578c2ecf20Sopenharmony_ci found = 0; 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: element not found, skipping: " 2608c2ecf20Sopenharmony_ci "%s\n", __func__, name); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci_out: 2648c2ecf20Sopenharmony_ci if (!list_empty(&pipeline->list)) 2658c2ecf20Sopenharmony_ci pipeline->inuse = 1; 2668c2ecf20Sopenharmony_ci else 2678c2ecf20Sopenharmony_ci pipeline->inuse = 0; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci kfree(dup); 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_civoid dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct dsp_pipeline_entry *entry; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (!pipeline) 2788c2ecf20Sopenharmony_ci return; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci list_for_each_entry(entry, &pipeline->list, list) 2818c2ecf20Sopenharmony_ci if (entry->elem->process_tx) 2828c2ecf20Sopenharmony_ci entry->elem->process_tx(entry->p, data, len); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_civoid dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len, 2868c2ecf20Sopenharmony_ci unsigned int txlen) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct dsp_pipeline_entry *entry; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (!pipeline) 2918c2ecf20Sopenharmony_ci return; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci list_for_each_entry_reverse(entry, &pipeline->list, list) 2948c2ecf20Sopenharmony_ci if (entry->elem->process_rx) 2958c2ecf20Sopenharmony_ci entry->elem->process_rx(entry->p, data, len, txlen); 2968c2ecf20Sopenharmony_ci} 297