@@ -132,6 +132,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
},
{
.phys_base = OMAP34XX_MCBSP2_BASE,
+ .phys_base_st = OMAP34XX_MCBSP2_ST_BASE,
.dma_rx_sync = OMAP24XX_DMA_MCBSP2_RX,
.dma_tx_sync = OMAP24XX_DMA_MCBSP2_TX,
.rx_irq = INT_24XX_MCBSP2_IRQ_RX,
@@ -141,6 +142,7 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
},
{
.phys_base = OMAP34XX_MCBSP3_BASE,
+ .phys_base_st = OMAP34XX_MCBSP3_ST_BASE,
.dma_rx_sync = OMAP24XX_DMA_MCBSP3_RX,
.dma_tx_sync = OMAP24XX_DMA_MCBSP3_TX,
.rx_irq = INT_24XX_MCBSP3_IRQ_RX,
@@ -49,6 +49,9 @@
#define OMAP34XX_MCBSP1_BASE 0x48074000
#define OMAP34XX_MCBSP2_BASE 0x49022000
+#define OMAP34XX_MCBSP2_ST_BASE 0x49028000
+#define OMAP34XX_MCBSP3_BASE 0x49024000
+#define OMAP34XX_MCBSP3_ST_BASE 0x4902A000
#define OMAP34XX_MCBSP3_BASE 0x49024000
#define OMAP34XX_MCBSP4_BASE 0x49026000
#define OMAP34XX_MCBSP5_BASE 0x48096000
@@ -147,6 +150,15 @@
#define OMAP_MCBSP_REG_WAKEUPEN 0xA8
#define OMAP_MCBSP_REG_XCCR 0xAC
#define OMAP_MCBSP_REG_RCCR 0xB0
+#define OMAP_MCBSP_REG_SSELCR 0xBC
+
+#define OMAP_ST_REG_REV 0x00
+#define OMAP_ST_REG_SYSCONFIG 0x10
+#define OMAP_ST_REG_IRQSTATUS 0x18
+#define OMAP_ST_REG_IRQENABLE 0x1C
+#define OMAP_ST_REG_SGAINCR 0x24
+#define OMAP_ST_REG_SFIRCR 0x28
+#define OMAP_ST_REG_SSELCR 0x2C
#define AUDIO_MCBSP_DATAWRITE (OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1)
#define AUDIO_MCBSP_DATAREAD (OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1)
@@ -265,6 +277,24 @@
#define ENAWAKEUP 0x0004
#define SOFTRST 0x0002
+/********************** McBSP SSELCR bit definitions ***********************/
+#define SIDETONEEN 0x0400
+
+/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/
+#define ST_AUTOIDLE 0x0001
+
+/********************** McBSP Sidetone SGAINCR bit definitions *************/
+#define ST_CH1GAIN(value) ((value<<16)) /* Bits 16:31 */
+#define ST_CH0GAIN(value) (value) /* Bits 0:15 */
+
+/********************** McBSP Sidetone SFIRCR bit definitions **************/
+#define ST_FIRCOEFF(value) (value) /* Bits 0:15 */
+
+/********************** McBSP Sidetone SSELCR bit definitions **************/
+#define ST_COEFFWRDONE 0x0004
+#define ST_COEFFWREN 0x0002
+#define ST_SIDETONEEN 0x0001
+
/********************** McBSP DMA operating modes **************************/
#define MCBSP_DMA_MODE_ELEMENT 0
#define MCBSP_DMA_MODE_THRESHOLD 1
@@ -375,10 +405,25 @@ struct omap_mcbsp_platform_data {
u16 rx_irq, tx_irq;
struct omap_mcbsp_ops *ops;
#ifdef CONFIG_ARCH_OMAP34XX
+ /* Sidetone block for McBSP 2 and 3 */
+ unsigned long phys_base_st;
u16 buffer_size;
#endif
};
+#define OMAP_MCBSP_ST_CHANNEL_0 (1 << 0)
+#define OMAP_MCBSP_ST_CHANNEL_1 (1 << 1)
+
+struct omap_mcbsp_st_data {
+ void __iomem *io_base_st;
+ bool running;
+ bool enabled;
+ s16 taps[128]; /* Sidetone filter coefficients */
+ int nr_taps; /* Number of filter coefficients in use */
+ s16 ch0gain;
+ s16 ch1gain;
+};
+
struct omap_mcbsp {
struct device *dev;
unsigned long phys_base;
@@ -411,6 +456,7 @@ struct omap_mcbsp {
struct clk *iclk;
struct clk *fclk;
#ifdef CONFIG_ARCH_OMAP34XX
+ struct omap_mcbsp_st_data *st_data;
int dma_op_mode;
u16 max_tx_thres;
u16 max_rx_thres;
@@ -459,4 +505,21 @@ int omap_mcbsp_pollread(unsigned int id, u16 * buf);
int omap_mcbsp_pollwrite(unsigned int id, u16 buf);
int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type);
+#ifdef CONFIG_ARCH_OMAP34XX
+/* Sidetone specific API */
+int omap_st_set_chgain(unsigned int id, s16 ch0gain, s16 ch1gain, int channels);
+int omap_st_get_chgain(unsigned int id, s16 *ch0gain, s16 *ch1gain);
+int omap_st_enable(unsigned int id);
+int omap_st_disable(unsigned int id);
+int omap_st_is_enabled(unsigned int id);
+#else
+static inline int omap_st_set_chgain(unsigned int id, s16 ch0gain,
+ s16 ch1gain, int channels) { return 0; }
+static inline int omap_st_get_chgain(unsigned int id, s16 *ch0gain,
+ s16 *ch1gain) { return 0; }
+static inline int omap_st_enable(unsigned int id) { return 0; }
+static inline int omap_st_disable(unsigned int id) { return 0; }
+static inline int omap_st_is_enabled(unsigned int id) { return 0; }
+#endif
+
#endif
@@ -26,6 +26,9 @@
#include <plat/dma.h>
#include <plat/mcbsp.h>
+#ifdef CONFIG_ARCH_OMAP34XX
+#include "../mach-omap2/cm-regbits-34xx.h"
+#endif
struct omap_mcbsp **mcbsp_ptr;
int omap_mcbsp_count;
@@ -54,6 +57,11 @@ int omap_mcbsp_read(void __iomem *io_base, u16 reg)
#define omap_mcbsp_check_valid_id(id) (id < omap_mcbsp_count)
#define id_to_mcbsp_ptr(id) mcbsp_ptr[id];
+#define OMAP_ST_READ(base, reg) \
+ omap_mcbsp_read(base, OMAP_ST_REG_##reg)
+#define OMAP_ST_WRITE(base, reg, val) \
+ omap_mcbsp_write(base, OMAP_ST_REG_##reg, val)
+
static void omap_mcbsp_dump_reg(u8 id)
{
struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id);
@@ -199,6 +207,268 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
EXPORT_SYMBOL(omap_mcbsp_config);
#ifdef CONFIG_ARCH_OMAP34XX
+static void omap_st_on(struct omap_mcbsp *mcbsp)
+{
+ void __iomem *io_base_mcbsp;
+ void __iomem *io_base_st;
+ unsigned int w;
+
+ io_base_mcbsp = mcbsp->io_base;
+ io_base_st = mcbsp->st_data->io_base_st;
+
+ /*
+ * Sidetone uses McBSP ICLK - which must not idle when sidetones
+ * are enabled or sidetones start sounding ugly.
+ */
+ w = cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
+ w &= ~(1 << (mcbsp->id - 2));
+ cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
+
+ /* Enable McBSP Sidetone */
+ w = OMAP_MCBSP_READ(io_base_mcbsp, SSELCR);
+ OMAP_MCBSP_WRITE(io_base_mcbsp, SSELCR, w | SIDETONEEN);
+
+ w = OMAP_ST_READ(io_base_st, SYSCONFIG);
+ OMAP_ST_WRITE(io_base_st, SYSCONFIG, w & ~(ST_AUTOIDLE));
+
+ /* Enable Sidetone from Sidetone Core */
+ w = OMAP_ST_READ(io_base_st, SSELCR);
+ OMAP_ST_WRITE(io_base_st, SSELCR, w | ST_SIDETONEEN);
+}
+
+static void omap_st_off(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data;
+ void __iomem *io_base_mcbsp;
+ void __iomem *io_base_st;
+ unsigned int w;
+
+ st_data = mcbsp->st_data;
+ io_base_mcbsp = mcbsp->io_base;
+ io_base_st = mcbsp->st_data->io_base_st;
+
+ w = OMAP_ST_READ(io_base_st, SSELCR);
+ OMAP_ST_WRITE(io_base_st, SSELCR, w & ~(ST_SIDETONEEN));
+
+ w = OMAP_ST_READ(io_base_st, SYSCONFIG);
+ OMAP_ST_WRITE(io_base_st, SYSCONFIG, w | ST_AUTOIDLE);
+
+ w = OMAP_MCBSP_READ(io_base_mcbsp, SSELCR);
+ OMAP_MCBSP_WRITE(io_base_mcbsp, SSELCR, w & ~(SIDETONEEN));
+
+ w = cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
+ w |= 1 << (mcbsp->id - 2);
+ cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
+}
+
+static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
+{
+ void __iomem *io_base;
+ u16 val, i;
+
+ io_base = mcbsp->st_data->io_base_st;
+
+ val = OMAP_ST_READ(io_base, SYSCONFIG);
+ OMAP_ST_WRITE(io_base, SYSCONFIG, val & ~(ST_AUTOIDLE));
+
+ val = OMAP_ST_READ(io_base, SSELCR);
+
+ if (val & ST_COEFFWREN)
+ OMAP_ST_WRITE(io_base, SSELCR, val & ~(ST_COEFFWREN));
+
+ OMAP_ST_WRITE(io_base, SSELCR, val | ST_COEFFWREN);
+
+ for (i = 0; i < 128; i++)
+ OMAP_ST_WRITE(io_base, SFIRCR, fir[i]);
+
+ i = 0;
+
+ val = OMAP_ST_READ(io_base, SSELCR);
+ while (!(val & ST_COEFFWRDONE) && (++i < 1000))
+ val = OMAP_ST_READ(io_base, SSELCR);
+
+ OMAP_ST_WRITE(io_base, SSELCR, val & ~(ST_COEFFWREN));
+
+ if (i == 1000)
+ dev_err(mcbsp->dev, "McBSP FIR load error!\n");
+}
+
+static void omap_st_chgain(struct omap_mcbsp_st_data *st_data)
+{
+ void __iomem *io_base;
+ u16 w;
+
+ io_base = st_data->io_base_st;
+
+ w = OMAP_ST_READ(io_base, SYSCONFIG);
+ OMAP_ST_WRITE(io_base, SYSCONFIG, w & ~(ST_AUTOIDLE));
+
+ w = OMAP_ST_READ(io_base, SSELCR);
+
+ OMAP_ST_WRITE(io_base, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \
+ ST_CH1GAIN(st_data->ch1gain));
+}
+
+int omap_st_set_chgain(unsigned int id, s16 ch0gain, s16 ch1gain,
+ int channels)
+{
+ struct omap_mcbsp *mcbsp;
+ struct omap_mcbsp_st_data *st_data;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+ st_data = mcbsp->st_data;
+
+ if (!st_data)
+ return -ENOENT;
+
+ spin_lock_irq(&mcbsp->lock);
+ if (channels & OMAP_MCBSP_ST_CHANNEL_0)
+ st_data->ch0gain = ch0gain;
+
+ if (channels & OMAP_MCBSP_ST_CHANNEL_1)
+ st_data->ch1gain = ch1gain;
+
+ if (st_data->enabled)
+ omap_st_chgain(st_data);
+ spin_unlock_irq(&mcbsp->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_st_set_chgain);
+
+int omap_st_get_chgain(unsigned int id, s16 *ch0gain, s16 *ch1gain)
+{
+ struct omap_mcbsp *mcbsp;
+ struct omap_mcbsp_st_data *st_data;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+ st_data = mcbsp->st_data;
+
+ if (!st_data)
+ return -ENOENT;
+
+ spin_lock_irq(&mcbsp->lock);
+ *ch0gain = st_data->ch0gain;
+ *ch1gain = st_data->ch1gain;
+ spin_unlock_irq(&mcbsp->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_st_get_chgain);
+
+static int omap_st_start(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ if (st_data && st_data->enabled && !st_data->running) {
+ omap_st_fir_write(mcbsp, st_data->taps);
+ omap_st_chgain(st_data);
+
+ if (!mcbsp->free) {
+ omap_st_on(mcbsp);
+ st_data->running = 1;
+ }
+ }
+
+ return 0;
+}
+
+int omap_st_enable(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+ struct omap_mcbsp_st_data *st_data;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+ st_data = mcbsp->st_data;
+
+ if (!st_data)
+ return -ENODEV;
+
+ spin_lock_irq(&mcbsp->lock);
+ st_data->enabled = 1;
+ omap_st_start(mcbsp);
+ spin_unlock_irq(&mcbsp->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(omap_st_enable);
+
+static int omap_st_stop(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ if (st_data && st_data->running) {
+ if (!mcbsp->free) {
+ omap_st_off(mcbsp);
+ st_data->running = 0;
+ }
+ }
+
+ return 0;
+}
+
+int omap_st_disable(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+ struct omap_mcbsp_st_data *st_data;
+ int ret = 0;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+ st_data = mcbsp->st_data;
+
+ if (!st_data)
+ return -ENODEV;
+
+ spin_lock_irq(&mcbsp->lock);
+ omap_st_stop(mcbsp);
+ st_data->enabled = 0;
+ spin_unlock_irq(&mcbsp->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(omap_st_disable);
+
+int omap_st_is_enabled(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+ struct omap_mcbsp_st_data *st_data;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+ st_data = mcbsp->st_data;
+
+ if (!st_data)
+ return -ENODEV;
+
+
+ return st_data->enabled;
+}
+EXPORT_SYMBOL(omap_st_is_enabled);
+
/*
* omap_mcbsp_set_tx_threshold configures how to deal
* with transmit threshold. the threshold value and handler can be
@@ -356,6 +626,8 @@ static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
#else
static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
+static inline void omap_st_start(struct omap_mcbsp *mcbsp) {}
+static inline void omap_st_stop(struct omap_mcbsp *mcbsp) {}
#endif
/*
@@ -526,6 +798,9 @@ void omap_mcbsp_start(unsigned int id, int tx, int rx)
mcbsp = id_to_mcbsp_ptr(id);
io_base = mcbsp->io_base;
+ if (cpu_is_omap34xx())
+ omap_st_start(mcbsp);
+
mcbsp->rx_word_length = (OMAP_MCBSP_READ(io_base, RCR1) >> 5) & 0x7;
mcbsp->tx_word_length = (OMAP_MCBSP_READ(io_base, XCR1) >> 5) & 0x7;
@@ -619,6 +894,9 @@ void omap_mcbsp_stop(unsigned int id, int tx, int rx)
w = OMAP_MCBSP_READ(io_base, SPCR2);
OMAP_MCBSP_WRITE(io_base, SPCR2, w & ~(1 << 6));
}
+
+ if (cpu_is_omap34xx())
+ omap_st_stop(mcbsp);
}
EXPORT_SYMBOL(omap_mcbsp_stop);
@@ -1198,6 +1476,64 @@ unlock:
static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
+static ssize_t st_taps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ ssize_t status = 0;
+ int i;
+
+ spin_lock_irq(&mcbsp->lock);
+ for (i = 0; i < st_data->nr_taps; i++)
+ status += sprintf(&buf[status], (i ? ", %d" : "%d"),
+ st_data->taps[i]);
+ if (i)
+ status += sprintf(&buf[status], "\n");
+ spin_unlock_irq(&mcbsp->lock);
+
+ return status;
+}
+
+static ssize_t st_taps_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ int val, tmp, status, i = 0;
+
+ spin_lock_irq(&mcbsp->lock);
+ memset(st_data->taps, 0, sizeof(st_data->taps));
+ st_data->nr_taps = 0;
+
+ do {
+ status = sscanf(buf, "%d%n", &val, &tmp);
+ if (status < 0 || status == 0) {
+ size = -EINVAL;
+ goto out;
+ }
+ if (val < -32768 || val > 32767) {
+ size = -EINVAL;
+ goto out;
+ }
+ st_data->taps[i++] = val;
+ buf += tmp;
+ if (*buf != ',')
+ break;
+ buf++;
+ } while (1);
+
+ st_data->nr_taps = i;
+
+out:
+ spin_unlock_irq(&mcbsp->lock);
+
+ return size;
+}
+
+static DEVICE_ATTR(st_taps, 0644, st_taps_show, st_taps_store);
+
static const struct attribute *additional_attrs[] = {
&dev_attr_max_tx_thres.attr,
&dev_attr_max_rx_thres.attr,
@@ -1219,6 +1555,60 @@ static inline void __devexit omap_additional_remove(struct device *dev)
sysfs_remove_group(&dev->kobj, &additional_attr_group);
}
+static const struct attribute *sidetone_attrs[] = {
+ &dev_attr_st_taps.attr,
+ NULL,
+};
+
+static const struct attribute_group sidetone_attr_group = {
+ .attrs = (struct attribute **)sidetone_attrs,
+};
+
+int __devinit omap_st_add(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_platform_data *pdata = mcbsp->pdata;
+ struct omap_mcbsp_st_data *st_data;
+ int err;
+
+ st_data = kzalloc(sizeof(*mcbsp->st_data), GFP_KERNEL);
+ if (!st_data) {
+ err = -ENOMEM;
+ goto err1;
+ }
+
+ st_data->io_base_st = ioremap(pdata->phys_base_st, SZ_4K);
+ if (!st_data->io_base_st) {
+ err = -ENOMEM;
+ goto err2;
+ }
+
+ err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
+ if (err)
+ goto err3;
+
+ mcbsp->st_data = st_data;
+ return 0;
+
+err3:
+ iounmap(st_data->io_base_st);
+err2:
+ kfree(st_data);
+err1:
+ return err;
+
+}
+
+static void __devexit omap_st_remove(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ if (st_data) {
+ sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
+ iounmap(st_data->io_base_st);
+ kfree(st_data);
+ }
+}
+
static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
{
mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
@@ -1232,6 +1622,12 @@ static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
if (omap_additional_add(mcbsp->dev))
dev_warn(mcbsp->dev,
"Unable to create additional controls\n");
+
+ if (mcbsp->id == 2 || mcbsp->id == 3)
+ if (omap_st_add(mcbsp))
+ dev_warn(mcbsp->dev,
+ "Unable to create sidetone controls\n");
+
} else {
mcbsp->max_tx_thres = -EINVAL;
mcbsp->max_rx_thres = -EINVAL;
@@ -1240,8 +1636,12 @@ static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
{
- if (cpu_is_omap34xx())
+ if (cpu_is_omap34xx()) {
omap_additional_remove(mcbsp->dev);
+
+ if (mcbsp->id == 2 || mcbsp->id == 3)
+ omap_st_remove(mcbsp);
+ }
}
#else
static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}