From 760158d29b16f4cf28d8d7bd635f3e63c2d87e8b Mon Sep 17 00:00:00 2001 From: XiaoTan Luo Date: Fri, 10 Sep 2021 14:29:02 +0800 Subject: [PATCH] ASoC: rockchip: add machine driver for hdmi audio this patch is used for rockchip HDMI audio output. Signed-off-by: XiaoTan Luo Change-Id: I577179e7563ad241014d023da12af1e622e84c9a --- sound/soc/rockchip/Kconfig | 8 + sound/soc/rockchip/Makefile | 2 + sound/soc/rockchip/rockchip_hdmi.c | 265 +++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 sound/soc/rockchip/rockchip_hdmi.c diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index b49408e86e7b..186c40b7048d 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -108,6 +108,14 @@ config SND_SOC_ROCKCHIP_RT5651_RK628 Say Y or M here if you want to add support for SoC audio on Rockchip boards for RT5651 RK628 HDMIIn +config SND_SOC_ROCKCHIP_HDMI + tristate "ASoC support for Rockchip HDMI audio" + depends on SND_SOC_ROCKCHIP && CLKDEV_LOOKUP + select SND_SOC_HDMI_CODEC + help + Say Y or M here if you want to add support for SoC audio on Rockchip + boards using built-in HDMI or external HDMI. + config SND_SOC_RK3288_HDMI_ANALOG tristate "ASoC support multiple codecs for Rockchip RK3288 boards" depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index 7541e2a636a3..927ba2aa5924 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o obj-$(CONFIG_SND_SOC_ROCKCHIP_VAD) += snd-soc-rockchip-vad.o +snd-soc-rockchip-hdmi-objs := rockchip_hdmi.o snd-soc-rockchip-max98090-objs := rockchip_max98090.o snd-soc-rockchip-multicodecs-objs := rockchip_multicodecs.o snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o @@ -30,6 +31,7 @@ snd-soc-rockchip-rt5651-rk628-objs := rockchip_rt5651_rk628.o snd-soc-rk3288-hdmi-analog-objs := rk3288_hdmi_analog.o snd-soc-rk3399-gru-sound-objs := rk3399_gru_sound.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_HDMI) += snd-soc-rockchip-hdmi.o obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o obj-$(CONFIG_SND_SOC_ROCKCHIP_MULTICODECS) += snd-soc-rockchip-multicodecs.o obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o diff --git a/sound/soc/rockchip/rockchip_hdmi.c b/sound/soc/rockchip/rockchip_hdmi.c new file mode 100644 index 000000000000..66c6862f3133 --- /dev/null +++ b/sound/soc/rockchip/rockchip_hdmi.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip machine ASoC driver for Rockchip HDMI audio output + * + * Copyright (C) 2021 Rockchip Electronics Co., Ltd + * + * Authors: XiaoTan Luo + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rk-hdmi-sound" +#define MAX_CODECS 2 +#define DEFAULT_MCLK_FS 256 + +struct rk_hdmi_data { + struct snd_soc_card card; + struct snd_soc_dai_link dai; + struct snd_soc_jack hdmi_jack; + struct snd_soc_jack_pin hdmi_jack_pin; + unsigned int mclk_fs; + bool jack_det; +}; + +static int rk_hdmi_fill_widget_info(struct device *dev, + struct snd_soc_dapm_widget *w, enum snd_soc_dapm_type id, + void *priv, const char *wname, const char *stream, + struct snd_kcontrol_new *wc, int numkc, + int (*event)(struct snd_soc_dapm_widget *, + struct snd_kcontrol *, int), unsigned short event_flags) +{ + w->id = id; + w->name = devm_kstrdup(dev, wname, GFP_KERNEL); + if (!w->name) + return -ENOMEM; + + w->sname = stream; + w->reg = SND_SOC_NOPM; + w->shift = 0; + w->kcontrol_news = wc; + w->num_kcontrols = numkc; + w->priv = priv; + w->event = event; + w->event_flags = event_flags; + + return 0; +} + +static int rk_dailink_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = rtd->card; + struct rk_hdmi_data *rk_data = snd_soc_card_get_drvdata(rtd->card); + struct device *dev = rtd->card->dev; + int ret = 0; + struct snd_soc_dapm_widget *widgets; + + if (!rk_data->jack_det) + return 0; + + widgets = devm_kcalloc(card->dapm.dev, 1, + sizeof(*widgets), GFP_KERNEL); + if (!widgets) + return -ENOMEM; + + ret = rk_hdmi_fill_widget_info(card->dapm.dev, widgets, + snd_soc_dapm_line, NULL, + rk_data->hdmi_jack_pin.pin, + NULL, NULL, 0, NULL, 0); + if (ret < 0) + return ret; + + ret = snd_soc_dapm_new_controls(&card->dapm, widgets, 1); + if (ret < 0) + return ret; + + ret = snd_soc_dapm_new_widgets(rtd->card); + if (ret < 0) + return ret; + + ret = snd_soc_card_jack_new(rtd->card, + rk_data->hdmi_jack_pin.pin, + rk_data->hdmi_jack_pin.mask, + &rk_data->hdmi_jack, + &rk_data->hdmi_jack_pin, 1); + if (ret) { + dev_err(dev, "Can't new HDMI Jack %d\n", ret); + return ret; + } + return snd_soc_component_set_jack(codec_dai->component, + &rk_data->hdmi_jack, NULL); + +} + +static int rk_hdmi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct rk_hdmi_data *rk_data = snd_soc_card_get_drvdata(rtd->card); + unsigned int mclk; + int ret; + + mclk = params_rate(params) * rk_data->mclk_fs; + + ret = snd_soc_dai_set_sysclk(codec_dai, substream->stream, mclk, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) { + dev_err(codec_dai->dev, + "Set codec_dai sysclk failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, substream->stream, mclk, + SND_SOC_CLOCK_OUT); + if (ret && ret != -ENOTSUPP) { + dev_err(cpu_dai->dev, + "Set cpu_dai sysclk failed: %d\n", ret); + return ret; + } + + return 0; + +} + +static struct snd_soc_ops rk_ops = { + .hw_params = rk_hdmi_hw_params, +}; + +static int rk_hdmi_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_dai_link_component *codecs; + struct of_phandle_args args; + struct device_node *codec_np, *cpu_np; + struct rk_hdmi_data *rk_data; + int count; + u32 val; + int ret = 0, i = 0, idx = 0; + + rk_data = devm_kzalloc(&pdev->dev, sizeof(*rk_data), GFP_KERNEL); + if (!rk_data) + return -ENOMEM; + + rk_data->card.dev = &pdev->dev; + rk_data->dai.init = &rk_dailink_init; + rk_data->dai.ops = &rk_ops; + rk_data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + /* Parse the card name from DT */ + ret = snd_soc_of_parse_card_name(&rk_data->card, "rockchip,card-name"); + if (ret < 0) + return ret; + rk_data->dai.name = rk_data->card.name; + rk_data->dai.stream_name = rk_data->card.name; + count = of_count_phandle_with_args(np, "rockchip,codec", NULL); + if (count < 0 || count > MAX_CODECS) + return -EINVAL; + + /* refine codecs, remove unavailable node */ + for (i = 0; i < count; i++) { + codec_np = of_parse_phandle(np, "rockchip,codec", i); + if (!codec_np) + return -ENODEV; + if (of_device_is_available(codec_np)) + idx++; + of_node_put(codec_np); + } + + if (!idx) + return -ENODEV; + + codecs = devm_kcalloc(&pdev->dev, idx, + sizeof(*codecs), GFP_KERNEL); + rk_data->dai.codecs = codecs; + rk_data->dai.num_codecs = idx; + idx = 0; + for (i = 0; i < count; i++) { + codec_np = of_parse_phandle(np, "rockchip,codec", i); + if (!codec_np) + return -ENODEV; + if (!of_device_is_available(codec_np)) + continue; + + ret = of_parse_phandle_with_fixed_args(np, "rockchip,codec", + 0, i, &args); + if (ret) { + of_node_put(codec_np); + return ret; + } + + codecs[idx].of_node = codec_np; + of_node_put(codec_np); + ret = snd_soc_get_dai_name(&args, &codecs[idx].dai_name); + if (ret) + return ret; + idx++; + } + + cpu_np = of_parse_phandle(np, "rockchip,cpu", 0); + if (!cpu_np) + return -ENODEV; + + rk_data->mclk_fs = DEFAULT_MCLK_FS; + if (!of_property_read_u32(np, "rockchip,mclk-fs", &val)) + rk_data->mclk_fs = val; + + rk_data->jack_det = + of_property_read_bool(np, "rockchip,jack-det"); + + rk_data->dai.cpu_of_node = cpu_np; + rk_data->dai.platform_of_node = cpu_np; + of_node_put(cpu_np); + + rk_data->hdmi_jack_pin.pin = rk_data->card.name; + rk_data->hdmi_jack_pin.mask = SND_JACK_LINEOUT; + rk_data->card.num_links = 1; + rk_data->card.owner = THIS_MODULE; + rk_data->card.dai_link = &rk_data->dai; + + snd_soc_card_set_drvdata(&rk_data->card, rk_data); + ret = devm_snd_soc_register_card(&pdev->dev, &rk_data->card); + if (ret == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (ret) { + dev_err(&pdev->dev, "card register failed %d\n", ret); + return ret; + } + platform_set_drvdata(pdev, &rk_data->card); + + return ret; +} + +static const struct of_device_id rockchip_sound_of_match[] = { + { .compatible = "rockchip,hdmi", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, rockchip_sound_of_match); + +static struct platform_driver rockchip_sound_driver = { + .probe = rk_hdmi_probe, + .driver = { + .name = DRV_NAME, + .pm = &snd_soc_pm_ops, + .of_match_table = rockchip_sound_of_match, + }, +}; + +module_platform_driver(rockchip_sound_driver); + +MODULE_AUTHOR("XiaoTan Luo "); +MODULE_DESCRIPTION("Rockchip HDMI ASoC machine driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME);