File Index
Files and directories
Sign in
FILE

sdca_function_device.c

File preview with syntax highlighting.

linux-6.19.14/sound/soc/sdca/sdca_function_device.c C
  1// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
  2// Copyright(c) 2024 Intel Corporation.
  3
  4/*
  5 * SDCA Function Device management
  6 */
  7
  8#include <linux/acpi.h>
  9#include <linux/module.h>
 10#include <linux/auxiliary_bus.h>
 11#include <linux/soundwire/sdw.h>
 12#include <sound/sdca.h>
 13#include <sound/sdca_function.h>
 14#include "sdca_function_device.h"
 15
 16/*
 17 * A SoundWire device can have multiple SDCA functions identified by
 18 * their type and ADR. there can be multiple SoundWire devices per
 19 * link, or multiple devices spread across multiple links. An IDA is
 20 * required to identify each instance.
 21 */
 22static DEFINE_IDA(sdca_function_ida);
 23
 24static void sdca_dev_release(struct device *dev)
 25{
 26	struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
 27	struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev);
 28
 29	ida_free(&sdca_function_ida, auxdev->id);
 30	kfree(sdev);
 31}
 32
 33/* alloc, init and add link devices */
 34static struct sdca_dev *sdca_dev_register(struct device *parent,
 35					  struct sdca_function_desc *function_desc)
 36{
 37	struct sdca_dev *sdev;
 38	struct auxiliary_device *auxdev;
 39	int ret;
 40	int rc;
 41
 42	sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
 43	if (!sdev)
 44		return ERR_PTR(-ENOMEM);
 45
 46	auxdev = &sdev->auxdev;
 47	auxdev->name = function_desc->name;
 48	auxdev->dev.parent = parent;
 49	auxdev->dev.fwnode = function_desc->node;
 50	auxdev->dev.release = sdca_dev_release;
 51
 52	sdev->function.desc = function_desc;
 53
 54	rc = ida_alloc(&sdca_function_ida, GFP_KERNEL);
 55	if (rc < 0) {
 56		kfree(sdev);
 57		return ERR_PTR(rc);
 58	}
 59	auxdev->id = rc;
 60
 61	/* now follow the two-step init/add sequence */
 62	ret = auxiliary_device_init(auxdev);
 63	if (ret < 0) {
 64		dev_err(parent, "failed to initialize SDCA function dev %s\n",
 65			function_desc->name);
 66		ida_free(&sdca_function_ida, auxdev->id);
 67		kfree(sdev);
 68		return ERR_PTR(ret);
 69	}
 70
 71	ret = auxiliary_device_add(auxdev);
 72	if (ret < 0) {
 73		dev_err(parent, "failed to add SDCA function dev %s\n",
 74			sdev->auxdev.name);
 75		/* sdev will be freed with the put_device() and .release sequence */
 76		auxiliary_device_uninit(&sdev->auxdev);
 77		return ERR_PTR(ret);
 78	}
 79
 80	return sdev;
 81}
 82
 83static void sdca_dev_unregister(struct sdca_dev *sdev)
 84{
 85	auxiliary_device_delete(&sdev->auxdev);
 86	auxiliary_device_uninit(&sdev->auxdev);
 87}
 88
 89int sdca_dev_register_functions(struct sdw_slave *slave)
 90{
 91	struct sdca_device_data *sdca_data = &slave->sdca_data;
 92	int i;
 93
 94	for (i = 0; i < sdca_data->num_functions; i++) {
 95		struct sdca_dev *func_dev;
 96
 97		func_dev = sdca_dev_register(&slave->dev,
 98					     &sdca_data->function[i]);
 99		if (IS_ERR(func_dev))
100			return PTR_ERR(func_dev);
101
102		sdca_data->function[i].func_dev = func_dev;
103	}
104
105	return 0;
106}
107EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
108
109void sdca_dev_unregister_functions(struct sdw_slave *slave)
110{
111	struct sdca_device_data *sdca_data = &slave->sdca_data;
112	int i;
113
114	for (i = 0; i < sdca_data->num_functions; i++)
115		sdca_dev_unregister(sdca_data->function[i].func_dev);
116}
117EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");
~/ Read-only file preview.