[alsa-devel] [PATCH 0/5] ASoC: omap-mcbsp: Cleanup and split core/sidetone support
Hi,
While preparing for merging the davinci and omap directories (I will send the series for that shortly) I have taken some time to do cleanup on the McBSP driver.
The mcbsp.c/h files were the result when we moved code from arch/arm/plat-omap to sound/soc/omap/ a long time ago and it contained code for McBSP core and the OMAP3 sidetone functionality.
With this series I tried to split the core and sidetone code to separate files.
There were two reasons for this: 1. to have the OMAP specific DAI driver files prefixed with omap-* under the new sound/soc/ti/ directory 2. easier to find functions as all sidetone code is in separate file.
Regards, Peter --- Peter Ujfalusi (5): ASoC: omap-mcbsp: Clean up dma_data addr initialization code ASoC: omap-mcbsp: Clean up the interrupt handlers ASoC: omap-mcbsp: Simplify the mcbsp_start/_stop function parameters ASoC: omap-mcbsp: Move out the FIFO check from set_threshold and get_delay ASoC: omap-mcbsp: Re-arrange files for core McBSP and Sidetone function split
sound/soc/omap/Makefile | 2 +- sound/soc/omap/mcbsp.c | 1104 ----------------- sound/soc/omap/{mcbsp.h => omap-mcbsp-priv.h} | 126 +- sound/soc/omap/omap-mcbsp-st.c | 516 ++++++++ sound/soc/omap/omap-mcbsp.c | 855 ++++++++++--- sound/soc/omap/omap-mcbsp.h | 8 +- 6 files changed, 1281 insertions(+), 1330 deletions(-) delete mode 100644 sound/soc/omap/mcbsp.c rename sound/soc/omap/{mcbsp.h => omap-mcbsp-priv.h} (70%) create mode 100644 sound/soc/omap/omap-mcbsp-st.c
The dma_data.maxburst is configured runtime so no need to initialize it.
Make the omap_mcbsp_dma_reg_params() a bit more intuitive to read for the first glance by using SNDRV_PCM_STREAM_PLAYBACK/CAPTURE and to group the outermost if case by stream direction.
While there, fix the outdated comment for the function.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/mcbsp.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-)
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 79d4dc785e5c..8c3cedfcb878 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -227,28 +227,27 @@ void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
/** * 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) + * @mcbsp: omap_mcbsp struct for the McBSP instance + * @stream: Stream direction (playback/capture) * * 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 + * to be used by DMA for transferring/receiving data */ static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, unsigned int stream) { int data_reg;
- if (mcbsp->pdata->reg_size == 2) { - if (stream) - data_reg = OMAP_MCBSP_REG_DRR1; - else + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mcbsp->pdata->reg_size == 2) data_reg = OMAP_MCBSP_REG_DXR1; - } else { - if (stream) - data_reg = OMAP_MCBSP_REG_DRR; else data_reg = OMAP_MCBSP_REG_DXR; + } else { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DRR1; + else + data_reg = OMAP_MCBSP_REG_DRR; }
return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; @@ -1034,11 +1033,10 @@ int omap_mcbsp_init(struct platform_device *pdev) mcbsp->dma_data[1].filter_data = "rx"; }
- mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, 0); - mcbsp->dma_data[0].maxburst = 4; - - mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, 1); - mcbsp->dma_data[1].maxburst = 4; + mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_PLAYBACK); + mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_CAPTURE);
mcbsp->fclk = clk_get(&pdev->dev, "fck"); if (IS_ERR(mcbsp->fclk)) {
The parameter name of dev_id is leftover from the old times when we passed numeric ID as data for the interrupt handlers.
The mcbsp_rx and mcbsp_tx is misleading as they are pointers to the mcbsp struct.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/mcbsp.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 8c3cedfcb878..fca0b0e9186b 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -110,9 +110,9 @@ static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) dev_dbg(mcbsp->dev, "***********************\n"); }
-static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id) +static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) { - struct omap_mcbsp *mcbsp = dev_id; + struct omap_mcbsp *mcbsp = data; u16 irqst;
irqst = MCBSP_READ(mcbsp, IRQST); @@ -151,37 +151,37 @@ static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; }
-static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id) +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) { - struct omap_mcbsp *mcbsp_tx = dev_id; + struct omap_mcbsp *mcbsp = data; u16 irqst_spcr2;
- irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2); - dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); + irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); + dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
if (irqst_spcr2 & XSYNC_ERR) { - dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n", + dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", irqst_spcr2); /* Writing zero to XSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2)); + MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); }
return IRQ_HANDLED; }
-static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id) +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) { - struct omap_mcbsp *mcbsp_rx = dev_id; + struct omap_mcbsp *mcbsp = data; u16 irqst_spcr1;
- irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1); - dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); + irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); + dev_dbg(mcbsp->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", + dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", irqst_spcr1); /* Writing zero to RSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1)); + MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); }
return IRQ_HANDLED;
We either start/stop TX or RX, never both. Move the tx/rx direction selection within the functions.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/mcbsp.c | 8 ++++++-- sound/soc/omap/mcbsp.h | 4 ++-- sound/soc/omap/omap-mcbsp.c | 9 ++++----- 3 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index fca0b0e9186b..7ff22561f00f 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -637,8 +637,10 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp) * If no transmitter or receiver is active prior calling, then sample-rate * generator and frame sync are started. */ -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx) +void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) { + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; int enable_srg = 0; u16 w;
@@ -694,8 +696,10 @@ void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx) omap_mcbsp_dump_reg(mcbsp); }
-void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx) +void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) { + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; int idle; u16 w;
diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/mcbsp.h index 46ae1269a698..92472c6ef358 100644 --- a/sound/soc/omap/mcbsp.h +++ b/sound/soc/omap/mcbsp.h @@ -339,8 +339,8 @@ u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp); int omap_mcbsp_get_dma_op_mode(struct omap_mcbsp *mcbsp); int omap_mcbsp_request(struct omap_mcbsp *mcbsp); void omap_mcbsp_free(struct omap_mcbsp *mcbsp); -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx); -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx); +void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream); +void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream);
/* McBSP functional clock source changing function */ int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id); diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index caa24c463b3e..a18b7ecc3a2e 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -201,27 +201,26 @@ static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: mcbsp->active++; - omap_mcbsp_start(mcbsp, play, !play); + omap_mcbsp_start(mcbsp, substream->stream); break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - omap_mcbsp_stop(mcbsp, play, !play); + omap_mcbsp_stop(mcbsp, substream->stream); mcbsp->active--; break; default: - err = -EINVAL; + return -EINVAL; }
- return err; + return 0; }
static snd_pcm_sframes_t omap_mcbsp_dai_delay(
Check if the McBSP have FIFO in the omap_mcbsp_set_threshold() and omap_mcbsp_dai_delay() delay function to skip calling the lower layer if it is not needed.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/mcbsp.c | 12 ------------ sound/soc/omap/omap-mcbsp.c | 8 ++++++++ 2 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 7ff22561f00f..b19168f5c110 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -450,9 +450,6 @@ int omap_st_is_enabled(struct omap_mcbsp *mcbsp) */ void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) { - if (mcbsp->pdata->buffer_size == 0) - return; - if (threshold && threshold <= mcbsp->max_tx_thres) MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); } @@ -464,9 +461,6 @@ void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) */ void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) { - if (mcbsp->pdata->buffer_size == 0) - return; - if (threshold && threshold <= mcbsp->max_rx_thres) MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); } @@ -478,9 +472,6 @@ u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) { u16 buffstat;
- if (mcbsp->pdata->buffer_size == 0) - return 0; - /* Returns the number of free locations in the buffer */ buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
@@ -496,9 +487,6 @@ u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) { u16 buffstat, threshold;
- if (mcbsp->pdata->buffer_size == 0) - return 0; - /* Returns the number of used locations in the buffer */ buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); /* RX threshold */ diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index a18b7ecc3a2e..69a6b8ad6d42 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -71,6 +71,10 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream, struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); int words;
+ /* No need to proceed further if McBSP does not have FIFO */ + if (mcbsp->pdata->buffer_size == 0) + return; + /* * Configure McBSP threshold based on either: * packet_size, when the sDMA is in packet mode, or based on the @@ -233,6 +237,10 @@ static snd_pcm_sframes_t omap_mcbsp_dai_delay( u16 fifo_use; snd_pcm_sframes_t delay;
+ /* No need to proceed further if McBSP does not have FIFO */ + if (mcbsp->pdata->buffer_size == 0) + return 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) fifo_use = omap_mcbsp_get_tx_delay(mcbsp); else
The mcbsp.c was copied a while back from arch/arm/plat-omap/mcbsp.c and it contained a mix of McBSP and McBSP sidetone functions.
Create new file structure with the following split: omap-mcbsp.c - McBSP related functions omap-mcbsp-st.c - McBSP sidetone functionality omap-mcbsp-priv.h - Private header for internal use omap-mcbsp.h - Header for user drivers
I have tried to do the code move with minimal code change, cleanup patches can be based on the new structure.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@ti.com --- sound/soc/omap/Makefile | 2 +- sound/soc/omap/mcbsp.c | 1094 ----------------- sound/soc/omap/{mcbsp.h => omap-mcbsp-priv.h} | 126 +- sound/soc/omap/omap-mcbsp-st.c | 516 ++++++++ sound/soc/omap/omap-mcbsp.c | 838 ++++++++++--- sound/soc/omap/omap-mcbsp.h | 8 +- 6 files changed, 1269 insertions(+), 1315 deletions(-) delete mode 100644 sound/soc/omap/mcbsp.c rename sound/soc/omap/{mcbsp.h => omap-mcbsp-priv.h} (70%) create mode 100644 sound/soc/omap/omap-mcbsp-st.c
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 53eba3413485..8922aa5a840c 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -2,7 +2,7 @@ # OMAP Platform Support snd-soc-sdma-objs := sdma-pcm.o snd-soc-omap-dmic-objs := omap-dmic.o -snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o +snd-soc-omap-mcbsp-objs := omap-mcbsp.o omap-mcbsp-st.o snd-soc-omap-mcpdm-objs := omap-mcpdm.o snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c deleted file mode 100644 index b19168f5c110..000000000000 --- a/sound/soc/omap/mcbsp.c +++ /dev/null @@ -1,1094 +0,0 @@ -/* - * sound/soc/omap/mcbsp.c - * - * Copyright (C) 2004 Nokia Corporation - * Author: Samuel Ortiz samuel.ortiz@nokia.com - * - * Contact: Jarkko Nikula jarkko.nikula@bitmer.com - * Peter Ujfalusi peter.ujfalusi@ti.com - * - * 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> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/err.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/pm_runtime.h> - -#include <linux/platform_data/asoc-ti-mcbsp.h> - -#include "mcbsp.h" - -static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) -{ - void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; - - if (mcbsp->pdata->reg_size == 2) { - ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; - writew_relaxed((u16)val, addr); - } else { - ((u32 *)mcbsp->reg_cache)[reg] = val; - writel_relaxed(val, addr); - } -} - -static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache) -{ - void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; - - if (mcbsp->pdata->reg_size == 2) { - return !from_cache ? readw_relaxed(addr) : - ((u16 *)mcbsp->reg_cache)[reg]; - } else { - return !from_cache ? readl_relaxed(addr) : - ((u32 *)mcbsp->reg_cache)[reg]; - } -} - -static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) -{ - writel_relaxed(val, mcbsp->st_data->io_base_st + reg); -} - -static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) -{ - return readl_relaxed(mcbsp->st_data->io_base_st + reg); -} - -#define MCBSP_READ(mcbsp, reg) \ - omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) -#define MCBSP_WRITE(mcbsp, reg, val) \ - omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) -#define MCBSP_READ_CACHE(mcbsp, reg) \ - omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) - -#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) - -static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) -{ - dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); - dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", - MCBSP_READ(mcbsp, DRR2)); - dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", - MCBSP_READ(mcbsp, DRR1)); - dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", - MCBSP_READ(mcbsp, DXR2)); - dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", - MCBSP_READ(mcbsp, DXR1)); - dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", - MCBSP_READ(mcbsp, SPCR2)); - dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", - MCBSP_READ(mcbsp, SPCR1)); - dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", - MCBSP_READ(mcbsp, RCR2)); - dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", - MCBSP_READ(mcbsp, RCR1)); - dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", - MCBSP_READ(mcbsp, XCR2)); - dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", - MCBSP_READ(mcbsp, XCR1)); - dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", - MCBSP_READ(mcbsp, SRGR2)); - dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", - MCBSP_READ(mcbsp, SRGR1)); - dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", - MCBSP_READ(mcbsp, PCR0)); - dev_dbg(mcbsp->dev, "***********************\n"); -} - -static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - 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; -} - -static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - u16 irqst_spcr2; - - irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); - dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); - - if (irqst_spcr2 & XSYNC_ERR) { - dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", - irqst_spcr2); - /* Writing zero to XSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); - } - - return IRQ_HANDLED; -} - -static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - u16 irqst_spcr1; - - irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); - dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); - - if (irqst_spcr1 & RSYNC_ERR) { - dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", - irqst_spcr1); - /* Writing zero to RSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); - } - - 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(). - */ -void omap_mcbsp_config(struct omap_mcbsp *mcbsp, - const struct omap_mcbsp_reg_cfg *config) -{ - dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", - mcbsp->id, mcbsp->phys_base); - - /* We write the given config */ - 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); - if (mcbsp->pdata->has_ccr) { - MCBSP_WRITE(mcbsp, XCCR, config->xccr); - MCBSP_WRITE(mcbsp, RCCR, config->rccr); - } - /* Enable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); - - /* Enable TX/RX sync error interrupts by default */ - if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | - RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); -} - -/** - * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register - * @mcbsp: omap_mcbsp struct for the McBSP instance - * @stream: Stream direction (playback/capture) - * - * Returns the address of mcbsp data transmit register or data receive register - * to be used by DMA for transferring/receiving data - */ -static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, - unsigned int stream) -{ - int data_reg; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (mcbsp->pdata->reg_size == 2) - data_reg = OMAP_MCBSP_REG_DXR1; - else - data_reg = OMAP_MCBSP_REG_DXR; - } else { - if (mcbsp->pdata->reg_size == 2) - data_reg = OMAP_MCBSP_REG_DRR1; - else - data_reg = OMAP_MCBSP_REG_DRR; - } - - return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; -} - -static void omap_st_on(struct omap_mcbsp *mcbsp) -{ - unsigned int w; - - if (mcbsp->pdata->force_ick_on) - mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); - - /* Disable Sidetone clock auto-gating for normal operation */ - w = MCBSP_ST_READ(mcbsp, SYSCONFIG); - MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); - - /* 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)); - - /* Enable Sidetone clock auto-gating to reduce power consumption */ - w = MCBSP_ST_READ(mcbsp, SYSCONFIG); - MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); - - if (mcbsp->pdata->force_ick_on) - mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); -} - -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)); -} - -int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - 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; -} - -int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - 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; - - if (st_data->enabled && !st_data->running) { - 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; -} - -int omap_st_enable(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (!st_data) - return -ENODEV; - - spin_lock_irq(&mcbsp->lock); - st_data->enabled = 1; - omap_st_start(mcbsp); - spin_unlock_irq(&mcbsp->lock); - - return 0; -} - -static int omap_st_stop(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (st_data->running) { - if (!mcbsp->free) { - omap_st_off(mcbsp); - st_data->running = 0; - } - } - - return 0; -} - -int omap_st_disable(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - 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; -} - -int omap_st_is_enabled(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (!st_data) - return -ENODEV; - - return st_data->enabled; -} - -/* - * 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. - */ -void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) -{ - if (threshold && threshold <= mcbsp->max_tx_thres) - MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); -} - -/* - * 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. - */ -void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) -{ - if (threshold && threshold <= mcbsp->max_rx_thres) - MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); -} - -/* - * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO - */ -u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) -{ - u16 buffstat; - - /* Returns the number of free locations in the buffer */ - buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); - - /* Number of slots are different in McBSP ports */ - return mcbsp->pdata->buffer_size - buffstat; -} - -/* - * 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) - */ -u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) -{ - u16 buffstat, threshold; - - /* 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; -} - -int omap_mcbsp_request(struct omap_mcbsp *mcbsp) -{ - void *reg_cache; - int err; - - reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); - if (!reg_cache) { - return -ENOMEM; - } - - spin_lock(&mcbsp->lock); - if (!mcbsp->free) { - dev_err(mcbsp->dev, "McBSP%d is currently in use\n", - mcbsp->id); - err = -EBUSY; - goto err_kfree; - } - - mcbsp->free = false; - mcbsp->reg_cache = reg_cache; - spin_unlock(&mcbsp->lock); - - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) - mcbsp->pdata->ops->request(mcbsp->id - 1); - - /* - * Make sure that transmitter, receiver and sample-rate generator are - * not running before activating IRQs. - */ - MCBSP_WRITE(mcbsp, SPCR1, 0); - MCBSP_WRITE(mcbsp, SPCR2, 0); - - 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; - } - - err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, - "McBSP RX", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); - goto err_free_irq; - } - } - - return 0; -err_free_irq: - free_irq(mcbsp->tx_irq, (void *)mcbsp); -err_clk_disable: - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id - 1); - - /* Disable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - - spin_lock(&mcbsp->lock); - mcbsp->free = true; - mcbsp->reg_cache = NULL; -err_kfree: - spin_unlock(&mcbsp->lock); - kfree(reg_cache); - - return err; -} - -void omap_mcbsp_free(struct omap_mcbsp *mcbsp) -{ - void *reg_cache; - - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id - 1); - - /* Disable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - - /* Disable interrupt requests */ - if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, 0); - - if (mcbsp->irq) { - free_irq(mcbsp->irq, (void *)mcbsp); - } else { - free_irq(mcbsp->rx_irq, (void *)mcbsp); - free_irq(mcbsp->tx_irq, (void *)mcbsp); - } - - reg_cache = mcbsp->reg_cache; - - /* - * 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. - */ - if (!mcbsp_omap1()) - omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); - - spin_lock(&mcbsp->lock); - if (mcbsp->free) - dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); - else - mcbsp->free = true; - mcbsp->reg_cache = NULL; - spin_unlock(&mcbsp->lock); - - kfree(reg_cache); -} - -/* - * 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. - */ -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) -{ - int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); - int rx = !tx; - int enable_srg = 0; - u16 w; - - if (mcbsp->st_data) - omap_st_start(mcbsp); - - /* 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); - - if (enable_srg) { - /* Start the sample generator */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); - } - - /* Enable transmitter and receiver */ - tx &= 1; - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | tx); - - rx &= 1; - w = MCBSP_READ_CACHE(mcbsp, SPCR1); - MCBSP_WRITE(mcbsp, SPCR1, w | rx); - - /* - * 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); - - if (enable_srg) { - /* Start frame sync */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); - } - - if (mcbsp->pdata->has_ccr) { - /* Release the transmitter and receiver */ - w = MCBSP_READ_CACHE(mcbsp, XCCR); - w &= ~(tx ? XDISABLE : 0); - MCBSP_WRITE(mcbsp, XCCR, w); - w = MCBSP_READ_CACHE(mcbsp, RCCR); - w &= ~(rx ? RDISABLE : 0); - MCBSP_WRITE(mcbsp, RCCR, w); - } - - /* Dump McBSP Regs */ - omap_mcbsp_dump_reg(mcbsp); -} - -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) -{ - int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); - int rx = !tx; - int idle; - u16 w; - - /* Reset transmitter */ - tx &= 1; - if (mcbsp->pdata->has_ccr) { - w = MCBSP_READ_CACHE(mcbsp, XCCR); - w |= (tx ? XDISABLE : 0); - MCBSP_WRITE(mcbsp, XCCR, w); - } - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); - - /* Reset receiver */ - rx &= 1; - if (mcbsp->pdata->has_ccr) { - w = MCBSP_READ_CACHE(mcbsp, RCCR); - w |= (rx ? RDISABLE : 0); - MCBSP_WRITE(mcbsp, RCCR, w); - } - w = MCBSP_READ_CACHE(mcbsp, SPCR1); - MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); - - idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | - MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); - - if (idle) { - /* Reset the sample rate generator */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); - } - - if (mcbsp->st_data) - omap_st_stop(mcbsp); -} - -int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) -{ - struct clk *fck_src; - const char *src; - int r; - - if (fck_src_id == MCBSP_CLKS_PAD_SRC) - src = "pad_fck"; - else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) - src = "prcm_fck"; - else - return -EINVAL; - - fck_src = clk_get(mcbsp->dev, src); - if (IS_ERR(fck_src)) { - dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); - return -EINVAL; - } - - pm_runtime_put_sync(mcbsp->dev); - - 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; - } - - pm_runtime_get_sync(mcbsp->dev); - - clk_put(fck_src); - - return 0; - -} - -#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 = kstrtoul(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 const char *dma_op_modes[] = { - "element", "threshold", -}; - -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, i = 0; - ssize_t len = 0; - const char * const *s; - - dma_op_mode = mcbsp->dma_op_mode; - - 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; -} - -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); - int i; - - i = sysfs_match_string(dma_op_modes, buf); - if (i < 0) - return i; - - spin_lock_irq(&mcbsp->lock); - if (!mcbsp->free) { - size = -EBUSY; - goto unlock; - } - mcbsp->dma_op_mode = i; - -unlock: - spin_unlock_irq(&mcbsp->lock); - - return size; -} - -static DEVICE_ATTR_RW(dma_op_mode); - -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 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_RW(st_taps); - -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, -}; - -static int omap_st_add(struct omap_mcbsp *mcbsp, struct resource *res) -{ - struct omap_mcbsp_st_data *st_data; - int err; - - st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); - if (!st_data) - return -ENOMEM; - - st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); - if (IS_ERR(st_data->mcbsp_iclk)) { - dev_warn(mcbsp->dev, - "Failed to get ick, sidetone might be broken\n"); - st_data->mcbsp_iclk = NULL; - } - - st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, - resource_size(res)); - if (!st_data->io_base_st) - return -ENOMEM; - - err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); - if (err) - return err; - - mcbsp->st_data = st_data; - return 0; -} - -/* - * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. - * 730 has only 2 McBSP, and both of them are MPU peripherals. - */ -int omap_mcbsp_init(struct platform_device *pdev) -{ - struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); - struct resource *res; - int ret = 0; - - spin_lock_init(&mcbsp->lock); - mcbsp->free = true; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!res) - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mcbsp->io_base)) - return PTR_ERR(mcbsp->io_base); - - mcbsp->phys_base = res->start; - mcbsp->reg_cache_size = resource_size(res); - - 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; - - /* - * 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; - } - } - - 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]; - - 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"; - } - - mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, - SNDRV_PCM_STREAM_PLAYBACK); - mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, - SNDRV_PCM_STREAM_CAPTURE); - - mcbsp->fclk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(mcbsp->fclk)) { - ret = PTR_ERR(mcbsp->fclk); - dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); - return ret; - } - - 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; - } - - 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; - } - } - - return 0; - -err_st: - if (mcbsp->pdata->buffer_size) - sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); -err_thres: - clk_put(mcbsp->fclk); - return ret; -} - -void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp) -{ - if (mcbsp->pdata->buffer_size) - sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); - - if (mcbsp->st_data) { - sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); - clk_put(mcbsp->st_data->mcbsp_iclk); - } -} diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/omap-mcbsp-priv.h similarity index 70% rename from sound/soc/omap/mcbsp.h rename to sound/soc/omap/omap-mcbsp-priv.h index 92472c6ef358..7865cda4bf0a 100644 --- a/sound/soc/omap/mcbsp.h +++ b/sound/soc/omap/omap-mcbsp-priv.h @@ -1,28 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * sound/soc/omap/mcbsp.h - * * OMAP Multi-Channel Buffered Serial Port * * Contact: Jarkko Nikula jarkko.nikula@bitmer.com * Peter Ujfalusi peter.ujfalusi@ti.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * */ -#ifndef __ASOC_MCBSP_H -#define __ASOC_MCBSP_H + +#ifndef __OMAP_MCBSP_PRIV_H__ +#define __OMAP_MCBSP_PRIV_H__ + +#include <linux/platform_data/asoc-ti-mcbsp.h>
#ifdef CONFIG_ARCH_OMAP1 #define mcbsp_omap1() 1 @@ -30,8 +17,6 @@ #define mcbsp_omap1() 0 #endif
-#include <sound/dmaengine_pcm.h> - /* McBSP register numbers. Register address offset = num * reg_step */ enum { /* Common registers */ @@ -85,15 +70,6 @@ enum { OMAP_MCBSP_REG_SSELCR, };
-/* OMAP3 sidetone control registers */ -#define OMAP_ST_REG_REV 0x00 -#define OMAP_ST_REG_SYSCONFIG 0x10 -#define OMAP_ST_REG_IRQSTATUS 0x18 -#define OMAP_ST_REG_IRQENABLE 0x1C -#define OMAP_ST_REG_SGAINCR 0x24 -#define OMAP_ST_REG_SFIRCR 0x28 -#define OMAP_ST_REG_SSELCR 0x2C - /************************** McBSP SPCR1 bit definitions ***********************/ #define RRST BIT(0) #define RRDY BIT(1) @@ -202,24 +178,6 @@ enum { #define SIDLEMODE(value) (((value) & 0x3) << 3) #define CLOCKACTIVITY(value) (((value) & 0x3) << 8)
-/********************** McBSP SSELCR bit definitions ***********************/ -#define SIDETONEEN BIT(10) - -/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ -#define ST_AUTOIDLE BIT(0) - -/********************** McBSP Sidetone SGAINCR bit definitions *************/ -#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ -#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ - -/********************** McBSP Sidetone SFIRCR bit definitions **************/ -#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ - -/********************** McBSP Sidetone SSELCR bit definitions **************/ -#define ST_SIDETONEEN BIT(0) -#define ST_COEFFWREN BIT(1) -#define ST_COEFFWRDONE BIT(2) - /********************** McBSP DMA operating modes **************************/ #define MCBSP_DMA_MODE_ELEMENT 0 #define MCBSP_DMA_MODE_THRESHOLD 1 @@ -278,16 +236,7 @@ struct omap_mcbsp_reg_cfg { u16 rccr; };
-struct omap_mcbsp_st_data { - void __iomem *io_base_st; - struct clk *mcbsp_iclk; - bool running; - bool enabled; - s16 taps[128]; /* Sidetone filter coefficients */ - int nr_taps; /* Number of filter coefficients in use */ - s16 ch0gain; - s16 ch1gain; -}; +struct omap_mcbsp_st_data;
struct omap_mcbsp { struct device *dev; @@ -330,29 +279,46 @@ struct omap_mcbsp { struct pm_qos_request pm_qos_req; };
-void omap_mcbsp_config(struct omap_mcbsp *mcbsp, - const struct omap_mcbsp_reg_cfg *config); -void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); -void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); -u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp); -u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp); -int omap_mcbsp_get_dma_op_mode(struct omap_mcbsp *mcbsp); -int omap_mcbsp_request(struct omap_mcbsp *mcbsp); -void omap_mcbsp_free(struct omap_mcbsp *mcbsp); -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream); -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream); - -/* McBSP functional clock source changing function */ -int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id); +static inline void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; + writew_relaxed((u16)val, addr); + } else { + ((u32 *)mcbsp->reg_cache)[reg] = val; + writel_relaxed(val, addr); + } +} + +static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, + bool from_cache) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + return !from_cache ? readw_relaxed(addr) : + ((u16 *)mcbsp->reg_cache)[reg]; + } else { + return !from_cache ? readl_relaxed(addr) : + ((u32 *)mcbsp->reg_cache)[reg]; + } +} + +#define MCBSP_READ(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) +#define MCBSP_WRITE(mcbsp, reg, val) \ + omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) +#define MCBSP_READ_CACHE(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) +
/* Sidetone specific API */ -int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain); -int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain); -int omap_st_enable(struct omap_mcbsp *mcbsp); -int omap_st_disable(struct omap_mcbsp *mcbsp); -int omap_st_is_enabled(struct omap_mcbsp *mcbsp); +int omap_mcbsp_st_init(struct platform_device *pdev); +void omap_mcbsp_st_cleanup(struct platform_device *pdev);
-int omap_mcbsp_init(struct platform_device *pdev); -void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp); +int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp); +int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp);
-#endif /* __ASOC_MCBSP_H */ +#endif /* __OMAP_MCBSP_PRIV_H__ */ diff --git a/sound/soc/omap/omap-mcbsp-st.c b/sound/soc/omap/omap-mcbsp-st.c new file mode 100644 index 000000000000..1a3fe854e856 --- /dev/null +++ b/sound/soc/omap/omap-mcbsp-st.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * McBSP Sidetone support + * + * Copyright (C) 2004 Nokia Corporation + * Author: Samuel Ortiz samuel.ortiz@nokia.com + * + * Contact: Jarkko Nikula jarkko.nikula@bitmer.com + * Peter Ujfalusi peter.ujfalusi@ti.com + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> + +#include "omap-mcbsp.h" +#include "omap-mcbsp-priv.h" + +/* OMAP3 sidetone control registers */ +#define OMAP_ST_REG_REV 0x00 +#define OMAP_ST_REG_SYSCONFIG 0x10 +#define OMAP_ST_REG_IRQSTATUS 0x18 +#define OMAP_ST_REG_IRQENABLE 0x1C +#define OMAP_ST_REG_SGAINCR 0x24 +#define OMAP_ST_REG_SFIRCR 0x28 +#define OMAP_ST_REG_SSELCR 0x2C + +/********************** McBSP SSELCR bit definitions ***********************/ +#define SIDETONEEN BIT(10) + +/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ +#define ST_AUTOIDLE BIT(0) + +/********************** McBSP Sidetone SGAINCR bit definitions *************/ +#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ +#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ + +/********************** McBSP Sidetone SFIRCR bit definitions **************/ +#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ + +/********************** McBSP Sidetone SSELCR bit definitions **************/ +#define ST_SIDETONEEN BIT(0) +#define ST_COEFFWREN BIT(1) +#define ST_COEFFWRDONE BIT(2) + +struct omap_mcbsp_st_data { + void __iomem *io_base_st; + struct clk *mcbsp_iclk; + bool running; + bool enabled; + s16 taps[128]; /* Sidetone filter coefficients */ + int nr_taps; /* Number of filter coefficients in use */ + s16 ch0gain; + s16 ch1gain; +}; + +static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + writel_relaxed(val, mcbsp->st_data->io_base_st + reg); +} + +static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) +{ + return readl_relaxed(mcbsp->st_data->io_base_st + reg); +} + +#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) + +static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + if (mcbsp->pdata->force_ick_on) + mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); + + /* Disable Sidetone clock auto-gating for normal operation */ + w = MCBSP_ST_READ(mcbsp, SYSCONFIG); + MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); + + /* 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_mcbsp_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)); + + /* Enable Sidetone clock auto-gating to reduce power consumption */ + w = MCBSP_ST_READ(mcbsp, SYSCONFIG); + MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); + + if (mcbsp->pdata->force_ick_on) + mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); +} + +static void omap_mcbsp_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_mcbsp_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)); +} + +static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, + s16 chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + 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_mcbsp_st_chgain(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, + s16 *chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + 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_mcbsp_st_enable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + st_data->enabled = 1; + omap_mcbsp_st_start(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return 0; +} + +static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + omap_mcbsp_st_stop(mcbsp); + st_data->enabled = 0; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + return st_data->enabled; +} + +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_RW(st_taps); + +static const struct attribute *sidetone_attrs[] = { + &dev_attr_st_taps.attr, + NULL, +}; + +static const struct attribute_group sidetone_attr_group = { + .attrs = (struct attribute **)sidetone_attrs, +}; + +int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->enabled && !st_data->running) { + omap_mcbsp_st_fir_write(mcbsp, st_data->taps); + omap_mcbsp_st_chgain(mcbsp); + + if (!mcbsp->free) { + omap_mcbsp_st_on(mcbsp); + st_data->running = 1; + } + } + + return 0; +} + +int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->running) { + if (!mcbsp->free) { + omap_mcbsp_st_off(mcbsp); + st_data->running = 0; + } + } + + return 0; +} + +int omap_mcbsp_st_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct omap_mcbsp_st_data *st_data; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); + if (!res) + return 0; + + st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); + if (!st_data) + return -ENOMEM; + + st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); + if (IS_ERR(st_data->mcbsp_iclk)) { + dev_warn(mcbsp->dev, + "Failed to get ick, sidetone might be broken\n"); + st_data->mcbsp_iclk = NULL; + } + + st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, + resource_size(res)); + if (!st_data->io_base_st) + return -ENOMEM; + + ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); + if (ret) + return ret; + + mcbsp->st_data = st_data; + + return 0; +} + +void omap_mcbsp_st_cleanup(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + + if (mcbsp->st_data) { + sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); + clk_put(mcbsp->st_data->mcbsp_iclk); + } +} + +static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int max = mc->max; + int min = mc->min; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = min; + uinfo->value.integer.max = max; + return 0; +} + +#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ +static int \ +omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ + struct snd_ctl_elem_value *uc) \ +{ \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ + struct soc_mixer_control *mc = \ + (struct soc_mixer_control *)kc->private_value; \ + int max = mc->max; \ + int min = mc->min; \ + int val = uc->value.integer.value[0]; \ + \ + if (val < min || val > max) \ + return -EINVAL; \ + \ + /* OMAP McBSP implementation uses index values 0..4 */ \ + return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \ +} \ + \ +static int \ +omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ + struct snd_ctl_elem_value *uc) \ +{ \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ + s16 chgain; \ + \ + if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \ + return -EAGAIN; \ + \ + uc->value.integer.value[0] = chgain; \ + return 0; \ +} + +OMAP_MCBSP_ST_CHANNEL_VOLUME(0) +OMAP_MCBSP_ST_CHANNEL_VOLUME(1) + +static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + u8 value = ucontrol->value.integer.value[0]; + + if (value == omap_mcbsp_st_is_enabled(mcbsp)) + return 0; + + if (value) + omap_mcbsp_st_enable(mcbsp); + else + omap_mcbsp_st_disable(mcbsp); + + return 1; +} + +static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp); + return 0; +} + +#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = omap_mcbsp_st_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.min = xmin, .max = xmax} } + +#define OMAP_MCBSP_ST_CONTROLS(port) \ +static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ +SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ + omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ +OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ + -32768, 32767, \ + omap_mcbsp_get_st_ch0_volume, \ + omap_mcbsp_set_st_ch0_volume), \ +OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ + -32768, 32767, \ + omap_mcbsp_get_st_ch1_volume, \ + omap_mcbsp_set_st_ch1_volume), \ +} + +OMAP_MCBSP_ST_CONTROLS(2); +OMAP_MCBSP_ST_CONTROLS(3); + +int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + if (!mcbsp->st_data) { + dev_warn(mcbsp->dev, "No sidetone data for port\n"); + return 0; + } + + switch (port_id) { + case 2: /* McBSP 2 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp2_st_controls, + ARRAY_SIZE(omap_mcbsp2_st_controls)); + case 3: /* McBSP 3 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp3_st_controls, + ARRAY_SIZE(omap_mcbsp3_st_controls)); + default: + dev_err(mcbsp->dev, "Port %d not supported\n", port_id); + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 69a6b8ad6d42..e48fad11a0cc 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -35,21 +35,12 @@ #include <sound/soc.h> #include <sound/dmaengine_pcm.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h> -#include "mcbsp.h" +#include "omap-mcbsp-priv.h" #include "omap-mcbsp.h" #include "sdma-pcm.h"
#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
-#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ - xhandler_get, xhandler_put) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = omap_mcbsp_st_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = (unsigned long) &(struct soc_mixer_control) \ - {.min = xmin, .max = xmax} } - enum { OMAP_MCBSP_WORD_8 = 0, OMAP_MCBSP_WORD_12, @@ -59,6 +50,702 @@ enum { OMAP_MCBSP_WORD_32, };
+static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) +{ + dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); + dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2)); + dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1)); + dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2)); + dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1)); + dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2)); + dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1)); + dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2)); + dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1)); + dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2)); + dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1)); + dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2)); + dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1)); + dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0)); + dev_dbg(mcbsp->dev, "***********************\n"); +} + +static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) +{ + struct clk *fck_src; + const char *src; + int r; + + if (fck_src_id == MCBSP_CLKS_PAD_SRC) + src = "pad_fck"; + else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) + src = "prcm_fck"; + else + return -EINVAL; + + fck_src = clk_get(mcbsp->dev, src); + if (IS_ERR(fck_src)) { + dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); + return -EINVAL; + } + + pm_runtime_put_sync(mcbsp->dev); + + 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; + } + + pm_runtime_get_sync(mcbsp->dev); + + clk_put(fck_src); + + return 0; +} + +static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + 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; +} + +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst_spcr2; + + irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); + dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); + + if (irqst_spcr2 & XSYNC_ERR) { + dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", + irqst_spcr2); + /* Writing zero to XSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst_spcr1; + + irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); + dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); + + if (irqst_spcr1 & RSYNC_ERR) { + dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", + irqst_spcr1); + /* Writing zero to RSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); + } + + 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(). + */ +static void omap_mcbsp_config(struct omap_mcbsp *mcbsp, + const struct omap_mcbsp_reg_cfg *config) +{ + dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", + mcbsp->id, mcbsp->phys_base); + + /* We write the given config */ + 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); + if (mcbsp->pdata->has_ccr) { + MCBSP_WRITE(mcbsp, XCCR, config->xccr); + MCBSP_WRITE(mcbsp, RCCR, config->rccr); + } + /* Enable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); + + /* Enable TX/RX sync error interrupts by default */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | + RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); +} + +/** + * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register + * @mcbsp: omap_mcbsp struct for the McBSP instance + * @stream: Stream direction (playback/capture) + * + * Returns the address of mcbsp data transmit register or data receive register + * to be used by DMA for transferring/receiving data + */ +static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, + unsigned int stream) +{ + int data_reg; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DXR1; + else + data_reg = OMAP_MCBSP_REG_DXR; + } else { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DRR1; + else + data_reg = OMAP_MCBSP_REG_DRR; + } + + return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; +} + +/* + * 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. + */ +static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (threshold && threshold <= mcbsp->max_tx_thres) + MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); +} + +/* + * 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. + */ +static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (threshold && threshold <= mcbsp->max_rx_thres) + MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); +} + +/* + * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO + */ +static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat; + + /* Returns the number of free locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); + + /* Number of slots are different in McBSP ports */ + return mcbsp->pdata->buffer_size - buffstat; +} + +/* + * 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) + */ +static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat, threshold; + + /* 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; +} + +static int omap_mcbsp_request(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + int err; + + reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); + if (!reg_cache) + return -ENOMEM; + + spin_lock(&mcbsp->lock); + if (!mcbsp->free) { + dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id); + err = -EBUSY; + goto err_kfree; + } + + mcbsp->free = false; + mcbsp->reg_cache = reg_cache; + spin_unlock(&mcbsp->lock); + + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) + mcbsp->pdata->ops->request(mcbsp->id - 1); + + /* + * Make sure that transmitter, receiver and sample-rate generator are + * not running before activating IRQs. + */ + MCBSP_WRITE(mcbsp, SPCR1, 0); + MCBSP_WRITE(mcbsp, SPCR2, 0); + + 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; + } + + err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP RX", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); + goto err_free_irq; + } + } + + return 0; +err_free_irq: + free_irq(mcbsp->tx_irq, (void *)mcbsp); +err_clk_disable: + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + spin_lock(&mcbsp->lock); + mcbsp->free = true; + mcbsp->reg_cache = NULL; +err_kfree: + spin_unlock(&mcbsp->lock); + kfree(reg_cache); + + return err; +} + +static void omap_mcbsp_free(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + /* Disable interrupt requests */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, 0); + + if (mcbsp->irq) { + free_irq(mcbsp->irq, (void *)mcbsp); + } else { + free_irq(mcbsp->rx_irq, (void *)mcbsp); + free_irq(mcbsp->tx_irq, (void *)mcbsp); + } + + reg_cache = mcbsp->reg_cache; + + /* + * 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. + */ + if (!mcbsp_omap1()) + omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); + + spin_lock(&mcbsp->lock); + if (mcbsp->free) + dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); + else + mcbsp->free = true; + mcbsp->reg_cache = NULL; + spin_unlock(&mcbsp->lock); + + kfree(reg_cache); +} + +/* + * 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. + */ +static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) +{ + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; + int enable_srg = 0; + u16 w; + + if (mcbsp->st_data) + omap_mcbsp_st_start(mcbsp); + + /* 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); + + if (enable_srg) { + /* Start the sample generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); + } + + /* Enable transmitter and receiver */ + tx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | tx); + + rx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w | rx); + + /* + * 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); + + if (enable_srg) { + /* Start frame sync */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); + } + + if (mcbsp->pdata->has_ccr) { + /* Release the transmitter and receiver */ + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w &= ~(tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w &= ~(rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + + /* Dump McBSP Regs */ + omap_mcbsp_dump_reg(mcbsp); +} + +static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) +{ + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; + int idle; + u16 w; + + /* Reset transmitter */ + tx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w |= (tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); + + /* Reset receiver */ + rx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w |= (rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); + + idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (idle) { + /* Reset the sample rate generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); + } + + if (mcbsp->st_data) + omap_mcbsp_st_stop(mcbsp); +} + +#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 = kstrtoul(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 const char * const dma_op_modes[] = { + "element", "threshold", +}; + +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, i = 0; + ssize_t len = 0; + const char * const *s; + + dma_op_mode = mcbsp->dma_op_mode; + + 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; +} + +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); + int i; + + i = sysfs_match_string(dma_op_modes, buf); + if (i < 0) + return i; + + spin_lock_irq(&mcbsp->lock); + if (!mcbsp->free) { + size = -EBUSY; + goto unlock; + } + mcbsp->dma_op_mode = i; + +unlock: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR_RW(dma_op_mode); + +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, +}; + +/* + * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. + * 730 has only 2 McBSP, and both of them are MPU peripherals. + */ +static int omap_mcbsp_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct resource *res; + int ret = 0; + + spin_lock_init(&mcbsp->lock); + mcbsp->free = true; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mcbsp->io_base)) + return PTR_ERR(mcbsp->io_base); + + mcbsp->phys_base = res->start; + mcbsp->reg_cache_size = resource_size(res); + + 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; + + /* + * 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; + } + } + + 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]; + + 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"; + } + + mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_PLAYBACK); + mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_CAPTURE); + + mcbsp->fclk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(mcbsp->fclk)) { + ret = PTR_ERR(mcbsp->fclk); + dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); + return ret; + } + + 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; + } + + ret = omap_mcbsp_st_init(pdev); + if (ret) + goto err_st; + + return 0; + +err_st: + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); +err_thres: + clk_put(mcbsp->fclk); + return ret; +} + /* * Stream DMA parameters. DMA request line and port address are set runtime * since they are different between OMAP1 and later OMAPs @@ -656,132 +1343,6 @@ static const struct snd_soc_component_driver omap_mcbsp_component = { .name = "omap-mcbsp", };
-static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int max = mc->max; - int min = mc->min; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = min; - uinfo->value.integer.max = max; - return 0; -} - -#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ -static int \ -omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ - struct snd_ctl_elem_value *uc) \ -{ \ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ - struct soc_mixer_control *mc = \ - (struct soc_mixer_control *)kc->private_value; \ - int max = mc->max; \ - int min = mc->min; \ - int val = uc->value.integer.value[0]; \ - \ - if (val < min || val > max) \ - return -EINVAL; \ - \ - /* OMAP McBSP implementation uses index values 0..4 */ \ - return omap_st_set_chgain(mcbsp, channel, val); \ -} \ - \ -static int \ -omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ - struct snd_ctl_elem_value *uc) \ -{ \ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ - s16 chgain; \ - \ - if (omap_st_get_chgain(mcbsp, channel, &chgain)) \ - return -EAGAIN; \ - \ - uc->value.integer.value[0] = chgain; \ - return 0; \ -} - -OMAP_MCBSP_ST_CHANNEL_VOLUME(0) -OMAP_MCBSP_ST_CHANNEL_VOLUME(1) - -static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - u8 value = ucontrol->value.integer.value[0]; - - if (value == omap_st_is_enabled(mcbsp)) - return 0; - - if (value) - omap_st_enable(mcbsp); - else - omap_st_disable(mcbsp); - - return 1; -} - -static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - ucontrol->value.integer.value[0] = omap_st_is_enabled(mcbsp); - return 0; -} - -#define OMAP_MCBSP_ST_CONTROLS(port) \ -static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ -SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ - omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ -OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ - -32768, 32767, \ - omap_mcbsp_get_st_ch0_volume, \ - omap_mcbsp_set_st_ch0_volume), \ -OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ - -32768, 32767, \ - omap_mcbsp_get_st_ch1_volume, \ - omap_mcbsp_set_st_ch1_volume), \ -} - -OMAP_MCBSP_ST_CONTROLS(2); -OMAP_MCBSP_ST_CONTROLS(3); - -int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) -{ - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - if (!mcbsp->st_data) { - dev_warn(mcbsp->dev, "No sidetone data for port\n"); - return 0; - } - - switch (port_id) { - case 2: /* McBSP 2 */ - return snd_soc_add_dai_controls(cpu_dai, - omap_mcbsp2_st_controls, - ARRAY_SIZE(omap_mcbsp2_st_controls)); - case 3: /* McBSP 3 */ - return snd_soc_add_dai_controls(cpu_dai, - omap_mcbsp3_st_controls, - ARRAY_SIZE(omap_mcbsp3_st_controls)); - default: - dev_err(mcbsp->dev, "Port %d not supported\n", port_id); - break; - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); - static struct omap_mcbsp_platform_data omap2420_pdata = { .reg_step = 4, .reg_size = 2, @@ -893,7 +1454,10 @@ static int asoc_mcbsp_remove(struct platform_device *pdev) if (pm_qos_request_active(&mcbsp->pm_qos_req)) pm_qos_remove_request(&mcbsp->pm_qos_req);
- omap_mcbsp_cleanup(mcbsp); + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); + + omap_mcbsp_st_cleanup(pdev);
clk_put(mcbsp->fclk);
diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index 2e3369c27be3..7911d24898c9 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h @@ -22,8 +22,10 @@ * */
-#ifndef __OMAP_I2S_H__ -#define __OMAP_I2S_H__ +#ifndef __OMAP_MCBSP_H__ +#define __OMAP_MCBSP_H__ + +#include <sound/dmaengine_pcm.h>
/* Source clocks for McBSP sample rate generator */ enum omap_mcbsp_clksrg_clk { @@ -41,4 +43,4 @@ enum omap_mcbsp_div {
int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id);
-#endif +#endif /* __OMAP_MCBSP_H__ */
participants (1)
-
Peter Ujfalusi