// SPDX-License-Identifier: GPL-2.0-only // // uda1342.c -- UDA1342 ALSA SoC Codec driver // Based on the WM87xx drivers by Liam Girdwood and Richard Purdie // // Copyright 2007 Dension Audio Systems Ltd. // Copyright 2024 Loongson Technology Co.,Ltd. // // Modifications by Christian Pellegrin // Further cleanup and restructuring by: // Binbin Zhou #include #include #include #include #include #include #include #include #include "uda1342.h" #define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE) struct uda1342_priv { int sysclk; int dai_fmt; struct snd_pcm_substream *provider_substream; struct snd_pcm_substream *consumer_substream; struct regmap *regmap; struct i2c_client *i2c; }; static const struct reg_default uda1342_reg_defaults[] = { { 0x00, 0x1042 }, { 0x01, 0x0000 }, { 0x10, 0x0088 }, { 0x11, 0x0000 }, { 0x12, 0x0000 }, { 0x20, 0x0080 }, { 0x21, 0x0080 }, }; static int uda1342_mute(struct snd_soc_dai *dai, int mute, int direction) { struct snd_soc_component *component = dai->component; struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component); unsigned int mask; unsigned int val = 0; /* Master mute */ mask = BIT(5); if (mute) val = mask; return regmap_update_bits(uda1342->regmap, 0x10, mask, val); } static int uda1342_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *provider_runtime; if (uda1342->provider_substream) { provider_runtime = uda1342->provider_substream->runtime; snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_RATE, provider_runtime->rate); snd_pcm_hw_constraint_single(substream->runtime, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, provider_runtime->sample_bits); uda1342->consumer_substream = substream; } else { uda1342->provider_substream = substream; } return 0; } static void uda1342_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component); if (uda1342->provider_substream == substream) uda1342->provider_substream = uda1342->consumer_substream; uda1342->consumer_substream = NULL; } static int uda1342_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component); struct device *dev = &uda1342->i2c->dev; unsigned int hw_params = 0; if (substream == uda1342->consumer_substream) return 0; /* set SYSCLK / fs ratio */ switch (uda1342->sysclk / params_rate(params)) { case 512: break; case 384: hw_params |= BIT(4); break; case 256: hw_params |= BIT(5); break; default: dev_err(dev, "unsupported frequency\n"); return -EINVAL; } /* set DAI format and word length */ switch (uda1342->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: break; case SND_SOC_DAIFMT_RIGHT_J: switch (params_width(params)) { case 16: hw_params |= BIT(1); break; case 18: hw_params |= BIT(2); break; case 20: hw_params |= BIT(2) | BIT(1); break; default: dev_err(dev, "unsupported format (right)\n"); return -EINVAL; } break; case SND_SOC_DAIFMT_LEFT_J: hw_params |= BIT(3); break; default: dev_err(dev, "unsupported format\n"); return -EINVAL; } return regmap_update_bits(uda1342->regmap, 0x0, STATUS0_DAIFMT_MASK | STATUS0_SYSCLK_MASK, hw_params); } static int uda1342_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_component *component = codec_dai->component; struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component); struct device *dev = &uda1342->i2c->dev; /* * Anything between 256fs*8Khz and 512fs*48Khz should be acceptable * because the codec is slave. Of course limitations of the clock * master (the IIS controller) apply. * We'll error out on set_hw_params if it's not OK */ if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { uda1342->sysclk = freq; return 0; } dev_err(dev, "unsupported sysclk\n"); return -EINVAL; } static int uda1342_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_component *component = codec_dai->component; struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component); /* codec supports only full consumer mode */ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_BC_FC) { dev_err(&uda1342->i2c->dev, "unsupported consumer mode.\n"); return -EINVAL; } /* We can't setup DAI format here as it depends on the word bit num */ /* so let's just store the value for later */ uda1342->dai_fmt = fmt; return 0; } static const struct snd_kcontrol_new uda1342_snd_controls[] = { SOC_SINGLE("Master Playback Volume", 0x11, 0, 0x3F, 1), SOC_SINGLE("Analog1 Volume", 0x12, 0, 0x1F, 1), }; /* Common DAPM widgets */ static const struct snd_soc_dapm_widget uda1342_dapm_widgets[] = { SND_SOC_DAPM_INPUT("VINL1"), SND_SOC_DAPM_INPUT("VINR1"), SND_SOC_DAPM_INPUT("VINL2"), SND_SOC_DAPM_INPUT("VINR2"), SND_SOC_DAPM_DAC("DAC", "Playback", 0, 1, 0), SND_SOC_DAPM_ADC("ADC", "Capture", 0, 9, 0), SND_SOC_DAPM_OUTPUT("VOUTL"), SND_SOC_DAPM_OUTPUT("VOUTR"), }; static const struct snd_soc_dapm_route uda1342_dapm_routes[] = { { "ADC", NULL, "VINL1" }, { "ADC", NULL, "VINR1" }, { "ADC", NULL, "VINL2" }, { "ADC", NULL, "VINR2" }, { "VOUTL", NULL, "DAC" }, { "VOUTR", NULL, "DAC" }, }; static const struct snd_soc_dai_ops uda1342_dai_ops = { .startup = uda1342_startup, .shutdown = uda1342_shutdown, .hw_params = uda1342_hw_params, .mute_stream = uda1342_mute, .set_sysclk = uda1342_set_dai_sysclk, .set_fmt = uda1342_set_dai_fmt, }; static struct snd_soc_dai_driver uda1342_dai = { .name = "uda1342-hifi", /* playback capabilities */ .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = UDA134X_FORMATS, }, /* capture capabilities */ .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = UDA134X_FORMATS, }, /* pcm operations */ .ops = &uda1342_dai_ops, }; static const struct snd_soc_component_driver soc_component_dev_uda1342 = { .controls = uda1342_snd_controls, .num_controls = ARRAY_SIZE(uda1342_snd_controls), .dapm_widgets = uda1342_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(uda1342_dapm_widgets), .dapm_routes = uda1342_dapm_routes, .num_dapm_routes = ARRAY_SIZE(uda1342_dapm_routes), .suspend_bias_off = 1, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, }; static const struct regmap_config uda1342_regmap = { .reg_bits = 8, .val_bits = 16, .max_register = 0x21, .reg_defaults = uda1342_reg_defaults, .num_reg_defaults = ARRAY_SIZE(uda1342_reg_defaults), .cache_type = REGCACHE_MAPLE, }; static int uda1342_i2c_probe(struct i2c_client *i2c) { struct uda1342_priv *uda1342; uda1342 = devm_kzalloc(&i2c->dev, sizeof(*uda1342), GFP_KERNEL); if (!uda1342) return -ENOMEM; uda1342->regmap = devm_regmap_init_i2c(i2c, &uda1342_regmap); if (IS_ERR(uda1342->regmap)) return PTR_ERR(uda1342->regmap); i2c_set_clientdata(i2c, uda1342); uda1342->i2c = i2c; return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_uda1342, &uda1342_dai, 1); } static int uda1342_suspend(struct device *dev) { struct uda1342_priv *uda1342 = dev_get_drvdata(dev); regcache_cache_only(uda1342->regmap, true); return 0; } static int uda1342_resume(struct device *dev) { struct uda1342_priv *uda1342 = dev_get_drvdata(dev); regcache_mark_dirty(uda1342->regmap); regcache_sync(uda1342->regmap); return 0; } static DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops, uda1342_suspend, uda1342_resume, NULL); static const struct i2c_device_id uda1342_i2c_id[] = { { "uda1342", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id); static const struct of_device_id uda1342_of_match[] = { { .compatible = "nxp,uda1342" }, { } }; MODULE_DEVICE_TABLE(of, uda1342_of_match); static struct i2c_driver uda1342_i2c_driver = { .driver = { .name = "uda1342", .of_match_table = uda1342_of_match, .pm = pm_sleep_ptr(&uda1342_pm_ops), }, .probe = uda1342_i2c_probe, .id_table = uda1342_i2c_id, }; module_i2c_driver(uda1342_i2c_driver); MODULE_DESCRIPTION("UDA1342 ALSA soc codec driver"); MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin "); MODULE_AUTHOR("Binbin Zhou "); MODULE_LICENSE("GPL");