Path: blob/master/sound/soc/sdca/sdca_function_device.c
38189 views
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)1// Copyright(c) 2024 Intel Corporation.23/*4* SDCA Function Device management5*/67#include <linux/acpi.h>8#include <linux/module.h>9#include <linux/auxiliary_bus.h>10#include <linux/soundwire/sdw.h>11#include <sound/sdca.h>12#include <sound/sdca_function.h>13#include "sdca_function_device.h"1415/*16* A SoundWire device can have multiple SDCA functions identified by17* their type and ADR. there can be multiple SoundWire devices per18* link, or multiple devices spread across multiple links. An IDA is19* required to identify each instance.20*/21static DEFINE_IDA(sdca_function_ida);2223static void sdca_dev_release(struct device *dev)24{25struct auxiliary_device *auxdev = to_auxiliary_dev(dev);26struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev);2728ida_free(&sdca_function_ida, auxdev->id);29kfree(sdev);30}3132/* alloc, init and add link devices */33static struct sdca_dev *sdca_dev_register(struct device *parent,34struct sdca_function_desc *function_desc)35{36struct sdca_dev *sdev;37struct auxiliary_device *auxdev;38int ret;39int rc;4041sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);42if (!sdev)43return ERR_PTR(-ENOMEM);4445auxdev = &sdev->auxdev;46auxdev->name = function_desc->name;47auxdev->dev.parent = parent;48auxdev->dev.fwnode = function_desc->node;49auxdev->dev.release = sdca_dev_release;5051sdev->function.desc = function_desc;5253rc = ida_alloc(&sdca_function_ida, GFP_KERNEL);54if (rc < 0) {55kfree(sdev);56return ERR_PTR(rc);57}58auxdev->id = rc;5960/* now follow the two-step init/add sequence */61ret = auxiliary_device_init(auxdev);62if (ret < 0) {63dev_err(parent, "failed to initialize SDCA function dev %s\n",64function_desc->name);65ida_free(&sdca_function_ida, auxdev->id);66kfree(sdev);67return ERR_PTR(ret);68}6970ret = auxiliary_device_add(auxdev);71if (ret < 0) {72dev_err(parent, "failed to add SDCA function dev %s\n",73sdev->auxdev.name);74/* sdev will be freed with the put_device() and .release sequence */75auxiliary_device_uninit(&sdev->auxdev);76return ERR_PTR(ret);77}7879return sdev;80}8182static void sdca_dev_unregister(struct sdca_dev *sdev)83{84auxiliary_device_delete(&sdev->auxdev);85auxiliary_device_uninit(&sdev->auxdev);86}8788int sdca_dev_register_functions(struct sdw_slave *slave)89{90struct sdca_device_data *sdca_data = &slave->sdca_data;91int i;9293for (i = 0; i < sdca_data->num_functions; i++) {94struct sdca_dev *func_dev;9596func_dev = sdca_dev_register(&slave->dev,97&sdca_data->function[i]);98if (IS_ERR(func_dev))99return PTR_ERR(func_dev);100101sdca_data->function[i].func_dev = func_dev;102}103104return 0;105}106EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");107108void sdca_dev_unregister_functions(struct sdw_slave *slave)109{110struct sdca_device_data *sdca_data = &slave->sdca_data;111int i;112113for (i = 0; i < sdca_data->num_functions; i++)114sdca_dev_unregister(sdca_data->function[i].func_dev);115}116EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");117118119