blob: 5e6d3096c7253265c87a6ead2704111db210f1cf [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/wait.h>
20#include <linux/completion.h>
21#include <linux/interrupt.h>
22#include <linux/err.h>
Russell Kingf8ce2542006-01-07 16:15:52 +000023#include <linux/clk.h>
Tony Lindgren04fbf6a2007-02-12 10:50:53 -080024#include <linux/delay.h>
Eduardo Valentinfb78d802008-07-03 12:24:39 +030025#include <linux/io.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010026
Tony Lindgrence491cf2009-10-20 09:40:47 -070027#include <plat/dma.h>
28#include <plat/mcbsp.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010029
Eero Nurkkalad912fa92010-02-22 12:21:11 +000030#include "../mach-omap2/cm-regbits-34xx.h"
31
Chandra Shekharb4b58f52008-10-08 10:01:39 +030032struct omap_mcbsp **mcbsp_ptr;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080033int omap_mcbsp_count, omap_mcbsp_cache_size;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +030034
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080035void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030036{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080037 if (cpu_class_is_omap1()) {
38 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)] = (u16)val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080039 __raw_writew((u16)val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080040 } else if (cpu_is_omap2420()) {
41 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)] = (u16)val;
42 __raw_writew((u16)val, mcbsp->io_base + reg);
43 } else {
44 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)] = val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080045 __raw_writel(val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080046 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030047}
48
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080049int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030050{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080051 if (cpu_class_is_omap1()) {
52 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
53 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)];
54 } else if (cpu_is_omap2420()) {
55 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
56 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)];
57 } else {
58 return !from_cache ? __raw_readl(mcbsp->io_base + reg) :
59 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)];
60 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030061}
62
Eero Nurkkalad912fa92010-02-22 12:21:11 +000063#ifdef CONFIG_ARCH_OMAP3
64void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
65{
66 __raw_writel(val, mcbsp->st_data->io_base_st + reg);
67}
68
69int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
70{
71 return __raw_readl(mcbsp->st_data->io_base_st + reg);
72}
73#endif
74
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080075#define MCBSP_READ(mcbsp, reg) \
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080076 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080077#define MCBSP_WRITE(mcbsp, reg, val) \
78 omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080079#define MCBSP_READ_CACHE(mcbsp, reg) \
80 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030081
82#define omap_mcbsp_check_valid_id(id) (id < omap_mcbsp_count)
83#define id_to_mcbsp_ptr(id) mcbsp_ptr[id];
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010084
Eero Nurkkalad912fa92010-02-22 12:21:11 +000085#define MCBSP_ST_READ(mcbsp, reg) \
86 omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
87#define MCBSP_ST_WRITE(mcbsp, reg, val) \
88 omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
89
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010090static void omap_mcbsp_dump_reg(u8 id)
91{
Chandra Shekharb4b58f52008-10-08 10:01:39 +030092 struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id);
93
94 dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
95 dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080096 MCBSP_READ(mcbsp, DRR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030097 dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080098 MCBSP_READ(mcbsp, DRR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030099 dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800100 MCBSP_READ(mcbsp, DXR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300101 dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800102 MCBSP_READ(mcbsp, DXR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300103 dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800104 MCBSP_READ(mcbsp, SPCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300105 dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800106 MCBSP_READ(mcbsp, SPCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300107 dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800108 MCBSP_READ(mcbsp, RCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300109 dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800110 MCBSP_READ(mcbsp, RCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300111 dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800112 MCBSP_READ(mcbsp, XCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300113 dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800114 MCBSP_READ(mcbsp, XCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300115 dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800116 MCBSP_READ(mcbsp, SRGR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300117 dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800118 MCBSP_READ(mcbsp, SRGR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300119 dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800120 MCBSP_READ(mcbsp, PCR0));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300121 dev_dbg(mcbsp->dev, "***********************\n");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100122}
123
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700124static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100125{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400126 struct omap_mcbsp *mcbsp_tx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700127 u16 irqst_spcr2;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100128
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800129 irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700130 dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100131
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700132 if (irqst_spcr2 & XSYNC_ERR) {
133 dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n",
134 irqst_spcr2);
135 /* Writing zero to XSYNC_ERR clears the IRQ */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800136 MCBSP_WRITE(mcbsp_tx, SPCR2,
137 MCBSP_READ_CACHE(mcbsp_tx, SPCR2) & ~(XSYNC_ERR));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700138 } else {
139 complete(&mcbsp_tx->tx_irq_completion);
140 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300141
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100142 return IRQ_HANDLED;
143}
144
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700145static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100146{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400147 struct omap_mcbsp *mcbsp_rx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700148 u16 irqst_spcr1;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100149
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800150 irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700151 dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100152
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700153 if (irqst_spcr1 & RSYNC_ERR) {
154 dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n",
155 irqst_spcr1);
156 /* Writing zero to RSYNC_ERR clears the IRQ */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800157 MCBSP_WRITE(mcbsp_rx, SPCR1,
158 MCBSP_READ_CACHE(mcbsp_rx, SPCR1) & ~(RSYNC_ERR));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700159 } else {
160 complete(&mcbsp_rx->tx_irq_completion);
161 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300162
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100163 return IRQ_HANDLED;
164}
165
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100166static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data)
167{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400168 struct omap_mcbsp *mcbsp_dma_tx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100169
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300170 dev_dbg(mcbsp_dma_tx->dev, "TX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800171 MCBSP_READ(mcbsp_dma_tx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100172
173 /* We can free the channels */
174 omap_free_dma(mcbsp_dma_tx->dma_tx_lch);
175 mcbsp_dma_tx->dma_tx_lch = -1;
176
177 complete(&mcbsp_dma_tx->tx_dma_completion);
178}
179
180static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data)
181{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400182 struct omap_mcbsp *mcbsp_dma_rx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100183
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300184 dev_dbg(mcbsp_dma_rx->dev, "RX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800185 MCBSP_READ(mcbsp_dma_rx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100186
187 /* We can free the channels */
188 omap_free_dma(mcbsp_dma_rx->dma_rx_lch);
189 mcbsp_dma_rx->dma_rx_lch = -1;
190
191 complete(&mcbsp_dma_rx->rx_dma_completion);
192}
193
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100194/*
195 * omap_mcbsp_config simply write a config to the
196 * appropriate McBSP.
197 * You either call this function or set the McBSP registers
198 * by yourself before calling omap_mcbsp_start().
199 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300200void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100201{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300202 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100203
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300204 if (!omap_mcbsp_check_valid_id(id)) {
205 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
206 return;
207 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300208 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300209
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300210 dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
211 mcbsp->id, mcbsp->phys_base);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100212
213 /* We write the given config */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800214 MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
215 MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
216 MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
217 MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
218 MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
219 MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
220 MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
221 MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
222 MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
223 MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
224 MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
Syed Rafiuddina5b92cc2009-07-28 18:57:10 +0530225 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800226 MCBSP_WRITE(mcbsp, XCCR, config->xccr);
227 MCBSP_WRITE(mcbsp, RCCR, config->rccr);
Tony Lindgren3127f8f2009-01-15 13:09:54 +0200228 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100229}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300230EXPORT_SYMBOL(omap_mcbsp_config);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100231
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -0800232#ifdef CONFIG_ARCH_OMAP3
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000233static void omap_st_on(struct omap_mcbsp *mcbsp)
234{
235 unsigned int w;
236
237 /*
238 * Sidetone uses McBSP ICLK - which must not idle when sidetones
239 * are enabled or sidetones start sounding ugly.
240 */
241 w = cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
242 w &= ~(1 << (mcbsp->id - 2));
243 cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
244
245 /* Enable McBSP Sidetone */
246 w = MCBSP_READ(mcbsp, SSELCR);
247 MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
248
249 w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
250 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
251
252 /* Enable Sidetone from Sidetone Core */
253 w = MCBSP_ST_READ(mcbsp, SSELCR);
254 MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
255}
256
257static void omap_st_off(struct omap_mcbsp *mcbsp)
258{
259 unsigned int w;
260
261 w = MCBSP_ST_READ(mcbsp, SSELCR);
262 MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
263
264 w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
265 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
266
267 w = MCBSP_READ(mcbsp, SSELCR);
268 MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
269
270 w = cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
271 w |= 1 << (mcbsp->id - 2);
272 cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
273}
274
275static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
276{
277 u16 val, i;
278
279 val = MCBSP_ST_READ(mcbsp, SYSCONFIG);
280 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, val & ~(ST_AUTOIDLE));
281
282 val = MCBSP_ST_READ(mcbsp, SSELCR);
283
284 if (val & ST_COEFFWREN)
285 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
286
287 MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
288
289 for (i = 0; i < 128; i++)
290 MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
291
292 i = 0;
293
294 val = MCBSP_ST_READ(mcbsp, SSELCR);
295 while (!(val & ST_COEFFWRDONE) && (++i < 1000))
296 val = MCBSP_ST_READ(mcbsp, SSELCR);
297
298 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
299
300 if (i == 1000)
301 dev_err(mcbsp->dev, "McBSP FIR load error!\n");
302}
303
304static void omap_st_chgain(struct omap_mcbsp *mcbsp)
305{
306 u16 w;
307 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
308
309 w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
310 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE));
311
312 w = MCBSP_ST_READ(mcbsp, SSELCR);
313
314 MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \
315 ST_CH1GAIN(st_data->ch1gain));
316}
317
318int omap_st_set_chgain(unsigned int id, int channel, s16 chgain)
319{
320 struct omap_mcbsp *mcbsp;
321 struct omap_mcbsp_st_data *st_data;
322 int ret = 0;
323
324 if (!omap_mcbsp_check_valid_id(id)) {
325 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
326 return -ENODEV;
327 }
328
329 mcbsp = id_to_mcbsp_ptr(id);
330 st_data = mcbsp->st_data;
331
332 if (!st_data)
333 return -ENOENT;
334
335 spin_lock_irq(&mcbsp->lock);
336 if (channel == 0)
337 st_data->ch0gain = chgain;
338 else if (channel == 1)
339 st_data->ch1gain = chgain;
340 else
341 ret = -EINVAL;
342
343 if (st_data->enabled)
344 omap_st_chgain(mcbsp);
345 spin_unlock_irq(&mcbsp->lock);
346
347 return ret;
348}
349EXPORT_SYMBOL(omap_st_set_chgain);
350
351int omap_st_get_chgain(unsigned int id, int channel, s16 *chgain)
352{
353 struct omap_mcbsp *mcbsp;
354 struct omap_mcbsp_st_data *st_data;
355 int ret = 0;
356
357 if (!omap_mcbsp_check_valid_id(id)) {
358 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
359 return -ENODEV;
360 }
361
362 mcbsp = id_to_mcbsp_ptr(id);
363 st_data = mcbsp->st_data;
364
365 if (!st_data)
366 return -ENOENT;
367
368 spin_lock_irq(&mcbsp->lock);
369 if (channel == 0)
370 *chgain = st_data->ch0gain;
371 else if (channel == 1)
372 *chgain = st_data->ch1gain;
373 else
374 ret = -EINVAL;
375 spin_unlock_irq(&mcbsp->lock);
376
377 return ret;
378}
379EXPORT_SYMBOL(omap_st_get_chgain);
380
381static int omap_st_start(struct omap_mcbsp *mcbsp)
382{
383 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
384
385 if (st_data && st_data->enabled && !st_data->running) {
386 omap_st_fir_write(mcbsp, st_data->taps);
387 omap_st_chgain(mcbsp);
388
389 if (!mcbsp->free) {
390 omap_st_on(mcbsp);
391 st_data->running = 1;
392 }
393 }
394
395 return 0;
396}
397
398int omap_st_enable(unsigned int id)
399{
400 struct omap_mcbsp *mcbsp;
401 struct omap_mcbsp_st_data *st_data;
402
403 if (!omap_mcbsp_check_valid_id(id)) {
404 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
405 return -ENODEV;
406 }
407
408 mcbsp = id_to_mcbsp_ptr(id);
409 st_data = mcbsp->st_data;
410
411 if (!st_data)
412 return -ENODEV;
413
414 spin_lock_irq(&mcbsp->lock);
415 st_data->enabled = 1;
416 omap_st_start(mcbsp);
417 spin_unlock_irq(&mcbsp->lock);
418
419 return 0;
420}
421EXPORT_SYMBOL(omap_st_enable);
422
423static int omap_st_stop(struct omap_mcbsp *mcbsp)
424{
425 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
426
427 if (st_data && st_data->running) {
428 if (!mcbsp->free) {
429 omap_st_off(mcbsp);
430 st_data->running = 0;
431 }
432 }
433
434 return 0;
435}
436
437int omap_st_disable(unsigned int id)
438{
439 struct omap_mcbsp *mcbsp;
440 struct omap_mcbsp_st_data *st_data;
441 int ret = 0;
442
443 if (!omap_mcbsp_check_valid_id(id)) {
444 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
445 return -ENODEV;
446 }
447
448 mcbsp = id_to_mcbsp_ptr(id);
449 st_data = mcbsp->st_data;
450
451 if (!st_data)
452 return -ENODEV;
453
454 spin_lock_irq(&mcbsp->lock);
455 omap_st_stop(mcbsp);
456 st_data->enabled = 0;
457 spin_unlock_irq(&mcbsp->lock);
458
459 return ret;
460}
461EXPORT_SYMBOL(omap_st_disable);
462
463int omap_st_is_enabled(unsigned int id)
464{
465 struct omap_mcbsp *mcbsp;
466 struct omap_mcbsp_st_data *st_data;
467
468 if (!omap_mcbsp_check_valid_id(id)) {
469 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
470 return -ENODEV;
471 }
472
473 mcbsp = id_to_mcbsp_ptr(id);
474 st_data = mcbsp->st_data;
475
476 if (!st_data)
477 return -ENODEV;
478
479
480 return st_data->enabled;
481}
482EXPORT_SYMBOL(omap_st_is_enabled);
483
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300484/*
485 * omap_mcbsp_set_tx_threshold configures how to deal
486 * with transmit threshold. the threshold value and handler can be
487 * configure in here.
488 */
489void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
490{
491 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300492
493 if (!cpu_is_omap34xx())
494 return;
495
496 if (!omap_mcbsp_check_valid_id(id)) {
497 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
498 return;
499 }
500 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300501
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800502 MCBSP_WRITE(mcbsp, THRSH2, threshold);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300503}
504EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
505
506/*
507 * omap_mcbsp_set_rx_threshold configures how to deal
508 * with receive threshold. the threshold value and handler can be
509 * configure in here.
510 */
511void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
512{
513 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300514
515 if (!cpu_is_omap34xx())
516 return;
517
518 if (!omap_mcbsp_check_valid_id(id)) {
519 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
520 return;
521 }
522 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300523
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800524 MCBSP_WRITE(mcbsp, THRSH1, threshold);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300525}
526EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
Eduardo Valentina1a56f52009-08-20 16:18:11 +0300527
528/*
529 * omap_mcbsp_get_max_tx_thres just return the current configured
530 * maximum threshold for transmission
531 */
532u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
533{
534 struct omap_mcbsp *mcbsp;
535
536 if (!omap_mcbsp_check_valid_id(id)) {
537 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
538 return -ENODEV;
539 }
540 mcbsp = id_to_mcbsp_ptr(id);
541
542 return mcbsp->max_tx_thres;
543}
544EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
545
546/*
547 * omap_mcbsp_get_max_rx_thres just return the current configured
548 * maximum threshold for reception
549 */
550u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
551{
552 struct omap_mcbsp *mcbsp;
553
554 if (!omap_mcbsp_check_valid_id(id)) {
555 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
556 return -ENODEV;
557 }
558 mcbsp = id_to_mcbsp_ptr(id);
559
560 return mcbsp->max_rx_thres;
561}
562EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300563
Peter Ujfalusi7dc976e2010-03-03 15:08:08 +0200564#define MCBSP2_FIFO_SIZE 0x500 /* 1024 + 256 locations */
565#define MCBSP1345_FIFO_SIZE 0x80 /* 128 locations */
566/*
567 * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
568 */
569u16 omap_mcbsp_get_tx_delay(unsigned int id)
570{
571 struct omap_mcbsp *mcbsp;
572 u16 buffstat;
573
574 if (!omap_mcbsp_check_valid_id(id)) {
575 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
576 return -ENODEV;
577 }
578 mcbsp = id_to_mcbsp_ptr(id);
579
580 /* Returns the number of free locations in the buffer */
581 buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
582
583 /* Number of slots are different in McBSP ports */
584 if (mcbsp->id == 2)
585 return MCBSP2_FIFO_SIZE - buffstat;
586 else
587 return MCBSP1345_FIFO_SIZE - buffstat;
588}
589EXPORT_SYMBOL(omap_mcbsp_get_tx_delay);
590
591/*
592 * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
593 * to reach the threshold value (when the DMA will be triggered to read it)
594 */
595u16 omap_mcbsp_get_rx_delay(unsigned int id)
596{
597 struct omap_mcbsp *mcbsp;
598 u16 buffstat, threshold;
599
600 if (!omap_mcbsp_check_valid_id(id)) {
601 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
602 return -ENODEV;
603 }
604 mcbsp = id_to_mcbsp_ptr(id);
605
606 /* Returns the number of used locations in the buffer */
607 buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
608 /* RX threshold */
609 threshold = MCBSP_READ(mcbsp, THRSH1);
610
611 /* Return the number of location till we reach the threshold limit */
612 if (threshold <= buffstat)
613 return 0;
614 else
615 return threshold - buffstat;
616}
617EXPORT_SYMBOL(omap_mcbsp_get_rx_delay);
618
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300619/*
620 * omap_mcbsp_get_dma_op_mode just return the current configured
621 * operating mode for the mcbsp channel
622 */
623int omap_mcbsp_get_dma_op_mode(unsigned int id)
624{
625 struct omap_mcbsp *mcbsp;
626 int dma_op_mode;
627
628 if (!omap_mcbsp_check_valid_id(id)) {
629 printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
630 return -ENODEV;
631 }
632 mcbsp = id_to_mcbsp_ptr(id);
633
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300634 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300635
636 return dma_op_mode;
637}
638EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300639
640static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
641{
642 /*
643 * Enable wakup behavior, smart idle and all wakeups
644 * REVISIT: some wakeups may be unnecessary
645 */
646 if (cpu_is_omap34xx()) {
647 u16 syscon;
648
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800649 syscon = MCBSP_READ(mcbsp, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300650 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eduardo Valentind99a7452009-08-20 16:18:18 +0300651
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300652 if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
653 syscon |= (ENAWAKEUP | SIDLEMODE(0x02) |
654 CLOCKACTIVITY(0x02));
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800655 MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300656 } else {
Eduardo Valentind99a7452009-08-20 16:18:18 +0300657 syscon |= SIDLEMODE(0x01);
Eero Nurkkalafa3935b2009-08-20 16:18:19 +0300658 }
Eduardo Valentind99a7452009-08-20 16:18:18 +0300659
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800660 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300661 }
662}
663
664static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
665{
666 /*
667 * Disable wakup behavior, smart idle and all wakeups
668 */
669 if (cpu_is_omap34xx()) {
670 u16 syscon;
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300671
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800672 syscon = MCBSP_READ(mcbsp, SYSCON);
Eero Nurkkala2ba93f82009-08-20 16:18:17 +0300673 syscon &= ~(ENAWAKEUP | SIDLEMODE(0x03) | CLOCKACTIVITY(0x03));
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300674 /*
675 * HW bug workaround - If no_idle mode is taken, we need to
676 * go to smart_idle before going to always_idle, or the
677 * device will not hit retention anymore.
678 */
679 syscon |= SIDLEMODE(0x02);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800680 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300681
682 syscon &= ~(SIDLEMODE(0x03));
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800683 MCBSP_WRITE(mcbsp, SYSCON, syscon);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300684
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800685 MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300686 }
687}
688#else
689static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
690static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000691static inline void omap_st_start(struct omap_mcbsp *mcbsp) {}
692static inline void omap_st_stop(struct omap_mcbsp *mcbsp) {}
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300693#endif
694
Tony Lindgren120db2c2006-04-02 17:46:27 +0100695/*
696 * We can choose between IRQ based or polled IO.
697 * This needs to be called before omap_mcbsp_request().
698 */
699int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type)
700{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300701 struct omap_mcbsp *mcbsp;
702
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300703 if (!omap_mcbsp_check_valid_id(id)) {
704 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
705 return -ENODEV;
706 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300707 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100708
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300709 spin_lock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100710
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300711 if (!mcbsp->free) {
712 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
713 mcbsp->id);
714 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100715 return -EINVAL;
716 }
717
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300718 mcbsp->io_type = io_type;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100719
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300720 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100721
722 return 0;
723}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300724EXPORT_SYMBOL(omap_mcbsp_set_io_type);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100725
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100726int omap_mcbsp_request(unsigned int id)
727{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300728 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800729 void *reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100730 int err;
731
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300732 if (!omap_mcbsp_check_valid_id(id)) {
733 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
734 return -ENODEV;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100735 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300736 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300737
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800738 reg_cache = kzalloc(omap_mcbsp_cache_size, GFP_KERNEL);
739 if (!reg_cache) {
740 return -ENOMEM;
741 }
742
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300743 spin_lock(&mcbsp->lock);
744 if (!mcbsp->free) {
745 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
746 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800747 err = -EBUSY;
748 goto err_kfree;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100749 }
750
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300751 mcbsp->free = 0;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800752 mcbsp->reg_cache = reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300753 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100754
Russell Kingb820ce42009-01-23 10:26:46 +0000755 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
756 mcbsp->pdata->ops->request(id);
757
758 clk_enable(mcbsp->iclk);
759 clk_enable(mcbsp->fclk);
760
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300761 /* Do procedure specific to omap34xx arch, if applicable */
762 omap34xx_mcbsp_request(mcbsp);
763
Jarkko Nikula5a070552008-10-08 10:01:41 +0300764 /*
765 * Make sure that transmitter, receiver and sample-rate generator are
766 * not running before activating IRQs.
767 */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800768 MCBSP_WRITE(mcbsp, SPCR1, 0);
769 MCBSP_WRITE(mcbsp, SPCR2, 0);
Jarkko Nikula5a070552008-10-08 10:01:41 +0300770
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300771 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
Tony Lindgren120db2c2006-04-02 17:46:27 +0100772 /* We need to get IRQs here */
Jarkko Nikula5a070552008-10-08 10:01:41 +0300773 init_completion(&mcbsp->tx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300774 err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
775 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100776 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300777 dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
778 "for McBSP%d\n", mcbsp->tx_irq,
779 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800780 goto err_clk_disable;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100781 }
782
Jarkko Nikula5a070552008-10-08 10:01:41 +0300783 init_completion(&mcbsp->rx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300784 err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler,
785 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100786 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300787 dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
788 "for McBSP%d\n", mcbsp->rx_irq,
789 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800790 goto err_free_irq;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100791 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100792 }
793
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100794 return 0;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800795err_free_irq:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800796 free_irq(mcbsp->tx_irq, (void *)mcbsp);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800797err_clk_disable:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800798 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800799 mcbsp->pdata->ops->free(id);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800800
801 /* Do procedure specific to omap34xx arch, if applicable */
802 omap34xx_mcbsp_free(mcbsp);
803
804 clk_disable(mcbsp->fclk);
805 clk_disable(mcbsp->iclk);
806
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800807 spin_lock(&mcbsp->lock);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800808 mcbsp->free = 1;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800809 mcbsp->reg_cache = NULL;
810err_kfree:
811 spin_unlock(&mcbsp->lock);
812 kfree(reg_cache);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800813
814 return err;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100815}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300816EXPORT_SYMBOL(omap_mcbsp_request);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100817
818void omap_mcbsp_free(unsigned int id)
819{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300820 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800821 void *reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300822
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300823 if (!omap_mcbsp_check_valid_id(id)) {
824 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100825 return;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100826 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300827 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100828
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300829 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
830 mcbsp->pdata->ops->free(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300831
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300832 /* Do procedure specific to omap34xx arch, if applicable */
833 omap34xx_mcbsp_free(mcbsp);
834
Russell Kingb820ce42009-01-23 10:26:46 +0000835 clk_disable(mcbsp->fclk);
836 clk_disable(mcbsp->iclk);
837
838 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
839 /* Free IRQs */
840 free_irq(mcbsp->rx_irq, (void *)mcbsp);
841 free_irq(mcbsp->tx_irq, (void *)mcbsp);
842 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100843
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800844 reg_cache = mcbsp->reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100845
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800846 spin_lock(&mcbsp->lock);
847 if (mcbsp->free)
848 dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
849 else
850 mcbsp->free = 1;
851 mcbsp->reg_cache = NULL;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300852 spin_unlock(&mcbsp->lock);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800853
854 if (reg_cache)
855 kfree(reg_cache);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100856}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300857EXPORT_SYMBOL(omap_mcbsp_free);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100858
859/*
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300860 * Here we start the McBSP, by enabling transmitter, receiver or both.
861 * If no transmitter or receiver is active prior calling, then sample-rate
862 * generator and frame sync are started.
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100863 */
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300864void omap_mcbsp_start(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100865{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300866 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300867 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100868 u16 w;
869
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300870 if (!omap_mcbsp_check_valid_id(id)) {
871 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100872 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300873 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300874 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100875
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000876 if (cpu_is_omap34xx())
877 omap_st_start(mcbsp);
878
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800879 mcbsp->rx_word_length = (MCBSP_READ_CACHE(mcbsp, RCR1) >> 5) & 0x7;
880 mcbsp->tx_word_length = (MCBSP_READ_CACHE(mcbsp, XCR1) >> 5) & 0x7;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100881
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800882 idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
883 MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300884
885 if (idle) {
886 /* Start the sample generator */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800887 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800888 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300889 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100890
891 /* Enable transmitter and receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300892 tx &= 1;
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800893 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800894 MCBSP_WRITE(mcbsp, SPCR2, w | tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100895
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300896 rx &= 1;
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800897 w = MCBSP_READ_CACHE(mcbsp, SPCR1);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800898 MCBSP_WRITE(mcbsp, SPCR1, w | rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100899
Eduardo Valentin44a63112009-08-20 16:18:09 +0300900 /*
901 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
902 * REVISIT: 100us may give enough time for two CLKSRG, however
903 * due to some unknown PM related, clock gating etc. reason it
904 * is now at 500us.
905 */
906 udelay(500);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100907
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300908 if (idle) {
909 /* Start frame sync */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800910 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800911 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300912 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100913
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300914 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
915 /* Release the transmitter and receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800916 w = MCBSP_READ_CACHE(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300917 w &= ~(tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800918 MCBSP_WRITE(mcbsp, XCCR, w);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800919 w = MCBSP_READ_CACHE(mcbsp, RCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300920 w &= ~(rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800921 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300922 }
923
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100924 /* Dump McBSP Regs */
925 omap_mcbsp_dump_reg(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100926}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300927EXPORT_SYMBOL(omap_mcbsp_start);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100928
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300929void omap_mcbsp_stop(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100930{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300931 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300932 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100933 u16 w;
934
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300935 if (!omap_mcbsp_check_valid_id(id)) {
936 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100937 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300938 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100939
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300940 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100941
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300942 /* Reset transmitter */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300943 tx &= 1;
944 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800945 w = MCBSP_READ_CACHE(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300946 w |= (tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800947 MCBSP_WRITE(mcbsp, XCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300948 }
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800949 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800950 MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100951
952 /* Reset receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300953 rx &= 1;
954 if (cpu_is_omap2430() || cpu_is_omap34xx()) {
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800955 w = MCBSP_READ_CACHE(mcbsp, RCCR);
Jarkko Nikulaa93d4ed2009-10-14 09:56:35 -0700956 w |= (rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800957 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300958 }
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800959 w = MCBSP_READ_CACHE(mcbsp, SPCR1);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800960 MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100961
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800962 idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
963 MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300964
965 if (idle) {
966 /* Reset the sample rate generator */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800967 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800968 MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300969 }
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000970
971 if (cpu_is_omap34xx())
972 omap_st_stop(mcbsp);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100973}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300974EXPORT_SYMBOL(omap_mcbsp_stop);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100975
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100976/* polled mcbsp i/o operations */
977int omap_mcbsp_pollwrite(unsigned int id, u16 buf)
978{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300979 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300980
981 if (!omap_mcbsp_check_valid_id(id)) {
982 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
983 return -ENODEV;
984 }
985
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300986 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300987
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800988 MCBSP_WRITE(mcbsp, DXR1, buf);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100989 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800990 if (MCBSP_READ(mcbsp, SPCR2) & XSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100991 /* clear error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800992 MCBSP_WRITE(mcbsp, SPCR2,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800993 MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XSYNC_ERR));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100994 /* resend */
995 return -1;
996 } else {
997 /* wait for transmit confirmation */
998 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800999 while (!(MCBSP_READ(mcbsp, SPCR2) & XRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001000 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001001 MCBSP_WRITE(mcbsp, SPCR2,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001002 MCBSP_READ_CACHE(mcbsp, SPCR2) &
1003 (~XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001004 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001005 MCBSP_WRITE(mcbsp, SPCR2,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001006 MCBSP_READ_CACHE(mcbsp, SPCR2) |
1007 (XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001008 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001009 dev_err(mcbsp->dev, "Could not write to"
1010 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001011 return -2;
1012 }
1013 }
1014 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001015
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001016 return 0;
1017}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001018EXPORT_SYMBOL(omap_mcbsp_pollwrite);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001019
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001020int omap_mcbsp_pollread(unsigned int id, u16 *buf)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001021{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001022 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001023
1024 if (!omap_mcbsp_check_valid_id(id)) {
1025 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1026 return -ENODEV;
1027 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001028 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001029
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001030 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001031 if (MCBSP_READ(mcbsp, SPCR1) & RSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001032 /* clear error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001033 MCBSP_WRITE(mcbsp, SPCR1,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001034 MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RSYNC_ERR));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001035 /* resend */
1036 return -1;
1037 } else {
1038 /* wait for recieve confirmation */
1039 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001040 while (!(MCBSP_READ(mcbsp, SPCR1) & RRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001041 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001042 MCBSP_WRITE(mcbsp, SPCR1,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001043 MCBSP_READ_CACHE(mcbsp, SPCR1) &
1044 (~RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001045 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001046 MCBSP_WRITE(mcbsp, SPCR1,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001047 MCBSP_READ_CACHE(mcbsp, SPCR1) |
1048 (RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001049 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001050 dev_err(mcbsp->dev, "Could not read from"
1051 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001052 return -2;
1053 }
1054 }
1055 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001056 *buf = MCBSP_READ(mcbsp, DRR1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001057
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001058 return 0;
1059}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001060EXPORT_SYMBOL(omap_mcbsp_pollread);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001061
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001062/*
1063 * IRQ based word transmission.
1064 */
1065void omap_mcbsp_xmit_word(unsigned int id, u32 word)
1066{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001067 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001068 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001069
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001070 if (!omap_mcbsp_check_valid_id(id)) {
1071 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001072 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001073 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001074
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001075 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001076 word_length = mcbsp->tx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001077
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001078 wait_for_completion(&mcbsp->tx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001079
1080 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001081 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
1082 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001083}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001084EXPORT_SYMBOL(omap_mcbsp_xmit_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001085
1086u32 omap_mcbsp_recv_word(unsigned int id)
1087{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001088 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001089 u16 word_lsb, word_msb = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001090 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001091
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001092 if (!omap_mcbsp_check_valid_id(id)) {
1093 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1094 return -ENODEV;
1095 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001096 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001097
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001098 word_length = mcbsp->rx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001099
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001100 wait_for_completion(&mcbsp->rx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001101
1102 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001103 word_msb = MCBSP_READ(mcbsp, DRR2);
1104 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001105
1106 return (word_lsb | (word_msb << 16));
1107}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001108EXPORT_SYMBOL(omap_mcbsp_recv_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001109
Tony Lindgren120db2c2006-04-02 17:46:27 +01001110int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word)
1111{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001112 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001113 omap_mcbsp_word_length tx_word_length;
1114 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001115 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
1116
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001117 if (!omap_mcbsp_check_valid_id(id)) {
1118 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1119 return -ENODEV;
1120 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001121 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001122 tx_word_length = mcbsp->tx_word_length;
1123 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001124
Tony Lindgren120db2c2006-04-02 17:46:27 +01001125 if (tx_word_length != rx_word_length)
1126 return -EINVAL;
1127
1128 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001129 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001130 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001131 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001132 if (attempts++ > 1000) {
1133 /* We must reset the transmitter */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001134 MCBSP_WRITE(mcbsp, SPCR2,
1135 MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001136 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001137 MCBSP_WRITE(mcbsp, SPCR2,
1138 MCBSP_READ_CACHE(mcbsp, SPCR2) | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001139 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001140 dev_err(mcbsp->dev, "McBSP%d transmitter not "
1141 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001142 return -EAGAIN;
1143 }
1144 }
1145
1146 /* Now we can push the data */
1147 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001148 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
1149 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001150
1151 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001152 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001153 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001154 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001155 if (attempts++ > 1000) {
1156 /* We must reset the receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001157 MCBSP_WRITE(mcbsp, SPCR1,
1158 MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001159 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001160 MCBSP_WRITE(mcbsp, SPCR1,
1161 MCBSP_READ_CACHE(mcbsp, SPCR1) | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001162 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001163 dev_err(mcbsp->dev, "McBSP%d receiver not "
1164 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001165 return -EAGAIN;
1166 }
1167 }
1168
1169 /* Receiver is ready, let's read the dummy data */
1170 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001171 word_msb = MCBSP_READ(mcbsp, DRR2);
1172 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001173
1174 return 0;
1175}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001176EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001177
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001178int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 *word)
Tony Lindgren120db2c2006-04-02 17:46:27 +01001179{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001180 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +01001181 u32 clock_word = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001182 omap_mcbsp_word_length tx_word_length;
1183 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001184 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
1185
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001186 if (!omap_mcbsp_check_valid_id(id)) {
1187 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1188 return -ENODEV;
1189 }
1190
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001191 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001192
1193 tx_word_length = mcbsp->tx_word_length;
1194 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001195
Tony Lindgren120db2c2006-04-02 17:46:27 +01001196 if (tx_word_length != rx_word_length)
1197 return -EINVAL;
1198
1199 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001200 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001201 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001202 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001203 if (attempts++ > 1000) {
1204 /* We must reset the transmitter */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001205 MCBSP_WRITE(mcbsp, SPCR2,
1206 MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001207 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001208 MCBSP_WRITE(mcbsp, SPCR2,
1209 MCBSP_READ_CACHE(mcbsp, SPCR2) | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001210 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001211 dev_err(mcbsp->dev, "McBSP%d transmitter not "
1212 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001213 return -EAGAIN;
1214 }
1215 }
1216
1217 /* We first need to enable the bus clock */
1218 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001219 MCBSP_WRITE(mcbsp, DXR2, clock_word >> 16);
1220 MCBSP_WRITE(mcbsp, DXR1, clock_word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001221
1222 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001223 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001224 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001225 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001226 if (attempts++ > 1000) {
1227 /* We must reset the receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001228 MCBSP_WRITE(mcbsp, SPCR1,
1229 MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001230 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001231 MCBSP_WRITE(mcbsp, SPCR1,
1232 MCBSP_READ_CACHE(mcbsp, SPCR1) | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001233 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001234 dev_err(mcbsp->dev, "McBSP%d receiver not "
1235 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001236 return -EAGAIN;
1237 }
1238 }
1239
1240 /* Receiver is ready, there is something for us */
1241 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001242 word_msb = MCBSP_READ(mcbsp, DRR2);
1243 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001244
1245 word[0] = (word_lsb | (word_msb << 16));
1246
1247 return 0;
1248}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001249EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001250
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001251/*
1252 * Simple DMA based buffer rx/tx routines.
1253 * Nothing fancy, just a single buffer tx/rx through DMA.
1254 * The DMA resources are released once the transfer is done.
1255 * For anything fancier, you should use your own customized DMA
1256 * routines and callbacks.
1257 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001258int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer,
1259 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001260{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001261 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001262 int dma_tx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001263 int src_port = 0;
1264 int dest_port = 0;
1265 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001266
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001267 if (!omap_mcbsp_check_valid_id(id)) {
1268 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1269 return -ENODEV;
1270 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001271 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001272
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001273 if (omap_request_dma(mcbsp->dma_tx_sync, "McBSP TX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001274 omap_mcbsp_tx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001275 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001276 &dma_tx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001277 dev_err(mcbsp->dev, " Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001278 "McBSP%d TX. Trying IRQ based TX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001279 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001280 return -EAGAIN;
1281 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001282 mcbsp->dma_tx_lch = dma_tx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001283
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001284 dev_err(mcbsp->dev, "McBSP%d TX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001285 dma_tx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001286
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001287 init_completion(&mcbsp->tx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001288
Tony Lindgren120db2c2006-04-02 17:46:27 +01001289 if (cpu_class_is_omap1()) {
1290 src_port = OMAP_DMA_PORT_TIPB;
1291 dest_port = OMAP_DMA_PORT_EMIFF;
1292 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001293 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001294 sync_dev = mcbsp->dma_tx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001295
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001296 omap_set_dma_transfer_params(mcbsp->dma_tx_lch,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001297 OMAP_DMA_DATA_TYPE_S16,
1298 length >> 1, 1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001299 OMAP_DMA_SYNC_ELEMENT,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001300 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001301
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001302 omap_set_dma_dest_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001303 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001304 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001305 mcbsp->phys_base + OMAP_MCBSP_REG_DXR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001306 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001307
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001308 omap_set_dma_src_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001309 dest_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001310 OMAP_DMA_AMODE_POST_INC,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001311 buffer,
1312 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001313
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001314 omap_start_dma(mcbsp->dma_tx_lch);
1315 wait_for_completion(&mcbsp->tx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001316
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001317 return 0;
1318}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001319EXPORT_SYMBOL(omap_mcbsp_xmit_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001320
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001321int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer,
1322 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001323{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001324 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001325 int dma_rx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001326 int src_port = 0;
1327 int dest_port = 0;
1328 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001329
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001330 if (!omap_mcbsp_check_valid_id(id)) {
1331 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1332 return -ENODEV;
1333 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001334 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001335
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001336 if (omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001337 omap_mcbsp_rx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001338 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001339 &dma_rx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001340 dev_err(mcbsp->dev, "Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001341 "McBSP%d RX. Trying IRQ based RX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001342 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001343 return -EAGAIN;
1344 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001345 mcbsp->dma_rx_lch = dma_rx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001346
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001347 dev_err(mcbsp->dev, "McBSP%d RX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001348 dma_rx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001349
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001350 init_completion(&mcbsp->rx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001351
Tony Lindgren120db2c2006-04-02 17:46:27 +01001352 if (cpu_class_is_omap1()) {
1353 src_port = OMAP_DMA_PORT_TIPB;
1354 dest_port = OMAP_DMA_PORT_EMIFF;
1355 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001356 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001357 sync_dev = mcbsp->dma_rx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001358
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001359 omap_set_dma_transfer_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001360 OMAP_DMA_DATA_TYPE_S16,
1361 length >> 1, 1,
1362 OMAP_DMA_SYNC_ELEMENT,
1363 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001364
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001365 omap_set_dma_src_params(mcbsp->dma_rx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001366 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001367 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001368 mcbsp->phys_base + OMAP_MCBSP_REG_DRR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001369 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001370
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001371 omap_set_dma_dest_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001372 dest_port,
1373 OMAP_DMA_AMODE_POST_INC,
1374 buffer,
1375 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001376
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001377 omap_start_dma(mcbsp->dma_rx_lch);
1378 wait_for_completion(&mcbsp->rx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001379
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001380 return 0;
1381}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001382EXPORT_SYMBOL(omap_mcbsp_recv_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001383
1384/*
1385 * SPI wrapper.
1386 * Since SPI setup is much simpler than the generic McBSP one,
1387 * this wrapper just need an omap_mcbsp_spi_cfg structure as an input.
1388 * Once this is done, you can call omap_mcbsp_start().
1389 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001390void omap_mcbsp_set_spi_mode(unsigned int id,
1391 const struct omap_mcbsp_spi_cfg *spi_cfg)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001392{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001393 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001394 struct omap_mcbsp_reg_cfg mcbsp_cfg;
1395
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001396 if (!omap_mcbsp_check_valid_id(id)) {
1397 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001398 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001399 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001400 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001401
1402 memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg));
1403
1404 /* SPI has only one frame */
1405 mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0));
1406 mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0));
1407
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001408 /* Clock stop mode */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001409 if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY)
1410 mcbsp_cfg.spcr1 |= (1 << 12);
1411 else
1412 mcbsp_cfg.spcr1 |= (3 << 11);
1413
1414 /* Set clock parities */
1415 if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1416 mcbsp_cfg.pcr0 |= CLKRP;
1417 else
1418 mcbsp_cfg.pcr0 &= ~CLKRP;
1419
1420 if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1421 mcbsp_cfg.pcr0 &= ~CLKXP;
1422 else
1423 mcbsp_cfg.pcr0 |= CLKXP;
1424
1425 /* Set SCLKME to 0 and CLKSM to 1 */
1426 mcbsp_cfg.pcr0 &= ~SCLKME;
1427 mcbsp_cfg.srgr2 |= CLKSM;
1428
1429 /* Set FSXP */
1430 if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH)
1431 mcbsp_cfg.pcr0 &= ~FSXP;
1432 else
1433 mcbsp_cfg.pcr0 |= FSXP;
1434
1435 if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) {
1436 mcbsp_cfg.pcr0 |= CLKXM;
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001437 mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div - 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001438 mcbsp_cfg.pcr0 |= FSXM;
1439 mcbsp_cfg.srgr2 &= ~FSGM;
1440 mcbsp_cfg.xcr2 |= XDATDLY(1);
1441 mcbsp_cfg.rcr2 |= RDATDLY(1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001442 } else {
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001443 mcbsp_cfg.pcr0 &= ~CLKXM;
1444 mcbsp_cfg.srgr1 |= CLKGDV(1);
1445 mcbsp_cfg.pcr0 &= ~FSXM;
1446 mcbsp_cfg.xcr2 &= ~XDATDLY(3);
1447 mcbsp_cfg.rcr2 &= ~RDATDLY(3);
1448 }
1449
1450 mcbsp_cfg.xcr2 &= ~XPHASE;
1451 mcbsp_cfg.rcr2 &= ~RPHASE;
1452
1453 omap_mcbsp_config(id, &mcbsp_cfg);
1454}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001455EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001456
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001457#ifdef CONFIG_ARCH_OMAP3
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001458#define max_thres(m) (mcbsp->pdata->buffer_size)
1459#define valid_threshold(m, val) ((val) <= max_thres(m))
1460#define THRESHOLD_PROP_BUILDER(prop) \
1461static ssize_t prop##_show(struct device *dev, \
1462 struct device_attribute *attr, char *buf) \
1463{ \
1464 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1465 \
1466 return sprintf(buf, "%u\n", mcbsp->prop); \
1467} \
1468 \
1469static ssize_t prop##_store(struct device *dev, \
1470 struct device_attribute *attr, \
1471 const char *buf, size_t size) \
1472{ \
1473 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1474 unsigned long val; \
1475 int status; \
1476 \
1477 status = strict_strtoul(buf, 0, &val); \
1478 if (status) \
1479 return status; \
1480 \
1481 if (!valid_threshold(mcbsp, val)) \
1482 return -EDOM; \
1483 \
1484 mcbsp->prop = val; \
1485 return size; \
1486} \
1487 \
1488static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
1489
1490THRESHOLD_PROP_BUILDER(max_tx_thres);
1491THRESHOLD_PROP_BUILDER(max_rx_thres);
1492
Jarkko Nikula9b300502009-08-24 17:45:50 +03001493static const char *dma_op_modes[] = {
1494 "element", "threshold", "frame",
1495};
1496
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001497static ssize_t dma_op_mode_show(struct device *dev,
1498 struct device_attribute *attr, char *buf)
1499{
1500 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001501 int dma_op_mode, i = 0;
1502 ssize_t len = 0;
1503 const char * const *s;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001504
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001505 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001506
Jarkko Nikula9b300502009-08-24 17:45:50 +03001507 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
1508 if (dma_op_mode == i)
1509 len += sprintf(buf + len, "[%s] ", *s);
1510 else
1511 len += sprintf(buf + len, "%s ", *s);
1512 }
1513 len += sprintf(buf + len, "\n");
1514
1515 return len;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001516}
1517
1518static ssize_t dma_op_mode_store(struct device *dev,
1519 struct device_attribute *attr,
1520 const char *buf, size_t size)
1521{
1522 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001523 const char * const *s;
1524 int i = 0;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001525
Jarkko Nikula9b300502009-08-24 17:45:50 +03001526 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
1527 if (sysfs_streq(buf, *s))
1528 break;
1529
1530 if (i == ARRAY_SIZE(dma_op_modes))
1531 return -EINVAL;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001532
1533 spin_lock_irq(&mcbsp->lock);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001534 if (!mcbsp->free) {
1535 size = -EBUSY;
1536 goto unlock;
1537 }
Jarkko Nikula9b300502009-08-24 17:45:50 +03001538 mcbsp->dma_op_mode = i;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001539
1540unlock:
1541 spin_unlock_irq(&mcbsp->lock);
1542
1543 return size;
1544}
1545
1546static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
1547
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001548static ssize_t st_taps_show(struct device *dev,
1549 struct device_attribute *attr, char *buf)
1550{
1551 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1552 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1553 ssize_t status = 0;
1554 int i;
1555
1556 spin_lock_irq(&mcbsp->lock);
1557 for (i = 0; i < st_data->nr_taps; i++)
1558 status += sprintf(&buf[status], (i ? ", %d" : "%d"),
1559 st_data->taps[i]);
1560 if (i)
1561 status += sprintf(&buf[status], "\n");
1562 spin_unlock_irq(&mcbsp->lock);
1563
1564 return status;
1565}
1566
1567static ssize_t st_taps_store(struct device *dev,
1568 struct device_attribute *attr,
1569 const char *buf, size_t size)
1570{
1571 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1572 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1573 int val, tmp, status, i = 0;
1574
1575 spin_lock_irq(&mcbsp->lock);
1576 memset(st_data->taps, 0, sizeof(st_data->taps));
1577 st_data->nr_taps = 0;
1578
1579 do {
1580 status = sscanf(buf, "%d%n", &val, &tmp);
1581 if (status < 0 || status == 0) {
1582 size = -EINVAL;
1583 goto out;
1584 }
1585 if (val < -32768 || val > 32767) {
1586 size = -EINVAL;
1587 goto out;
1588 }
1589 st_data->taps[i++] = val;
1590 buf += tmp;
1591 if (*buf != ',')
1592 break;
1593 buf++;
1594 } while (1);
1595
1596 st_data->nr_taps = i;
1597
1598out:
1599 spin_unlock_irq(&mcbsp->lock);
1600
1601 return size;
1602}
1603
1604static DEVICE_ATTR(st_taps, 0644, st_taps_show, st_taps_store);
1605
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001606static const struct attribute *additional_attrs[] = {
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001607 &dev_attr_max_tx_thres.attr,
1608 &dev_attr_max_rx_thres.attr,
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001609 &dev_attr_dma_op_mode.attr,
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001610 NULL,
1611};
1612
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001613static const struct attribute_group additional_attr_group = {
1614 .attrs = (struct attribute **)additional_attrs,
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001615};
1616
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001617static inline int __devinit omap_additional_add(struct device *dev)
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001618{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001619 return sysfs_create_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001620}
1621
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001622static inline void __devexit omap_additional_remove(struct device *dev)
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001623{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001624 sysfs_remove_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001625}
1626
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001627static const struct attribute *sidetone_attrs[] = {
1628 &dev_attr_st_taps.attr,
1629 NULL,
1630};
1631
1632static const struct attribute_group sidetone_attr_group = {
1633 .attrs = (struct attribute **)sidetone_attrs,
1634};
1635
1636int __devinit omap_st_add(struct omap_mcbsp *mcbsp)
1637{
1638 struct omap_mcbsp_platform_data *pdata = mcbsp->pdata;
1639 struct omap_mcbsp_st_data *st_data;
1640 int err;
1641
1642 st_data = kzalloc(sizeof(*mcbsp->st_data), GFP_KERNEL);
1643 if (!st_data) {
1644 err = -ENOMEM;
1645 goto err1;
1646 }
1647
1648 st_data->io_base_st = ioremap(pdata->phys_base_st, SZ_4K);
1649 if (!st_data->io_base_st) {
1650 err = -ENOMEM;
1651 goto err2;
1652 }
1653
1654 err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
1655 if (err)
1656 goto err3;
1657
1658 mcbsp->st_data = st_data;
1659 return 0;
1660
1661err3:
1662 iounmap(st_data->io_base_st);
1663err2:
1664 kfree(st_data);
1665err1:
1666 return err;
1667
1668}
1669
1670static void __devexit omap_st_remove(struct omap_mcbsp *mcbsp)
1671{
1672 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1673
1674 if (st_data) {
1675 sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
1676 iounmap(st_data->io_base_st);
1677 kfree(st_data);
1678 }
1679}
1680
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001681static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
1682{
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001683 mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001684 if (cpu_is_omap34xx()) {
1685 mcbsp->max_tx_thres = max_thres(mcbsp);
1686 mcbsp->max_rx_thres = max_thres(mcbsp);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001687 /*
1688 * REVISIT: Set dmap_op_mode to THRESHOLD as default
1689 * for mcbsp2 instances.
1690 */
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001691 if (omap_additional_add(mcbsp->dev))
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001692 dev_warn(mcbsp->dev,
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001693 "Unable to create additional controls\n");
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001694
1695 if (mcbsp->id == 2 || mcbsp->id == 3)
1696 if (omap_st_add(mcbsp))
1697 dev_warn(mcbsp->dev,
1698 "Unable to create sidetone controls\n");
1699
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001700 } else {
1701 mcbsp->max_tx_thres = -EINVAL;
1702 mcbsp->max_rx_thres = -EINVAL;
1703 }
1704}
1705
1706static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
1707{
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001708 if (cpu_is_omap34xx()) {
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001709 omap_additional_remove(mcbsp->dev);
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001710
1711 if (mcbsp->id == 2 || mcbsp->id == 3)
1712 omap_st_remove(mcbsp);
1713 }
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001714}
1715#else
1716static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
1717static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001718#endif /* CONFIG_ARCH_OMAP3 */
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001719
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001720/*
1721 * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
1722 * 730 has only 2 McBSP, and both of them are MPU peripherals.
1723 */
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001724static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001725{
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001726 struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001727 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001728 int id = pdev->id - 1;
1729 int ret = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001730
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001731 if (!pdata) {
1732 dev_err(&pdev->dev, "McBSP device initialized without"
1733 "platform data\n");
1734 ret = -EINVAL;
1735 goto exit;
1736 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001737
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001738 dev_dbg(&pdev->dev, "Initializing OMAP McBSP (%d).\n", pdev->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001739
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001740 if (id >= omap_mcbsp_count) {
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001741 dev_err(&pdev->dev, "Invalid McBSP device id (%d)\n", id);
1742 ret = -EINVAL;
1743 goto exit;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001744 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001745
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001746 mcbsp = kzalloc(sizeof(struct omap_mcbsp), GFP_KERNEL);
1747 if (!mcbsp) {
1748 ret = -ENOMEM;
1749 goto exit;
1750 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001751
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001752 spin_lock_init(&mcbsp->lock);
1753 mcbsp->id = id + 1;
1754 mcbsp->free = 1;
1755 mcbsp->dma_tx_lch = -1;
1756 mcbsp->dma_rx_lch = -1;
1757
1758 mcbsp->phys_base = pdata->phys_base;
1759 mcbsp->io_base = ioremap(pdata->phys_base, SZ_4K);
1760 if (!mcbsp->io_base) {
Russell Kingd592dd12008-09-04 14:25:42 +01001761 ret = -ENOMEM;
1762 goto err_ioremap;
1763 }
1764
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001765 /* Default I/O is IRQ based */
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001766 mcbsp->io_type = OMAP_MCBSP_IRQ_IO;
1767 mcbsp->tx_irq = pdata->tx_irq;
1768 mcbsp->rx_irq = pdata->rx_irq;
1769 mcbsp->dma_rx_sync = pdata->dma_rx_sync;
1770 mcbsp->dma_tx_sync = pdata->dma_tx_sync;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001771
Russell Kingb820ce42009-01-23 10:26:46 +00001772 mcbsp->iclk = clk_get(&pdev->dev, "ick");
1773 if (IS_ERR(mcbsp->iclk)) {
1774 ret = PTR_ERR(mcbsp->iclk);
1775 dev_err(&pdev->dev, "unable to get ick: %d\n", ret);
1776 goto err_iclk;
1777 }
Stanley.Miao06151152009-01-29 08:57:12 -08001778
Russell Kingb820ce42009-01-23 10:26:46 +00001779 mcbsp->fclk = clk_get(&pdev->dev, "fck");
1780 if (IS_ERR(mcbsp->fclk)) {
1781 ret = PTR_ERR(mcbsp->fclk);
1782 dev_err(&pdev->dev, "unable to get fck: %d\n", ret);
1783 goto err_fclk;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001784 }
1785
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001786 mcbsp->pdata = pdata;
1787 mcbsp->dev = &pdev->dev;
Russell Kingb820ce42009-01-23 10:26:46 +00001788 mcbsp_ptr[id] = mcbsp;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001789 platform_set_drvdata(pdev, mcbsp);
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001790
1791 /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
1792 omap34xx_device_init(mcbsp);
1793
Russell Kingd592dd12008-09-04 14:25:42 +01001794 return 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001795
Russell Kingb820ce42009-01-23 10:26:46 +00001796err_fclk:
1797 clk_put(mcbsp->iclk);
1798err_iclk:
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001799 iounmap(mcbsp->io_base);
Russell Kingd592dd12008-09-04 14:25:42 +01001800err_ioremap:
Russell Kingb820ce42009-01-23 10:26:46 +00001801 kfree(mcbsp);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001802exit:
1803 return ret;
1804}
1805
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001806static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001807{
1808 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
1809
1810 platform_set_drvdata(pdev, NULL);
1811 if (mcbsp) {
1812
1813 if (mcbsp->pdata && mcbsp->pdata->ops &&
1814 mcbsp->pdata->ops->free)
1815 mcbsp->pdata->ops->free(mcbsp->id);
1816
Eduardo Valentina1a56f52009-08-20 16:18:11 +03001817 omap34xx_device_exit(mcbsp);
1818
Russell Kingb820ce42009-01-23 10:26:46 +00001819 clk_disable(mcbsp->fclk);
1820 clk_disable(mcbsp->iclk);
1821 clk_put(mcbsp->fclk);
1822 clk_put(mcbsp->iclk);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001823
Russell Kingd592dd12008-09-04 14:25:42 +01001824 iounmap(mcbsp->io_base);
1825
Russell Kingb820ce42009-01-23 10:26:46 +00001826 mcbsp->fclk = NULL;
1827 mcbsp->iclk = NULL;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001828 mcbsp->free = 0;
1829 mcbsp->dev = NULL;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001830 }
1831
1832 return 0;
1833}
1834
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001835static struct platform_driver omap_mcbsp_driver = {
1836 .probe = omap_mcbsp_probe,
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001837 .remove = __devexit_p(omap_mcbsp_remove),
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001838 .driver = {
1839 .name = "omap-mcbsp",
1840 },
1841};
1842
1843int __init omap_mcbsp_init(void)
1844{
1845 /* Register the McBSP driver */
1846 return platform_driver_register(&omap_mcbsp_driver);
1847}