Merge tag 'sound-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[sfrench/cifs-2.6.git] / sound / soc / codecs / cs42l42.c
index 811b7b1c9732e13ee365ca1ff223b4e0c28221b2..bf982e145e945f4774d5c306522426f381433c14 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -461,64 +462,78 @@ static const struct snd_kcontrol_new cs42l42_snd_controls[] = {
                                0x3f, 1, mixer_tlv)
 };
 
-static int cs42l42_hpdrv_evt(struct snd_soc_dapm_widget *w,
-                               struct snd_kcontrol *kcontrol, int event)
-{
-       struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
-
-       if (event & SND_SOC_DAPM_POST_PMU) {
-               /* Enable the channels */
-               snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_EN,
-                               CS42L42_ASP_RX0_CH_EN_MASK,
-                               (CS42L42_ASP_RX0_CH1_EN |
-                               CS42L42_ASP_RX0_CH2_EN) <<
-                               CS42L42_ASP_RX0_CH_EN_SHIFT);
-
-               /* Power up */
-               snd_soc_component_update_bits(component, CS42L42_PWR_CTL1,
-                       CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
-                               CS42L42_HP_PDN_MASK, 0);
-       } else if (event & SND_SOC_DAPM_PRE_PMD) {
-               /* Disable the channels */
-               snd_soc_component_update_bits(component, CS42L42_ASP_RX_DAI0_EN,
-                               CS42L42_ASP_RX0_CH_EN_MASK, 0);
-
-               /* Power down */
-               snd_soc_component_update_bits(component, CS42L42_PWR_CTL1,
-                       CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
-                               CS42L42_HP_PDN_MASK,
-                       CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
-                               CS42L42_HP_PDN_MASK);
-       } else {
-               dev_err(component->dev, "Invalid event 0x%x\n", event);
-       }
-       return 0;
-}
-
 static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
+       /* Playback Path */
        SND_SOC_DAPM_OUTPUT("HP"),
-       SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS42L42_ASP_CLK_CFG,
-                                       CS42L42_ASP_SCLK_EN_SHIFT, false),
-       SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0,
-                                       0, NULL, 0, cs42l42_hpdrv_evt,
-                                       SND_SOC_DAPM_POST_PMU |
-                                       SND_SOC_DAPM_PRE_PMD)
+       SND_SOC_DAPM_DAC("DAC", NULL, CS42L42_PWR_CTL1, CS42L42_HP_PDN_SHIFT, 1),
+       SND_SOC_DAPM_MIXER("MIXER", CS42L42_PWR_CTL1, CS42L42_MIXER_PDN_SHIFT, 1, NULL, 0),
+       SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, CS42L42_ASP_RX_DAI0_EN, CS42L42_ASP_RX0_CH1_SHIFT, 0),
+       SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, CS42L42_ASP_RX_DAI0_EN, CS42L42_ASP_RX0_CH2_SHIFT, 0),
+
+       /* Playback Requirements */
+       SND_SOC_DAPM_SUPPLY("ASP DAI0", CS42L42_PWR_CTL1, CS42L42_ASP_DAI_PDN_SHIFT, 1, NULL, 0),
+
+       /* Capture Path */
+       SND_SOC_DAPM_INPUT("HS"),
+       SND_SOC_DAPM_ADC("ADC", NULL, CS42L42_PWR_CTL1, CS42L42_ADC_PDN_SHIFT, 1),
+       SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH1_SHIFT, 0),
+       SND_SOC_DAPM_AIF_OUT("SDOUT2", NULL, 1, CS42L42_ASP_TX_CH_EN, CS42L42_ASP_TX0_CH2_SHIFT, 0),
+
+       /* Capture Requirements */
+       SND_SOC_DAPM_SUPPLY("ASP DAO0", CS42L42_PWR_CTL1, CS42L42_ASP_DAO_PDN_SHIFT, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ASP TX EN", CS42L42_ASP_TX_SZ_EN, CS42L42_ASP_TX_EN_SHIFT, 0, NULL, 0),
+
+       /* Playback/Capture Requirements */
+       SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0),
 };
 
 static const struct snd_soc_dapm_route cs42l42_audio_map[] = {
-       {"SDIN", NULL, "Playback"},
-       {"HPDRV", NULL, "SDIN"},
-       {"HP", NULL, "HPDRV"}
+       /* Playback Path */
+       {"HP", NULL, "DAC"},
+       {"DAC", NULL, "MIXER"},
+       {"MIXER", NULL, "SDIN1"},
+       {"MIXER", NULL, "SDIN2"},
+       {"SDIN1", NULL, "Playback"},
+       {"SDIN2", NULL, "Playback"},
+
+       /* Playback Requirements */
+       {"SDIN1", NULL, "ASP DAI0"},
+       {"SDIN2", NULL, "ASP DAI0"},
+       {"SDIN1", NULL, "SCLK"},
+       {"SDIN2", NULL, "SCLK"},
+
+       /* Capture Path */
+       {"ADC", NULL, "HS"},
+       { "SDOUT1", NULL, "ADC" },
+       { "SDOUT2", NULL, "ADC" },
+       { "Capture", NULL, "SDOUT1" },
+       { "Capture", NULL, "SDOUT2" },
+
+       /* Capture Requirements */
+       { "SDOUT1", NULL, "ASP DAO0" },
+       { "SDOUT2", NULL, "ASP DAO0" },
+       { "SDOUT1", NULL, "SCLK" },
+       { "SDOUT2", NULL, "SCLK" },
+       { "SDOUT1", NULL, "ASP TX EN" },
+       { "SDOUT2", NULL, "ASP TX EN" },
 };
 
 static int cs42l42_component_probe(struct snd_soc_component *component)
 {
        struct cs42l42_private *cs42l42 =
                (struct cs42l42_private *)snd_soc_component_get_drvdata(component);
+       struct snd_soc_card *crd = component->card;
+       int ret = 0;
 
        cs42l42->component = component;
 
-       return 0;
+       ret = snd_soc_card_jack_new(crd, "CS42L42 Headset", SND_JACK_HEADSET | SND_JACK_BTN_0 |
+                                   SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3,
+                                   &cs42l42->jack, NULL, 0);
+       if (ret < 0)
+               dev_err(component->dev, "Cannot create CS42L42 Headset: %d\n", ret);
+
+       return ret;
 }
 
 static const struct snd_soc_component_driver soc_component_dev_cs42l42 = {
@@ -534,6 +549,24 @@ static const struct snd_soc_component_driver soc_component_dev_cs42l42 = {
        .non_legacy_dai_naming  = 1,
 };
 
+/* Switch to SCLK. Atomic delay after the write to allow the switch to complete. */
+static const struct reg_sequence cs42l42_to_sclk_seq[] = {
+       {
+               .reg = CS42L42_OSC_SWITCH,
+               .def = CS42L42_SCLK_PRESENT_MASK,
+               .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US,
+       },
+};
+
+/* Switch to OSC. Atomic delay after the write to allow the switch to complete. */
+static const struct reg_sequence cs42l42_to_osc_seq[] = {
+       {
+               .reg = CS42L42_OSC_SWITCH,
+               .def = 0,
+               .delay_us = CS42L42_CLOCK_SWITCH_DELAY_US,
+       },
+};
+
 struct cs42l42_pll_params {
        u32 sclk;
        u8 mclk_div;
@@ -573,10 +606,16 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
 {
        struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
        int i;
+       u32 clk;
        u32 fsync;
 
+       if (!cs42l42->sclk)
+               clk = cs42l42->bclk;
+       else
+               clk = cs42l42->sclk;
+
        for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
-               if (pll_ratio_table[i].sclk == cs42l42->sclk) {
+               if (pll_ratio_table[i].sclk == clk) {
                        /* Configure the internal sample rate */
                        snd_soc_component_update_bits(component, CS42L42_MCLK_CTL,
                                        CS42L42_INTERNAL_FS_MASK,
@@ -596,12 +635,12 @@ static int cs42l42_pll_config(struct snd_soc_component *component)
                                        (pll_ratio_table[i].mclk_div <<
                                        CS42L42_MCLKDIV_SHIFT));
                        /* Set up the LRCLK */
-                       fsync = cs42l42->sclk / cs42l42->srate;
-                       if (((fsync * cs42l42->srate) != cs42l42->sclk)
+                       fsync = clk / cs42l42->srate;
+                       if (((fsync * cs42l42->srate) != clk)
                                || ((fsync % 2) != 0)) {
                                dev_err(component->dev,
                                        "Unsupported sclk %d/sample rate %d\n",
-                                       cs42l42->sclk,
+                                       clk,
                                        cs42l42->srate);
                                return -EINVAL;
                        }
@@ -768,12 +807,25 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
 {
        struct snd_soc_component *component = dai->component;
        struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+       unsigned int channels = params_channels(params);
        unsigned int width = (params_width(params) / 8) - 1;
        unsigned int val = 0;
 
        cs42l42->srate = params_rate(params);
+       cs42l42->bclk = snd_soc_params_to_bclk(params);
 
        switch(substream->stream) {
+       case SNDRV_PCM_STREAM_CAPTURE:
+               if (channels == 2) {
+                       val |= CS42L42_ASP_TX_CH2_AP_MASK;
+                       val |= width << CS42L42_ASP_TX_CH2_RES_SHIFT;
+               }
+               val |= width << CS42L42_ASP_TX_CH1_RES_SHIFT;
+
+               snd_soc_component_update_bits(component, CS42L42_ASP_TX_CH_AP_RES,
+                               CS42L42_ASP_TX_CH1_AP_MASK | CS42L42_ASP_TX_CH2_AP_MASK |
+                               CS42L42_ASP_TX_CH2_RES_MASK | CS42L42_ASP_TX_CH1_RES_MASK, val);
+               break;
        case SNDRV_PCM_STREAM_PLAYBACK:
                val |= width << CS42L42_ASP_RX_CH_RES_SHIFT;
                /* channel 1 on low LRCLK */
@@ -804,52 +856,73 @@ static int cs42l42_set_sysclk(struct snd_soc_dai *dai,
        return 0;
 }
 
-static int cs42l42_mute(struct snd_soc_dai *dai, int mute, int direction)
+static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
 {
        struct snd_soc_component *component = dai->component;
+       struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
        unsigned int regval;
        u8 fullScaleVol;
+       int ret;
 
        if (mute) {
-               /* Mark SCLK as not present to turn on the internal
-                * oscillator.
-                */
-               snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH,
-                                               CS42L42_SCLK_PRESENT_MASK, 0);
-
-               snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
-                               CS42L42_PLL_START_MASK,
-                               0 << CS42L42_PLL_START_SHIFT);
-
                /* Mute the headphone */
-               snd_soc_component_update_bits(component, CS42L42_HP_CTL,
-                               CS42L42_HP_ANA_AMUTE_MASK |
-                               CS42L42_HP_ANA_BMUTE_MASK,
-                               CS42L42_HP_ANA_AMUTE_MASK |
-                               CS42L42_HP_ANA_BMUTE_MASK);
-       } else {
-               snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
-                               CS42L42_PLL_START_MASK,
-                               1 << CS42L42_PLL_START_SHIFT);
-               /* Read the headphone load */
-               regval = snd_soc_component_read(component, CS42L42_LOAD_DET_RCSTAT);
-               if (((regval & CS42L42_RLA_STAT_MASK) >>
-                       CS42L42_RLA_STAT_SHIFT) == CS42L42_RLA_STAT_15_OHM) {
-                       fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK;
-               } else {
-                       fullScaleVol = 0;
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       snd_soc_component_update_bits(component, CS42L42_HP_CTL,
+                                                     CS42L42_HP_ANA_AMUTE_MASK |
+                                                     CS42L42_HP_ANA_BMUTE_MASK,
+                                                     CS42L42_HP_ANA_AMUTE_MASK |
+                                                     CS42L42_HP_ANA_BMUTE_MASK);
+
+               cs42l42->stream_use &= ~(1 << stream);
+               if(!cs42l42->stream_use) {
+                       /*
+                        * Switch to the internal oscillator.
+                        * SCLK must remain running until after this clock switch.
+                        * Without a source of clock the I2C bus doesn't work.
+                        */
+                       regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_osc_seq,
+                                              ARRAY_SIZE(cs42l42_to_osc_seq));
+                       snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
+                                                     CS42L42_PLL_START_MASK, 0);
                }
+       } else {
+               if (!cs42l42->stream_use) {
+                       /* SCLK must be running before codec unmute */
+                       if ((cs42l42->bclk < 11289600) && (cs42l42->sclk < 11289600)) {
+                               snd_soc_component_update_bits(component, CS42L42_PLL_CTL1,
+                                                             CS42L42_PLL_START_MASK, 1);
+                               ret = regmap_read_poll_timeout(cs42l42->regmap,
+                                                              CS42L42_PLL_LOCK_STATUS,
+                                                              regval,
+                                                              (regval & 1),
+                                                              CS42L42_PLL_LOCK_POLL_US,
+                                                              CS42L42_PLL_LOCK_TIMEOUT_US);
+                               if (ret < 0)
+                                       dev_warn(component->dev, "PLL failed to lock: %d\n", ret);
+                       }
 
-               /* Un-mute the headphone, set the full scale volume flag */
-               snd_soc_component_update_bits(component, CS42L42_HP_CTL,
-                               CS42L42_HP_ANA_AMUTE_MASK |
-                               CS42L42_HP_ANA_BMUTE_MASK |
-                               CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol);
+                       /* Mark SCLK as present, turn off internal oscillator */
+                       regmap_multi_reg_write(cs42l42->regmap, cs42l42_to_sclk_seq,
+                                              ARRAY_SIZE(cs42l42_to_sclk_seq));
+               }
+               cs42l42->stream_use |= 1 << stream;
+
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       /* Read the headphone load */
+                       regval = snd_soc_component_read(component, CS42L42_LOAD_DET_RCSTAT);
+                       if (((regval & CS42L42_RLA_STAT_MASK) >> CS42L42_RLA_STAT_SHIFT) ==
+                           CS42L42_RLA_STAT_15_OHM) {
+                               fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK;
+                       } else {
+                               fullScaleVol = 0;
+                       }
 
-               /* Mark SCLK as present, turn off internal oscillator */
-               snd_soc_component_update_bits(component, CS42L42_OSC_SWITCH,
-                               CS42L42_SCLK_PRESENT_MASK,
-                               CS42L42_SCLK_PRESENT_MASK);
+                       /* Un-mute the headphone, set the full scale volume flag */
+                       snd_soc_component_update_bits(component, CS42L42_HP_CTL,
+                                                     CS42L42_HP_ANA_AMUTE_MASK |
+                                                     CS42L42_HP_ANA_BMUTE_MASK |
+                                                     CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol);
+               }
        }
 
        return 0;
@@ -864,8 +937,7 @@ static const struct snd_soc_dai_ops cs42l42_ops = {
        .hw_params      = cs42l42_pcm_hw_params,
        .set_fmt        = cs42l42_set_dai_fmt,
        .set_sysclk     = cs42l42_set_sysclk,
-       .mute_stream    = cs42l42_mute,
-       .no_capture_mute = 1,
+       .mute_stream    = cs42l42_mute_stream,
 };
 
 static struct snd_soc_dai_driver cs42l42_dai = {
@@ -884,6 +956,8 @@ static struct snd_soc_dai_driver cs42l42_dai = {
                        .rates = SNDRV_PCM_RATE_8000_192000,
                        .formats = CS42L42_FORMATS,
                },
+               .symmetric_rate = 1,
+               .symmetric_sample_bits = 1,
                .ops = &cs42l42_ops,
 };
 
@@ -1169,7 +1243,7 @@ static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
                                (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
 }
 
-static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
+static int cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
 {
        int bias_level;
        unsigned int detect_status;
@@ -1212,17 +1286,24 @@ static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
 
        switch (bias_level) {
        case 1: /* Function C button press */
+               bias_level = SND_JACK_BTN_2;
                dev_dbg(cs42l42->component->dev, "Function C button press\n");
                break;
        case 2: /* Function B button press */
+               bias_level = SND_JACK_BTN_1;
                dev_dbg(cs42l42->component->dev, "Function B button press\n");
                break;
        case 3: /* Function D button press */
+               bias_level = SND_JACK_BTN_3;
                dev_dbg(cs42l42->component->dev, "Function D button press\n");
                break;
        case 4: /* Function A button press */
+               bias_level = SND_JACK_BTN_0;
                dev_dbg(cs42l42->component->dev, "Function A button press\n");
                break;
+       default:
+               bias_level = 0;
+               break;
        }
 
        /* Set button detect level sensitivity back to default */
@@ -1252,6 +1333,8 @@ static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
                (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
                (1 << CS42L42_M_SHORT_RLS_SHIFT) |
                (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+       return bias_level;
 }
 
 struct cs42l42_irq_params {
@@ -1296,6 +1379,8 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
        unsigned int current_plug_status;
        unsigned int current_button_status;
        unsigned int i;
+       int report = 0;
+
 
        /* Read sticky registers to clear interurpt */
        for (i = 0; i < ARRAY_SIZE(stickies); i++) {
@@ -1322,9 +1407,20 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
        if ((~masks[5]) & irq_params_table[5].mask) {
                if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) {
                        cs42l42_process_hs_type_detect(cs42l42);
-                       dev_dbg(component->dev,
-                               "Auto detect done (%d)\n",
-                               cs42l42->hs_type);
+                       switch(cs42l42->hs_type){
+                       case CS42L42_PLUG_CTIA:
+                       case CS42L42_PLUG_OMTP:
+                               snd_soc_jack_report(&cs42l42->jack, SND_JACK_HEADSET,
+                                                   SND_JACK_HEADSET);
+                               break;
+                       case CS42L42_PLUG_HEADPHONE:
+                               snd_soc_jack_report(&cs42l42->jack, SND_JACK_HEADPHONE,
+                                                   SND_JACK_HEADPHONE);
+                               break;
+                       default:
+                               break;
+                       }
+                       dev_dbg(component->dev, "Auto detect done (%d)\n", cs42l42->hs_type);
                }
        }
 
@@ -1342,8 +1438,19 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
                        if (cs42l42->plug_state != CS42L42_TS_UNPLUG) {
                                cs42l42->plug_state = CS42L42_TS_UNPLUG;
                                cs42l42_cancel_hs_type_detect(cs42l42);
-                               dev_dbg(component->dev,
-                                       "Unplug event\n");
+
+                               switch(cs42l42->hs_type){
+                               case CS42L42_PLUG_CTIA:
+                               case CS42L42_PLUG_OMTP:
+                                       snd_soc_jack_report(&cs42l42->jack, 0, SND_JACK_HEADSET);
+                                       break;
+                               case CS42L42_PLUG_HEADPHONE:
+                                       snd_soc_jack_report(&cs42l42->jack, 0, SND_JACK_HEADPHONE);
+                                       break;
+                               default:
+                                       break;
+                               }
+                               dev_dbg(component->dev, "Unplug event\n");
                        }
                        break;
 
@@ -1358,14 +1465,15 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
                if (!(current_button_status &
                        CS42L42_M_HSBIAS_HIZ_MASK)) {
 
-                       if (current_button_status &
-                               CS42L42_M_DETECT_TF_MASK) {
-                               dev_dbg(component->dev,
-                                       "Button released\n");
-                       } else if (current_button_status &
-                               CS42L42_M_DETECT_FT_MASK) {
-                               cs42l42_handle_button_press(cs42l42);
+                       if (current_button_status & CS42L42_M_DETECT_TF_MASK) {
+                               dev_dbg(component->dev, "Button released\n");
+                               report = 0;
+                       } else if (current_button_status & CS42L42_M_DETECT_FT_MASK) {
+                               report = cs42l42_handle_button_press(cs42l42);
+
                        }
+                       snd_soc_jack_report(&cs42l42->jack, report, SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+                                                                  SND_JACK_BTN_2 | SND_JACK_BTN_3);
                }
        }
 
@@ -1749,8 +1857,10 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
        /* Reset the Device */
        cs42l42->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
                "reset", GPIOD_OUT_LOW);
-       if (IS_ERR(cs42l42->reset_gpio))
-               return PTR_ERR(cs42l42->reset_gpio);
+       if (IS_ERR(cs42l42->reset_gpio)) {
+               ret = PTR_ERR(cs42l42->reset_gpio);
+               goto err_disable;
+       }
 
        if (cs42l42->reset_gpio) {
                dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
@@ -1784,13 +1894,13 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
                dev_err(&i2c_client->dev,
                        "CS42L42 Device ID (%X). Expected %X\n",
                        devid, CS42L42_CHIP_ID);
-               return ret;
+               goto err_disable;
        }
 
        ret = regmap_read(cs42l42->regmap, CS42L42_REVID, &reg);
        if (ret < 0) {
                dev_err(&i2c_client->dev, "Get Revision ID failed\n");
-               return ret;
+               goto err_disable;
        }
 
        dev_info(&i2c_client->dev,
@@ -1816,7 +1926,7 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
        if (i2c_client->dev.of_node) {
                ret = cs42l42_handle_device_data(i2c_client, cs42l42);
                if (ret != 0)
-                       return ret;
+                       goto err_disable;
        }
 
        /* Setup headset detection */
@@ -1842,8 +1952,9 @@ static int cs42l42_i2c_remove(struct i2c_client *i2c_client)
 {
        struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client);
 
-       /* Hold down reset */
-       gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+       devm_free_irq(&i2c_client->dev, i2c_client->irq, cs42l42);
+       pm_runtime_suspend(&i2c_client->dev);
+       pm_runtime_disable(&i2c_client->dev);
 
        return 0;
 }