[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