diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/Kconfig | 15 | ||||
-rw-r--r-- | sound/soc/codecs/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/codecs/cs42l42-i2c.c | 106 | ||||
-rw-r--r-- | sound/soc/codecs/cs42l42.c | 259 | ||||
-rw-r--r-- | sound/soc/codecs/cs42l42.h | 24 | ||||
-rw-r--r-- | sound/soc/codecs/cs42l83-i2c.c | 242 |
6 files changed, 526 insertions, 126 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index c7d83fe999e9..444cee829a26 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -692,9 +692,15 @@ config SND_SOC_CS35L45_I2C Enable support for Cirrus Logic CS35L45 smart speaker amplifier with I2C control. +config SND_SOC_CS42L42_CORE + tristate + config SND_SOC_CS42L42 - tristate "Cirrus Logic CS42L42 CODEC" + tristate "Cirrus Logic CS42L42 CODEC (I2C)" depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS42L42_CORE config SND_SOC_CS42L51 tristate @@ -716,6 +722,13 @@ config SND_SOC_CS42L73 tristate "Cirrus Logic CS42L73 CODEC" depends on I2C +config SND_SOC_CS42L83 + tristate "Cirrus Logic CS42L83 CODEC" + depends on I2C + select REGMAP + select REGMAP_I2C + select SND_SOC_CS42L42_CORE + config SND_SOC_CS4234 tristate "Cirrus Logic CS4234 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 16a01635dd04..9170ee1447dd 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -65,11 +65,13 @@ snd-soc-cs35l45-objs := cs35l45.o snd-soc-cs35l45-spi-objs := cs35l45-spi.o snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o snd-soc-cs42l42-objs := cs42l42.o +snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o snd-soc-cs42l56-objs := cs42l56.o snd-soc-cs42l73-objs := cs42l73.o +snd-soc-cs42l83-i2c-objs := cs42l83-i2c.o snd-soc-cs4234-objs := cs4234.o snd-soc-cs4265-objs := cs4265.o snd-soc-cs4270-objs := cs4270.o @@ -422,12 +424,14 @@ obj-$(CONFIG_SND_SOC_CS35L45_TABLES) += snd-soc-cs35l45-tables.o obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o -obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o +obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o +obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o +obj-$(CONFIG_SND_SOC_CS42L83) += snd-soc-cs42l83-i2c.o obj-$(CONFIG_SND_SOC_CS4234) += snd-soc-cs4234.o obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c new file mode 100644 index 000000000000..1900ec75576e --- /dev/null +++ b/sound/soc/codecs/cs42l42-i2c.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l42-i2c.c -- CS42L42 ALSA SoC audio driver for I2C + * + * Copyright 2016, 2022 Cirrus Logic, Inc. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "cs42l42.h" + +static int cs42l42_i2c_probe(struct i2c_client *i2c_client) +{ + struct device *dev = &i2c_client->dev; + struct cs42l42_private *cs42l42; + struct regmap *regmap; + int ret; + + cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL); + if (!cs42l42) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(&i2c_client->dev, PTR_ERR(regmap), + "regmap_init() failed\n"); + + cs42l42->devid = CS42L42_CHIP_ID; + cs42l42->dev = dev; + cs42l42->regmap = regmap; + cs42l42->irq = i2c_client->irq; + + ret = cs42l42_common_probe(cs42l42, &cs42l42_soc_component, &cs42l42_dai); + if (ret) + return ret; + + return cs42l42_init(cs42l42); +} + +static int cs42l42_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs42l42_private *cs42l42 = dev_get_drvdata(&i2c_client->dev); + + cs42l42_common_remove(cs42l42); + + return 0; +} + +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 const struct dev_pm_ops cs42l42_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_i2c_resume) +}; + +static const struct of_device_id __maybe_unused cs42l42_of_match[] = { + { .compatible = "cirrus,cs42l42", }, + {} +}; +MODULE_DEVICE_TABLE(of, cs42l42_of_match); + +static const struct acpi_device_id __maybe_unused cs42l42_acpi_match[] = { + {"10134242", 0,}, + {} +}; +MODULE_DEVICE_TABLE(acpi, cs42l42_acpi_match); + +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_i2c_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); + +MODULE_DESCRIPTION("ASoC CS42L42 I2C driver"); +MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE); diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index 21368ed77c6d..bdc7e6bed6ac 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -15,7 +15,6 @@ #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> @@ -172,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: @@ -331,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: @@ -363,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, @@ -374,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, @@ -393,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); @@ -579,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), @@ -590,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[] = { @@ -647,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) @@ -895,19 +893,28 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream, 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); - /* - * 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; + 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; - /* I2S frame always has multiple of 2 channels */ - cs42l42->bclk = snd_soc_tdm_params_to_bclk(params, slot_width, 0, 2); + /* 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: @@ -947,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; @@ -980,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; @@ -1083,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", @@ -1106,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) { @@ -1626,7 +1646,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; } @@ -2101,7 +2121,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; @@ -2161,8 +2181,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; @@ -2184,6 +2205,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); @@ -2196,41 +2227,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; } @@ -2238,13 +2268,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); @@ -2252,48 +2282,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) { - dev_err_probe(&i2c_client->dev, ret, - "Failed to request IRQ\n"); + 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, ®); 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, @@ -2312,22 +2368,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: @@ -2336,78 +2392,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>"); diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h index 50299c9f283a..a72136664112 100644 --- a/sound/soc/codecs/cs42l42.h +++ b/sound/soc/codecs/cs42l42.h @@ -20,6 +20,8 @@ #include <linux/regulator/consumer.h> #include <sound/jack.h> #include <sound/cs42l42.h> +#include <sound/soc-component.h> +#include <sound/soc-dai.h> struct cs42l42_private { struct regmap *regmap; @@ -29,9 +31,11 @@ struct cs42l42_private { struct completion pdn_done; struct snd_soc_jack *jack; struct mutex irq_lock; + int devid; + int irq; int pll_config; - int bclk; u32 sclk; + u32 bclk_ratio; u32 srate; u8 plug_state; u8 hs_type; @@ -47,6 +51,24 @@ struct cs42l42_private { u8 stream_use; bool hp_adc_up_pending; bool suspended; + bool init_done; }; +extern const struct regmap_range_cfg cs42l42_page_range; +extern const struct regmap_config cs42l42_regmap; +extern const struct snd_soc_component_driver cs42l42_soc_component; +extern struct snd_soc_dai_driver cs42l42_dai; + +bool cs42l42_readable_register(struct device *dev, unsigned int reg); +bool cs42l42_volatile_register(struct device *dev, unsigned int reg); + +int cs42l42_suspend(struct device *dev); +int cs42l42_resume(struct device *dev); +void cs42l42_resume_restore(struct device *dev); +int cs42l42_common_probe(struct cs42l42_private *cs42l42, + const struct snd_soc_component_driver *component_drv, + struct snd_soc_dai_driver *dai); +int cs42l42_init(struct cs42l42_private *cs42l42); +void cs42l42_common_remove(struct cs42l42_private *cs42l42); + #endif /* __CS42L42_H__ */ diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c new file mode 100644 index 000000000000..ba8772aa51e1 --- /dev/null +++ b/sound/soc/codecs/cs42l83-i2c.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cs42l83-i2c.c -- CS42L83 ALSA SoC audio driver for I2C + * + * Based on cs42l42-i2c.c: + * Copyright 2016, 2022 Cirrus Logic, Inc. + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include "cs42l42.h" + +static const struct reg_default cs42l83_reg_defaults[] = { + { CS42L42_FRZ_CTL, 0x00 }, + { CS42L42_SRC_CTL, 0x10 }, + { CS42L42_MCLK_CTL, 0x00 }, /* <- only deviation from CS42L42 */ + { CS42L42_SFTRAMP_RATE, 0xA4 }, + { CS42L42_SLOW_START_ENABLE, 0x70 }, + { CS42L42_I2C_DEBOUNCE, 0x88 }, + { CS42L42_I2C_STRETCH, 0x03 }, + { CS42L42_I2C_TIMEOUT, 0xB7 }, + { CS42L42_PWR_CTL1, 0xFF }, + { CS42L42_PWR_CTL2, 0x84 }, + { CS42L42_PWR_CTL3, 0x20 }, + { CS42L42_RSENSE_CTL1, 0x40 }, + { CS42L42_RSENSE_CTL2, 0x00 }, + { CS42L42_OSC_SWITCH, 0x00 }, + { CS42L42_RSENSE_CTL3, 0x1B }, + { CS42L42_TSENSE_CTL, 0x1B }, + { CS42L42_TSRS_INT_DISABLE, 0x00 }, + { CS42L42_HSDET_CTL1, 0x77 }, + { CS42L42_HSDET_CTL2, 0x00 }, + { CS42L42_HS_SWITCH_CTL, 0xF3 }, + { CS42L42_HS_CLAMP_DISABLE, 0x00 }, + { CS42L42_MCLK_SRC_SEL, 0x00 }, + { CS42L42_SPDIF_CLK_CFG, 0x00 }, + { CS42L42_FSYNC_PW_LOWER, 0x00 }, + { CS42L42_FSYNC_PW_UPPER, 0x00 }, + { CS42L42_FSYNC_P_LOWER, 0xF9 }, + { CS42L42_FSYNC_P_UPPER, 0x00 }, + { CS42L42_ASP_CLK_CFG, 0x00 }, + { CS42L42_ASP_FRM_CFG, 0x10 }, + { CS42L42_FS_RATE_EN, 0x00 }, + { CS42L42_IN_ASRC_CLK, 0x00 }, + { CS42L42_OUT_ASRC_CLK, 0x00 }, + { CS42L42_PLL_DIV_CFG1, 0x00 }, + { CS42L42_ADC_OVFL_INT_MASK, 0x01 }, + { CS42L42_MIXER_INT_MASK, 0x0F }, + { CS42L42_SRC_INT_MASK, 0x0F }, + { CS42L42_ASP_RX_INT_MASK, 0x1F }, + { CS42L42_ASP_TX_INT_MASK, 0x0F }, + { CS42L42_CODEC_INT_MASK, 0x03 }, + { CS42L42_SRCPL_INT_MASK, 0x7F }, + { CS42L42_VPMON_INT_MASK, 0x01 }, + { CS42L42_PLL_LOCK_INT_MASK, 0x01 }, + { CS42L42_TSRS_PLUG_INT_MASK, 0x0F }, + { CS42L42_PLL_CTL1, 0x00 }, + { CS42L42_PLL_DIV_FRAC0, 0x00 }, + { CS42L42_PLL_DIV_FRAC1, 0x00 }, + { CS42L42_PLL_DIV_FRAC2, 0x00 }, + { CS42L42_PLL_DIV_INT, 0x40 }, + { CS42L42_PLL_CTL3, 0x10 }, + { CS42L42_PLL_CAL_RATIO, 0x80 }, + { CS42L42_PLL_CTL4, 0x03 }, + { CS42L42_LOAD_DET_EN, 0x00 }, + { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 }, + { CS42L42_WAKE_CTL, 0xC0 }, + { CS42L42_ADC_DISABLE_MUTE, 0x00 }, + { CS42L42_TIPSENSE_CTL, 0x02 }, + { CS42L42_MISC_DET_CTL, 0x03 }, + { CS42L42_MIC_DET_CTL1, 0x1F }, + { CS42L42_MIC_DET_CTL2, 0x2F }, + { CS42L42_DET_INT1_MASK, 0xE0 }, + { CS42L42_DET_INT2_MASK, 0xFF }, + { CS42L42_HS_BIAS_CTL, 0xC2 }, + { CS42L42_ADC_CTL, 0x00 }, + { CS42L42_ADC_VOLUME, 0x00 }, + { CS42L42_ADC_WNF_HPF_CTL, 0x71 }, + { CS42L42_DAC_CTL1, 0x00 }, + { CS42L42_DAC_CTL2, 0x02 }, + { CS42L42_HP_CTL, 0x0D }, + { CS42L42_CLASSH_CTL, 0x07 }, + { CS42L42_MIXER_CHA_VOL, 0x3F }, + { CS42L42_MIXER_ADC_VOL, 0x3F }, + { CS42L42_MIXER_CHB_VOL, 0x3F }, + { CS42L42_EQ_COEF_IN0, 0x00 }, + { CS42L42_EQ_COEF_IN1, 0x00 }, + { CS42L42_EQ_COEF_IN2, 0x00 }, + { CS42L42_EQ_COEF_IN3, 0x00 }, + { CS42L42_EQ_COEF_RW, 0x00 }, + { CS42L42_EQ_COEF_OUT0, 0x00 }, + { CS42L42_EQ_COEF_OUT1, 0x00 }, + { CS42L42_EQ_COEF_OUT2, 0x00 }, + { CS42L42_EQ_COEF_OUT3, 0x00 }, + { CS42L42_EQ_INIT_STAT, 0x00 }, + { CS42L42_EQ_START_FILT, 0x00 }, + { CS42L42_EQ_MUTE_CTL, 0x00 }, + { CS42L42_SP_RX_CH_SEL, 0x04 }, + { CS42L42_SP_RX_ISOC_CTL, 0x04 }, + { CS42L42_SP_RX_FS, 0x8C }, + { CS42l42_SPDIF_CH_SEL, 0x0E }, + { CS42L42_SP_TX_ISOC_CTL, 0x04 }, + { CS42L42_SP_TX_FS, 0xCC }, + { CS42L42_SPDIF_SW_CTL1, 0x3F }, + { CS42L42_SRC_SDIN_FS, 0x40 }, + { CS42L42_SRC_SDOUT_FS, 0x40 }, + { CS42L42_SPDIF_CTL1, 0x01 }, + { CS42L42_SPDIF_CTL2, 0x00 }, + { CS42L42_SPDIF_CTL3, 0x00 }, + { CS42L42_SPDIF_CTL4, 0x42 }, + { CS42L42_ASP_TX_SZ_EN, 0x00 }, + { CS42L42_ASP_TX_CH_EN, 0x00 }, + { CS42L42_ASP_TX_CH_AP_RES, 0x0F }, + { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 }, + { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_EN, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 }, + { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 }, + { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 }, +}; + +/* + * This is all the same as for CS42L42 but we + * replace the on-reset register defaults. + */ +const struct regmap_config cs42l83_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = cs42l42_readable_register, + .volatile_reg = cs42l42_volatile_register, + + .ranges = &cs42l42_page_range, + .num_ranges = 1, + + .max_register = CS42L42_MAX_REGISTER, + .reg_defaults = cs42l83_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(cs42l83_reg_defaults), + .cache_type = REGCACHE_RBTREE, + + .use_single_read = true, + .use_single_write = true, +}; + +static int cs42l83_i2c_probe(struct i2c_client *i2c_client) +{ + struct device *dev = &i2c_client->dev; + struct cs42l42_private *cs42l83; + struct regmap *regmap; + int ret; + + cs42l83 = devm_kzalloc(dev, sizeof(*cs42l83), GFP_KERNEL); + if (!cs42l83) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(i2c_client, &cs42l83_regmap); + if (IS_ERR(regmap)) + return dev_err_probe(&i2c_client->dev, PTR_ERR(regmap), + "regmap_init() failed\n"); + + cs42l83->devid = CS42L83_CHIP_ID; + cs42l83->dev = dev; + cs42l83->regmap = regmap; + cs42l83->irq = i2c_client->irq; + + ret = cs42l42_common_probe(cs42l83, &cs42l42_soc_component, &cs42l42_dai); + if (ret) + return ret; + + return cs42l42_init(cs42l83); +} + +static int cs42l83_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs42l42_private *cs42l83 = dev_get_drvdata(&i2c_client->dev); + + cs42l42_common_remove(cs42l83); + + return 0; +} + +static int __maybe_unused cs42l83_i2c_resume(struct device *dev) +{ + int ret; + + ret = cs42l42_resume(dev); + if (ret) + return ret; + + cs42l42_resume_restore(dev); + + return 0; +} + +static const struct dev_pm_ops cs42l83_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l83_i2c_resume) +}; + +static const struct of_device_id __maybe_unused cs42l83_of_match[] = { + { .compatible = "cirrus,cs42l83", }, + {} +}; +MODULE_DEVICE_TABLE(of, cs42l83_of_match); + +static struct i2c_driver cs42l83_i2c_driver = { + .driver = { + .name = "cs42l83", + .pm = &cs42l83_i2c_pm_ops, + .of_match_table = of_match_ptr(cs42l83_of_match), + }, + .probe_new = cs42l83_i2c_probe, + .remove = cs42l83_i2c_remove, +}; + +module_i2c_driver(cs42l83_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS42L83 I2C driver"); +MODULE_AUTHOR("Martin PoviĊĦer <povik+lin@cutebit.org>"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE); |