2005-07-10 18:58:15 +00:00
|
|
|
/*
|
2012-01-26 10:47:22 +00:00
|
|
|
* sound/soc/omap/mcbsp.c
|
2005-07-10 18:58:15 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 2004 Nokia Corporation
|
|
|
|
* Author: Samuel Ortiz <samuel.ortiz@nokia.com>
|
|
|
|
*
|
2012-01-26 10:47:22 +00:00
|
|
|
* Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
|
|
|
|
* Peter Ujfalusi <peter.ujfalusi@ti.com>
|
2005-07-10 18:58:15 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* Multichannel mode not supported.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/device.h>
|
2008-07-03 09:24:39 +00:00
|
|
|
#include <linux/platform_device.h>
|
2005-07-10 18:58:15 +00:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/err.h>
|
2006-01-07 16:15:52 +00:00
|
|
|
#include <linux/clk.h>
|
2007-02-12 18:50:53 +00:00
|
|
|
#include <linux/delay.h>
|
2008-07-03 09:24:39 +00:00
|
|
|
#include <linux/io.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2012-08-16 13:41:00 +00:00
|
|
|
#include <linux/pm_runtime.h>
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2012-08-24 13:21:06 +00:00
|
|
|
#include <linux/platform_data/asoc-ti-mcbsp.h>
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2012-02-03 11:11:47 +00:00
|
|
|
#include "mcbsp.h"
|
|
|
|
|
2010-10-08 17:00:19 +00:00
|
|
|
static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
|
2008-10-08 07:01:39 +00:00
|
|
|
{
|
2011-09-26 07:45:39 +00:00
|
|
|
void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
|
|
|
|
|
|
|
|
if (mcbsp->pdata->reg_size == 2) {
|
|
|
|
((u16 *)mcbsp->reg_cache)[reg] = (u16)val;
|
2013-11-16 00:01:19 +00:00
|
|
|
writew_relaxed((u16)val, addr);
|
2010-02-15 18:03:33 +00:00
|
|
|
} else {
|
2011-09-26 07:45:39 +00:00
|
|
|
((u32 *)mcbsp->reg_cache)[reg] = val;
|
2013-11-16 00:01:19 +00:00
|
|
|
writel_relaxed(val, addr);
|
2010-02-15 18:03:33 +00:00
|
|
|
}
|
2008-10-08 07:01:39 +00:00
|
|
|
}
|
|
|
|
|
2010-10-08 17:00:19 +00:00
|
|
|
static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
|
2008-10-08 07:01:39 +00:00
|
|
|
{
|
2011-09-26 07:45:39 +00:00
|
|
|
void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step;
|
|
|
|
|
|
|
|
if (mcbsp->pdata->reg_size == 2) {
|
2013-11-16 00:01:19 +00:00
|
|
|
return !from_cache ? readw_relaxed(addr) :
|
2011-09-26 07:45:39 +00:00
|
|
|
((u16 *)mcbsp->reg_cache)[reg];
|
2010-02-15 18:03:33 +00:00
|
|
|
} else {
|
2013-11-16 00:01:19 +00:00
|
|
|
return !from_cache ? readl_relaxed(addr) :
|
2011-09-26 07:45:39 +00:00
|
|
|
((u32 *)mcbsp->reg_cache)[reg];
|
2010-02-15 18:03:33 +00:00
|
|
|
}
|
2008-10-08 07:01:39 +00:00
|
|
|
}
|
|
|
|
|
2010-10-08 17:00:19 +00:00
|
|
|
static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
|
2010-02-22 12:21:11 +00:00
|
|
|
{
|
2013-11-16 00:01:19 +00:00
|
|
|
writel_relaxed(val, mcbsp->st_data->io_base_st + reg);
|
2010-02-22 12:21:11 +00:00
|
|
|
}
|
|
|
|
|
2010-10-08 17:00:19 +00:00
|
|
|
static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
|
2010-02-22 12:21:11 +00:00
|
|
|
{
|
2013-11-16 00:01:19 +00:00
|
|
|
return readl_relaxed(mcbsp->st_data->io_base_st + reg);
|
2010-02-22 12:21:11 +00:00
|
|
|
}
|
|
|
|
|
2010-02-15 18:03:32 +00:00
|
|
|
#define MCBSP_READ(mcbsp, reg) \
|
2010-02-15 18:03:33 +00:00
|
|
|
omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
|
2010-02-15 18:03:32 +00:00
|
|
|
#define MCBSP_WRITE(mcbsp, reg, val) \
|
|
|
|
omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
|
2010-02-15 18:03:33 +00:00
|
|
|
#define MCBSP_READ_CACHE(mcbsp, reg) \
|
|
|
|
omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
|
2008-10-08 07:01:39 +00:00
|
|
|
|
2010-02-22 12:21:11 +00:00
|
|
|
#define MCBSP_ST_READ(mcbsp, reg) \
|
|
|
|
omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
|
|
|
|
#define MCBSP_ST_WRITE(mcbsp, reg, val) \
|
|
|
|
omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
|
|
|
|
dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, DRR2));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, DRR1));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, DXR2));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, DXR1));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, SPCR2));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, SPCR1));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, RCR2));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, RCR1));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, XCR2));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, XCR1));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, SRGR2));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, SRGR1));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n",
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_READ(mcbsp, PCR0));
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "***********************\n");
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|
|
|
|
|
2012-03-19 15:05:39 +00:00
|
|
|
static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct omap_mcbsp *mcbsp = dev_id;
|
|
|
|
u16 irqst;
|
|
|
|
|
|
|
|
irqst = MCBSP_READ(mcbsp, IRQST);
|
|
|
|
dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst);
|
|
|
|
|
|
|
|
if (irqst & RSYNCERREN)
|
|
|
|
dev_err(mcbsp->dev, "RX Frame Sync Error!\n");
|
|
|
|
if (irqst & RFSREN)
|
|
|
|
dev_dbg(mcbsp->dev, "RX Frame Sync\n");
|
|
|
|
if (irqst & REOFEN)
|
|
|
|
dev_dbg(mcbsp->dev, "RX End Of Frame\n");
|
|
|
|
if (irqst & RRDYEN)
|
|
|
|
dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n");
|
|
|
|
if (irqst & RUNDFLEN)
|
|
|
|
dev_err(mcbsp->dev, "RX Buffer Underflow!\n");
|
|
|
|
if (irqst & ROVFLEN)
|
|
|
|
dev_err(mcbsp->dev, "RX Buffer Overflow!\n");
|
|
|
|
|
|
|
|
if (irqst & XSYNCERREN)
|
|
|
|
dev_err(mcbsp->dev, "TX Frame Sync Error!\n");
|
|
|
|
if (irqst & XFSXEN)
|
|
|
|
dev_dbg(mcbsp->dev, "TX Frame Sync\n");
|
|
|
|
if (irqst & XEOFEN)
|
|
|
|
dev_dbg(mcbsp->dev, "TX End Of Frame\n");
|
|
|
|
if (irqst & XRDYEN)
|
|
|
|
dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n");
|
|
|
|
if (irqst & XUNDFLEN)
|
|
|
|
dev_err(mcbsp->dev, "TX Buffer Underflow!\n");
|
|
|
|
if (irqst & XOVFLEN)
|
|
|
|
dev_err(mcbsp->dev, "TX Buffer Overflow!\n");
|
|
|
|
if (irqst & XEMPTYEOFEN)
|
|
|
|
dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n");
|
|
|
|
|
|
|
|
MCBSP_WRITE(mcbsp, IRQST, irqst);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2006-10-06 17:53:39 +00:00
|
|
|
static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2007-10-26 09:40:25 +00:00
|
|
|
struct omap_mcbsp *mcbsp_tx = dev_id;
|
2009-05-25 18:08:42 +00:00
|
|
|
u16 irqst_spcr2;
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2010-02-15 18:03:32 +00:00
|
|
|
irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2);
|
2009-05-25 18:08:42 +00:00
|
|
|
dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2009-05-25 18:08:42 +00:00
|
|
|
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 */
|
2010-02-23 15:50:38 +00:00
|
|
|
MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2));
|
2009-05-25 18:08:42 +00:00
|
|
|
}
|
2008-07-03 09:24:39 +00:00
|
|
|
|
2005-07-10 18:58:15 +00:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2006-10-06 17:53:39 +00:00
|
|
|
static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2007-10-26 09:40:25 +00:00
|
|
|
struct omap_mcbsp *mcbsp_rx = dev_id;
|
2009-05-25 18:08:42 +00:00
|
|
|
u16 irqst_spcr1;
|
|
|
|
|
2010-02-15 18:03:32 +00:00
|
|
|
irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1);
|
2009-05-25 18:08:42 +00:00
|
|
|
dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
|
|
|
|
|
|
|
|
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 */
|
2010-02-23 15:50:38 +00:00
|
|
|
MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1));
|
2009-05-25 18:08:42 +00:00
|
|
|
}
|
2008-07-03 09:24:39 +00:00
|
|
|
|
2005-07-10 18:58:15 +00:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* omap_mcbsp_config simply write a config to the
|
|
|
|
* appropriate McBSP.
|
|
|
|
* You either call this function or set the McBSP registers
|
|
|
|
* by yourself before calling omap_mcbsp_start().
|
|
|
|
*/
|
2012-02-14 16:20:58 +00:00
|
|
|
void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
|
|
|
|
const struct omap_mcbsp_reg_cfg *config)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2008-10-08 07:01:39 +00:00
|
|
|
dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
|
|
|
|
mcbsp->id, mcbsp->phys_base);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
|
|
|
/* We write the given config */
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
|
|
|
|
MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
|
|
|
|
MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
|
|
|
|
MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
|
|
|
|
MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
|
|
|
|
MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
|
|
|
|
MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
|
|
|
|
MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
|
|
|
|
MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
|
|
|
|
MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
|
|
|
|
MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
|
2011-09-26 07:45:41 +00:00
|
|
|
if (mcbsp->pdata->has_ccr) {
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, XCCR, config->xccr);
|
|
|
|
MCBSP_WRITE(mcbsp, RCCR, config->rccr);
|
2009-01-15 11:09:54 +00:00
|
|
|
}
|
2012-03-05 09:27:40 +00:00
|
|
|
/* Enable wakeup behavior */
|
|
|
|
if (mcbsp->pdata->has_wakeup)
|
|
|
|
MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
|
2012-03-19 15:05:39 +00:00
|
|
|
|
|
|
|
/* Enable TX/RX sync error interrupts by default */
|
|
|
|
if (mcbsp->irq)
|
|
|
|
MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN);
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|
|
|
|
|
2011-02-24 09:46:55 +00:00
|
|
|
/**
|
|
|
|
* omap_mcbsp_dma_reg_params - returns the address of mcbsp data register
|
|
|
|
* @id - mcbsp id
|
|
|
|
* @stream - indicates the direction of data flow (rx or tx)
|
|
|
|
*
|
|
|
|
* Returns the address of mcbsp data transmit register or data receive register
|
|
|
|
* to be used by DMA for transferring/receiving data based on the value of
|
|
|
|
* @stream for the requested mcbsp given by @id
|
|
|
|
*/
|
2012-02-14 13:41:29 +00:00
|
|
|
static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp,
|
|
|
|
unsigned int stream)
|
2011-02-24 09:46:55 +00:00
|
|
|
{
|
|
|
|
int data_reg;
|
|
|
|
|
2011-09-26 07:45:39 +00:00
|
|
|
if (mcbsp->pdata->reg_size == 2) {
|
2011-02-24 09:46:55 +00:00
|
|
|
if (stream)
|
2011-09-26 07:45:39 +00:00
|
|
|
data_reg = OMAP_MCBSP_REG_DRR1;
|
2011-02-24 09:46:55 +00:00
|
|
|
else
|
2011-09-26 07:45:39 +00:00
|
|
|
data_reg = OMAP_MCBSP_REG_DXR1;
|
2011-02-24 09:46:55 +00:00
|
|
|
} else {
|
|
|
|
if (stream)
|
2011-09-26 07:45:39 +00:00
|
|
|
data_reg = OMAP_MCBSP_REG_DRR;
|
2011-02-24 09:46:55 +00:00
|
|
|
else
|
2011-09-26 07:45:39 +00:00
|
|
|
data_reg = OMAP_MCBSP_REG_DXR;
|
2011-02-24 09:46:55 +00:00
|
|
|
}
|
|
|
|
|
2011-09-26 07:45:39 +00:00
|
|
|
return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step;
|
2011-02-24 09:46:55 +00:00
|
|
|
}
|
|
|
|
|
2010-02-22 12:21:11 +00:00
|
|
|
static void omap_st_on(struct omap_mcbsp *mcbsp)
|
|
|
|
{
|
|
|
|
unsigned int w;
|
|
|
|
|
2011-09-26 07:45:44 +00:00
|
|
|
if (mcbsp->pdata->enable_st_clock)
|
|
|
|
mcbsp->pdata->enable_st_clock(mcbsp->id, 1);
|
2010-02-22 12:21:11 +00:00
|
|
|
|
|
|
|
/* Enable McBSP Sidetone */
|
|
|
|
w = MCBSP_READ(mcbsp, SSELCR);
|
|
|
|
MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
|
|
|
|
|
|
|
|
/* Enable Sidetone from Sidetone Core */
|
|
|
|
w = MCBSP_ST_READ(mcbsp, SSELCR);
|
|
|
|
MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_st_off(struct omap_mcbsp *mcbsp)
|
|
|
|
{
|
|
|
|
unsigned int w;
|
|
|
|
|
|
|
|
w = MCBSP_ST_READ(mcbsp, SSELCR);
|
|
|
|
MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
|
|
|
|
|
|
|
|
w = MCBSP_READ(mcbsp, SSELCR);
|
|
|
|
MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
|
|
|
|
|
2011-09-26 07:45:44 +00:00
|
|
|
if (mcbsp->pdata->enable_st_clock)
|
|
|
|
mcbsp->pdata->enable_st_clock(mcbsp->id, 0);
|
2010-02-22 12:21:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
|
|
|
|
{
|
|
|
|
u16 val, i;
|
|
|
|
|
|
|
|
val = MCBSP_ST_READ(mcbsp, SSELCR);
|
|
|
|
|
|
|
|
if (val & ST_COEFFWREN)
|
|
|
|
MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
|
|
|
|
|
|
|
|
MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
|
|
|
|
|
|
|
|
for (i = 0; i < 128; i++)
|
|
|
|
MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
val = MCBSP_ST_READ(mcbsp, SSELCR);
|
|
|
|
while (!(val & ST_COEFFWRDONE) && (++i < 1000))
|
|
|
|
val = MCBSP_ST_READ(mcbsp, SSELCR);
|
|
|
|
|
|
|
|
MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
|
|
|
|
|
|
|
|
if (i == 1000)
|
|
|
|
dev_err(mcbsp->dev, "McBSP FIR load error!\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void omap_st_chgain(struct omap_mcbsp *mcbsp)
|
|
|
|
{
|
|
|
|
u16 w;
|
|
|
|
struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
|
|
|
|
|
|
|
|
w = MCBSP_ST_READ(mcbsp, SSELCR);
|
|
|
|
|
|
|
|
MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \
|
|
|
|
ST_CH1GAIN(st_data->ch1gain));
|
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain)
|
2010-02-22 12:21:11 +00:00
|
|
|
{
|
2012-02-23 13:38:37 +00:00
|
|
|
struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
|
2010-02-22 12:21:11 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!st_data)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
spin_lock_irq(&mcbsp->lock);
|
|
|
|
if (channel == 0)
|
|
|
|
st_data->ch0gain = chgain;
|
|
|
|
else if (channel == 1)
|
|
|
|
st_data->ch1gain = chgain;
|
|
|
|
else
|
|
|
|
ret = -EINVAL;
|
|
|
|
|
|
|
|
if (st_data->enabled)
|
|
|
|
omap_st_chgain(mcbsp);
|
|
|
|
spin_unlock_irq(&mcbsp->lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain)
|
2010-02-22 12:21:11 +00:00
|
|
|
{
|
2012-02-23 13:38:37 +00:00
|
|
|
struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
|
2010-02-22 12:21:11 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (!st_data)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
spin_lock_irq(&mcbsp->lock);
|
|
|
|
if (channel == 0)
|
|
|
|
*chgain = st_data->ch0gain;
|
|
|
|
else if (channel == 1)
|
|
|
|
*chgain = st_data->ch1gain;
|
|
|
|
else
|
|
|
|
ret = -EINVAL;
|
|
|
|
spin_unlock_irq(&mcbsp->lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap_st_start(struct omap_mcbsp *mcbsp)
|
|
|
|
{
|
|
|
|
struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
|
|
|
|
|
2012-02-23 13:40:55 +00:00
|
|
|
if (st_data->enabled && !st_data->running) {
|
2010-02-22 12:21:11 +00:00
|
|
|
omap_st_fir_write(mcbsp, st_data->taps);
|
|
|
|
omap_st_chgain(mcbsp);
|
|
|
|
|
|
|
|
if (!mcbsp->free) {
|
|
|
|
omap_st_on(mcbsp);
|
|
|
|
st_data->running = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
int omap_st_enable(struct omap_mcbsp *mcbsp)
|
2010-02-22 12:21:11 +00:00
|
|
|
{
|
2012-02-23 13:38:37 +00:00
|
|
|
struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
|
2010-02-22 12:21:11 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int omap_st_stop(struct omap_mcbsp *mcbsp)
|
|
|
|
{
|
|
|
|
struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
|
|
|
|
|
2012-02-23 13:40:55 +00:00
|
|
|
if (st_data->running) {
|
2010-02-22 12:21:11 +00:00
|
|
|
if (!mcbsp->free) {
|
|
|
|
omap_st_off(mcbsp);
|
|
|
|
st_data->running = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
int omap_st_disable(struct omap_mcbsp *mcbsp)
|
2010-02-22 12:21:11 +00:00
|
|
|
{
|
2012-02-23 13:38:37 +00:00
|
|
|
struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
|
2010-02-22 12:21:11 +00:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
int omap_st_is_enabled(struct omap_mcbsp *mcbsp)
|
2010-02-22 12:21:11 +00:00
|
|
|
{
|
2012-02-23 13:38:37 +00:00
|
|
|
struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
|
2010-02-22 12:21:11 +00:00
|
|
|
|
|
|
|
if (!st_data)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return st_data->enabled;
|
|
|
|
}
|
|
|
|
|
2009-08-20 13:18:10 +00:00
|
|
|
/*
|
2010-06-03 04:39:33 +00:00
|
|
|
* omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
|
|
|
|
* The threshold parameter is 1 based, and it is converted (threshold - 1)
|
|
|
|
* for the THRSH2 register.
|
2009-08-20 13:18:10 +00:00
|
|
|
*/
|
2012-02-14 16:20:58 +00:00
|
|
|
void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
|
2009-08-20 13:18:10 +00:00
|
|
|
{
|
2011-09-26 07:45:42 +00:00
|
|
|
if (mcbsp->pdata->buffer_size == 0)
|
|
|
|
return;
|
2009-08-20 13:18:10 +00:00
|
|
|
|
2010-06-03 04:39:33 +00:00
|
|
|
if (threshold && threshold <= mcbsp->max_tx_thres)
|
|
|
|
MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
|
2009-08-20 13:18:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-06-03 04:39:33 +00:00
|
|
|
* omap_mcbsp_set_rx_threshold configures the receive threshold in words.
|
|
|
|
* The threshold parameter is 1 based, and it is converted (threshold - 1)
|
|
|
|
* for the THRSH1 register.
|
2009-08-20 13:18:10 +00:00
|
|
|
*/
|
2012-02-14 16:20:58 +00:00
|
|
|
void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
|
2009-08-20 13:18:10 +00:00
|
|
|
{
|
2011-09-26 07:45:42 +00:00
|
|
|
if (mcbsp->pdata->buffer_size == 0)
|
|
|
|
return;
|
2009-08-20 13:18:10 +00:00
|
|
|
|
2010-06-03 04:39:33 +00:00
|
|
|
if (threshold && threshold <= mcbsp->max_rx_thres)
|
|
|
|
MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
|
2009-08-20 13:18:10 +00:00
|
|
|
}
|
2009-08-20 13:18:11 +00:00
|
|
|
|
2010-03-03 13:08:08 +00:00
|
|
|
/*
|
|
|
|
* omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
|
|
|
|
*/
|
2012-02-14 16:20:58 +00:00
|
|
|
u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp)
|
2010-03-03 13:08:08 +00:00
|
|
|
{
|
|
|
|
u16 buffstat;
|
|
|
|
|
2011-09-26 07:45:42 +00:00
|
|
|
if (mcbsp->pdata->buffer_size == 0)
|
|
|
|
return 0;
|
2010-03-03 13:08:08 +00:00
|
|
|
|
|
|
|
/* Returns the number of free locations in the buffer */
|
|
|
|
buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
|
|
|
|
|
|
|
|
/* Number of slots are different in McBSP ports */
|
2010-06-03 04:39:34 +00:00
|
|
|
return mcbsp->pdata->buffer_size - buffstat;
|
2010-03-03 13:08:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
|
|
|
|
* to reach the threshold value (when the DMA will be triggered to read it)
|
|
|
|
*/
|
2012-02-14 16:20:58 +00:00
|
|
|
u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp)
|
2010-03-03 13:08:08 +00:00
|
|
|
{
|
|
|
|
u16 buffstat, threshold;
|
|
|
|
|
2011-09-26 07:45:42 +00:00
|
|
|
if (mcbsp->pdata->buffer_size == 0)
|
|
|
|
return 0;
|
2010-03-03 13:08:08 +00:00
|
|
|
|
|
|
|
/* Returns the number of used locations in the buffer */
|
|
|
|
buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
|
|
|
|
/* RX threshold */
|
|
|
|
threshold = MCBSP_READ(mcbsp, THRSH1);
|
|
|
|
|
|
|
|
/* Return the number of location till we reach the threshold limit */
|
|
|
|
if (threshold <= buffstat)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return threshold - buffstat;
|
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
int omap_mcbsp_request(struct omap_mcbsp *mcbsp)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2010-02-15 18:03:33 +00:00
|
|
|
void *reg_cache;
|
2005-07-10 18:58:15 +00:00
|
|
|
int err;
|
|
|
|
|
2011-09-26 07:45:43 +00:00
|
|
|
reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL);
|
2010-02-15 18:03:33 +00:00
|
|
|
if (!reg_cache) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2008-10-08 07:01:39 +00:00
|
|
|
spin_lock(&mcbsp->lock);
|
|
|
|
if (!mcbsp->free) {
|
|
|
|
dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
|
|
|
|
mcbsp->id);
|
2010-02-15 18:03:33 +00:00
|
|
|
err = -EBUSY;
|
|
|
|
goto err_kfree;
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|
|
|
|
|
2010-12-08 00:25:41 +00:00
|
|
|
mcbsp->free = false;
|
2010-02-15 18:03:33 +00:00
|
|
|
mcbsp->reg_cache = reg_cache;
|
2008-10-08 07:01:39 +00:00
|
|
|
spin_unlock(&mcbsp->lock);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2009-01-23 10:26:46 +00:00
|
|
|
if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
|
2012-02-14 16:20:58 +00:00
|
|
|
mcbsp->pdata->ops->request(mcbsp->id - 1);
|
2009-01-23 10:26:46 +00:00
|
|
|
|
2008-10-08 07:01:41 +00:00
|
|
|
/*
|
|
|
|
* Make sure that transmitter, receiver and sample-rate generator are
|
|
|
|
* not running before activating IRQs.
|
|
|
|
*/
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR1, 0);
|
|
|
|
MCBSP_WRITE(mcbsp, SPCR2, 0);
|
2008-10-08 07:01:41 +00:00
|
|
|
|
2012-03-19 15:05:39 +00:00
|
|
|
if (mcbsp->irq) {
|
|
|
|
err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0,
|
|
|
|
"McBSP", (void *)mcbsp);
|
|
|
|
if (err != 0) {
|
|
|
|
dev_err(mcbsp->dev, "Unable to request IRQ\n");
|
|
|
|
goto err_clk_disable;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0,
|
|
|
|
"McBSP TX", (void *)mcbsp);
|
|
|
|
if (err != 0) {
|
|
|
|
dev_err(mcbsp->dev, "Unable to request TX IRQ\n");
|
|
|
|
goto err_clk_disable;
|
|
|
|
}
|
2011-06-14 11:23:52 +00:00
|
|
|
|
2012-03-19 15:05:39 +00:00
|
|
|
err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0,
|
|
|
|
"McBSP RX", (void *)mcbsp);
|
2006-04-02 16:46:27 +00:00
|
|
|
if (err != 0) {
|
2012-03-19 15:05:39 +00:00
|
|
|
dev_err(mcbsp->dev, "Unable to request RX IRQ\n");
|
2011-06-14 11:23:52 +00:00
|
|
|
goto err_free_irq;
|
2006-04-02 16:46:27 +00:00
|
|
|
}
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2010-02-15 18:03:33 +00:00
|
|
|
err_free_irq:
|
2010-01-08 18:29:04 +00:00
|
|
|
free_irq(mcbsp->tx_irq, (void *)mcbsp);
|
2010-02-15 18:03:33 +00:00
|
|
|
err_clk_disable:
|
2010-01-08 18:29:04 +00:00
|
|
|
if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
|
2012-02-14 16:20:58 +00:00
|
|
|
mcbsp->pdata->ops->free(mcbsp->id - 1);
|
2010-01-08 18:29:04 +00:00
|
|
|
|
2011-09-26 07:45:40 +00:00
|
|
|
/* Disable wakeup behavior */
|
|
|
|
if (mcbsp->pdata->has_wakeup)
|
|
|
|
MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
|
2010-01-08 18:29:04 +00:00
|
|
|
|
2010-02-15 18:03:33 +00:00
|
|
|
spin_lock(&mcbsp->lock);
|
2010-12-08 00:25:41 +00:00
|
|
|
mcbsp->free = true;
|
2010-02-15 18:03:33 +00:00
|
|
|
mcbsp->reg_cache = NULL;
|
|
|
|
err_kfree:
|
|
|
|
spin_unlock(&mcbsp->lock);
|
|
|
|
kfree(reg_cache);
|
2010-01-08 18:29:04 +00:00
|
|
|
|
|
|
|
return err;
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2010-02-15 18:03:33 +00:00
|
|
|
void *reg_cache;
|
2008-10-08 07:01:39 +00:00
|
|
|
|
|
|
|
if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
|
2012-02-14 16:20:58 +00:00
|
|
|
mcbsp->pdata->ops->free(mcbsp->id - 1);
|
2008-07-03 09:24:39 +00:00
|
|
|
|
2011-09-26 07:45:40 +00:00
|
|
|
/* Disable wakeup behavior */
|
|
|
|
if (mcbsp->pdata->has_wakeup)
|
|
|
|
MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
|
2009-08-20 13:18:15 +00:00
|
|
|
|
2012-03-19 15:05:39 +00:00
|
|
|
/* Disable interrupt requests */
|
|
|
|
if (mcbsp->irq)
|
|
|
|
MCBSP_WRITE(mcbsp, IRQEN, 0);
|
|
|
|
|
|
|
|
if (mcbsp->irq) {
|
|
|
|
free_irq(mcbsp->irq, (void *)mcbsp);
|
|
|
|
} else {
|
2011-06-14 11:23:52 +00:00
|
|
|
free_irq(mcbsp->rx_irq, (void *)mcbsp);
|
2012-03-19 15:05:39 +00:00
|
|
|
free_irq(mcbsp->tx_irq, (void *)mcbsp);
|
|
|
|
}
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2010-02-15 18:03:33 +00:00
|
|
|
reg_cache = mcbsp->reg_cache;
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2012-03-05 09:32:27 +00:00
|
|
|
/*
|
|
|
|
* Select CLKS source from internal source unconditionally before
|
|
|
|
* marking the McBSP port as free.
|
|
|
|
* If the external clock source via MCBSP_CLKS pin has been selected the
|
|
|
|
* system will refuse to enter idle if the CLKS pin source is not reset
|
|
|
|
* back to internal source.
|
|
|
|
*/
|
2012-11-21 17:42:25 +00:00
|
|
|
if (!mcbsp_omap1())
|
2012-03-05 09:32:27 +00:00
|
|
|
omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC);
|
|
|
|
|
2010-02-15 18:03:33 +00:00
|
|
|
spin_lock(&mcbsp->lock);
|
|
|
|
if (mcbsp->free)
|
|
|
|
dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
|
|
|
|
else
|
2010-12-08 00:25:41 +00:00
|
|
|
mcbsp->free = true;
|
2010-02-15 18:03:33 +00:00
|
|
|
mcbsp->reg_cache = NULL;
|
2008-10-08 07:01:39 +00:00
|
|
|
spin_unlock(&mcbsp->lock);
|
2010-02-15 18:03:33 +00:00
|
|
|
|
|
|
|
if (reg_cache)
|
|
|
|
kfree(reg_cache);
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-08-07 06:59:47 +00:00
|
|
|
* Here we start the McBSP, by enabling transmitter, receiver or both.
|
|
|
|
* If no transmitter or receiver is active prior calling, then sample-rate
|
|
|
|
* generator and frame sync are started.
|
2005-07-10 18:58:15 +00:00
|
|
|
*/
|
2012-02-14 16:20:58 +00:00
|
|
|
void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2010-08-31 08:11:44 +00:00
|
|
|
int enable_srg = 0;
|
2005-07-10 18:58:15 +00:00
|
|
|
u16 w;
|
|
|
|
|
2011-09-26 07:45:45 +00:00
|
|
|
if (mcbsp->st_data)
|
2010-02-22 12:21:11 +00:00
|
|
|
omap_st_start(mcbsp);
|
|
|
|
|
2010-08-31 08:11:44 +00:00
|
|
|
/* Only enable SRG, if McBSP is master */
|
|
|
|
w = MCBSP_READ_CACHE(mcbsp, PCR0);
|
|
|
|
if (w & (FSXM | FSRM | CLKXM | CLKRM))
|
|
|
|
enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
|
|
|
|
MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
|
2009-08-07 06:59:47 +00:00
|
|
|
|
2010-08-31 08:11:44 +00:00
|
|
|
if (enable_srg) {
|
2009-08-07 06:59:47 +00:00
|
|
|
/* Start the sample generator */
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, SPCR2);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
|
2009-08-07 06:59:47 +00:00
|
|
|
}
|
2005-07-10 18:58:15 +00:00
|
|
|
|
|
|
|
/* Enable transmitter and receiver */
|
2009-08-23 09:24:27 +00:00
|
|
|
tx &= 1;
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, SPCR2);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR2, w | tx);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2009-08-23 09:24:27 +00:00
|
|
|
rx &= 1;
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, SPCR1);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR1, w | rx);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2009-08-20 13:18:09 +00:00
|
|
|
/*
|
|
|
|
* Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
|
|
|
|
* REVISIT: 100us may give enough time for two CLKSRG, however
|
|
|
|
* due to some unknown PM related, clock gating etc. reason it
|
|
|
|
* is now at 500us.
|
|
|
|
*/
|
|
|
|
udelay(500);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2010-08-31 08:11:44 +00:00
|
|
|
if (enable_srg) {
|
2009-08-07 06:59:47 +00:00
|
|
|
/* Start frame sync */
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, SPCR2);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
|
2009-08-07 06:59:47 +00:00
|
|
|
}
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2011-09-26 07:45:41 +00:00
|
|
|
if (mcbsp->pdata->has_ccr) {
|
2009-08-23 09:24:27 +00:00
|
|
|
/* Release the transmitter and receiver */
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, XCCR);
|
2009-08-23 09:24:27 +00:00
|
|
|
w &= ~(tx ? XDISABLE : 0);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, XCCR, w);
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, RCCR);
|
2009-08-23 09:24:27 +00:00
|
|
|
w &= ~(rx ? RDISABLE : 0);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, RCCR, w);
|
2009-08-23 09:24:27 +00:00
|
|
|
}
|
|
|
|
|
2005-07-10 18:58:15 +00:00
|
|
|
/* Dump McBSP Regs */
|
2012-02-14 16:20:58 +00:00
|
|
|
omap_mcbsp_dump_reg(mcbsp);
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2009-08-07 06:59:47 +00:00
|
|
|
int idle;
|
2005-07-10 18:58:15 +00:00
|
|
|
u16 w;
|
|
|
|
|
2008-07-03 09:24:39 +00:00
|
|
|
/* Reset transmitter */
|
2009-08-23 09:24:27 +00:00
|
|
|
tx &= 1;
|
2011-09-26 07:45:41 +00:00
|
|
|
if (mcbsp->pdata->has_ccr) {
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, XCCR);
|
2009-08-23 09:24:27 +00:00
|
|
|
w |= (tx ? XDISABLE : 0);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, XCCR, w);
|
2009-08-23 09:24:27 +00:00
|
|
|
}
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, SPCR2);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
|
|
|
/* Reset receiver */
|
2009-08-23 09:24:27 +00:00
|
|
|
rx &= 1;
|
2011-09-26 07:45:41 +00:00
|
|
|
if (mcbsp->pdata->has_ccr) {
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, RCCR);
|
2009-10-14 16:56:35 +00:00
|
|
|
w |= (rx ? RDISABLE : 0);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, RCCR, w);
|
2009-08-23 09:24:27 +00:00
|
|
|
}
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, SPCR1);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
|
|
|
|
MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
|
2009-08-07 06:59:47 +00:00
|
|
|
|
|
|
|
if (idle) {
|
|
|
|
/* Reset the sample rate generator */
|
omap: McBSP: Use cache when modifying individual register bits
Change the way McBSP registers are updated: use cached values instead of
relying upon those read back from the device.
With this patch, I have finally managed to get rid of all random
playback/recording hangups on my OMAP1510 based Amstrad Delta hardware. Before
that, values read back from McBSP registers to be used for updating them
happened to be errornous.
From the hardware side, the issue appeared to be caused by a relatively high
power requirements of an external USB adapter connected to the board's printer
dedicated USB port.
I think there is one important point that makes this patch worth of applying,
apart from my hardware quality. With the current code, if it ever happens to
any machine, no matter if OMAP1510 or newer, to read incorrect value from a
McBSP register, this wrong value will get written back without any checking.
That can lead to hardware damage if, for example, an input pin is turned into
output as a result.
Applies on top of patch 3 from this series:
[PATCH v9 3/4] OMAP: McBSP: Introduce caching in register write operations
Tested on OMAP1510 based Amstrad Delta using linux-omap for-next, commit
fb7380d70e041e4b3892f6b19dff7efb609d15a4 (2.6.33-rc3+ dated 2010-01-11).
Compile-tested with omap_3430sdp_defconfig.
Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Peter Ujfalusi <peter.ujfalusi@nokia.com>
Acked-by: Jarkko Nikula <jhnikula@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
2010-02-15 18:03:33 +00:00
|
|
|
w = MCBSP_READ_CACHE(mcbsp, SPCR2);
|
2010-02-15 18:03:32 +00:00
|
|
|
MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
|
2009-08-07 06:59:47 +00:00
|
|
|
}
|
2010-02-22 12:21:11 +00:00
|
|
|
|
2011-09-26 07:45:45 +00:00
|
|
|
if (mcbsp->st_data)
|
2010-02-22 12:21:11 +00:00
|
|
|
omap_st_stop(mcbsp);
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|
|
|
|
|
2012-02-14 16:20:58 +00:00
|
|
|
int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id)
|
2011-07-01 08:52:25 +00:00
|
|
|
{
|
2012-08-16 13:41:00 +00:00
|
|
|
struct clk *fck_src;
|
2011-09-26 07:45:48 +00:00
|
|
|
const char *src;
|
2012-08-16 13:41:00 +00:00
|
|
|
int r;
|
2011-09-26 07:45:48 +00:00
|
|
|
|
|
|
|
if (fck_src_id == MCBSP_CLKS_PAD_SRC)
|
2012-08-16 13:41:00 +00:00
|
|
|
src = "pad_fck";
|
2011-09-26 07:45:48 +00:00
|
|
|
else if (fck_src_id == MCBSP_CLKS_PRCM_SRC)
|
2012-08-16 13:41:00 +00:00
|
|
|
src = "prcm_fck";
|
2011-09-26 07:45:48 +00:00
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-08-16 13:41:00 +00:00
|
|
|
fck_src = clk_get(mcbsp->dev, src);
|
|
|
|
if (IS_ERR(fck_src)) {
|
|
|
|
dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src);
|
2011-09-26 07:45:48 +00:00
|
|
|
return -EINVAL;
|
2012-08-16 13:41:00 +00:00
|
|
|
}
|
2012-03-08 11:34:16 +00:00
|
|
|
|
2012-08-16 13:41:00 +00:00
|
|
|
pm_runtime_put_sync(mcbsp->dev);
|
2012-02-14 16:20:58 +00:00
|
|
|
|
2012-08-16 13:41:00 +00:00
|
|
|
r = clk_set_parent(mcbsp->fclk, fck_src);
|
|
|
|
if (r) {
|
|
|
|
dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n",
|
|
|
|
src);
|
|
|
|
clk_put(fck_src);
|
|
|
|
return r;
|
2012-03-08 09:01:37 +00:00
|
|
|
}
|
2011-09-26 07:45:49 +00:00
|
|
|
|
2012-08-16 13:41:00 +00:00
|
|
|
pm_runtime_get_sync(mcbsp->dev);
|
|
|
|
|
|
|
|
clk_put(fck_src);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2011-07-01 08:52:25 +00:00
|
|
|
}
|
|
|
|
|
2009-08-20 13:18:11 +00:00
|
|
|
#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; \
|
|
|
|
\
|
2013-07-19 07:24:59 +00:00
|
|
|
status = kstrtoul(buf, 0, &val); \
|
2009-08-20 13:18:11 +00:00
|
|
|
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);
|
|
|
|
|
2009-08-24 14:45:50 +00:00
|
|
|
static const char *dma_op_modes[] = {
|
2012-03-15 10:29:49 +00:00
|
|
|
"element", "threshold",
|
2009-08-24 14:45:50 +00:00
|
|
|
};
|
|
|
|
|
2009-08-20 13:18:14 +00:00
|
|
|
static ssize_t dma_op_mode_show(struct device *dev,
|
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
|
{
|
|
|
|
struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
|
2009-08-24 14:45:50 +00:00
|
|
|
int dma_op_mode, i = 0;
|
|
|
|
ssize_t len = 0;
|
|
|
|
const char * const *s;
|
2009-08-20 13:18:14 +00:00
|
|
|
|
|
|
|
dma_op_mode = mcbsp->dma_op_mode;
|
|
|
|
|
2009-08-24 14:45:50 +00:00
|
|
|
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
|
|
|
|
if (dma_op_mode == i)
|
|
|
|
len += sprintf(buf + len, "[%s] ", *s);
|
|
|
|
else
|
|
|
|
len += sprintf(buf + len, "%s ", *s);
|
|
|
|
}
|
|
|
|
len += sprintf(buf + len, "\n");
|
|
|
|
|
|
|
|
return len;
|
2009-08-20 13:18:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2009-08-24 14:45:50 +00:00
|
|
|
const char * const *s;
|
|
|
|
int i = 0;
|
2009-08-20 13:18:14 +00:00
|
|
|
|
2009-08-24 14:45:50 +00:00
|
|
|
for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
|
|
|
|
if (sysfs_streq(buf, *s))
|
|
|
|
break;
|
2009-08-20 13:18:14 +00:00
|
|
|
|
2009-08-24 14:45:50 +00:00
|
|
|
if (i == ARRAY_SIZE(dma_op_modes))
|
|
|
|
return -EINVAL;
|
2009-08-20 13:18:14 +00:00
|
|
|
|
2009-08-24 14:45:50 +00:00
|
|
|
spin_lock_irq(&mcbsp->lock);
|
2009-08-20 13:18:14 +00:00
|
|
|
if (!mcbsp->free) {
|
|
|
|
size = -EBUSY;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2009-08-24 14:45:50 +00:00
|
|
|
mcbsp->dma_op_mode = i;
|
2009-08-20 13:18:14 +00:00
|
|
|
|
|
|
|
unlock:
|
|
|
|
spin_unlock_irq(&mcbsp->lock);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
|
|
|
|
|
2011-09-26 07:45:42 +00:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2010-02-22 12:21:11 +00:00
|
|
|
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 *sidetone_attrs[] = {
|
|
|
|
&dev_attr_st_taps.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group sidetone_attr_group = {
|
|
|
|
.attrs = (struct attribute **)sidetone_attrs,
|
|
|
|
};
|
|
|
|
|
2012-12-07 14:26:29 +00:00
|
|
|
static int omap_st_add(struct omap_mcbsp *mcbsp, struct resource *res)
|
2010-02-22 12:21:11 +00:00
|
|
|
{
|
|
|
|
struct omap_mcbsp_st_data *st_data;
|
|
|
|
int err;
|
|
|
|
|
2012-02-14 12:52:42 +00:00
|
|
|
st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL);
|
|
|
|
if (!st_data)
|
|
|
|
return -ENOMEM;
|
2010-02-22 12:21:11 +00:00
|
|
|
|
2012-02-14 12:52:42 +00:00
|
|
|
st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start,
|
|
|
|
resource_size(res));
|
|
|
|
if (!st_data->io_base_st)
|
|
|
|
return -ENOMEM;
|
2010-02-22 12:21:11 +00:00
|
|
|
|
|
|
|
err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
|
|
|
|
if (err)
|
2012-02-14 12:52:42 +00:00
|
|
|
return err;
|
2010-02-22 12:21:11 +00:00
|
|
|
|
|
|
|
mcbsp->st_data = st_data;
|
|
|
|
return 0;
|
2009-08-20 13:18:11 +00:00
|
|
|
}
|
|
|
|
|
2005-07-10 18:58:15 +00:00
|
|
|
/*
|
|
|
|
* McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
|
|
|
|
* 730 has only 2 McBSP, and both of them are MPU peripherals.
|
|
|
|
*/
|
2012-12-07 14:26:29 +00:00
|
|
|
int omap_mcbsp_init(struct platform_device *pdev)
|
2008-07-03 09:24:39 +00:00
|
|
|
{
|
2012-02-14 12:52:42 +00:00
|
|
|
struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
|
2011-02-24 20:51:45 +00:00
|
|
|
struct resource *res;
|
2008-07-03 09:24:39 +00:00
|
|
|
int ret = 0;
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2008-10-08 07:01:39 +00:00
|
|
|
spin_lock_init(&mcbsp->lock);
|
2010-12-08 00:25:41 +00:00
|
|
|
mcbsp->free = true;
|
2008-07-03 09:24:39 +00:00
|
|
|
|
2011-02-24 20:51:45 +00:00
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
|
|
|
|
if (!res) {
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
if (!res) {
|
2012-02-14 12:52:42 +00:00
|
|
|
dev_err(mcbsp->dev, "invalid memory resource\n");
|
|
|
|
return -ENOMEM;
|
2011-02-24 20:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
2012-02-14 12:52:42 +00:00
|
|
|
if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
|
|
|
|
dev_name(&pdev->dev))) {
|
|
|
|
dev_err(mcbsp->dev, "memory region already claimed\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2011-02-24 20:51:45 +00:00
|
|
|
mcbsp->phys_base = res->start;
|
2011-09-26 07:45:43 +00:00
|
|
|
mcbsp->reg_cache_size = resource_size(res);
|
2012-02-14 12:52:42 +00:00
|
|
|
mcbsp->io_base = devm_ioremap(&pdev->dev, res->start,
|
|
|
|
resource_size(res));
|
|
|
|
if (!mcbsp->io_base)
|
|
|
|
return -ENOMEM;
|
2008-09-04 13:25:42 +00:00
|
|
|
|
2011-02-24 20:51:45 +00:00
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
|
|
|
|
if (!res)
|
|
|
|
mcbsp->phys_dma_base = mcbsp->phys_base;
|
|
|
|
else
|
|
|
|
mcbsp->phys_dma_base = res->start;
|
|
|
|
|
2012-03-19 15:05:39 +00:00
|
|
|
/*
|
|
|
|
* OMAP1, 2 uses two interrupt lines: TX, RX
|
|
|
|
* OMAP2430, OMAP3 SoC have combined IRQ line as well.
|
|
|
|
* OMAP4 and newer SoC only have the combined IRQ line.
|
|
|
|
* Use the combined IRQ if available since it gives better debugging
|
|
|
|
* possibilities.
|
|
|
|
*/
|
|
|
|
mcbsp->irq = platform_get_irq_byname(pdev, "common");
|
|
|
|
if (mcbsp->irq == -ENXIO) {
|
|
|
|
mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
|
|
|
|
|
|
|
|
if (mcbsp->tx_irq == -ENXIO) {
|
|
|
|
mcbsp->irq = platform_get_irq(pdev, 0);
|
|
|
|
mcbsp->tx_irq = 0;
|
|
|
|
} else {
|
|
|
|
mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
|
|
|
|
mcbsp->irq = 0;
|
|
|
|
}
|
2012-03-07 09:15:37 +00:00
|
|
|
}
|
2011-02-24 09:46:50 +00:00
|
|
|
|
2013-07-11 12:35:46 +00:00
|
|
|
if (!pdev->dev.of_node) {
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
|
|
|
|
if (!res) {
|
|
|
|
dev_err(&pdev->dev, "invalid tx DMA channel\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
mcbsp->dma_req[0] = res->start;
|
|
|
|
mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0];
|
2011-02-24 20:51:45 +00:00
|
|
|
|
2013-07-11 12:35:46 +00:00
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
|
|
|
|
if (!res) {
|
|
|
|
dev_err(&pdev->dev, "invalid rx DMA channel\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
mcbsp->dma_req[1] = res->start;
|
|
|
|
mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1];
|
|
|
|
} else {
|
|
|
|
mcbsp->dma_data[0].filter_data = "tx";
|
|
|
|
mcbsp->dma_data[1].filter_data = "rx";
|
2011-02-24 20:51:45 +00:00
|
|
|
}
|
2013-07-11 12:35:46 +00:00
|
|
|
|
2013-04-03 09:06:05 +00:00
|
|
|
mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, 0);
|
|
|
|
mcbsp->dma_data[0].maxburst = 4;
|
2008-07-03 09:24:39 +00:00
|
|
|
|
2013-07-11 12:35:46 +00:00
|
|
|
mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, 1);
|
|
|
|
mcbsp->dma_data[1].maxburst = 4;
|
|
|
|
|
2009-01-23 10:26:46 +00:00
|
|
|
mcbsp->fclk = clk_get(&pdev->dev, "fck");
|
|
|
|
if (IS_ERR(mcbsp->fclk)) {
|
|
|
|
ret = PTR_ERR(mcbsp->fclk);
|
2012-02-14 12:52:42 +00:00
|
|
|
dev_err(mcbsp->dev, "unable to get fck: %d\n", ret);
|
|
|
|
return ret;
|
2008-07-03 09:24:39 +00:00
|
|
|
}
|
|
|
|
|
2011-09-26 07:45:42 +00:00
|
|
|
mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
|
|
|
|
if (mcbsp->pdata->buffer_size) {
|
|
|
|
/*
|
|
|
|
* Initially configure the maximum thresholds to a safe value.
|
|
|
|
* The McBSP FIFO usage with these values should not go under
|
|
|
|
* 16 locations.
|
|
|
|
* If the whole FIFO without safety buffer is used, than there
|
|
|
|
* is a possibility that the DMA will be not able to push the
|
|
|
|
* new data on time, causing channel shifts in runtime.
|
|
|
|
*/
|
|
|
|
mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
|
|
|
|
mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
|
|
|
|
|
|
|
|
ret = sysfs_create_group(&mcbsp->dev->kobj,
|
|
|
|
&additional_attr_group);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(mcbsp->dev,
|
|
|
|
"Unable to create additional controls\n");
|
|
|
|
goto err_thres;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mcbsp->max_tx_thres = -EINVAL;
|
|
|
|
mcbsp->max_rx_thres = -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-09-26 07:45:45 +00:00
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
|
|
|
|
if (res) {
|
|
|
|
ret = omap_st_add(mcbsp, res);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(mcbsp->dev,
|
|
|
|
"Unable to create sidetone controls\n");
|
|
|
|
goto err_st;
|
|
|
|
}
|
|
|
|
}
|
2009-08-20 13:18:11 +00:00
|
|
|
|
2008-09-04 13:25:42 +00:00
|
|
|
return 0;
|
2008-07-03 09:24:39 +00:00
|
|
|
|
2011-09-26 07:45:45 +00:00
|
|
|
err_st:
|
|
|
|
if (mcbsp->pdata->buffer_size)
|
2012-02-14 12:52:42 +00:00
|
|
|
sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
|
2011-09-26 07:45:42 +00:00
|
|
|
err_thres:
|
|
|
|
clk_put(mcbsp->fclk);
|
2008-07-03 09:24:39 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2006-04-02 16:46:27 +00:00
|
|
|
|
2012-12-07 14:26:29 +00:00
|
|
|
void omap_mcbsp_sysfs_remove(struct omap_mcbsp *mcbsp)
|
2005-07-10 18:58:15 +00:00
|
|
|
{
|
2012-02-14 12:52:42 +00:00
|
|
|
if (mcbsp->pdata->buffer_size)
|
|
|
|
sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
|
2005-07-10 18:58:15 +00:00
|
|
|
|
2012-02-14 12:52:42 +00:00
|
|
|
if (mcbsp->st_data)
|
|
|
|
sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
|
2005-07-10 18:58:15 +00:00
|
|
|
}
|