blob: 4b233e8cf9054a15241f7dfc0ac68957dda5903a [file] [log] [blame]
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001/*
2 * linux/arch/arm/plat-omap/mcbsp.c
3 *
4 * Copyright (C) 2004 Nokia Corporation
5 * Author: Samuel Ortiz <samuel.ortiz@nokia.com>
6 *
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * Multichannel mode not supported.
13 */
14
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/device.h>
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +030018#include <linux/platform_device.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010019#include <linux/interrupt.h>
20#include <linux/err.h>
Russell Kingf8ce2542006-01-07 16:15:52 +000021#include <linux/clk.h>
Tony Lindgren04fbf6a2007-02-12 10:50:53 -080022#include <linux/delay.h>
Eduardo Valentinfb78d802008-07-03 12:24:39 +030023#include <linux/io.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090024#include <linux/slab.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010025
Tony Lindgrence491cf2009-10-20 09:40:47 -070026#include <plat/mcbsp.h>
Kishon Vijay Abraham Ie95496d2011-02-24 15:16:54 +053027#include <linux/pm_runtime.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010028
Paul Walmsley59fb6592010-12-21 15:30:55 -070029/* XXX These "sideways" includes are a sign that something is wrong */
30#include "../mach-omap2/cm2xxx_3xxx.h"
Eero Nurkkalad912fa92010-02-22 12:21:11 +000031#include "../mach-omap2/cm-regbits-34xx.h"
32
Chandra Shekharb4b58f52008-10-08 10:01:39 +030033struct omap_mcbsp **mcbsp_ptr;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080034int omap_mcbsp_count, omap_mcbsp_cache_size;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +030035
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -070036static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030037{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080038 if (cpu_class_is_omap1()) {
39 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)] = (u16)val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080040 __raw_writew((u16)val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080041 } else if (cpu_is_omap2420()) {
42 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)] = (u16)val;
43 __raw_writew((u16)val, mcbsp->io_base + reg);
44 } else {
45 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)] = val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080046 __raw_writel(val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080047 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030048}
49
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -070050static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030051{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080052 if (cpu_class_is_omap1()) {
53 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
54 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)];
55 } else if (cpu_is_omap2420()) {
56 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
57 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)];
58 } else {
59 return !from_cache ? __raw_readl(mcbsp->io_base + reg) :
60 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)];
61 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030062}
63
Eero Nurkkalad912fa92010-02-22 12:21:11 +000064#ifdef CONFIG_ARCH_OMAP3
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -070065static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
Eero Nurkkalad912fa92010-02-22 12:21:11 +000066{
67 __raw_writel(val, mcbsp->st_data->io_base_st + reg);
68}
69
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -070070static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
Eero Nurkkalad912fa92010-02-22 12:21:11 +000071{
72 return __raw_readl(mcbsp->st_data->io_base_st + reg);
73}
74#endif
75
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080076#define MCBSP_READ(mcbsp, reg) \
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080077 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080078#define MCBSP_WRITE(mcbsp, reg, val) \
79 omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080080#define MCBSP_READ_CACHE(mcbsp, reg) \
81 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030082
Eero Nurkkalad912fa92010-02-22 12:21:11 +000083#define MCBSP_ST_READ(mcbsp, reg) \
84 omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
85#define MCBSP_ST_WRITE(mcbsp, reg, val) \
86 omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
87
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010088static void omap_mcbsp_dump_reg(u8 id)
89{
Chandra Shekharb4b58f52008-10-08 10:01:39 +030090 struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id);
91
92 dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
93 dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080094 MCBSP_READ(mcbsp, DRR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030095 dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080096 MCBSP_READ(mcbsp, DRR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030097 dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080098 MCBSP_READ(mcbsp, DXR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030099 dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800100 MCBSP_READ(mcbsp, DXR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300101 dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800102 MCBSP_READ(mcbsp, SPCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300103 dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800104 MCBSP_READ(mcbsp, SPCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300105 dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800106 MCBSP_READ(mcbsp, RCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300107 dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800108 MCBSP_READ(mcbsp, RCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300109 dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800110 MCBSP_READ(mcbsp, XCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300111 dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800112 MCBSP_READ(mcbsp, XCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300113 dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800114 MCBSP_READ(mcbsp, SRGR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300115 dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800116 MCBSP_READ(mcbsp, SRGR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300117 dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800118 MCBSP_READ(mcbsp, PCR0));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300119 dev_dbg(mcbsp->dev, "***********************\n");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100120}
121
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700122static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100123{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400124 struct omap_mcbsp *mcbsp_tx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700125 u16 irqst_spcr2;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100126
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800127 irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700128 dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100129
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700130 if (irqst_spcr2 & XSYNC_ERR) {
131 dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n",
132 irqst_spcr2);
133 /* Writing zero to XSYNC_ERR clears the IRQ */
Janusz Krzysztofik0841cb82010-02-23 15:50:38 +0000134 MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700135 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300136
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100137 return IRQ_HANDLED;
138}
139
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700140static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100141{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400142 struct omap_mcbsp *mcbsp_rx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700143 u16 irqst_spcr1;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100144
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800145 irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700146 dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100147
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700148 if (irqst_spcr1 & RSYNC_ERR) {
149 dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n",
150 irqst_spcr1);
151 /* Writing zero to RSYNC_ERR clears the IRQ */
Janusz Krzysztofik0841cb82010-02-23 15:50:38 +0000152 MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700153 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300154
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100155 return IRQ_HANDLED;
156}
157
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100158/*
159 * omap_mcbsp_config simply write a config to the
160 * appropriate McBSP.
161 * You either call this function or set the McBSP registers
162 * by yourself before calling omap_mcbsp_start().
163 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300164void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100165{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300166 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100167
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300168 if (!omap_mcbsp_check_valid_id(id)) {
169 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
170 return;
171 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300172 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300173
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300174 dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
175 mcbsp->id, mcbsp->phys_base);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100176
177 /* We write the given config */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800178 MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
179 MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
180 MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
181 MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
182 MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
183 MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
184 MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
185 MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
186 MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
187 MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
188 MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
Syed Rafiuddina5b92cc2009-07-28 18:57:10 +0530189 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800190 MCBSP_WRITE(mcbsp, XCCR, config->xccr);
191 MCBSP_WRITE(mcbsp, RCCR, config->rccr);
Tony Lindgren3127f8f2009-01-15 13:09:54 +0200192 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100193}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300194EXPORT_SYMBOL(omap_mcbsp_config);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100195
Kishon Vijay Abraham I9504ba62011-02-24 15:16:55 +0530196/**
197 * omap_mcbsp_dma_params - returns the dma channel number
198 * @id - mcbsp id
199 * @stream - indicates the direction of data flow (rx or tx)
200 *
201 * Returns the dma channel number for the rx channel or tx channel
202 * based on the value of @stream for the requested mcbsp given by @id
203 */
204int omap_mcbsp_dma_ch_params(unsigned int id, unsigned int stream)
205{
206 struct omap_mcbsp *mcbsp;
207
208 if (!omap_mcbsp_check_valid_id(id)) {
209 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
210 return -ENODEV;
211 }
212 mcbsp = id_to_mcbsp_ptr(id);
213
214 if (stream)
215 return mcbsp->dma_rx_sync;
216 else
217 return mcbsp->dma_tx_sync;
218}
219EXPORT_SYMBOL(omap_mcbsp_dma_ch_params);
220
221/**
222 * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register
223 * @id - mcbsp id
224 * @stream - indicates the direction of data flow (rx or tx)
225 *
226 * Returns the address of mcbsp data transmit register or data receive register
227 * to be used by DMA for transferring/receiving data based on the value of
228 * @stream for the requested mcbsp given by @id
229 */
230int omap_mcbsp_dma_reg_params(unsigned int id, unsigned int stream)
231{
232 struct omap_mcbsp *mcbsp;
233 int data_reg;
234
235 if (!omap_mcbsp_check_valid_id(id)) {
236 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
237 return -ENODEV;
238 }
239 mcbsp = id_to_mcbsp_ptr(id);
240
241 data_reg = mcbsp->phys_dma_base;
242
243 if (mcbsp->mcbsp_config_type < MCBSP_CONFIG_TYPE2) {
244 if (stream)
245 data_reg += OMAP_MCBSP_REG_DRR1;
246 else
247 data_reg += OMAP_MCBSP_REG_DXR1;
248 } else {
249 if (stream)
250 data_reg += OMAP_MCBSP_REG_DRR;
251 else
252 data_reg += OMAP_MCBSP_REG_DXR;
253 }
254
255 return data_reg;
256}
257EXPORT_SYMBOL(omap_mcbsp_dma_reg_params);
258
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -0800259#ifdef CONFIG_ARCH_OMAP3
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000260static void omap_st_on(struct omap_mcbsp *mcbsp)
261{
262 unsigned int w;
263
264 /*
265 * Sidetone uses McBSP ICLK - which must not idle when sidetones
266 * are enabled or sidetones start sounding ugly.
267 */
Paul Walmsleyc4d7e582010-12-21 21:05:14 -0700268 w = omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000269 w &= ~(1 << (mcbsp->id - 2));
Paul Walmsleyc4d7e582010-12-21 21:05:14 -0700270 omap2_cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000271
272 /* Enable McBSP Sidetone */
273 w = MCBSP_READ(mcbsp, SSELCR);
274 MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
275
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000276 /* Enable Sidetone from Sidetone Core */
277 w = MCBSP_ST_READ(mcbsp, SSELCR);
278 MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
279}
280
281static void omap_st_off(struct omap_mcbsp *mcbsp)
282{
283 unsigned int w;
284
285 w = MCBSP_ST_READ(mcbsp, SSELCR);
286 MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
287
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000288 w = MCBSP_READ(mcbsp, SSELCR);
289 MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
290
Paul Walmsleyc4d7e582010-12-21 21:05:14 -0700291 w = omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000292 w |= 1 << (mcbsp->id - 2);
Paul Walmsleyc4d7e582010-12-21 21:05:14 -0700293 omap2_cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000294}
295
296static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
297{
298 u16 val, i;
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000299
300 val = MCBSP_ST_READ(mcbsp, SSELCR);
301
302 if (val & ST_COEFFWREN)
303 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
304
305 MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
306
307 for (i = 0; i < 128; i++)
308 MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
309
310 i = 0;
311
312 val = MCBSP_ST_READ(mcbsp, SSELCR);
313 while (!(val & ST_COEFFWRDONE) && (++i < 1000))
314 val = MCBSP_ST_READ(mcbsp, SSELCR);
315
316 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
317
318 if (i == 1000)
319 dev_err(mcbsp->dev, "McBSP FIR load error!\n");
320}
321
322static void omap_st_chgain(struct omap_mcbsp *mcbsp)
323{
324 u16 w;
325 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000326
327 w = MCBSP_ST_READ(mcbsp, SSELCR);
328
329 MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \
330 ST_CH1GAIN(st_data->ch1gain));
331}
332
333int omap_st_set_chgain(unsigned int id, int channel, s16 chgain)
334{
335 struct omap_mcbsp *mcbsp;
336 struct omap_mcbsp_st_data *st_data;
337 int ret = 0;
338
339 if (!omap_mcbsp_check_valid_id(id)) {
340 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
341 return -ENODEV;
342 }
343
344 mcbsp = id_to_mcbsp_ptr(id);
345 st_data = mcbsp->st_data;
346
347 if (!st_data)
348 return -ENOENT;
349
350 spin_lock_irq(&mcbsp->lock);
351 if (channel == 0)
352 st_data->ch0gain = chgain;
353 else if (channel == 1)
354 st_data->ch1gain = chgain;
355 else
356 ret = -EINVAL;
357
358 if (st_data->enabled)
359 omap_st_chgain(mcbsp);
360 spin_unlock_irq(&mcbsp->lock);
361
362 return ret;
363}
364EXPORT_SYMBOL(omap_st_set_chgain);
365
366int omap_st_get_chgain(unsigned int id, int channel, s16 *chgain)
367{
368 struct omap_mcbsp *mcbsp;
369 struct omap_mcbsp_st_data *st_data;
370 int ret = 0;
371
372 if (!omap_mcbsp_check_valid_id(id)) {
373 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
374 return -ENODEV;
375 }
376
377 mcbsp = id_to_mcbsp_ptr(id);
378 st_data = mcbsp->st_data;
379
380 if (!st_data)
381 return -ENOENT;
382
383 spin_lock_irq(&mcbsp->lock);
384 if (channel == 0)
385 *chgain = st_data->ch0gain;
386 else if (channel == 1)
387 *chgain = st_data->ch1gain;
388 else
389 ret = -EINVAL;
390 spin_unlock_irq(&mcbsp->lock);
391
392 return ret;
393}
394EXPORT_SYMBOL(omap_st_get_chgain);
395
396static int omap_st_start(struct omap_mcbsp *mcbsp)
397{
398 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
399
400 if (st_data && st_data->enabled && !st_data->running) {
401 omap_st_fir_write(mcbsp, st_data->taps);
402 omap_st_chgain(mcbsp);
403
404 if (!mcbsp->free) {
405 omap_st_on(mcbsp);
406 st_data->running = 1;
407 }
408 }
409
410 return 0;
411}
412
413int omap_st_enable(unsigned int id)
414{
415 struct omap_mcbsp *mcbsp;
416 struct omap_mcbsp_st_data *st_data;
417
418 if (!omap_mcbsp_check_valid_id(id)) {
419 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
420 return -ENODEV;
421 }
422
423 mcbsp = id_to_mcbsp_ptr(id);
424 st_data = mcbsp->st_data;
425
426 if (!st_data)
427 return -ENODEV;
428
429 spin_lock_irq(&mcbsp->lock);
430 st_data->enabled = 1;
431 omap_st_start(mcbsp);
432 spin_unlock_irq(&mcbsp->lock);
433
434 return 0;
435}
436EXPORT_SYMBOL(omap_st_enable);
437
438static int omap_st_stop(struct omap_mcbsp *mcbsp)
439{
440 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
441
442 if (st_data && st_data->running) {
443 if (!mcbsp->free) {
444 omap_st_off(mcbsp);
445 st_data->running = 0;
446 }
447 }
448
449 return 0;
450}
451
452int omap_st_disable(unsigned int id)
453{
454 struct omap_mcbsp *mcbsp;
455 struct omap_mcbsp_st_data *st_data;
456 int ret = 0;
457
458 if (!omap_mcbsp_check_valid_id(id)) {
459 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
460 return -ENODEV;
461 }
462
463 mcbsp = id_to_mcbsp_ptr(id);
464 st_data = mcbsp->st_data;
465
466 if (!st_data)
467 return -ENODEV;
468
469 spin_lock_irq(&mcbsp->lock);
470 omap_st_stop(mcbsp);
471 st_data->enabled = 0;
472 spin_unlock_irq(&mcbsp->lock);
473
474 return ret;
475}
476EXPORT_SYMBOL(omap_st_disable);
477
478int omap_st_is_enabled(unsigned int id)
479{
480 struct omap_mcbsp *mcbsp;
481 struct omap_mcbsp_st_data *st_data;
482
483 if (!omap_mcbsp_check_valid_id(id)) {
484 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
485 return -ENODEV;
486 }
487
488 mcbsp = id_to_mcbsp_ptr(id);
489 st_data = mcbsp->st_data;
490
491 if (!st_data)
492 return -ENODEV;
493
494
495 return st_data->enabled;
496}
497EXPORT_SYMBOL(omap_st_is_enabled);
498
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300499/*
Peter Ujfalusi451fd822010-06-03 07:39:33 +0300500 * omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
501 * The threshold parameter is 1 based, and it is converted (threshold - 1)
502 * for the THRSH2 register.
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300503 */
504void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
505{
506 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300507
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500508 if (!cpu_is_omap34xx() && !cpu_is_omap44xx())
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300509 return;
510
511 if (!omap_mcbsp_check_valid_id(id)) {
512 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
513 return;
514 }
515 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300516
Peter Ujfalusi451fd822010-06-03 07:39:33 +0300517 if (threshold && threshold <= mcbsp->max_tx_thres)
518 MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300519}
520EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
521
522/*
Peter Ujfalusi451fd822010-06-03 07:39:33 +0300523 * omap_mcbsp_set_rx_threshold configures the receive threshold in words.
524 * The threshold parameter is 1 based, and it is converted (threshold - 1)
525 * for the THRSH1 register.
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300526 */
527void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
528{
529 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300530
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500531 if (!cpu_is_omap34xx() && !cpu_is_omap44xx())
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300532 return;
533
534 if (!omap_mcbsp_check_valid_id(id)) {
535 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
536 return;
537 }
538 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300539
Peter Ujfalusi451fd822010-06-03 07:39:33 +0300540 if (threshold && threshold <= mcbsp->max_rx_thres)
541 MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300542}
543EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +0300544
545/*
546 * omap_mcbsp_get_max_tx_thres just return the current configured
547 * maximum threshold for transmission
548 */
549u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
550{
551 struct omap_mcbsp *mcbsp;
552
553 if (!omap_mcbsp_check_valid_id(id)) {
554 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
555 return -ENODEV;
556 }
557 mcbsp = id_to_mcbsp_ptr(id);
558
559 return mcbsp->max_tx_thres;
560}
561EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
562
563/*
564 * omap_mcbsp_get_max_rx_thres just return the current configured
565 * maximum threshold for reception
566 */
567u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
568{
569 struct omap_mcbsp *mcbsp;
570
571 if (!omap_mcbsp_check_valid_id(id)) {
572 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
573 return -ENODEV;
574 }
575 mcbsp = id_to_mcbsp_ptr(id);
576
577 return mcbsp->max_rx_thres;
578}
579EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300580
Peter Ujfalusi0acce822010-06-03 07:39:32 +0300581u16 omap_mcbsp_get_fifo_size(unsigned int id)
582{
583 struct omap_mcbsp *mcbsp;
584
585 if (!omap_mcbsp_check_valid_id(id)) {
586 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
587 return -ENODEV;
588 }
589 mcbsp = id_to_mcbsp_ptr(id);
590
591 return mcbsp->pdata->buffer_size;
592}
593EXPORT_SYMBOL(omap_mcbsp_get_fifo_size);
594
Peter Ujfalusi7dc976e2010-03-03 15:08:08 +0200595/*
596 * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
597 */
598u16 omap_mcbsp_get_tx_delay(unsigned int id)
599{
600 struct omap_mcbsp *mcbsp;
601 u16 buffstat;
602
603 if (!omap_mcbsp_check_valid_id(id)) {
604 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
605 return -ENODEV;
606 }
607 mcbsp = id_to_mcbsp_ptr(id);
608
609 /* Returns the number of free locations in the buffer */
610 buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
611
612 /* Number of slots are different in McBSP ports */
Peter Ujfalusif10b8ad2010-06-03 07:39:34 +0300613 return mcbsp->pdata->buffer_size - buffstat;
Peter Ujfalusi7dc976e2010-03-03 15:08:08 +0200614}
615EXPORT_SYMBOL(omap_mcbsp_get_tx_delay);
616
617/*
618 * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
619 * to reach the threshold value (when the DMA will be triggered to read it)
620 */
621u16 omap_mcbsp_get_rx_delay(unsigned int id)
622{
623 struct omap_mcbsp *mcbsp;
624 u16 buffstat, threshold;
625
626 if (!omap_mcbsp_check_valid_id(id)) {
627 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
628 return -ENODEV;
629 }
630 mcbsp = id_to_mcbsp_ptr(id);
631
632 /* Returns the number of used locations in the buffer */
633 buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
634 /* RX threshold */
635 threshold = MCBSP_READ(mcbsp, THRSH1);
636
637 /* Return the number of location till we reach the threshold limit */
638 if (threshold <= buffstat)
639 return 0;
640 else
641 return threshold - buffstat;
642}
643EXPORT_SYMBOL(omap_mcbsp_get_rx_delay);
644
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300645/*
646 * omap_mcbsp_get_dma_op_mode just return the current configured
647 * operating mode for the mcbsp channel
648 */
649int omap_mcbsp_get_dma_op_mode(unsigned int id)
650{
651 struct omap_mcbsp *mcbsp;
652 int dma_op_mode;
653
654 if (!omap_mcbsp_check_valid_id(id)) {
655 printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
656 return -ENODEV;
657 }
658 mcbsp = id_to_mcbsp_ptr(id);
659
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300660 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300661
662 return dma_op_mode;
663}
664EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300665
666static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
667{
668 /*
669 * Enable wakup behavior, smart idle and all wakeups
670 * REVISIT: some wakeups may be unnecessary
671 */
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500672 if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530673 MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300674 }
675}
676
677static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
678{
679 /*
680 * Disable wakup behavior, smart idle and all wakeups
681 */
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500682 if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300683 /*
684 * HW bug workaround - If no_idle mode is taken, we need to
685 * go to smart_idle before going to always_idle, or the
686 * device will not hit retention anymore.
687 */
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300688
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800689 MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300690 }
691}
692#else
693static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
694static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000695static inline void omap_st_start(struct omap_mcbsp *mcbsp) {}
696static inline void omap_st_stop(struct omap_mcbsp *mcbsp) {}
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300697#endif
698
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100699int omap_mcbsp_request(unsigned int id)
700{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300701 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800702 void *reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100703 int err;
704
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300705 if (!omap_mcbsp_check_valid_id(id)) {
706 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
707 return -ENODEV;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100708 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300709 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300710
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800711 reg_cache = kzalloc(omap_mcbsp_cache_size, GFP_KERNEL);
712 if (!reg_cache) {
713 return -ENOMEM;
714 }
715
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300716 spin_lock(&mcbsp->lock);
717 if (!mcbsp->free) {
718 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
719 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800720 err = -EBUSY;
721 goto err_kfree;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100722 }
723
Shubhrajyoti D6722a722010-12-07 16:25:41 -0800724 mcbsp->free = false;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800725 mcbsp->reg_cache = reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300726 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100727
Russell Kingb820ce42009-01-23 10:26:46 +0000728 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
729 mcbsp->pdata->ops->request(id);
730
Kishon Vijay Abraham Ie95496d2011-02-24 15:16:54 +0530731 pm_runtime_get_sync(mcbsp->dev);
Russell Kingb820ce42009-01-23 10:26:46 +0000732
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300733 /* Do procedure specific to omap34xx arch, if applicable */
734 omap34xx_mcbsp_request(mcbsp);
735
Jarkko Nikula5a070552008-10-08 10:01:41 +0300736 /*
737 * Make sure that transmitter, receiver and sample-rate generator are
738 * not running before activating IRQs.
739 */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800740 MCBSP_WRITE(mcbsp, SPCR1, 0);
741 MCBSP_WRITE(mcbsp, SPCR2, 0);
Jarkko Nikula5a070552008-10-08 10:01:41 +0300742
Jarkko Nikulabafe2722011-06-14 11:23:52 +0000743 err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
744 0, "McBSP", (void *)mcbsp);
745 if (err != 0) {
746 dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
747 "for McBSP%d\n", mcbsp->tx_irq,
748 mcbsp->id);
749 goto err_clk_disable;
750 }
Tony Lindgren120db2c2006-04-02 17:46:27 +0100751
Jarkko Nikulabafe2722011-06-14 11:23:52 +0000752 if (mcbsp->rx_irq) {
753 err = request_irq(mcbsp->rx_irq,
754 omap_mcbsp_rx_irq_handler,
755 0, "McBSP", (void *)mcbsp);
756 if (err != 0) {
757 dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
758 "for McBSP%d\n", mcbsp->rx_irq,
759 mcbsp->id);
760 goto err_free_irq;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100761 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100762 }
763
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100764 return 0;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800765err_free_irq:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800766 free_irq(mcbsp->tx_irq, (void *)mcbsp);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800767err_clk_disable:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800768 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800769 mcbsp->pdata->ops->free(id);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800770
771 /* Do procedure specific to omap34xx arch, if applicable */
772 omap34xx_mcbsp_free(mcbsp);
773
Kishon Vijay Abraham Ie95496d2011-02-24 15:16:54 +0530774 pm_runtime_put_sync(mcbsp->dev);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800775
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800776 spin_lock(&mcbsp->lock);
Shubhrajyoti D6722a722010-12-07 16:25:41 -0800777 mcbsp->free = true;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800778 mcbsp->reg_cache = NULL;
779err_kfree:
780 spin_unlock(&mcbsp->lock);
781 kfree(reg_cache);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800782
783 return err;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100784}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300785EXPORT_SYMBOL(omap_mcbsp_request);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100786
787void omap_mcbsp_free(unsigned int id)
788{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300789 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800790 void *reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300791
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300792 if (!omap_mcbsp_check_valid_id(id)) {
793 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100794 return;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100795 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300796 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100797
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300798 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
799 mcbsp->pdata->ops->free(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300800
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300801 /* Do procedure specific to omap34xx arch, if applicable */
802 omap34xx_mcbsp_free(mcbsp);
803
Kishon Vijay Abraham Ie95496d2011-02-24 15:16:54 +0530804 pm_runtime_put_sync(mcbsp->dev);
Russell Kingb820ce42009-01-23 10:26:46 +0000805
Jarkko Nikulabafe2722011-06-14 11:23:52 +0000806 if (mcbsp->rx_irq)
807 free_irq(mcbsp->rx_irq, (void *)mcbsp);
808 free_irq(mcbsp->tx_irq, (void *)mcbsp);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100809
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800810 reg_cache = mcbsp->reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100811
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800812 spin_lock(&mcbsp->lock);
813 if (mcbsp->free)
814 dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
815 else
Shubhrajyoti D6722a722010-12-07 16:25:41 -0800816 mcbsp->free = true;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800817 mcbsp->reg_cache = NULL;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300818 spin_unlock(&mcbsp->lock);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800819
820 if (reg_cache)
821 kfree(reg_cache);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100822}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300823EXPORT_SYMBOL(omap_mcbsp_free);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100824
825/*
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300826 * Here we start the McBSP, by enabling transmitter, receiver or both.
827 * If no transmitter or receiver is active prior calling, then sample-rate
828 * generator and frame sync are started.
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100829 */
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300830void omap_mcbsp_start(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100831{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300832 struct omap_mcbsp *mcbsp;
Peter Ujfalusice3f0542010-08-31 08:11:44 +0000833 int enable_srg = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100834 u16 w;
835
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300836 if (!omap_mcbsp_check_valid_id(id)) {
837 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100838 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300839 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300840 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100841
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000842 if (cpu_is_omap34xx())
843 omap_st_start(mcbsp);
844
Peter Ujfalusice3f0542010-08-31 08:11:44 +0000845 /* Only enable SRG, if McBSP is master */
846 w = MCBSP_READ_CACHE(mcbsp, PCR0);
847 if (w & (FSXM | FSRM | CLKXM | CLKRM))
848 enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
849 MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300850
Peter Ujfalusice3f0542010-08-31 08:11:44 +0000851 if (enable_srg) {
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300852 /* Start the sample generator */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800853 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800854 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300855 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100856
857 /* Enable transmitter and receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300858 tx &= 1;
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800859 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800860 MCBSP_WRITE(mcbsp, SPCR2, w | tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100861
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300862 rx &= 1;
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800863 w = MCBSP_READ_CACHE(mcbsp, SPCR1);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800864 MCBSP_WRITE(mcbsp, SPCR1, w | rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100865
Eduardo Valentin44a63112009-08-20 16:18:09 +0300866 /*
867 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
868 * REVISIT: 100us may give enough time for two CLKSRG, however
869 * due to some unknown PM related, clock gating etc. reason it
870 * is now at 500us.
871 */
872 udelay(500);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100873
Peter Ujfalusice3f0542010-08-31 08:11:44 +0000874 if (enable_srg) {
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300875 /* Start frame sync */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800876 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800877 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300878 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100879
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500880 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300881 /* Release the transmitter and receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800882 w = MCBSP_READ_CACHE(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300883 w &= ~(tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800884 MCBSP_WRITE(mcbsp, XCCR, w);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800885 w = MCBSP_READ_CACHE(mcbsp, RCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300886 w &= ~(rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800887 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300888 }
889
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100890 /* Dump McBSP Regs */
891 omap_mcbsp_dump_reg(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100892}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300893EXPORT_SYMBOL(omap_mcbsp_start);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100894
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300895void omap_mcbsp_stop(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100896{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300897 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300898 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100899 u16 w;
900
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300901 if (!omap_mcbsp_check_valid_id(id)) {
902 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100903 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300904 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100905
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300906 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100907
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300908 /* Reset transmitter */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300909 tx &= 1;
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500910 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800911 w = MCBSP_READ_CACHE(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300912 w |= (tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800913 MCBSP_WRITE(mcbsp, XCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300914 }
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800915 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800916 MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100917
918 /* Reset receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300919 rx &= 1;
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500920 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800921 w = MCBSP_READ_CACHE(mcbsp, RCCR);
Jarkko Nikulaa93d4ed2009-10-14 09:56:35 -0700922 w |= (rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800923 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300924 }
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800925 w = MCBSP_READ_CACHE(mcbsp, SPCR1);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800926 MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100927
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800928 idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
929 MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300930
931 if (idle) {
932 /* Reset the sample rate generator */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800933 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800934 MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300935 }
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000936
937 if (cpu_is_omap34xx())
938 omap_st_stop(mcbsp);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100939}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300940EXPORT_SYMBOL(omap_mcbsp_stop);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100941
Paul Walmsley69d042d2011-07-01 08:52:25 +0000942/*
943 * The following functions are only required on an OMAP1-only build.
944 * mach-omap2/mcbsp.c contains the real functions
945 */
946#ifndef CONFIG_ARCH_OMAP2PLUS
947int omap2_mcbsp_set_clks_src(u8 id, u8 fck_src_id)
948{
949 WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
950 __func__);
951 return -EINVAL;
952}
953
954void omap2_mcbsp1_mux_clkr_src(u8 mux)
955{
956 WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
957 __func__);
958 return;
959}
960
961void omap2_mcbsp1_mux_fsr_src(u8 mux)
962{
963 WARN(1, "%s: should never be called on an OMAP1-only kernel\n",
964 __func__);
965 return;
966}
967#endif
968
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -0800969#ifdef CONFIG_ARCH_OMAP3
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +0300970#define max_thres(m) (mcbsp->pdata->buffer_size)
971#define valid_threshold(m, val) ((val) <= max_thres(m))
972#define THRESHOLD_PROP_BUILDER(prop) \
973static ssize_t prop##_show(struct device *dev, \
974 struct device_attribute *attr, char *buf) \
975{ \
976 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
977 \
978 return sprintf(buf, "%u\n", mcbsp->prop); \
979} \
980 \
981static ssize_t prop##_store(struct device *dev, \
982 struct device_attribute *attr, \
983 const char *buf, size_t size) \
984{ \
985 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
986 unsigned long val; \
987 int status; \
988 \
989 status = strict_strtoul(buf, 0, &val); \
990 if (status) \
991 return status; \
992 \
993 if (!valid_threshold(mcbsp, val)) \
994 return -EDOM; \
995 \
996 mcbsp->prop = val; \
997 return size; \
998} \
999 \
1000static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
1001
1002THRESHOLD_PROP_BUILDER(max_tx_thres);
1003THRESHOLD_PROP_BUILDER(max_rx_thres);
1004
Jarkko Nikula9b300502009-08-24 17:45:50 +03001005static const char *dma_op_modes[] = {
1006 "element", "threshold", "frame",
1007};
1008
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001009static ssize_t dma_op_mode_show(struct device *dev,
1010 struct device_attribute *attr, char *buf)
1011{
1012 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001013 int dma_op_mode, i = 0;
1014 ssize_t len = 0;
1015 const char * const *s;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001016
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001017 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001018
Jarkko Nikula9b300502009-08-24 17:45:50 +03001019 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
1020 if (dma_op_mode == i)
1021 len += sprintf(buf + len, "[%s] ", *s);
1022 else
1023 len += sprintf(buf + len, "%s ", *s);
1024 }
1025 len += sprintf(buf + len, "\n");
1026
1027 return len;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001028}
1029
1030static ssize_t dma_op_mode_store(struct device *dev,
1031 struct device_attribute *attr,
1032 const char *buf, size_t size)
1033{
1034 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001035 const char * const *s;
1036 int i = 0;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001037
Jarkko Nikula9b300502009-08-24 17:45:50 +03001038 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
1039 if (sysfs_streq(buf, *s))
1040 break;
1041
1042 if (i == ARRAY_SIZE(dma_op_modes))
1043 return -EINVAL;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001044
1045 spin_lock_irq(&mcbsp->lock);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001046 if (!mcbsp->free) {
1047 size = -EBUSY;
1048 goto unlock;
1049 }
Jarkko Nikula9b300502009-08-24 17:45:50 +03001050 mcbsp->dma_op_mode = i;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001051
1052unlock:
1053 spin_unlock_irq(&mcbsp->lock);
1054
1055 return size;
1056}
1057
1058static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
1059
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001060static ssize_t st_taps_show(struct device *dev,
1061 struct device_attribute *attr, char *buf)
1062{
1063 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1064 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1065 ssize_t status = 0;
1066 int i;
1067
1068 spin_lock_irq(&mcbsp->lock);
1069 for (i = 0; i < st_data->nr_taps; i++)
1070 status += sprintf(&buf[status], (i ? ", %d" : "%d"),
1071 st_data->taps[i]);
1072 if (i)
1073 status += sprintf(&buf[status], "\n");
1074 spin_unlock_irq(&mcbsp->lock);
1075
1076 return status;
1077}
1078
1079static ssize_t st_taps_store(struct device *dev,
1080 struct device_attribute *attr,
1081 const char *buf, size_t size)
1082{
1083 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1084 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1085 int val, tmp, status, i = 0;
1086
1087 spin_lock_irq(&mcbsp->lock);
1088 memset(st_data->taps, 0, sizeof(st_data->taps));
1089 st_data->nr_taps = 0;
1090
1091 do {
1092 status = sscanf(buf, "%d%n", &val, &tmp);
1093 if (status < 0 || status == 0) {
1094 size = -EINVAL;
1095 goto out;
1096 }
1097 if (val < -32768 || val > 32767) {
1098 size = -EINVAL;
1099 goto out;
1100 }
1101 st_data->taps[i++] = val;
1102 buf += tmp;
1103 if (*buf != ',')
1104 break;
1105 buf++;
1106 } while (1);
1107
1108 st_data->nr_taps = i;
1109
1110out:
1111 spin_unlock_irq(&mcbsp->lock);
1112
1113 return size;
1114}
1115
1116static DEVICE_ATTR(st_taps, 0644, st_taps_show, st_taps_store);
1117
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001118static const struct attribute *additional_attrs[] = {
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001119 &dev_attr_max_tx_thres.attr,
1120 &dev_attr_max_rx_thres.attr,
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001121 &dev_attr_dma_op_mode.attr,
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001122 NULL,
1123};
1124
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001125static const struct attribute_group additional_attr_group = {
1126 .attrs = (struct attribute **)additional_attrs,
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001127};
1128
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001129static inline int __devinit omap_additional_add(struct device *dev)
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001130{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001131 return sysfs_create_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001132}
1133
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001134static inline void __devexit omap_additional_remove(struct device *dev)
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001135{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001136 sysfs_remove_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001137}
1138
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001139static const struct attribute *sidetone_attrs[] = {
1140 &dev_attr_st_taps.attr,
1141 NULL,
1142};
1143
1144static const struct attribute_group sidetone_attr_group = {
1145 .attrs = (struct attribute **)sidetone_attrs,
1146};
1147
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -07001148static int __devinit omap_st_add(struct omap_mcbsp *mcbsp)
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001149{
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001150 struct platform_device *pdev;
1151 struct resource *res;
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001152 struct omap_mcbsp_st_data *st_data;
1153 int err;
1154
1155 st_data = kzalloc(sizeof(*mcbsp->st_data), GFP_KERNEL);
1156 if (!st_data) {
1157 err = -ENOMEM;
1158 goto err1;
1159 }
1160
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001161 pdev = container_of(mcbsp->dev, struct platform_device, dev);
1162
1163 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
1164 st_data->io_base_st = ioremap(res->start, resource_size(res));
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001165 if (!st_data->io_base_st) {
1166 err = -ENOMEM;
1167 goto err2;
1168 }
1169
1170 err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
1171 if (err)
1172 goto err3;
1173
1174 mcbsp->st_data = st_data;
1175 return 0;
1176
1177err3:
1178 iounmap(st_data->io_base_st);
1179err2:
1180 kfree(st_data);
1181err1:
1182 return err;
1183
1184}
1185
1186static void __devexit omap_st_remove(struct omap_mcbsp *mcbsp)
1187{
1188 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1189
1190 if (st_data) {
1191 sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
1192 iounmap(st_data->io_base_st);
1193 kfree(st_data);
1194 }
1195}
1196
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001197static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
1198{
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001199 mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001200 if (cpu_is_omap34xx()) {
Peter Ujfalusi451fd822010-06-03 07:39:33 +03001201 /*
1202 * Initially configure the maximum thresholds to a safe value.
1203 * The McBSP FIFO usage with these values should not go under
1204 * 16 locations.
1205 * If the whole FIFO without safety buffer is used, than there
1206 * is a possibility that the DMA will be not able to push the
1207 * new data on time, causing channel shifts in runtime.
1208 */
1209 mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
1210 mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001211 /*
1212 * REVISIT: Set dmap_op_mode to THRESHOLD as default
1213 * for mcbsp2 instances.
1214 */
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001215 if (omap_additional_add(mcbsp->dev))
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001216 dev_warn(mcbsp->dev,
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001217 "Unable to create additional controls\n");
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001218
1219 if (mcbsp->id == 2 || mcbsp->id == 3)
1220 if (omap_st_add(mcbsp))
1221 dev_warn(mcbsp->dev,
1222 "Unable to create sidetone controls\n");
1223
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001224 } else {
1225 mcbsp->max_tx_thres = -EINVAL;
1226 mcbsp->max_rx_thres = -EINVAL;
1227 }
1228}
1229
1230static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
1231{
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001232 if (cpu_is_omap34xx()) {
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001233 omap_additional_remove(mcbsp->dev);
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001234
1235 if (mcbsp->id == 2 || mcbsp->id == 3)
1236 omap_st_remove(mcbsp);
1237 }
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001238}
1239#else
1240static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
1241static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001242#endif /* CONFIG_ARCH_OMAP3 */
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001243
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001244/*
1245 * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
1246 * 730 has only 2 McBSP, and both of them are MPU peripherals.
1247 */
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001248static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001249{
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001250 struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001251 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001252 int id = pdev->id - 1;
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001253 struct resource *res;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001254 int ret = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001255
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001256 if (!pdata) {
1257 dev_err(&pdev->dev, "McBSP device initialized without"
1258 "platform data\n");
1259 ret = -EINVAL;
1260 goto exit;
1261 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001262
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001263 dev_dbg(&pdev->dev, "Initializing OMAP McBSP (%d).\n", pdev->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001264
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001265 if (id >= omap_mcbsp_count) {
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001266 dev_err(&pdev->dev, "Invalid McBSP device id (%d)\n", id);
1267 ret = -EINVAL;
1268 goto exit;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001269 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001270
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001271 mcbsp = kzalloc(sizeof(struct omap_mcbsp), GFP_KERNEL);
1272 if (!mcbsp) {
1273 ret = -ENOMEM;
1274 goto exit;
1275 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001276
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001277 spin_lock_init(&mcbsp->lock);
1278 mcbsp->id = id + 1;
Shubhrajyoti D6722a722010-12-07 16:25:41 -08001279 mcbsp->free = true;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001280
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001281 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
1282 if (!res) {
1283 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1284 if (!res) {
1285 dev_err(&pdev->dev, "%s:mcbsp%d has invalid memory"
1286 "resource\n", __func__, pdev->id);
1287 ret = -ENOMEM;
1288 goto exit;
1289 }
1290 }
1291 mcbsp->phys_base = res->start;
1292 omap_mcbsp_cache_size = resource_size(res);
1293 mcbsp->io_base = ioremap(res->start, resource_size(res));
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001294 if (!mcbsp->io_base) {
Russell Kingd592dd12008-09-04 14:25:42 +01001295 ret = -ENOMEM;
1296 goto err_ioremap;
1297 }
1298
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001299 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
1300 if (!res)
1301 mcbsp->phys_dma_base = mcbsp->phys_base;
1302 else
1303 mcbsp->phys_dma_base = res->start;
1304
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001305 mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
1306 mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
1307
Kishon Vijay Abraham Icb7e9de2011-02-24 15:16:50 +05301308 /* From OMAP4 there will be a single irq line */
1309 if (mcbsp->tx_irq == -ENXIO)
1310 mcbsp->tx_irq = platform_get_irq(pdev, 0);
1311
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001312 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
1313 if (!res) {
1314 dev_err(&pdev->dev, "%s:mcbsp%d has invalid rx DMA channel\n",
1315 __func__, pdev->id);
1316 ret = -ENODEV;
1317 goto err_res;
1318 }
1319 mcbsp->dma_rx_sync = res->start;
1320
1321 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
1322 if (!res) {
1323 dev_err(&pdev->dev, "%s:mcbsp%d has invalid tx DMA channel\n",
1324 __func__, pdev->id);
1325 ret = -ENODEV;
1326 goto err_res;
1327 }
1328 mcbsp->dma_tx_sync = res->start;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001329
Russell Kingb820ce42009-01-23 10:26:46 +00001330 mcbsp->fclk = clk_get(&pdev->dev, "fck");
1331 if (IS_ERR(mcbsp->fclk)) {
1332 ret = PTR_ERR(mcbsp->fclk);
1333 dev_err(&pdev->dev, "unable to get fck: %d\n", ret);
Kishon Vijay Abraham Ie95496d2011-02-24 15:16:54 +05301334 goto err_res;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001335 }
1336
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001337 mcbsp->pdata = pdata;
1338 mcbsp->dev = &pdev->dev;
Russell Kingb820ce42009-01-23 10:26:46 +00001339 mcbsp_ptr[id] = mcbsp;
Kishon Vijay Abraham I9504ba62011-02-24 15:16:55 +05301340 mcbsp->mcbsp_config_type = pdata->mcbsp_config_type;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001341 platform_set_drvdata(pdev, mcbsp);
Kishon Vijay Abraham Ie95496d2011-02-24 15:16:54 +05301342 pm_runtime_enable(mcbsp->dev);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001343
1344 /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
1345 omap34xx_device_init(mcbsp);
1346
Russell Kingd592dd12008-09-04 14:25:42 +01001347 return 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001348
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001349err_res:
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001350 iounmap(mcbsp->io_base);
Russell Kingd592dd12008-09-04 14:25:42 +01001351err_ioremap:
Russell Kingb820ce42009-01-23 10:26:46 +00001352 kfree(mcbsp);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001353exit:
1354 return ret;
1355}
1356
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001357static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001358{
1359 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
1360
1361 platform_set_drvdata(pdev, NULL);
1362 if (mcbsp) {
1363
1364 if (mcbsp->pdata && mcbsp->pdata->ops &&
1365 mcbsp->pdata->ops->free)
1366 mcbsp->pdata->ops->free(mcbsp->id);
1367
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001368 omap34xx_device_exit(mcbsp);
1369
Russell Kingb820ce42009-01-23 10:26:46 +00001370 clk_put(mcbsp->fclk);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001371
Russell Kingd592dd12008-09-04 14:25:42 +01001372 iounmap(mcbsp->io_base);
Jarkko Nikula5f3b7282010-12-07 16:25:40 -08001373 kfree(mcbsp);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001374 }
1375
1376 return 0;
1377}
1378
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001379static struct platform_driver omap_mcbsp_driver = {
1380 .probe = omap_mcbsp_probe,
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001381 .remove = __devexit_p(omap_mcbsp_remove),
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001382 .driver = {
1383 .name = "omap-mcbsp",
1384 },
1385};
1386
1387int __init omap_mcbsp_init(void)
1388{
1389 /* Register the McBSP driver */
1390 return platform_driver_register(&omap_mcbsp_driver);
1391}