aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/cs42l42.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2022-10-03 16:30:42 +0200
committerTakashi Iwai <tiwai@suse.de>2022-10-03 16:30:42 +0200
commit86a4d29e75540e20f991e72f17aa51d0e775a397 (patch)
tree87d99ee3855d97da60074fdc9c0cdd63dd2da66b /sound/soc/codecs/cs42l42.c
parentMerge branch 'for-next' into for-linus (diff)
parentASoC: rockchip: i2s: use regmap_read_poll_timeout_atomic to poll I2S_CLR (diff)
downloadlinux-dev-86a4d29e75540e20f991e72f17aa51d0e775a397.tar.xz
linux-dev-86a4d29e75540e20f991e72f17aa51d0e775a397.zip
Merge tag 'asoc-v6.1' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Updates for v6.1 This has been a very quiet release for the core but quite a busy one for drivers with a big crop of new drivers and lots of feature additions and fixes to existing ones: - A new string helper parse_int_array_user(). - Improvements to the SOF IPC4 code, especially around trace. - Support for AMD Rembrant DSPs, AMD Pink Sardine ACP 6.2, Apple Silcon systems, Everest ES8326, Intel Sky Lake and Kaby Lake, MediaTek MT8186 support, NXP i.MX8ULP DSPs, Qualcomm SC8280XP, SM8250 and SM8450 and Texas Instruments SRC4392 There is a conflict with the conversion of I2C remove functions to void in the cs42l42 driver which is fairly straightforward to resolve but should be highlighted to Linus.
Diffstat (limited to 'sound/soc/codecs/cs42l42.c')
-rw-r--r--sound/soc/codecs/cs42l42.c286
1 files changed, 151 insertions, 135 deletions
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index daafd4251ce6..2fefbcf7bd13 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -12,10 +12,9 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/version.h>
-#include <linux/kernel.h>
+#include <linux/types.h>
#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -37,6 +36,14 @@
#include "cs42l42.h"
#include "cirrus_legacy.h"
+static const char * const cs42l42_supply_names[] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD_FILT",
+ "VL",
+};
+
static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_FRZ_CTL, 0x00 },
{ CS42L42_SRC_CTL, 0x10 },
@@ -164,7 +171,7 @@ static const struct reg_default cs42l42_reg_defaults[] = {
{ CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 },
};
-static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
+bool cs42l42_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS42L42_PAGE_REGISTER:
@@ -323,8 +330,9 @@ static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
return false;
}
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_readable_register, SND_SOC_CS42L42_CORE);
-static bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
+bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case CS42L42_DEVID_AB:
@@ -355,8 +363,9 @@ static bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
return false;
}
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_volatile_register, SND_SOC_CS42L42_CORE);
-static const struct regmap_range_cfg cs42l42_page_range = {
+const struct regmap_range_cfg cs42l42_page_range = {
.name = "Pages",
.range_min = 0,
.range_max = CS42L42_MAX_REGISTER,
@@ -366,8 +375,9 @@ static const struct regmap_range_cfg cs42l42_page_range = {
.window_start = 0,
.window_len = 256,
};
+EXPORT_SYMBOL_NS_GPL(cs42l42_page_range, SND_SOC_CS42L42_CORE);
-static const struct regmap_config cs42l42_regmap = {
+const struct regmap_config cs42l42_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -385,6 +395,7 @@ static const struct regmap_config cs42l42_regmap = {
.use_single_read = true,
.use_single_write = true,
};
+EXPORT_SYMBOL_NS_GPL(cs42l42_regmap, SND_SOC_CS42L42_CORE);
static DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 100, true);
static DECLARE_TLV_DB_SCALE(mixer_tlv, -6300, 100, true);
@@ -395,7 +406,7 @@ static int cs42l42_slow_start_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
u8 val;
- /* all bits of SLOW_START_EN much change together */
+ /* all bits of SLOW_START_EN must change together */
switch (ucontrol->value.integer.value[0]) {
case 0:
val = 0;
@@ -571,7 +582,7 @@ static int cs42l42_set_jack(struct snd_soc_component *component, struct snd_soc_
return 0;
}
-static const struct snd_soc_component_driver soc_component_dev_cs42l42 = {
+const struct snd_soc_component_driver cs42l42_soc_component = {
.set_jack = cs42l42_set_jack,
.dapm_widgets = cs42l42_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets),
@@ -582,6 +593,7 @@ static const struct snd_soc_component_driver soc_component_dev_cs42l42 = {
.idle_bias_on = 1,
.endianness = 1,
};
+EXPORT_SYMBOL_NS_GPL(cs42l42_soc_component, SND_SOC_CS42L42_CORE);
/* Switch to SCLK. Atomic delay after the write to allow the switch to complete. */
static const struct reg_sequence cs42l42_to_sclk_seq[] = {
@@ -639,18 +651,12 @@ static const struct cs42l42_pll_params pll_ratio_table[] = {
{ 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}
};
-static int cs42l42_pll_config(struct snd_soc_component *component)
+static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk)
{
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;
-
/* Don't reconfigure if there is an audio stream running */
if (cs42l42->stream_use) {
if (pll_ratio_table[cs42l42->pll_config].sclk == clk)
@@ -885,22 +891,30 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
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 slot_width = 0;
unsigned int val = 0;
+ unsigned int bclk;
int ret;
cs42l42->srate = params_rate(params);
- cs42l42->bclk = snd_soc_params_to_bclk(params);
- /* I2S frame always has 2 channels even for mono audio */
- if (channels == 1)
- cs42l42->bclk *= 2;
+ if (cs42l42->bclk_ratio) {
+ /* machine driver has set the BCLK/samp-rate ratio */
+ bclk = cs42l42->bclk_ratio * params_rate(params);
+ } else if (cs42l42->sclk) {
+ /* machine driver has set the SCLK */
+ bclk = cs42l42->sclk;
+ } else {
+ /*
+ * Assume 24-bit samples are in 32-bit slots, to prevent SCLK being
+ * more than assumed (which would result in overclocking).
+ */
+ if (params_width(params) == 24)
+ slot_width = 32;
- /*
- * Assume 24-bit samples are in 32-bit slots, to prevent SCLK being
- * more than assumed (which would result in overclocking).
- */
- if (params_width(params) == 24)
- cs42l42->bclk = (cs42l42->bclk / 3) * 4;
+ /* I2S frame always has multiple of 2 channels */
+ bclk = snd_soc_tdm_params_to_bclk(params, slot_width, 0, 2);
+ }
switch (substream->stream) {
case SNDRV_PCM_STREAM_CAPTURE:
@@ -940,7 +954,7 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- ret = cs42l42_pll_config(component);
+ ret = cs42l42_pll_config(component, bclk);
if (ret)
return ret;
@@ -973,6 +987,17 @@ static int cs42l42_set_sysclk(struct snd_soc_dai *dai,
return -EINVAL;
}
+static int cs42l42_set_bclk_ratio(struct snd_soc_dai *dai,
+ unsigned int bclk_ratio)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
+
+ cs42l42->bclk_ratio = bclk_ratio;
+
+ return 0;
+}
+
static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
struct snd_soc_component *component = dai->component;
@@ -1076,10 +1101,11 @@ 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,
+ .set_bclk_ratio = cs42l42_set_bclk_ratio,
.mute_stream = cs42l42_mute_stream,
};
-static struct snd_soc_dai_driver cs42l42_dai = {
+struct snd_soc_dai_driver cs42l42_dai = {
.name = "cs42l42",
.playback = {
.stream_name = "Playback",
@@ -1099,6 +1125,7 @@ static struct snd_soc_dai_driver cs42l42_dai = {
.symmetric_sample_bits = 1,
.ops = &cs42l42_ops,
};
+EXPORT_SYMBOL_NS_GPL(cs42l42_dai, SND_SOC_CS42L42_CORE);
static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42)
{
@@ -1172,14 +1199,11 @@ static void cs42l42_manual_hs_type_detect(struct cs42l42_private *cs42l42)
cs42l42->hs_type = CS42L42_PLUG_OMTP;
hs_det_sw = CS42L42_HSDET_SW_TYPE2;
break;
- case CS42L42_HSDET_COMP_TYPE3:
+ /* Detect Type 3 and Type 4 Headsets as Headphones */
+ default:
cs42l42->hs_type = CS42L42_PLUG_HEADPHONE;
hs_det_sw = CS42L42_HSDET_SW_TYPE3;
break;
- default:
- cs42l42->hs_type = CS42L42_PLUG_INVALID;
- hs_det_sw = CS42L42_HSDET_SW_TYPE4;
- break;
}
}
@@ -1619,7 +1643,7 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
unsigned int i;
mutex_lock(&cs42l42->irq_lock);
- if (cs42l42->suspended) {
+ if (cs42l42->suspended || !cs42l42->init_done) {
mutex_unlock(&cs42l42->irq_lock);
return IRQ_NONE;
}
@@ -2094,7 +2118,7 @@ static const struct reg_sequence __maybe_unused cs42l42_shutdown_seq[] = {
REG_SEQ0(CS42L42_PWR_CTL1, 0xFF)
};
-static int __maybe_unused cs42l42_suspend(struct device *dev)
+int cs42l42_suspend(struct device *dev)
{
struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
unsigned int reg;
@@ -2154,8 +2178,9 @@ static int __maybe_unused cs42l42_suspend(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_suspend, SND_SOC_CS42L42_CORE);
-static int __maybe_unused cs42l42_resume(struct device *dev)
+int cs42l42_resume(struct device *dev)
{
struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
int ret;
@@ -2177,6 +2202,16 @@ static int __maybe_unused cs42l42_resume(struct device *dev)
gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
+ dev_dbg(dev, "System resume powered up\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_resume, SND_SOC_CS42L42_CORE);
+
+void cs42l42_resume_restore(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+
regcache_cache_only(cs42l42->regmap, false);
regcache_mark_dirty(cs42l42->regmap);
@@ -2189,40 +2224,40 @@ static int __maybe_unused cs42l42_resume(struct device *dev)
mutex_unlock(&cs42l42->irq_lock);
dev_dbg(dev, "System resumed\n");
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_resume_restore, SND_SOC_CS42L42_CORE);
+
+static int __maybe_unused cs42l42_i2c_resume(struct device *dev)
+{
+ int ret;
+
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ cs42l42_resume_restore(dev);
return 0;
}
-static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
+int cs42l42_common_probe(struct cs42l42_private *cs42l42,
+ const struct snd_soc_component_driver *component_drv,
+ struct snd_soc_dai_driver *dai)
{
- struct cs42l42_private *cs42l42;
- int ret, i, devid;
- unsigned int reg;
-
- cs42l42 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l42_private),
- GFP_KERNEL);
- if (!cs42l42)
- return -ENOMEM;
+ int ret, i;
- cs42l42->dev = &i2c_client->dev;
- i2c_set_clientdata(i2c_client, cs42l42);
+ dev_set_drvdata(cs42l42->dev, cs42l42);
mutex_init(&cs42l42->irq_lock);
- cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap);
- if (IS_ERR(cs42l42->regmap)) {
- ret = PTR_ERR(cs42l42->regmap);
- dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
- return ret;
- }
-
+ BUILD_BUG_ON(ARRAY_SIZE(cs42l42_supply_names) != ARRAY_SIZE(cs42l42->supplies));
for (i = 0; i < ARRAY_SIZE(cs42l42->supplies); i++)
cs42l42->supplies[i].supply = cs42l42_supply_names[i];
- ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ret = devm_regulator_bulk_get(cs42l42->dev,
ARRAY_SIZE(cs42l42->supplies),
cs42l42->supplies);
if (ret != 0) {
- dev_err(&i2c_client->dev,
+ dev_err(cs42l42->dev,
"Failed to request supplies: %d\n", ret);
return ret;
}
@@ -2230,13 +2265,13 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies),
cs42l42->supplies);
if (ret != 0) {
- dev_err(&i2c_client->dev,
+ dev_err(cs42l42->dev,
"Failed to enable supplies: %d\n", ret);
return ret;
}
/* Reset the Device */
- cs42l42->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ cs42l42->reset_gpio = devm_gpiod_get_optional(cs42l42->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(cs42l42->reset_gpio)) {
ret = PTR_ERR(cs42l42->reset_gpio);
@@ -2244,50 +2279,74 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
}
if (cs42l42->reset_gpio) {
- dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
+ dev_dbg(cs42l42->dev, "Found reset GPIO\n");
gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
}
usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
/* Request IRQ if one was specified */
- if (i2c_client->irq) {
- ret = request_threaded_irq(i2c_client->irq,
+ if (cs42l42->irq) {
+ ret = request_threaded_irq(cs42l42->irq,
NULL, cs42l42_irq_thread,
IRQF_ONESHOT | IRQF_TRIGGER_LOW,
"cs42l42", cs42l42);
- if (ret == -EPROBE_DEFER) {
- goto err_disable_noirq;
- } else if (ret != 0) {
- dev_err(&i2c_client->dev,
- "Failed to request IRQ: %d\n", ret);
+ if (ret) {
+ dev_err_probe(cs42l42->dev, ret,
+ "Failed to request IRQ\n");
goto err_disable_noirq;
}
}
+ /* Register codec now so it can EPROBE_DEFER */
+ ret = devm_snd_soc_register_component(cs42l42->dev, component_drv, dai, 1);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
+
+err_disable_noirq:
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+err_disable_noreset:
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l42_common_probe, SND_SOC_CS42L42_CORE);
+
+int cs42l42_init(struct cs42l42_private *cs42l42)
+{
+ unsigned int reg;
+ int devid, ret;
+
/* initialize codec */
devid = cirrus_read_device_id(cs42l42->regmap, CS42L42_DEVID_AB);
if (devid < 0) {
ret = devid;
- dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ dev_err(cs42l42->dev, "Failed to read device ID: %d\n", ret);
goto err_disable;
}
- if (devid != CS42L42_CHIP_ID) {
+ if (devid != cs42l42->devid) {
ret = -ENODEV;
- dev_err(&i2c_client->dev,
- "CS42L42 Device ID (%X). Expected %X\n",
- devid, CS42L42_CHIP_ID);
+ dev_err(cs42l42->dev,
+ "CS42L%x Device ID (%X). Expected %X\n",
+ cs42l42->devid & 0xff, devid, cs42l42->devid);
goto err_disable;
}
ret = regmap_read(cs42l42->regmap, CS42L42_REVID, &reg);
if (ret < 0) {
- dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ dev_err(cs42l42->dev, "Get Revision ID failed\n");
goto err_shutdown;
}
- dev_info(&i2c_client->dev,
- "Cirrus Logic CS42L42, Revision: %02X\n", reg & 0xFF);
+ dev_info(cs42l42->dev,
+ "Cirrus Logic CS42L%x, Revision: %02X\n",
+ cs42l42->devid & 0xff, reg & 0xFF);
/* Power up the codec */
regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL1,
@@ -2306,22 +2365,22 @@ static int cs42l42_i2c_probe(struct i2c_client *i2c_client)
(1 << CS42L42_ADC_PDN_SHIFT) |
(0 << CS42L42_PDN_ALL_SHIFT));
- ret = cs42l42_handle_device_data(&i2c_client->dev, cs42l42);
+ ret = cs42l42_handle_device_data(cs42l42->dev, cs42l42);
if (ret != 0)
goto err_shutdown;
/* Setup headset detection */
cs42l42_setup_hs_type_detect(cs42l42);
+ /*
+ * Set init_done before unmasking interrupts so any triggered
+ * immediately will be handled.
+ */
+ cs42l42->init_done = true;
+
/* Mask/Unmask Interrupts */
cs42l42_set_interrupt_masks(cs42l42);
- /* Register codec for machine driver */
- ret = devm_snd_soc_register_component(&i2c_client->dev,
- &soc_component_dev_cs42l42, &cs42l42_dai, 1);
- if (ret < 0)
- goto err_shutdown;
-
return 0;
err_shutdown:
@@ -2330,78 +2389,35 @@ err_shutdown:
regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
err_disable:
- if (i2c_client->irq)
- free_irq(i2c_client->irq, cs42l42);
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
-err_disable_noirq:
gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
-err_disable_noreset:
regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
cs42l42->supplies);
return ret;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_init, SND_SOC_CS42L42_CORE);
-static int cs42l42_i2c_remove(struct i2c_client *i2c_client)
+void cs42l42_common_remove(struct cs42l42_private *cs42l42)
{
- struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client);
-
- if (i2c_client->irq)
- free_irq(i2c_client->irq, cs42l42);
+ if (cs42l42->irq)
+ free_irq(cs42l42->irq, cs42l42);
/*
* The driver might not have control of reset and power supplies,
* so ensure that the chip internals are powered down.
*/
- regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff);
- regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff);
- regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
+ if (cs42l42->init_done) {
+ regmap_write(cs42l42->regmap, CS42L42_CODEC_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK, 0xff);
+ regmap_write(cs42l42->regmap, CS42L42_PWR_CTL1, 0xff);
+ }
gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies), cs42l42->supplies);
-
- return 0;
}
-
-static const struct dev_pm_ops cs42l42_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_resume)
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id cs42l42_of_match[] = {
- { .compatible = "cirrus,cs42l42", },
- {}
-};
-MODULE_DEVICE_TABLE(of, cs42l42_of_match);
-#endif
-
-#ifdef CONFIG_ACPI
-static const struct acpi_device_id cs42l42_acpi_match[] = {
- {"10134242", 0,},
- {}
-};
-MODULE_DEVICE_TABLE(acpi, cs42l42_acpi_match);
-#endif
-
-static const struct i2c_device_id cs42l42_id[] = {
- {"cs42l42", 0},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, cs42l42_id);
-
-static struct i2c_driver cs42l42_i2c_driver = {
- .driver = {
- .name = "cs42l42",
- .pm = &cs42l42_pm_ops,
- .of_match_table = of_match_ptr(cs42l42_of_match),
- .acpi_match_table = ACPI_PTR(cs42l42_acpi_match),
- },
- .id_table = cs42l42_id,
- .probe_new = cs42l42_i2c_probe,
- .remove = cs42l42_i2c_remove,
-};
-
-module_i2c_driver(cs42l42_i2c_driver);
+EXPORT_SYMBOL_NS_GPL(cs42l42_common_remove, SND_SOC_CS42L42_CORE);
MODULE_DESCRIPTION("ASoC CS42L42 driver");
MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");