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