ASoC: Qualcomm Tambora (WCD9378) SDCA codec

Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> says:

Add support for the Qualcomm Tambora (WCD9378) headset codec in
SDCA-compliant mode over SoundWire. On ARM/DT platforms without
ACPI/DisCo firmware, the SDCA topology and SoundWire port properties
are supplied as static data through the codec driver.

Patches 1-5 extend the ASoC SDCA class framework so that a
class-based codec driver can be built on ARM/DT: allow builds
without ACPI, expose the SoundWire probe/remove library entry
points, add class_ops with a populate_function hook, and translate
sound-dai cells by entity index.

Patches 6-7 add the WCD9378 dt-binding and driver. The same
silicon can be fused for a mobile (non-SDCA) or an SDCA-compliant
mode; the two share the SoundWire class-ID compatible
(sdw20217011000) and are selected apart by the qcom,sdca-compliant
boolean property.

Tested with HPH playback and headset-mic capture on the Snapdragon
X2 Elite Glymur CRD and Lenovo T14s Gen7.

Jack detection is not wired up in this series.

Link: https://patch.msgid.link/20260925161049.758913-1-srinivas.kandagatla@oss.qualcomm.com
This commit is contained in:
Mark Brown 2026-09-29 12:50:08 +01:00
commit 22a310fa92
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
14 changed files with 1531 additions and 71 deletions

View File

@ -0,0 +1,105 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/qcom,wcd9378-sdw.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm WCD9378 (Tambora) SoundWire codec
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
description:
Qualcomm WCD9378 (Tambora) SoundWire codec. The same silicon can be
fused for a mobile (non-SDCA) mode or an SDCA-compliant mode; the
qcom,sdca-compliant property selects the latter and is required by
this driver.
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
const: sdw20217011000
reg:
maxItems: 1
qcom,sdca-compliant:
description:
Present when the codec is fused as SDCA-compliant. The mobile
(non-SDCA) variant carries the same class-ID compatible and is
handled by a different driver.
type: boolean
qcom,port-mapping:
description:
Each entry maps a slave data port to a master data port.
Entries are in order starting from slave port 1; slave port 0
is reserved and not represented. SDCA-compliant mode uses 8
entries covering DP1..DP8.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 8
maxItems: 8
reset-gpios:
description: GPIO used to release the codec from reset.
maxItems: 1
vdd-buck-supply:
description: 1.8 V analog buck supply.
vdd-rxtx-supply:
description: 1.8 V RX/TX supply.
vdd-io-supply:
description: Digital I/O supply.
vdd-mic-bias-supply:
description: Mic bias supply.
'#sound-dai-cells':
const: 1
required:
- compatible
- reg
- qcom,sdca-compliant
- qcom,port-mapping
- reset-gpios
- vdd-buck-supply
- vdd-rxtx-supply
- vdd-io-supply
- vdd-mic-bias-supply
- '#sound-dai-cells'
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
soundwire@7630000 {
reg = <0x07630000 0x10000>;
#address-cells = <2>;
#size-cells = <0>;
wcd9378c_sdw: audio-codec@0,3 {
compatible = "sdw20217011000";
reg = <0 3>;
qcom,sdca-compliant;
qcom,port-mapping = <2 2 3 4 5 6 7 8>;
reset-gpios = <&tlmm 191 GPIO_ACTIVE_LOW>;
vdd-buck-supply = <&vreg_l15b_1p8>;
vdd-rxtx-supply = <&vreg_l15b_1p8>;
vdd-io-supply = <&vreg_l18b_1p2>;
vdd-mic-bias-supply = <&vreg_bob1>;
#sound-dai-cells = <1>;
};
};
...

View File

@ -60,20 +60,16 @@ enum sdca_quirk {
SDCA_QUIRKS_SKIP_FUNC_TYPE_PATCHING,
};
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA)
#if IS_ENABLED(CONFIG_SND_SOC_SDCA)
void sdca_lookup_functions(struct sdw_slave *slave);
void sdca_lookup_swft(struct sdw_slave *slave);
void sdca_lookup_interface_revision(struct sdw_slave *slave);
bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk);
int sdca_dev_register_functions(struct sdw_slave *slave);
void sdca_dev_unregister_functions(struct sdw_slave *slave);
#else
static inline void sdca_lookup_functions(struct sdw_slave *slave) {}
static inline void sdca_lookup_swft(struct sdw_slave *slave) {}
static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {}
static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk)
{
return false;
@ -88,4 +84,21 @@ static inline void sdca_dev_unregister_functions(struct sdw_slave *slave) {}
#endif
/*
* Called from the SoundWire bus during peripheral enumeration; gated on
* ACPI to avoid a soundwire_bus <-> snd_soc_sdca module cycle on DT builds
* (where the bodies are stubs anyway).
*/
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA)
void sdca_lookup_functions(struct sdw_slave *slave);
void sdca_lookup_interface_revision(struct sdw_slave *slave);
#else
static inline void sdca_lookup_functions(struct sdw_slave *slave) {}
static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {}
#endif
#endif

View File

@ -0,0 +1,70 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* The MIPI SDCA specification is available for public downloads at
* https://www.mipi.org/mipi-sdca-v1-0-download
*
* Copyright (C) 2025 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#ifndef __SDCA_CLASS_H__
#define __SDCA_CLASS_H__
#include <linux/completion.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
struct device;
struct regmap;
struct sdw_slave;
struct sdca_function_data;
/**
* struct sdca_class_ops - optional codec-provided class callbacks
* @populate_function: fill @function (entities, clusters, init_table, ...)
* from static tables in place of sdca_parse_function() on
* DT/non-DisCo platforms. Called once per SDCA function with
* @dev pointing at that function's device. Pure data source;
* performs no bus I/O. Return 0 on success or a negative errno.
*/
struct sdca_class_ops {
int (*populate_function)(struct device *dev,
struct sdca_function_data *function);
};
struct sdca_class_drv {
struct device *dev;
struct regmap *dev_regmap;
struct sdw_slave *sdw;
struct sdca_interrupt_info *irq_info;
const struct sdca_class_ops *ops;
struct mutex regmap_lock;
/* Serialise function initialisations */
struct mutex init_lock;
struct work_struct boot_work;
};
/* Library helpers used by codec-specific SDCA SoundWire drivers. */
int sdca_class_probe(struct sdw_slave *sdw,
struct sdca_class_drv *drv,
const struct sdca_class_ops *ops);
void sdca_class_remove(struct sdca_class_drv *drv);
/*
* PM helpers. Codec drivers embed sdca_class_drv in their own priv,
* own dev_set_drvdata(), and compose these into their own dev_pm_ops:
*
* static int wcd_runtime_suspend(struct device *dev) {
* struct wcd_priv *priv = dev_get_drvdata(dev);
* return sdca_class_runtime_suspend(&priv->class);
* }
*/
int sdca_class_runtime_suspend(struct sdca_class_drv *drv);
int sdca_class_runtime_resume(struct sdca_class_drv *drv);
int sdca_class_system_suspend(struct sdca_class_drv *drv);
int sdca_class_system_resume(struct sdca_class_drv *drv);
#endif /* __SDCA_CLASS_H__ */

View File

@ -2552,6 +2552,17 @@ config SND_SOC_WCD939X_SDW
The WCD9390/9395 is a audio codec IC Integrated in
Qualcomm SoCs like SM8650.
config SND_SOC_WCD9378_SDCA
tristate "Qualcomm Tambora (WCD9378) SDCA codec"
depends on SOUNDWIRE
depends on SND_SOC_SDCA_CLASS
help
This enables support for the Qualcomm Tambora (WCD9378) headset
codec when driven via the SDCA class driver on ARM platforms
without ACPI/DisCo tables. It provides the static SDCA topology
and SoundWire data port properties transcribed from the factory
ACPI tables.
config SND_SOC_WM0010
tristate
depends on SPI_MASTER

View File

@ -369,6 +369,7 @@ snd-soc-wcd938x-y := wcd938x.o
snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
snd-soc-wcd939x-y := wcd939x.o
snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
snd-soc-wcd9378-y := wcd9378-sdw.o wcd9378-sdca.o
snd-soc-wm-adsp-y := wm_adsp.o
snd-soc-wm-adsp-test-y := wm_adsp_fw_find_test.o
snd-soc-wm0010-y := wm0010.o
@ -825,6 +826,7 @@ ifdef CONFIG_SND_SOC_WCD939X_SDW
# avoid link failure by forcing sdw code built-in when needed
obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
endif
obj-$(CONFIG_SND_SOC_WCD9378_SDCA) += snd-soc-wcd9378.o
obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,20 @@
/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
/* Copyright (c) 2025 Qualcomm Technologies, Inc. */
#ifndef _WCD9378_SDCA_H
#define _WCD9378_SDCA_H
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
int wcd9378_sdca_probe(struct sdw_slave *slave,
const struct sdw_device_id *id);
void wcd9378_sdca_remove(struct sdw_slave *slave);
int wcd9378_sdca_read_prop(struct sdw_slave *slave);
int wcd9378_sdca_runtime_suspend(struct device *dev);
int wcd9378_sdca_runtime_resume(struct device *dev);
int wcd9378_sdca_system_suspend(struct device *dev);
int wcd9378_sdca_system_resume(struct device *dev);
#endif /* _WCD9378_SDCA_H */

View File

@ -0,0 +1,51 @@
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
// Copyright (c) 2025 Qualcomm Technologies, Inc.
/*
* WCD9378 (Tambora) SoundWire driver glue for the SDCA compute-mode
* codec. The SDCA topology and hardware-specific probe logic live in
* wcd9378-sdca.c; this file carries the SoundWire slave driver plumbing
* and the module boilerplate.
*/
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include "wcd9378-sdca.h"
static const struct dev_pm_ops wcd9378_sdw_pm_ops = {
SYSTEM_SLEEP_PM_OPS(wcd9378_sdca_system_suspend,
wcd9378_sdca_system_resume)
RUNTIME_PM_OPS(wcd9378_sdca_runtime_suspend,
wcd9378_sdca_runtime_resume, NULL)
};
static const struct sdw_slave_ops wcd9378_sdw_ops = {
.read_prop = wcd9378_sdca_read_prop,
};
static const struct sdw_device_id wcd9378_sdw_id[] = {
SDW_SLAVE_ENTRY(0x0217, 0x0110, 0),
{ }
};
MODULE_DEVICE_TABLE(sdw, wcd9378_sdw_id);
static struct sdw_driver wcd9378_sdw_driver = {
.driver = {
.name = "wcd9378",
.pm = pm_ptr(&wcd9378_sdw_pm_ops),
},
.probe = wcd9378_sdca_probe,
.remove = wcd9378_sdca_remove,
.id_table = wcd9378_sdw_id,
.ops = &wcd9378_sdw_ops,
};
module_sdw_driver(wcd9378_sdw_driver);
MODULE_DESCRIPTION("Qualcomm WCD9378 (Tambora) SoundWire codec");
MODULE_AUTHOR("Qualcomm Technologies, Inc.");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_IMPORT_NS("SND_SOC_SDCA");
MODULE_IMPORT_NS("SND_SOC_SDCA_CLASS");

View File

@ -3,11 +3,13 @@ menu "SoundWire (SDCA)"
config SND_SOC_SDCA
tristate "SDCA core support"
depends on ACPI
select AUXILIARY_BUS
help
This option enables support for the MIPI SoundWire Device
Class for Audio (SDCA).
Class for Audio (SDCA). The ACPI-specific enumeration
helpers are only built when CONFIG_ACPI is enabled; on
non-ACPI platforms the SDCA function data is supplied
statically by the codec driver.
config SND_SOC_SDCA_HID
bool "SDCA HID support"

View File

@ -20,7 +20,7 @@
#include <sound/sdca_function.h>
#include <sound/sdca_interrupts.h>
#include <sound/sdca_regmap.h>
#include "sdca_class.h"
#include <sound/sdca_class.h>
#define CLASS_SDW_ATTACH_TIMEOUT_MS 5000
@ -136,18 +136,35 @@ static void class_boot_work(struct work_struct *work)
pm_runtime_put_sync(drv->dev);
}
static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
/**
* sdca_class_probe - SDCA class SoundWire slave probe helper
* @sdw: SoundWire slave
* @drv: caller-allocated sdca_class_drv storage. The caller (a codec
* driver, or the built-in class_sdw_driver in this file) owns the
* allocation and sets its own dev_set_drvdata() -- the framework
* does not touch drvdata. Typically embedded in the codec's own
* priv struct so codec drivers can keep per-slave state.
* @ops: optional codec-provided class callbacks (may be NULL for
* pure-generic SDCA parts that need no quirks)
*
* Codec-specific SoundWire drivers call this from their .probe after
* allocating a struct sdca_class_drv (usually embedded in their own
* priv) and setting drvdata to their priv. The framework fills in the
* sdca_class_drv fields, sets up the class regmap, and queues the
* deferred boot work.
*/
int sdca_class_probe(struct sdw_slave *sdw,
struct sdca_class_drv *drv,
const struct sdca_class_ops *ops)
{
struct device *dev = &sdw->dev;
struct regmap_config *dev_config;
struct sdca_class_drv *drv;
int ret;
sdca_lookup_swft(sdw);
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
return -EINVAL;
dev_config = devm_kmemdup(dev, &class_dev_regmap_config,
sizeof(*dev_config), GFP_KERNEL);
@ -156,11 +173,10 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
drv->dev = dev;
drv->sdw = sdw;
drv->ops = ops;
mutex_init(&drv->regmap_lock);
mutex_init(&drv->init_lock);
dev_set_drvdata(drv->dev, drv);
INIT_WORK(&drv->boot_work, class_boot_work);
dev_config->lock_arg = &drv->regmap_lock;
@ -185,39 +201,83 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
return 0;
}
EXPORT_SYMBOL_NS_GPL(sdca_class_probe, "SND_SOC_SDCA_CLASS");
static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
{
struct sdca_class_drv *drv;
/*
* Pure-generic SDCA parts: no codec priv to embed, so allocate a
* bare sdca_class_drv here and stash it in drvdata for the
* built-in PM ops to fetch.
*/
drv = devm_kzalloc(&sdw->dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
return -ENOMEM;
dev_set_drvdata(&sdw->dev, drv);
return sdca_class_probe(sdw, drv, NULL);
}
/**
* sdca_class_remove - SDCA class SoundWire slave remove helper
* @drv: caller-owned sdca_class_drv (the one handed to sdca_class_probe()).
*
* Cancels the deferred boot work so devres can safely free @drv and the
* embedding codec priv without racing class_boot_work. Codec-specific
* SoundWire drivers that call sdca_class_probe() must call this from
* their .remove with the same drv pointer they passed to probe.
*/
void sdca_class_remove(struct sdca_class_drv *drv)
{
cancel_work_sync(&drv->boot_work);
}
EXPORT_SYMBOL_NS_GPL(sdca_class_remove, "SND_SOC_SDCA_CLASS");
static void class_sdw_remove(struct sdw_slave *sdw)
{
struct device *dev = &sdw->dev;
struct sdca_class_drv *drv = dev_get_drvdata(dev);
struct sdca_class_drv *drv = dev_get_drvdata(&sdw->dev);
cancel_work_sync(&drv->boot_work);
sdca_class_remove(drv);
}
static int class_suspend(struct device *dev)
/**
* sdca_class_system_suspend - SDCA class system suspend helper
* @drv: caller-owned sdca_class_drv.
*
* Codec drivers compose this into their own dev_pm_ops. Disables the
* SoundWire interrupt and forces runtime suspend of the underlying
* class regmap.
*/
int sdca_class_system_suspend(struct sdca_class_drv *drv)
{
struct sdca_class_drv *drv = dev_get_drvdata(dev);
int ret;
disable_irq(drv->sdw->irq);
ret = pm_runtime_force_suspend(dev);
ret = pm_runtime_force_suspend(drv->dev);
if (ret) {
dev_err(dev, "failed to force suspend: %d\n", ret);
dev_err(drv->dev, "failed to force suspend: %d\n", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(sdca_class_system_suspend, "SND_SOC_SDCA_CLASS");
static int class_resume(struct device *dev)
/**
* sdca_class_system_resume - SDCA class system resume helper
* @drv: caller-owned sdca_class_drv.
*/
int sdca_class_system_resume(struct sdca_class_drv *drv)
{
struct sdca_class_drv *drv = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_force_resume(dev);
ret = pm_runtime_force_resume(drv->dev);
if (ret) {
dev_err(dev, "failed to force resume: %d\n", ret);
dev_err(drv->dev, "failed to force resume: %d\n", ret);
return ret;
}
@ -225,11 +285,14 @@ static int class_resume(struct device *dev)
return 0;
}
EXPORT_SYMBOL_NS_GPL(sdca_class_system_resume, "SND_SOC_SDCA_CLASS");
static int class_runtime_suspend(struct device *dev)
/**
* sdca_class_runtime_suspend - SDCA class runtime suspend helper
* @drv: caller-owned sdca_class_drv.
*/
int sdca_class_runtime_suspend(struct sdca_class_drv *drv)
{
struct sdca_class_drv *drv = dev_get_drvdata(dev);
/*
* Whilst the driver doesn't power the chip down here, going into runtime
* suspend lets the SoundWire bus power down, which means the driver
@ -239,10 +302,14 @@ static int class_runtime_suspend(struct device *dev)
return 0;
}
EXPORT_SYMBOL_NS_GPL(sdca_class_runtime_suspend, "SND_SOC_SDCA_CLASS");
static int class_runtime_resume(struct device *dev)
/**
* sdca_class_runtime_resume - SDCA class runtime resume helper
* @drv: caller-owned sdca_class_drv.
*/
int sdca_class_runtime_resume(struct sdca_class_drv *drv)
{
struct sdca_class_drv *drv = dev_get_drvdata(dev);
int ret;
ret = sdw_slave_wait_for_init(drv->sdw, CLASS_SDW_ATTACH_TIMEOUT_MS);
@ -265,10 +332,37 @@ static int class_runtime_resume(struct device *dev)
return ret;
}
EXPORT_SYMBOL_NS_GPL(sdca_class_runtime_resume, "SND_SOC_SDCA_CLASS");
static const struct dev_pm_ops class_pm_ops = {
SYSTEM_SLEEP_PM_OPS(class_suspend, class_resume)
RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
/*
* Convenience dev_pm_ops used by the built-in class_sdw_driver, which
* stashes its sdca_class_drv in drvdata directly. Codec drivers that
* embed sdca_class_drv in their own priv compose their own dev_pm_ops
* using the sdca_class_*_suspend/resume helpers above.
*/
static int class_pm_system_suspend(struct device *dev)
{
return sdca_class_system_suspend(dev_get_drvdata(dev));
}
static int class_pm_system_resume(struct device *dev)
{
return sdca_class_system_resume(dev_get_drvdata(dev));
}
static int class_pm_runtime_suspend(struct device *dev)
{
return sdca_class_runtime_suspend(dev_get_drvdata(dev));
}
static int class_pm_runtime_resume(struct device *dev)
{
return sdca_class_runtime_resume(dev_get_drvdata(dev));
}
static const struct dev_pm_ops sdca_class_pm_ops = {
SYSTEM_SLEEP_PM_OPS(class_pm_system_suspend, class_pm_system_resume)
RUNTIME_PM_OPS(class_pm_runtime_suspend, class_pm_runtime_resume, NULL)
};
static const struct sdw_device_id class_sdw_id[] = {
@ -282,7 +376,7 @@ MODULE_DEVICE_TABLE(sdw, class_sdw_id);
static struct sdw_driver class_sdw_driver = {
.driver = {
.name = "sdca_class",
.pm = pm_ptr(&class_pm_ops),
.pm = pm_ptr(&sdca_class_pm_ops),
},
.probe = class_sdw_probe,

View File

@ -1,35 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* The MIPI SDCA specification is available for public downloads at
* https://www.mipi.org/mipi-sdca-v1-0-download
*
* Copyright (C) 2025 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#ifndef __SDCA_CLASS_H__
#define __SDCA_CLASS_H__
#include <linux/completion.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
struct device;
struct regmap;
struct sdw_slave;
struct sdca_function_data;
struct sdca_class_drv {
struct device *dev;
struct regmap *dev_regmap;
struct sdw_slave *sdw;
struct sdca_interrupt_info *irq_info;
struct mutex regmap_lock;
/* Serialise function initialisations */
struct mutex init_lock;
struct work_struct boot_work;
};
#endif /* __SDCA_CLASS_H__ */

View File

@ -26,7 +26,7 @@
#include <sound/soc-component.h>
#include <sound/soc-dai.h>
#include <sound/soc.h>
#include "sdca_class.h"
#include <sound/sdca_class.h>
#include "sdca_function_device.h"
struct class_function_drv {
@ -216,9 +216,43 @@ static int class_function_set_jack(struct snd_soc_component *component,
return sdca_jack_set_jack(core->irq_info, jack);
}
/*
* DT phandle cell is the SDCA entity index (matches dais[].id), not the
* positional DAI index the default xlate assumes.
*/
static int class_function_of_xlate_dai_name(struct snd_soc_component *component,
const struct of_phandle_args *args,
const char **dai_name)
{
struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
struct sdca_function_data *function = drv->function;
struct sdca_entity *entity;
u32 target;
if (args->args_count != 1)
return -EINVAL;
target = args->args[0];
if (target >= function->num_entities)
goto err;
entity = &function->entities[target];
if ((entity->type != SDCA_ENTITY_TYPE_IT &&
entity->type != SDCA_ENTITY_TYPE_OT) || !entity->iot.is_dataport)
goto err;
*dai_name = entity->label;
return 0;
err:
dev_err(component->dev, "xlate: no dataport entity at index %u (num_entities=%d)\n",
target, function->num_entities);
return -EINVAL;
}
static const struct snd_soc_component_driver class_function_component_drv = {
.fixup_controls = class_function_component_fixup_controls,
.remove = class_function_component_remove,
.of_xlate_dai_name = class_function_of_xlate_dai_name,
.endianness = 1,
};
@ -329,7 +363,14 @@ static int class_function_probe(struct auxiliary_device *auxdev,
drv->core = core;
drv->function = &sdev->function;
ret = sdca_parse_function(dev, drv->function);
if (core->ops && core->ops->populate_function) {
ret = core->ops->populate_function(dev, drv->function);
} else if (drv->function->desc->node) {
ret = sdca_parse_function(dev, drv->function);
} else {
dev_err(dev, "no firmware node and no populate_function hook\n");
return -ENOENT;
}
if (ret)
return ret;

View File

@ -15,6 +15,7 @@
#include <sound/sdca.h>
#include <sound/sdca_function.h>
#if IS_ENABLED(CONFIG_ACPI)
void sdca_lookup_interface_revision(struct sdw_slave *slave)
{
struct fwnode_handle *fwnode = slave->dev.fwnode;
@ -45,6 +46,9 @@ void sdca_lookup_swft(struct sdw_slave *slave)
devm_add_action_or_reset(&slave->dev, devm_acpi_table_put,
slave->sdca_data.swft);
}
#else
void sdca_lookup_swft(struct sdw_slave *slave) { }
#endif
EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA");
static bool sdca_device_quirk_rt712_vb(struct sdw_slave *slave)

View File

@ -25,6 +25,7 @@
*/
#define SDCA_PROPERTY_LENGTH 64
#if IS_ENABLED(CONFIG_ACPI)
static int patch_sdca_function_type(u32 interface_revision, u32 *function_type)
{
/*
@ -201,6 +202,7 @@ void sdca_lookup_functions(struct sdw_slave *slave)
acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data);
}
EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA");
#endif
struct raw_init_write {
__le32 addr;