OSDN Git Service

ASoC: max98357a: avoid speaker pop when playback startup
authorMac Chiang <mac.chiang@intel.com>
Wed, 19 Jun 2019 10:18:33 +0000 (18:18 +0800)
committerMark Brown <broonie@kernel.org>
Tue, 2 Jul 2019 14:53:38 +0000 (15:53 +0100)
Loud speaker pop happens during playback even when in slience
playback. Specify Max98357a amp delay times to make sure
clocks are always earlier than sdmode on.

Signed-off-by: Mac Chiang <mac.chiang@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Documentation/devicetree/bindings/sound/max98357a.txt
sound/soc/codecs/max98357a.c

index 28645a2..4bce14c 100644 (file)
@@ -9,6 +9,10 @@ Optional properties:
 - sdmode-gpios : GPIO specifier for the chip's SD_MODE pin.
         If this option is not specified then driver does not manage
         the pin state (e.g. chip is always on).
+- sdmode-delay : specify delay time for SD_MODE pin.
+        If this option is specified, which means it's required i2s clocks
+        ready before SD_MODE is unmuted in order to avoid the speaker pop noise.
+        It's observed that 5ms is sufficient.
 
 Example:
 
index 6f724c9..6f0e28f 100644 (file)
 #include <sound/soc-dai.h>
 #include <sound/soc-dapm.h>
 
+struct max98357a_priv {
+       struct delayed_work enable_sdmode_work;
+       struct gpio_desc *sdmode;
+       unsigned int sdmode_delay;
+};
+
+static void max98357a_enable_sdmode_work(struct work_struct *work)
+{
+       struct max98357a_priv *max98357a =
+       container_of(work, struct max98357a_priv,
+                       enable_sdmode_work.work);
+
+       gpiod_set_value(max98357a->sdmode, 1);
+}
+
 static int max98357a_daiops_trigger(struct snd_pcm_substream *substream,
                int cmd, struct snd_soc_dai *dai)
 {
-       struct gpio_desc *sdmode = snd_soc_dai_get_drvdata(dai);
+       struct max98357a_priv *max98357a = snd_soc_dai_get_drvdata(dai);
 
-       if (!sdmode)
+       if (!max98357a->sdmode)
                return 0;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               gpiod_set_value(sdmode, 1);
+               queue_delayed_work(system_power_efficient_wq,
+                               &max98357a->enable_sdmode_work,
+                               msecs_to_jiffies(max98357a->sdmode_delay));
                break;
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               gpiod_set_value(sdmode, 0);
+               cancel_delayed_work_sync(&max98357a->enable_sdmode_work);
+               gpiod_set_value(max98357a->sdmode, 0);
                break;
        }
 
@@ -90,14 +108,33 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
 
 static int max98357a_platform_probe(struct platform_device *pdev)
 {
-       struct gpio_desc *sdmode;
+       struct max98357a_priv *max98357a;
+       int ret;
 
-       sdmode = devm_gpiod_get_optional(&pdev->dev,
+       max98357a = devm_kzalloc(&pdev->dev, sizeof(*max98357a), GFP_KERNEL);
+
+       if (!max98357a)
+               return -ENOMEM;
+
+       max98357a->sdmode = devm_gpiod_get_optional(&pdev->dev,
                                "sdmode", GPIOD_OUT_LOW);
-       if (IS_ERR(sdmode))
-               return PTR_ERR(sdmode);
 
-       dev_set_drvdata(&pdev->dev, sdmode);
+       if (IS_ERR(max98357a->sdmode))
+               return PTR_ERR(max98357a->sdmode);
+
+       ret = device_property_read_u32(&pdev->dev, "sdmode-delay",
+                                       &max98357a->sdmode_delay);
+
+       if (ret) {
+               max98357a->sdmode_delay = 0;
+               dev_dbg(&pdev->dev,
+                       "no optional property 'sdmode-delay' found, default: no delay\n");
+       }
+
+       dev_set_drvdata(&pdev->dev, max98357a);
+
+       INIT_DELAYED_WORK(&max98357a->enable_sdmode_work,
+                               max98357a_enable_sdmode_work);
 
        return devm_snd_soc_register_component(&pdev->dev,
                        &max98357a_component_driver,