Linux Sound subsystem development
 help / color / mirror / Atom feed
From: Cezary Rojewski <cezary.rojewski@intel.com>
To: Yauhen Kharuzhy <jekhor@gmail.com>
Cc: <linux-sound@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	"Hans de Goede" <hansg@kernel.org>,
	Liam Girdwood <liam.r.girdwood@linux.intel.com>,
	Peter Ujfalusi <peter.ujfalusi@linux.intel.com>,
	Bard Liao <yung-chuan.liao@linux.intel.com>,
	Ranjani Sridharan <ranjani.sridharan@linux.intel.com>,
	Kai Vehmanen <kai.vehmanen@linux.intel.com>,
	Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>,
	Mark Brown <broonie@kernel.org>
Subject: Re: [PATCH v2 3/3] ASoC: Intel: cht_yogabook: Add driver for Lenovo Yoga Book tablets
Date: Mon, 2 Mar 2026 13:03:04 +0100	[thread overview]
Message-ID: <7932c58b-b6fa-40c3-8967-7710d84f9667@intel.com> (raw)
In-Reply-To: <20260301-asoc-yogabook-v2-v2-3-adcc7ed40985@gmail.com>

On 2026-03-01 10:33 PM, Yauhen Kharuzhy wrote:
> Add a new ASoC machine driver for Lenovo Yoga Book Intel Cherry
> Trail-based tablets (YB1-X90F/L, YB1-X91F/L models).
> 
> This platform uses a Realtek ALC5677 codec accompanied by a TS3A227E jack
> configuration detection IC.
> 
> The driver is based on the cht_bsw_rt5672.c mainline driver and the driver
> from the vendor's Android kernel [1].
> 
> There are no other known Cherry Trail platforms using an RT5677 codec, so
> the driver is named 'cht_yogabook', and some device-specific tricks are
> hardcoded, such as jack events and additional GPIOs controlling the
> speaker amplifier.

I'd switch to more specific naming. From the Intel-maintainer point of 
view, it's easier to navigate between configurations when 
<platform>-<codec> naming is in use. TBH, we do not see "yogabook", we 
configuration composed of Cherrytrail-based device and rt5677 I2C codec.

For the entire driver:
- fix alignments
- fix char-per-line limit, submission rules expect 100c-per-line limit

Below additional comments.

> 
> [1] https://github.com/deadman96385/android_kernel_lenovo_yeti/blob/master/sound/soc/intel/board/cht_bl_dpcm_rt5677.c
> 
> Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>

...


> diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
> index 25a1a9066cbf..58bd6029545b 100644
> --- a/sound/soc/intel/boards/Makefile
> +++ b/sound/soc/intel/boards/Makefile
> @@ -15,6 +15,7 @@ snd-soc-sst-cht-bsw-nau8824-y := cht_bsw_nau8824.o
>   snd-soc-sst-byt-cht-cx2072x-y := bytcht_cx2072x.o
>   snd-soc-sst-byt-cht-da7213-y := bytcht_da7213.o
>   snd-soc-sst-byt-cht-es8316-y := bytcht_es8316.o
> +snd-soc-sst-cht-yogabook-objs := cht_yogabook.o

Let's follow recommendation from Takashi [1]. Suffix "-objs" is obsolete.

[1]: https://lore.kernel.org/all/20240507155540.24815-13-tiwai@suse.de/

>   snd-soc-sst-byt-cht-nocodec-y := bytcht_nocodec.o
>   snd-soc-sof_rt5682-y := sof_rt5682.o
>   snd-soc-sof_cs42l42-y := sof_cs42l42.o
> @@ -47,6 +48,7 @@ obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_NAU8824_MACH) += snd-soc-sst-cht-bsw-nau8824.
>   obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_CX2072X_MACH) += snd-soc-sst-byt-cht-cx2072x.o
>   obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
>   obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o
> +obj-$(CONFIG_SND_SOC_INTEL_CHT_YOGABOOK_MACH) += snd-soc-sst-cht-yogabook.o
>   obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
>   obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o
>   obj-$(CONFIG_SND_SOC_INTEL_EHL_RT5660_MACH) += snd-soc-ehl-rt5660.o
> diff --git a/sound/soc/intel/boards/cht_yogabook.c b/sound/soc/intel/boards/cht_yogabook.c
> new file mode 100644
> index 000000000000..9d945cad5660
> --- /dev/null
> +++ b/sound/soc/intel/boards/cht_yogabook.c
> @@ -0,0 +1,551 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + *  cht_yogabook.c - ASoc Machine driver for Lenovo Yoga Book YB1-X90/X91
> + *  tablets, based on Intel Cherrytrail platform with RT5677 codec.
> + *
> + *  Copyright (C) 2026 Yauhen Kharuzhy <jekhor@gmail.com>
> + *
> + *  Based on cht_bsw_rt5672.c:
> + *  Copyright (C) 2014 Intel Corp
> + *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
> + *          Mengdong Lin <mengdong.lin@intel.com>
> + *
> + *  And based on the cht_bl_dpcm_rt5677.c from the Lenovo's Android kernel:
> + *  Copyright (C) 2014 Intel Corp
> + *  Author: Mythri P K <mythri.p.k@intel.com>

Not sure about the boiler plate here. Mentioning people is OK but the 
emails are all obsolete. Such information does not help anyone. Perhaps 
"based on XYZ" is enough.

> + */
> +
> +#include <linux/clk.h>
> +#include <linux/dmi.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/i2c.h>
> +#include <linux/input.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <sound/jack.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc-acpi.h>
> +#include <sound/soc.h>
> +#include "../../codecs/rt5677.h"
> +#include "../../codecs/ts3a227e.h"
> +#include "../atom/sst-atom-controls.h"
> +
> +#define RT5677_I2C_DEFAULT	"i2c-rt5677"
> +
> +/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
> +#define CHT_PLAT_CLK_3_HZ	19200000
> +#define CHT_CODEC_DAI		"rt5677-aif1"
> +
> +struct cht_yb_private {
> +	char codec_name[SND_ACPI_I2C_ID_LEN];
> +	struct snd_soc_jack jack;
> +	struct clk *mclk;
> +	struct gpio_desc *gpio_spk_en1;
> +	struct gpio_desc *gpio_spk_en2;
> +	struct gpio_desc *gpio_hp_en;
> +};
> +
> +static int cht_yb_platform_clock_control(struct snd_soc_dapm_widget *w,
> +					 struct snd_kcontrol *k, int  event)

Please replace 'k' with 'kctl' for the entire file.

> +{
> +	struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
> +	struct snd_soc_dai *codec_dai;
> +	struct cht_yb_private *ctx = snd_soc_card_get_drvdata(card);
> +	int ret;
> +
> +	codec_dai = snd_soc_card_get_codec_dai(card, CHT_CODEC_DAI);
> +	if (!codec_dai) {
> +		dev_err(card->dev,
> +			"Codec dai not found; Unable to set platform clock\n");
> +		return -EIO;
> +	}
> +
> +	if (SND_SOC_DAPM_EVENT_ON(event)) {
> +		if (ctx->mclk) {
> +			ret = clk_prepare_enable(ctx->mclk);
> +			if (ret < 0) {
> +				dev_err(card->dev,
> +					"could not configure MCLK state");
> +				return ret;
> +			}
> +		}
> +
> +		/* set codec PLL source to the 19.2MHz platform clock (MCLK) */
> +		ret = snd_soc_dai_set_pll(codec_dai, 0, RT5677_PLL1_S_MCLK,
> +					  CHT_PLAT_CLK_3_HZ, 48000 * 512);
> +		if (ret < 0) {
> +			dev_err(card->dev, "can't set codec pll: %d\n", ret);
> +			return ret;
> +		}
> +
> +		/* set codec sysclk source to PLL */
> +		ret = snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_PLL1,
> +					     48000 * 512, SND_SOC_CLOCK_IN);
> +		if (ret < 0) {
> +			dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
> +			return ret;
> +		}
> +	} else {
> +		/* Set codec sysclk source to its internal clock because codec
> +		 * PLL will be off when idle and MCLK will also be off by ACPI
> +		 * when codec is runtime suspended. Codec needs clock for jack
> +		 * detection and button press.
> +		 */
> +		snd_soc_dai_set_sysclk(codec_dai, RT5677_SCLK_S_RCCLK,
> +				       48000 * 512, SND_SOC_CLOCK_IN);
> +
> +		if (ctx->mclk)
> +			clk_disable_unprepare(ctx->mclk);
> +	}

The entire function is made of if-statement. I'd split this into:

xyz_platform_clock_control()
|_ xyz_platform_clock_enable()
|_ xyz_platform_clock_disable()


> +	return 0;
> +}
> +
> +static int cht_yb_hp_event(struct snd_soc_dapm_widget *w,
> +			   struct snd_kcontrol *k, int event)
> +{
> +	struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
> +	struct cht_yb_private *ctx = snd_soc_card_get_drvdata(card);
> +
> +	dev_dbg(card->dev, "HP event: %s\n",
> +		SND_SOC_DAPM_EVENT_ON(event) ? "ON" : "OFF");

These dev_dbg() can be dropped. Function tracer or DAPM events found 
within tracing/events are sufficient.

> +
> +	gpiod_set_value_cansleep(ctx->gpio_hp_en, SND_SOC_DAPM_EVENT_ON(event));
> +
> +	return 0;
> +}
> +
> +static int cht_yb_spk_event(struct snd_soc_dapm_widget *w,
> +			    struct snd_kcontrol *k, int event)
> +{
> +	struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
> +	struct cht_yb_private *ctx = snd_soc_card_get_drvdata(card);
> +
> +	dev_dbg(card->dev, "SPK event: %s\n",
> +		SND_SOC_DAPM_EVENT_ON(event) ? "ON" : "OFF");

Ditto.

> +
> +	gpiod_set_value_cansleep(ctx->gpio_spk_en1,
> +				 SND_SOC_DAPM_EVENT_ON(event));
> +	gpiod_set_value_cansleep(ctx->gpio_spk_en2,
> +				 SND_SOC_DAPM_EVENT_ON(event));
> +
> +	return 0;
> +}

...

> +static int cht_yb_codec_init(struct snd_soc_pcm_runtime *runtime)
> +{
> +	int ret = 0;

Move as last.

> +	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
> +	struct snd_soc_component *component = codec_dai->component;
> +	struct cht_yb_private *ctx = snd_soc_card_get_drvdata(runtime->card);
> +
> +	/* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1.
> +	 * The ASRC clock source is clk_i2s1_asrc.
> +	 */
> +	rt5677_sel_asrc_clk_src(component, RT5677_DA_STEREO_FILTER |
> +			RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE,
> +			RT5677_CLK_SEL_I2S1_ASRC);
> +	/* Enable codec ASRC function for Mono ADC L.
> +	 * The ASRC clock source is clk_sys2_asrc.
> +	 */
> +	rt5677_sel_asrc_clk_src(component, RT5677_AD_MONO_L_FILTER,
> +				RT5677_CLK_SEL_SYS2);
> +
> +	ctx->gpio_spk_en1 = devm_gpiod_get(component->dev, "speaker-enable",
> +					   GPIOD_OUT_LOW);
> +	if (IS_ERR(ctx->gpio_spk_en1)) {
> +		dev_err(component->dev, "Can't find speaker enable GPIO\n");
> +		return PTR_ERR(ctx->gpio_spk_en1);
> +	}
> +
> +	ctx->gpio_spk_en2 = devm_gpiod_get(component->dev, "speaker-enable2",
> +					   GPIOD_OUT_LOW);
> +	if (IS_ERR(ctx->gpio_spk_en2)) {
> +		dev_err(component->dev, "Can't find speaker enable 2 GPIO\n");
> +		return PTR_ERR(ctx->gpio_spk_en2);
> +	}
> +
> +	ctx->gpio_hp_en = devm_gpiod_get(component->dev, "headphone-enable",
> +					 GPIOD_OUT_LOW);
> +	if (IS_ERR(ctx->gpio_hp_en)) {
> +		dev_err(component->dev, "Can't find headphone enable GPIO\n");

The messages here are misleading. "Can't find ..." when devm_gpiod_get() 
returns different IS_ERR() than -ENOENT is incorrect. I'd just state 
that XYZ operation failed and append the return code. Scales nicely into 
the future.

> +		return PTR_ERR(ctx->gpio_hp_en);
> +	}
> +
> +	if (ctx->mclk) {

If no-MCLK case even valid here? You're providing new driver for an 
older, specific configuration. Perhaps limiting the code to the actual 
(and only) user is the way to go?

> +		/*
> +		 * The firmware might enable the clock at
> +		 * boot (this information may or may not
> +		 * be reflected in the enable clock register).
> +		 * To change the rate we must disable the clock
> +		 * first to cover these cases. Due to common
> +		 * clock framework restrictions that do not allow
> +		 * to disable a clock that has not been enabled,
> +		 * we need to enable the clock first.
> +		 */
> +		ret = clk_prepare_enable(ctx->mclk);
> +		if (!ret)
> +			clk_disable_unprepare(ctx->mclk);
> +
> +		ret = clk_set_rate(ctx->mclk, CHT_PLAT_CLK_3_HZ);
> +
> +		if (ret) {
> +			dev_err(runtime->dev, "unable to set MCLK rate\n");
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}

...

> +#define SOF_CARD_NAME "cht yogabook"
> +#define SOF_DRIVER_NAME "SOF"
> +
> +#define CARD_NAME "cht-yogabook"
> +#define DRIVER_NAME NULL

Have you tested the driver with both, legacy -and- SOF firmware? If 
you're using just one of them, let's limit the driver to that one. 
Anything else can be part of a follow up series if there is a need to 
support multiple solutions. Otherwise we'd be merging code with no 
coverage and no user.

> +
> +static int snd_cht_yb_mc_probe(struct platform_device *pdev)
> +{
> +	int ret_val = 0;
> +	struct cht_yb_private *drv;

No. 'drv', short for 'driver', is not the right name for the private 
context. Typically 'priv' or 'data' is used.

> +	struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;

dev_get_platdata(), no reason to avoid well-established APIs.

> +	const char *platform_name;
> +	struct acpi_device *adev;
> +	struct device *codec_dev;
> +	bool sof_parent;
> +	int i;
> +
> +	drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
> +	if (!drv)
> +		return -ENOMEM;
> +
> +	strscpy(drv->codec_name, RT5677_I2C_DEFAULT);
> +
> +	/* fixup codec name based on HID if ACPI node is present */
> +	adev = acpi_dev_get_first_match_dev(mach->id, NULL, -1);
> +	if (adev) {
> +		snprintf(drv->codec_name, sizeof(drv->codec_name),
> +			 "i2c-%s", acpi_dev_name(adev));
> +		dev_info(&pdev->dev, "real codec name: %s\n", drv->codec_name);
> +
> +		put_device(&adev->dev);
> +		for (i = 0; i < ARRAY_SIZE(cht_yb_dailink); i++) {
> +			if (cht_yb_dailink[i].codecs->name &&
> +			    !strcmp(cht_yb_dailink[i].codecs->name,
> +				    RT5677_I2C_DEFAULT)) {
> +				cht_yb_dailink[i].codecs->name = drv->codec_name;
> +				break;
> +			}
> +		}
> +	}
> +
> +	codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL,
> +					    drv->codec_name);
> +	if (!codec_dev)
> +		return -EPROBE_DEFER;
> +
> +	if (adev) {
> +		ret_val = devm_acpi_dev_add_driver_gpios(codec_dev,
> +							 cht_yb_gpios);
> +		if (ret_val)
> +			dev_warn(&pdev->dev,
> +				 "Unable to add GPIO mapping table: %d\n",
> +				 ret_val);
> +	}
> +
> +	/* override platform name, if required */
> +	snd_soc_card_cht_yb.dev = &pdev->dev;
> +	platform_name = mach->mach_params.platform;
> +
> +	ret_val = snd_soc_fixup_dai_links_platform_name(&snd_soc_card_cht_yb,
> +							platform_name);
> +	if (ret_val) {
> +		dev_err(&pdev->dev,
> +			"snd_soc_fixup_dai_links_platform_name failed: %d\n",
> +			ret_val);
> +		return ret_val;
> +	}
> +
> +	drv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
> +	if (IS_ERR(drv->mclk)) {
> +		dev_err(&pdev->dev,
> +			"Failed to get MCLK from pmc_plt_clk_3: %ld\n",
> +			PTR_ERR(drv->mclk));
> +		return PTR_ERR(drv->mclk);
> +	}
> +	snd_soc_card_set_drvdata(&snd_soc_card_cht_yb, drv);
> +
> +	sof_parent = snd_soc_acpi_sof_parent(&pdev->dev);
> +
> +	/* set the card and driver name */
> +	if (sof_parent) {
> +		snd_soc_card_cht_yb.name = SOF_CARD_NAME;
> +		snd_soc_card_cht_yb.driver_name = SOF_DRIVER_NAME;
> +	} else {
> +		snd_soc_card_cht_yb.name = CARD_NAME;
> +		snd_soc_card_cht_yb.driver_name = DRIVER_NAME;
> +	}
> +
> +	/* register the soc card */
> +	snd_soc_card_cht_yb.dev = &pdev->dev;

I'd move away from static-card approach and dynamically allocate the 
object here, in the probe() function.

> +	ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht_yb);
> +	if (ret_val) {
> +		dev_err(&pdev->dev,
> +			"snd_soc_register_card failed %d\n", ret_val);
> +		return ret_val;
> +	}
> +	platform_set_drvdata(pdev, &snd_soc_card_cht_yb);

This seems bogus. The probe() found here is not the function that 
spawned the platform-device 'pdev' yet it attempts to manipulate 
device's private data. At the same time there is no 
platform_get_drvdata() anywhere. Unless there's a strong reason for the 
call, please drop the line.

> +
> +	return ret_val;
> +}
> +
> +static struct platform_driver snd_cht_yb_mc_driver = {
> +	.driver = {
> +		.name = "cht-yogabook",

It seems .pm assignment is missing. I see that the "base" is missing 
this too. Looks like a flaw, I'd assign snd_soc_pm_ops and re-test. The 
ops describe basic bring-up and tear-down procedures and I'm expecting 
them to notify us about any problems rather than harm the configuration.

> +	},
> +	.probe = snd_cht_yb_mc_probe,
> +};
> +
> +module_platform_driver(snd_cht_yb_mc_driver);
> +
> +MODULE_DESCRIPTION("Lenovo Yoga Book YB1-X9x machine driver");
> +MODULE_AUTHOR("Yauhen Kharuzhy");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:cht-yogabook");
> 


  reply	other threads:[~2026-03-02 12:03 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-01 21:33 [PATCH v2 0/3] Add ASoC machine driver for Lenovo YB1 tablets Yauhen Kharuzhy
2026-03-01 21:33 ` [PATCH v2 1/3] ASoC: Intel: soc-acpi-cht: Unify device quirks Yauhen Kharuzhy
2026-03-02 15:54   ` Péter Ujfalusi
2026-03-02 22:33     ` Yauhen Kharuzhy
2026-03-03  7:10       ` Péter Ujfalusi
2026-03-01 21:33 ` [PATCH v2 2/3] ASoC: Intel: soc-acpi-cht: Add Lenovo Yoga Book entries Yauhen Kharuzhy
2026-03-02 15:48   ` Péter Ujfalusi
2026-03-01 21:33 ` [PATCH v2 3/3] ASoC: Intel: cht_yogabook: Add driver for Lenovo Yoga Book tablets Yauhen Kharuzhy
2026-03-02 12:03   ` Cezary Rojewski [this message]
2026-03-03  0:12     ` Yauhen Kharuzhy
2026-03-03  9:30       ` Cezary Rojewski
2026-03-03 20:45         ` Yauhen Kharuzhy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7932c58b-b6fa-40c3-8967-7710d84f9667@intel.com \
    --to=cezary.rojewski@intel.com \
    --cc=broonie@kernel.org \
    --cc=hansg@kernel.org \
    --cc=jekhor@gmail.com \
    --cc=kai.vehmanen@linux.intel.com \
    --cc=liam.r.girdwood@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=peter.ujfalusi@linux.intel.com \
    --cc=pierre-louis.bossart@linux.dev \
    --cc=ranjani.sridharan@linux.intel.com \
    --cc=yung-chuan.liao@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox