// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)// Copyright(c) 2024 Intel Corporation./* * SDCA Function Device management */#include <linux/acpi.h>#include <linux/module.h>#include <linux/auxiliary_bus.h>#include <linux/soundwire/sdw.h>#include <sound/sdca.h>#include <sound/sdca_function.h>#include "sdca_function_device.h"/* * A SoundWire device can have multiple SDCA functions identified by * their type and ADR. there can be multiple SoundWire devices per * link, or multiple devices spread across multiple links. An IDA is * required to identify each instance. */static DEFINE_IDA(sdca_function_ida);static void sdca_dev_release(struct device *dev){ struct auxiliary_device *auxdev = to_auxiliary_dev(dev); struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev); ida_free(&sdca_function_ida, auxdev->id); kfree(sdev);}/* alloc, init and add link devices */static struct sdca_dev *sdca_dev_register(struct device *parent, struct sdca_function_desc *function_desc, struct acpi_table_swft *swft){ struct sdca_dev *sdev; struct auxiliary_device *auxdev; int ret; int rc; sdev = kzalloc_obj(*sdev); if (!sdev) return ERR_PTR(-ENOMEM); auxdev = &sdev->auxdev; auxdev->name = function_desc->name; auxdev->dev.parent = parent; auxdev->dev.fwnode = function_desc->node; auxdev->dev.release = sdca_dev_release; sdev->function.desc = function_desc; sdev->function.fdl_data.swft = swft; rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); if (rc < 0) { kfree(sdev); return ERR_PTR(rc); } auxdev->id = rc; /* now follow the two-step init/add sequence */ ret = auxiliary_device_init(auxdev); if (ret < 0) { dev_err(parent, "failed to initialize SDCA function dev %s\n", function_desc->name); ida_free(&sdca_function_ida, auxdev->id); kfree(sdev); return ERR_PTR(ret); } ret = auxiliary_device_add(auxdev); if (ret < 0) { dev_err(parent, "failed to add SDCA function dev %s\n", sdev->auxdev.name); /* sdev will be freed with the put_device() and .release sequence */ auxiliary_device_uninit(&sdev->auxdev); return ERR_PTR(ret); } return sdev;}static void sdca_dev_unregister(struct sdca_dev *sdev){ if (!sdev) return; auxiliary_device_delete(&sdev->auxdev); auxiliary_device_uninit(&sdev->auxdev);}int sdca_dev_register_functions(struct sdw_slave *slave){ struct sdca_device_data *sdca_data = &slave->sdca_data; int i; int ret; for (i = 0; i < sdca_data->num_functions; i++) { struct sdca_dev *func_dev; func_dev = sdca_dev_register(&slave->dev, &sdca_data->function[i], sdca_data->swft); if (IS_ERR(func_dev)) { ret = PTR_ERR(func_dev); /* * Unregister functions that were successfully * registered before this failure. This also * sets func_dev to NULL so the caller will not * try to unregister them again. */ sdca_dev_unregister_functions(slave); return ret; } sdca_data->function[i].func_dev = func_dev; } return 0;}EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");void sdca_dev_unregister_functions(struct sdw_slave *slave){ struct sdca_device_data *sdca_data = &slave->sdca_data; int i; for (i = 0; i < sdca_data->num_functions; i++) { if (!sdca_data->function[i].func_dev) continue; sdca_dev_unregister(sdca_data->function[i].func_dev); sdca_data->function[i].func_dev = NULL; }}EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright(c) 2024 Intel Corporation.
/*
* SDCA Function Device management
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/auxiliary_bus.h>
#include <linux/soundwire/sdw.h>
#include <sound/sdca.h>
#include <sound/sdca_function.h>
#include "sdca_function_device.h"
/*
* A SoundWire device can have multiple SDCA functions identified by
* their type and ADR. there can be multiple SoundWire devices per
* link, or multiple devices spread across multiple links. An IDA is
* required to identify each instance.
*/
static DEFINE_IDA(sdca_function_ida);
static void sdca_dev_release(struct device *dev)
{
struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev);
ida_free(&sdca_function_ida, auxdev->id);
kfree(sdev);
}
/* alloc, init and add link devices */
static struct sdca_dev *sdca_dev_register(struct device *parent,
struct sdca_function_desc *function_desc,
struct acpi_table_swft *swft)
{
struct sdca_dev *sdev;
struct auxiliary_device *auxdev;
int ret;
int rc;
sdev = kzalloc_obj(*sdev);
if (!sdev)
return ERR_PTR(-ENOMEM);
auxdev = &sdev->auxdev;
auxdev->name = function_desc->name;
auxdev->dev.parent = parent;
auxdev->dev.fwnode = function_desc->node;
auxdev->dev.release = sdca_dev_release;
sdev->function.desc = function_desc;
sdev->function.fdl_data.swft = swft;
rc = ida_alloc(&sdca_function_ida, GFP_KERNEL);
if (rc < 0) {
kfree(sdev);
return ERR_PTR(rc);
}
auxdev->id = rc;
/* now follow the two-step init/add sequence */
ret = auxiliary_device_init(auxdev);
if (ret < 0) {
dev_err(parent, "failed to initialize SDCA function dev %s\n",
function_desc->name);
ida_free(&sdca_function_ida, auxdev->id);
kfree(sdev);
return ERR_PTR(ret);
}
ret = auxiliary_device_add(auxdev);
if (ret < 0) {
dev_err(parent, "failed to add SDCA function dev %s\n",
sdev->auxdev.name);
/* sdev will be freed with the put_device() and .release sequence */
auxiliary_device_uninit(&sdev->auxdev);
return ERR_PTR(ret);
}
return sdev;
}
static void sdca_dev_unregister(struct sdca_dev *sdev)
{
if (!sdev)
return;
auxiliary_device_delete(&sdev->auxdev);
auxiliary_device_uninit(&sdev->auxdev);
}
int sdca_dev_register_functions(struct sdw_slave *slave)
{
struct sdca_device_data *sdca_data = &slave->sdca_data;
int i;
int ret;
for (i = 0; i < sdca_data->num_functions; i++) {
struct sdca_dev *func_dev;
func_dev = sdca_dev_register(&slave->dev,
&sdca_data->function[i],
sdca_data->swft);
if (IS_ERR(func_dev)) {
ret = PTR_ERR(func_dev);
/*
* Unregister functions that were successfully
* registered before this failure. This also
* sets func_dev to NULL so the caller will not
* try to unregister them again.
*/
sdca_dev_unregister_functions(slave);
return ret;
}
sdca_data->function[i].func_dev = func_dev;
}
return 0;
}
EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
void sdca_dev_unregister_functions(struct sdw_slave *slave)
{
struct sdca_device_data *sdca_data = &slave->sdca_data;
int i;
for (i = 0; i < sdca_data->num_functions; i++) {
if (!sdca_data->function[i].func_dev)
continue;
sdca_dev_unregister(sdca_data->function[i].func_dev);
sdca_data->function[i].func_dev = NULL;
}
}
EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");