xref: /linux/sound/soc/ti/omap-mcbsp-st.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1f2055e14SPeter Ujfalusi // SPDX-License-Identifier: GPL-2.0
2f2055e14SPeter Ujfalusi /*
3f2055e14SPeter Ujfalusi  * McBSP Sidetone support
4f2055e14SPeter Ujfalusi  *
5f2055e14SPeter Ujfalusi  * Copyright (C) 2004 Nokia Corporation
6f2055e14SPeter Ujfalusi  * Author: Samuel Ortiz <samuel.ortiz@nokia.com>
7f2055e14SPeter Ujfalusi  *
8f2055e14SPeter Ujfalusi  * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
9f2055e14SPeter Ujfalusi  *          Peter Ujfalusi <peter.ujfalusi@ti.com>
10f2055e14SPeter Ujfalusi  */
11f2055e14SPeter Ujfalusi 
12f2055e14SPeter Ujfalusi #include <linux/module.h>
13f2055e14SPeter Ujfalusi #include <linux/init.h>
14f2055e14SPeter Ujfalusi #include <linux/device.h>
15f2055e14SPeter Ujfalusi #include <linux/platform_device.h>
16f2055e14SPeter Ujfalusi #include <linux/interrupt.h>
17f2055e14SPeter Ujfalusi #include <linux/err.h>
18f2055e14SPeter Ujfalusi #include <linux/clk.h>
19f2055e14SPeter Ujfalusi #include <linux/delay.h>
20f2055e14SPeter Ujfalusi #include <linux/io.h>
21f2055e14SPeter Ujfalusi #include <linux/slab.h>
22f2055e14SPeter Ujfalusi 
23f2055e14SPeter Ujfalusi #include "omap-mcbsp.h"
24f2055e14SPeter Ujfalusi #include "omap-mcbsp-priv.h"
25f2055e14SPeter Ujfalusi 
26f2055e14SPeter Ujfalusi /* OMAP3 sidetone control registers */
27f2055e14SPeter Ujfalusi #define OMAP_ST_REG_REV		0x00
28f2055e14SPeter Ujfalusi #define OMAP_ST_REG_SYSCONFIG	0x10
29f2055e14SPeter Ujfalusi #define OMAP_ST_REG_IRQSTATUS	0x18
30f2055e14SPeter Ujfalusi #define OMAP_ST_REG_IRQENABLE	0x1C
31f2055e14SPeter Ujfalusi #define OMAP_ST_REG_SGAINCR	0x24
32f2055e14SPeter Ujfalusi #define OMAP_ST_REG_SFIRCR	0x28
33f2055e14SPeter Ujfalusi #define OMAP_ST_REG_SSELCR	0x2C
34f2055e14SPeter Ujfalusi 
35f2055e14SPeter Ujfalusi /********************** McBSP SSELCR bit definitions ***********************/
36f2055e14SPeter Ujfalusi #define SIDETONEEN		BIT(10)
37f2055e14SPeter Ujfalusi 
38f2055e14SPeter Ujfalusi /********************** McBSP Sidetone SYSCONFIG bit definitions ***********/
39f2055e14SPeter Ujfalusi #define ST_AUTOIDLE		BIT(0)
40f2055e14SPeter Ujfalusi 
41f2055e14SPeter Ujfalusi /********************** McBSP Sidetone SGAINCR bit definitions *************/
42f2055e14SPeter Ujfalusi #define ST_CH0GAIN(value)	((value) & 0xffff)	/* Bits 0:15 */
43f2055e14SPeter Ujfalusi #define ST_CH1GAIN(value)	(((value) & 0xffff) << 16) /* Bits 16:31 */
44f2055e14SPeter Ujfalusi 
45f2055e14SPeter Ujfalusi /********************** McBSP Sidetone SFIRCR bit definitions **************/
46f2055e14SPeter Ujfalusi #define ST_FIRCOEFF(value)	((value) & 0xffff)	/* Bits 0:15 */
47f2055e14SPeter Ujfalusi 
48f2055e14SPeter Ujfalusi /********************** McBSP Sidetone SSELCR bit definitions **************/
49f2055e14SPeter Ujfalusi #define ST_SIDETONEEN		BIT(0)
50f2055e14SPeter Ujfalusi #define ST_COEFFWREN		BIT(1)
51f2055e14SPeter Ujfalusi #define ST_COEFFWRDONE		BIT(2)
52f2055e14SPeter Ujfalusi 
53f2055e14SPeter Ujfalusi struct omap_mcbsp_st_data {
54f2055e14SPeter Ujfalusi 	void __iomem *io_base_st;
55f2055e14SPeter Ujfalusi 	struct clk *mcbsp_iclk;
56f2055e14SPeter Ujfalusi 	bool running;
57f2055e14SPeter Ujfalusi 	bool enabled;
58f2055e14SPeter Ujfalusi 	s16 taps[128];	/* Sidetone filter coefficients */
59f2055e14SPeter Ujfalusi 	int nr_taps;	/* Number of filter coefficients in use */
60f2055e14SPeter Ujfalusi 	s16 ch0gain;
61f2055e14SPeter Ujfalusi 	s16 ch1gain;
62f2055e14SPeter Ujfalusi };
63f2055e14SPeter Ujfalusi 
omap_mcbsp_st_write(struct omap_mcbsp * mcbsp,u16 reg,u32 val)64f2055e14SPeter Ujfalusi static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
65f2055e14SPeter Ujfalusi {
66f2055e14SPeter Ujfalusi 	writel_relaxed(val, mcbsp->st_data->io_base_st + reg);
67f2055e14SPeter Ujfalusi }
68f2055e14SPeter Ujfalusi 
omap_mcbsp_st_read(struct omap_mcbsp * mcbsp,u16 reg)69f2055e14SPeter Ujfalusi static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
70f2055e14SPeter Ujfalusi {
71f2055e14SPeter Ujfalusi 	return readl_relaxed(mcbsp->st_data->io_base_st + reg);
72f2055e14SPeter Ujfalusi }
73f2055e14SPeter Ujfalusi 
74f2055e14SPeter Ujfalusi #define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
75f2055e14SPeter Ujfalusi #define MCBSP_ST_WRITE(mcbsp, reg, val) \
76f2055e14SPeter Ujfalusi 			omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
77f2055e14SPeter Ujfalusi 
omap_mcbsp_st_on(struct omap_mcbsp * mcbsp)78f2055e14SPeter Ujfalusi static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp)
79f2055e14SPeter Ujfalusi {
80f2055e14SPeter Ujfalusi 	unsigned int w;
81f2055e14SPeter Ujfalusi 
82f2055e14SPeter Ujfalusi 	if (mcbsp->pdata->force_ick_on)
83f2055e14SPeter Ujfalusi 		mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true);
84f2055e14SPeter Ujfalusi 
85f2055e14SPeter Ujfalusi 	/* Disable Sidetone clock auto-gating for normal operation */
86f2055e14SPeter Ujfalusi 	w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
87f2055e14SPeter Ujfalusi 	MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
88f2055e14SPeter Ujfalusi 
89f2055e14SPeter Ujfalusi 	/* Enable McBSP Sidetone */
90f2055e14SPeter Ujfalusi 	w = MCBSP_READ(mcbsp, SSELCR);
91f2055e14SPeter Ujfalusi 	MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
92f2055e14SPeter Ujfalusi 
93f2055e14SPeter Ujfalusi 	/* Enable Sidetone from Sidetone Core */
94f2055e14SPeter Ujfalusi 	w = MCBSP_ST_READ(mcbsp, SSELCR);
95f2055e14SPeter Ujfalusi 	MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
96f2055e14SPeter Ujfalusi }
97f2055e14SPeter Ujfalusi 
omap_mcbsp_st_off(struct omap_mcbsp * mcbsp)98f2055e14SPeter Ujfalusi static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp)
99f2055e14SPeter Ujfalusi {
100f2055e14SPeter Ujfalusi 	unsigned int w;
101f2055e14SPeter Ujfalusi 
102f2055e14SPeter Ujfalusi 	w = MCBSP_ST_READ(mcbsp, SSELCR);
103f2055e14SPeter Ujfalusi 	MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
104f2055e14SPeter Ujfalusi 
105f2055e14SPeter Ujfalusi 	w = MCBSP_READ(mcbsp, SSELCR);
106f2055e14SPeter Ujfalusi 	MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
107f2055e14SPeter Ujfalusi 
108f2055e14SPeter Ujfalusi 	/* Enable Sidetone clock auto-gating to reduce power consumption */
109f2055e14SPeter Ujfalusi 	w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
110f2055e14SPeter Ujfalusi 	MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
111f2055e14SPeter Ujfalusi 
112f2055e14SPeter Ujfalusi 	if (mcbsp->pdata->force_ick_on)
113f2055e14SPeter Ujfalusi 		mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false);
114f2055e14SPeter Ujfalusi }
115f2055e14SPeter Ujfalusi 
omap_mcbsp_st_fir_write(struct omap_mcbsp * mcbsp,s16 * fir)116f2055e14SPeter Ujfalusi static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
117f2055e14SPeter Ujfalusi {
118f2055e14SPeter Ujfalusi 	u16 val, i;
119f2055e14SPeter Ujfalusi 
120f2055e14SPeter Ujfalusi 	val = MCBSP_ST_READ(mcbsp, SSELCR);
121f2055e14SPeter Ujfalusi 
122f2055e14SPeter Ujfalusi 	if (val & ST_COEFFWREN)
123f2055e14SPeter Ujfalusi 		MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
124f2055e14SPeter Ujfalusi 
125f2055e14SPeter Ujfalusi 	MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
126f2055e14SPeter Ujfalusi 
127f2055e14SPeter Ujfalusi 	for (i = 0; i < 128; i++)
128f2055e14SPeter Ujfalusi 		MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
129f2055e14SPeter Ujfalusi 
130f2055e14SPeter Ujfalusi 	i = 0;
131f2055e14SPeter Ujfalusi 
132f2055e14SPeter Ujfalusi 	val = MCBSP_ST_READ(mcbsp, SSELCR);
133f2055e14SPeter Ujfalusi 	while (!(val & ST_COEFFWRDONE) && (++i < 1000))
134f2055e14SPeter Ujfalusi 		val = MCBSP_ST_READ(mcbsp, SSELCR);
135f2055e14SPeter Ujfalusi 
136f2055e14SPeter Ujfalusi 	MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
137f2055e14SPeter Ujfalusi 
138f2055e14SPeter Ujfalusi 	if (i == 1000)
139f2055e14SPeter Ujfalusi 		dev_err(mcbsp->dev, "McBSP FIR load error!\n");
140f2055e14SPeter Ujfalusi }
141f2055e14SPeter Ujfalusi 
omap_mcbsp_st_chgain(struct omap_mcbsp * mcbsp)142f2055e14SPeter Ujfalusi static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp)
143f2055e14SPeter Ujfalusi {
144f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
145f2055e14SPeter Ujfalusi 
146f2055e14SPeter Ujfalusi 	MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) |
147f2055e14SPeter Ujfalusi 		       ST_CH1GAIN(st_data->ch1gain));
148f2055e14SPeter Ujfalusi }
149f2055e14SPeter Ujfalusi 
omap_mcbsp_st_set_chgain(struct omap_mcbsp * mcbsp,int channel,s16 chgain)150f2055e14SPeter Ujfalusi static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel,
151f2055e14SPeter Ujfalusi 				    s16 chgain)
152f2055e14SPeter Ujfalusi {
153f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
154f2055e14SPeter Ujfalusi 	int ret = 0;
155f2055e14SPeter Ujfalusi 
156f2055e14SPeter Ujfalusi 	if (!st_data)
157f2055e14SPeter Ujfalusi 		return -ENOENT;
158f2055e14SPeter Ujfalusi 
159f2055e14SPeter Ujfalusi 	spin_lock_irq(&mcbsp->lock);
160f2055e14SPeter Ujfalusi 	if (channel == 0)
161f2055e14SPeter Ujfalusi 		st_data->ch0gain = chgain;
162f2055e14SPeter Ujfalusi 	else if (channel == 1)
163f2055e14SPeter Ujfalusi 		st_data->ch1gain = chgain;
164f2055e14SPeter Ujfalusi 	else
165f2055e14SPeter Ujfalusi 		ret = -EINVAL;
166f2055e14SPeter Ujfalusi 
167f2055e14SPeter Ujfalusi 	if (st_data->enabled)
168f2055e14SPeter Ujfalusi 		omap_mcbsp_st_chgain(mcbsp);
169f2055e14SPeter Ujfalusi 	spin_unlock_irq(&mcbsp->lock);
170f2055e14SPeter Ujfalusi 
171f2055e14SPeter Ujfalusi 	return ret;
172f2055e14SPeter Ujfalusi }
173f2055e14SPeter Ujfalusi 
omap_mcbsp_st_get_chgain(struct omap_mcbsp * mcbsp,int channel,s16 * chgain)174f2055e14SPeter Ujfalusi static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel,
175f2055e14SPeter Ujfalusi 				    s16 *chgain)
176f2055e14SPeter Ujfalusi {
177f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
178f2055e14SPeter Ujfalusi 	int ret = 0;
179f2055e14SPeter Ujfalusi 
180f2055e14SPeter Ujfalusi 	if (!st_data)
181f2055e14SPeter Ujfalusi 		return -ENOENT;
182f2055e14SPeter Ujfalusi 
183f2055e14SPeter Ujfalusi 	spin_lock_irq(&mcbsp->lock);
184f2055e14SPeter Ujfalusi 	if (channel == 0)
185f2055e14SPeter Ujfalusi 		*chgain = st_data->ch0gain;
186f2055e14SPeter Ujfalusi 	else if (channel == 1)
187f2055e14SPeter Ujfalusi 		*chgain = st_data->ch1gain;
188f2055e14SPeter Ujfalusi 	else
189f2055e14SPeter Ujfalusi 		ret = -EINVAL;
190f2055e14SPeter Ujfalusi 	spin_unlock_irq(&mcbsp->lock);
191f2055e14SPeter Ujfalusi 
192f2055e14SPeter Ujfalusi 	return ret;
193f2055e14SPeter Ujfalusi }
194f2055e14SPeter Ujfalusi 
omap_mcbsp_st_enable(struct omap_mcbsp * mcbsp)195f2055e14SPeter Ujfalusi static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp)
196f2055e14SPeter Ujfalusi {
197f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
198f2055e14SPeter Ujfalusi 
199f2055e14SPeter Ujfalusi 	if (!st_data)
200f2055e14SPeter Ujfalusi 		return -ENODEV;
201f2055e14SPeter Ujfalusi 
202f2055e14SPeter Ujfalusi 	spin_lock_irq(&mcbsp->lock);
203f2055e14SPeter Ujfalusi 	st_data->enabled = 1;
204f2055e14SPeter Ujfalusi 	omap_mcbsp_st_start(mcbsp);
205f2055e14SPeter Ujfalusi 	spin_unlock_irq(&mcbsp->lock);
206f2055e14SPeter Ujfalusi 
207f2055e14SPeter Ujfalusi 	return 0;
208f2055e14SPeter Ujfalusi }
209f2055e14SPeter Ujfalusi 
omap_mcbsp_st_disable(struct omap_mcbsp * mcbsp)210f2055e14SPeter Ujfalusi static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp)
211f2055e14SPeter Ujfalusi {
212f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
213f2055e14SPeter Ujfalusi 	int ret = 0;
214f2055e14SPeter Ujfalusi 
215f2055e14SPeter Ujfalusi 	if (!st_data)
216f2055e14SPeter Ujfalusi 		return -ENODEV;
217f2055e14SPeter Ujfalusi 
218f2055e14SPeter Ujfalusi 	spin_lock_irq(&mcbsp->lock);
219f2055e14SPeter Ujfalusi 	omap_mcbsp_st_stop(mcbsp);
220f2055e14SPeter Ujfalusi 	st_data->enabled = 0;
221f2055e14SPeter Ujfalusi 	spin_unlock_irq(&mcbsp->lock);
222f2055e14SPeter Ujfalusi 
223f2055e14SPeter Ujfalusi 	return ret;
224f2055e14SPeter Ujfalusi }
225f2055e14SPeter Ujfalusi 
omap_mcbsp_st_is_enabled(struct omap_mcbsp * mcbsp)226f2055e14SPeter Ujfalusi static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp)
227f2055e14SPeter Ujfalusi {
228f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
229f2055e14SPeter Ujfalusi 
230f2055e14SPeter Ujfalusi 	if (!st_data)
231f2055e14SPeter Ujfalusi 		return -ENODEV;
232f2055e14SPeter Ujfalusi 
233f2055e14SPeter Ujfalusi 	return st_data->enabled;
234f2055e14SPeter Ujfalusi }
235f2055e14SPeter Ujfalusi 
st_taps_show(struct device * dev,struct device_attribute * attr,char * buf)236f2055e14SPeter Ujfalusi static ssize_t st_taps_show(struct device *dev,
237f2055e14SPeter Ujfalusi 			    struct device_attribute *attr, char *buf)
238f2055e14SPeter Ujfalusi {
239f2055e14SPeter Ujfalusi 	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
240f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
241f2055e14SPeter Ujfalusi 	ssize_t status = 0;
242f2055e14SPeter Ujfalusi 	int i;
243f2055e14SPeter Ujfalusi 
244f2055e14SPeter Ujfalusi 	spin_lock_irq(&mcbsp->lock);
245f2055e14SPeter Ujfalusi 	for (i = 0; i < st_data->nr_taps; i++)
246a111ae4dSTakashi Iwai 		status += sysfs_emit_at(buf, status, (i ? ", %d" : "%d"),
247f2055e14SPeter Ujfalusi 					st_data->taps[i]);
248f2055e14SPeter Ujfalusi 	if (i)
249a111ae4dSTakashi Iwai 		status += sysfs_emit_at(buf, status, "\n");
250f2055e14SPeter Ujfalusi 	spin_unlock_irq(&mcbsp->lock);
251f2055e14SPeter Ujfalusi 
252f2055e14SPeter Ujfalusi 	return status;
253f2055e14SPeter Ujfalusi }
254f2055e14SPeter Ujfalusi 
st_taps_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)255f2055e14SPeter Ujfalusi static ssize_t st_taps_store(struct device *dev,
256f2055e14SPeter Ujfalusi 			     struct device_attribute *attr,
257f2055e14SPeter Ujfalusi 			     const char *buf, size_t size)
258f2055e14SPeter Ujfalusi {
259f2055e14SPeter Ujfalusi 	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
260f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
261f2055e14SPeter Ujfalusi 	int val, tmp, status, i = 0;
262f2055e14SPeter Ujfalusi 
263f2055e14SPeter Ujfalusi 	spin_lock_irq(&mcbsp->lock);
264f2055e14SPeter Ujfalusi 	memset(st_data->taps, 0, sizeof(st_data->taps));
265f2055e14SPeter Ujfalusi 	st_data->nr_taps = 0;
266f2055e14SPeter Ujfalusi 
267f2055e14SPeter Ujfalusi 	do {
268f2055e14SPeter Ujfalusi 		status = sscanf(buf, "%d%n", &val, &tmp);
269f2055e14SPeter Ujfalusi 		if (status < 0 || status == 0) {
270f2055e14SPeter Ujfalusi 			size = -EINVAL;
271f2055e14SPeter Ujfalusi 			goto out;
272f2055e14SPeter Ujfalusi 		}
273f2055e14SPeter Ujfalusi 		if (val < -32768 || val > 32767) {
274f2055e14SPeter Ujfalusi 			size = -EINVAL;
275f2055e14SPeter Ujfalusi 			goto out;
276f2055e14SPeter Ujfalusi 		}
277f2055e14SPeter Ujfalusi 		st_data->taps[i++] = val;
278f2055e14SPeter Ujfalusi 		buf += tmp;
279f2055e14SPeter Ujfalusi 		if (*buf != ',')
280f2055e14SPeter Ujfalusi 			break;
281f2055e14SPeter Ujfalusi 		buf++;
282f2055e14SPeter Ujfalusi 	} while (1);
283f2055e14SPeter Ujfalusi 
284f2055e14SPeter Ujfalusi 	st_data->nr_taps = i;
285f2055e14SPeter Ujfalusi 
286f2055e14SPeter Ujfalusi out:
287f2055e14SPeter Ujfalusi 	spin_unlock_irq(&mcbsp->lock);
288f2055e14SPeter Ujfalusi 
289f2055e14SPeter Ujfalusi 	return size;
290f2055e14SPeter Ujfalusi }
291f2055e14SPeter Ujfalusi 
292f2055e14SPeter Ujfalusi static DEVICE_ATTR_RW(st_taps);
293f2055e14SPeter Ujfalusi 
294f2055e14SPeter Ujfalusi static const struct attribute *sidetone_attrs[] = {
295f2055e14SPeter Ujfalusi 	&dev_attr_st_taps.attr,
296f2055e14SPeter Ujfalusi 	NULL,
297f2055e14SPeter Ujfalusi };
298f2055e14SPeter Ujfalusi 
299f2055e14SPeter Ujfalusi static const struct attribute_group sidetone_attr_group = {
300f2055e14SPeter Ujfalusi 	.attrs = (struct attribute **)sidetone_attrs,
301f2055e14SPeter Ujfalusi };
302f2055e14SPeter Ujfalusi 
omap_mcbsp_st_start(struct omap_mcbsp * mcbsp)303f2055e14SPeter Ujfalusi int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp)
304f2055e14SPeter Ujfalusi {
305f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
306f2055e14SPeter Ujfalusi 
307f2055e14SPeter Ujfalusi 	if (st_data->enabled && !st_data->running) {
308f2055e14SPeter Ujfalusi 		omap_mcbsp_st_fir_write(mcbsp, st_data->taps);
309f2055e14SPeter Ujfalusi 		omap_mcbsp_st_chgain(mcbsp);
310f2055e14SPeter Ujfalusi 
311f2055e14SPeter Ujfalusi 		if (!mcbsp->free) {
312f2055e14SPeter Ujfalusi 			omap_mcbsp_st_on(mcbsp);
313f2055e14SPeter Ujfalusi 			st_data->running = 1;
314f2055e14SPeter Ujfalusi 		}
315f2055e14SPeter Ujfalusi 	}
316f2055e14SPeter Ujfalusi 
317f2055e14SPeter Ujfalusi 	return 0;
318f2055e14SPeter Ujfalusi }
319f2055e14SPeter Ujfalusi 
omap_mcbsp_st_stop(struct omap_mcbsp * mcbsp)320f2055e14SPeter Ujfalusi int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp)
321f2055e14SPeter Ujfalusi {
322f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
323f2055e14SPeter Ujfalusi 
324f2055e14SPeter Ujfalusi 	if (st_data->running) {
325f2055e14SPeter Ujfalusi 		if (!mcbsp->free) {
326f2055e14SPeter Ujfalusi 			omap_mcbsp_st_off(mcbsp);
327f2055e14SPeter Ujfalusi 			st_data->running = 0;
328f2055e14SPeter Ujfalusi 		}
329f2055e14SPeter Ujfalusi 	}
330f2055e14SPeter Ujfalusi 
331f2055e14SPeter Ujfalusi 	return 0;
332f2055e14SPeter Ujfalusi }
333f2055e14SPeter Ujfalusi 
omap_mcbsp_st_init(struct platform_device * pdev)334f2055e14SPeter Ujfalusi int omap_mcbsp_st_init(struct platform_device *pdev)
335f2055e14SPeter Ujfalusi {
336f2055e14SPeter Ujfalusi 	struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
337f2055e14SPeter Ujfalusi 	struct omap_mcbsp_st_data *st_data;
338f2055e14SPeter Ujfalusi 	struct resource *res;
339f2055e14SPeter Ujfalusi 	int ret;
340f2055e14SPeter Ujfalusi 
341f2055e14SPeter Ujfalusi 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
342f2055e14SPeter Ujfalusi 	if (!res)
343f2055e14SPeter Ujfalusi 		return 0;
344f2055e14SPeter Ujfalusi 
345f2055e14SPeter Ujfalusi 	st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL);
346f2055e14SPeter Ujfalusi 	if (!st_data)
347f2055e14SPeter Ujfalusi 		return -ENOMEM;
348f2055e14SPeter Ujfalusi 
349f0d96937SDavid Owens 	st_data->mcbsp_iclk = devm_clk_get(mcbsp->dev, "ick");
350f2055e14SPeter Ujfalusi 	if (IS_ERR(st_data->mcbsp_iclk)) {
351f2055e14SPeter Ujfalusi 		dev_warn(mcbsp->dev,
352f2055e14SPeter Ujfalusi 			 "Failed to get ick, sidetone might be broken\n");
353f2055e14SPeter Ujfalusi 		st_data->mcbsp_iclk = NULL;
354f2055e14SPeter Ujfalusi 	}
355f2055e14SPeter Ujfalusi 
356f2055e14SPeter Ujfalusi 	st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start,
357f2055e14SPeter Ujfalusi 					   resource_size(res));
358f2055e14SPeter Ujfalusi 	if (!st_data->io_base_st)
359f2055e14SPeter Ujfalusi 		return -ENOMEM;
360f2055e14SPeter Ujfalusi 
361f0d96937SDavid Owens 	ret = devm_device_add_group(mcbsp->dev, &sidetone_attr_group);
362f2055e14SPeter Ujfalusi 	if (ret)
363f2055e14SPeter Ujfalusi 		return ret;
364f2055e14SPeter Ujfalusi 
365f2055e14SPeter Ujfalusi 	mcbsp->st_data = st_data;
366f2055e14SPeter Ujfalusi 
367f2055e14SPeter Ujfalusi 	return 0;
368f2055e14SPeter Ujfalusi }
369f2055e14SPeter Ujfalusi 
omap_mcbsp_st_info_volsw(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)370f2055e14SPeter Ujfalusi static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
371f2055e14SPeter Ujfalusi 				    struct snd_ctl_elem_info *uinfo)
372f2055e14SPeter Ujfalusi {
373f2055e14SPeter Ujfalusi 	struct soc_mixer_control *mc =
374f2055e14SPeter Ujfalusi 		(struct soc_mixer_control *)kcontrol->private_value;
375f2055e14SPeter Ujfalusi 	int max = mc->max;
376f2055e14SPeter Ujfalusi 	int min = mc->min;
377f2055e14SPeter Ujfalusi 
378f2055e14SPeter Ujfalusi 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
379f2055e14SPeter Ujfalusi 	uinfo->count = 1;
380f2055e14SPeter Ujfalusi 	uinfo->value.integer.min = min;
381f2055e14SPeter Ujfalusi 	uinfo->value.integer.max = max;
382f2055e14SPeter Ujfalusi 	return 0;
383f2055e14SPeter Ujfalusi }
384f2055e14SPeter Ujfalusi 
385f2055e14SPeter Ujfalusi #define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel)				\
386f2055e14SPeter Ujfalusi static int								\
387f2055e14SPeter Ujfalusi omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc,		\
388f2055e14SPeter Ujfalusi 				       struct snd_ctl_elem_value *uc)	\
389f2055e14SPeter Ujfalusi {									\
390f2055e14SPeter Ujfalusi 	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc);		\
391f2055e14SPeter Ujfalusi 	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);	\
392f2055e14SPeter Ujfalusi 	struct soc_mixer_control *mc =					\
393f2055e14SPeter Ujfalusi 		(struct soc_mixer_control *)kc->private_value;		\
394f2055e14SPeter Ujfalusi 	int max = mc->max;						\
395f2055e14SPeter Ujfalusi 	int min = mc->min;						\
396f2055e14SPeter Ujfalusi 	int val = uc->value.integer.value[0];				\
397f2055e14SPeter Ujfalusi 									\
398f2055e14SPeter Ujfalusi 	if (val < min || val > max)					\
399f2055e14SPeter Ujfalusi 		return -EINVAL;						\
400f2055e14SPeter Ujfalusi 									\
401f2055e14SPeter Ujfalusi 	/* OMAP McBSP implementation uses index values 0..4 */		\
402f2055e14SPeter Ujfalusi 	return omap_mcbsp_st_set_chgain(mcbsp, channel, val);		\
403f2055e14SPeter Ujfalusi }									\
404f2055e14SPeter Ujfalusi 									\
405f2055e14SPeter Ujfalusi static int								\
406f2055e14SPeter Ujfalusi omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc,		\
407f2055e14SPeter Ujfalusi 				       struct snd_ctl_elem_value *uc)	\
408f2055e14SPeter Ujfalusi {									\
409f2055e14SPeter Ujfalusi 	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc);		\
410f2055e14SPeter Ujfalusi 	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);	\
411f2055e14SPeter Ujfalusi 	s16 chgain;							\
412f2055e14SPeter Ujfalusi 									\
413f2055e14SPeter Ujfalusi 	if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain))		\
414f2055e14SPeter Ujfalusi 		return -EAGAIN;						\
415f2055e14SPeter Ujfalusi 									\
416f2055e14SPeter Ujfalusi 	uc->value.integer.value[0] = chgain;				\
417f2055e14SPeter Ujfalusi 	return 0;							\
418f2055e14SPeter Ujfalusi }
419f2055e14SPeter Ujfalusi 
420f2055e14SPeter Ujfalusi OMAP_MCBSP_ST_CHANNEL_VOLUME(0)
421f2055e14SPeter Ujfalusi OMAP_MCBSP_ST_CHANNEL_VOLUME(1)
422f2055e14SPeter Ujfalusi 
omap_mcbsp_st_put_mode(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)423f2055e14SPeter Ujfalusi static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
424f2055e14SPeter Ujfalusi 				  struct snd_ctl_elem_value *ucontrol)
425f2055e14SPeter Ujfalusi {
426f2055e14SPeter Ujfalusi 	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
427f2055e14SPeter Ujfalusi 	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
428f2055e14SPeter Ujfalusi 	u8 value = ucontrol->value.integer.value[0];
429f2055e14SPeter Ujfalusi 
430f2055e14SPeter Ujfalusi 	if (value == omap_mcbsp_st_is_enabled(mcbsp))
431f2055e14SPeter Ujfalusi 		return 0;
432f2055e14SPeter Ujfalusi 
433f2055e14SPeter Ujfalusi 	if (value)
434f2055e14SPeter Ujfalusi 		omap_mcbsp_st_enable(mcbsp);
435f2055e14SPeter Ujfalusi 	else
436f2055e14SPeter Ujfalusi 		omap_mcbsp_st_disable(mcbsp);
437f2055e14SPeter Ujfalusi 
438f2055e14SPeter Ujfalusi 	return 1;
439f2055e14SPeter Ujfalusi }
440f2055e14SPeter Ujfalusi 
omap_mcbsp_st_get_mode(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)441f2055e14SPeter Ujfalusi static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol,
442f2055e14SPeter Ujfalusi 				  struct snd_ctl_elem_value *ucontrol)
443f2055e14SPeter Ujfalusi {
444f2055e14SPeter Ujfalusi 	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
445f2055e14SPeter Ujfalusi 	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
446f2055e14SPeter Ujfalusi 
447f2055e14SPeter Ujfalusi 	ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp);
448f2055e14SPeter Ujfalusi 	return 0;
449f2055e14SPeter Ujfalusi }
450f2055e14SPeter Ujfalusi 
451f2055e14SPeter Ujfalusi #define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax,		\
452f2055e14SPeter Ujfalusi 				      xhandler_get, xhandler_put)	\
453f2055e14SPeter Ujfalusi {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,		\
454f2055e14SPeter Ujfalusi 	.info = omap_mcbsp_st_info_volsw,				\
455f2055e14SPeter Ujfalusi 	.get = xhandler_get, .put = xhandler_put,			\
456f2055e14SPeter Ujfalusi 	.private_value = (unsigned long)&(struct soc_mixer_control)	\
457f2055e14SPeter Ujfalusi 	{.min = xmin, .max = xmax} }
458f2055e14SPeter Ujfalusi 
459f2055e14SPeter Ujfalusi #define OMAP_MCBSP_ST_CONTROLS(port)					  \
460f2055e14SPeter Ujfalusi static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \
461f2055e14SPeter Ujfalusi SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0,		  \
462f2055e14SPeter Ujfalusi 	       omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode),		  \
463f2055e14SPeter Ujfalusi OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \
464f2055e14SPeter Ujfalusi 			      -32768, 32767,				  \
465f2055e14SPeter Ujfalusi 			      omap_mcbsp_get_st_ch0_volume,		  \
466f2055e14SPeter Ujfalusi 			      omap_mcbsp_set_st_ch0_volume),		  \
467f2055e14SPeter Ujfalusi OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
468f2055e14SPeter Ujfalusi 			      -32768, 32767,				  \
469f2055e14SPeter Ujfalusi 			      omap_mcbsp_get_st_ch1_volume,		  \
470f2055e14SPeter Ujfalusi 			      omap_mcbsp_set_st_ch1_volume),		  \
471f2055e14SPeter Ujfalusi }
472f2055e14SPeter Ujfalusi 
473f2055e14SPeter Ujfalusi OMAP_MCBSP_ST_CONTROLS(2);
474f2055e14SPeter Ujfalusi OMAP_MCBSP_ST_CONTROLS(3);
475f2055e14SPeter Ujfalusi 
omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime * rtd,int port_id)476f2055e14SPeter Ujfalusi int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id)
477f2055e14SPeter Ujfalusi {
478*1af52932SKuninori Morimoto 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
479f2055e14SPeter Ujfalusi 	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
480f2055e14SPeter Ujfalusi 
481f2055e14SPeter Ujfalusi 	if (!mcbsp->st_data) {
482f2055e14SPeter Ujfalusi 		dev_warn(mcbsp->dev, "No sidetone data for port\n");
483f2055e14SPeter Ujfalusi 		return 0;
484f2055e14SPeter Ujfalusi 	}
485f2055e14SPeter Ujfalusi 
486f2055e14SPeter Ujfalusi 	switch (port_id) {
487f2055e14SPeter Ujfalusi 	case 2: /* McBSP 2 */
488f2055e14SPeter Ujfalusi 		return snd_soc_add_dai_controls(cpu_dai,
489f2055e14SPeter Ujfalusi 					omap_mcbsp2_st_controls,
490f2055e14SPeter Ujfalusi 					ARRAY_SIZE(omap_mcbsp2_st_controls));
491f2055e14SPeter Ujfalusi 	case 3: /* McBSP 3 */
492f2055e14SPeter Ujfalusi 		return snd_soc_add_dai_controls(cpu_dai,
493f2055e14SPeter Ujfalusi 					omap_mcbsp3_st_controls,
494f2055e14SPeter Ujfalusi 					ARRAY_SIZE(omap_mcbsp3_st_controls));
495f2055e14SPeter Ujfalusi 	default:
496f2055e14SPeter Ujfalusi 		dev_err(mcbsp->dev, "Port %d not supported\n", port_id);
497f2055e14SPeter Ujfalusi 		break;
498f2055e14SPeter Ujfalusi 	}
499f2055e14SPeter Ujfalusi 
500f2055e14SPeter Ujfalusi 	return -EINVAL;
501f2055e14SPeter Ujfalusi }
502f2055e14SPeter Ujfalusi EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
503