12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f2055e14SPeter Ujfalusi /*
3f2055e14SPeter Ujfalusi * omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port
4f2055e14SPeter Ujfalusi *
5f2055e14SPeter Ujfalusi * Copyright (C) 2008 Nokia Corporation
6f2055e14SPeter Ujfalusi *
7f2055e14SPeter Ujfalusi * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com>
8f2055e14SPeter Ujfalusi * Peter Ujfalusi <peter.ujfalusi@ti.com>
9f2055e14SPeter Ujfalusi */
10f2055e14SPeter Ujfalusi
11f2055e14SPeter Ujfalusi #include <linux/init.h>
12f2055e14SPeter Ujfalusi #include <linux/module.h>
13f2055e14SPeter Ujfalusi #include <linux/device.h>
14f2055e14SPeter Ujfalusi #include <linux/pm_runtime.h>
15f2055e14SPeter Ujfalusi #include <linux/of.h>
16f2055e14SPeter Ujfalusi #include <linux/of_device.h>
17f2055e14SPeter Ujfalusi #include <sound/core.h>
18f2055e14SPeter Ujfalusi #include <sound/pcm.h>
19f2055e14SPeter Ujfalusi #include <sound/pcm_params.h>
20f2055e14SPeter Ujfalusi #include <sound/initval.h>
21f2055e14SPeter Ujfalusi #include <sound/soc.h>
22f2055e14SPeter Ujfalusi #include <sound/dmaengine_pcm.h>
23f2055e14SPeter Ujfalusi
24f2055e14SPeter Ujfalusi #include "omap-mcbsp-priv.h"
25f2055e14SPeter Ujfalusi #include "omap-mcbsp.h"
26f2055e14SPeter Ujfalusi #include "sdma-pcm.h"
27f2055e14SPeter Ujfalusi
28f2055e14SPeter Ujfalusi #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
29f2055e14SPeter Ujfalusi
30f2055e14SPeter Ujfalusi enum {
31f2055e14SPeter Ujfalusi OMAP_MCBSP_WORD_8 = 0,
32f2055e14SPeter Ujfalusi OMAP_MCBSP_WORD_12,
33f2055e14SPeter Ujfalusi OMAP_MCBSP_WORD_16,
34f2055e14SPeter Ujfalusi OMAP_MCBSP_WORD_20,
35f2055e14SPeter Ujfalusi OMAP_MCBSP_WORD_24,
36f2055e14SPeter Ujfalusi OMAP_MCBSP_WORD_32,
37f2055e14SPeter Ujfalusi };
38f2055e14SPeter Ujfalusi
omap_mcbsp_dump_reg(struct omap_mcbsp * mcbsp)39f2055e14SPeter Ujfalusi static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp)
40f2055e14SPeter Ujfalusi {
41f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
42f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2));
43f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1));
44f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2));
45f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1));
46f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2));
47f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1));
48f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2));
49f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1));
50f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2));
51f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1));
52f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2));
53f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1));
54f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0));
55f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "***********************\n");
56f2055e14SPeter Ujfalusi }
57f2055e14SPeter Ujfalusi
omap2_mcbsp_set_clks_src(struct omap_mcbsp * mcbsp,u8 fck_src_id)58f2055e14SPeter Ujfalusi static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id)
59f2055e14SPeter Ujfalusi {
60f2055e14SPeter Ujfalusi struct clk *fck_src;
61f2055e14SPeter Ujfalusi const char *src;
62f2055e14SPeter Ujfalusi int r;
63f2055e14SPeter Ujfalusi
64f2055e14SPeter Ujfalusi if (fck_src_id == MCBSP_CLKS_PAD_SRC)
65f2055e14SPeter Ujfalusi src = "pad_fck";
66f2055e14SPeter Ujfalusi else if (fck_src_id == MCBSP_CLKS_PRCM_SRC)
67f2055e14SPeter Ujfalusi src = "prcm_fck";
68f2055e14SPeter Ujfalusi else
69f2055e14SPeter Ujfalusi return -EINVAL;
70f2055e14SPeter Ujfalusi
71f2055e14SPeter Ujfalusi fck_src = clk_get(mcbsp->dev, src);
72f2055e14SPeter Ujfalusi if (IS_ERR(fck_src)) {
7382e7c8b9SAndreas Kemnade dev_info(mcbsp->dev, "CLKS: could not clk_get() %s\n", src);
7482e7c8b9SAndreas Kemnade return 0;
75f2055e14SPeter Ujfalusi }
76f2055e14SPeter Ujfalusi
77*dc0cbcbaSTony Lindgren if (mcbsp->active)
78f2055e14SPeter Ujfalusi pm_runtime_put_sync(mcbsp->dev);
79f2055e14SPeter Ujfalusi
80f2055e14SPeter Ujfalusi r = clk_set_parent(mcbsp->fclk, fck_src);
81c553d290SDinghao Liu if (r)
82f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n",
83f2055e14SPeter Ujfalusi src);
84f2055e14SPeter Ujfalusi
85*dc0cbcbaSTony Lindgren if (mcbsp->active)
86f2055e14SPeter Ujfalusi pm_runtime_get_sync(mcbsp->dev);
87f2055e14SPeter Ujfalusi
88f2055e14SPeter Ujfalusi clk_put(fck_src);
89f2055e14SPeter Ujfalusi
90c553d290SDinghao Liu return r;
91f2055e14SPeter Ujfalusi }
92f2055e14SPeter Ujfalusi
omap_mcbsp_irq_handler(int irq,void * data)93f2055e14SPeter Ujfalusi static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data)
94f2055e14SPeter Ujfalusi {
95f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = data;
96f2055e14SPeter Ujfalusi u16 irqst;
97f2055e14SPeter Ujfalusi
98f2055e14SPeter Ujfalusi irqst = MCBSP_READ(mcbsp, IRQST);
99f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst);
100f2055e14SPeter Ujfalusi
101f2055e14SPeter Ujfalusi if (irqst & RSYNCERREN)
102f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "RX Frame Sync Error!\n");
103f2055e14SPeter Ujfalusi if (irqst & RFSREN)
104f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "RX Frame Sync\n");
105f2055e14SPeter Ujfalusi if (irqst & REOFEN)
106f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "RX End Of Frame\n");
107f2055e14SPeter Ujfalusi if (irqst & RRDYEN)
108f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n");
109f2055e14SPeter Ujfalusi if (irqst & RUNDFLEN)
110f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "RX Buffer Underflow!\n");
111f2055e14SPeter Ujfalusi if (irqst & ROVFLEN)
112f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "RX Buffer Overflow!\n");
113f2055e14SPeter Ujfalusi
114f2055e14SPeter Ujfalusi if (irqst & XSYNCERREN)
115f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "TX Frame Sync Error!\n");
116f2055e14SPeter Ujfalusi if (irqst & XFSXEN)
117f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "TX Frame Sync\n");
118f2055e14SPeter Ujfalusi if (irqst & XEOFEN)
119f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "TX End Of Frame\n");
120f2055e14SPeter Ujfalusi if (irqst & XRDYEN)
121f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n");
122f2055e14SPeter Ujfalusi if (irqst & XUNDFLEN)
123f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "TX Buffer Underflow!\n");
124f2055e14SPeter Ujfalusi if (irqst & XOVFLEN)
125f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "TX Buffer Overflow!\n");
126f2055e14SPeter Ujfalusi if (irqst & XEMPTYEOFEN)
127f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n");
128f2055e14SPeter Ujfalusi
129f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, IRQST, irqst);
130f2055e14SPeter Ujfalusi
131f2055e14SPeter Ujfalusi return IRQ_HANDLED;
132f2055e14SPeter Ujfalusi }
133f2055e14SPeter Ujfalusi
omap_mcbsp_tx_irq_handler(int irq,void * data)134f2055e14SPeter Ujfalusi static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data)
135f2055e14SPeter Ujfalusi {
136f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = data;
137f2055e14SPeter Ujfalusi u16 irqst_spcr2;
138f2055e14SPeter Ujfalusi
139f2055e14SPeter Ujfalusi irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2);
140f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
141f2055e14SPeter Ujfalusi
142f2055e14SPeter Ujfalusi if (irqst_spcr2 & XSYNC_ERR) {
143f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n",
144f2055e14SPeter Ujfalusi irqst_spcr2);
145f2055e14SPeter Ujfalusi /* Writing zero to XSYNC_ERR clears the IRQ */
146f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2));
147f2055e14SPeter Ujfalusi }
148f2055e14SPeter Ujfalusi
149f2055e14SPeter Ujfalusi return IRQ_HANDLED;
150f2055e14SPeter Ujfalusi }
151f2055e14SPeter Ujfalusi
omap_mcbsp_rx_irq_handler(int irq,void * data)152f2055e14SPeter Ujfalusi static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data)
153f2055e14SPeter Ujfalusi {
154f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = data;
155f2055e14SPeter Ujfalusi u16 irqst_spcr1;
156f2055e14SPeter Ujfalusi
157f2055e14SPeter Ujfalusi irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1);
158f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
159f2055e14SPeter Ujfalusi
160f2055e14SPeter Ujfalusi if (irqst_spcr1 & RSYNC_ERR) {
161f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n",
162f2055e14SPeter Ujfalusi irqst_spcr1);
163f2055e14SPeter Ujfalusi /* Writing zero to RSYNC_ERR clears the IRQ */
164f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1));
165f2055e14SPeter Ujfalusi }
166f2055e14SPeter Ujfalusi
167f2055e14SPeter Ujfalusi return IRQ_HANDLED;
168f2055e14SPeter Ujfalusi }
169f2055e14SPeter Ujfalusi
170f2055e14SPeter Ujfalusi /*
171f2055e14SPeter Ujfalusi * omap_mcbsp_config simply write a config to the
172f2055e14SPeter Ujfalusi * appropriate McBSP.
173f2055e14SPeter Ujfalusi * You either call this function or set the McBSP registers
174f2055e14SPeter Ujfalusi * by yourself before calling omap_mcbsp_start().
175f2055e14SPeter Ujfalusi */
omap_mcbsp_config(struct omap_mcbsp * mcbsp,const struct omap_mcbsp_reg_cfg * config)176f2055e14SPeter Ujfalusi static void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
177f2055e14SPeter Ujfalusi const struct omap_mcbsp_reg_cfg *config)
178f2055e14SPeter Ujfalusi {
179f2055e14SPeter Ujfalusi dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
180f2055e14SPeter Ujfalusi mcbsp->id, mcbsp->phys_base);
181f2055e14SPeter Ujfalusi
182f2055e14SPeter Ujfalusi /* We write the given config */
183f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
184f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
185f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
186f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
187f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
188f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
189f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
190f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
191f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
192f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
193f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
194f2055e14SPeter Ujfalusi if (mcbsp->pdata->has_ccr) {
195f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, XCCR, config->xccr);
196f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, RCCR, config->rccr);
197f2055e14SPeter Ujfalusi }
198f2055e14SPeter Ujfalusi /* Enable wakeup behavior */
199f2055e14SPeter Ujfalusi if (mcbsp->pdata->has_wakeup)
200f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
201f2055e14SPeter Ujfalusi
202f2055e14SPeter Ujfalusi /* Enable TX/RX sync error interrupts by default */
203f2055e14SPeter Ujfalusi if (mcbsp->irq)
204f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN |
205f2055e14SPeter Ujfalusi RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN);
206f2055e14SPeter Ujfalusi }
207f2055e14SPeter Ujfalusi
208f2055e14SPeter Ujfalusi /**
209f2055e14SPeter Ujfalusi * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register
210f2055e14SPeter Ujfalusi * @mcbsp: omap_mcbsp struct for the McBSP instance
211f2055e14SPeter Ujfalusi * @stream: Stream direction (playback/capture)
212f2055e14SPeter Ujfalusi *
213f2055e14SPeter Ujfalusi * Returns the address of mcbsp data transmit register or data receive register
214f2055e14SPeter Ujfalusi * to be used by DMA for transferring/receiving data
215f2055e14SPeter Ujfalusi */
omap_mcbsp_dma_reg_params(struct omap_mcbsp * mcbsp,unsigned int stream)216f2055e14SPeter Ujfalusi static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp,
217f2055e14SPeter Ujfalusi unsigned int stream)
218f2055e14SPeter Ujfalusi {
219f2055e14SPeter Ujfalusi int data_reg;
220f2055e14SPeter Ujfalusi
221f2055e14SPeter Ujfalusi if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
222f2055e14SPeter Ujfalusi if (mcbsp->pdata->reg_size == 2)
223f2055e14SPeter Ujfalusi data_reg = OMAP_MCBSP_REG_DXR1;
224f2055e14SPeter Ujfalusi else
225f2055e14SPeter Ujfalusi data_reg = OMAP_MCBSP_REG_DXR;
226f2055e14SPeter Ujfalusi } else {
227f2055e14SPeter Ujfalusi if (mcbsp->pdata->reg_size == 2)
228f2055e14SPeter Ujfalusi data_reg = OMAP_MCBSP_REG_DRR1;
229f2055e14SPeter Ujfalusi else
230f2055e14SPeter Ujfalusi data_reg = OMAP_MCBSP_REG_DRR;
231f2055e14SPeter Ujfalusi }
232f2055e14SPeter Ujfalusi
233f2055e14SPeter Ujfalusi return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step;
234f2055e14SPeter Ujfalusi }
235f2055e14SPeter Ujfalusi
236f2055e14SPeter Ujfalusi /*
237f2055e14SPeter Ujfalusi * omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
238f2055e14SPeter Ujfalusi * The threshold parameter is 1 based, and it is converted (threshold - 1)
239f2055e14SPeter Ujfalusi * for the THRSH2 register.
240f2055e14SPeter Ujfalusi */
omap_mcbsp_set_tx_threshold(struct omap_mcbsp * mcbsp,u16 threshold)241f2055e14SPeter Ujfalusi static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
242f2055e14SPeter Ujfalusi {
243f2055e14SPeter Ujfalusi if (threshold && threshold <= mcbsp->max_tx_thres)
244f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
245f2055e14SPeter Ujfalusi }
246f2055e14SPeter Ujfalusi
247f2055e14SPeter Ujfalusi /*
248f2055e14SPeter Ujfalusi * omap_mcbsp_set_rx_threshold configures the receive threshold in words.
249f2055e14SPeter Ujfalusi * The threshold parameter is 1 based, and it is converted (threshold - 1)
250f2055e14SPeter Ujfalusi * for the THRSH1 register.
251f2055e14SPeter Ujfalusi */
omap_mcbsp_set_rx_threshold(struct omap_mcbsp * mcbsp,u16 threshold)252f2055e14SPeter Ujfalusi static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold)
253f2055e14SPeter Ujfalusi {
254f2055e14SPeter Ujfalusi if (threshold && threshold <= mcbsp->max_rx_thres)
255f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
256f2055e14SPeter Ujfalusi }
257f2055e14SPeter Ujfalusi
258f2055e14SPeter Ujfalusi /*
259f2055e14SPeter Ujfalusi * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
260f2055e14SPeter Ujfalusi */
omap_mcbsp_get_tx_delay(struct omap_mcbsp * mcbsp)261f2055e14SPeter Ujfalusi static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp)
262f2055e14SPeter Ujfalusi {
263f2055e14SPeter Ujfalusi u16 buffstat;
264f2055e14SPeter Ujfalusi
265f2055e14SPeter Ujfalusi /* Returns the number of free locations in the buffer */
266f2055e14SPeter Ujfalusi buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
267f2055e14SPeter Ujfalusi
268f2055e14SPeter Ujfalusi /* Number of slots are different in McBSP ports */
269f2055e14SPeter Ujfalusi return mcbsp->pdata->buffer_size - buffstat;
270f2055e14SPeter Ujfalusi }
271f2055e14SPeter Ujfalusi
272f2055e14SPeter Ujfalusi /*
273f2055e14SPeter Ujfalusi * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
274f2055e14SPeter Ujfalusi * to reach the threshold value (when the DMA will be triggered to read it)
275f2055e14SPeter Ujfalusi */
omap_mcbsp_get_rx_delay(struct omap_mcbsp * mcbsp)276f2055e14SPeter Ujfalusi static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp)
277f2055e14SPeter Ujfalusi {
278f2055e14SPeter Ujfalusi u16 buffstat, threshold;
279f2055e14SPeter Ujfalusi
280f2055e14SPeter Ujfalusi /* Returns the number of used locations in the buffer */
281f2055e14SPeter Ujfalusi buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
282f2055e14SPeter Ujfalusi /* RX threshold */
283f2055e14SPeter Ujfalusi threshold = MCBSP_READ(mcbsp, THRSH1);
284f2055e14SPeter Ujfalusi
285f2055e14SPeter Ujfalusi /* Return the number of location till we reach the threshold limit */
286f2055e14SPeter Ujfalusi if (threshold <= buffstat)
287f2055e14SPeter Ujfalusi return 0;
288f2055e14SPeter Ujfalusi else
289f2055e14SPeter Ujfalusi return threshold - buffstat;
290f2055e14SPeter Ujfalusi }
291f2055e14SPeter Ujfalusi
omap_mcbsp_request(struct omap_mcbsp * mcbsp)292f2055e14SPeter Ujfalusi static int omap_mcbsp_request(struct omap_mcbsp *mcbsp)
293f2055e14SPeter Ujfalusi {
294f2055e14SPeter Ujfalusi void *reg_cache;
295f2055e14SPeter Ujfalusi int err;
296f2055e14SPeter Ujfalusi
297f2055e14SPeter Ujfalusi reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL);
298f2055e14SPeter Ujfalusi if (!reg_cache)
299f2055e14SPeter Ujfalusi return -ENOMEM;
300f2055e14SPeter Ujfalusi
301f2055e14SPeter Ujfalusi spin_lock(&mcbsp->lock);
302f2055e14SPeter Ujfalusi if (!mcbsp->free) {
303f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id);
304f2055e14SPeter Ujfalusi err = -EBUSY;
305f2055e14SPeter Ujfalusi goto err_kfree;
306f2055e14SPeter Ujfalusi }
307f2055e14SPeter Ujfalusi
308f2055e14SPeter Ujfalusi mcbsp->free = false;
309f2055e14SPeter Ujfalusi mcbsp->reg_cache = reg_cache;
310f2055e14SPeter Ujfalusi spin_unlock(&mcbsp->lock);
311f2055e14SPeter Ujfalusi
312f2055e14SPeter Ujfalusi if(mcbsp->pdata->ops && mcbsp->pdata->ops->request)
313f2055e14SPeter Ujfalusi mcbsp->pdata->ops->request(mcbsp->id - 1);
314f2055e14SPeter Ujfalusi
315f2055e14SPeter Ujfalusi /*
316f2055e14SPeter Ujfalusi * Make sure that transmitter, receiver and sample-rate generator are
317f2055e14SPeter Ujfalusi * not running before activating IRQs.
318f2055e14SPeter Ujfalusi */
319f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR1, 0);
320f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR2, 0);
321f2055e14SPeter Ujfalusi
322f2055e14SPeter Ujfalusi if (mcbsp->irq) {
323f2055e14SPeter Ujfalusi err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0,
324f2055e14SPeter Ujfalusi "McBSP", (void *)mcbsp);
325f2055e14SPeter Ujfalusi if (err != 0) {
326f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "Unable to request IRQ\n");
327f2055e14SPeter Ujfalusi goto err_clk_disable;
328f2055e14SPeter Ujfalusi }
329f2055e14SPeter Ujfalusi } else {
330f2055e14SPeter Ujfalusi err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0,
331f2055e14SPeter Ujfalusi "McBSP TX", (void *)mcbsp);
332f2055e14SPeter Ujfalusi if (err != 0) {
333f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "Unable to request TX IRQ\n");
334f2055e14SPeter Ujfalusi goto err_clk_disable;
335f2055e14SPeter Ujfalusi }
336f2055e14SPeter Ujfalusi
337f2055e14SPeter Ujfalusi err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0,
338f2055e14SPeter Ujfalusi "McBSP RX", (void *)mcbsp);
339f2055e14SPeter Ujfalusi if (err != 0) {
340f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "Unable to request RX IRQ\n");
341f2055e14SPeter Ujfalusi goto err_free_irq;
342f2055e14SPeter Ujfalusi }
343f2055e14SPeter Ujfalusi }
344f2055e14SPeter Ujfalusi
345f2055e14SPeter Ujfalusi return 0;
346f2055e14SPeter Ujfalusi err_free_irq:
347f2055e14SPeter Ujfalusi free_irq(mcbsp->tx_irq, (void *)mcbsp);
348f2055e14SPeter Ujfalusi err_clk_disable:
349f2055e14SPeter Ujfalusi if(mcbsp->pdata->ops && mcbsp->pdata->ops->free)
350f2055e14SPeter Ujfalusi mcbsp->pdata->ops->free(mcbsp->id - 1);
351f2055e14SPeter Ujfalusi
352f2055e14SPeter Ujfalusi /* Disable wakeup behavior */
353f2055e14SPeter Ujfalusi if (mcbsp->pdata->has_wakeup)
354f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
355f2055e14SPeter Ujfalusi
356f2055e14SPeter Ujfalusi spin_lock(&mcbsp->lock);
357f2055e14SPeter Ujfalusi mcbsp->free = true;
358f2055e14SPeter Ujfalusi mcbsp->reg_cache = NULL;
359f2055e14SPeter Ujfalusi err_kfree:
360f2055e14SPeter Ujfalusi spin_unlock(&mcbsp->lock);
361f2055e14SPeter Ujfalusi kfree(reg_cache);
362f2055e14SPeter Ujfalusi
363f2055e14SPeter Ujfalusi return err;
364f2055e14SPeter Ujfalusi }
365f2055e14SPeter Ujfalusi
omap_mcbsp_free(struct omap_mcbsp * mcbsp)366f2055e14SPeter Ujfalusi static void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
367f2055e14SPeter Ujfalusi {
368f2055e14SPeter Ujfalusi void *reg_cache;
369f2055e14SPeter Ujfalusi
370f2055e14SPeter Ujfalusi if(mcbsp->pdata->ops && mcbsp->pdata->ops->free)
371f2055e14SPeter Ujfalusi mcbsp->pdata->ops->free(mcbsp->id - 1);
372f2055e14SPeter Ujfalusi
373f2055e14SPeter Ujfalusi /* Disable wakeup behavior */
374f2055e14SPeter Ujfalusi if (mcbsp->pdata->has_wakeup)
375f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
376f2055e14SPeter Ujfalusi
377f2055e14SPeter Ujfalusi /* Disable interrupt requests */
3783d37b27fSPierre-Louis Bossart if (mcbsp->irq) {
379f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, IRQEN, 0);
380f2055e14SPeter Ujfalusi
381f2055e14SPeter Ujfalusi free_irq(mcbsp->irq, (void *)mcbsp);
382f2055e14SPeter Ujfalusi } else {
383f2055e14SPeter Ujfalusi free_irq(mcbsp->rx_irq, (void *)mcbsp);
384f2055e14SPeter Ujfalusi free_irq(mcbsp->tx_irq, (void *)mcbsp);
385f2055e14SPeter Ujfalusi }
386f2055e14SPeter Ujfalusi
387f2055e14SPeter Ujfalusi reg_cache = mcbsp->reg_cache;
388f2055e14SPeter Ujfalusi
389f2055e14SPeter Ujfalusi /*
390f2055e14SPeter Ujfalusi * Select CLKS source from internal source unconditionally before
391f2055e14SPeter Ujfalusi * marking the McBSP port as free.
392f2055e14SPeter Ujfalusi * If the external clock source via MCBSP_CLKS pin has been selected the
393f2055e14SPeter Ujfalusi * system will refuse to enter idle if the CLKS pin source is not reset
394f2055e14SPeter Ujfalusi * back to internal source.
395f2055e14SPeter Ujfalusi */
396f2055e14SPeter Ujfalusi if (!mcbsp_omap1())
397f2055e14SPeter Ujfalusi omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC);
398f2055e14SPeter Ujfalusi
399f2055e14SPeter Ujfalusi spin_lock(&mcbsp->lock);
400f2055e14SPeter Ujfalusi if (mcbsp->free)
401f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
402f2055e14SPeter Ujfalusi else
403f2055e14SPeter Ujfalusi mcbsp->free = true;
404f2055e14SPeter Ujfalusi mcbsp->reg_cache = NULL;
405f2055e14SPeter Ujfalusi spin_unlock(&mcbsp->lock);
406f2055e14SPeter Ujfalusi
407f2055e14SPeter Ujfalusi kfree(reg_cache);
408f2055e14SPeter Ujfalusi }
409f2055e14SPeter Ujfalusi
410f2055e14SPeter Ujfalusi /*
411f2055e14SPeter Ujfalusi * Here we start the McBSP, by enabling transmitter, receiver or both.
412f2055e14SPeter Ujfalusi * If no transmitter or receiver is active prior calling, then sample-rate
413f2055e14SPeter Ujfalusi * generator and frame sync are started.
414f2055e14SPeter Ujfalusi */
omap_mcbsp_start(struct omap_mcbsp * mcbsp,int stream)415f2055e14SPeter Ujfalusi static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream)
416f2055e14SPeter Ujfalusi {
417f2055e14SPeter Ujfalusi int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
418f2055e14SPeter Ujfalusi int rx = !tx;
419f2055e14SPeter Ujfalusi int enable_srg = 0;
420f2055e14SPeter Ujfalusi u16 w;
421f2055e14SPeter Ujfalusi
422f2055e14SPeter Ujfalusi if (mcbsp->st_data)
423f2055e14SPeter Ujfalusi omap_mcbsp_st_start(mcbsp);
424f2055e14SPeter Ujfalusi
425f2055e14SPeter Ujfalusi /* Only enable SRG, if McBSP is master */
426f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, PCR0);
427f2055e14SPeter Ujfalusi if (w & (FSXM | FSRM | CLKXM | CLKRM))
428f2055e14SPeter Ujfalusi enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
429f2055e14SPeter Ujfalusi MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
430f2055e14SPeter Ujfalusi
431f2055e14SPeter Ujfalusi if (enable_srg) {
432f2055e14SPeter Ujfalusi /* Start the sample generator */
433f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, SPCR2);
434f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
435f2055e14SPeter Ujfalusi }
436f2055e14SPeter Ujfalusi
437f2055e14SPeter Ujfalusi /* Enable transmitter and receiver */
438f2055e14SPeter Ujfalusi tx &= 1;
439f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, SPCR2);
440f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR2, w | tx);
441f2055e14SPeter Ujfalusi
442f2055e14SPeter Ujfalusi rx &= 1;
443f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, SPCR1);
444f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR1, w | rx);
445f2055e14SPeter Ujfalusi
446f2055e14SPeter Ujfalusi /*
447f2055e14SPeter Ujfalusi * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
448f2055e14SPeter Ujfalusi * REVISIT: 100us may give enough time for two CLKSRG, however
449f2055e14SPeter Ujfalusi * due to some unknown PM related, clock gating etc. reason it
450f2055e14SPeter Ujfalusi * is now at 500us.
451f2055e14SPeter Ujfalusi */
452f2055e14SPeter Ujfalusi udelay(500);
453f2055e14SPeter Ujfalusi
454f2055e14SPeter Ujfalusi if (enable_srg) {
455f2055e14SPeter Ujfalusi /* Start frame sync */
456f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, SPCR2);
457f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
458f2055e14SPeter Ujfalusi }
459f2055e14SPeter Ujfalusi
460f2055e14SPeter Ujfalusi if (mcbsp->pdata->has_ccr) {
461f2055e14SPeter Ujfalusi /* Release the transmitter and receiver */
462f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, XCCR);
463f2055e14SPeter Ujfalusi w &= ~(tx ? XDISABLE : 0);
464f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, XCCR, w);
465f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, RCCR);
466f2055e14SPeter Ujfalusi w &= ~(rx ? RDISABLE : 0);
467f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, RCCR, w);
468f2055e14SPeter Ujfalusi }
469f2055e14SPeter Ujfalusi
470f2055e14SPeter Ujfalusi /* Dump McBSP Regs */
471f2055e14SPeter Ujfalusi omap_mcbsp_dump_reg(mcbsp);
472f2055e14SPeter Ujfalusi }
473f2055e14SPeter Ujfalusi
omap_mcbsp_stop(struct omap_mcbsp * mcbsp,int stream)474f2055e14SPeter Ujfalusi static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream)
475f2055e14SPeter Ujfalusi {
476f2055e14SPeter Ujfalusi int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK);
477f2055e14SPeter Ujfalusi int rx = !tx;
478f2055e14SPeter Ujfalusi int idle;
479f2055e14SPeter Ujfalusi u16 w;
480f2055e14SPeter Ujfalusi
481f2055e14SPeter Ujfalusi /* Reset transmitter */
482f2055e14SPeter Ujfalusi tx &= 1;
483f2055e14SPeter Ujfalusi if (mcbsp->pdata->has_ccr) {
484f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, XCCR);
485f2055e14SPeter Ujfalusi w |= (tx ? XDISABLE : 0);
486f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, XCCR, w);
487f2055e14SPeter Ujfalusi }
488f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, SPCR2);
489f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
490f2055e14SPeter Ujfalusi
491f2055e14SPeter Ujfalusi /* Reset receiver */
492f2055e14SPeter Ujfalusi rx &= 1;
493f2055e14SPeter Ujfalusi if (mcbsp->pdata->has_ccr) {
494f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, RCCR);
495f2055e14SPeter Ujfalusi w |= (rx ? RDISABLE : 0);
496f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, RCCR, w);
497f2055e14SPeter Ujfalusi }
498f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, SPCR1);
499f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
500f2055e14SPeter Ujfalusi
501f2055e14SPeter Ujfalusi idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
502f2055e14SPeter Ujfalusi MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
503f2055e14SPeter Ujfalusi
504f2055e14SPeter Ujfalusi if (idle) {
505f2055e14SPeter Ujfalusi /* Reset the sample rate generator */
506f2055e14SPeter Ujfalusi w = MCBSP_READ_CACHE(mcbsp, SPCR2);
507f2055e14SPeter Ujfalusi MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
508f2055e14SPeter Ujfalusi }
509f2055e14SPeter Ujfalusi
510f2055e14SPeter Ujfalusi if (mcbsp->st_data)
511f2055e14SPeter Ujfalusi omap_mcbsp_st_stop(mcbsp);
512f2055e14SPeter Ujfalusi }
513f2055e14SPeter Ujfalusi
514f2055e14SPeter Ujfalusi #define max_thres(m) (mcbsp->pdata->buffer_size)
515f2055e14SPeter Ujfalusi #define valid_threshold(m, val) ((val) <= max_thres(m))
516f2055e14SPeter Ujfalusi #define THRESHOLD_PROP_BUILDER(prop) \
517f2055e14SPeter Ujfalusi static ssize_t prop##_show(struct device *dev, \
518f2055e14SPeter Ujfalusi struct device_attribute *attr, char *buf) \
519f2055e14SPeter Ujfalusi { \
520f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
521f2055e14SPeter Ujfalusi \
522a111ae4dSTakashi Iwai return sysfs_emit(buf, "%u\n", mcbsp->prop); \
523f2055e14SPeter Ujfalusi } \
524f2055e14SPeter Ujfalusi \
525f2055e14SPeter Ujfalusi static ssize_t prop##_store(struct device *dev, \
526f2055e14SPeter Ujfalusi struct device_attribute *attr, \
527f2055e14SPeter Ujfalusi const char *buf, size_t size) \
528f2055e14SPeter Ujfalusi { \
529f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
530f2055e14SPeter Ujfalusi unsigned long val; \
531f2055e14SPeter Ujfalusi int status; \
532f2055e14SPeter Ujfalusi \
533f2055e14SPeter Ujfalusi status = kstrtoul(buf, 0, &val); \
534f2055e14SPeter Ujfalusi if (status) \
535f2055e14SPeter Ujfalusi return status; \
536f2055e14SPeter Ujfalusi \
537f2055e14SPeter Ujfalusi if (!valid_threshold(mcbsp, val)) \
538f2055e14SPeter Ujfalusi return -EDOM; \
539f2055e14SPeter Ujfalusi \
540f2055e14SPeter Ujfalusi mcbsp->prop = val; \
541f2055e14SPeter Ujfalusi return size; \
542f2055e14SPeter Ujfalusi } \
543f2055e14SPeter Ujfalusi \
544b1b384deSYueHaibing static DEVICE_ATTR_RW(prop)
545f2055e14SPeter Ujfalusi
546f2055e14SPeter Ujfalusi THRESHOLD_PROP_BUILDER(max_tx_thres);
547f2055e14SPeter Ujfalusi THRESHOLD_PROP_BUILDER(max_rx_thres);
548f2055e14SPeter Ujfalusi
549f2055e14SPeter Ujfalusi static const char * const dma_op_modes[] = {
550f2055e14SPeter Ujfalusi "element", "threshold",
551f2055e14SPeter Ujfalusi };
552f2055e14SPeter Ujfalusi
dma_op_mode_show(struct device * dev,struct device_attribute * attr,char * buf)553f2055e14SPeter Ujfalusi static ssize_t dma_op_mode_show(struct device *dev,
554f2055e14SPeter Ujfalusi struct device_attribute *attr, char *buf)
555f2055e14SPeter Ujfalusi {
556f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
557f2055e14SPeter Ujfalusi int dma_op_mode, i = 0;
558f2055e14SPeter Ujfalusi ssize_t len = 0;
559f2055e14SPeter Ujfalusi const char * const *s;
560f2055e14SPeter Ujfalusi
561f2055e14SPeter Ujfalusi dma_op_mode = mcbsp->dma_op_mode;
562f2055e14SPeter Ujfalusi
563f2055e14SPeter Ujfalusi for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
564f2055e14SPeter Ujfalusi if (dma_op_mode == i)
565a111ae4dSTakashi Iwai len += sysfs_emit_at(buf, len, "[%s] ", *s);
566f2055e14SPeter Ujfalusi else
567a111ae4dSTakashi Iwai len += sysfs_emit_at(buf, len, "%s ", *s);
568f2055e14SPeter Ujfalusi }
569a111ae4dSTakashi Iwai len += sysfs_emit_at(buf, len, "\n");
570f2055e14SPeter Ujfalusi
571f2055e14SPeter Ujfalusi return len;
572f2055e14SPeter Ujfalusi }
573f2055e14SPeter Ujfalusi
dma_op_mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)574f2055e14SPeter Ujfalusi static ssize_t dma_op_mode_store(struct device *dev,
575f2055e14SPeter Ujfalusi struct device_attribute *attr, const char *buf,
576f2055e14SPeter Ujfalusi size_t size)
577f2055e14SPeter Ujfalusi {
578f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
579f2055e14SPeter Ujfalusi int i;
580f2055e14SPeter Ujfalusi
581f2055e14SPeter Ujfalusi i = sysfs_match_string(dma_op_modes, buf);
582f2055e14SPeter Ujfalusi if (i < 0)
583f2055e14SPeter Ujfalusi return i;
584f2055e14SPeter Ujfalusi
585f2055e14SPeter Ujfalusi spin_lock_irq(&mcbsp->lock);
586f2055e14SPeter Ujfalusi if (!mcbsp->free) {
587f2055e14SPeter Ujfalusi size = -EBUSY;
588f2055e14SPeter Ujfalusi goto unlock;
589f2055e14SPeter Ujfalusi }
590f2055e14SPeter Ujfalusi mcbsp->dma_op_mode = i;
591f2055e14SPeter Ujfalusi
592f2055e14SPeter Ujfalusi unlock:
593f2055e14SPeter Ujfalusi spin_unlock_irq(&mcbsp->lock);
594f2055e14SPeter Ujfalusi
595f2055e14SPeter Ujfalusi return size;
596f2055e14SPeter Ujfalusi }
597f2055e14SPeter Ujfalusi
598f2055e14SPeter Ujfalusi static DEVICE_ATTR_RW(dma_op_mode);
599f2055e14SPeter Ujfalusi
600f2055e14SPeter Ujfalusi static const struct attribute *additional_attrs[] = {
601f2055e14SPeter Ujfalusi &dev_attr_max_tx_thres.attr,
602f2055e14SPeter Ujfalusi &dev_attr_max_rx_thres.attr,
603f2055e14SPeter Ujfalusi &dev_attr_dma_op_mode.attr,
604f2055e14SPeter Ujfalusi NULL,
605f2055e14SPeter Ujfalusi };
606f2055e14SPeter Ujfalusi
607f2055e14SPeter Ujfalusi static const struct attribute_group additional_attr_group = {
608f2055e14SPeter Ujfalusi .attrs = (struct attribute **)additional_attrs,
609f2055e14SPeter Ujfalusi };
610f2055e14SPeter Ujfalusi
611f2055e14SPeter Ujfalusi /*
612f2055e14SPeter Ujfalusi * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
613f2055e14SPeter Ujfalusi * 730 has only 2 McBSP, and both of them are MPU peripherals.
614f2055e14SPeter Ujfalusi */
omap_mcbsp_init(struct platform_device * pdev)615f2055e14SPeter Ujfalusi static int omap_mcbsp_init(struct platform_device *pdev)
616f2055e14SPeter Ujfalusi {
617f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
618f2055e14SPeter Ujfalusi struct resource *res;
6197a0431bbSPierre-Louis Bossart int ret;
620f2055e14SPeter Ujfalusi
621f2055e14SPeter Ujfalusi spin_lock_init(&mcbsp->lock);
622f2055e14SPeter Ujfalusi mcbsp->free = true;
623f2055e14SPeter Ujfalusi
624f2055e14SPeter Ujfalusi res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
625f2055e14SPeter Ujfalusi if (!res)
626f2055e14SPeter Ujfalusi res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
627f2055e14SPeter Ujfalusi
628f2055e14SPeter Ujfalusi mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res);
629f2055e14SPeter Ujfalusi if (IS_ERR(mcbsp->io_base))
630f2055e14SPeter Ujfalusi return PTR_ERR(mcbsp->io_base);
631f2055e14SPeter Ujfalusi
632f2055e14SPeter Ujfalusi mcbsp->phys_base = res->start;
633f2055e14SPeter Ujfalusi mcbsp->reg_cache_size = resource_size(res);
634f2055e14SPeter Ujfalusi
635f2055e14SPeter Ujfalusi res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
636f2055e14SPeter Ujfalusi if (!res)
637f2055e14SPeter Ujfalusi mcbsp->phys_dma_base = mcbsp->phys_base;
638f2055e14SPeter Ujfalusi else
639f2055e14SPeter Ujfalusi mcbsp->phys_dma_base = res->start;
640f2055e14SPeter Ujfalusi
641f2055e14SPeter Ujfalusi /*
642f2055e14SPeter Ujfalusi * OMAP1, 2 uses two interrupt lines: TX, RX
643f2055e14SPeter Ujfalusi * OMAP2430, OMAP3 SoC have combined IRQ line as well.
644f2055e14SPeter Ujfalusi * OMAP4 and newer SoC only have the combined IRQ line.
645f2055e14SPeter Ujfalusi * Use the combined IRQ if available since it gives better debugging
646f2055e14SPeter Ujfalusi * possibilities.
647f2055e14SPeter Ujfalusi */
648f2055e14SPeter Ujfalusi mcbsp->irq = platform_get_irq_byname(pdev, "common");
649f2055e14SPeter Ujfalusi if (mcbsp->irq == -ENXIO) {
650f2055e14SPeter Ujfalusi mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
651f2055e14SPeter Ujfalusi
652f2055e14SPeter Ujfalusi if (mcbsp->tx_irq == -ENXIO) {
653f2055e14SPeter Ujfalusi mcbsp->irq = platform_get_irq(pdev, 0);
654f2055e14SPeter Ujfalusi mcbsp->tx_irq = 0;
655f2055e14SPeter Ujfalusi } else {
656f2055e14SPeter Ujfalusi mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
657f2055e14SPeter Ujfalusi mcbsp->irq = 0;
658f2055e14SPeter Ujfalusi }
659f2055e14SPeter Ujfalusi }
660f2055e14SPeter Ujfalusi
661f2055e14SPeter Ujfalusi if (!pdev->dev.of_node) {
662f2055e14SPeter Ujfalusi res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
663f2055e14SPeter Ujfalusi if (!res) {
664f2055e14SPeter Ujfalusi dev_err(&pdev->dev, "invalid tx DMA channel\n");
665f2055e14SPeter Ujfalusi return -ENODEV;
666f2055e14SPeter Ujfalusi }
667f2055e14SPeter Ujfalusi mcbsp->dma_req[0] = res->start;
668f2055e14SPeter Ujfalusi mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0];
669f2055e14SPeter Ujfalusi
670f2055e14SPeter Ujfalusi res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
671f2055e14SPeter Ujfalusi if (!res) {
672f2055e14SPeter Ujfalusi dev_err(&pdev->dev, "invalid rx DMA channel\n");
673f2055e14SPeter Ujfalusi return -ENODEV;
674f2055e14SPeter Ujfalusi }
675f2055e14SPeter Ujfalusi mcbsp->dma_req[1] = res->start;
676f2055e14SPeter Ujfalusi mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1];
677f2055e14SPeter Ujfalusi } else {
678f2055e14SPeter Ujfalusi mcbsp->dma_data[0].filter_data = "tx";
679f2055e14SPeter Ujfalusi mcbsp->dma_data[1].filter_data = "rx";
680f2055e14SPeter Ujfalusi }
681f2055e14SPeter Ujfalusi
682f2055e14SPeter Ujfalusi mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp,
683f2055e14SPeter Ujfalusi SNDRV_PCM_STREAM_PLAYBACK);
684f2055e14SPeter Ujfalusi mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp,
685f2055e14SPeter Ujfalusi SNDRV_PCM_STREAM_CAPTURE);
686f2055e14SPeter Ujfalusi
68703990fd5SChristophe JAILLET mcbsp->fclk = devm_clk_get(&pdev->dev, "fck");
688f2055e14SPeter Ujfalusi if (IS_ERR(mcbsp->fclk)) {
689f2055e14SPeter Ujfalusi ret = PTR_ERR(mcbsp->fclk);
690f2055e14SPeter Ujfalusi dev_err(mcbsp->dev, "unable to get fck: %d\n", ret);
691f2055e14SPeter Ujfalusi return ret;
692f2055e14SPeter Ujfalusi }
693f2055e14SPeter Ujfalusi
694f2055e14SPeter Ujfalusi mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
695f2055e14SPeter Ujfalusi if (mcbsp->pdata->buffer_size) {
696f2055e14SPeter Ujfalusi /*
697f2055e14SPeter Ujfalusi * Initially configure the maximum thresholds to a safe value.
698f2055e14SPeter Ujfalusi * The McBSP FIFO usage with these values should not go under
699f2055e14SPeter Ujfalusi * 16 locations.
700f2055e14SPeter Ujfalusi * If the whole FIFO without safety buffer is used, than there
701f2055e14SPeter Ujfalusi * is a possibility that the DMA will be not able to push the
702f2055e14SPeter Ujfalusi * new data on time, causing channel shifts in runtime.
703f2055e14SPeter Ujfalusi */
704f2055e14SPeter Ujfalusi mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
705f2055e14SPeter Ujfalusi mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
706f2055e14SPeter Ujfalusi
707f0d96937SDavid Owens ret = devm_device_add_group(mcbsp->dev, &additional_attr_group);
708f2055e14SPeter Ujfalusi if (ret) {
709f2055e14SPeter Ujfalusi dev_err(mcbsp->dev,
710f2055e14SPeter Ujfalusi "Unable to create additional controls\n");
71103990fd5SChristophe JAILLET return ret;
712f2055e14SPeter Ujfalusi }
713f2055e14SPeter Ujfalusi }
714f2055e14SPeter Ujfalusi
715f0d96937SDavid Owens return omap_mcbsp_st_init(pdev);
716f2055e14SPeter Ujfalusi }
717f2055e14SPeter Ujfalusi
718f2055e14SPeter Ujfalusi /*
719f2055e14SPeter Ujfalusi * Stream DMA parameters. DMA request line and port address are set runtime
720f2055e14SPeter Ujfalusi * since they are different between OMAP1 and later OMAPs
721f2055e14SPeter Ujfalusi */
omap_mcbsp_set_threshold(struct snd_pcm_substream * substream,unsigned int packet_size)722f2055e14SPeter Ujfalusi static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream,
723f2055e14SPeter Ujfalusi unsigned int packet_size)
724f2055e14SPeter Ujfalusi {
72502cde14aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
7262842b871SKuninori Morimoto struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
727f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
728f2055e14SPeter Ujfalusi int words;
729f2055e14SPeter Ujfalusi
730f2055e14SPeter Ujfalusi /* No need to proceed further if McBSP does not have FIFO */
731f2055e14SPeter Ujfalusi if (mcbsp->pdata->buffer_size == 0)
732f2055e14SPeter Ujfalusi return;
733f2055e14SPeter Ujfalusi
734f2055e14SPeter Ujfalusi /*
735f2055e14SPeter Ujfalusi * Configure McBSP threshold based on either:
736f2055e14SPeter Ujfalusi * packet_size, when the sDMA is in packet mode, or based on the
737f2055e14SPeter Ujfalusi * period size in THRESHOLD mode, otherwise use McBSP threshold = 1
738f2055e14SPeter Ujfalusi * for mono streams.
739f2055e14SPeter Ujfalusi */
740f2055e14SPeter Ujfalusi if (packet_size)
741f2055e14SPeter Ujfalusi words = packet_size;
742f2055e14SPeter Ujfalusi else
743f2055e14SPeter Ujfalusi words = 1;
744f2055e14SPeter Ujfalusi
745f2055e14SPeter Ujfalusi /* Configure McBSP internal buffer usage */
746f2055e14SPeter Ujfalusi if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
747f2055e14SPeter Ujfalusi omap_mcbsp_set_tx_threshold(mcbsp, words);
748f2055e14SPeter Ujfalusi else
749f2055e14SPeter Ujfalusi omap_mcbsp_set_rx_threshold(mcbsp, words);
750f2055e14SPeter Ujfalusi }
751f2055e14SPeter Ujfalusi
omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params * params,struct snd_pcm_hw_rule * rule)752f2055e14SPeter Ujfalusi static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params,
753f2055e14SPeter Ujfalusi struct snd_pcm_hw_rule *rule)
754f2055e14SPeter Ujfalusi {
755f2055e14SPeter Ujfalusi struct snd_interval *buffer_size = hw_param_interval(params,
756f2055e14SPeter Ujfalusi SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
757f2055e14SPeter Ujfalusi struct snd_interval *channels = hw_param_interval(params,
758f2055e14SPeter Ujfalusi SNDRV_PCM_HW_PARAM_CHANNELS);
759f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = rule->private;
760f2055e14SPeter Ujfalusi struct snd_interval frames;
761f2055e14SPeter Ujfalusi int size;
762f2055e14SPeter Ujfalusi
763f2055e14SPeter Ujfalusi snd_interval_any(&frames);
764f2055e14SPeter Ujfalusi size = mcbsp->pdata->buffer_size;
765f2055e14SPeter Ujfalusi
766f2055e14SPeter Ujfalusi frames.min = size / channels->min;
767f2055e14SPeter Ujfalusi frames.integer = 1;
768f2055e14SPeter Ujfalusi return snd_interval_refine(buffer_size, &frames);
769f2055e14SPeter Ujfalusi }
770f2055e14SPeter Ujfalusi
omap_mcbsp_dai_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)771f2055e14SPeter Ujfalusi static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
772f2055e14SPeter Ujfalusi struct snd_soc_dai *cpu_dai)
773f2055e14SPeter Ujfalusi {
774f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
775f2055e14SPeter Ujfalusi int err = 0;
776f2055e14SPeter Ujfalusi
77736ad1a87SKuninori Morimoto if (!snd_soc_dai_active(cpu_dai))
778f2055e14SPeter Ujfalusi err = omap_mcbsp_request(mcbsp);
779f2055e14SPeter Ujfalusi
780f2055e14SPeter Ujfalusi /*
781f2055e14SPeter Ujfalusi * OMAP3 McBSP FIFO is word structured.
782f2055e14SPeter Ujfalusi * McBSP2 has 1024 + 256 = 1280 word long buffer,
783f2055e14SPeter Ujfalusi * McBSP1,3,4,5 has 128 word long buffer
784f2055e14SPeter Ujfalusi * This means that the size of the FIFO depends on the sample format.
785f2055e14SPeter Ujfalusi * For example on McBSP3:
786f2055e14SPeter Ujfalusi * 16bit samples: size is 128 * 2 = 256 bytes
787f2055e14SPeter Ujfalusi * 32bit samples: size is 128 * 4 = 512 bytes
788f2055e14SPeter Ujfalusi * It is simpler to place constraint for buffer and period based on
789f2055e14SPeter Ujfalusi * channels.
790f2055e14SPeter Ujfalusi * McBSP3 as example again (16 or 32 bit samples):
791f2055e14SPeter Ujfalusi * 1 channel (mono): size is 128 frames (128 words)
792f2055e14SPeter Ujfalusi * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
793f2055e14SPeter Ujfalusi * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
794f2055e14SPeter Ujfalusi */
795f2055e14SPeter Ujfalusi if (mcbsp->pdata->buffer_size) {
796f2055e14SPeter Ujfalusi /*
797f2055e14SPeter Ujfalusi * Rule for the buffer size. We should not allow
798f2055e14SPeter Ujfalusi * smaller buffer than the FIFO size to avoid underruns.
799f2055e14SPeter Ujfalusi * This applies only for the playback stream.
800f2055e14SPeter Ujfalusi */
801f2055e14SPeter Ujfalusi if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
802f2055e14SPeter Ujfalusi snd_pcm_hw_rule_add(substream->runtime, 0,
803f2055e14SPeter Ujfalusi SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
804f2055e14SPeter Ujfalusi omap_mcbsp_hwrule_min_buffersize,
805f2055e14SPeter Ujfalusi mcbsp,
806f2055e14SPeter Ujfalusi SNDRV_PCM_HW_PARAM_CHANNELS, -1);
807f2055e14SPeter Ujfalusi
808f2055e14SPeter Ujfalusi /* Make sure, that the period size is always even */
809f2055e14SPeter Ujfalusi snd_pcm_hw_constraint_step(substream->runtime, 0,
810f2055e14SPeter Ujfalusi SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
811f2055e14SPeter Ujfalusi }
812f2055e14SPeter Ujfalusi
813f2055e14SPeter Ujfalusi return err;
814f2055e14SPeter Ujfalusi }
815f2055e14SPeter Ujfalusi
omap_mcbsp_dai_shutdown(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)816f2055e14SPeter Ujfalusi static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
817f2055e14SPeter Ujfalusi struct snd_soc_dai *cpu_dai)
818f2055e14SPeter Ujfalusi {
819f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
820f2055e14SPeter Ujfalusi int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
821f2055e14SPeter Ujfalusi int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
822f2055e14SPeter Ujfalusi int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
823f2055e14SPeter Ujfalusi
824f2055e14SPeter Ujfalusi if (mcbsp->latency[stream2])
8255371a79bSRafael J. Wysocki cpu_latency_qos_update_request(&mcbsp->pm_qos_req,
826f2055e14SPeter Ujfalusi mcbsp->latency[stream2]);
827f2055e14SPeter Ujfalusi else if (mcbsp->latency[stream1])
8285371a79bSRafael J. Wysocki cpu_latency_qos_remove_request(&mcbsp->pm_qos_req);
829f2055e14SPeter Ujfalusi
830f2055e14SPeter Ujfalusi mcbsp->latency[stream1] = 0;
831f2055e14SPeter Ujfalusi
83236ad1a87SKuninori Morimoto if (!snd_soc_dai_active(cpu_dai)) {
833f2055e14SPeter Ujfalusi omap_mcbsp_free(mcbsp);
834f2055e14SPeter Ujfalusi mcbsp->configured = 0;
835f2055e14SPeter Ujfalusi }
836f2055e14SPeter Ujfalusi }
837f2055e14SPeter Ujfalusi
omap_mcbsp_dai_prepare(struct snd_pcm_substream * substream,struct snd_soc_dai * cpu_dai)838f2055e14SPeter Ujfalusi static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream,
839f2055e14SPeter Ujfalusi struct snd_soc_dai *cpu_dai)
840f2055e14SPeter Ujfalusi {
841f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
842f2055e14SPeter Ujfalusi struct pm_qos_request *pm_qos_req = &mcbsp->pm_qos_req;
843f2055e14SPeter Ujfalusi int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
844f2055e14SPeter Ujfalusi int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE;
845f2055e14SPeter Ujfalusi int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
846f2055e14SPeter Ujfalusi int latency = mcbsp->latency[stream2];
847f2055e14SPeter Ujfalusi
848f2055e14SPeter Ujfalusi /* Prevent omap hardware from hitting off between FIFO fills */
849f2055e14SPeter Ujfalusi if (!latency || mcbsp->latency[stream1] < latency)
850f2055e14SPeter Ujfalusi latency = mcbsp->latency[stream1];
851f2055e14SPeter Ujfalusi
8525371a79bSRafael J. Wysocki if (cpu_latency_qos_request_active(pm_qos_req))
8535371a79bSRafael J. Wysocki cpu_latency_qos_update_request(pm_qos_req, latency);
854f2055e14SPeter Ujfalusi else if (latency)
8555371a79bSRafael J. Wysocki cpu_latency_qos_add_request(pm_qos_req, latency);
856f2055e14SPeter Ujfalusi
857f2055e14SPeter Ujfalusi return 0;
858f2055e14SPeter Ujfalusi }
859f2055e14SPeter Ujfalusi
omap_mcbsp_dai_trigger(struct snd_pcm_substream * substream,int cmd,struct snd_soc_dai * cpu_dai)860f2055e14SPeter Ujfalusi static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
861f2055e14SPeter Ujfalusi struct snd_soc_dai *cpu_dai)
862f2055e14SPeter Ujfalusi {
863f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
864f2055e14SPeter Ujfalusi
865f2055e14SPeter Ujfalusi switch (cmd) {
866f2055e14SPeter Ujfalusi case SNDRV_PCM_TRIGGER_START:
867f2055e14SPeter Ujfalusi case SNDRV_PCM_TRIGGER_RESUME:
868f2055e14SPeter Ujfalusi case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
869f2055e14SPeter Ujfalusi mcbsp->active++;
870f2055e14SPeter Ujfalusi omap_mcbsp_start(mcbsp, substream->stream);
871f2055e14SPeter Ujfalusi break;
872f2055e14SPeter Ujfalusi
873f2055e14SPeter Ujfalusi case SNDRV_PCM_TRIGGER_STOP:
874f2055e14SPeter Ujfalusi case SNDRV_PCM_TRIGGER_SUSPEND:
875f2055e14SPeter Ujfalusi case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
876f2055e14SPeter Ujfalusi omap_mcbsp_stop(mcbsp, substream->stream);
877f2055e14SPeter Ujfalusi mcbsp->active--;
878f2055e14SPeter Ujfalusi break;
879f2055e14SPeter Ujfalusi default:
880f2055e14SPeter Ujfalusi return -EINVAL;
881f2055e14SPeter Ujfalusi }
882f2055e14SPeter Ujfalusi
883f2055e14SPeter Ujfalusi return 0;
884f2055e14SPeter Ujfalusi }
885f2055e14SPeter Ujfalusi
omap_mcbsp_dai_delay(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)886f2055e14SPeter Ujfalusi static snd_pcm_sframes_t omap_mcbsp_dai_delay(
887f2055e14SPeter Ujfalusi struct snd_pcm_substream *substream,
888f2055e14SPeter Ujfalusi struct snd_soc_dai *dai)
889f2055e14SPeter Ujfalusi {
89002cde14aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
8912842b871SKuninori Morimoto struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
892f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
893f2055e14SPeter Ujfalusi u16 fifo_use;
894f2055e14SPeter Ujfalusi snd_pcm_sframes_t delay;
895f2055e14SPeter Ujfalusi
896f2055e14SPeter Ujfalusi /* No need to proceed further if McBSP does not have FIFO */
897f2055e14SPeter Ujfalusi if (mcbsp->pdata->buffer_size == 0)
898f2055e14SPeter Ujfalusi return 0;
899f2055e14SPeter Ujfalusi
900f2055e14SPeter Ujfalusi if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
901f2055e14SPeter Ujfalusi fifo_use = omap_mcbsp_get_tx_delay(mcbsp);
902f2055e14SPeter Ujfalusi else
903f2055e14SPeter Ujfalusi fifo_use = omap_mcbsp_get_rx_delay(mcbsp);
904f2055e14SPeter Ujfalusi
905f2055e14SPeter Ujfalusi /*
906f2055e14SPeter Ujfalusi * Divide the used locations with the channel count to get the
907f2055e14SPeter Ujfalusi * FIFO usage in samples (don't care about partial samples in the
908f2055e14SPeter Ujfalusi * buffer).
909f2055e14SPeter Ujfalusi */
910f2055e14SPeter Ujfalusi delay = fifo_use / substream->runtime->channels;
911f2055e14SPeter Ujfalusi
912f2055e14SPeter Ujfalusi return delay;
913f2055e14SPeter Ujfalusi }
914f2055e14SPeter Ujfalusi
omap_mcbsp_dai_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * cpu_dai)915f2055e14SPeter Ujfalusi static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
916f2055e14SPeter Ujfalusi struct snd_pcm_hw_params *params,
917f2055e14SPeter Ujfalusi struct snd_soc_dai *cpu_dai)
918f2055e14SPeter Ujfalusi {
919f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
920f2055e14SPeter Ujfalusi struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
921f2055e14SPeter Ujfalusi struct snd_dmaengine_dai_dma_data *dma_data;
922f2055e14SPeter Ujfalusi int wlen, channels, wpf;
923f2055e14SPeter Ujfalusi int pkt_size = 0;
924f2055e14SPeter Ujfalusi unsigned int format, div, framesize, master;
925f2055e14SPeter Ujfalusi unsigned int buffer_size = mcbsp->pdata->buffer_size;
926f2055e14SPeter Ujfalusi
927f2055e14SPeter Ujfalusi dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
928f2055e14SPeter Ujfalusi channels = params_channels(params);
929f2055e14SPeter Ujfalusi
930f2055e14SPeter Ujfalusi switch (params_format(params)) {
931f2055e14SPeter Ujfalusi case SNDRV_PCM_FORMAT_S16_LE:
932f2055e14SPeter Ujfalusi wlen = 16;
933f2055e14SPeter Ujfalusi break;
934f2055e14SPeter Ujfalusi case SNDRV_PCM_FORMAT_S32_LE:
935f2055e14SPeter Ujfalusi wlen = 32;
936f2055e14SPeter Ujfalusi break;
937f2055e14SPeter Ujfalusi default:
938f2055e14SPeter Ujfalusi return -EINVAL;
939f2055e14SPeter Ujfalusi }
940f2055e14SPeter Ujfalusi if (buffer_size) {
941f2055e14SPeter Ujfalusi int latency;
942f2055e14SPeter Ujfalusi
943f2055e14SPeter Ujfalusi if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
944f2055e14SPeter Ujfalusi int period_words, max_thrsh;
945f2055e14SPeter Ujfalusi int divider = 0;
946f2055e14SPeter Ujfalusi
947f2055e14SPeter Ujfalusi period_words = params_period_bytes(params) / (wlen / 8);
948f2055e14SPeter Ujfalusi if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
949f2055e14SPeter Ujfalusi max_thrsh = mcbsp->max_tx_thres;
950f2055e14SPeter Ujfalusi else
951f2055e14SPeter Ujfalusi max_thrsh = mcbsp->max_rx_thres;
952f2055e14SPeter Ujfalusi /*
953f2055e14SPeter Ujfalusi * Use sDMA packet mode if McBSP is in threshold mode:
954f2055e14SPeter Ujfalusi * If period words less than the FIFO size the packet
955f2055e14SPeter Ujfalusi * size is set to the number of period words, otherwise
956f2055e14SPeter Ujfalusi * Look for the biggest threshold value which divides
957f2055e14SPeter Ujfalusi * the period size evenly.
958f2055e14SPeter Ujfalusi */
959f2055e14SPeter Ujfalusi divider = period_words / max_thrsh;
960f2055e14SPeter Ujfalusi if (period_words % max_thrsh)
961f2055e14SPeter Ujfalusi divider++;
962f2055e14SPeter Ujfalusi while (period_words % divider &&
963f2055e14SPeter Ujfalusi divider < period_words)
964f2055e14SPeter Ujfalusi divider++;
965f2055e14SPeter Ujfalusi if (divider == period_words)
966f2055e14SPeter Ujfalusi return -EINVAL;
967f2055e14SPeter Ujfalusi
968f2055e14SPeter Ujfalusi pkt_size = period_words / divider;
969f2055e14SPeter Ujfalusi } else if (channels > 1) {
970f2055e14SPeter Ujfalusi /* Use packet mode for non mono streams */
971f2055e14SPeter Ujfalusi pkt_size = channels;
972f2055e14SPeter Ujfalusi }
973f2055e14SPeter Ujfalusi
974f2055e14SPeter Ujfalusi latency = (buffer_size - pkt_size) / channels;
975f2055e14SPeter Ujfalusi latency = latency * USEC_PER_SEC /
976f2055e14SPeter Ujfalusi (params->rate_num / params->rate_den);
977f2055e14SPeter Ujfalusi mcbsp->latency[substream->stream] = latency;
978f2055e14SPeter Ujfalusi
979f2055e14SPeter Ujfalusi omap_mcbsp_set_threshold(substream, pkt_size);
980f2055e14SPeter Ujfalusi }
981f2055e14SPeter Ujfalusi
982f2055e14SPeter Ujfalusi dma_data->maxburst = pkt_size;
983f2055e14SPeter Ujfalusi
984f2055e14SPeter Ujfalusi if (mcbsp->configured) {
985f2055e14SPeter Ujfalusi /* McBSP already configured by another stream */
986f2055e14SPeter Ujfalusi return 0;
987f2055e14SPeter Ujfalusi }
988f2055e14SPeter Ujfalusi
989f2055e14SPeter Ujfalusi regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7));
990f2055e14SPeter Ujfalusi regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7));
991f2055e14SPeter Ujfalusi regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7));
992f2055e14SPeter Ujfalusi regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7));
993f2055e14SPeter Ujfalusi format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
994f2055e14SPeter Ujfalusi wpf = channels;
995f2055e14SPeter Ujfalusi if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
996f2055e14SPeter Ujfalusi format == SND_SOC_DAIFMT_LEFT_J)) {
997f2055e14SPeter Ujfalusi /* Use dual-phase frames */
998f2055e14SPeter Ujfalusi regs->rcr2 |= RPHASE;
999f2055e14SPeter Ujfalusi regs->xcr2 |= XPHASE;
1000f2055e14SPeter Ujfalusi /* Set 1 word per (McBSP) frame for phase1 and phase2 */
1001f2055e14SPeter Ujfalusi wpf--;
1002f2055e14SPeter Ujfalusi regs->rcr2 |= RFRLEN2(wpf - 1);
1003f2055e14SPeter Ujfalusi regs->xcr2 |= XFRLEN2(wpf - 1);
1004f2055e14SPeter Ujfalusi }
1005f2055e14SPeter Ujfalusi
1006f2055e14SPeter Ujfalusi regs->rcr1 |= RFRLEN1(wpf - 1);
1007f2055e14SPeter Ujfalusi regs->xcr1 |= XFRLEN1(wpf - 1);
1008f2055e14SPeter Ujfalusi
1009f2055e14SPeter Ujfalusi switch (params_format(params)) {
1010f2055e14SPeter Ujfalusi case SNDRV_PCM_FORMAT_S16_LE:
1011f2055e14SPeter Ujfalusi /* Set word lengths */
1012f2055e14SPeter Ujfalusi regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
1013f2055e14SPeter Ujfalusi regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
1014f2055e14SPeter Ujfalusi regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16);
1015f2055e14SPeter Ujfalusi regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16);
1016f2055e14SPeter Ujfalusi break;
1017f2055e14SPeter Ujfalusi case SNDRV_PCM_FORMAT_S32_LE:
1018f2055e14SPeter Ujfalusi /* Set word lengths */
1019f2055e14SPeter Ujfalusi regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32);
1020f2055e14SPeter Ujfalusi regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32);
1021f2055e14SPeter Ujfalusi regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32);
1022f2055e14SPeter Ujfalusi regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32);
1023f2055e14SPeter Ujfalusi break;
1024f2055e14SPeter Ujfalusi default:
1025f2055e14SPeter Ujfalusi /* Unsupported PCM format */
1026f2055e14SPeter Ujfalusi return -EINVAL;
1027f2055e14SPeter Ujfalusi }
1028f2055e14SPeter Ujfalusi
1029f2055e14SPeter Ujfalusi /* In McBSP master modes, FRAME (i.e. sample rate) is generated
1030f2055e14SPeter Ujfalusi * by _counting_ BCLKs. Calculate frame size in BCLKs */
1031563ff63dSCharles Keepax master = mcbsp->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
1032563ff63dSCharles Keepax if (master == SND_SOC_DAIFMT_BP_FP) {
1033f2055e14SPeter Ujfalusi div = mcbsp->clk_div ? mcbsp->clk_div : 1;
1034f2055e14SPeter Ujfalusi framesize = (mcbsp->in_freq / div) / params_rate(params);
1035f2055e14SPeter Ujfalusi
1036f2055e14SPeter Ujfalusi if (framesize < wlen * channels) {
1037f2055e14SPeter Ujfalusi printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
1038f2055e14SPeter Ujfalusi "channels\n", __func__);
1039f2055e14SPeter Ujfalusi return -EINVAL;
1040f2055e14SPeter Ujfalusi }
1041f2055e14SPeter Ujfalusi } else
1042f2055e14SPeter Ujfalusi framesize = wlen * channels;
1043f2055e14SPeter Ujfalusi
1044f2055e14SPeter Ujfalusi /* Set FS period and length in terms of bit clock periods */
1045f2055e14SPeter Ujfalusi regs->srgr2 &= ~FPER(0xfff);
1046f2055e14SPeter Ujfalusi regs->srgr1 &= ~FWID(0xff);
1047f2055e14SPeter Ujfalusi switch (format) {
1048f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_I2S:
1049f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_LEFT_J:
1050f2055e14SPeter Ujfalusi regs->srgr2 |= FPER(framesize - 1);
1051f2055e14SPeter Ujfalusi regs->srgr1 |= FWID((framesize >> 1) - 1);
1052f2055e14SPeter Ujfalusi break;
1053f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_DSP_A:
1054f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_DSP_B:
1055f2055e14SPeter Ujfalusi regs->srgr2 |= FPER(framesize - 1);
1056f2055e14SPeter Ujfalusi regs->srgr1 |= FWID(0);
1057f2055e14SPeter Ujfalusi break;
1058f2055e14SPeter Ujfalusi }
1059f2055e14SPeter Ujfalusi
1060f2055e14SPeter Ujfalusi omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs);
1061f2055e14SPeter Ujfalusi mcbsp->wlen = wlen;
1062f2055e14SPeter Ujfalusi mcbsp->configured = 1;
1063f2055e14SPeter Ujfalusi
1064f2055e14SPeter Ujfalusi return 0;
1065f2055e14SPeter Ujfalusi }
1066f2055e14SPeter Ujfalusi
1067f2055e14SPeter Ujfalusi /*
1068f2055e14SPeter Ujfalusi * This must be called before _set_clkdiv and _set_sysclk since McBSP register
1069f2055e14SPeter Ujfalusi * cache is initialized here
1070f2055e14SPeter Ujfalusi */
omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai * cpu_dai,unsigned int fmt)1071f2055e14SPeter Ujfalusi static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
1072f2055e14SPeter Ujfalusi unsigned int fmt)
1073f2055e14SPeter Ujfalusi {
1074f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
1075f2055e14SPeter Ujfalusi struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
1076f2055e14SPeter Ujfalusi bool inv_fs = false;
1077f2055e14SPeter Ujfalusi
1078f2055e14SPeter Ujfalusi if (mcbsp->configured)
1079f2055e14SPeter Ujfalusi return 0;
1080f2055e14SPeter Ujfalusi
1081f2055e14SPeter Ujfalusi mcbsp->fmt = fmt;
1082f2055e14SPeter Ujfalusi memset(regs, 0, sizeof(*regs));
1083f2055e14SPeter Ujfalusi /* Generic McBSP register settings */
1084f2055e14SPeter Ujfalusi regs->spcr2 |= XINTM(3) | FREE;
1085f2055e14SPeter Ujfalusi regs->spcr1 |= RINTM(3);
1086f2055e14SPeter Ujfalusi /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */
1087f2055e14SPeter Ujfalusi if (!mcbsp->pdata->has_ccr) {
1088f2055e14SPeter Ujfalusi regs->rcr2 |= RFIG;
1089f2055e14SPeter Ujfalusi regs->xcr2 |= XFIG;
1090f2055e14SPeter Ujfalusi }
1091f2055e14SPeter Ujfalusi
1092f2055e14SPeter Ujfalusi /* Configure XCCR/RCCR only for revisions which have ccr registers */
1093f2055e14SPeter Ujfalusi if (mcbsp->pdata->has_ccr) {
1094f2055e14SPeter Ujfalusi regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
1095f2055e14SPeter Ujfalusi regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
1096f2055e14SPeter Ujfalusi }
1097f2055e14SPeter Ujfalusi
1098f2055e14SPeter Ujfalusi switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
1099f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_I2S:
1100f2055e14SPeter Ujfalusi /* 1-bit data delay */
1101f2055e14SPeter Ujfalusi regs->rcr2 |= RDATDLY(1);
1102f2055e14SPeter Ujfalusi regs->xcr2 |= XDATDLY(1);
1103f2055e14SPeter Ujfalusi break;
1104f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_LEFT_J:
1105f2055e14SPeter Ujfalusi /* 0-bit data delay */
1106f2055e14SPeter Ujfalusi regs->rcr2 |= RDATDLY(0);
1107f2055e14SPeter Ujfalusi regs->xcr2 |= XDATDLY(0);
1108f2055e14SPeter Ujfalusi regs->spcr1 |= RJUST(2);
1109f2055e14SPeter Ujfalusi /* Invert FS polarity configuration */
1110f2055e14SPeter Ujfalusi inv_fs = true;
1111f2055e14SPeter Ujfalusi break;
1112f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_DSP_A:
1113f2055e14SPeter Ujfalusi /* 1-bit data delay */
1114f2055e14SPeter Ujfalusi regs->rcr2 |= RDATDLY(1);
1115f2055e14SPeter Ujfalusi regs->xcr2 |= XDATDLY(1);
1116f2055e14SPeter Ujfalusi /* Invert FS polarity configuration */
1117f2055e14SPeter Ujfalusi inv_fs = true;
1118f2055e14SPeter Ujfalusi break;
1119f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_DSP_B:
1120f2055e14SPeter Ujfalusi /* 0-bit data delay */
1121f2055e14SPeter Ujfalusi regs->rcr2 |= RDATDLY(0);
1122f2055e14SPeter Ujfalusi regs->xcr2 |= XDATDLY(0);
1123f2055e14SPeter Ujfalusi /* Invert FS polarity configuration */
1124f2055e14SPeter Ujfalusi inv_fs = true;
1125f2055e14SPeter Ujfalusi break;
1126f2055e14SPeter Ujfalusi default:
1127f2055e14SPeter Ujfalusi /* Unsupported data format */
1128f2055e14SPeter Ujfalusi return -EINVAL;
1129f2055e14SPeter Ujfalusi }
1130f2055e14SPeter Ujfalusi
1131563ff63dSCharles Keepax switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
1132563ff63dSCharles Keepax case SND_SOC_DAIFMT_BP_FP:
1133f2055e14SPeter Ujfalusi /* McBSP master. Set FS and bit clocks as outputs */
1134f2055e14SPeter Ujfalusi regs->pcr0 |= FSXM | FSRM |
1135f2055e14SPeter Ujfalusi CLKXM | CLKRM;
1136f2055e14SPeter Ujfalusi /* Sample rate generator drives the FS */
1137f2055e14SPeter Ujfalusi regs->srgr2 |= FSGM;
1138f2055e14SPeter Ujfalusi break;
1139563ff63dSCharles Keepax case SND_SOC_DAIFMT_BC_FP:
1140f2055e14SPeter Ujfalusi /* McBSP slave. FS clock as output */
1141f2055e14SPeter Ujfalusi regs->srgr2 |= FSGM;
1142f2055e14SPeter Ujfalusi regs->pcr0 |= FSXM | FSRM;
1143f2055e14SPeter Ujfalusi break;
1144563ff63dSCharles Keepax case SND_SOC_DAIFMT_BC_FC:
1145f2055e14SPeter Ujfalusi /* McBSP slave */
1146f2055e14SPeter Ujfalusi break;
1147f2055e14SPeter Ujfalusi default:
1148f2055e14SPeter Ujfalusi /* Unsupported master/slave configuration */
1149f2055e14SPeter Ujfalusi return -EINVAL;
1150f2055e14SPeter Ujfalusi }
1151f2055e14SPeter Ujfalusi
1152f2055e14SPeter Ujfalusi /* Set bit clock (CLKX/CLKR) and FS polarities */
1153f2055e14SPeter Ujfalusi switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
1154f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_NB_NF:
1155f2055e14SPeter Ujfalusi /*
1156f2055e14SPeter Ujfalusi * Normal BCLK + FS.
1157f2055e14SPeter Ujfalusi * FS active low. TX data driven on falling edge of bit clock
1158f2055e14SPeter Ujfalusi * and RX data sampled on rising edge of bit clock.
1159f2055e14SPeter Ujfalusi */
1160f2055e14SPeter Ujfalusi regs->pcr0 |= FSXP | FSRP |
1161f2055e14SPeter Ujfalusi CLKXP | CLKRP;
1162f2055e14SPeter Ujfalusi break;
1163f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_NB_IF:
1164f2055e14SPeter Ujfalusi regs->pcr0 |= CLKXP | CLKRP;
1165f2055e14SPeter Ujfalusi break;
1166f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_IB_NF:
1167f2055e14SPeter Ujfalusi regs->pcr0 |= FSXP | FSRP;
1168f2055e14SPeter Ujfalusi break;
1169f2055e14SPeter Ujfalusi case SND_SOC_DAIFMT_IB_IF:
1170f2055e14SPeter Ujfalusi break;
1171f2055e14SPeter Ujfalusi default:
1172f2055e14SPeter Ujfalusi return -EINVAL;
1173f2055e14SPeter Ujfalusi }
11741597bfbfSJason Yan if (inv_fs)
1175f2055e14SPeter Ujfalusi regs->pcr0 ^= FSXP | FSRP;
1176f2055e14SPeter Ujfalusi
1177f2055e14SPeter Ujfalusi return 0;
1178f2055e14SPeter Ujfalusi }
1179f2055e14SPeter Ujfalusi
omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai * cpu_dai,int div_id,int div)1180f2055e14SPeter Ujfalusi static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
1181f2055e14SPeter Ujfalusi int div_id, int div)
1182f2055e14SPeter Ujfalusi {
1183f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
1184f2055e14SPeter Ujfalusi struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
1185f2055e14SPeter Ujfalusi
1186f2055e14SPeter Ujfalusi if (div_id != OMAP_MCBSP_CLKGDV)
1187f2055e14SPeter Ujfalusi return -ENODEV;
1188f2055e14SPeter Ujfalusi
1189f2055e14SPeter Ujfalusi mcbsp->clk_div = div;
1190f2055e14SPeter Ujfalusi regs->srgr1 &= ~CLKGDV(0xff);
1191f2055e14SPeter Ujfalusi regs->srgr1 |= CLKGDV(div - 1);
1192f2055e14SPeter Ujfalusi
1193f2055e14SPeter Ujfalusi return 0;
1194f2055e14SPeter Ujfalusi }
1195f2055e14SPeter Ujfalusi
omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai * cpu_dai,int clk_id,unsigned int freq,int dir)1196f2055e14SPeter Ujfalusi static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
1197f2055e14SPeter Ujfalusi int clk_id, unsigned int freq,
1198f2055e14SPeter Ujfalusi int dir)
1199f2055e14SPeter Ujfalusi {
1200f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai);
1201f2055e14SPeter Ujfalusi struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs;
1202f2055e14SPeter Ujfalusi int err = 0;
1203f2055e14SPeter Ujfalusi
1204f2055e14SPeter Ujfalusi if (mcbsp->active) {
1205f2055e14SPeter Ujfalusi if (freq == mcbsp->in_freq)
1206f2055e14SPeter Ujfalusi return 0;
1207f2055e14SPeter Ujfalusi else
1208f2055e14SPeter Ujfalusi return -EBUSY;
1209f2055e14SPeter Ujfalusi }
1210f2055e14SPeter Ujfalusi
1211f2055e14SPeter Ujfalusi mcbsp->in_freq = freq;
1212f2055e14SPeter Ujfalusi regs->srgr2 &= ~CLKSM;
1213f2055e14SPeter Ujfalusi regs->pcr0 &= ~SCLKME;
1214f2055e14SPeter Ujfalusi
1215f2055e14SPeter Ujfalusi switch (clk_id) {
1216f2055e14SPeter Ujfalusi case OMAP_MCBSP_SYSCLK_CLK:
1217f2055e14SPeter Ujfalusi regs->srgr2 |= CLKSM;
1218f2055e14SPeter Ujfalusi break;
1219f2055e14SPeter Ujfalusi case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
1220f2055e14SPeter Ujfalusi if (mcbsp_omap1()) {
1221f2055e14SPeter Ujfalusi err = -EINVAL;
1222f2055e14SPeter Ujfalusi break;
1223f2055e14SPeter Ujfalusi }
1224f2055e14SPeter Ujfalusi err = omap2_mcbsp_set_clks_src(mcbsp,
1225f2055e14SPeter Ujfalusi MCBSP_CLKS_PRCM_SRC);
1226f2055e14SPeter Ujfalusi break;
1227f2055e14SPeter Ujfalusi case OMAP_MCBSP_SYSCLK_CLKS_EXT:
1228f2055e14SPeter Ujfalusi if (mcbsp_omap1()) {
1229f2055e14SPeter Ujfalusi err = 0;
1230f2055e14SPeter Ujfalusi break;
1231f2055e14SPeter Ujfalusi }
1232f2055e14SPeter Ujfalusi err = omap2_mcbsp_set_clks_src(mcbsp,
1233f2055e14SPeter Ujfalusi MCBSP_CLKS_PAD_SRC);
1234f2055e14SPeter Ujfalusi break;
1235f2055e14SPeter Ujfalusi
1236f2055e14SPeter Ujfalusi case OMAP_MCBSP_SYSCLK_CLKX_EXT:
1237f2055e14SPeter Ujfalusi regs->srgr2 |= CLKSM;
1238f2055e14SPeter Ujfalusi regs->pcr0 |= SCLKME;
1239f2055e14SPeter Ujfalusi /*
1240f2055e14SPeter Ujfalusi * If McBSP is master but yet the CLKX/CLKR pin drives the SRG,
1241f2055e14SPeter Ujfalusi * disable output on those pins. This enables to inject the
1242f2055e14SPeter Ujfalusi * reference clock through CLKX/CLKR. For this to work
1243f2055e14SPeter Ujfalusi * set_dai_sysclk() _needs_ to be called after set_dai_fmt().
1244f2055e14SPeter Ujfalusi */
1245f2055e14SPeter Ujfalusi regs->pcr0 &= ~CLKXM;
1246f2055e14SPeter Ujfalusi break;
1247f2055e14SPeter Ujfalusi case OMAP_MCBSP_SYSCLK_CLKR_EXT:
1248f2055e14SPeter Ujfalusi regs->pcr0 |= SCLKME;
1249f2055e14SPeter Ujfalusi /* Disable ouput on CLKR pin in master mode */
1250f2055e14SPeter Ujfalusi regs->pcr0 &= ~CLKRM;
1251f2055e14SPeter Ujfalusi break;
1252f2055e14SPeter Ujfalusi default:
1253f2055e14SPeter Ujfalusi err = -ENODEV;
1254f2055e14SPeter Ujfalusi }
1255f2055e14SPeter Ujfalusi
1256f2055e14SPeter Ujfalusi return err;
1257f2055e14SPeter Ujfalusi }
1258f2055e14SPeter Ujfalusi
omap_mcbsp_probe(struct snd_soc_dai * dai)1259f2055e14SPeter Ujfalusi static int omap_mcbsp_probe(struct snd_soc_dai *dai)
1260f2055e14SPeter Ujfalusi {
1261f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai);
1262f2055e14SPeter Ujfalusi
1263f2055e14SPeter Ujfalusi pm_runtime_enable(mcbsp->dev);
1264f2055e14SPeter Ujfalusi
1265f2055e14SPeter Ujfalusi snd_soc_dai_init_dma_data(dai,
1266f2055e14SPeter Ujfalusi &mcbsp->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
1267f2055e14SPeter Ujfalusi &mcbsp->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
1268f2055e14SPeter Ujfalusi
1269f2055e14SPeter Ujfalusi return 0;
1270f2055e14SPeter Ujfalusi }
1271f2055e14SPeter Ujfalusi
omap_mcbsp_remove(struct snd_soc_dai * dai)1272f2055e14SPeter Ujfalusi static int omap_mcbsp_remove(struct snd_soc_dai *dai)
1273f2055e14SPeter Ujfalusi {
1274f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai);
1275f2055e14SPeter Ujfalusi
1276f2055e14SPeter Ujfalusi pm_runtime_disable(mcbsp->dev);
1277f2055e14SPeter Ujfalusi
1278f2055e14SPeter Ujfalusi return 0;
1279f2055e14SPeter Ujfalusi }
1280f2055e14SPeter Ujfalusi
1281dd9d64deSKuninori Morimoto static const struct snd_soc_dai_ops mcbsp_dai_ops = {
1282f2055e14SPeter Ujfalusi .probe = omap_mcbsp_probe,
1283f2055e14SPeter Ujfalusi .remove = omap_mcbsp_remove,
1284dd9d64deSKuninori Morimoto .startup = omap_mcbsp_dai_startup,
1285dd9d64deSKuninori Morimoto .shutdown = omap_mcbsp_dai_shutdown,
1286dd9d64deSKuninori Morimoto .prepare = omap_mcbsp_dai_prepare,
1287dd9d64deSKuninori Morimoto .trigger = omap_mcbsp_dai_trigger,
1288dd9d64deSKuninori Morimoto .delay = omap_mcbsp_dai_delay,
1289dd9d64deSKuninori Morimoto .hw_params = omap_mcbsp_dai_hw_params,
1290dd9d64deSKuninori Morimoto .set_fmt = omap_mcbsp_dai_set_dai_fmt,
1291dd9d64deSKuninori Morimoto .set_clkdiv = omap_mcbsp_dai_set_clkdiv,
1292dd9d64deSKuninori Morimoto .set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
1293dd9d64deSKuninori Morimoto };
1294dd9d64deSKuninori Morimoto
1295dd9d64deSKuninori Morimoto static struct snd_soc_dai_driver omap_mcbsp_dai = {
1296f2055e14SPeter Ujfalusi .playback = {
1297f2055e14SPeter Ujfalusi .channels_min = 1,
1298f2055e14SPeter Ujfalusi .channels_max = 16,
1299f2055e14SPeter Ujfalusi .rates = OMAP_MCBSP_RATES,
1300f2055e14SPeter Ujfalusi .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
1301f2055e14SPeter Ujfalusi },
1302f2055e14SPeter Ujfalusi .capture = {
1303f2055e14SPeter Ujfalusi .channels_min = 1,
1304f2055e14SPeter Ujfalusi .channels_max = 16,
1305f2055e14SPeter Ujfalusi .rates = OMAP_MCBSP_RATES,
1306f2055e14SPeter Ujfalusi .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
1307f2055e14SPeter Ujfalusi },
1308f2055e14SPeter Ujfalusi .ops = &mcbsp_dai_ops,
1309f2055e14SPeter Ujfalusi };
1310f2055e14SPeter Ujfalusi
1311f2055e14SPeter Ujfalusi static const struct snd_soc_component_driver omap_mcbsp_component = {
1312f2055e14SPeter Ujfalusi .name = "omap-mcbsp",
131339c84e77SCharles Keepax .legacy_dai_naming = 1,
1314f2055e14SPeter Ujfalusi };
1315f2055e14SPeter Ujfalusi
1316f2055e14SPeter Ujfalusi static struct omap_mcbsp_platform_data omap2420_pdata = {
1317f2055e14SPeter Ujfalusi .reg_step = 4,
1318f2055e14SPeter Ujfalusi .reg_size = 2,
1319f2055e14SPeter Ujfalusi };
1320f2055e14SPeter Ujfalusi
1321f2055e14SPeter Ujfalusi static struct omap_mcbsp_platform_data omap2430_pdata = {
1322f2055e14SPeter Ujfalusi .reg_step = 4,
1323f2055e14SPeter Ujfalusi .reg_size = 4,
1324f2055e14SPeter Ujfalusi .has_ccr = true,
1325f2055e14SPeter Ujfalusi };
1326f2055e14SPeter Ujfalusi
1327f2055e14SPeter Ujfalusi static struct omap_mcbsp_platform_data omap3_pdata = {
1328f2055e14SPeter Ujfalusi .reg_step = 4,
1329f2055e14SPeter Ujfalusi .reg_size = 4,
1330f2055e14SPeter Ujfalusi .has_ccr = true,
1331f2055e14SPeter Ujfalusi .has_wakeup = true,
1332f2055e14SPeter Ujfalusi };
1333f2055e14SPeter Ujfalusi
1334f2055e14SPeter Ujfalusi static struct omap_mcbsp_platform_data omap4_pdata = {
1335f2055e14SPeter Ujfalusi .reg_step = 4,
1336f2055e14SPeter Ujfalusi .reg_size = 4,
1337f2055e14SPeter Ujfalusi .has_ccr = true,
1338f2055e14SPeter Ujfalusi .has_wakeup = true,
1339f2055e14SPeter Ujfalusi };
1340f2055e14SPeter Ujfalusi
1341f2055e14SPeter Ujfalusi static const struct of_device_id omap_mcbsp_of_match[] = {
1342f2055e14SPeter Ujfalusi {
1343f2055e14SPeter Ujfalusi .compatible = "ti,omap2420-mcbsp",
1344f2055e14SPeter Ujfalusi .data = &omap2420_pdata,
1345f2055e14SPeter Ujfalusi },
1346f2055e14SPeter Ujfalusi {
1347f2055e14SPeter Ujfalusi .compatible = "ti,omap2430-mcbsp",
1348f2055e14SPeter Ujfalusi .data = &omap2430_pdata,
1349f2055e14SPeter Ujfalusi },
1350f2055e14SPeter Ujfalusi {
1351f2055e14SPeter Ujfalusi .compatible = "ti,omap3-mcbsp",
1352f2055e14SPeter Ujfalusi .data = &omap3_pdata,
1353f2055e14SPeter Ujfalusi },
1354f2055e14SPeter Ujfalusi {
1355f2055e14SPeter Ujfalusi .compatible = "ti,omap4-mcbsp",
1356f2055e14SPeter Ujfalusi .data = &omap4_pdata,
1357f2055e14SPeter Ujfalusi },
1358f2055e14SPeter Ujfalusi { },
1359f2055e14SPeter Ujfalusi };
1360f2055e14SPeter Ujfalusi MODULE_DEVICE_TABLE(of, omap_mcbsp_of_match);
1361f2055e14SPeter Ujfalusi
asoc_mcbsp_probe(struct platform_device * pdev)1362f2055e14SPeter Ujfalusi static int asoc_mcbsp_probe(struct platform_device *pdev)
1363f2055e14SPeter Ujfalusi {
1364f2055e14SPeter Ujfalusi struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev);
1365f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp;
1366f2055e14SPeter Ujfalusi const struct of_device_id *match;
1367f2055e14SPeter Ujfalusi int ret;
1368f2055e14SPeter Ujfalusi
1369f2055e14SPeter Ujfalusi match = of_match_device(omap_mcbsp_of_match, &pdev->dev);
1370f2055e14SPeter Ujfalusi if (match) {
1371f2055e14SPeter Ujfalusi struct device_node *node = pdev->dev.of_node;
1372f2055e14SPeter Ujfalusi struct omap_mcbsp_platform_data *pdata_quirk = pdata;
1373f2055e14SPeter Ujfalusi int buffer_size;
1374f2055e14SPeter Ujfalusi
1375f2055e14SPeter Ujfalusi pdata = devm_kzalloc(&pdev->dev,
1376f2055e14SPeter Ujfalusi sizeof(struct omap_mcbsp_platform_data),
1377f2055e14SPeter Ujfalusi GFP_KERNEL);
1378f2055e14SPeter Ujfalusi if (!pdata)
1379f2055e14SPeter Ujfalusi return -ENOMEM;
1380f2055e14SPeter Ujfalusi
1381f2055e14SPeter Ujfalusi memcpy(pdata, match->data, sizeof(*pdata));
1382f2055e14SPeter Ujfalusi if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size))
1383f2055e14SPeter Ujfalusi pdata->buffer_size = buffer_size;
1384f2055e14SPeter Ujfalusi if (pdata_quirk)
1385f2055e14SPeter Ujfalusi pdata->force_ick_on = pdata_quirk->force_ick_on;
1386f2055e14SPeter Ujfalusi } else if (!pdata) {
1387f2055e14SPeter Ujfalusi dev_err(&pdev->dev, "missing platform data.\n");
1388f2055e14SPeter Ujfalusi return -EINVAL;
1389f2055e14SPeter Ujfalusi }
1390f2055e14SPeter Ujfalusi mcbsp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcbsp), GFP_KERNEL);
1391f2055e14SPeter Ujfalusi if (!mcbsp)
1392f2055e14SPeter Ujfalusi return -ENOMEM;
1393f2055e14SPeter Ujfalusi
1394f2055e14SPeter Ujfalusi mcbsp->id = pdev->id;
1395f2055e14SPeter Ujfalusi mcbsp->pdata = pdata;
1396f2055e14SPeter Ujfalusi mcbsp->dev = &pdev->dev;
1397f2055e14SPeter Ujfalusi platform_set_drvdata(pdev, mcbsp);
1398f2055e14SPeter Ujfalusi
1399f2055e14SPeter Ujfalusi ret = omap_mcbsp_init(pdev);
1400f2055e14SPeter Ujfalusi if (ret)
1401f2055e14SPeter Ujfalusi return ret;
1402f2055e14SPeter Ujfalusi
1403f2055e14SPeter Ujfalusi if (mcbsp->pdata->reg_size == 2) {
1404f2055e14SPeter Ujfalusi omap_mcbsp_dai.playback.formats = SNDRV_PCM_FMTBIT_S16_LE;
1405f2055e14SPeter Ujfalusi omap_mcbsp_dai.capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
1406f2055e14SPeter Ujfalusi }
1407f2055e14SPeter Ujfalusi
1408f2055e14SPeter Ujfalusi ret = devm_snd_soc_register_component(&pdev->dev,
1409f2055e14SPeter Ujfalusi &omap_mcbsp_component,
1410f2055e14SPeter Ujfalusi &omap_mcbsp_dai, 1);
1411f2055e14SPeter Ujfalusi if (ret)
1412f2055e14SPeter Ujfalusi return ret;
1413f2055e14SPeter Ujfalusi
14143e802e90SJanusz Krzysztofik return sdma_pcm_platform_register(&pdev->dev, "tx", "rx");
1415f2055e14SPeter Ujfalusi }
1416f2055e14SPeter Ujfalusi
asoc_mcbsp_remove(struct platform_device * pdev)14179b6818bbSUwe Kleine-König static void asoc_mcbsp_remove(struct platform_device *pdev)
1418f2055e14SPeter Ujfalusi {
1419f2055e14SPeter Ujfalusi struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
1420f2055e14SPeter Ujfalusi
1421f2055e14SPeter Ujfalusi if (mcbsp->pdata->ops && mcbsp->pdata->ops->free)
1422f2055e14SPeter Ujfalusi mcbsp->pdata->ops->free(mcbsp->id);
1423f2055e14SPeter Ujfalusi
14245371a79bSRafael J. Wysocki if (cpu_latency_qos_request_active(&mcbsp->pm_qos_req))
14255371a79bSRafael J. Wysocki cpu_latency_qos_remove_request(&mcbsp->pm_qos_req);
1426f2055e14SPeter Ujfalusi }
1427f2055e14SPeter Ujfalusi
1428f2055e14SPeter Ujfalusi static struct platform_driver asoc_mcbsp_driver = {
1429f2055e14SPeter Ujfalusi .driver = {
1430f2055e14SPeter Ujfalusi .name = "omap-mcbsp",
1431f2055e14SPeter Ujfalusi .of_match_table = omap_mcbsp_of_match,
1432f2055e14SPeter Ujfalusi },
1433f2055e14SPeter Ujfalusi
1434f2055e14SPeter Ujfalusi .probe = asoc_mcbsp_probe,
14359b6818bbSUwe Kleine-König .remove_new = asoc_mcbsp_remove,
1436f2055e14SPeter Ujfalusi };
1437f2055e14SPeter Ujfalusi
1438f2055e14SPeter Ujfalusi module_platform_driver(asoc_mcbsp_driver);
1439f2055e14SPeter Ujfalusi
1440f2055e14SPeter Ujfalusi MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
1441f2055e14SPeter Ujfalusi MODULE_DESCRIPTION("OMAP I2S SoC Interface");
1442f2055e14SPeter Ujfalusi MODULE_LICENSE("GPL");
1443f2055e14SPeter Ujfalusi MODULE_ALIAS("platform:omap-mcbsp");
1444