[Letux-kernel] [Internal RFC 15/16] ASoC: omap3pandora: Rewrite sound card driver as a platform driver

H. Nikolaus Schaller hns at goldelico.com
Wed Sep 7 21:26:14 CEST 2022



> Am 07.09.2022 um 20:01 schrieb H. Nikolaus Schaller <hns at goldelico.com>:
> 
> From: Grond <grond66 at riseup.net>
> 
> This change takes advantage of numerous prior changes and moves a
> significant amount of code into the new PCM1773 codec driver (and removes
> a hardcoded GPIO and regulator in the process). By using this new driver we
> can remove the prior fiction that playback audio goes through the TWL4030.
> 
> Previously, the sound card driver worked by checking (through various means)
> if it was running on the Pandora, and if so, it would create a new platform
> device from nothing to function as the sound card, and rely on some weirdness
> to get registered as an ASoC card driver. This has not been the way to do ASoC
> card drivers for a while now, so instead we register a platform driver which
> matches a device in the Pandora's DT and registers the appropriate ASoC card
> driver when found. This also means that the driver will only load when the DT
> says that we need it, and we can stop manually loading this thing.
> 
> Signed-off-by: Grond <grond66 at riseup.net>
> Signed-off-by: H. Nikolaus Schaller <hns at goldelico.com>
> ---
> sound/soc/ti/omap3pandora.c | 430 +++++++++++++++++++++++++++---------
> 1 file changed, 326 insertions(+), 104 deletions(-)
> 
> diff --git a/sound/soc/ti/omap3pandora.c b/sound/soc/ti/omap3pandora.c
> index 7c35d0b7863e4..f668936265f6b 100644
> --- a/sound/soc/ti/omap3pandora.c
> +++ b/sound/soc/ti/omap3pandora.c
> @@ -11,6 +11,7 @@
> #include <linux/delay.h>
> #include <linux/regulator/consumer.h>
> #include <linux/module.h>
> +#include <linux/of_gpio.h>
> 
> #include <sound/core.h>
> #include <sound/pcm.h>
> @@ -18,83 +19,263 @@
> 
> #include <asm/mach-types.h>
> #include <linux/platform_data/asoc-ti-mcbsp.h>
> +#include <linux/mfd/twl4030-audio.h>
> 
> #include "omap-mcbsp.h"
> 
> -#define OMAP3_PANDORA_DAC_POWER_GPIO	118
> -#define OMAP3_PANDORA_AMP_POWER_GPIO	14
> +#define AMP_GPIO_NAME			 "amp-gpio"
> 
> -#define PREFIX "ASoC omap3pandora: "
> +struct omap3pandora_sound {
> +	int amp_gpio;
> +	struct regulator *amp_regulator;
> 
> -static struct regulator *omap3pandora_dac_reg;
> +	struct snd_pcm_substream *playback_stream;
> +	struct snd_pcm_substream *capture_stream;
> 
> -static int omap3pandora_hw_params(struct snd_pcm_substream *substream,
> +	struct mutex sample_rate_lock; // protects all fields after
> +	unsigned int sample_rate;
> +	int sample_rate_users;
> +};
> +
> +static struct snd_soc_dai_link omap3pandora_dai[];
> +
> +static int omap3pandora_common_hw_params(struct snd_pcm_substream *substream,
> 	struct snd_pcm_hw_params *params)
> {
> 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
> -	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
> 	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
> +	struct device *dev = rtd->dev;
> 	int ret;
> 
> -	/* Set the codec system clock for DAC and ADC */
> -	ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
> -					    SND_SOC_CLOCK_IN);
> -	if (ret < 0) {
> -		pr_err(PREFIX "can't set codec system clock\n");
> -		return ret;
> -	}
> -
> 	/* Set McBSP clock to external */
> 	ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT,
> 				     256 * params_rate(params),
> 				     SND_SOC_CLOCK_IN);
> 	if (ret < 0) {
> -		pr_err(PREFIX "can't set cpu system clock\n");
> +		dev_err(dev, "cannot set McBSP clock to external: %d\n", ret);
> 		return ret;
> 	}
> 
> 	ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8);
> 	if (ret < 0) {
> -		pr_err(PREFIX "can't set SRG clock divider\n");
> +		dev_err(dev, "cannot set McBSP clock divider: %d\n", ret);
> 		return ret;
> 	}
> 
> 	return 0;
> }
> 
> -static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w,
> -	struct snd_kcontrol *k, int event)
> +static inline int constrain_sample_rate(struct snd_pcm_substream *substream,
> +	unsigned int sample_rate)
> +{
> +	return snd_pcm_hw_constraint_single(substream->runtime,
> +					    SNDRV_PCM_HW_PARAM_RATE,
> +					    sample_rate);
> +}
> +
> +static inline void relax_sample_rate(struct snd_pcm_substream *substream)
> +{
> +	const struct snd_interval default_sample_rate_range = {
> +		.min = substream->runtime->hw.rate_min,
> +		.max = substream->runtime->hw.rate_max,
> +		.openmin = 1,
> +		.openmax = 1,
> +	};
> +
> +	*constrs_interval(&substream->runtime->hw_constraints,
> +			  SNDRV_PCM_HW_PARAM_RATE) =
> +		default_sample_rate_range;
> +}
> +
> +static void release_sample_rate(struct omap3pandora_sound *ctx,
> +	struct snd_pcm_substream *substream)
> +{
> +	mutex_lock(&ctx->sample_rate_lock);
> +
> +	if (ctx->sample_rate_users > 0)
> +		--ctx->sample_rate_users;
> +	if (ctx->sample_rate_users == 0)
> +		ctx->sample_rate = 0;
> +
> +	relax_sample_rate(substream);
> +
> +	mutex_unlock(&ctx->sample_rate_lock);
> +}
> +
> +static int grab_sample_rate(struct omap3pandora_sound *ctx,
> +	struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> +{
> +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
> +	struct device *dev = rtd->dev;
> +	int ret;
> +
> +	ret = mutex_lock_interruptible(&ctx->sample_rate_lock);
> +	if (ret)
> +		return ret;
> +
> +	if (++ctx->sample_rate_users == 1)
> +		ctx->sample_rate = params_rate(params);
> +
> +	mutex_unlock(&ctx->sample_rate_lock);
> +
> +	ret = constrain_sample_rate(substream, ctx->sample_rate);
> +	if (ret < 0)
> +		goto err;
> +
> +	ret = constrain_sample_rate(substream, params_rate(params));
> +	if (ret < 0) {
> +		dev_dbg(dev, "attempted to use sample rate different from other active substream; on the Pandora, this is impossible, as capture and playback use the same sample clock");
> +		goto err;
> +	}
> +
> +	return 0;
> +
> +err:
> +	release_sample_rate(ctx, substream);
> +
> +	return ret;
> +}
> +
> +static int omap3pandora_playback_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> {
> +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
> +	struct snd_soc_card *card = rtd->card;
> +	struct omap3pandora_sound *ctx = snd_soc_card_get_drvdata(card);
> +	struct device *dev = rtd->dev;
> +	struct snd_soc_pcm_runtime *tmp_rtd;
> +	struct snd_soc_pcm_runtime *twl4030_rtd = NULL;
> 	int ret;
> 
> 	/*
> -	 * The PCM1773 DAC datasheet requires 1ms delay between switching
> -	 * VCC power on/off and /PD pin high/low
> +	 * We need to set the APLL clock rate on the TWL4030 because it feeds
> +	 * both the DAI of the PCM1773. So find the appropriate RTD and call
> +	 * the TWL's .set_sysclk callback through there. Ugly, but it must be
> +	 * done.
> 	 */
> -	if (SND_SOC_DAPM_EVENT_ON(event)) {
> -		ret = regulator_enable(omap3pandora_dac_reg);
> -		if (ret) {
> -			dev_err(w->dapm->dev, "Failed to power DAC: %d\n", ret);
> -			return ret;
> +	for_each_card_rtds(card, tmp_rtd)
> +		if (tmp_rtd->dai_link == &omap3pandora_dai[1]) {
> +			twl4030_rtd = tmp_rtd;
> +			break;
> 		}
> -		mdelay(1);
> -		gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
> -	} else {
> -		gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
> -		mdelay(1);
> -		regulator_disable(omap3pandora_dac_reg);
> +	if (!twl4030_rtd) {
> +		dev_err(dev, "cannot find TWL4030 runtime data to set APLL rate\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(twl4030_rtd, 0),
> +				     TWL4030_CLOCK_APLL, params_rate(params),
> +				     SND_SOC_CLOCK_OUT);
> +	if (ret) {
> +		dev_err(dev, "cannot set TWL4030 APLL rate via set_sysclk interface: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	if (!ctx->playback_stream) {
> +		ctx->playback_stream = substream;
> +		ret = grab_sample_rate(ctx, substream, params);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return omap3pandora_common_hw_params(substream, params);
> +}
> +
> +static int omap3pandora_capture_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params)
> +{
> +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
> +	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
> +	struct device *dev = rtd->dev;
> +	struct snd_soc_card *card = rtd->card;
> +	struct omap3pandora_sound *ctx = snd_soc_card_get_drvdata(card);
> +	int ret;
> +
> +	/* Set the codec system clock for DAC and ADC */
> +	ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
> +				     SND_SOC_CLOCK_IN);
> +	if (ret < 0) {
> +		dev_err(dev, "cannot set TWL4030 system clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (!ctx->capture_stream) {
> +		ctx->capture_stream = substream;
> +		ret = grab_sample_rate(ctx, substream, params);
> +		if (ret)
> +			return ret;
> 	}
> 
> +	return omap3pandora_common_hw_params(substream, params);
> +}
> +
> +static int omap3pandora_playback_hw_free(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
> +	struct snd_soc_card *card = rtd->card;
> +	struct omap3pandora_sound *ctx = snd_soc_card_get_drvdata(card);
> +
> +	if (ctx->playback_stream)
> +		release_sample_rate(ctx, substream);
> +	ctx->playback_stream = NULL;
> +
> +	return 0;
> +}
> +
> +static int omap3pandora_common_startup(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
> +	struct snd_soc_card *card = rtd->card;
> +	struct omap3pandora_sound *ctx = snd_soc_card_get_drvdata(card);
> +	unsigned int sample_rate = ctx->sample_rate;
> +
> +	if (!sample_rate)
> +		return 0;
> +
> +	return constrain_sample_rate(substream, sample_rate);
> +}
> +
> +static int omap3pandora_capture_hw_free(struct snd_pcm_substream *substream)
> +{
> +	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
> +	struct snd_soc_card *card = rtd->card;
> +	struct omap3pandora_sound *ctx = snd_soc_card_get_drvdata(card);
> +
> +	if (ctx->capture_stream)
> +		release_sample_rate(ctx, substream);
> +	ctx->capture_stream = NULL;
> +
> 	return 0;
> }
> 
> static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
> 	struct snd_kcontrol *k, int event)
> {
> -	if (SND_SOC_DAPM_EVENT_ON(event))
> -		gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
> -	else
> -		gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
> +	struct snd_soc_card *card = w->dapm->card;
> +	struct device *dev = card->dev;
> +	struct omap3pandora_sound *ctx =
> +		snd_soc_card_get_drvdata(card);
> +	int ret;
> +
> +	if (SND_SOC_DAPM_EVENT_ON(event)) {
> +		ret = regulator_enable(ctx->amp_regulator);
> +		if (ret) {
> +			dev_err(dev, "error enabling amplifier regulator: %d\n", ret);
> +			return ret;
> +		}
> +
> +		gpio_set_value(ctx->amp_gpio, 1);
> +	} else {
> +		gpio_set_value(ctx->amp_gpio, 0);
> +
> +		ret = regulator_disable(ctx->amp_regulator);
> +		if (ret) {
> +			dev_err(dev, "error disabling amplifier regulator: %d\n", ret);
> +			return ret;
> +		}
> +	}
> 
> 	return 0;
> }
> @@ -108,9 +289,6 @@ static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
>  *  |P| <--- TWL4030 <--------- Line In and MICs
>  */
> static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {
> -	SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,
> -			   0, 0, omap3pandora_dac_event,
> -			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
> 	SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
> 			   0, 0, NULL, 0, omap3pandora_hp_event,
> 			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
> @@ -123,9 +301,15 @@ static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = {
> };
> 
> static const struct snd_soc_dapm_route omap3pandora_map[] = {
> -	{"PCM DAC", NULL, "APLL Enable"},
> -	{"Headphone Amplifier", NULL, "PCM DAC"},
> -	{"Line Out", NULL, "PCM DAC"},
> +	/*
> +	 * The APLL signal produced by the TWL4030 is routed to the PCM1773 (in
> +	 * addition to supplying the clock for the McBSPs) so we need to make
> +	 * sure that APLL gets enabled before the PCM1773 begins operation.
> +	 */
> +	{"PCM1773 DAC", NULL, "APLL Enable"},
> +
> +	{"Headphone Amplifier", NULL, "PCM1773 DAC"},
> +	{"Line Out", NULL, "PCM1773 DAC"},
> 	{"Headphone Jack", NULL, "Headphone Amplifier"},
> 
> 	{"AUXL", NULL, "Line In"},
> @@ -170,15 +354,23 @@ static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
> 	return 0;
> }
> 
> -static const struct snd_soc_ops omap3pandora_ops = {
> -	.hw_params = omap3pandora_hw_params,
> +static const struct snd_soc_ops omap3pandora_playback_ops = {
> +	.startup = omap3pandora_common_startup,
> +	.hw_params = omap3pandora_playback_hw_params,
> +	.hw_free = omap3pandora_playback_hw_free,
> +};
> +
> +static const struct snd_soc_ops omap3pandora_capture_ops = {
> +	.startup = omap3pandora_common_startup,
> +	.hw_params = omap3pandora_capture_hw_params,
> +	.hw_free = omap3pandora_capture_hw_free,
> };
> 
> /* Digital audio interface glue - connects codec <--> CPU */
> #if IS_BUILTIN(CONFIG_SND_SOC_OMAP3_PANDORA)
> SND_SOC_DAILINK_DEFS(out,
> 	DAILINK_COMP_ARRAY(COMP_CPU("omap-mcbsp.2")),
> -	DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-hifi")),
> +	DAILINK_COMP_ARRAY(COMP_CODEC("pcm1773-codec", "pcm1773-hifi")),
> 	DAILINK_COMP_ARRAY(COMP_PLATFORM("omap-mcbsp.2")));
> 
> SND_SOC_DAILINK_DEFS(in,
> @@ -188,7 +380,7 @@ SND_SOC_DAILINK_DEFS(in,
> #else /* IS_BUILTIN(CONFIG_SND_SOC_OMAP3_PANDORA) */
> SND_SOC_DAILINK_DEFS(out,
> 	DAILINK_COMP_ARRAY(COMP_CPU("49022000.mcbsp")),
> -	DAILINK_COMP_ARRAY(COMP_CODEC("twl4030-codec", "twl4030-hifi")),
> +	DAILINK_COMP_ARRAY(COMP_CODEC("pcm1773-codec", "pcm1773-hifi")),
> 	DAILINK_COMP_ARRAY(COMP_PLATFORM("49022000.mcbsp")));
> 
> SND_SOC_DAILINK_DEFS(in,
> @@ -203,16 +395,18 @@ static struct snd_soc_dai_link omap3pandora_dai[] = {
> 		.stream_name = "HiFi Out",
> 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
> 			   SND_SOC_DAIFMT_CBS_CFS,
> -		.ops = &omap3pandora_ops,
> +		.ops = &omap3pandora_playback_ops,
> 		.init = omap3pandora_out_init,
> +		.playback_only = 1,
> 		SND_SOC_DAILINK_REG(out),
> 	}, {
> 		.name = "TWL4030",
> 		.stream_name = "Line/Mic In",
> 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
> 			   SND_SOC_DAIFMT_CBS_CFS,
> -		.ops = &omap3pandora_ops,
> +		.ops = &omap3pandora_capture_ops,
> 		.init = omap3pandora_in_init,
> +		.capture_only = 1,
> 		SND_SOC_DAILINK_REG(in),
> 	}
> };
> @@ -228,92 +422,120 @@ static struct snd_soc_card snd_soc_card_omap3pandora = {
> 	.num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets),
> 	.dapm_routes = omap3pandora_map,
> 	.num_dapm_routes = ARRAY_SIZE(omap3pandora_map),
> +	.owner = THIS_MODULE,
> };
> 
> -static struct platform_device *omap3pandora_snd_device;
> -
> -static int __init omap3pandora_soc_init(void)
> +static int omap3pandora_probe(struct platform_device *pdev)
> {
> 	int ret;
> +	struct device *dev = &pdev->dev;
> +	struct device_node *of_node = dev->of_node;
> +	struct snd_soc_card *card = &snd_soc_card_omap3pandora;
> +	struct omap3pandora_sound *ctx;
> +	int amp_gpio;
> +	struct regulator *reg;
> +
> +	if (!of_node) {
> +		dev_err(dev, "No DT data present; cannot determine amplifier powerup GPIO\n");
> +		return -EINVAL;
> +	}
> 
> -	if (!machine_is_omap3_pandora() &&
> -	    !of_machine_is_compatible("openpandora,omap3-pandora-600mhz") &&
> -	    !of_machine_is_compatible("openpandora,omap3-pandora-1ghz"))
> -		return -ENODEV;
> -
> -	pr_info("OMAP3 Pandora SoC init\n");
> -
> -	ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");
> -	if (ret) {
> -		pr_err(PREFIX "Failed to get DAC power GPIO\n");
> -		return ret;
> +	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx) {
> +		dev_dbg(dev, "cannot allocate space for runtime data\n");
> +		return -ENOMEM;
> 	}
> +	ctx->amp_gpio = -1;
> +	mutex_init(&ctx->sample_rate_lock);
> 
> -	ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
> -	if (ret) {
> -		pr_err(PREFIX "Failed to set DAC power GPIO direction\n");
> -		goto fail0;
> +	ret = of_get_named_gpio(of_node, AMP_GPIO_NAME, 0);

I think we whould rewrite this to devm and gpiod because it removes a lot of
the error paths.

> +	if (ret < 0) {
> +		dev_err(dev, "cannot find GPIO named " AMP_GPIO_NAME " in device tree\n");
> +		goto fail;
> 	}
> +	amp_gpio = ret;
> 
> -	ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power");
> +	ret = gpio_request(amp_gpio, "amplifier power");

handled by gpiod.

> 	if (ret) {
> -		pr_err(PREFIX "Failed to get amp power GPIO\n");
> -		goto fail0;
> +		dev_err(dev, "Failed to request amplifier powerup GPIO %d: %d\n",
> +			ctx->amp_gpio, ret);
> +		goto fail;
> 	}
> +	ctx->amp_gpio = amp_gpio;
> 
> -	ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
> +	ret = gpio_direction_output(ctx->amp_gpio, 0);

handled by gpiod.

> 	if (ret) {
> -		pr_err(PREFIX "Failed to set amp power GPIO direction\n");
> -		goto fail1;
> +		dev_err(dev, "Failed to set amplifier powerup GPIO direction: %d\n",
> +			ret);
> +		goto fail;
> 	}
> 
> -	omap3pandora_snd_device = platform_device_alloc("soc-audio", -1);
> -	if (omap3pandora_snd_device == NULL) {
> -		pr_err(PREFIX "Platform device allocation failed\n");
> -		ret = -ENOMEM;
> -		goto fail1;
> +	reg = regulator_get(dev, "opnd,amp");

devm regulator

> +	if (IS_ERR(reg)) {
> +		ret = PTR_ERR(reg);
> +		dev_err(dev, "Failed to request regulator for amplifier power: %d\n", ret);
> +		goto fail;
> 	}
> +	ctx->amp_regulator = reg;
> 
> -	platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora);
> -
> -	ret = platform_device_add(omap3pandora_snd_device);
> +	card->dev = dev;
> +	ret = snd_soc_register_card(card);
> 	if (ret) {
> -		pr_err(PREFIX "Unable to add platform device\n");
> -		goto fail2;
> +		dev_err(dev, "Failed to register sound card: %d\n", ret);
> +		goto fail;
> 	}
> 
> -	omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");
> -	if (IS_ERR(omap3pandora_dac_reg)) {
> -		pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",
> -			dev_name(&omap3pandora_snd_device->dev),
> -			PTR_ERR(omap3pandora_dac_reg));
> -		ret = PTR_ERR(omap3pandora_dac_reg);
> -		goto fail3;
> -	}
> +	snd_soc_card_set_drvdata(card, ctx);
> 
> 	return 0;
> 
> -fail3:
> -	platform_device_del(omap3pandora_snd_device);
> -fail2:
> -	platform_device_put(omap3pandora_snd_device);
> -fail1:
> -	gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
> -fail0:
> -	gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
> +fail:
> +	if (ctx) {
> +		if (ctx->amp_gpio >= 0)
> +			gpio_free(ctx->amp_gpio);
> +		if (ctx->amp_regulator)
> +			regulator_put(ctx->amp_regulator);

all not needed if we use devm.

> +	}
> +
> 	return ret;
> }
> -module_init(omap3pandora_soc_init);
> 
> -static void __exit omap3pandora_soc_exit(void)
> +static int omap3pandora_remove(struct platform_device *pdev)
> {
> -	regulator_put(omap3pandora_dac_reg);
> -	platform_device_unregister(omap3pandora_snd_device);
> -	gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
> -	gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
> +	struct snd_soc_card *card = platform_get_drvdata(pdev);
> +	struct omap3pandora_sound *ctx = snd_soc_card_get_drvdata(card);
> +
> +	snd_soc_unregister_card(card);
> +
> +	if (ctx->amp_gpio >= 0)
> +		gpio_free(ctx->amp_gpio);
> +
> +	if (ctx->amp_regulator)
> +		regulator_put(ctx->amp_regulator);

not needed with devm.

> +
> +	mutex_destroy(&ctx->sample_rate_lock);
> +
> +	return 0;
> }
> -module_exit(omap3pandora_soc_exit);
> +
> +static const struct of_device_id omap3pandora_of_match[] = {
> +	{ .compatible = "openpandora,omap3pandora-sound", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, omap3pandora_of_match);
> +
> +static struct platform_driver omap3pandora_driver = {
> +	.driver = {
> +		.name = "omap3pandora-sound",
> +		.of_match_table = omap3pandora_of_match,
> +	},
> +	.probe = omap3pandora_probe,
> +	.remove = omap3pandora_remove,
> +};
> +
> +module_platform_driver(omap3pandora_driver);
> 
> MODULE_AUTHOR("Grazvydas Ignotas <notasas at gmail.com>");
> MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora");
> MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:omap3pandora-sound");
> -- 
> 2.33.0
> 



More information about the Letux-kernel mailing list