Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bbba96a
dt-bindings: sound: describe Denali protected audio
geocausa Aug 23, 2026
840000d
soundwire: add optional slave transport metadata
geocausa Aug 23, 2026
9b61451
ASoC: qcom: x1e80100: prepare Denali feedback backends
geocausa Aug 23, 2026
e30fc75
soundwire: support opt-in SIMPLE transport extensions
geocausa Aug 23, 2026
e0bde77
soundwire: qcom: expose Denali WSA feedback DAIs
geocausa Aug 23, 2026
6a1d152
ASoC: wsa884x: add opt-in VI and CPS feedback DAIs
geocausa Aug 23, 2026
8046919
ASoC: lpass-wsa-macro: add Denali feedback endpoints
geocausa Aug 23, 2026
0d5a008
ASoC: q6dsp: expose Denali WSA protection streams
ooaklee Aug 23, 2026
af2f1d6
soc: qcom: apr: drain callbacks before freeing services
ooaklee Sep 1, 2026
9ef4471
ASoC: q6apm: correlate commands and memory mappings
ooaklee Sep 1, 2026
3e13209
ASoC: q6dsp: parse FullIO topology extensions
geocausa Aug 23, 2026
8997725
ASoC: q6apm: add graph-scoped OOB configuration
geocausa Aug 23, 2026
d6670b7
ASoC: q6apm: run FullIO protection per graph
geocausa Aug 23, 2026
5965691
ASoC: qcom: wire Denali protection backends
ooaklee Aug 31, 2026
048a9b4
dt-bindings: sound: describe WSA884x speaker load
ooaklee Aug 31, 2026
c912416
ASoC: wsa884x: add opt-in 2S 4-ohm profile
geocausa Jul 29, 2026
e1dfe37
ASoC: codecs: order WSA protection clocks after both PAs
geocausa Aug 23, 2026
b03caeb
arm64: dts: qcom: denali: describe VI and CPS audio paths
geocausa Aug 23, 2026
766f34d
arm64: dts: qcom: denali: select 4-ohm speaker profile
ooaklee Aug 31, 2026
61c0ef0
ASoC: q6dsp: allow modules with more than eight input ports
ooaklee Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ properties:
- qcom,sm8650-sndcard
- qcom,sm8750-sndcard
- const: qcom,sm8450-sndcard
- items:
- const: microsoft,denali-sndcard
- const: qcom,x1e80100-sndcard
- enum:
- dell,xps13-9345-sndcard
- fairphone,fp4-sndcard
Expand Down
35 changes: 33 additions & 2 deletions Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,39 @@ properties:
minItems: 6
maxItems: 6

qcom,enable-cps:
type: boolean
description: Enable the dedicated CPS SoundWire source DAI on data port 6.

qcom,cps-offset1:
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 255
description: |
Slave-only SoundWire DP6 OffsetCtrl1 override for CPS. This does not
change the physical master-port schedule selected by qcom,port-mapping.

qcom,visense-channel-mask:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 255
description: |
Native WSA data-port-5 channel-enable mask for the VISENSE protection
sidechain. If absent, the codec default is used.

qcom,speaker-load-ohms:
enum: [4, 8]
description: |
Nominal speaker load impedance in ohms. A 4-ohm load selects the
matching PA gain and PBR thresholds when the amplifier reports a 2S
VPHX supply. If absent, the existing 8-ohm defaults are retained.

'#sound-dai-cells':
const: 0
enum: [0, 1]
description: |
Zero selects the legacy single speaker-playback DAI. With one cell,
DAI 0 is speaker playback, DAI 1 is the render-coupled VISENSE
protection-feedback sidechain, and DAI 2 is the dedicated CPS feedback
sidechain when qcom,enable-cps is present.

vdd-1p8-supply: true
vdd-io-supply: true
Expand Down Expand Up @@ -75,7 +106,7 @@ examples:
pinctrl-names = "default";
pinctrl-0 = <&spkr_2_sd_n_active>;
powerdown-gpios = <&lpass_tlmm 18 GPIO_ACTIVE_LOW>;
#sound-dai-cells = <0>;
#sound-dai-cells = <1>;
sound-name-prefix = "SpkrRight";
vdd-1p8-supply = <&vreg_l15b_1p8>;
vdd-io-supply = <&vreg_l3g_1p2>;
Expand Down
62 changes: 58 additions & 4 deletions arch/arm64/boot/dts/qcom/x1-microsoft-denali.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@
};

sound {
compatible = "qcom,x1e80100-sndcard";
compatible = "microsoft,denali-sndcard",
"qcom,x1e80100-sndcard";
model = "X1E80100-Microsoft-Surface-Pro-11";
audio-routing = "SpkrLeft IN", "WSA WSA_SPK1 OUT",
"SpkrRight IN", "WSA WSA_SPK2 OUT",
Expand All @@ -318,7 +319,7 @@
link-name = "WSA Playback";

codec {
sound-dai = <&left_spkr>, <&right_spkr>,
sound-dai = <&left_spkr 0>, <&right_spkr 0>,
<&swr0 0>, <&lpass_wsamacro 0>;
};

Expand All @@ -331,6 +332,51 @@
};
};

wsa-vi-dai-link {
link-name = "WSA VI Protection";

/*
* One two-channel Windows-equivalent feedback transport:
* amplifier VISENSE ports 10/11 -> WSA macro VI ->
* SoundWire DIN0 -> AudioReach WSA TX0.
*/
codec {
sound-dai = <&left_spkr 1>, <&right_spkr 1>,
<&swr0 9>, <&lpass_wsamacro 2>;
};

cpu {
sound-dai = <&q6apmbedai WSA_CODEC_DMA_TX_0>;
};

platform {
sound-dai = <&q6apm>;
};
};

wsa-cps-dai-link {
link-name = "WSA CPS Protection";

/*
* Dedicated CPS sidechain: both WSA8845 DP6 sources share
* physical SoundWire master port 13. swr0 DAI 10 is only
* the independent ASoC/SoundWire stream handle; qcom,port-mapping
* below remains authoritative for the physical master ports.
*/
codec {
sound-dai = <&left_spkr 2>, <&right_spkr 2>,
<&swr0 10>, <&lpass_wsamacro 4>;
};

cpu {
sound-dai = <&q6apmbedai WSA_CODEC_DMA_TX_1>;
};

platform {
sound-dai = <&q6apm>;
};
};

va-dai-link {
link-name = "VA Capture";

Expand Down Expand Up @@ -1075,23 +1121,31 @@
compatible = "sdw20217020400";
reg = <0 0>;
reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>;
#sound-dai-cells = <0>;
#sound-dai-cells = <1>;
sound-name-prefix = "SpkrLeft";
vdd-1p8-supply = <&vreg_l15b_1p8>;
vdd-io-supply = <&vreg_l12b_1p2>;
qcom,port-mapping = <1 2 3 7 10 13>;
qcom,speaker-load-ohms = <4>;
qcom,visense-channel-mask = <3>;
qcom,enable-cps;
qcom,cps-offset1 = <0>;
};

/* WSA8845, Right Speaker */
right_spkr: speaker@0,1 {
compatible = "sdw20217020400";
reg = <0 1>;
reset-gpios = <&lpass_tlmm 12 GPIO_ACTIVE_LOW>;
#sound-dai-cells = <0>;
#sound-dai-cells = <1>;
sound-name-prefix = "SpkrRight";
vdd-1p8-supply = <&vreg_l15b_1p8>;
vdd-io-supply = <&vreg_l12b_1p2>;
qcom,port-mapping = <4 5 6 7 11 13>;
qcom,speaker-load-ohms = <4>;
qcom,visense-channel-mask = <3>;
qcom,enable-cps;
qcom,cps-offset1 = <25>;
};
};

Expand Down
61 changes: 48 additions & 13 deletions drivers/soc/qcom/apr.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,35 @@ int apr_send_pkt(struct apr_device *adev, struct apr_pkt *pkt)
}
EXPORT_SYMBOL_GPL(apr_send_pkt);

void gpr_free_port(gpr_port_t *port)
static void pkt_router_svc_put(struct pkt_router_svc *svc)
{
if (refcount_dec_and_test(&svc->callback_refs))
complete(&svc->callbacks_drained);
}

static void pkt_router_svc_remove(struct pkt_router_svc *svc)
{
struct packet_router *gpr = port->pr;
struct packet_router *pr = svc->pr;
unsigned long flags;

spin_lock_irqsave(&gpr->svcs_lock, flags);
idr_remove(&gpr->svcs_idr, port->id);
spin_unlock_irqrestore(&gpr->svcs_lock, flags);
spin_lock_irqsave(&pr->svcs_lock, flags);
idr_remove(&pr->svcs_idr, svc->id);
spin_unlock_irqrestore(&pr->svcs_lock, flags);

if (!refcount_dec_and_test(&svc->callback_refs))
wait_for_completion(&svc->callbacks_drained);
}

/**
* gpr_free_port() - unregister a dynamic GPR port after callbacks drain
* @port: dynamic port returned by gpr_alloc_port()
*
* Context: process context, and never from @port's own callback.
*/
void gpr_free_port(gpr_port_t *port)
{
might_sleep();
pkt_router_svc_remove(port);
kfree(port);
}
EXPORT_SYMBOL_GPL(gpr_free_port);
Expand All @@ -105,6 +125,8 @@ gpr_port_t *gpr_alloc_port(struct apr_device *gdev, struct device *dev,
svc->priv = priv;
svc->dev = dev;
spin_lock_init(&svc->lock);
refcount_set(&svc->callback_refs, 1);
init_completion(&svc->callbacks_drained);

spin_lock(&pr->svcs_lock);
id = idr_alloc_cyclic(&pr->svcs_idr, svc, GPR_DYNAMIC_PORT_START,
Expand Down Expand Up @@ -191,7 +213,7 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf
{
uint16_t hdr_size, msg_type, ver, svc_id;
struct pkt_router_svc *svc;
struct apr_device *adev;
struct apr_device *adev = NULL;
struct apr_driver *adrv = NULL;
struct apr_resp_pkt resp;
struct apr_hdr *hdr;
Expand Down Expand Up @@ -232,13 +254,17 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf
svc_id = hdr->dest_svc;
spin_lock_irqsave(&apr->svcs_lock, flags);
svc = idr_find(&apr->svcs_idr, svc_id);
if (svc && !refcount_inc_not_zero(&svc->callback_refs))
svc = NULL;
if (svc && svc->dev->driver) {
adev = svc_to_apr_device(svc);
adrv = to_apr_driver(adev->dev.driver);
}
spin_unlock_irqrestore(&apr->svcs_lock, flags);

if (!adrv || !adev) {
if (svc)
pkt_router_svc_put(svc);
dev_err(apr->dev, "APR: service is not registered (%d)\n",
svc_id);
return -EINVAL;
Expand All @@ -255,6 +281,7 @@ static int apr_do_rx_callback(struct packet_router *apr, struct apr_rx_buf *abuf
resp.payload = buf + hdr_size;

adrv->callback(adev, &resp);
pkt_router_svc_put(svc);

return 0;
}
Expand Down Expand Up @@ -298,6 +325,8 @@ static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf

spin_lock_irqsave(&gpr->svcs_lock, flags);
svc = idr_find(&gpr->svcs_idr, hdr->dest_port);
if (svc && !refcount_inc_not_zero(&svc->callback_refs))
svc = NULL;
spin_unlock_irqrestore(&gpr->svcs_lock, flags);

if (!svc) {
Expand All @@ -308,6 +337,7 @@ static int gpr_do_rx_callback(struct packet_router *gpr, struct apr_rx_buf *abuf

if (svc->callback)
svc->callback(&resp, svc->priv, 0);
pkt_router_svc_put(svc);

return 0;
}
Expand Down Expand Up @@ -378,13 +408,10 @@ static void apr_device_remove(struct device *dev)
{
struct apr_device *adev = to_apr_device(dev);
struct apr_driver *adrv = to_apr_driver(dev->driver);
struct packet_router *apr = dev_get_drvdata(adev->dev.parent);

pkt_router_svc_remove(&adev->svc);
if (adrv->remove)
adrv->remove(adev);
spin_lock(&apr->svcs_lock);
idr_remove(&apr->svcs_idr, adev->svc.id);
spin_unlock(&apr->svcs_lock);
}

static int apr_uevent(const struct device *dev, struct kobj_uevent_env *env)
Expand Down Expand Up @@ -428,6 +455,8 @@ static int apr_add_device(struct device *dev, struct device_node *np,
svc->priv = adev;
svc->dev = dev;
spin_lock_init(&svc->lock);
refcount_set(&svc->callback_refs, 1);
init_completion(&svc->callbacks_drained);

adev->domain_id = domain_id;

Expand Down Expand Up @@ -458,26 +487,32 @@ static int apr_add_device(struct device *dev, struct device_node *np,
spin_unlock(&apr->svcs_lock);
if (ret < 0) {
dev_err(dev, "idr_alloc failed: %d\n", ret);
goto out;
goto free_adev;
}

/* Protection domain is optional, it does not exist on older platforms */
ret = of_property_read_string_index(np, "qcom,protection-domain",
1, &adev->service_path);
if (ret < 0 && ret != -EINVAL) {
dev_err(dev, "Failed to read second value of qcom,protection-domain\n");
goto out;
goto remove_svc;
}

dev_info(dev, "Adding APR/GPR dev: %s\n", dev_name(&adev->dev));

ret = device_register(&adev->dev);
if (ret) {
dev_err(dev, "device_register failed: %d\n", ret);
pkt_router_svc_remove(svc);
put_device(&adev->dev);
}

out:
return ret;

remove_svc:
pkt_router_svc_remove(svc);
free_adev:
kfree(adev);
return ret;
}

Expand Down
4 changes: 4 additions & 0 deletions drivers/soundwire/bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ int sdw_find_col_index(int col);
* @ch_mask: Channel mask
* @transport_params: Transport parameters
* @port_params: Port parameters
* @transport_params_override_mask: Bitmask of per-slave transport overrides
* @transport_params_override: Per-slave transport override values
* @port_node: List node for Master or Slave port_list
* @lane: Which lane is used
*
Expand All @@ -128,6 +130,8 @@ struct sdw_port_runtime {
int ch_mask;
struct sdw_transport_params transport_params;
struct sdw_port_params port_params;
unsigned int transport_params_override_mask;
struct sdw_transport_params transport_params_override;
struct list_head port_node;
unsigned int lane;
};
Expand Down
Loading