#include <mach/dma.h>
#include <mach/mcbsp.h>
+#ifdef CONFIG_ARCH_OMAP34XX
+#include "../mach-omap2/cm-regbits-34xx.h"
+#endif
struct omap_mcbsp **mcbsp_ptr;
int omap_mcbsp_count;
#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)
+
+#define omap_st_check_valid_id(id) ((id == 1 || id == 2) && \
+ id < omap_mcbsp_count)
+
static void omap_mcbsp_dump_reg(u8 id)
{
struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id);
static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
{
struct omap_mcbsp *mcbsp_tx = dev_id;
+ u16 irqst_spcr2;
- dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n",
- OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2));
+ irqst_spcr2 = OMAP_MCBSP_READ(mcbsp_tx->io_base, SPCR2);
+ dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
- complete(&mcbsp_tx->tx_irq_completion);
+ if (irqst_spcr2 & XSYNC_ERR) {
+ dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n",
+ irqst_spcr2);
+ /* Writing zero to XSYNC_ERR clears the IRQ */
+ OMAP_MCBSP_WRITE(mcbsp_tx->io_base, SPCR2,
+ irqst_spcr2 & ~(XSYNC_ERR));
+ } else {
+ complete(&mcbsp_tx->tx_irq_completion);
+ }
return IRQ_HANDLED;
}
static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
{
struct omap_mcbsp *mcbsp_rx = dev_id;
+ u16 irqst_spcr1;
- dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n",
- OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR2));
+ irqst_spcr1 = OMAP_MCBSP_READ(mcbsp_rx->io_base, SPCR1);
+ dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
- complete(&mcbsp_rx->rx_irq_completion);
+ if (irqst_spcr1 & RSYNC_ERR) {
+ dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n",
+ irqst_spcr1);
+ /* Writing zero to RSYNC_ERR clears the IRQ */
+ OMAP_MCBSP_WRITE(mcbsp_rx->io_base, SPCR1,
+ irqst_spcr1 & ~(RSYNC_ERR));
+ } else {
+ complete(&mcbsp_rx->tx_irq_completion);
+ }
return IRQ_HANDLED;
}
OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2);
OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1);
OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0);
+ if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+ OMAP_MCBSP_WRITE(io_base, XCCR, config->xccr);
+ OMAP_MCBSP_WRITE(io_base, RCCR, config->rccr);
+ }
}
EXPORT_SYMBOL(omap_mcbsp_config);
+#ifdef CONFIG_ARCH_OMAP34XX
+/*
+ * omap_mcbsp_set_tx_threshold configures how to deal
+ * with transmit threshold. the threshold value and handler can be
+ * configure in here.
+ */
+void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
+{
+ struct omap_mcbsp *mcbsp;
+ void __iomem *io_base;
+
+ if (!cpu_is_omap34xx())
+ return;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+ io_base = mcbsp->io_base;
+
+ OMAP_MCBSP_WRITE(io_base, THRSH2, threshold);
+}
+EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
+
+/*
+ * omap_mcbsp_set_rx_threshold configures how to deal
+ * with receive threshold. the threshold value and handler can be
+ * configure in here.
+ */
+void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
+{
+ struct omap_mcbsp *mcbsp;
+ void __iomem *io_base;
+
+ if (!cpu_is_omap34xx())
+ return;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+ io_base = mcbsp->io_base;
+
+ OMAP_MCBSP_WRITE(io_base, THRSH1, threshold);
+}
+EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
+
+/*
+ * omap_mcbsp_get_max_tx_thres just return the current configured
+ * maximum threshold for transmission
+ */
+u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+
+ 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);
+
+ return mcbsp->max_tx_thres;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
+
+/*
+ * omap_mcbsp_get_max_rx_thres just return the current configured
+ * maximum threshold for reception
+ */
+u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+
+ 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);
+
+ return mcbsp->max_rx_thres;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
+
+/*
+ * omap_mcbsp_get_dma_op_mode just return the current configured
+ * operating mode for the mcbsp channel
+ */
+int omap_mcbsp_get_dma_op_mode(unsigned int id)
+{
+ struct omap_mcbsp *mcbsp;
+ int dma_op_mode;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
+ return -ENODEV;
+ }
+ mcbsp = id_to_mcbsp_ptr(id);
+
+ spin_lock_irq(&mcbsp->lock);
+ dma_op_mode = mcbsp->dma_op_mode;
+ spin_unlock_irq(&mcbsp->lock);
+
+ return dma_op_mode;
+}
+EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
+#endif
+
/*
* We can choose between IRQ based or polled IO.
* This needs to be called before omap_mcbsp_request().
}
mcbsp = id_to_mcbsp_ptr(id);
- spin_lock(&mcbsp->lock);
+ spin_lock_irq(&mcbsp->lock);
if (!mcbsp->free) {
dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
mcbsp->id);
- spin_unlock(&mcbsp->lock);
+ spin_unlock_irq(&mcbsp->lock);
return -EINVAL;
}
mcbsp->io_type = io_type;
- spin_unlock(&mcbsp->lock);
+ spin_unlock_irq(&mcbsp->lock);
return 0;
}
int omap_mcbsp_request(unsigned int id)
{
struct omap_mcbsp *mcbsp;
+ unsigned long flags;
+ int i;
int err;
+ u16 syscon;
if (!omap_mcbsp_check_valid_id(id)) {
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
mcbsp->pdata->ops->request(id);
- clk_enable(mcbsp->clk);
-
- spin_lock(&mcbsp->lock);
+ spin_lock_irqsave(&mcbsp->lock, flags);
if (!mcbsp->free) {
dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
mcbsp->id);
- spin_unlock(&mcbsp->lock);
+ spin_unlock_irqrestore(&mcbsp->lock, flags);
return -1;
}
mcbsp->free = 0;
- spin_unlock(&mcbsp->lock);
+ spin_unlock_irqrestore(&mcbsp->lock, flags);
+
+ for (i = 0; i < mcbsp->num_clks; i++)
+ clk_enable(mcbsp->clks[i]);
+
+ /*
+ * Enable wakup behavior, smart idle and all wakeups
+ * REVISIT: some wakeups may be unnecessary
+ */
+ if (cpu_is_omap34xx()) {
+ syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
+ syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
+
+ spin_lock_irq(&mcbsp->lock);
+ if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
+ syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
+ CLOCKACTIVITY(0x02));
+ OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN,
+ WAKEUPEN_ALL);
+ } else {
+ syscon |= SIDLEMODE(0x01);
+ }
+ spin_unlock_irq(&mcbsp->lock);
+
+ OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+ }
/*
* Make sure that transmitter, receiver and sample-rate generator are
void omap_mcbsp_free(unsigned int id)
{
struct omap_mcbsp *mcbsp;
+ unsigned long flags;
+ int i;
+ u16 syscon;
if (!omap_mcbsp_check_valid_id(id)) {
printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
mcbsp->pdata->ops->free(id);
- clk_disable(mcbsp->clk);
+ /*
+ * Disable wakup behavior, smart idle and all wakeups
+ */
+ if (cpu_is_omap34xx()) {
+ syscon = OMAP_MCBSP_READ(mcbsp->io_base, SYSCON);
+ syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
+ /*
+ * HW bug workaround - If no_idle mode is taken, we need to
+ * go to smart_idle before going to always_idle, or the
+ * device will not hit retention anymore.
+ */
+ syscon |= SIDLEMODE(0x02);
+ OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+
+ syscon &= ~(SIDLEMODE(0x03));
+ OMAP_MCBSP_WRITE(mcbsp->io_base, SYSCON, syscon);
+
+ OMAP_MCBSP_WRITE(mcbsp->io_base, WAKEUPEN, 0);
+ }
- spin_lock(&mcbsp->lock);
+ spin_lock_irqsave(&mcbsp->lock, flags);
if (mcbsp->free) {
dev_err(mcbsp->dev, "McBSP%d was not reserved\n",
mcbsp->id);
- spin_unlock(&mcbsp->lock);
+ spin_unlock_irqrestore(&mcbsp->lock, flags);
return;
}
+ spin_unlock_irqrestore(&mcbsp->lock, flags);
+
+ for (i = mcbsp->num_clks - 1; i >= 0; i--)
+ clk_disable(mcbsp->clks[i]);
+ spin_lock_irqsave(&mcbsp->lock, flags);
mcbsp->free = 1;
- spin_unlock(&mcbsp->lock);
+ spin_unlock_irqrestore(&mcbsp->lock, flags);
if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
/* Free IRQs */
}
EXPORT_SYMBOL(omap_mcbsp_free);
+#ifdef CONFIG_ARCH_OMAP34XX
+static void omap_st_enable(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data;
+ void __iomem *io_base_mcbsp;
+ void __iomem *io_base_st;
+ unsigned int w;
+
+ io_base_mcbsp = mcbsp->io_base;
+ st_data = mcbsp->st_data;
+ io_base_st = 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 &= ~(mcbsp->id - 1);
+ 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_disable(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data;
+ void __iomem *io_base_mcbsp;
+ void __iomem *io_base_st;
+ unsigned int w;
+
+ io_base_mcbsp = mcbsp->io_base;
+ st_data = mcbsp->st_data;
+ io_base_st = 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 |= (mcbsp->id - 1);
+ cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
+}
+
+static void omap_st_enable_autoidle(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data;
+ void __iomem *io_base_st;
+ unsigned int w;
+
+ st_data = mcbsp->st_data;
+ io_base_st = st_data->io_base_st;
+
+ w = OMAP_ST_READ(io_base_st, SYSCONFIG);
+ OMAP_ST_WRITE(io_base_st, SYSCONFIG, w | ST_AUTOIDLE);
+}
+
+static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
+{
+ struct omap_mcbsp_st_data *st_data;
+ void __iomem *io_base;
+ u16 w, i;
+
+ st_data = mcbsp->st_data;
+ 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);
+
+ if (w & ST_COEFFWREN)
+ OMAP_ST_WRITE(io_base, SSELCR, w & ~(ST_COEFFWREN));
+
+ OMAP_ST_WRITE(io_base, SSELCR, w | ST_COEFFWREN);
+
+ for (i = 0; i < 128; i++)
+ OMAP_ST_WRITE(io_base, SFIRCR, fir[i]);
+
+ i = 0;
+
+ w = OMAP_ST_READ(io_base, SSELCR);
+ while (!(w & ST_COEFFWRDONE) && (++i < 1000))
+ w = OMAP_ST_READ(io_base, SSELCR);
+
+ OMAP_ST_WRITE(io_base, SSELCR, w & ~(ST_COEFFWREN));
+
+ if (i == 1000)
+ dev_err(mcbsp->dev, "McBSP FIR load error!\n");
+}
+
+static void omap_st_chgain(struct omap_mcbsp *mcbsp, s16 ch0gain, s16 ch1gain)
+{
+ struct omap_mcbsp_st_data *st_data;
+ void __iomem *io_base;
+ u16 w;
+
+ st_data = mcbsp->st_data;
+ 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(ch0gain) | \
+ ST_CH1GAIN(ch1gain));
+}
+
+static void omap_st_start(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mcbsp->lock, flags);
+ if (st_data) {
+ omap_st_fir_write(mcbsp, mcbsp->st_data->taps);
+ omap_st_chgain(mcbsp,
+ mcbsp->st_data->ch0gain,
+ mcbsp->st_data->ch1gain);
+ if (st_data->enabled)
+ omap_st_enable(mcbsp);
+ else
+ omap_st_enable_autoidle(mcbsp);
+ st_data->running = 1;
+ }
+ spin_unlock_irqrestore(&mcbsp->lock, flags);
+}
+
+static void omap_st_stop(struct omap_mcbsp *mcbsp)
+{
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mcbsp->lock, flags);
+ if (st_data && st_data->running) {
+ omap_st_disable(mcbsp);
+ st_data->running = 0;
+ }
+ spin_unlock_irqrestore(&mcbsp->lock, flags);
+}
+#else
+static inline void omap_st_start(struct omap_mcbsp *mcbsp) {}
+static inline void omap_st_stop(struct omap_mcbsp *mcbsp) {}
+#endif /* CONFIG_ARCH_OMAP34XX */
+
/*
* Here we start the McBSP, by enabling the sample
* generator, both transmitter and receivers,
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;
w = OMAP_MCBSP_READ(io_base, SPCR1);
OMAP_MCBSP_WRITE(io_base, SPCR1, w | 1);
- udelay(100);
+ /* Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec */
+ udelay(500);
/* Start frame sync */
w = OMAP_MCBSP_READ(io_base, SPCR2);
/* Reset the sample rate generator */
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);
+void omap_mcbsp_xmit_enable(unsigned int id, u8 enable)
+{
+ struct omap_mcbsp *mcbsp;
+ void __iomem *io_base;
+ u16 w;
+
+ if (!(cpu_is_omap2430() || cpu_is_omap34xx()))
+ return;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+ io_base = mcbsp->io_base;
+
+ w = OMAP_MCBSP_READ(io_base, XCCR);
+
+ if (enable)
+ OMAP_MCBSP_WRITE(io_base, XCCR, w & ~(XDISABLE));
+ else
+ OMAP_MCBSP_WRITE(io_base, XCCR, w | XDISABLE);
+}
+EXPORT_SYMBOL(omap_mcbsp_xmit_enable);
+
+void omap_mcbsp_recv_enable(unsigned int id, u8 enable)
+{
+ struct omap_mcbsp *mcbsp;
+ void __iomem *io_base;
+ u16 w;
+
+ if (!(cpu_is_omap2430() || cpu_is_omap34xx()))
+ return;
+
+ if (!omap_mcbsp_check_valid_id(id)) {
+ printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
+ return;
+ }
+
+ mcbsp = id_to_mcbsp_ptr(id);
+ io_base = mcbsp->io_base;
+
+ w = OMAP_MCBSP_READ(io_base, RCCR);
+
+ if (enable)
+ OMAP_MCBSP_WRITE(io_base, RCCR, w & ~(RDISABLE));
+ else
+ OMAP_MCBSP_WRITE(io_base, RCCR, w | RDISABLE);
+}
+EXPORT_SYMBOL(omap_mcbsp_recv_enable);
+
/* polled mcbsp i/o operations */
int omap_mcbsp_pollwrite(unsigned int id, u16 buf)
{
}
EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
+#ifdef CONFIG_ARCH_OMAP34XX
+#define max_thres(m) (mcbsp->pdata->buffer_size)
+#define valid_threshold(m, val) ((val) <= max_thres(m))
+#define THRESHOLD_PROP_BUILDER(prop) \
+static ssize_t prop##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%u\n", mcbsp->prop); \
+} \
+ \
+static ssize_t prop##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t size) \
+{ \
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
+ unsigned long val; \
+ int status; \
+ \
+ status = strict_strtoul(buf, 0, &val); \
+ if (status) \
+ return status; \
+ \
+ if (!valid_threshold(mcbsp, val)) \
+ return -EDOM; \
+ \
+ mcbsp->prop = val; \
+ return size; \
+} \
+ \
+static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
+
+THRESHOLD_PROP_BUILDER(max_tx_thres);
+THRESHOLD_PROP_BUILDER(max_rx_thres);
+
+static ssize_t dma_op_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ int dma_op_mode;
+
+ spin_lock_irq(&mcbsp->lock);
+ dma_op_mode = mcbsp->dma_op_mode;
+ spin_unlock_irq(&mcbsp->lock);
+
+ return sprintf(buf, "%d\n", dma_op_mode);
+}
+
+static ssize_t dma_op_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
+ unsigned long val;
+ int status;
+
+ status = strict_strtoul(buf, 0, &val);
+ if (status)
+ return status;
+
+ spin_lock_irq(&mcbsp->lock);
+
+ if (!mcbsp->free) {
+ size = -EBUSY;
+ goto unlock;
+ }
+
+ if (val > MCBSP_DMA_MODE_FRAME || val < MCBSP_DMA_MODE_ELEMENT) {
+ size = -EINVAL;
+ goto unlock;
+ }
+
+ mcbsp->dma_op_mode = val;
+
+unlock:
+ spin_unlock_irq(&mcbsp->lock);
+
+ return size;
+}
+
+static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
+
+static ssize_t st_enable_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;
+
+ return sprintf(buf, "%d\n", st_data->enabled);
+}
+
+static ssize_t st_enable_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;
+ unsigned long val;
+ int status;
+
+ status = strict_strtoul(buf, 0, &val);
+ if (status)
+ return status;
+
+ spin_lock_irq(&mcbsp->lock);
+ st_data->enabled = !!val;
+
+ if (st_data->running) {
+ if (st_data->enabled)
+ omap_st_enable(mcbsp);
+ else
+ omap_st_disable(mcbsp);
+ }
+ spin_unlock_irq(&mcbsp->lock);
+
+ return size;
+}
+
+static DEVICE_ATTR(st_enable, 0644, st_enable_show, st_enable_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 ssize_t st_chgain_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;
+
+ if (strcmp("st_ch0gain", attr->attr.name) == 0)
+ return sprintf(buf, "%d\n", st_data->ch0gain);
+ else
+ return sprintf(buf, "%d\n", st_data->ch1gain);
+}
+
+static ssize_t st_chgain_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;
+ long val;
+ int status;
+
+ status = strict_strtol(buf, 0, &val);
+ if (status)
+ return status;
+ if (val < -32768 || val > 32767)
+ return -EINVAL;
+
+ spin_lock_irq(&mcbsp->lock);
+ if (strcmp("st_ch0gain", attr->attr.name) == 0)
+ st_data->ch0gain = val;
+ else
+ st_data->ch1gain = val;
+
+ if (st_data->running)
+ omap_st_chgain(mcbsp,
+ mcbsp->st_data->ch0gain,
+ mcbsp->st_data->ch1gain);
+ spin_unlock_irq(&mcbsp->lock);
+
+ return size;
+}
+
+static DEVICE_ATTR(st_ch0gain, 0644, st_chgain_show, st_chgain_store);
+static DEVICE_ATTR(st_ch1gain, 0644, st_chgain_show, st_chgain_store);
+
+static const struct attribute *additional_attrs[] = {
+ &dev_attr_max_tx_thres.attr,
+ &dev_attr_max_rx_thres.attr,
+ &dev_attr_dma_op_mode.attr,
+ NULL,
+};
+
+static const struct attribute_group additional_attr_group = {
+ .attrs = (struct attribute **)additional_attrs,
+};
+
+static inline int __devinit omap_additional_add(struct platform_device *pdev)
+{
+ return sysfs_create_group(&pdev->dev.kobj, &additional_attr_group);
+}
+
+static inline void __devexit omap_additional_rem(struct platform_device *pdev)
+{
+ sysfs_remove_group(&pdev->dev.kobj, &additional_attr_group);
+}
+
+static const struct attribute *sidetone_attrs[] = {
+ &dev_attr_st_enable.attr,
+ &dev_attr_st_taps.attr,
+ &dev_attr_st_ch0gain.attr,
+ &dev_attr_st_ch1gain.attr,
+ NULL,
+};
+
+static const struct attribute_group sidetone_attr_group = {
+ .attrs = (struct attribute **)sidetone_attrs,
+};
+
+int __devinit omap_st_add(struct platform_device *pdev)
+{
+ struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
+ struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
+ 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(&pdev->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 platform_device *pdev)
+{
+ struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
+ struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
+
+ if (st_data) {
+ sysfs_remove_group(&pdev->dev.kobj, &sidetone_attr_group);
+ iounmap(st_data->io_base_st);
+ kfree(st_data);
+ }
+}
+#else
+static inline int __devinit omap_st_add(struct platform_device *pdev)
+{
+ return 0;
+}
+static inline void __devexit omap_st_remove(struct platform_device *pdev) {}
+static inline int __devinit omap_additional_add(struct platform_device *pdev)
+{
+ return 0;
+}
+static inline void __devexit omap_additional_rem(struct platform_device *pdev)
+{ }
+#endif /* CONFIG_ARCH_OMAP34XX */
+
/*
* McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
* 730 has only 2 McBSP, and both of them are MPU peripherals.
struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
struct omap_mcbsp *mcbsp;
int id = pdev->id - 1;
+ int i;
int ret = 0;
if (!pdata) {
mcbsp->dma_rx_sync = pdata->dma_rx_sync;
mcbsp->dma_tx_sync = pdata->dma_tx_sync;
- if (pdata->clk_name)
- mcbsp->clk = clk_get(&pdev->dev, pdata->clk_name);
- if (IS_ERR(mcbsp->clk)) {
- dev_err(&pdev->dev,
- "Invalid clock configuration for McBSP%d.\n",
- mcbsp->id);
- ret = PTR_ERR(mcbsp->clk);
- goto err_clk;
+ if (pdata->num_clks) {
+ mcbsp->num_clks = pdata->num_clks;
+ mcbsp->clks = kzalloc(mcbsp->num_clks * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!mcbsp->clks) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ for (i = 0; i < mcbsp->num_clks; i++) {
+ mcbsp->clks[i] = clk_get(&pdev->dev, pdata->clk_names[i]);
+ if (IS_ERR(mcbsp->clks[i])) {
+ dev_err(&pdev->dev,
+ "Invalid %s configuration for McBSP%d.\n",
+ pdata->clk_names[i], mcbsp->id);
+ ret = PTR_ERR(mcbsp->clks[i]);
+ goto err_clk;
+ }
+ }
+
}
mcbsp->pdata = pdata;
mcbsp->dev = &pdev->dev;
platform_set_drvdata(pdev, mcbsp);
+#ifdef CONFIG_ARCH_OMAP34XX
+ if (cpu_is_omap34xx()) {
+ mcbsp->max_tx_thres = max_thres(mcbsp);
+ mcbsp->max_rx_thres = max_thres(mcbsp);
+ mcbsp->dma_op_mode = MCBSP_DMA_MODE_THRESHOLD;
+ } else {
+ mcbsp->max_tx_thres = -EINVAL;
+ mcbsp->max_rx_thres = -EINVAL;
+ mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
+ }
+#endif
+
+ if (cpu_is_omap34xx()) {
+ if (mcbsp->id == 2 || mcbsp->id == 3)
+ if (omap_st_add(pdev))
+ dev_warn(&pdev->dev,
+ "Unable to create sidetone controls\n");
+
+ if (omap_additional_add(pdev))
+ dev_warn(&pdev->dev,
+ "Unable to create threshold controls\n");
+ }
+
return 0;
err_clk:
+ while (i--)
+ clk_put(mcbsp->clks[i]);
+ kfree(mcbsp->clks);
iounmap(mcbsp->io_base);
err_ioremap:
mcbsp->free = 0;
static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
{
struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
+ int i;
platform_set_drvdata(pdev, NULL);
if (mcbsp) {
mcbsp->pdata->ops->free)
mcbsp->pdata->ops->free(mcbsp->id);
- clk_disable(mcbsp->clk);
- clk_put(mcbsp->clk);
+ if (cpu_is_omap34xx()) {
+ if (mcbsp->id == 2 || mcbsp->id == 3)
+ omap_st_remove(pdev);
+
+ omap_additional_rem(pdev);
+ }
+
+ for (i = mcbsp->num_clks - 1; i >= 0; i--) {
+ clk_disable(mcbsp->clks[i]);
+ clk_put(mcbsp->clks[i]);
+ }
iounmap(mcbsp->io_base);
- mcbsp->clk = NULL;
+ if (mcbsp->num_clks) {
+ kfree(mcbsp->clks);
+ mcbsp->clks = NULL;
+ mcbsp->num_clks = 0;
+ }
mcbsp->free = 0;
mcbsp->dev = NULL;
}