[PATCH] ASoC: rockchip: fix device node reference leak in rk3288_hdmi_analog

From: Haotian Zhang

Date: Thu Oct 08 2026 - 14:19:32 EST


snd_rk_mc_probe() acquires references to the codec and CPU device nodes
with of_parse_phandle() and stores them in the static rk_dailink, but the
driver never calls of_node_put() on them. All error paths and the success
path simply return, and the driver has no remove callback, so the
references are leaked for the whole lifetime of the card and each failed
or repeated probe leaks another pair. The same pattern was fixed for the
sibling rockchip_rt5645.c driver.

Release the codec and CPU device nodes on every probe error path and in a
new remove callback, and also drop the extra reference placed in args.np
by of_parse_phandle_with_fixed_args().

Fixes: eaae2ea73593 ("ASoC: rockchip: Add machine driver for RK3288 boards that use analog/HDMI")
Assisted-by: DeepSeek-V4.1-Flash
Signed-off-by: Haotian Zhang <vulab@xxxxxxxxxxx>
---
sound/soc/rockchip/rk3288_hdmi_analog.c | 42 +++++++++++++++++++------
1 file changed, 33 insertions(+), 9 deletions(-)

diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c
index 541163ed56fc..9bb02d41d901 100644
--- a/sound/soc/rockchip/rk3288_hdmi_analog.c
+++ b/sound/soc/rockchip/rk3288_hdmi_analog.c
@@ -201,36 +201,59 @@ static int snd_rk_mc_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev,
"Unable to parse property 'rockchip,audio-codec'\n");
- return ret;
+ goto put_codec_of_node;
}

ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name);
- if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "Unable to get codec_dai_name\n");
+ of_node_put(args.np);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret,
+ "Unable to get codec_dai_name\n");
+ goto put_codec_of_node;
+ }

rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller",
0);
if (!rk_dailink.cpus->of_node) {
dev_err(&pdev->dev,
"Property 'rockchip,i2s-controller' missing or invalid\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto put_codec_of_node;
}

rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;

ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing");
if (ret)
- return ret;
+ goto put_cpu_of_node;

snd_soc_card_set_drvdata(card, machine);

ret = devm_snd_soc_register_card(&pdev->dev, card);
- if (ret)
- return dev_err_probe(&pdev->dev, ret,
- "Soc register card failed\n");
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret,
+ "Soc register card failed\n");
+ goto put_cpu_of_node;
+ }

return 0;
+
+put_cpu_of_node:
+ of_node_put(rk_dailink.cpus->of_node);
+ rk_dailink.cpus->of_node = NULL;
+put_codec_of_node:
+ of_node_put(rk_dailink.codecs[0].of_node);
+ rk_dailink.codecs[0].of_node = NULL;
+
+ return ret;
+}
+
+static void snd_rk_mc_remove(struct platform_device *pdev)
+{
+ of_node_put(rk_dailink.cpus->of_node);
+ rk_dailink.cpus->of_node = NULL;
+ of_node_put(rk_dailink.codecs[0].of_node);
+ rk_dailink.codecs[0].of_node = NULL;
}

static const struct of_device_id rockchip_sound_of_match[] = {
@@ -242,6 +265,7 @@ MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);

static struct platform_driver rockchip_sound_driver = {
.probe = snd_rk_mc_probe,
+ .remove = snd_rk_mc_remove,
.driver = {
.name = DRV_NAME,
.pm = &snd_soc_pm_ops,
--
2.25.1