mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
ASoC: codecs: Add uda1342 codec driver
The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA mic inputs), stereo audio DAC, with basic audio processing. Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn> Link: https://patch.msgid.link/927e46b48ca84865a216ce08e7c53df59c2a8c0b.1728459624.git.zhoubinbin@loongson.cn Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
de56743159
commit
de0fb25e37
4 changed files with 435 additions and 0 deletions
|
|
@ -283,6 +283,7 @@ config SND_SOC_ALL_CODECS
|
|||
imply SND_SOC_TWL4030
|
||||
imply SND_SOC_TWL6040
|
||||
imply SND_SOC_UDA1334
|
||||
imply SND_SOC_UDA1342
|
||||
imply SND_SOC_UDA1380
|
||||
imply SND_SOC_WCD9335
|
||||
imply SND_SOC_WCD934X
|
||||
|
|
@ -2131,6 +2132,13 @@ config SND_SOC_UDA1334
|
|||
and has basic features such as de-emphasis (at 44.1 kHz sampling
|
||||
rate) and mute.
|
||||
|
||||
config SND_SOC_UDA1342
|
||||
tristate "NXP UDA1342 CODEC"
|
||||
depends on I2C
|
||||
help
|
||||
The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA
|
||||
mic inputs), stereo audio DAC, with basic audio processing.
|
||||
|
||||
config SND_SOC_UDA1380
|
||||
tristate
|
||||
depends on I2C
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ snd-soc-ts3a227e-y := ts3a227e.o
|
|||
snd-soc-twl4030-y := twl4030.o
|
||||
snd-soc-twl6040-y := twl6040.o
|
||||
snd-soc-uda1334-y := uda1334.o
|
||||
snd-soc-uda1342-y := uda1342.o
|
||||
snd-soc-uda1380-y := uda1380.o
|
||||
snd-soc-wcd-classh-y := wcd-clsh-v2.o
|
||||
snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o
|
||||
|
|
@ -735,6 +736,7 @@ obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o
|
|||
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
|
||||
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
|
||||
obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
|
||||
obj-$(CONFIG_SND_SOC_UDA1342) += snd-soc-uda1342.o
|
||||
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
|
||||
obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o
|
||||
obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
|
||||
|
|
|
|||
347
sound/soc/codecs/uda1342.c
Normal file
347
sound/soc/codecs/uda1342.c
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
// 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 <chripell@evolware.org>
|
||||
// Further cleanup and restructuring by:
|
||||
// Binbin Zhou <zhoubinbin@loongson.cn>
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
|
||||
#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 <chripell@evolware.org>");
|
||||
MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
|
||||
MODULE_LICENSE("GPL");
|
||||
78
sound/soc/codecs/uda1342.h
Normal file
78
sound/soc/codecs/uda1342.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Audio support for NXP UDA1342
|
||||
*
|
||||
* Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
|
||||
* Copyright (c) 2024 Binbin Zhou <zhoubinbin@loongson.cn>
|
||||
*/
|
||||
|
||||
#ifndef _UDA1342_H
|
||||
#define _UDA1342_H
|
||||
|
||||
#define UDA1342_CLK 0x00
|
||||
#define UDA1342_IFACE 0x01
|
||||
#define UDA1342_PM 0x02
|
||||
#define UDA1342_AMIX 0x03
|
||||
#define UDA1342_HP 0x04
|
||||
#define UDA1342_MVOL 0x11
|
||||
#define UDA1342_MIXVOL 0x12
|
||||
#define UDA1342_MODE 0x12
|
||||
#define UDA1342_DEEMP 0x13
|
||||
#define UDA1342_MIXER 0x14
|
||||
#define UDA1342_INTSTAT 0x18
|
||||
#define UDA1342_DEC 0x20
|
||||
#define UDA1342_PGA 0x21
|
||||
#define UDA1342_ADC 0x22
|
||||
#define UDA1342_AGC 0x23
|
||||
#define UDA1342_DECSTAT 0x28
|
||||
#define UDA1342_RESET 0x7f
|
||||
|
||||
/* Register flags */
|
||||
#define R00_EN_ADC 0x0800
|
||||
#define R00_EN_DEC 0x0400
|
||||
#define R00_EN_DAC 0x0200
|
||||
#define R00_EN_INT 0x0100
|
||||
#define R00_DAC_CLK 0x0010
|
||||
#define R01_SFORI_I2S 0x0000
|
||||
#define R01_SFORI_LSB16 0x0100
|
||||
#define R01_SFORI_LSB18 0x0200
|
||||
#define R01_SFORI_LSB20 0x0300
|
||||
#define R01_SFORI_MSB 0x0500
|
||||
#define R01_SFORI_MASK 0x0700
|
||||
#define R01_SFORO_I2S 0x0000
|
||||
#define R01_SFORO_LSB16 0x0001
|
||||
#define R01_SFORO_LSB18 0x0002
|
||||
#define R01_SFORO_LSB20 0x0003
|
||||
#define R01_SFORO_LSB24 0x0004
|
||||
#define R01_SFORO_MSB 0x0005
|
||||
#define R01_SFORO_MASK 0x0007
|
||||
#define R01_SEL_SOURCE 0x0040
|
||||
#define R01_SIM 0x0010
|
||||
#define R02_PON_PLL 0x8000
|
||||
#define R02_PON_HP 0x2000
|
||||
#define R02_PON_DAC 0x0400
|
||||
#define R02_PON_BIAS 0x0100
|
||||
#define R02_EN_AVC 0x0080
|
||||
#define R02_PON_AVC 0x0040
|
||||
#define R02_PON_LNA 0x0010
|
||||
#define R02_PON_PGAL 0x0008
|
||||
#define R02_PON_ADCL 0x0004
|
||||
#define R02_PON_PGAR 0x0002
|
||||
#define R02_PON_ADCR 0x0001
|
||||
#define R13_MTM 0x4000
|
||||
#define R14_SILENCE 0x0080
|
||||
#define R14_SDET_ON 0x0040
|
||||
#define R21_MT_ADC 0x8000
|
||||
#define R22_SEL_LNA 0x0008
|
||||
#define R22_SEL_MIC 0x0004
|
||||
#define R22_SKIP_DCFIL 0x0002
|
||||
#define R23_AGC_EN 0x0001
|
||||
|
||||
#define UDA1342_DAI_DUPLEX 0 /* playback and capture on single DAI */
|
||||
#define UDA1342_DAI_PLAYBACK 1 /* playback DAI */
|
||||
#define UDA1342_DAI_CAPTURE 2 /* capture DAI */
|
||||
|
||||
#define STATUS0_DAIFMT_MASK (~(7 << 1))
|
||||
#define STATUS0_SYSCLK_MASK (~(3 << 4))
|
||||
|
||||
#endif /* _UDA1342_H */
|
||||
Loading…
Add table
Add a link
Reference in a new issue