aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/cs42l42.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/cs42l42.c')
-rw-r--r--sound/soc/codecs/cs42l42.c284
1 files changed, 151 insertions, 133 deletions
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index 53274ea84ebd..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,76 +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 void 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);
}
-
-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>");