353 lines · c
1/* SPDX-License-Identifier: GPL-2.0+ */2/*3 * Cirrus Logic CS4341A ALSA SoC Codec Driver4 * Author: Alexander Shiyan <shc_work@mail.ru>5 */6 7#include <linux/i2c.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/regmap.h>11#include <linux/spi/spi.h>12 13#include <sound/pcm.h>14#include <sound/pcm_params.h>15#include <sound/soc.h>16#include <sound/tlv.h>17 18#define CS4341_REG_MODE1 0x0019#define CS4341_REG_MODE2 0x0120#define CS4341_REG_MIX 0x0221#define CS4341_REG_VOLA 0x0322#define CS4341_REG_VOLB 0x0423 24#define CS4341_MODE2_DIF (7 << 4)25#define CS4341_MODE2_DIF_I2S_24 (0 << 4)26#define CS4341_MODE2_DIF_I2S_16 (1 << 4)27#define CS4341_MODE2_DIF_LJ_24 (2 << 4)28#define CS4341_MODE2_DIF_RJ_24 (3 << 4)29#define CS4341_MODE2_DIF_RJ_16 (5 << 4)30#define CS4341_VOLX_MUTE (1 << 7)31 32struct cs4341_priv {33 unsigned int fmt;34 struct regmap *regmap;35 struct regmap_config regcfg;36};37 38static const struct reg_default cs4341_reg_defaults[] = {39 { CS4341_REG_MODE1, 0x00 },40 { CS4341_REG_MODE2, 0x82 },41 { CS4341_REG_MIX, 0x49 },42 { CS4341_REG_VOLA, 0x80 },43 { CS4341_REG_VOLB, 0x80 },44};45 46static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format)47{48 struct snd_soc_component *component = dai->component;49 struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);50 51 switch (format & SND_SOC_DAIFMT_MASTER_MASK) {52 case SND_SOC_DAIFMT_CBS_CFS:53 break;54 default:55 return -EINVAL;56 }57 58 switch (format & SND_SOC_DAIFMT_INV_MASK) {59 case SND_SOC_DAIFMT_NB_NF:60 break;61 default:62 return -EINVAL;63 }64 65 switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {66 case SND_SOC_DAIFMT_I2S:67 case SND_SOC_DAIFMT_LEFT_J:68 case SND_SOC_DAIFMT_RIGHT_J:69 cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK;70 break;71 default:72 return -EINVAL;73 }74 75 return 0;76}77 78static int cs4341_hw_params(struct snd_pcm_substream *substream,79 struct snd_pcm_hw_params *params,80 struct snd_soc_dai *dai)81{82 struct snd_soc_component *component = dai->component;83 struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component);84 unsigned int mode = 0;85 int b24 = 0;86 87 switch (params_format(params)) {88 case SNDRV_PCM_FORMAT_S24_LE:89 b24 = 1;90 break;91 case SNDRV_PCM_FORMAT_S16_LE:92 break;93 default:94 dev_err(component->dev, "Unsupported PCM format 0x%08x.\n",95 params_format(params));96 return -EINVAL;97 }98 99 switch (cs4341->fmt) {100 case SND_SOC_DAIFMT_I2S:101 mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16;102 break;103 case SND_SOC_DAIFMT_LEFT_J:104 mode = CS4341_MODE2_DIF_LJ_24;105 break;106 case SND_SOC_DAIFMT_RIGHT_J:107 mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16;108 break;109 default:110 dev_err(component->dev, "Unsupported DAI format 0x%08x.\n",111 cs4341->fmt);112 return -EINVAL;113 }114 115 return snd_soc_component_update_bits(component, CS4341_REG_MODE2,116 CS4341_MODE2_DIF, mode);117}118 119static int cs4341_mute(struct snd_soc_dai *dai, int mute, int direction)120{121 struct snd_soc_component *component = dai->component;122 int ret;123 124 ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA,125 CS4341_VOLX_MUTE,126 mute ? CS4341_VOLX_MUTE : 0);127 if (ret < 0)128 return ret;129 130 return snd_soc_component_update_bits(component, CS4341_REG_VOLB,131 CS4341_VOLX_MUTE,132 mute ? CS4341_VOLX_MUTE : 0);133}134 135static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0);136 137static const char * const deemph[] = {138 "None", "44.1k", "48k", "32k",139};140 141static const struct soc_enum deemph_enum =142 SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph);143 144static const char * const srzc[] = {145 "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC",146};147 148static const struct soc_enum srzc_enum =149 SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc);150 151 152static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = {153 SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0),154 SND_SOC_DAPM_OUTPUT("OutA"),155 SND_SOC_DAPM_OUTPUT("OutB"),156};157 158static const struct snd_soc_dapm_route cs4341_routes[] = {159 { "OutA", NULL, "HiFi DAC" },160 { "OutB", NULL, "HiFi DAC" },161 { "DAC Playback", NULL, "OutA" },162 { "DAC Playback", NULL, "OutB" },163};164 165static const struct snd_kcontrol_new cs4341_controls[] = {166 SOC_DOUBLE_R_TLV("Master Playback Volume",167 CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv),168 SOC_ENUM("De-Emphasis Control", deemph_enum),169 SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum),170 SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0),171 SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0),172};173 174static const struct snd_soc_dai_ops cs4341_dai_ops = {175 .set_fmt = cs4341_set_fmt,176 .hw_params = cs4341_hw_params,177 .mute_stream = cs4341_mute,178 .no_capture_mute = 1,179};180 181static struct snd_soc_dai_driver cs4341_dai = {182 .name = "cs4341a-hifi",183 .playback = {184 .stream_name = "DAC Playback",185 .channels_min = 1,186 .channels_max = 2,187 .rates = SNDRV_PCM_RATE_8000_96000,188 .formats = SNDRV_PCM_FMTBIT_S16_LE |189 SNDRV_PCM_FMTBIT_S24_LE,190 },191 .ops = &cs4341_dai_ops,192 .symmetric_rate = 1,193};194 195static const struct snd_soc_component_driver soc_component_cs4341 = {196 .controls = cs4341_controls,197 .num_controls = ARRAY_SIZE(cs4341_controls),198 .dapm_widgets = cs4341_dapm_widgets,199 .num_dapm_widgets = ARRAY_SIZE(cs4341_dapm_widgets),200 .dapm_routes = cs4341_routes,201 .num_dapm_routes = ARRAY_SIZE(cs4341_routes),202 .idle_bias_on = 1,203 .use_pmdown_time = 1,204 .endianness = 1,205};206 207static const struct of_device_id __maybe_unused cs4341_dt_ids[] = {208 { .compatible = "cirrus,cs4341a", },209 { }210};211MODULE_DEVICE_TABLE(of, cs4341_dt_ids);212 213static int cs4341_probe(struct device *dev)214{215 struct cs4341_priv *cs4341 = dev_get_drvdata(dev);216 int i;217 218 for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++)219 regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg,220 cs4341_reg_defaults[i].def);221 222 return devm_snd_soc_register_component(dev, &soc_component_cs4341,223 &cs4341_dai, 1);224}225 226#if IS_ENABLED(CONFIG_I2C)227static int cs4341_i2c_probe(struct i2c_client *i2c)228{229 struct cs4341_priv *cs4341;230 231 cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL);232 if (!cs4341)233 return -ENOMEM;234 235 i2c_set_clientdata(i2c, cs4341);236 237 cs4341->regcfg.reg_bits = 8;238 cs4341->regcfg.val_bits = 8;239 cs4341->regcfg.max_register = CS4341_REG_VOLB;240 cs4341->regcfg.cache_type = REGCACHE_FLAT;241 cs4341->regcfg.reg_defaults = cs4341_reg_defaults;242 cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);243 cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg);244 if (IS_ERR(cs4341->regmap))245 return PTR_ERR(cs4341->regmap);246 247 return cs4341_probe(&i2c->dev);248}249 250static const struct i2c_device_id cs4341_i2c_id[] = {251 { "cs4341" },252 { }253};254MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id);255 256static struct i2c_driver cs4341_i2c_driver = {257 .driver = {258 .name = "cs4341-i2c",259 .of_match_table = of_match_ptr(cs4341_dt_ids),260 },261 .probe = cs4341_i2c_probe,262 .id_table = cs4341_i2c_id,263};264#endif265 266#if defined(CONFIG_SPI_MASTER)267static bool cs4341_reg_readable(struct device *dev, unsigned int reg)268{269 return false;270}271 272static int cs4341_spi_probe(struct spi_device *spi)273{274 struct cs4341_priv *cs4341;275 int ret;276 277 cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL);278 if (!cs4341)279 return -ENOMEM;280 281 if (!spi->bits_per_word)282 spi->bits_per_word = 8;283 if (!spi->max_speed_hz)284 spi->max_speed_hz = 6000000;285 ret = spi_setup(spi);286 if (ret)287 return ret;288 289 spi_set_drvdata(spi, cs4341);290 291 cs4341->regcfg.reg_bits = 16;292 cs4341->regcfg.val_bits = 8;293 cs4341->regcfg.write_flag_mask = 0x20;294 cs4341->regcfg.max_register = CS4341_REG_VOLB;295 cs4341->regcfg.cache_type = REGCACHE_FLAT;296 cs4341->regcfg.readable_reg = cs4341_reg_readable;297 cs4341->regcfg.reg_defaults = cs4341_reg_defaults;298 cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults);299 cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg);300 if (IS_ERR(cs4341->regmap))301 return PTR_ERR(cs4341->regmap);302 303 return cs4341_probe(&spi->dev);304}305 306static const struct spi_device_id cs4341_spi_ids[] = {307 { "cs4341a" },308 { }309};310MODULE_DEVICE_TABLE(spi, cs4341_spi_ids);311 312static struct spi_driver cs4341_spi_driver = {313 .driver = {314 .name = "cs4341-spi",315 .of_match_table = of_match_ptr(cs4341_dt_ids),316 },317 .probe = cs4341_spi_probe,318 .id_table = cs4341_spi_ids,319};320#endif321 322static int __init cs4341_init(void)323{324 int ret = 0;325 326#if IS_ENABLED(CONFIG_I2C)327 ret = i2c_add_driver(&cs4341_i2c_driver);328 if (ret)329 return ret;330#endif331#if defined(CONFIG_SPI_MASTER)332 ret = spi_register_driver(&cs4341_spi_driver);333#endif334 335 return ret;336}337module_init(cs4341_init);338 339static void __exit cs4341_exit(void)340{341#if IS_ENABLED(CONFIG_I2C)342 i2c_del_driver(&cs4341_i2c_driver);343#endif344#if defined(CONFIG_SPI_MASTER)345 spi_unregister_driver(&cs4341_spi_driver);346#endif347}348module_exit(cs4341_exit);349 350MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");351MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver");352MODULE_LICENSE("GPL");353