18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * McBSP Sidetone support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Nokia Corporation 68c2ecf20Sopenharmony_ci * Author: Samuel Ortiz <samuel.ortiz@nokia.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> 98c2ecf20Sopenharmony_ci * Peter Ujfalusi <peter.ujfalusi@ti.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/device.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/clk.h> 198c2ecf20Sopenharmony_ci#include <linux/delay.h> 208c2ecf20Sopenharmony_ci#include <linux/io.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "omap-mcbsp.h" 258c2ecf20Sopenharmony_ci#include "omap-mcbsp-priv.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* OMAP3 sidetone control registers */ 288c2ecf20Sopenharmony_ci#define OMAP_ST_REG_REV 0x00 298c2ecf20Sopenharmony_ci#define OMAP_ST_REG_SYSCONFIG 0x10 308c2ecf20Sopenharmony_ci#define OMAP_ST_REG_IRQSTATUS 0x18 318c2ecf20Sopenharmony_ci#define OMAP_ST_REG_IRQENABLE 0x1C 328c2ecf20Sopenharmony_ci#define OMAP_ST_REG_SGAINCR 0x24 338c2ecf20Sopenharmony_ci#define OMAP_ST_REG_SFIRCR 0x28 348c2ecf20Sopenharmony_ci#define OMAP_ST_REG_SSELCR 0x2C 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/********************** McBSP SSELCR bit definitions ***********************/ 378c2ecf20Sopenharmony_ci#define SIDETONEEN BIT(10) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ 408c2ecf20Sopenharmony_ci#define ST_AUTOIDLE BIT(0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/********************** McBSP Sidetone SGAINCR bit definitions *************/ 438c2ecf20Sopenharmony_ci#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ 448c2ecf20Sopenharmony_ci#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/********************** McBSP Sidetone SFIRCR bit definitions **************/ 478c2ecf20Sopenharmony_ci#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/********************** McBSP Sidetone SSELCR bit definitions **************/ 508c2ecf20Sopenharmony_ci#define ST_SIDETONEEN BIT(0) 518c2ecf20Sopenharmony_ci#define ST_COEFFWREN BIT(1) 528c2ecf20Sopenharmony_ci#define ST_COEFFWRDONE BIT(2) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct omap_mcbsp_st_data { 558c2ecf20Sopenharmony_ci void __iomem *io_base_st; 568c2ecf20Sopenharmony_ci struct clk *mcbsp_iclk; 578c2ecf20Sopenharmony_ci bool running; 588c2ecf20Sopenharmony_ci bool enabled; 598c2ecf20Sopenharmony_ci s16 taps[128]; /* Sidetone filter coefficients */ 608c2ecf20Sopenharmony_ci int nr_taps; /* Number of filter coefficients in use */ 618c2ecf20Sopenharmony_ci s16 ch0gain; 628c2ecf20Sopenharmony_ci s16 ch1gain; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci writel_relaxed(val, mcbsp->st_data->io_base_st + reg); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci return readl_relaxed(mcbsp->st_data->io_base_st + reg); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) 768c2ecf20Sopenharmony_ci#define MCBSP_ST_WRITE(mcbsp, reg, val) \ 778c2ecf20Sopenharmony_ci omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci unsigned int w; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (mcbsp->pdata->force_ick_on) 848c2ecf20Sopenharmony_ci mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Disable Sidetone clock auto-gating for normal operation */ 878c2ecf20Sopenharmony_ci w = MCBSP_ST_READ(mcbsp, SYSCONFIG); 888c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Enable McBSP Sidetone */ 918c2ecf20Sopenharmony_ci w = MCBSP_READ(mcbsp, SSELCR); 928c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Enable Sidetone from Sidetone Core */ 958c2ecf20Sopenharmony_ci w = MCBSP_ST_READ(mcbsp, SSELCR); 968c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci unsigned int w; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci w = MCBSP_ST_READ(mcbsp, SSELCR); 1048c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci w = MCBSP_READ(mcbsp, SSELCR); 1078c2ecf20Sopenharmony_ci MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Enable Sidetone clock auto-gating to reduce power consumption */ 1108c2ecf20Sopenharmony_ci w = MCBSP_ST_READ(mcbsp, SYSCONFIG); 1118c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (mcbsp->pdata->force_ick_on) 1148c2ecf20Sopenharmony_ci mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u16 val, i; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci val = MCBSP_ST_READ(mcbsp, SSELCR); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (val & ST_COEFFWREN) 1248c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci for (i = 0; i < 128; i++) 1298c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci i = 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci val = MCBSP_ST_READ(mcbsp, SSELCR); 1348c2ecf20Sopenharmony_ci while (!(val & ST_COEFFWRDONE) && (++i < 1000)) 1358c2ecf20Sopenharmony_ci val = MCBSP_ST_READ(mcbsp, SSELCR); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (i == 1000) 1408c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "McBSP FIR load error!\n"); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | 1488c2ecf20Sopenharmony_ci ST_CH1GAIN(st_data->ch1gain)); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, 1528c2ecf20Sopenharmony_ci s16 chgain) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 1558c2ecf20Sopenharmony_ci int ret = 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!st_data) 1588c2ecf20Sopenharmony_ci return -ENOENT; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci spin_lock_irq(&mcbsp->lock); 1618c2ecf20Sopenharmony_ci if (channel == 0) 1628c2ecf20Sopenharmony_ci st_data->ch0gain = chgain; 1638c2ecf20Sopenharmony_ci else if (channel == 1) 1648c2ecf20Sopenharmony_ci st_data->ch1gain = chgain; 1658c2ecf20Sopenharmony_ci else 1668c2ecf20Sopenharmony_ci ret = -EINVAL; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (st_data->enabled) 1698c2ecf20Sopenharmony_ci omap_mcbsp_st_chgain(mcbsp); 1708c2ecf20Sopenharmony_ci spin_unlock_irq(&mcbsp->lock); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return ret; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, 1768c2ecf20Sopenharmony_ci s16 *chgain) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 1798c2ecf20Sopenharmony_ci int ret = 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (!st_data) 1828c2ecf20Sopenharmony_ci return -ENOENT; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci spin_lock_irq(&mcbsp->lock); 1858c2ecf20Sopenharmony_ci if (channel == 0) 1868c2ecf20Sopenharmony_ci *chgain = st_data->ch0gain; 1878c2ecf20Sopenharmony_ci else if (channel == 1) 1888c2ecf20Sopenharmony_ci *chgain = st_data->ch1gain; 1898c2ecf20Sopenharmony_ci else 1908c2ecf20Sopenharmony_ci ret = -EINVAL; 1918c2ecf20Sopenharmony_ci spin_unlock_irq(&mcbsp->lock); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci if (!st_data) 2018c2ecf20Sopenharmony_ci return -ENODEV; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci spin_lock_irq(&mcbsp->lock); 2048c2ecf20Sopenharmony_ci st_data->enabled = 1; 2058c2ecf20Sopenharmony_ci omap_mcbsp_st_start(mcbsp); 2068c2ecf20Sopenharmony_ci spin_unlock_irq(&mcbsp->lock); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return 0; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 2148c2ecf20Sopenharmony_ci int ret = 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (!st_data) 2178c2ecf20Sopenharmony_ci return -ENODEV; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci spin_lock_irq(&mcbsp->lock); 2208c2ecf20Sopenharmony_ci omap_mcbsp_st_stop(mcbsp); 2218c2ecf20Sopenharmony_ci st_data->enabled = 0; 2228c2ecf20Sopenharmony_ci spin_unlock_irq(&mcbsp->lock); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci return ret; 2258c2ecf20Sopenharmony_ci} 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (!st_data) 2328c2ecf20Sopenharmony_ci return -ENODEV; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return st_data->enabled; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic ssize_t st_taps_show(struct device *dev, 2388c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); 2418c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 2428c2ecf20Sopenharmony_ci ssize_t status = 0; 2438c2ecf20Sopenharmony_ci int i; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci spin_lock_irq(&mcbsp->lock); 2468c2ecf20Sopenharmony_ci for (i = 0; i < st_data->nr_taps; i++) 2478c2ecf20Sopenharmony_ci status += sprintf(&buf[status], (i ? ", %d" : "%d"), 2488c2ecf20Sopenharmony_ci st_data->taps[i]); 2498c2ecf20Sopenharmony_ci if (i) 2508c2ecf20Sopenharmony_ci status += sprintf(&buf[status], "\n"); 2518c2ecf20Sopenharmony_ci spin_unlock_irq(&mcbsp->lock); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return status; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic ssize_t st_taps_store(struct device *dev, 2578c2ecf20Sopenharmony_ci struct device_attribute *attr, 2588c2ecf20Sopenharmony_ci const char *buf, size_t size) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); 2618c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 2628c2ecf20Sopenharmony_ci int val, tmp, status, i = 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci spin_lock_irq(&mcbsp->lock); 2658c2ecf20Sopenharmony_ci memset(st_data->taps, 0, sizeof(st_data->taps)); 2668c2ecf20Sopenharmony_ci st_data->nr_taps = 0; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci do { 2698c2ecf20Sopenharmony_ci status = sscanf(buf, "%d%n", &val, &tmp); 2708c2ecf20Sopenharmony_ci if (status < 0 || status == 0) { 2718c2ecf20Sopenharmony_ci size = -EINVAL; 2728c2ecf20Sopenharmony_ci goto out; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci if (val < -32768 || val > 32767) { 2758c2ecf20Sopenharmony_ci size = -EINVAL; 2768c2ecf20Sopenharmony_ci goto out; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci st_data->taps[i++] = val; 2798c2ecf20Sopenharmony_ci buf += tmp; 2808c2ecf20Sopenharmony_ci if (*buf != ',') 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci buf++; 2838c2ecf20Sopenharmony_ci } while (1); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci st_data->nr_taps = i; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ciout: 2888c2ecf20Sopenharmony_ci spin_unlock_irq(&mcbsp->lock); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return size; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(st_taps); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic const struct attribute *sidetone_attrs[] = { 2968c2ecf20Sopenharmony_ci &dev_attr_st_taps.attr, 2978c2ecf20Sopenharmony_ci NULL, 2988c2ecf20Sopenharmony_ci}; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic const struct attribute_group sidetone_attr_group = { 3018c2ecf20Sopenharmony_ci .attrs = (struct attribute **)sidetone_attrs, 3028c2ecf20Sopenharmony_ci}; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ciint omap_mcbsp_st_start(struct omap_mcbsp *mcbsp) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (st_data->enabled && !st_data->running) { 3098c2ecf20Sopenharmony_ci omap_mcbsp_st_fir_write(mcbsp, st_data->taps); 3108c2ecf20Sopenharmony_ci omap_mcbsp_st_chgain(mcbsp); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci if (!mcbsp->free) { 3138c2ecf20Sopenharmony_ci omap_mcbsp_st_on(mcbsp); 3148c2ecf20Sopenharmony_ci st_data->running = 1; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ciint omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (st_data->running) { 3268c2ecf20Sopenharmony_ci if (!mcbsp->free) { 3278c2ecf20Sopenharmony_ci omap_mcbsp_st_off(mcbsp); 3288c2ecf20Sopenharmony_ci st_data->running = 0; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ciint omap_mcbsp_st_init(struct platform_device *pdev) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); 3388c2ecf20Sopenharmony_ci struct omap_mcbsp_st_data *st_data; 3398c2ecf20Sopenharmony_ci struct resource *res; 3408c2ecf20Sopenharmony_ci int ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); 3438c2ecf20Sopenharmony_ci if (!res) 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); 3478c2ecf20Sopenharmony_ci if (!st_data) 3488c2ecf20Sopenharmony_ci return -ENOMEM; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); 3518c2ecf20Sopenharmony_ci if (IS_ERR(st_data->mcbsp_iclk)) { 3528c2ecf20Sopenharmony_ci dev_warn(mcbsp->dev, 3538c2ecf20Sopenharmony_ci "Failed to get ick, sidetone might be broken\n"); 3548c2ecf20Sopenharmony_ci st_data->mcbsp_iclk = NULL; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, 3588c2ecf20Sopenharmony_ci resource_size(res)); 3598c2ecf20Sopenharmony_ci if (!st_data->io_base_st) 3608c2ecf20Sopenharmony_ci return -ENOMEM; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); 3638c2ecf20Sopenharmony_ci if (ret) 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci mcbsp->st_data = st_data; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_civoid omap_mcbsp_st_cleanup(struct platform_device *pdev) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (mcbsp->st_data) { 3768c2ecf20Sopenharmony_ci sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); 3778c2ecf20Sopenharmony_ci clk_put(mcbsp->st_data->mcbsp_iclk); 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, 3828c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = 3858c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 3868c2ecf20Sopenharmony_ci int max = mc->max; 3878c2ecf20Sopenharmony_ci int min = mc->min; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 3908c2ecf20Sopenharmony_ci uinfo->count = 1; 3918c2ecf20Sopenharmony_ci uinfo->value.integer.min = min; 3928c2ecf20Sopenharmony_ci uinfo->value.integer.max = max; 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ 3978c2ecf20Sopenharmony_cistatic int \ 3988c2ecf20Sopenharmony_ciomap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ 3998c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uc) \ 4008c2ecf20Sopenharmony_ci{ \ 4018c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ 4028c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ 4038c2ecf20Sopenharmony_ci struct soc_mixer_control *mc = \ 4048c2ecf20Sopenharmony_ci (struct soc_mixer_control *)kc->private_value; \ 4058c2ecf20Sopenharmony_ci int max = mc->max; \ 4068c2ecf20Sopenharmony_ci int min = mc->min; \ 4078c2ecf20Sopenharmony_ci int val = uc->value.integer.value[0]; \ 4088c2ecf20Sopenharmony_ci \ 4098c2ecf20Sopenharmony_ci if (val < min || val > max) \ 4108c2ecf20Sopenharmony_ci return -EINVAL; \ 4118c2ecf20Sopenharmony_ci \ 4128c2ecf20Sopenharmony_ci /* OMAP McBSP implementation uses index values 0..4 */ \ 4138c2ecf20Sopenharmony_ci return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \ 4148c2ecf20Sopenharmony_ci} \ 4158c2ecf20Sopenharmony_ci \ 4168c2ecf20Sopenharmony_cistatic int \ 4178c2ecf20Sopenharmony_ciomap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ 4188c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *uc) \ 4198c2ecf20Sopenharmony_ci{ \ 4208c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ 4218c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ 4228c2ecf20Sopenharmony_ci s16 chgain; \ 4238c2ecf20Sopenharmony_ci \ 4248c2ecf20Sopenharmony_ci if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \ 4258c2ecf20Sopenharmony_ci return -EAGAIN; \ 4268c2ecf20Sopenharmony_ci \ 4278c2ecf20Sopenharmony_ci uc->value.integer.value[0] = chgain; \ 4288c2ecf20Sopenharmony_ci return 0; \ 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ciOMAP_MCBSP_ST_CHANNEL_VOLUME(0) 4328c2ecf20Sopenharmony_ciOMAP_MCBSP_ST_CHANNEL_VOLUME(1) 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, 4358c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); 4388c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 4398c2ecf20Sopenharmony_ci u8 value = ucontrol->value.integer.value[0]; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (value == omap_mcbsp_st_is_enabled(mcbsp)) 4428c2ecf20Sopenharmony_ci return 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (value) 4458c2ecf20Sopenharmony_ci omap_mcbsp_st_enable(mcbsp); 4468c2ecf20Sopenharmony_ci else 4478c2ecf20Sopenharmony_ci omap_mcbsp_st_disable(mcbsp); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 1; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, 4538c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); 4568c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp); 4598c2ecf20Sopenharmony_ci return 0; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ 4638c2ecf20Sopenharmony_ci xhandler_get, xhandler_put) \ 4648c2ecf20Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 4658c2ecf20Sopenharmony_ci .info = omap_mcbsp_st_info_volsw, \ 4668c2ecf20Sopenharmony_ci .get = xhandler_get, .put = xhandler_put, \ 4678c2ecf20Sopenharmony_ci .private_value = (unsigned long)&(struct soc_mixer_control) \ 4688c2ecf20Sopenharmony_ci {.min = xmin, .max = xmax} } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci#define OMAP_MCBSP_ST_CONTROLS(port) \ 4718c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ 4728c2ecf20Sopenharmony_ciSOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ 4738c2ecf20Sopenharmony_ci omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ 4748c2ecf20Sopenharmony_ciOMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ 4758c2ecf20Sopenharmony_ci -32768, 32767, \ 4768c2ecf20Sopenharmony_ci omap_mcbsp_get_st_ch0_volume, \ 4778c2ecf20Sopenharmony_ci omap_mcbsp_set_st_ch0_volume), \ 4788c2ecf20Sopenharmony_ciOMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ 4798c2ecf20Sopenharmony_ci -32768, 32767, \ 4808c2ecf20Sopenharmony_ci omap_mcbsp_get_st_ch1_volume, \ 4818c2ecf20Sopenharmony_ci omap_mcbsp_set_st_ch1_volume), \ 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ciOMAP_MCBSP_ST_CONTROLS(2); 4858c2ecf20Sopenharmony_ciOMAP_MCBSP_ST_CONTROLS(3); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ciint omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 4908c2ecf20Sopenharmony_ci struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (!mcbsp->st_data) { 4938c2ecf20Sopenharmony_ci dev_warn(mcbsp->dev, "No sidetone data for port\n"); 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci switch (port_id) { 4988c2ecf20Sopenharmony_ci case 2: /* McBSP 2 */ 4998c2ecf20Sopenharmony_ci return snd_soc_add_dai_controls(cpu_dai, 5008c2ecf20Sopenharmony_ci omap_mcbsp2_st_controls, 5018c2ecf20Sopenharmony_ci ARRAY_SIZE(omap_mcbsp2_st_controls)); 5028c2ecf20Sopenharmony_ci case 3: /* McBSP 3 */ 5038c2ecf20Sopenharmony_ci return snd_soc_add_dai_controls(cpu_dai, 5048c2ecf20Sopenharmony_ci omap_mcbsp3_st_controls, 5058c2ecf20Sopenharmony_ci ARRAY_SIZE(omap_mcbsp3_st_controls)); 5068c2ecf20Sopenharmony_ci default: 5078c2ecf20Sopenharmony_ci dev_err(mcbsp->dev, "Port %d not supported\n", port_id); 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return -EINVAL; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); 514