Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2//
  3// uda1342.c  --  UDA1342 ALSA SoC Codec driver
  4// Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
  5//
  6// Copyright 2007 Dension Audio Systems Ltd.
  7// Copyright 2024 Loongson Technology Co.,Ltd.
  8//
  9// Modifications by Christian Pellegrin <chripell@evolware.org>
 10// Further cleanup and restructuring by:
 11//         Binbin Zhou <zhoubinbin@loongson.cn>
 12
 13#include <linux/module.h>
 14#include <linux/i2c.h>
 15#include <sound/core.h>
 16#include <sound/pcm.h>
 17#include <sound/pcm_params.h>
 18#include <linux/pm_runtime.h>
 19#include <sound/soc.h>
 20#include <sound/tlv.h>
 21
 22#include "uda1342.h"
 23
 24#define UDA134X_FORMATS	(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
 25			 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
 26
 27struct uda1342_priv {
 28	int sysclk;
 29	int dai_fmt;
 30
 31	struct snd_pcm_substream *provider_substream;
 32	struct snd_pcm_substream *consumer_substream;
 33
 34	struct regmap *regmap;
 35	struct i2c_client *i2c;
 36};
 37
 38static const struct reg_default uda1342_reg_defaults[] = {
 39	{ 0x00, 0x1042 },
 40	{ 0x01, 0x0000 },
 41	{ 0x10, 0x0088 },
 42	{ 0x11, 0x0000 },
 43	{ 0x12, 0x0000 },
 44	{ 0x20, 0x0080 },
 45	{ 0x21, 0x0080 },
 46};
 47
 48static int uda1342_mute(struct snd_soc_dai *dai, int mute, int direction)
 49{
 50	struct snd_soc_component *component = dai->component;
 51	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
 52	unsigned int mask;
 53	unsigned int val = 0;
 54
 55	/* Master mute */
 56	mask = BIT(5);
 57	if (mute)
 58		val = mask;
 59
 60	return regmap_update_bits(uda1342->regmap, 0x10, mask, val);
 61}
 62
 63static int uda1342_startup(struct snd_pcm_substream *substream,
 64			   struct snd_soc_dai *dai)
 65{
 66	struct snd_soc_component *component = dai->component;
 67	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
 68	struct snd_pcm_runtime *provider_runtime;
 69
 70	if (uda1342->provider_substream) {
 71		provider_runtime = uda1342->provider_substream->runtime;
 72
 73		snd_pcm_hw_constraint_single(substream->runtime,
 74					     SNDRV_PCM_HW_PARAM_RATE, provider_runtime->rate);
 75		snd_pcm_hw_constraint_single(substream->runtime,
 76					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
 77					     provider_runtime->sample_bits);
 78
 79		uda1342->consumer_substream = substream;
 80	} else {
 81		uda1342->provider_substream = substream;
 82	}
 83
 84	return 0;
 85}
 86
 87static void uda1342_shutdown(struct snd_pcm_substream *substream,
 88			     struct snd_soc_dai *dai)
 89{
 90	struct snd_soc_component *component = dai->component;
 91	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
 92
 93	if (uda1342->provider_substream == substream)
 94		uda1342->provider_substream = uda1342->consumer_substream;
 95
 96	uda1342->consumer_substream = NULL;
 97}
 98
 99static int uda1342_hw_params(struct snd_pcm_substream *substream,
100			     struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
101{
102	struct snd_soc_component *component = dai->component;
103	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
104	struct device *dev = &uda1342->i2c->dev;
105	unsigned int hw_params = 0;
106
107	if (substream == uda1342->consumer_substream)
108		return 0;
109
110	/* set SYSCLK / fs ratio */
111	switch (uda1342->sysclk / params_rate(params)) {
112	case 512:
113		break;
114	case 384:
115		hw_params |= BIT(4);
116		break;
117	case 256:
118		hw_params |= BIT(5);
119		break;
120	default:
121		dev_err(dev, "unsupported frequency\n");
122		return -EINVAL;
123	}
124
125	/* set DAI format and word length */
126	switch (uda1342->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
127	case SND_SOC_DAIFMT_I2S:
128		break;
129	case SND_SOC_DAIFMT_RIGHT_J:
130		switch (params_width(params)) {
131		case 16:
132			hw_params |= BIT(1);
133			break;
134		case 18:
135			hw_params |= BIT(2);
136			break;
137		case 20:
138			hw_params |= BIT(2) | BIT(1);
139			break;
140		default:
141			dev_err(dev, "unsupported format (right)\n");
142			return -EINVAL;
143		}
144		break;
145	case SND_SOC_DAIFMT_LEFT_J:
146		hw_params |= BIT(3);
147		break;
148	default:
149		dev_err(dev, "unsupported format\n");
150		return -EINVAL;
151	}
152
153	return regmap_update_bits(uda1342->regmap, 0x0,
154				  STATUS0_DAIFMT_MASK | STATUS0_SYSCLK_MASK, hw_params);
155}
156
157static int uda1342_set_dai_sysclk(struct snd_soc_dai *codec_dai,
158				  int clk_id, unsigned int freq, int dir)
159{
160	struct snd_soc_component *component = codec_dai->component;
161	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
162	struct device *dev = &uda1342->i2c->dev;
163
164	/*
165	 * Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
166	 * because the codec is slave. Of course limitations of the clock
167	 * master (the IIS controller) apply.
168	 * We'll error out on set_hw_params if it's not OK
169	 */
170	if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
171		uda1342->sysclk = freq;
172		return 0;
173	}
174
175	dev_err(dev, "unsupported sysclk\n");
176
177	return -EINVAL;
178}
179
180static int uda1342_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
181{
182	struct snd_soc_component *component = codec_dai->component;
183	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
184
185	/* codec supports only full consumer mode */
186	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_BC_FC) {
187		dev_err(&uda1342->i2c->dev, "unsupported consumer mode.\n");
188		return -EINVAL;
189	}
190
191	/* We can't setup DAI format here as it depends on the word bit num */
192	/* so let's just store the value for later */
193	uda1342->dai_fmt = fmt;
194
195	return 0;
196}
197
198static const struct snd_kcontrol_new uda1342_snd_controls[] = {
199	SOC_SINGLE("Master Playback Volume", 0x11, 0, 0x3F, 1),
200	SOC_SINGLE("Analog1 Volume", 0x12, 0, 0x1F, 1),
201};
202
203/* Common DAPM widgets */
204static const struct snd_soc_dapm_widget uda1342_dapm_widgets[] = {
205	SND_SOC_DAPM_INPUT("VINL1"),
206	SND_SOC_DAPM_INPUT("VINR1"),
207	SND_SOC_DAPM_INPUT("VINL2"),
208	SND_SOC_DAPM_INPUT("VINR2"),
209
210	SND_SOC_DAPM_DAC("DAC", "Playback", 0, 1, 0),
211	SND_SOC_DAPM_ADC("ADC", "Capture", 0, 9, 0),
212
213	SND_SOC_DAPM_OUTPUT("VOUTL"),
214	SND_SOC_DAPM_OUTPUT("VOUTR"),
215};
216
217static const struct snd_soc_dapm_route uda1342_dapm_routes[] = {
218	{ "ADC", NULL, "VINL1" },
219	{ "ADC", NULL, "VINR1" },
220	{ "ADC", NULL, "VINL2" },
221	{ "ADC", NULL, "VINR2" },
222	{ "VOUTL", NULL, "DAC" },
223	{ "VOUTR", NULL, "DAC" },
224};
225
226static const struct snd_soc_dai_ops uda1342_dai_ops = {
227	.startup	= uda1342_startup,
228	.shutdown	= uda1342_shutdown,
229	.hw_params	= uda1342_hw_params,
230	.mute_stream	= uda1342_mute,
231	.set_sysclk	= uda1342_set_dai_sysclk,
232	.set_fmt	= uda1342_set_dai_fmt,
233};
234
235static struct snd_soc_dai_driver uda1342_dai = {
236	.name = "uda1342-hifi",
237	/* playback capabilities */
238	.playback = {
239		.stream_name = "Playback",
240		.channels_min = 1,
241		.channels_max = 2,
242		.rates = SNDRV_PCM_RATE_8000_48000,
243		.formats = UDA134X_FORMATS,
244	},
245	/* capture capabilities */
246	.capture = {
247		.stream_name = "Capture",
248		.channels_min = 1,
249		.channels_max = 2,
250		.rates = SNDRV_PCM_RATE_8000_48000,
251		.formats = UDA134X_FORMATS,
252	},
253	/* pcm operations */
254	.ops = &uda1342_dai_ops,
255};
256
257static const struct snd_soc_component_driver soc_component_dev_uda1342 = {
258	.controls		= uda1342_snd_controls,
259	.num_controls		= ARRAY_SIZE(uda1342_snd_controls),
260	.dapm_widgets		= uda1342_dapm_widgets,
261	.num_dapm_widgets	= ARRAY_SIZE(uda1342_dapm_widgets),
262	.dapm_routes		= uda1342_dapm_routes,
263	.num_dapm_routes	= ARRAY_SIZE(uda1342_dapm_routes),
264	.suspend_bias_off	= 1,
265	.idle_bias_on		= 1,
266	.use_pmdown_time	= 1,
267	.endianness		= 1,
268};
269
270static const struct regmap_config uda1342_regmap = {
271	.reg_bits = 8,
272	.val_bits = 16,
273	.max_register = 0x21,
274	.reg_defaults = uda1342_reg_defaults,
275	.num_reg_defaults = ARRAY_SIZE(uda1342_reg_defaults),
276	.cache_type = REGCACHE_MAPLE,
277};
278
279static int uda1342_i2c_probe(struct i2c_client *i2c)
280{
281	struct uda1342_priv *uda1342;
282
283	uda1342 = devm_kzalloc(&i2c->dev, sizeof(*uda1342), GFP_KERNEL);
284	if (!uda1342)
285		return -ENOMEM;
286
287	uda1342->regmap = devm_regmap_init_i2c(i2c, &uda1342_regmap);
288	if (IS_ERR(uda1342->regmap))
289		return PTR_ERR(uda1342->regmap);
290
291	i2c_set_clientdata(i2c, uda1342);
292	uda1342->i2c = i2c;
293
294	return devm_snd_soc_register_component(&i2c->dev,
295					       &soc_component_dev_uda1342,
296					       &uda1342_dai, 1);
297}
298
299static int uda1342_suspend(struct device *dev)
300{
301	struct uda1342_priv *uda1342 = dev_get_drvdata(dev);
302
303	regcache_cache_only(uda1342->regmap, true);
304
305	return 0;
306}
307
308static int uda1342_resume(struct device *dev)
309{
310	struct uda1342_priv *uda1342 = dev_get_drvdata(dev);
311
312	regcache_mark_dirty(uda1342->regmap);
313	regcache_sync(uda1342->regmap);
314
315	return 0;
316}
317
318static DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops,
319				 uda1342_suspend, uda1342_resume, NULL);
320
321static const struct i2c_device_id uda1342_i2c_id[] = {
322	 { "uda1342", 0 },
323	 { }
324};
325MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id);
326
327static const struct of_device_id uda1342_of_match[] = {
328	 { .compatible = "nxp,uda1342" },
329	 { }
330};
331MODULE_DEVICE_TABLE(of, uda1342_of_match);
332
333static struct i2c_driver uda1342_i2c_driver = {
334	.driver = {
335		.name =  "uda1342",
336		.of_match_table = uda1342_of_match,
337		.pm = pm_sleep_ptr(&uda1342_pm_ops),
338	 },
339	.probe = uda1342_i2c_probe,
340	.id_table = uda1342_i2c_id,
341};
342module_i2c_driver(uda1342_i2c_driver);
343
344MODULE_DESCRIPTION("UDA1342 ALSA soc codec driver");
345MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
346MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
347MODULE_LICENSE("GPL");