1 // SPDX-License-Identifier: GPL-2.0-only
3 * sound/soc/codecs/si476x.c -- Codec driver for SI476X chips
5 * Copyright (C) 2012 Innovative Converged Devices(ICD)
6 * Copyright (C) 2013 Andrey Smirnov
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <sound/pcm.h>
14 #include <sound/pcm_params.h>
15 #include <linux/regmap.h>
16 #include <sound/soc.h>
17 #include <sound/initval.h>
19 #include <linux/i2c.h>
21 #include <linux/mfd/si476x-core.h>
23 enum si476x_audio_registers {
24 SI476X_DIGITAL_IO_OUTPUT_FORMAT = 0x0203,
25 SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE = 0x0202,
28 enum si476x_digital_io_output_format {
29 SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT = 11,
30 SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT = 8,
33 #define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK ((0x7 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
34 (0x7 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
35 #define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK (0x7e)
37 enum si476x_daudio_formats {
38 SI476X_DAUDIO_MODE_I2S = (0x0 << 1),
39 SI476X_DAUDIO_MODE_DSP_A = (0x6 << 1),
40 SI476X_DAUDIO_MODE_DSP_B = (0x7 << 1),
41 SI476X_DAUDIO_MODE_LEFT_J = (0x8 << 1),
42 SI476X_DAUDIO_MODE_RIGHT_J = (0x9 << 1),
44 SI476X_DAUDIO_MODE_IB = (1 << 5),
45 SI476X_DAUDIO_MODE_IF = (1 << 6),
48 enum si476x_pcm_format {
49 SI476X_PCM_FORMAT_S8 = 2,
50 SI476X_PCM_FORMAT_S16_LE = 4,
51 SI476X_PCM_FORMAT_S20_3LE = 5,
52 SI476X_PCM_FORMAT_S24_LE = 6,
55 static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = {
56 SND_SOC_DAPM_OUTPUT("LOUT"),
57 SND_SOC_DAPM_OUTPUT("ROUT"),
60 static const struct snd_soc_dapm_route si476x_dapm_routes[] = {
61 { "Capture", NULL, "LOUT" },
62 { "Capture", NULL, "ROUT" },
65 static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
68 struct si476x_core *core = i2c_mfd_cell_to_core(codec_dai->dev);
72 if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
75 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
76 case SND_SOC_DAIFMT_DSP_A:
77 format |= SI476X_DAUDIO_MODE_DSP_A;
79 case SND_SOC_DAIFMT_DSP_B:
80 format |= SI476X_DAUDIO_MODE_DSP_B;
82 case SND_SOC_DAIFMT_I2S:
83 format |= SI476X_DAUDIO_MODE_I2S;
85 case SND_SOC_DAIFMT_RIGHT_J:
86 format |= SI476X_DAUDIO_MODE_RIGHT_J;
88 case SND_SOC_DAIFMT_LEFT_J:
89 format |= SI476X_DAUDIO_MODE_LEFT_J;
95 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
96 case SND_SOC_DAIFMT_DSP_A:
97 case SND_SOC_DAIFMT_DSP_B:
98 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
99 case SND_SOC_DAIFMT_NB_NF:
101 case SND_SOC_DAIFMT_IB_NF:
102 format |= SI476X_DAUDIO_MODE_IB;
108 case SND_SOC_DAIFMT_I2S:
109 case SND_SOC_DAIFMT_RIGHT_J:
110 case SND_SOC_DAIFMT_LEFT_J:
111 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
112 case SND_SOC_DAIFMT_NB_NF:
114 case SND_SOC_DAIFMT_IB_IF:
115 format |= SI476X_DAUDIO_MODE_IB |
116 SI476X_DAUDIO_MODE_IF;
118 case SND_SOC_DAIFMT_IB_NF:
119 format |= SI476X_DAUDIO_MODE_IB;
121 case SND_SOC_DAIFMT_NB_IF:
122 format |= SI476X_DAUDIO_MODE_IF;
132 si476x_core_lock(core);
134 err = snd_soc_component_update_bits(codec_dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
135 SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK,
138 si476x_core_unlock(core);
141 dev_err(codec_dai->component->dev, "Failed to set output format\n");
148 static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
149 struct snd_pcm_hw_params *params,
150 struct snd_soc_dai *dai)
152 struct si476x_core *core = i2c_mfd_cell_to_core(dai->dev);
153 int rate, width, err;
155 rate = params_rate(params);
156 if (rate < 32000 || rate > 48000) {
157 dev_err(dai->component->dev, "Rate: %d is not supported\n", rate);
161 switch (params_width(params)) {
163 width = SI476X_PCM_FORMAT_S8;
166 width = SI476X_PCM_FORMAT_S16_LE;
169 width = SI476X_PCM_FORMAT_S20_3LE;
172 width = SI476X_PCM_FORMAT_S24_LE;
178 si476x_core_lock(core);
180 err = snd_soc_component_write(dai->component, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
183 dev_err(dai->component->dev, "Failed to set sample rate\n");
187 err = snd_soc_component_update_bits(dai->component, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
188 SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK,
189 (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) |
190 (width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT));
192 dev_err(dai->component->dev, "Failed to set output width\n");
197 si476x_core_unlock(core);
202 static const struct snd_soc_dai_ops si476x_dai_ops = {
203 .hw_params = si476x_codec_hw_params,
204 .set_fmt = si476x_codec_set_dai_fmt,
207 static struct snd_soc_dai_driver si476x_dai = {
208 .name = "si476x-codec",
210 .stream_name = "Capture",
214 .rates = SNDRV_PCM_RATE_32000 |
215 SNDRV_PCM_RATE_44100 |
216 SNDRV_PCM_RATE_48000,
217 .formats = SNDRV_PCM_FMTBIT_S8 |
218 SNDRV_PCM_FMTBIT_S16_LE |
219 SNDRV_PCM_FMTBIT_S20_3LE |
220 SNDRV_PCM_FMTBIT_S24_LE
222 .ops = &si476x_dai_ops,
225 static int si476x_probe(struct snd_soc_component *component)
227 snd_soc_component_init_regmap(component,
228 dev_get_regmap(component->dev->parent, NULL));
233 static const struct snd_soc_component_driver soc_component_dev_si476x = {
234 .probe = si476x_probe,
235 .dapm_widgets = si476x_dapm_widgets,
236 .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
237 .dapm_routes = si476x_dapm_routes,
238 .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
240 .use_pmdown_time = 1,
242 .non_legacy_dai_naming = 1,
245 static int si476x_platform_probe(struct platform_device *pdev)
247 return devm_snd_soc_register_component(&pdev->dev,
248 &soc_component_dev_si476x,
252 MODULE_ALIAS("platform:si476x-codec");
254 static struct platform_driver si476x_platform_driver = {
256 .name = "si476x-codec",
258 .probe = si476x_platform_probe,
260 module_platform_driver(si476x_platform_driver);
263 MODULE_DESCRIPTION("ASoC Si4761/64 codec driver");
264 MODULE_LICENSE("GPL");