xref: /linux/sound/soc/codecs/si476x.c (revision 7a79e94e973639da7bf1b8242d504f9db9e5e848)
1330345ebSAndrey Smirnov #include <linux/module.h>
2330345ebSAndrey Smirnov #include <linux/slab.h>
3330345ebSAndrey Smirnov #include <sound/pcm.h>
4330345ebSAndrey Smirnov #include <sound/pcm_params.h>
5330345ebSAndrey Smirnov #include <sound/soc.h>
6330345ebSAndrey Smirnov #include <sound/initval.h>
7330345ebSAndrey Smirnov 
8330345ebSAndrey Smirnov #include <linux/i2c.h>
9330345ebSAndrey Smirnov 
10330345ebSAndrey Smirnov #include <linux/mfd/si476x-core.h>
11330345ebSAndrey Smirnov 
12330345ebSAndrey Smirnov enum si476x_audio_registers {
13330345ebSAndrey Smirnov 	SI476X_DIGITAL_IO_OUTPUT_FORMAT		= 0x0203,
14330345ebSAndrey Smirnov 	SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE	= 0x0202,
15330345ebSAndrey Smirnov };
16330345ebSAndrey Smirnov 
17330345ebSAndrey Smirnov enum si476x_digital_io_output_format {
18330345ebSAndrey Smirnov 	SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT	= 11,
19330345ebSAndrey Smirnov 	SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT	= 8,
20330345ebSAndrey Smirnov };
21330345ebSAndrey Smirnov 
22330345ebSAndrey Smirnov #define SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK	((0b111 << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) | \
23330345ebSAndrey Smirnov 						  (0b111 << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT))
24330345ebSAndrey Smirnov #define SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK	(0b1111110)
25330345ebSAndrey Smirnov 
26330345ebSAndrey Smirnov enum si476x_daudio_formats {
27330345ebSAndrey Smirnov 	SI476X_DAUDIO_MODE_I2S		= (0x0 << 1),
28330345ebSAndrey Smirnov 	SI476X_DAUDIO_MODE_DSP_A	= (0x6 << 1),
29330345ebSAndrey Smirnov 	SI476X_DAUDIO_MODE_DSP_B	= (0x7 << 1),
30330345ebSAndrey Smirnov 	SI476X_DAUDIO_MODE_LEFT_J	= (0x8 << 1),
31330345ebSAndrey Smirnov 	SI476X_DAUDIO_MODE_RIGHT_J	= (0x9 << 1),
32330345ebSAndrey Smirnov 
33330345ebSAndrey Smirnov 	SI476X_DAUDIO_MODE_IB		= (1 << 5),
34330345ebSAndrey Smirnov 	SI476X_DAUDIO_MODE_IF		= (1 << 6),
35330345ebSAndrey Smirnov };
36330345ebSAndrey Smirnov 
37330345ebSAndrey Smirnov enum si476x_pcm_format {
38330345ebSAndrey Smirnov 	SI476X_PCM_FORMAT_S8		= 2,
39330345ebSAndrey Smirnov 	SI476X_PCM_FORMAT_S16_LE	= 4,
40330345ebSAndrey Smirnov 	SI476X_PCM_FORMAT_S20_3LE	= 5,
41330345ebSAndrey Smirnov 	SI476X_PCM_FORMAT_S24_LE	= 6,
42330345ebSAndrey Smirnov };
43330345ebSAndrey Smirnov 
44330345ebSAndrey Smirnov static unsigned int si476x_codec_read(struct snd_soc_codec *codec,
45330345ebSAndrey Smirnov 				      unsigned int reg)
46330345ebSAndrey Smirnov {
47330345ebSAndrey Smirnov 	int err;
48330345ebSAndrey Smirnov 	struct si476x_core *core = codec->control_data;
49330345ebSAndrey Smirnov 
50330345ebSAndrey Smirnov 	si476x_core_lock(core);
51330345ebSAndrey Smirnov 	err = si476x_core_cmd_get_property(core, reg);
52330345ebSAndrey Smirnov 	si476x_core_unlock(core);
53330345ebSAndrey Smirnov 
54330345ebSAndrey Smirnov 	return err;
55330345ebSAndrey Smirnov }
56330345ebSAndrey Smirnov 
57330345ebSAndrey Smirnov static int si476x_codec_write(struct snd_soc_codec *codec,
58330345ebSAndrey Smirnov 			      unsigned int reg, unsigned int val)
59330345ebSAndrey Smirnov {
60330345ebSAndrey Smirnov 	int err;
61330345ebSAndrey Smirnov 	struct si476x_core *core = codec->control_data;
62330345ebSAndrey Smirnov 
63330345ebSAndrey Smirnov 	si476x_core_lock(core);
64330345ebSAndrey Smirnov 	err = si476x_core_cmd_set_property(core, reg, val);
65330345ebSAndrey Smirnov 	si476x_core_unlock(core);
66330345ebSAndrey Smirnov 
67330345ebSAndrey Smirnov 	return err;
68330345ebSAndrey Smirnov }
69330345ebSAndrey Smirnov 
70330345ebSAndrey Smirnov static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
71330345ebSAndrey Smirnov 				    unsigned int fmt)
72330345ebSAndrey Smirnov {
73330345ebSAndrey Smirnov 	int err;
74330345ebSAndrey Smirnov 	u16 format = 0;
75330345ebSAndrey Smirnov 
76330345ebSAndrey Smirnov 	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
77330345ebSAndrey Smirnov 		return -EINVAL;
78330345ebSAndrey Smirnov 
79330345ebSAndrey Smirnov 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
80330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_DSP_A:
81330345ebSAndrey Smirnov 		format |= SI476X_DAUDIO_MODE_DSP_A;
82330345ebSAndrey Smirnov 		break;
83330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_DSP_B:
84330345ebSAndrey Smirnov 		format |= SI476X_DAUDIO_MODE_DSP_B;
85330345ebSAndrey Smirnov 		break;
86330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_I2S:
87330345ebSAndrey Smirnov 		format |= SI476X_DAUDIO_MODE_I2S;
88330345ebSAndrey Smirnov 		break;
89330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_RIGHT_J:
90330345ebSAndrey Smirnov 		format |= SI476X_DAUDIO_MODE_RIGHT_J;
91330345ebSAndrey Smirnov 		break;
92330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_LEFT_J:
93330345ebSAndrey Smirnov 		format |= SI476X_DAUDIO_MODE_LEFT_J;
94330345ebSAndrey Smirnov 		break;
95330345ebSAndrey Smirnov 	default:
96330345ebSAndrey Smirnov 		return -EINVAL;
97330345ebSAndrey Smirnov 	}
98330345ebSAndrey Smirnov 
99330345ebSAndrey Smirnov 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
100330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_DSP_A:
101330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_DSP_B:
102330345ebSAndrey Smirnov 		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
103330345ebSAndrey Smirnov 		case SND_SOC_DAIFMT_NB_NF:
104330345ebSAndrey Smirnov 			break;
105330345ebSAndrey Smirnov 		case SND_SOC_DAIFMT_IB_NF:
106330345ebSAndrey Smirnov 			format |= SI476X_DAUDIO_MODE_IB;
107330345ebSAndrey Smirnov 			break;
108330345ebSAndrey Smirnov 		default:
109330345ebSAndrey Smirnov 			return -EINVAL;
110330345ebSAndrey Smirnov 		}
111330345ebSAndrey Smirnov 		break;
112330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_I2S:
113330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_RIGHT_J:
114330345ebSAndrey Smirnov 	case SND_SOC_DAIFMT_LEFT_J:
115330345ebSAndrey Smirnov 		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
116330345ebSAndrey Smirnov 		case SND_SOC_DAIFMT_NB_NF:
117330345ebSAndrey Smirnov 			break;
118330345ebSAndrey Smirnov 		case SND_SOC_DAIFMT_IB_IF:
119330345ebSAndrey Smirnov 			format |= SI476X_DAUDIO_MODE_IB |
120330345ebSAndrey Smirnov 				SI476X_DAUDIO_MODE_IF;
121330345ebSAndrey Smirnov 			break;
122330345ebSAndrey Smirnov 		case SND_SOC_DAIFMT_IB_NF:
123330345ebSAndrey Smirnov 			format |= SI476X_DAUDIO_MODE_IB;
124330345ebSAndrey Smirnov 			break;
125330345ebSAndrey Smirnov 		case SND_SOC_DAIFMT_NB_IF:
126330345ebSAndrey Smirnov 			format |= SI476X_DAUDIO_MODE_IF;
127330345ebSAndrey Smirnov 			break;
128330345ebSAndrey Smirnov 		default:
129330345ebSAndrey Smirnov 			return -EINVAL;
130330345ebSAndrey Smirnov 		}
131330345ebSAndrey Smirnov 		break;
132330345ebSAndrey Smirnov 	default:
133330345ebSAndrey Smirnov 		return -EINVAL;
134330345ebSAndrey Smirnov 	}
135330345ebSAndrey Smirnov 
136330345ebSAndrey Smirnov 	err = snd_soc_update_bits(codec_dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
137330345ebSAndrey Smirnov 				  SI476X_DIGITAL_IO_OUTPUT_FORMAT_MASK,
138330345ebSAndrey Smirnov 				  format);
139330345ebSAndrey Smirnov 	if (err < 0) {
140330345ebSAndrey Smirnov 		dev_err(codec_dai->codec->dev, "Failed to set output format\n");
141330345ebSAndrey Smirnov 		return err;
142330345ebSAndrey Smirnov 	}
143330345ebSAndrey Smirnov 
144330345ebSAndrey Smirnov 	return 0;
145330345ebSAndrey Smirnov }
146330345ebSAndrey Smirnov 
147330345ebSAndrey Smirnov static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
148330345ebSAndrey Smirnov 				  struct snd_pcm_hw_params *params,
149330345ebSAndrey Smirnov 				  struct snd_soc_dai *dai)
150330345ebSAndrey Smirnov {
151330345ebSAndrey Smirnov 	int rate, width, err;
152330345ebSAndrey Smirnov 
153330345ebSAndrey Smirnov 	rate = params_rate(params);
154330345ebSAndrey Smirnov 	if (rate < 32000 || rate > 48000) {
155330345ebSAndrey Smirnov 		dev_err(dai->codec->dev, "Rate: %d is not supported\n", rate);
156330345ebSAndrey Smirnov 		return -EINVAL;
157330345ebSAndrey Smirnov 	}
158330345ebSAndrey Smirnov 
159330345ebSAndrey Smirnov 	switch (params_format(params)) {
160330345ebSAndrey Smirnov 	case SNDRV_PCM_FORMAT_S8:
161330345ebSAndrey Smirnov 		width = SI476X_PCM_FORMAT_S8;
162330345ebSAndrey Smirnov 	case SNDRV_PCM_FORMAT_S16_LE:
163330345ebSAndrey Smirnov 		width = SI476X_PCM_FORMAT_S16_LE;
164330345ebSAndrey Smirnov 		break;
165330345ebSAndrey Smirnov 	case SNDRV_PCM_FORMAT_S20_3LE:
166330345ebSAndrey Smirnov 		width = SI476X_PCM_FORMAT_S20_3LE;
167330345ebSAndrey Smirnov 		break;
168330345ebSAndrey Smirnov 	case SNDRV_PCM_FORMAT_S24_LE:
169330345ebSAndrey Smirnov 		width = SI476X_PCM_FORMAT_S24_LE;
170330345ebSAndrey Smirnov 		break;
171330345ebSAndrey Smirnov 	default:
172330345ebSAndrey Smirnov 		return -EINVAL;
173330345ebSAndrey Smirnov 	}
174330345ebSAndrey Smirnov 
175330345ebSAndrey Smirnov 	err = snd_soc_write(dai->codec, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
176330345ebSAndrey Smirnov 			    rate);
177330345ebSAndrey Smirnov 	if (err < 0) {
178330345ebSAndrey Smirnov 		dev_err(dai->codec->dev, "Failed to set sample rate\n");
179330345ebSAndrey Smirnov 		return err;
180330345ebSAndrey Smirnov 	}
181330345ebSAndrey Smirnov 
182330345ebSAndrey Smirnov 	err = snd_soc_update_bits(dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
183330345ebSAndrey Smirnov 				  SI476X_DIGITAL_IO_OUTPUT_WIDTH_MASK,
184330345ebSAndrey Smirnov 				  (width << SI476X_DIGITAL_IO_SLOT_SIZE_SHIFT) |
185330345ebSAndrey Smirnov 				  (width << SI476X_DIGITAL_IO_SAMPLE_SIZE_SHIFT));
186330345ebSAndrey Smirnov 	if (err < 0) {
187330345ebSAndrey Smirnov 		dev_err(dai->codec->dev, "Failed to set output width\n");
188330345ebSAndrey Smirnov 		return err;
189330345ebSAndrey Smirnov 	}
190330345ebSAndrey Smirnov 
191330345ebSAndrey Smirnov 	return 0;
192330345ebSAndrey Smirnov }
193330345ebSAndrey Smirnov 
194330345ebSAndrey Smirnov static int si476x_codec_probe(struct snd_soc_codec *codec)
195330345ebSAndrey Smirnov {
196330345ebSAndrey Smirnov 	codec->control_data = i2c_mfd_cell_to_core(codec->dev);
197330345ebSAndrey Smirnov 	return 0;
198330345ebSAndrey Smirnov }
199330345ebSAndrey Smirnov 
200330345ebSAndrey Smirnov static struct snd_soc_dai_ops si476x_dai_ops = {
201330345ebSAndrey Smirnov 	.hw_params	= si476x_codec_hw_params,
202330345ebSAndrey Smirnov 	.set_fmt	= si476x_codec_set_dai_fmt,
203330345ebSAndrey Smirnov };
204330345ebSAndrey Smirnov 
205330345ebSAndrey Smirnov static struct snd_soc_dai_driver si476x_dai = {
206330345ebSAndrey Smirnov 	.name		= "si476x-codec",
207330345ebSAndrey Smirnov 	.capture	= {
208330345ebSAndrey Smirnov 		.stream_name	= "Capture",
209330345ebSAndrey Smirnov 		.channels_min	= 2,
210330345ebSAndrey Smirnov 		.channels_max	= 2,
211330345ebSAndrey Smirnov 
212330345ebSAndrey Smirnov 		.rates = SNDRV_PCM_RATE_32000 |
213330345ebSAndrey Smirnov 		SNDRV_PCM_RATE_44100 |
214330345ebSAndrey Smirnov 		SNDRV_PCM_RATE_48000,
215330345ebSAndrey Smirnov 		.formats = SNDRV_PCM_FMTBIT_S8 |
216330345ebSAndrey Smirnov 		SNDRV_PCM_FMTBIT_S16_LE |
217330345ebSAndrey Smirnov 		SNDRV_PCM_FMTBIT_S20_3LE |
218330345ebSAndrey Smirnov 		SNDRV_PCM_FMTBIT_S24_LE
219330345ebSAndrey Smirnov 	},
220330345ebSAndrey Smirnov 	.ops		= &si476x_dai_ops,
221330345ebSAndrey Smirnov };
222330345ebSAndrey Smirnov 
223330345ebSAndrey Smirnov static struct snd_soc_codec_driver soc_codec_dev_si476x = {
224330345ebSAndrey Smirnov 	.probe  = si476x_codec_probe,
225330345ebSAndrey Smirnov 	.read   = si476x_codec_read,
226330345ebSAndrey Smirnov 	.write  = si476x_codec_write,
227330345ebSAndrey Smirnov };
228330345ebSAndrey Smirnov 
229*7a79e94eSBill Pemberton static int si476x_platform_probe(struct platform_device *pdev)
230330345ebSAndrey Smirnov {
231330345ebSAndrey Smirnov 	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_si476x,
232330345ebSAndrey Smirnov 				      &si476x_dai, 1);
233330345ebSAndrey Smirnov }
234330345ebSAndrey Smirnov 
235*7a79e94eSBill Pemberton static int si476x_platform_remove(struct platform_device *pdev)
236330345ebSAndrey Smirnov {
237330345ebSAndrey Smirnov 	snd_soc_unregister_codec(&pdev->dev);
238330345ebSAndrey Smirnov 	return 0;
239330345ebSAndrey Smirnov }
240330345ebSAndrey Smirnov 
241330345ebSAndrey Smirnov MODULE_ALIAS("platform:si476x-codec");
242330345ebSAndrey Smirnov 
243330345ebSAndrey Smirnov static struct platform_driver si476x_platform_driver = {
244330345ebSAndrey Smirnov 	.driver		= {
245330345ebSAndrey Smirnov 		.name	= "si476x-codec",
246330345ebSAndrey Smirnov 		.owner	= THIS_MODULE,
247330345ebSAndrey Smirnov 	},
248330345ebSAndrey Smirnov 	.probe		= si476x_platform_probe,
249*7a79e94eSBill Pemberton 	.remove		= si476x_platform_remove,
250330345ebSAndrey Smirnov };
251330345ebSAndrey Smirnov module_platform_driver(si476x_platform_driver);
252330345ebSAndrey Smirnov 
253330345ebSAndrey Smirnov MODULE_AUTHOR("Andrey Smirnov <andrey.smirnov@convergeddevices.net>");
254330345ebSAndrey Smirnov MODULE_DESCRIPTION("ASoC Si4761/64 codec driver");
255330345ebSAndrey Smirnov MODULE_LICENSE("GPL");
256