162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * McBSP Sidetone support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2004 Nokia Corporation
662306a36Sopenharmony_ci * Author: Samuel Ortiz <samuel.ortiz@nokia.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
962306a36Sopenharmony_ci *          Peter Ujfalusi <peter.ujfalusi@ti.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/clk.h>
1962306a36Sopenharmony_ci#include <linux/delay.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "omap-mcbsp.h"
2462306a36Sopenharmony_ci#include "omap-mcbsp-priv.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* OMAP3 sidetone control registers */
2762306a36Sopenharmony_ci#define OMAP_ST_REG_REV		0x00
2862306a36Sopenharmony_ci#define OMAP_ST_REG_SYSCONFIG	0x10
2962306a36Sopenharmony_ci#define OMAP_ST_REG_IRQSTATUS	0x18
3062306a36Sopenharmony_ci#define OMAP_ST_REG_IRQENABLE	0x1C
3162306a36Sopenharmony_ci#define OMAP_ST_REG_SGAINCR	0x24
3262306a36Sopenharmony_ci#define OMAP_ST_REG_SFIRCR	0x28
3362306a36Sopenharmony_ci#define OMAP_ST_REG_SSELCR	0x2C
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/********************** McBSP SSELCR bit definitions ***********************/
3662306a36Sopenharmony_ci#define SIDETONEEN		BIT(10)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/
3962306a36Sopenharmony_ci#define ST_AUTOIDLE		BIT(0)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/********************** McBSP Sidetone SGAINCR bit definitions *************/
4262306a36Sopenharmony_ci#define ST_CH0GAIN(value)	((value) & 0xffff)	/* Bits 0:15 */
4362306a36Sopenharmony_ci#define ST_CH1GAIN(value)	(((value) & 0xffff) << 16) /* Bits 16:31 */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci/********************** McBSP Sidetone SFIRCR bit definitions **************/
4662306a36Sopenharmony_ci#define ST_FIRCOEFF(value)	((value) & 0xffff)	/* Bits 0:15 */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/********************** McBSP Sidetone SSELCR bit definitions **************/
4962306a36Sopenharmony_ci#define ST_SIDETONEEN		BIT(0)
5062306a36Sopenharmony_ci#define ST_COEFFWREN		BIT(1)
5162306a36Sopenharmony_ci#define ST_COEFFWRDONE		BIT(2)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct omap_mcbsp_st_data {
5462306a36Sopenharmony_ci	void __iomem *io_base_st;
5562306a36Sopenharmony_ci	struct clk *mcbsp_iclk;
5662306a36Sopenharmony_ci	bool running;
5762306a36Sopenharmony_ci	bool enabled;
5862306a36Sopenharmony_ci	s16 taps[128];	/* Sidetone filter coefficients */
5962306a36Sopenharmony_ci	int nr_taps;	/* Number of filter coefficients in use */
6062306a36Sopenharmony_ci	s16 ch0gain;
6162306a36Sopenharmony_ci	s16 ch1gain;
6262306a36Sopenharmony_ci};
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	writel_relaxed(val, mcbsp->st_data->io_base_st + reg);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	return readl_relaxed(mcbsp->st_data->io_base_st + reg);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
7562306a36Sopenharmony_ci#define MCBSP_ST_WRITE(mcbsp, reg, val) \
7662306a36Sopenharmony_ci			omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	unsigned int w;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (mcbsp->pdata->force_ick_on)
8362306a36Sopenharmony_ci		mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/* Disable Sidetone clock auto-gating for normal operation */
8662306a36Sopenharmony_ci	w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
8762306a36Sopenharmony_ci	MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Enable McBSP Sidetone */
9062306a36Sopenharmony_ci	w = MCBSP_READ(mcbsp, SSELCR);
9162306a36Sopenharmony_ci	MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Enable Sidetone from Sidetone Core */
9462306a36Sopenharmony_ci	w = MCBSP_ST_READ(mcbsp, SSELCR);
9562306a36Sopenharmony_ci	MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	unsigned int w;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	w = MCBSP_ST_READ(mcbsp, SSELCR);
10362306a36Sopenharmony_ci	MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	w = MCBSP_READ(mcbsp, SSELCR);
10662306a36Sopenharmony_ci	MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Enable Sidetone clock auto-gating to reduce power consumption */
10962306a36Sopenharmony_ci	w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
11062306a36Sopenharmony_ci	MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (mcbsp->pdata->force_ick_on)
11362306a36Sopenharmony_ci		mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	u16 val, i;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	val = MCBSP_ST_READ(mcbsp, SSELCR);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	if (val & ST_COEFFWREN)
12362306a36Sopenharmony_ci		MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	for (i = 0; i < 128; i++)
12862306a36Sopenharmony_ci		MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	i = 0;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	val = MCBSP_ST_READ(mcbsp, SSELCR);
13362306a36Sopenharmony_ci	while (!(val & ST_COEFFWRDONE) && (++i < 1000))
13462306a36Sopenharmony_ci		val = MCBSP_ST_READ(mcbsp, SSELCR);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (i == 1000)
13962306a36Sopenharmony_ci		dev_err(mcbsp->dev, "McBSP FIR load error!\n");
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) |
14762306a36Sopenharmony_ci		       ST_CH1GAIN(st_data->ch1gain));
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel,
15162306a36Sopenharmony_ci				    s16 chgain)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
15462306a36Sopenharmony_ci	int ret = 0;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!st_data)
15762306a36Sopenharmony_ci		return -ENOENT;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	spin_lock_irq(&mcbsp->lock);
16062306a36Sopenharmony_ci	if (channel == 0)
16162306a36Sopenharmony_ci		st_data->ch0gain = chgain;
16262306a36Sopenharmony_ci	else if (channel == 1)
16362306a36Sopenharmony_ci		st_data->ch1gain = chgain;
16462306a36Sopenharmony_ci	else
16562306a36Sopenharmony_ci		ret = -EINVAL;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (st_data->enabled)
16862306a36Sopenharmony_ci		omap_mcbsp_st_chgain(mcbsp);
16962306a36Sopenharmony_ci	spin_unlock_irq(&mcbsp->lock);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return ret;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel,
17562306a36Sopenharmony_ci				    s16 *chgain)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
17862306a36Sopenharmony_ci	int ret = 0;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (!st_data)
18162306a36Sopenharmony_ci		return -ENOENT;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	spin_lock_irq(&mcbsp->lock);
18462306a36Sopenharmony_ci	if (channel == 0)
18562306a36Sopenharmony_ci		*chgain = st_data->ch0gain;
18662306a36Sopenharmony_ci	else if (channel == 1)
18762306a36Sopenharmony_ci		*chgain = st_data->ch1gain;
18862306a36Sopenharmony_ci	else
18962306a36Sopenharmony_ci		ret = -EINVAL;
19062306a36Sopenharmony_ci	spin_unlock_irq(&mcbsp->lock);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return ret;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!st_data)
20062306a36Sopenharmony_ci		return -ENODEV;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	spin_lock_irq(&mcbsp->lock);
20362306a36Sopenharmony_ci	st_data->enabled = 1;
20462306a36Sopenharmony_ci	omap_mcbsp_st_start(mcbsp);
20562306a36Sopenharmony_ci	spin_unlock_irq(&mcbsp->lock);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
21362306a36Sopenharmony_ci	int ret = 0;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!st_data)
21662306a36Sopenharmony_ci		return -ENODEV;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	spin_lock_irq(&mcbsp->lock);
21962306a36Sopenharmony_ci	omap_mcbsp_st_stop(mcbsp);
22062306a36Sopenharmony_ci	st_data->enabled = 0;
22162306a36Sopenharmony_ci	spin_unlock_irq(&mcbsp->lock);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return ret;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (!st_data)
23162306a36Sopenharmony_ci		return -ENODEV;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return st_data->enabled;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic ssize_t st_taps_show(struct device *dev,
23762306a36Sopenharmony_ci			    struct device_attribute *attr, char *buf)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
24062306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
24162306a36Sopenharmony_ci	ssize_t status = 0;
24262306a36Sopenharmony_ci	int i;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	spin_lock_irq(&mcbsp->lock);
24562306a36Sopenharmony_ci	for (i = 0; i < st_data->nr_taps; i++)
24662306a36Sopenharmony_ci		status += sysfs_emit_at(buf, status, (i ? ", %d" : "%d"),
24762306a36Sopenharmony_ci					st_data->taps[i]);
24862306a36Sopenharmony_ci	if (i)
24962306a36Sopenharmony_ci		status += sysfs_emit_at(buf, status, "\n");
25062306a36Sopenharmony_ci	spin_unlock_irq(&mcbsp->lock);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	return status;
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic ssize_t st_taps_store(struct device *dev,
25662306a36Sopenharmony_ci			     struct device_attribute *attr,
25762306a36Sopenharmony_ci			     const char *buf, size_t size)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
26062306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
26162306a36Sopenharmony_ci	int val, tmp, status, i = 0;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	spin_lock_irq(&mcbsp->lock);
26462306a36Sopenharmony_ci	memset(st_data->taps, 0, sizeof(st_data->taps));
26562306a36Sopenharmony_ci	st_data->nr_taps = 0;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	do {
26862306a36Sopenharmony_ci		status = sscanf(buf, "%d%n", &val, &tmp);
26962306a36Sopenharmony_ci		if (status < 0 || status == 0) {
27062306a36Sopenharmony_ci			size = -EINVAL;
27162306a36Sopenharmony_ci			goto out;
27262306a36Sopenharmony_ci		}
27362306a36Sopenharmony_ci		if (val < -32768 || val > 32767) {
27462306a36Sopenharmony_ci			size = -EINVAL;
27562306a36Sopenharmony_ci			goto out;
27662306a36Sopenharmony_ci		}
27762306a36Sopenharmony_ci		st_data->taps[i++] = val;
27862306a36Sopenharmony_ci		buf += tmp;
27962306a36Sopenharmony_ci		if (*buf != ',')
28062306a36Sopenharmony_ci			break;
28162306a36Sopenharmony_ci		buf++;
28262306a36Sopenharmony_ci	} while (1);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	st_data->nr_taps = i;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ciout:
28762306a36Sopenharmony_ci	spin_unlock_irq(&mcbsp->lock);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return size;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(st_taps);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic const struct attribute *sidetone_attrs[] = {
29562306a36Sopenharmony_ci	&dev_attr_st_taps.attr,
29662306a36Sopenharmony_ci	NULL,
29762306a36Sopenharmony_ci};
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic const struct attribute_group sidetone_attr_group = {
30062306a36Sopenharmony_ci	.attrs = (struct attribute **)sidetone_attrs,
30162306a36Sopenharmony_ci};
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ciint omap_mcbsp_st_start(struct omap_mcbsp *mcbsp)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (st_data->enabled && !st_data->running) {
30862306a36Sopenharmony_ci		omap_mcbsp_st_fir_write(mcbsp, st_data->taps);
30962306a36Sopenharmony_ci		omap_mcbsp_st_chgain(mcbsp);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci		if (!mcbsp->free) {
31262306a36Sopenharmony_ci			omap_mcbsp_st_on(mcbsp);
31362306a36Sopenharmony_ci			st_data->running = 1;
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	return 0;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ciint omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (st_data->running) {
32562306a36Sopenharmony_ci		if (!mcbsp->free) {
32662306a36Sopenharmony_ci			omap_mcbsp_st_off(mcbsp);
32762306a36Sopenharmony_ci			st_data->running = 0;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return 0;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ciint omap_mcbsp_st_init(struct platform_device *pdev)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
33762306a36Sopenharmony_ci	struct omap_mcbsp_st_data *st_data;
33862306a36Sopenharmony_ci	struct resource *res;
33962306a36Sopenharmony_ci	int ret;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
34262306a36Sopenharmony_ci	if (!res)
34362306a36Sopenharmony_ci		return 0;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL);
34662306a36Sopenharmony_ci	if (!st_data)
34762306a36Sopenharmony_ci		return -ENOMEM;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	st_data->mcbsp_iclk = devm_clk_get(mcbsp->dev, "ick");
35062306a36Sopenharmony_ci	if (IS_ERR(st_data->mcbsp_iclk)) {
35162306a36Sopenharmony_ci		dev_warn(mcbsp->dev,
35262306a36Sopenharmony_ci			 "Failed to get ick, sidetone might be broken\n");
35362306a36Sopenharmony_ci		st_data->mcbsp_iclk = NULL;
35462306a36Sopenharmony_ci	}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start,
35762306a36Sopenharmony_ci					   resource_size(res));
35862306a36Sopenharmony_ci	if (!st_data->io_base_st)
35962306a36Sopenharmony_ci		return -ENOMEM;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	ret = devm_device_add_group(mcbsp->dev, &sidetone_attr_group);
36262306a36Sopenharmony_ci	if (ret)
36362306a36Sopenharmony_ci		return ret;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	mcbsp->st_data = st_data;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	return 0;
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
37162306a36Sopenharmony_ci				    struct snd_ctl_elem_info *uinfo)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct soc_mixer_control *mc =
37462306a36Sopenharmony_ci		(struct soc_mixer_control *)kcontrol->private_value;
37562306a36Sopenharmony_ci	int max = mc->max;
37662306a36Sopenharmony_ci	int min = mc->min;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
37962306a36Sopenharmony_ci	uinfo->count = 1;
38062306a36Sopenharmony_ci	uinfo->value.integer.min = min;
38162306a36Sopenharmony_ci	uinfo->value.integer.max = max;
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel)				\
38662306a36Sopenharmony_cistatic int								\
38762306a36Sopenharmony_ciomap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc,		\
38862306a36Sopenharmony_ci				       struct snd_ctl_elem_value *uc)	\
38962306a36Sopenharmony_ci{									\
39062306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc);		\
39162306a36Sopenharmony_ci	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);	\
39262306a36Sopenharmony_ci	struct soc_mixer_control *mc =					\
39362306a36Sopenharmony_ci		(struct soc_mixer_control *)kc->private_value;		\
39462306a36Sopenharmony_ci	int max = mc->max;						\
39562306a36Sopenharmony_ci	int min = mc->min;						\
39662306a36Sopenharmony_ci	int val = uc->value.integer.value[0];				\
39762306a36Sopenharmony_ci									\
39862306a36Sopenharmony_ci	if (val < min || val > max)					\
39962306a36Sopenharmony_ci		return -EINVAL;						\
40062306a36Sopenharmony_ci									\
40162306a36Sopenharmony_ci	/* OMAP McBSP implementation uses index values 0..4 */		\
40262306a36Sopenharmony_ci	return omap_mcbsp_st_set_chgain(mcbsp, channel, val);		\
40362306a36Sopenharmony_ci}									\
40462306a36Sopenharmony_ci									\
40562306a36Sopenharmony_cistatic int								\
40662306a36Sopenharmony_ciomap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc,		\
40762306a36Sopenharmony_ci				       struct snd_ctl_elem_value *uc)	\
40862306a36Sopenharmony_ci{									\
40962306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc);		\
41062306a36Sopenharmony_ci	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);	\
41162306a36Sopenharmony_ci	s16 chgain;							\
41262306a36Sopenharmony_ci									\
41362306a36Sopenharmony_ci	if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain))		\
41462306a36Sopenharmony_ci		return -EAGAIN;						\
41562306a36Sopenharmony_ci									\
41662306a36Sopenharmony_ci	uc->value.integer.value[0] = chgain;				\
41762306a36Sopenharmony_ci	return 0;							\
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ciOMAP_MCBSP_ST_CHANNEL_VOLUME(0)
42162306a36Sopenharmony_ciOMAP_MCBSP_ST_CHANNEL_VOLUME(1)
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
42462306a36Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
42762306a36Sopenharmony_ci	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
42862306a36Sopenharmony_ci	u8 value = ucontrol->value.integer.value[0];
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (value == omap_mcbsp_st_is_enabled(mcbsp))
43162306a36Sopenharmony_ci		return 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (value)
43462306a36Sopenharmony_ci		omap_mcbsp_st_enable(mcbsp);
43562306a36Sopenharmony_ci	else
43662306a36Sopenharmony_ci		omap_mcbsp_st_disable(mcbsp);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	return 1;
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol,
44262306a36Sopenharmony_ci				  struct snd_ctl_elem_value *ucontrol)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
44562306a36Sopenharmony_ci	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp);
44862306a36Sopenharmony_ci	return 0;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax,		\
45262306a36Sopenharmony_ci				      xhandler_get, xhandler_put)	\
45362306a36Sopenharmony_ci{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,		\
45462306a36Sopenharmony_ci	.info = omap_mcbsp_st_info_volsw,				\
45562306a36Sopenharmony_ci	.get = xhandler_get, .put = xhandler_put,			\
45662306a36Sopenharmony_ci	.private_value = (unsigned long)&(struct soc_mixer_control)	\
45762306a36Sopenharmony_ci	{.min = xmin, .max = xmax} }
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci#define OMAP_MCBSP_ST_CONTROLS(port)					  \
46062306a36Sopenharmony_cistatic const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \
46162306a36Sopenharmony_ciSOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0,		  \
46262306a36Sopenharmony_ci	       omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode),		  \
46362306a36Sopenharmony_ciOMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \
46462306a36Sopenharmony_ci			      -32768, 32767,				  \
46562306a36Sopenharmony_ci			      omap_mcbsp_get_st_ch0_volume,		  \
46662306a36Sopenharmony_ci			      omap_mcbsp_set_st_ch0_volume),		  \
46762306a36Sopenharmony_ciOMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \
46862306a36Sopenharmony_ci			      -32768, 32767,				  \
46962306a36Sopenharmony_ci			      omap_mcbsp_get_st_ch1_volume,		  \
47062306a36Sopenharmony_ci			      omap_mcbsp_set_st_ch1_volume),		  \
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ciOMAP_MCBSP_ST_CONTROLS(2);
47462306a36Sopenharmony_ciOMAP_MCBSP_ST_CONTROLS(3);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ciint omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
47962306a36Sopenharmony_ci	struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (!mcbsp->st_data) {
48262306a36Sopenharmony_ci		dev_warn(mcbsp->dev, "No sidetone data for port\n");
48362306a36Sopenharmony_ci		return 0;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	switch (port_id) {
48762306a36Sopenharmony_ci	case 2: /* McBSP 2 */
48862306a36Sopenharmony_ci		return snd_soc_add_dai_controls(cpu_dai,
48962306a36Sopenharmony_ci					omap_mcbsp2_st_controls,
49062306a36Sopenharmony_ci					ARRAY_SIZE(omap_mcbsp2_st_controls));
49162306a36Sopenharmony_ci	case 3: /* McBSP 3 */
49262306a36Sopenharmony_ci		return snd_soc_add_dai_controls(cpu_dai,
49362306a36Sopenharmony_ci					omap_mcbsp3_st_controls,
49462306a36Sopenharmony_ci					ARRAY_SIZE(omap_mcbsp3_st_controls));
49562306a36Sopenharmony_ci	default:
49662306a36Sopenharmony_ci		dev_err(mcbsp->dev, "Port %d not supported\n", port_id);
49762306a36Sopenharmony_ci		break;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return -EINVAL;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
503