xref: /openbmc/linux/sound/soc/codecs/jz4740.c (revision a484a9a3)
1 /*
2  * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  *  You should have received a copy of the  GNU General Public License along
9  *  with this program; if not, write  to the Free Software Foundation, Inc.,
10  *  675 Mass Ave, Cambridge, MA 02139, USA.
11  *
12  */
13 
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/slab.h>
18 #include <linux/io.h>
19 
20 #include <linux/delay.h>
21 
22 #include <sound/core.h>
23 #include <sound/pcm.h>
24 #include <sound/pcm_params.h>
25 #include <sound/initval.h>
26 #include <sound/soc.h>
27 #include <sound/tlv.h>
28 
29 #define JZ4740_REG_CODEC_1 0x0
30 #define JZ4740_REG_CODEC_2 0x1
31 
32 #define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
33 #define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
34 #define JZ4740_CODEC_1_SW1_ENABLE BIT(27)
35 #define JZ4740_CODEC_1_ADC_ENABLE BIT(26)
36 #define JZ4740_CODEC_1_SW2_ENABLE BIT(25)
37 #define JZ4740_CODEC_1_DAC_ENABLE BIT(24)
38 #define JZ4740_CODEC_1_VREF_DISABLE BIT(20)
39 #define JZ4740_CODEC_1_VREF_AMP_DISABLE BIT(19)
40 #define JZ4740_CODEC_1_VREF_PULLDOWN BIT(18)
41 #define JZ4740_CODEC_1_VREF_LOW_CURRENT BIT(17)
42 #define JZ4740_CODEC_1_VREF_HIGH_CURRENT BIT(16)
43 #define JZ4740_CODEC_1_HEADPHONE_DISABLE BIT(14)
44 #define JZ4740_CODEC_1_HEADPHONE_AMP_CHANGE_ANY BIT(13)
45 #define JZ4740_CODEC_1_HEADPHONE_CHARGE BIT(12)
46 #define JZ4740_CODEC_1_HEADPHONE_PULLDOWN (BIT(11) | BIT(10))
47 #define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M BIT(9)
48 #define JZ4740_CODEC_1_HEADPHONE_POWERDOWN BIT(8)
49 #define JZ4740_CODEC_1_SUSPEND BIT(1)
50 #define JZ4740_CODEC_1_RESET BIT(0)
51 
52 #define JZ4740_CODEC_1_LINE_ENABLE_OFFSET 29
53 #define JZ4740_CODEC_1_MIC_ENABLE_OFFSET 28
54 #define JZ4740_CODEC_1_SW1_ENABLE_OFFSET 27
55 #define JZ4740_CODEC_1_ADC_ENABLE_OFFSET 26
56 #define JZ4740_CODEC_1_SW2_ENABLE_OFFSET 25
57 #define JZ4740_CODEC_1_DAC_ENABLE_OFFSET 24
58 #define JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET 14
59 #define JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET 8
60 
61 #define JZ4740_CODEC_2_INPUT_VOLUME_MASK		0x1f0000
62 #define JZ4740_CODEC_2_SAMPLE_RATE_MASK			0x000f00
63 #define JZ4740_CODEC_2_MIC_BOOST_GAIN_MASK		0x000030
64 #define JZ4740_CODEC_2_HEADPHONE_VOLUME_MASK	0x000003
65 
66 #define JZ4740_CODEC_2_INPUT_VOLUME_OFFSET		16
67 #define JZ4740_CODEC_2_SAMPLE_RATE_OFFSET		 8
68 #define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET	 4
69 #define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET	 0
70 
71 static const uint32_t jz4740_codec_regs[] = {
72 	0x021b2302, 0x00170803,
73 };
74 
75 struct jz4740_codec {
76 	void __iomem *base;
77 	struct resource *mem;
78 };
79 
80 static unsigned int jz4740_codec_read(struct snd_soc_codec *codec,
81 	unsigned int reg)
82 {
83 	struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
84 	return readl(jz4740_codec->base + (reg << 2));
85 }
86 
87 static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
88 	unsigned int val)
89 {
90 	struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
91 	u32 *cache = codec->reg_cache;
92 
93 	cache[reg] = val;
94 	writel(val, jz4740_codec->base + (reg << 2));
95 
96 	return 0;
97 }
98 
99 static const unsigned int jz4740_mic_tlv[] = {
100 	TLV_DB_RANGE_HEAD(2),
101 	0, 2, TLV_DB_SCALE_ITEM(0, 600, 0),
102 	3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0),
103 };
104 
105 static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0);
106 static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0);
107 
108 static const struct snd_kcontrol_new jz4740_codec_controls[] = {
109 	SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2,
110 			JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0,
111 			jz4740_out_tlv),
112 	SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2,
113 			JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0,
114 			jz4740_in_tlv),
115 	SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
116 			JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
117 	SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2,
118 			JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0,
119 			jz4740_mic_tlv),
120 };
121 
122 static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
123 	SOC_DAPM_SINGLE("Bypass Switch", JZ4740_REG_CODEC_1,
124 			JZ4740_CODEC_1_SW1_ENABLE_OFFSET, 1, 0),
125 	SOC_DAPM_SINGLE("DAC Switch", JZ4740_REG_CODEC_1,
126 			JZ4740_CODEC_1_SW2_ENABLE_OFFSET, 1, 0),
127 };
128 
129 static const struct snd_kcontrol_new jz4740_codec_input_controls[] = {
130 	SOC_DAPM_SINGLE("Line Capture Switch", JZ4740_REG_CODEC_1,
131 			JZ4740_CODEC_1_LINE_ENABLE_OFFSET, 1, 0),
132 	SOC_DAPM_SINGLE("Mic Capture Switch", JZ4740_REG_CODEC_1,
133 			JZ4740_CODEC_1_MIC_ENABLE_OFFSET, 1, 0),
134 };
135 
136 static const struct snd_soc_dapm_widget jz4740_codec_dapm_widgets[] = {
137 	SND_SOC_DAPM_ADC("ADC", "Capture", JZ4740_REG_CODEC_1,
138 			JZ4740_CODEC_1_ADC_ENABLE_OFFSET, 0),
139 	SND_SOC_DAPM_DAC("DAC", "Playback", JZ4740_REG_CODEC_1,
140 			JZ4740_CODEC_1_DAC_ENABLE_OFFSET, 0),
141 
142 	SND_SOC_DAPM_MIXER("Output Mixer", JZ4740_REG_CODEC_1,
143 			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_OFFSET, 1,
144 			jz4740_codec_output_controls,
145 			ARRAY_SIZE(jz4740_codec_output_controls)),
146 
147 	SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0,
148 			jz4740_codec_input_controls,
149 			ARRAY_SIZE(jz4740_codec_input_controls)),
150 	SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
151 
152 	SND_SOC_DAPM_OUTPUT("LOUT"),
153 	SND_SOC_DAPM_OUTPUT("ROUT"),
154 
155 	SND_SOC_DAPM_INPUT("MIC"),
156 	SND_SOC_DAPM_INPUT("LIN"),
157 	SND_SOC_DAPM_INPUT("RIN"),
158 };
159 
160 static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
161 	{"Line Input", NULL, "LIN"},
162 	{"Line Input", NULL, "RIN"},
163 
164 	{"Input Mixer", "Line Capture Switch", "Line Input"},
165 	{"Input Mixer", "Mic Capture Switch", "MIC"},
166 
167 	{"ADC", NULL, "Input Mixer"},
168 
169 	{"Output Mixer", "Bypass Switch", "Input Mixer"},
170 	{"Output Mixer", "DAC Switch", "DAC"},
171 
172 	{"LOUT", NULL, "Output Mixer"},
173 	{"ROUT", NULL, "Output Mixer"},
174 };
175 
176 static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
177 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
178 {
179 	uint32_t val;
180 	struct snd_soc_codec *codec = dai->codec;
181 
182 	switch (params_rate(params)) {
183 	case 8000:
184 		val = 0;
185 		break;
186 	case 11025:
187 		val = 1;
188 		break;
189 	case 12000:
190 		val = 2;
191 		break;
192 	case 16000:
193 		val = 3;
194 		break;
195 	case 22050:
196 		val = 4;
197 		break;
198 	case 24000:
199 		val = 5;
200 		break;
201 	case 32000:
202 		val = 6;
203 		break;
204 	case 44100:
205 		val = 7;
206 		break;
207 	case 48000:
208 		val = 8;
209 		break;
210 	default:
211 		return -EINVAL;
212 	}
213 
214 	val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
215 
216 	snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
217 				JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
218 
219 	return 0;
220 }
221 
222 static const struct snd_soc_dai_ops jz4740_codec_dai_ops = {
223 	.hw_params = jz4740_codec_hw_params,
224 };
225 
226 static struct snd_soc_dai_driver jz4740_codec_dai = {
227 	.name = "jz4740-hifi",
228 	.playback = {
229 		.stream_name = "Playback",
230 		.channels_min = 2,
231 		.channels_max = 2,
232 		.rates = SNDRV_PCM_RATE_8000_48000,
233 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
234 	},
235 	.capture = {
236 		.stream_name = "Capture",
237 		.channels_min = 2,
238 		.channels_max = 2,
239 		.rates = SNDRV_PCM_RATE_8000_48000,
240 		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
241 	},
242 	.ops = &jz4740_codec_dai_ops,
243 	.symmetric_rates = 1,
244 };
245 
246 static void jz4740_codec_wakeup(struct snd_soc_codec *codec)
247 {
248 	int i;
249 	uint32_t *cache = codec->reg_cache;
250 
251 	snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
252 		JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
253 	udelay(2);
254 
255 	snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
256 		JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
257 
258 	for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i)
259 		jz4740_codec_write(codec, i, cache[i]);
260 }
261 
262 static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
263 	enum snd_soc_bias_level level)
264 {
265 	unsigned int mask;
266 	unsigned int value;
267 
268 	switch (level) {
269 	case SND_SOC_BIAS_ON:
270 		break;
271 	case SND_SOC_BIAS_PREPARE:
272 		mask = JZ4740_CODEC_1_VREF_DISABLE |
273 				JZ4740_CODEC_1_VREF_AMP_DISABLE |
274 				JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
275 		value = 0;
276 
277 		snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
278 		break;
279 	case SND_SOC_BIAS_STANDBY:
280 		/* The only way to clear the suspend flag is to reset the codec */
281 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
282 			jz4740_codec_wakeup(codec);
283 
284 		mask = JZ4740_CODEC_1_VREF_DISABLE |
285 			JZ4740_CODEC_1_VREF_AMP_DISABLE |
286 			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
287 		value = JZ4740_CODEC_1_VREF_DISABLE |
288 			JZ4740_CODEC_1_VREF_AMP_DISABLE |
289 			JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
290 
291 		snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
292 		break;
293 	case SND_SOC_BIAS_OFF:
294 		mask = JZ4740_CODEC_1_SUSPEND;
295 		value = JZ4740_CODEC_1_SUSPEND;
296 
297 		snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
298 		break;
299 	default:
300 		break;
301 	}
302 
303 	codec->dapm.bias_level = level;
304 
305 	return 0;
306 }
307 
308 static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
309 {
310 	snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
311 			JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
312 
313 	jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
314 
315 	return 0;
316 }
317 
318 static int jz4740_codec_dev_remove(struct snd_soc_codec *codec)
319 {
320 	jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
321 
322 	return 0;
323 }
324 
325 #ifdef CONFIG_PM_SLEEP
326 
327 static int jz4740_codec_suspend(struct snd_soc_codec *codec)
328 {
329 	return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
330 }
331 
332 static int jz4740_codec_resume(struct snd_soc_codec *codec)
333 {
334 	return jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
335 }
336 
337 #else
338 #define jz4740_codec_suspend NULL
339 #define jz4740_codec_resume NULL
340 #endif
341 
342 static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
343 	.probe = jz4740_codec_dev_probe,
344 	.remove = jz4740_codec_dev_remove,
345 	.suspend = jz4740_codec_suspend,
346 	.resume = jz4740_codec_resume,
347 	.read = jz4740_codec_read,
348 	.write = jz4740_codec_write,
349 	.set_bias_level = jz4740_codec_set_bias_level,
350 	.reg_cache_default	= jz4740_codec_regs,
351 	.reg_word_size = sizeof(u32),
352 	.reg_cache_size	= 2,
353 
354 	.controls = jz4740_codec_controls,
355 	.num_controls = ARRAY_SIZE(jz4740_codec_controls),
356 	.dapm_widgets = jz4740_codec_dapm_widgets,
357 	.num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
358 	.dapm_routes = jz4740_codec_dapm_routes,
359 	.num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
360 };
361 
362 static int __devinit jz4740_codec_probe(struct platform_device *pdev)
363 {
364 	int ret;
365 	struct jz4740_codec *jz4740_codec;
366 	struct resource *mem;
367 
368 	jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
369 				    GFP_KERNEL);
370 	if (!jz4740_codec)
371 		return -ENOMEM;
372 
373 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
374 	if (!mem) {
375 		dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
376 		ret = -ENOENT;
377 		goto err_out;
378 	}
379 
380 	mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
381 	if (!mem) {
382 		dev_err(&pdev->dev, "Failed to request mmio memory region\n");
383 		ret = -EBUSY;
384 		goto err_out;
385 	}
386 
387 	jz4740_codec->base = ioremap(mem->start, resource_size(mem));
388 	if (!jz4740_codec->base) {
389 		dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
390 		ret = -EBUSY;
391 		goto err_release_mem_region;
392 	}
393 	jz4740_codec->mem = mem;
394 
395 	platform_set_drvdata(pdev, jz4740_codec);
396 
397 	ret = snd_soc_register_codec(&pdev->dev,
398 			&soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1);
399 	if (ret) {
400 		dev_err(&pdev->dev, "Failed to register codec\n");
401 		goto err_iounmap;
402 	}
403 
404 	return 0;
405 
406 err_iounmap:
407 	iounmap(jz4740_codec->base);
408 err_release_mem_region:
409 	release_mem_region(mem->start, resource_size(mem));
410 err_out:
411 	return ret;
412 }
413 
414 static int __devexit jz4740_codec_remove(struct platform_device *pdev)
415 {
416 	struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
417 	struct resource *mem = jz4740_codec->mem;
418 
419 	snd_soc_unregister_codec(&pdev->dev);
420 
421 	iounmap(jz4740_codec->base);
422 	release_mem_region(mem->start, resource_size(mem));
423 
424 	platform_set_drvdata(pdev, NULL);
425 
426 	return 0;
427 }
428 
429 static struct platform_driver jz4740_codec_driver = {
430 	.probe = jz4740_codec_probe,
431 	.remove = __devexit_p(jz4740_codec_remove),
432 	.driver = {
433 		.name = "jz4740-codec",
434 		.owner = THIS_MODULE,
435 	},
436 };
437 
438 module_platform_driver(jz4740_codec_driver);
439 
440 MODULE_DESCRIPTION("JZ4740 SoC internal codec driver");
441 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
442 MODULE_LICENSE("GPL v2");
443 MODULE_ALIAS("platform:jz4740-codec");
444