blob: 67ec74ea25322f7f2fae6322f69565de9159a8ab [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010027
Tony Lindgrence491cf2009-10-20 09:40:47 -070028#include <plat/dma.h>
29#include <plat/mcbsp.h>
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +053030#include <plat/omap_device.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010031
Paul Walmsley59fb6592010-12-21 15:30:55 -070032/* XXX These "sideways" includes are a sign that something is wrong */
33#include "../mach-omap2/cm2xxx_3xxx.h"
Eero Nurkkalad912fa92010-02-22 12:21:11 +000034#include "../mach-omap2/cm-regbits-34xx.h"
35
Chandra Shekharb4b58f52008-10-08 10:01:39 +030036struct omap_mcbsp **mcbsp_ptr;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080037int omap_mcbsp_count, omap_mcbsp_cache_size;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +030038
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -070039static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030040{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080041 if (cpu_class_is_omap1()) {
42 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)] = (u16)val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080043 __raw_writew((u16)val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080044 } else if (cpu_is_omap2420()) {
45 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)] = (u16)val;
46 __raw_writew((u16)val, mcbsp->io_base + reg);
47 } else {
48 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)] = val;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080049 __raw_writel(val, mcbsp->io_base + reg);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080050 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030051}
52
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -070053static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030054{
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080055 if (cpu_class_is_omap1()) {
56 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
57 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u16)];
58 } else if (cpu_is_omap2420()) {
59 return !from_cache ? __raw_readw(mcbsp->io_base + reg) :
60 ((u16 *)mcbsp->reg_cache)[reg / sizeof(u32)];
61 } else {
62 return !from_cache ? __raw_readl(mcbsp->io_base + reg) :
63 ((u32 *)mcbsp->reg_cache)[reg / sizeof(u32)];
64 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +030065}
66
Eero Nurkkalad912fa92010-02-22 12:21:11 +000067#ifdef CONFIG_ARCH_OMAP3
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -070068static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val)
Eero Nurkkalad912fa92010-02-22 12:21:11 +000069{
70 __raw_writel(val, mcbsp->st_data->io_base_st + reg);
71}
72
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -070073static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg)
Eero Nurkkalad912fa92010-02-22 12:21:11 +000074{
75 return __raw_readl(mcbsp->st_data->io_base_st + reg);
76}
77#endif
78
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080079#define MCBSP_READ(mcbsp, reg) \
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080080 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080081#define MCBSP_WRITE(mcbsp, reg, val) \
82 omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -080083#define MCBSP_READ_CACHE(mcbsp, reg) \
84 omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1)
Chandra Shekharb4b58f52008-10-08 10:01:39 +030085
Eero Nurkkalad912fa92010-02-22 12:21:11 +000086#define MCBSP_ST_READ(mcbsp, reg) \
87 omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg)
88#define MCBSP_ST_WRITE(mcbsp, reg, val) \
89 omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val)
90
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010091static void omap_mcbsp_dump_reg(u8 id)
92{
Chandra Shekharb4b58f52008-10-08 10:01:39 +030093 struct omap_mcbsp *mcbsp = id_to_mcbsp_ptr(id);
94
95 dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id);
96 dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080097 MCBSP_READ(mcbsp, DRR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +030098 dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -080099 MCBSP_READ(mcbsp, DRR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300100 dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800101 MCBSP_READ(mcbsp, DXR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300102 dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800103 MCBSP_READ(mcbsp, DXR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300104 dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800105 MCBSP_READ(mcbsp, SPCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300106 dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800107 MCBSP_READ(mcbsp, SPCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300108 dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800109 MCBSP_READ(mcbsp, RCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300110 dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800111 MCBSP_READ(mcbsp, RCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300112 dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800113 MCBSP_READ(mcbsp, XCR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300114 dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800115 MCBSP_READ(mcbsp, XCR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300116 dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800117 MCBSP_READ(mcbsp, SRGR2));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300118 dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800119 MCBSP_READ(mcbsp, SRGR1));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300120 dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800121 MCBSP_READ(mcbsp, PCR0));
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300122 dev_dbg(mcbsp->dev, "***********************\n");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100123}
124
Linus Torvalds0cd61b62006-10-06 10:53:39 -0700125static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100126{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400127 struct omap_mcbsp *mcbsp_tx = dev_id;
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700128 u16 irqst_spcr2;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100129
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800130 irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700131 dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100132
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700133 if (irqst_spcr2 & XSYNC_ERR) {
134 dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n",
135 irqst_spcr2);
136 /* Writing zero to XSYNC_ERR clears the IRQ */
Janusz Krzysztofik0841cb82010-02-23 15:50:38 +0000137 MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2));
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 Krzysztofik0841cb82010-02-23 15:50:38 +0000157 MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1));
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700158 } else {
Scott Elliscb922d22010-09-23 18:47:23 -0700159 complete(&mcbsp_rx->rx_irq_completion);
Eero Nurkkalad6d834b2009-05-25 11:08:42 -0700160 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300161
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100162 return IRQ_HANDLED;
163}
164
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100165static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data)
166{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400167 struct omap_mcbsp *mcbsp_dma_tx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100168
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300169 dev_dbg(mcbsp_dma_tx->dev, "TX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800170 MCBSP_READ(mcbsp_dma_tx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100171
172 /* We can free the channels */
173 omap_free_dma(mcbsp_dma_tx->dma_tx_lch);
174 mcbsp_dma_tx->dma_tx_lch = -1;
175
176 complete(&mcbsp_dma_tx->tx_dma_completion);
177}
178
179static void omap_mcbsp_rx_dma_callback(int lch, u16 ch_status, void *data)
180{
Jeff Garzike8f2af12007-10-26 05:40:25 -0400181 struct omap_mcbsp *mcbsp_dma_rx = data;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100182
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300183 dev_dbg(mcbsp_dma_rx->dev, "RX DMA callback : 0x%x\n",
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800184 MCBSP_READ(mcbsp_dma_rx, SPCR2));
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100185
186 /* We can free the channels */
187 omap_free_dma(mcbsp_dma_rx->dma_rx_lch);
188 mcbsp_dma_rx->dma_rx_lch = -1;
189
190 complete(&mcbsp_dma_rx->rx_dma_completion);
191}
192
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100193/*
194 * omap_mcbsp_config simply write a config to the
195 * appropriate McBSP.
196 * You either call this function or set the McBSP registers
197 * by yourself before calling omap_mcbsp_start().
198 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300199void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100200{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300201 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100202
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300203 if (!omap_mcbsp_check_valid_id(id)) {
204 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
205 return;
206 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300207 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300208
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300209 dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n",
210 mcbsp->id, mcbsp->phys_base);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100211
212 /* We write the given config */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800213 MCBSP_WRITE(mcbsp, SPCR2, config->spcr2);
214 MCBSP_WRITE(mcbsp, SPCR1, config->spcr1);
215 MCBSP_WRITE(mcbsp, RCR2, config->rcr2);
216 MCBSP_WRITE(mcbsp, RCR1, config->rcr1);
217 MCBSP_WRITE(mcbsp, XCR2, config->xcr2);
218 MCBSP_WRITE(mcbsp, XCR1, config->xcr1);
219 MCBSP_WRITE(mcbsp, SRGR2, config->srgr2);
220 MCBSP_WRITE(mcbsp, SRGR1, config->srgr1);
221 MCBSP_WRITE(mcbsp, MCR2, config->mcr2);
222 MCBSP_WRITE(mcbsp, MCR1, config->mcr1);
223 MCBSP_WRITE(mcbsp, PCR0, config->pcr0);
Syed Rafiuddina5b92cc2009-07-28 18:57:10 +0530224 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800225 MCBSP_WRITE(mcbsp, XCCR, config->xccr);
226 MCBSP_WRITE(mcbsp, RCCR, config->rccr);
Tony Lindgren3127f8f2009-01-15 13:09:54 +0200227 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100228}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300229EXPORT_SYMBOL(omap_mcbsp_config);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100230
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -0800231#ifdef CONFIG_ARCH_OMAP3
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530232static struct omap_device *find_omap_device_by_dev(struct device *dev)
233{
234 struct platform_device *pdev = container_of(dev,
235 struct platform_device, dev);
236 return container_of(pdev, struct omap_device, pdev);
237}
238
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000239static void omap_st_on(struct omap_mcbsp *mcbsp)
240{
241 unsigned int w;
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530242 struct omap_device *od;
243
244 od = find_omap_device_by_dev(mcbsp->dev);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000245
246 /*
247 * Sidetone uses McBSP ICLK - which must not idle when sidetones
248 * are enabled or sidetones start sounding ugly.
249 */
Paul Walmsleyc4d7e582010-12-21 21:05:14 -0700250 w = omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000251 w &= ~(1 << (mcbsp->id - 2));
Paul Walmsleyc4d7e582010-12-21 21:05:14 -0700252 omap2_cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000253
254 /* Enable McBSP Sidetone */
255 w = MCBSP_READ(mcbsp, SSELCR);
256 MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN);
257
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000258 /* Enable Sidetone from Sidetone Core */
259 w = MCBSP_ST_READ(mcbsp, SSELCR);
260 MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN);
261}
262
263static void omap_st_off(struct omap_mcbsp *mcbsp)
264{
265 unsigned int w;
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530266 struct omap_device *od;
267
268 od = find_omap_device_by_dev(mcbsp->dev);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000269
270 w = MCBSP_ST_READ(mcbsp, SSELCR);
271 MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN));
272
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000273 w = MCBSP_READ(mcbsp, SSELCR);
274 MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN));
275
Paul Walmsleyc4d7e582010-12-21 21:05:14 -0700276 w = omap2_cm_read_mod_reg(OMAP3430_PER_MOD, CM_AUTOIDLE);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000277 w |= 1 << (mcbsp->id - 2);
Paul Walmsleyc4d7e582010-12-21 21:05:14 -0700278 omap2_cm_write_mod_reg(w, OMAP3430_PER_MOD, CM_AUTOIDLE);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000279}
280
281static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
282{
283 u16 val, i;
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530284 struct omap_device *od;
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000285
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530286 od = find_omap_device_by_dev(mcbsp->dev);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000287
288 val = MCBSP_ST_READ(mcbsp, SSELCR);
289
290 if (val & ST_COEFFWREN)
291 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
292
293 MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN);
294
295 for (i = 0; i < 128; i++)
296 MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]);
297
298 i = 0;
299
300 val = MCBSP_ST_READ(mcbsp, SSELCR);
301 while (!(val & ST_COEFFWRDONE) && (++i < 1000))
302 val = MCBSP_ST_READ(mcbsp, SSELCR);
303
304 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN));
305
306 if (i == 1000)
307 dev_err(mcbsp->dev, "McBSP FIR load error!\n");
308}
309
310static void omap_st_chgain(struct omap_mcbsp *mcbsp)
311{
312 u16 w;
313 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530314 struct omap_device *od;
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000315
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530316 od = find_omap_device_by_dev(mcbsp->dev);
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000317
318 w = MCBSP_ST_READ(mcbsp, SSELCR);
319
320 MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \
321 ST_CH1GAIN(st_data->ch1gain));
322}
323
324int omap_st_set_chgain(unsigned int id, int channel, s16 chgain)
325{
326 struct omap_mcbsp *mcbsp;
327 struct omap_mcbsp_st_data *st_data;
328 int ret = 0;
329
330 if (!omap_mcbsp_check_valid_id(id)) {
331 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
332 return -ENODEV;
333 }
334
335 mcbsp = id_to_mcbsp_ptr(id);
336 st_data = mcbsp->st_data;
337
338 if (!st_data)
339 return -ENOENT;
340
341 spin_lock_irq(&mcbsp->lock);
342 if (channel == 0)
343 st_data->ch0gain = chgain;
344 else if (channel == 1)
345 st_data->ch1gain = chgain;
346 else
347 ret = -EINVAL;
348
349 if (st_data->enabled)
350 omap_st_chgain(mcbsp);
351 spin_unlock_irq(&mcbsp->lock);
352
353 return ret;
354}
355EXPORT_SYMBOL(omap_st_set_chgain);
356
357int omap_st_get_chgain(unsigned int id, int channel, s16 *chgain)
358{
359 struct omap_mcbsp *mcbsp;
360 struct omap_mcbsp_st_data *st_data;
361 int ret = 0;
362
363 if (!omap_mcbsp_check_valid_id(id)) {
364 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
365 return -ENODEV;
366 }
367
368 mcbsp = id_to_mcbsp_ptr(id);
369 st_data = mcbsp->st_data;
370
371 if (!st_data)
372 return -ENOENT;
373
374 spin_lock_irq(&mcbsp->lock);
375 if (channel == 0)
376 *chgain = st_data->ch0gain;
377 else if (channel == 1)
378 *chgain = st_data->ch1gain;
379 else
380 ret = -EINVAL;
381 spin_unlock_irq(&mcbsp->lock);
382
383 return ret;
384}
385EXPORT_SYMBOL(omap_st_get_chgain);
386
387static int omap_st_start(struct omap_mcbsp *mcbsp)
388{
389 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
390
391 if (st_data && st_data->enabled && !st_data->running) {
392 omap_st_fir_write(mcbsp, st_data->taps);
393 omap_st_chgain(mcbsp);
394
395 if (!mcbsp->free) {
396 omap_st_on(mcbsp);
397 st_data->running = 1;
398 }
399 }
400
401 return 0;
402}
403
404int omap_st_enable(unsigned int id)
405{
406 struct omap_mcbsp *mcbsp;
407 struct omap_mcbsp_st_data *st_data;
408
409 if (!omap_mcbsp_check_valid_id(id)) {
410 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
411 return -ENODEV;
412 }
413
414 mcbsp = id_to_mcbsp_ptr(id);
415 st_data = mcbsp->st_data;
416
417 if (!st_data)
418 return -ENODEV;
419
420 spin_lock_irq(&mcbsp->lock);
421 st_data->enabled = 1;
422 omap_st_start(mcbsp);
423 spin_unlock_irq(&mcbsp->lock);
424
425 return 0;
426}
427EXPORT_SYMBOL(omap_st_enable);
428
429static int omap_st_stop(struct omap_mcbsp *mcbsp)
430{
431 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
432
433 if (st_data && st_data->running) {
434 if (!mcbsp->free) {
435 omap_st_off(mcbsp);
436 st_data->running = 0;
437 }
438 }
439
440 return 0;
441}
442
443int omap_st_disable(unsigned int id)
444{
445 struct omap_mcbsp *mcbsp;
446 struct omap_mcbsp_st_data *st_data;
447 int ret = 0;
448
449 if (!omap_mcbsp_check_valid_id(id)) {
450 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
451 return -ENODEV;
452 }
453
454 mcbsp = id_to_mcbsp_ptr(id);
455 st_data = mcbsp->st_data;
456
457 if (!st_data)
458 return -ENODEV;
459
460 spin_lock_irq(&mcbsp->lock);
461 omap_st_stop(mcbsp);
462 st_data->enabled = 0;
463 spin_unlock_irq(&mcbsp->lock);
464
465 return ret;
466}
467EXPORT_SYMBOL(omap_st_disable);
468
469int omap_st_is_enabled(unsigned int id)
470{
471 struct omap_mcbsp *mcbsp;
472 struct omap_mcbsp_st_data *st_data;
473
474 if (!omap_mcbsp_check_valid_id(id)) {
475 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
476 return -ENODEV;
477 }
478
479 mcbsp = id_to_mcbsp_ptr(id);
480 st_data = mcbsp->st_data;
481
482 if (!st_data)
483 return -ENODEV;
484
485
486 return st_data->enabled;
487}
488EXPORT_SYMBOL(omap_st_is_enabled);
489
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300490/*
Peter Ujfalusi451fd822010-06-03 07:39:33 +0300491 * omap_mcbsp_set_rx_threshold configures the transmit threshold in words.
492 * The threshold parameter is 1 based, and it is converted (threshold - 1)
493 * for the THRSH2 register.
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300494 */
495void omap_mcbsp_set_tx_threshold(unsigned int id, u16 threshold)
496{
497 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300498
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500499 if (!cpu_is_omap34xx() && !cpu_is_omap44xx())
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300500 return;
501
502 if (!omap_mcbsp_check_valid_id(id)) {
503 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
504 return;
505 }
506 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300507
Peter Ujfalusi451fd822010-06-03 07:39:33 +0300508 if (threshold && threshold <= mcbsp->max_tx_thres)
509 MCBSP_WRITE(mcbsp, THRSH2, threshold - 1);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300510}
511EXPORT_SYMBOL(omap_mcbsp_set_tx_threshold);
512
513/*
Peter Ujfalusi451fd822010-06-03 07:39:33 +0300514 * omap_mcbsp_set_rx_threshold configures the receive threshold in words.
515 * The threshold parameter is 1 based, and it is converted (threshold - 1)
516 * for the THRSH1 register.
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300517 */
518void omap_mcbsp_set_rx_threshold(unsigned int id, u16 threshold)
519{
520 struct omap_mcbsp *mcbsp;
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300521
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500522 if (!cpu_is_omap34xx() && !cpu_is_omap44xx())
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300523 return;
524
525 if (!omap_mcbsp_check_valid_id(id)) {
526 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
527 return;
528 }
529 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300530
Peter Ujfalusi451fd822010-06-03 07:39:33 +0300531 if (threshold && threshold <= mcbsp->max_rx_thres)
532 MCBSP_WRITE(mcbsp, THRSH1, threshold - 1);
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300533}
534EXPORT_SYMBOL(omap_mcbsp_set_rx_threshold);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +0300535
536/*
537 * omap_mcbsp_get_max_tx_thres just return the current configured
538 * maximum threshold for transmission
539 */
540u16 omap_mcbsp_get_max_tx_threshold(unsigned int id)
541{
542 struct omap_mcbsp *mcbsp;
543
544 if (!omap_mcbsp_check_valid_id(id)) {
545 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
546 return -ENODEV;
547 }
548 mcbsp = id_to_mcbsp_ptr(id);
549
550 return mcbsp->max_tx_thres;
551}
552EXPORT_SYMBOL(omap_mcbsp_get_max_tx_threshold);
553
554/*
555 * omap_mcbsp_get_max_rx_thres just return the current configured
556 * maximum threshold for reception
557 */
558u16 omap_mcbsp_get_max_rx_threshold(unsigned int id)
559{
560 struct omap_mcbsp *mcbsp;
561
562 if (!omap_mcbsp_check_valid_id(id)) {
563 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
564 return -ENODEV;
565 }
566 mcbsp = id_to_mcbsp_ptr(id);
567
568 return mcbsp->max_rx_thres;
569}
570EXPORT_SYMBOL(omap_mcbsp_get_max_rx_threshold);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300571
Peter Ujfalusi0acce822010-06-03 07:39:32 +0300572u16 omap_mcbsp_get_fifo_size(unsigned int id)
573{
574 struct omap_mcbsp *mcbsp;
575
576 if (!omap_mcbsp_check_valid_id(id)) {
577 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
578 return -ENODEV;
579 }
580 mcbsp = id_to_mcbsp_ptr(id);
581
582 return mcbsp->pdata->buffer_size;
583}
584EXPORT_SYMBOL(omap_mcbsp_get_fifo_size);
585
Peter Ujfalusi7dc976e2010-03-03 15:08:08 +0200586/*
587 * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO
588 */
589u16 omap_mcbsp_get_tx_delay(unsigned int id)
590{
591 struct omap_mcbsp *mcbsp;
592 u16 buffstat;
593
594 if (!omap_mcbsp_check_valid_id(id)) {
595 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
596 return -ENODEV;
597 }
598 mcbsp = id_to_mcbsp_ptr(id);
599
600 /* Returns the number of free locations in the buffer */
601 buffstat = MCBSP_READ(mcbsp, XBUFFSTAT);
602
603 /* Number of slots are different in McBSP ports */
Peter Ujfalusif10b8ad2010-06-03 07:39:34 +0300604 return mcbsp->pdata->buffer_size - buffstat;
Peter Ujfalusi7dc976e2010-03-03 15:08:08 +0200605}
606EXPORT_SYMBOL(omap_mcbsp_get_tx_delay);
607
608/*
609 * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO
610 * to reach the threshold value (when the DMA will be triggered to read it)
611 */
612u16 omap_mcbsp_get_rx_delay(unsigned int id)
613{
614 struct omap_mcbsp *mcbsp;
615 u16 buffstat, threshold;
616
617 if (!omap_mcbsp_check_valid_id(id)) {
618 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
619 return -ENODEV;
620 }
621 mcbsp = id_to_mcbsp_ptr(id);
622
623 /* Returns the number of used locations in the buffer */
624 buffstat = MCBSP_READ(mcbsp, RBUFFSTAT);
625 /* RX threshold */
626 threshold = MCBSP_READ(mcbsp, THRSH1);
627
628 /* Return the number of location till we reach the threshold limit */
629 if (threshold <= buffstat)
630 return 0;
631 else
632 return threshold - buffstat;
633}
634EXPORT_SYMBOL(omap_mcbsp_get_rx_delay);
635
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300636/*
637 * omap_mcbsp_get_dma_op_mode just return the current configured
638 * operating mode for the mcbsp channel
639 */
640int omap_mcbsp_get_dma_op_mode(unsigned int id)
641{
642 struct omap_mcbsp *mcbsp;
643 int dma_op_mode;
644
645 if (!omap_mcbsp_check_valid_id(id)) {
646 printk(KERN_ERR "%s: Invalid id (%u)\n", __func__, id + 1);
647 return -ENODEV;
648 }
649 mcbsp = id_to_mcbsp_ptr(id);
650
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300651 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +0300652
653 return dma_op_mode;
654}
655EXPORT_SYMBOL(omap_mcbsp_get_dma_op_mode);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300656
657static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp)
658{
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530659 struct omap_device *od;
660
661 od = find_omap_device_by_dev(mcbsp->dev);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300662 /*
663 * Enable wakup behavior, smart idle and all wakeups
664 * REVISIT: some wakeups may be unnecessary
665 */
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500666 if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530667 MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300668 }
669}
670
671static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp)
672{
Kishon Vijay Abraham If36d01d2011-02-24 15:16:53 +0530673 struct omap_device *od;
674
675 od = find_omap_device_by_dev(mcbsp->dev);
676
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300677 /*
678 * Disable wakup behavior, smart idle and all wakeups
679 */
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500680 if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
Eero Nurkkala72cc6d72009-08-20 16:18:20 +0300681 /*
682 * HW bug workaround - If no_idle mode is taken, we need to
683 * go to smart_idle before going to always_idle, or the
684 * device will not hit retention anymore.
685 */
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300686
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800687 MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300688 }
689}
690#else
691static inline void omap34xx_mcbsp_request(struct omap_mcbsp *mcbsp) {}
692static inline void omap34xx_mcbsp_free(struct omap_mcbsp *mcbsp) {}
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000693static inline void omap_st_start(struct omap_mcbsp *mcbsp) {}
694static inline void omap_st_stop(struct omap_mcbsp *mcbsp) {}
Eduardo Valentin7aa9ff52009-08-20 16:18:10 +0300695#endif
696
Tony Lindgren120db2c2006-04-02 17:46:27 +0100697/*
698 * We can choose between IRQ based or polled IO.
699 * This needs to be called before omap_mcbsp_request().
700 */
701int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type)
702{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300703 struct omap_mcbsp *mcbsp;
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;
708 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300709 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100710
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300711 spin_lock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100712
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300713 if (!mcbsp->free) {
714 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
715 mcbsp->id);
716 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100717 return -EINVAL;
718 }
719
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300720 mcbsp->io_type = io_type;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100721
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300722 spin_unlock(&mcbsp->lock);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100723
724 return 0;
725}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300726EXPORT_SYMBOL(omap_mcbsp_set_io_type);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100727
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100728int omap_mcbsp_request(unsigned int id)
729{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300730 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800731 void *reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100732 int err;
733
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300734 if (!omap_mcbsp_check_valid_id(id)) {
735 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
736 return -ENODEV;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100737 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300738 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300739
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800740 reg_cache = kzalloc(omap_mcbsp_cache_size, GFP_KERNEL);
741 if (!reg_cache) {
742 return -ENOMEM;
743 }
744
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300745 spin_lock(&mcbsp->lock);
746 if (!mcbsp->free) {
747 dev_err(mcbsp->dev, "McBSP%d is currently in use\n",
748 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800749 err = -EBUSY;
750 goto err_kfree;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100751 }
752
Shubhrajyoti D6722a722010-12-07 16:25:41 -0800753 mcbsp->free = false;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800754 mcbsp->reg_cache = reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300755 spin_unlock(&mcbsp->lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100756
Russell Kingb820ce42009-01-23 10:26:46 +0000757 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request)
758 mcbsp->pdata->ops->request(id);
759
760 clk_enable(mcbsp->iclk);
761 clk_enable(mcbsp->fclk);
762
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300763 /* Do procedure specific to omap34xx arch, if applicable */
764 omap34xx_mcbsp_request(mcbsp);
765
Jarkko Nikula5a070552008-10-08 10:01:41 +0300766 /*
767 * Make sure that transmitter, receiver and sample-rate generator are
768 * not running before activating IRQs.
769 */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800770 MCBSP_WRITE(mcbsp, SPCR1, 0);
771 MCBSP_WRITE(mcbsp, SPCR2, 0);
Jarkko Nikula5a070552008-10-08 10:01:41 +0300772
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300773 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
Tony Lindgren120db2c2006-04-02 17:46:27 +0100774 /* We need to get IRQs here */
Jarkko Nikula5a070552008-10-08 10:01:41 +0300775 init_completion(&mcbsp->tx_irq_completion);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300776 err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler,
777 0, "McBSP", (void *)mcbsp);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100778 if (err != 0) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300779 dev_err(mcbsp->dev, "Unable to request TX IRQ %d "
780 "for McBSP%d\n", mcbsp->tx_irq,
781 mcbsp->id);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800782 goto err_clk_disable;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100783 }
784
Jorge Eduardo Candelaria9319b9d2010-05-12 12:18:39 -0500785 if (mcbsp->rx_irq) {
786 init_completion(&mcbsp->rx_irq_completion);
787 err = request_irq(mcbsp->rx_irq,
788 omap_mcbsp_rx_irq_handler,
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300789 0, "McBSP", (void *)mcbsp);
Jorge Eduardo Candelaria9319b9d2010-05-12 12:18:39 -0500790 if (err != 0) {
791 dev_err(mcbsp->dev, "Unable to request RX IRQ %d "
792 "for McBSP%d\n", mcbsp->rx_irq,
793 mcbsp->id);
794 goto err_free_irq;
795 }
Tony Lindgren120db2c2006-04-02 17:46:27 +0100796 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100797 }
798
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100799 return 0;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800800err_free_irq:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800801 free_irq(mcbsp->tx_irq, (void *)mcbsp);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800802err_clk_disable:
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800803 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800804 mcbsp->pdata->ops->free(id);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800805
806 /* Do procedure specific to omap34xx arch, if applicable */
807 omap34xx_mcbsp_free(mcbsp);
808
809 clk_disable(mcbsp->fclk);
810 clk_disable(mcbsp->iclk);
811
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800812 spin_lock(&mcbsp->lock);
Shubhrajyoti D6722a722010-12-07 16:25:41 -0800813 mcbsp->free = true;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800814 mcbsp->reg_cache = NULL;
815err_kfree:
816 spin_unlock(&mcbsp->lock);
817 kfree(reg_cache);
Janusz Krzysztofik1866b542010-01-08 10:29:04 -0800818
819 return err;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100820}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300821EXPORT_SYMBOL(omap_mcbsp_request);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100822
823void omap_mcbsp_free(unsigned int id)
824{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300825 struct omap_mcbsp *mcbsp;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800826 void *reg_cache;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300827
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300828 if (!omap_mcbsp_check_valid_id(id)) {
829 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100830 return;
Tony Lindgren120db2c2006-04-02 17:46:27 +0100831 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300832 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren120db2c2006-04-02 17:46:27 +0100833
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300834 if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free)
835 mcbsp->pdata->ops->free(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300836
Eero Nurkkala2122fdc2009-08-20 16:18:15 +0300837 /* Do procedure specific to omap34xx arch, if applicable */
838 omap34xx_mcbsp_free(mcbsp);
839
Russell Kingb820ce42009-01-23 10:26:46 +0000840 clk_disable(mcbsp->fclk);
841 clk_disable(mcbsp->iclk);
842
843 if (mcbsp->io_type == OMAP_MCBSP_IRQ_IO) {
844 /* Free IRQs */
Jorge Eduardo Candelaria9319b9d2010-05-12 12:18:39 -0500845 if (mcbsp->rx_irq)
846 free_irq(mcbsp->rx_irq, (void *)mcbsp);
Russell Kingb820ce42009-01-23 10:26:46 +0000847 free_irq(mcbsp->tx_irq, (void *)mcbsp);
848 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100849
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800850 reg_cache = mcbsp->reg_cache;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100851
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800852 spin_lock(&mcbsp->lock);
853 if (mcbsp->free)
854 dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id);
855 else
Shubhrajyoti D6722a722010-12-07 16:25:41 -0800856 mcbsp->free = true;
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800857 mcbsp->reg_cache = NULL;
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300858 spin_unlock(&mcbsp->lock);
Janusz Krzysztofikc8c99692010-02-15 10:03:33 -0800859
860 if (reg_cache)
861 kfree(reg_cache);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100862}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300863EXPORT_SYMBOL(omap_mcbsp_free);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100864
865/*
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300866 * Here we start the McBSP, by enabling transmitter, receiver or both.
867 * If no transmitter or receiver is active prior calling, then sample-rate
868 * generator and frame sync are started.
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100869 */
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300870void omap_mcbsp_start(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100871{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300872 struct omap_mcbsp *mcbsp;
Peter Ujfalusice3f0542010-08-31 08:11:44 +0000873 int enable_srg = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100874 u16 w;
875
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300876 if (!omap_mcbsp_check_valid_id(id)) {
877 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100878 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300879 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300880 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100881
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000882 if (cpu_is_omap34xx())
883 omap_st_start(mcbsp);
884
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800885 mcbsp->rx_word_length = (MCBSP_READ_CACHE(mcbsp, RCR1) >> 5) & 0x7;
886 mcbsp->tx_word_length = (MCBSP_READ_CACHE(mcbsp, XCR1) >> 5) & 0x7;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100887
Peter Ujfalusice3f0542010-08-31 08:11:44 +0000888 /* Only enable SRG, if McBSP is master */
889 w = MCBSP_READ_CACHE(mcbsp, PCR0);
890 if (w & (FSXM | FSRM | CLKXM | CLKRM))
891 enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
892 MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300893
Peter Ujfalusice3f0542010-08-31 08:11:44 +0000894 if (enable_srg) {
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300895 /* Start the sample generator */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800896 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800897 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300898 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100899
900 /* Enable transmitter and receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300901 tx &= 1;
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800902 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800903 MCBSP_WRITE(mcbsp, SPCR2, w | tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100904
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300905 rx &= 1;
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800906 w = MCBSP_READ_CACHE(mcbsp, SPCR1);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800907 MCBSP_WRITE(mcbsp, SPCR1, w | rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100908
Eduardo Valentin44a63112009-08-20 16:18:09 +0300909 /*
910 * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec
911 * REVISIT: 100us may give enough time for two CLKSRG, however
912 * due to some unknown PM related, clock gating etc. reason it
913 * is now at 500us.
914 */
915 udelay(500);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100916
Peter Ujfalusice3f0542010-08-31 08:11:44 +0000917 if (enable_srg) {
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300918 /* Start frame sync */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800919 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800920 MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300921 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100922
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500923 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300924 /* Release the transmitter and receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800925 w = MCBSP_READ_CACHE(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300926 w &= ~(tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800927 MCBSP_WRITE(mcbsp, XCCR, w);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800928 w = MCBSP_READ_CACHE(mcbsp, RCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300929 w &= ~(rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800930 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300931 }
932
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100933 /* Dump McBSP Regs */
934 omap_mcbsp_dump_reg(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100935}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300936EXPORT_SYMBOL(omap_mcbsp_start);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100937
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300938void omap_mcbsp_stop(unsigned int id, int tx, int rx)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100939{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300940 struct omap_mcbsp *mcbsp;
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300941 int idle;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100942 u16 w;
943
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300944 if (!omap_mcbsp_check_valid_id(id)) {
945 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100946 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300947 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100948
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300949 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100950
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300951 /* Reset transmitter */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300952 tx &= 1;
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500953 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800954 w = MCBSP_READ_CACHE(mcbsp, XCCR);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300955 w |= (tx ? XDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800956 MCBSP_WRITE(mcbsp, XCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300957 }
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800958 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800959 MCBSP_WRITE(mcbsp, SPCR2, w & ~tx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100960
961 /* Reset receiver */
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300962 rx &= 1;
Jorge Eduardo Candelaria752ec2f2010-05-12 12:18:40 -0500963 if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800964 w = MCBSP_READ_CACHE(mcbsp, RCCR);
Jarkko Nikulaa93d4ed2009-10-14 09:56:35 -0700965 w |= (rx ? RDISABLE : 0);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800966 MCBSP_WRITE(mcbsp, RCCR, w);
Jarkko Nikulad09a2af2009-08-23 12:24:27 +0300967 }
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800968 w = MCBSP_READ_CACHE(mcbsp, SPCR1);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800969 MCBSP_WRITE(mcbsp, SPCR1, w & ~rx);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100970
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800971 idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) |
972 MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1);
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300973
974 if (idle) {
975 /* Reset the sample rate generator */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -0800976 w = MCBSP_READ_CACHE(mcbsp, SPCR2);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800977 MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6));
Jarkko Nikulac12abc02009-08-07 09:59:47 +0300978 }
Eero Nurkkalad912fa92010-02-22 12:21:11 +0000979
980 if (cpu_is_omap34xx())
981 omap_st_stop(mcbsp);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100982}
Eduardo Valentinfb78d802008-07-03 12:24:39 +0300983EXPORT_SYMBOL(omap_mcbsp_stop);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100984
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100985/* polled mcbsp i/o operations */
986int omap_mcbsp_pollwrite(unsigned int id, u16 buf)
987{
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300988 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +0300989
990 if (!omap_mcbsp_check_valid_id(id)) {
991 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
992 return -ENODEV;
993 }
994
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300995 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +0300996
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800997 MCBSP_WRITE(mcbsp, DXR1, buf);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100998 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -0800999 if (MCBSP_READ(mcbsp, SPCR2) & XSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001000 /* clear error */
Janusz Krzysztofik0841cb82010-02-23 15:50:38 +00001001 MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001002 /* resend */
1003 return -1;
1004 } else {
1005 /* wait for transmit confirmation */
1006 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001007 while (!(MCBSP_READ(mcbsp, SPCR2) & XRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001008 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001009 MCBSP_WRITE(mcbsp, SPCR2,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001010 MCBSP_READ_CACHE(mcbsp, SPCR2) &
1011 (~XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001012 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001013 MCBSP_WRITE(mcbsp, SPCR2,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001014 MCBSP_READ_CACHE(mcbsp, SPCR2) |
1015 (XRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001016 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001017 dev_err(mcbsp->dev, "Could not write to"
1018 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001019 return -2;
1020 }
1021 }
1022 }
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001023
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001024 return 0;
1025}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001026EXPORT_SYMBOL(omap_mcbsp_pollwrite);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001027
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001028int omap_mcbsp_pollread(unsigned int id, u16 *buf)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001029{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001030 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001031
1032 if (!omap_mcbsp_check_valid_id(id)) {
1033 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1034 return -ENODEV;
1035 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001036 mcbsp = id_to_mcbsp_ptr(id);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001037
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001038 /* if frame sync error - clear the error */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001039 if (MCBSP_READ(mcbsp, SPCR1) & RSYNC_ERR) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001040 /* clear error */
Janusz Krzysztofik0841cb82010-02-23 15:50:38 +00001041 MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001042 /* resend */
1043 return -1;
1044 } else {
1045 /* wait for recieve confirmation */
1046 int attemps = 0;
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001047 while (!(MCBSP_READ(mcbsp, SPCR1) & RRDY)) {
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001048 if (attemps++ > 1000) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001049 MCBSP_WRITE(mcbsp, SPCR1,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001050 MCBSP_READ_CACHE(mcbsp, SPCR1) &
1051 (~RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001052 udelay(10);
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001053 MCBSP_WRITE(mcbsp, SPCR1,
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001054 MCBSP_READ_CACHE(mcbsp, SPCR1) |
1055 (RRST));
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001056 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001057 dev_err(mcbsp->dev, "Could not read from"
1058 " McBSP%d Register\n", mcbsp->id);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001059 return -2;
1060 }
1061 }
1062 }
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001063 *buf = MCBSP_READ(mcbsp, DRR1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001064
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001065 return 0;
1066}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001067EXPORT_SYMBOL(omap_mcbsp_pollread);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +01001068
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001069/*
1070 * IRQ based word transmission.
1071 */
1072void omap_mcbsp_xmit_word(unsigned int id, u32 word)
1073{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001074 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001075 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001076
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001077 if (!omap_mcbsp_check_valid_id(id)) {
1078 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001079 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001080 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001081
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001082 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001083 word_length = mcbsp->tx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001084
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001085 wait_for_completion(&mcbsp->tx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001086
1087 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001088 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
1089 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001090}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001091EXPORT_SYMBOL(omap_mcbsp_xmit_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001092
1093u32 omap_mcbsp_recv_word(unsigned int id)
1094{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001095 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001096 u16 word_lsb, word_msb = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001097 omap_mcbsp_word_length word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001098
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001099 if (!omap_mcbsp_check_valid_id(id)) {
1100 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1101 return -ENODEV;
1102 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001103 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001104
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001105 word_length = mcbsp->rx_word_length;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001106
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001107 wait_for_completion(&mcbsp->rx_irq_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001108
1109 if (word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001110 word_msb = MCBSP_READ(mcbsp, DRR2);
1111 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001112
1113 return (word_lsb | (word_msb << 16));
1114}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001115EXPORT_SYMBOL(omap_mcbsp_recv_word);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001116
Tony Lindgren120db2c2006-04-02 17:46:27 +01001117int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word)
1118{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001119 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001120 omap_mcbsp_word_length tx_word_length;
1121 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001122 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
1123
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001124 if (!omap_mcbsp_check_valid_id(id)) {
1125 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1126 return -ENODEV;
1127 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001128 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001129 tx_word_length = mcbsp->tx_word_length;
1130 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001131
Tony Lindgren120db2c2006-04-02 17:46:27 +01001132 if (tx_word_length != rx_word_length)
1133 return -EINVAL;
1134
1135 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001136 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001137 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001138 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001139 if (attempts++ > 1000) {
1140 /* We must reset the transmitter */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001141 MCBSP_WRITE(mcbsp, SPCR2,
1142 MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001143 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001144 MCBSP_WRITE(mcbsp, SPCR2,
1145 MCBSP_READ_CACHE(mcbsp, SPCR2) | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001146 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001147 dev_err(mcbsp->dev, "McBSP%d transmitter not "
1148 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001149 return -EAGAIN;
1150 }
1151 }
1152
1153 /* Now we can push the data */
1154 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001155 MCBSP_WRITE(mcbsp, DXR2, word >> 16);
1156 MCBSP_WRITE(mcbsp, DXR1, word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001157
1158 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001159 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001160 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001161 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001162 if (attempts++ > 1000) {
1163 /* We must reset the receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001164 MCBSP_WRITE(mcbsp, SPCR1,
1165 MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001166 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001167 MCBSP_WRITE(mcbsp, SPCR1,
1168 MCBSP_READ_CACHE(mcbsp, SPCR1) | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001169 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001170 dev_err(mcbsp->dev, "McBSP%d receiver not "
1171 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001172 return -EAGAIN;
1173 }
1174 }
1175
1176 /* Receiver is ready, let's read the dummy data */
1177 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001178 word_msb = MCBSP_READ(mcbsp, DRR2);
1179 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001180
1181 return 0;
1182}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001183EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001184
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001185int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 *word)
Tony Lindgren120db2c2006-04-02 17:46:27 +01001186{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001187 struct omap_mcbsp *mcbsp;
Russell Kingd592dd12008-09-04 14:25:42 +01001188 u32 clock_word = 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001189 omap_mcbsp_word_length tx_word_length;
1190 omap_mcbsp_word_length rx_word_length;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001191 u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0;
1192
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001193 if (!omap_mcbsp_check_valid_id(id)) {
1194 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1195 return -ENODEV;
1196 }
1197
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001198 mcbsp = id_to_mcbsp_ptr(id);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001199
1200 tx_word_length = mcbsp->tx_word_length;
1201 rx_word_length = mcbsp->rx_word_length;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001202
Tony Lindgren120db2c2006-04-02 17:46:27 +01001203 if (tx_word_length != rx_word_length)
1204 return -EINVAL;
1205
1206 /* First we wait for the transmitter to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001207 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001208 while (!(spcr2 & XRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001209 spcr2 = MCBSP_READ(mcbsp, SPCR2);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001210 if (attempts++ > 1000) {
1211 /* We must reset the transmitter */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001212 MCBSP_WRITE(mcbsp, SPCR2,
1213 MCBSP_READ_CACHE(mcbsp, SPCR2) & (~XRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001214 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001215 MCBSP_WRITE(mcbsp, SPCR2,
1216 MCBSP_READ_CACHE(mcbsp, SPCR2) | XRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001217 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001218 dev_err(mcbsp->dev, "McBSP%d transmitter not "
1219 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001220 return -EAGAIN;
1221 }
1222 }
1223
1224 /* We first need to enable the bus clock */
1225 if (tx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001226 MCBSP_WRITE(mcbsp, DXR2, clock_word >> 16);
1227 MCBSP_WRITE(mcbsp, DXR1, clock_word & 0xffff);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001228
1229 /* We wait for the receiver to be ready */
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001230 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001231 while (!(spcr1 & RRDY)) {
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001232 spcr1 = MCBSP_READ(mcbsp, SPCR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001233 if (attempts++ > 1000) {
1234 /* We must reset the receiver */
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001235 MCBSP_WRITE(mcbsp, SPCR1,
1236 MCBSP_READ_CACHE(mcbsp, SPCR1) & (~RRST));
Tony Lindgren120db2c2006-04-02 17:46:27 +01001237 udelay(10);
Janusz Krzysztofik96fbd742010-02-15 10:03:33 -08001238 MCBSP_WRITE(mcbsp, SPCR1,
1239 MCBSP_READ_CACHE(mcbsp, SPCR1) | RRST);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001240 udelay(10);
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001241 dev_err(mcbsp->dev, "McBSP%d receiver not "
1242 "ready\n", mcbsp->id);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001243 return -EAGAIN;
1244 }
1245 }
1246
1247 /* Receiver is ready, there is something for us */
1248 if (rx_word_length > OMAP_MCBSP_WORD_16)
Janusz Krzysztofik8ea32002010-02-15 10:03:32 -08001249 word_msb = MCBSP_READ(mcbsp, DRR2);
1250 word_lsb = MCBSP_READ(mcbsp, DRR1);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001251
1252 word[0] = (word_lsb | (word_msb << 16));
1253
1254 return 0;
1255}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001256EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll);
Tony Lindgren120db2c2006-04-02 17:46:27 +01001257
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001258/*
1259 * Simple DMA based buffer rx/tx routines.
1260 * Nothing fancy, just a single buffer tx/rx through DMA.
1261 * The DMA resources are released once the transfer is done.
1262 * For anything fancier, you should use your own customized DMA
1263 * routines and callbacks.
1264 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001265int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer,
1266 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001267{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001268 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001269 int dma_tx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001270 int src_port = 0;
1271 int dest_port = 0;
1272 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001273
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001274 if (!omap_mcbsp_check_valid_id(id)) {
1275 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1276 return -ENODEV;
1277 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001278 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001279
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001280 if (omap_request_dma(mcbsp->dma_tx_sync, "McBSP TX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001281 omap_mcbsp_tx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001282 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001283 &dma_tx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001284 dev_err(mcbsp->dev, " Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001285 "McBSP%d TX. Trying IRQ based TX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001286 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001287 return -EAGAIN;
1288 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001289 mcbsp->dma_tx_lch = dma_tx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001290
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001291 dev_err(mcbsp->dev, "McBSP%d TX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001292 dma_tx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001293
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001294 init_completion(&mcbsp->tx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001295
Tony Lindgren120db2c2006-04-02 17:46:27 +01001296 if (cpu_class_is_omap1()) {
1297 src_port = OMAP_DMA_PORT_TIPB;
1298 dest_port = OMAP_DMA_PORT_EMIFF;
1299 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001300 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001301 sync_dev = mcbsp->dma_tx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001302
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001303 omap_set_dma_transfer_params(mcbsp->dma_tx_lch,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001304 OMAP_DMA_DATA_TYPE_S16,
1305 length >> 1, 1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001306 OMAP_DMA_SYNC_ELEMENT,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001307 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001308
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001309 omap_set_dma_dest_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001310 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001311 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001312 mcbsp->phys_base + OMAP_MCBSP_REG_DXR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001313 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001314
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001315 omap_set_dma_src_params(mcbsp->dma_tx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001316 dest_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001317 OMAP_DMA_AMODE_POST_INC,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001318 buffer,
1319 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001320
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001321 omap_start_dma(mcbsp->dma_tx_lch);
1322 wait_for_completion(&mcbsp->tx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001323
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001324 return 0;
1325}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001326EXPORT_SYMBOL(omap_mcbsp_xmit_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001327
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001328int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer,
1329 unsigned int length)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001330{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001331 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001332 int dma_rx_ch;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001333 int src_port = 0;
1334 int dest_port = 0;
1335 int sync_dev = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001336
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001337 if (!omap_mcbsp_check_valid_id(id)) {
1338 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
1339 return -ENODEV;
1340 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001341 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001342
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001343 if (omap_request_dma(mcbsp->dma_rx_sync, "McBSP RX",
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001344 omap_mcbsp_rx_dma_callback,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001345 mcbsp,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001346 &dma_rx_ch)) {
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001347 dev_err(mcbsp->dev, "Unable to request DMA channel for "
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001348 "McBSP%d RX. Trying IRQ based RX\n",
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001349 mcbsp->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001350 return -EAGAIN;
1351 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001352 mcbsp->dma_rx_lch = dma_rx_ch;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001353
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001354 dev_err(mcbsp->dev, "McBSP%d RX DMA on channel %d\n", mcbsp->id,
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001355 dma_rx_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001356
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001357 init_completion(&mcbsp->rx_dma_completion);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001358
Tony Lindgren120db2c2006-04-02 17:46:27 +01001359 if (cpu_class_is_omap1()) {
1360 src_port = OMAP_DMA_PORT_TIPB;
1361 dest_port = OMAP_DMA_PORT_EMIFF;
1362 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001363 if (cpu_class_is_omap2())
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001364 sync_dev = mcbsp->dma_rx_sync;
Tony Lindgren120db2c2006-04-02 17:46:27 +01001365
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001366 omap_set_dma_transfer_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001367 OMAP_DMA_DATA_TYPE_S16,
1368 length >> 1, 1,
1369 OMAP_DMA_SYNC_ELEMENT,
1370 sync_dev, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001371
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001372 omap_set_dma_src_params(mcbsp->dma_rx_lch,
Tony Lindgren120db2c2006-04-02 17:46:27 +01001373 src_port,
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001374 OMAP_DMA_AMODE_CONSTANT,
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001375 mcbsp->phys_base + OMAP_MCBSP_REG_DRR1,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001376 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001377
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001378 omap_set_dma_dest_params(mcbsp->dma_rx_lch,
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001379 dest_port,
1380 OMAP_DMA_AMODE_POST_INC,
1381 buffer,
1382 0, 0);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001383
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001384 omap_start_dma(mcbsp->dma_rx_lch);
1385 wait_for_completion(&mcbsp->rx_dma_completion);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001386
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001387 return 0;
1388}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001389EXPORT_SYMBOL(omap_mcbsp_recv_buffer);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001390
1391/*
1392 * SPI wrapper.
1393 * Since SPI setup is much simpler than the generic McBSP one,
1394 * this wrapper just need an omap_mcbsp_spi_cfg structure as an input.
1395 * Once this is done, you can call omap_mcbsp_start().
1396 */
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001397void omap_mcbsp_set_spi_mode(unsigned int id,
1398 const struct omap_mcbsp_spi_cfg *spi_cfg)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001399{
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001400 struct omap_mcbsp *mcbsp;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001401 struct omap_mcbsp_reg_cfg mcbsp_cfg;
1402
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001403 if (!omap_mcbsp_check_valid_id(id)) {
1404 printk(KERN_ERR "%s: Invalid id (%d)\n", __func__, id + 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001405 return;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001406 }
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001407 mcbsp = id_to_mcbsp_ptr(id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001408
1409 memset(&mcbsp_cfg, 0, sizeof(struct omap_mcbsp_reg_cfg));
1410
1411 /* SPI has only one frame */
1412 mcbsp_cfg.rcr1 |= (RWDLEN1(spi_cfg->word_length) | RFRLEN1(0));
1413 mcbsp_cfg.xcr1 |= (XWDLEN1(spi_cfg->word_length) | XFRLEN1(0));
1414
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001415 /* Clock stop mode */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001416 if (spi_cfg->clk_stp_mode == OMAP_MCBSP_CLK_STP_MODE_NO_DELAY)
1417 mcbsp_cfg.spcr1 |= (1 << 12);
1418 else
1419 mcbsp_cfg.spcr1 |= (3 << 11);
1420
1421 /* Set clock parities */
1422 if (spi_cfg->rx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1423 mcbsp_cfg.pcr0 |= CLKRP;
1424 else
1425 mcbsp_cfg.pcr0 &= ~CLKRP;
1426
1427 if (spi_cfg->tx_clock_polarity == OMAP_MCBSP_CLK_RISING)
1428 mcbsp_cfg.pcr0 &= ~CLKXP;
1429 else
1430 mcbsp_cfg.pcr0 |= CLKXP;
1431
1432 /* Set SCLKME to 0 and CLKSM to 1 */
1433 mcbsp_cfg.pcr0 &= ~SCLKME;
1434 mcbsp_cfg.srgr2 |= CLKSM;
1435
1436 /* Set FSXP */
1437 if (spi_cfg->fsx_polarity == OMAP_MCBSP_FS_ACTIVE_HIGH)
1438 mcbsp_cfg.pcr0 &= ~FSXP;
1439 else
1440 mcbsp_cfg.pcr0 |= FSXP;
1441
1442 if (spi_cfg->spi_mode == OMAP_MCBSP_SPI_MASTER) {
1443 mcbsp_cfg.pcr0 |= CLKXM;
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001444 mcbsp_cfg.srgr1 |= CLKGDV(spi_cfg->clk_div - 1);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001445 mcbsp_cfg.pcr0 |= FSXM;
1446 mcbsp_cfg.srgr2 &= ~FSGM;
1447 mcbsp_cfg.xcr2 |= XDATDLY(1);
1448 mcbsp_cfg.rcr2 |= RDATDLY(1);
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001449 } else {
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001450 mcbsp_cfg.pcr0 &= ~CLKXM;
1451 mcbsp_cfg.srgr1 |= CLKGDV(1);
1452 mcbsp_cfg.pcr0 &= ~FSXM;
1453 mcbsp_cfg.xcr2 &= ~XDATDLY(3);
1454 mcbsp_cfg.rcr2 &= ~RDATDLY(3);
1455 }
1456
1457 mcbsp_cfg.xcr2 &= ~XPHASE;
1458 mcbsp_cfg.rcr2 &= ~RPHASE;
1459
1460 omap_mcbsp_config(id, &mcbsp_cfg);
1461}
Eduardo Valentinfb78d802008-07-03 12:24:39 +03001462EXPORT_SYMBOL(omap_mcbsp_set_spi_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001463
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001464#ifdef CONFIG_ARCH_OMAP3
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001465#define max_thres(m) (mcbsp->pdata->buffer_size)
1466#define valid_threshold(m, val) ((val) <= max_thres(m))
1467#define THRESHOLD_PROP_BUILDER(prop) \
1468static ssize_t prop##_show(struct device *dev, \
1469 struct device_attribute *attr, char *buf) \
1470{ \
1471 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1472 \
1473 return sprintf(buf, "%u\n", mcbsp->prop); \
1474} \
1475 \
1476static ssize_t prop##_store(struct device *dev, \
1477 struct device_attribute *attr, \
1478 const char *buf, size_t size) \
1479{ \
1480 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \
1481 unsigned long val; \
1482 int status; \
1483 \
1484 status = strict_strtoul(buf, 0, &val); \
1485 if (status) \
1486 return status; \
1487 \
1488 if (!valid_threshold(mcbsp, val)) \
1489 return -EDOM; \
1490 \
1491 mcbsp->prop = val; \
1492 return size; \
1493} \
1494 \
1495static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store);
1496
1497THRESHOLD_PROP_BUILDER(max_tx_thres);
1498THRESHOLD_PROP_BUILDER(max_rx_thres);
1499
Jarkko Nikula9b300502009-08-24 17:45:50 +03001500static const char *dma_op_modes[] = {
1501 "element", "threshold", "frame",
1502};
1503
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001504static ssize_t dma_op_mode_show(struct device *dev,
1505 struct device_attribute *attr, char *buf)
1506{
1507 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001508 int dma_op_mode, i = 0;
1509 ssize_t len = 0;
1510 const char * const *s;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001511
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001512 dma_op_mode = mcbsp->dma_op_mode;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001513
Jarkko Nikula9b300502009-08-24 17:45:50 +03001514 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) {
1515 if (dma_op_mode == i)
1516 len += sprintf(buf + len, "[%s] ", *s);
1517 else
1518 len += sprintf(buf + len, "%s ", *s);
1519 }
1520 len += sprintf(buf + len, "\n");
1521
1522 return len;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001523}
1524
1525static ssize_t dma_op_mode_store(struct device *dev,
1526 struct device_attribute *attr,
1527 const char *buf, size_t size)
1528{
1529 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
Jarkko Nikula9b300502009-08-24 17:45:50 +03001530 const char * const *s;
1531 int i = 0;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001532
Jarkko Nikula9b300502009-08-24 17:45:50 +03001533 for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++)
1534 if (sysfs_streq(buf, *s))
1535 break;
1536
1537 if (i == ARRAY_SIZE(dma_op_modes))
1538 return -EINVAL;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001539
1540 spin_lock_irq(&mcbsp->lock);
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001541 if (!mcbsp->free) {
1542 size = -EBUSY;
1543 goto unlock;
1544 }
Jarkko Nikula9b300502009-08-24 17:45:50 +03001545 mcbsp->dma_op_mode = i;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001546
1547unlock:
1548 spin_unlock_irq(&mcbsp->lock);
1549
1550 return size;
1551}
1552
1553static DEVICE_ATTR(dma_op_mode, 0644, dma_op_mode_show, dma_op_mode_store);
1554
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001555static ssize_t st_taps_show(struct device *dev,
1556 struct device_attribute *attr, char *buf)
1557{
1558 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1559 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1560 ssize_t status = 0;
1561 int i;
1562
1563 spin_lock_irq(&mcbsp->lock);
1564 for (i = 0; i < st_data->nr_taps; i++)
1565 status += sprintf(&buf[status], (i ? ", %d" : "%d"),
1566 st_data->taps[i]);
1567 if (i)
1568 status += sprintf(&buf[status], "\n");
1569 spin_unlock_irq(&mcbsp->lock);
1570
1571 return status;
1572}
1573
1574static ssize_t st_taps_store(struct device *dev,
1575 struct device_attribute *attr,
1576 const char *buf, size_t size)
1577{
1578 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev);
1579 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1580 int val, tmp, status, i = 0;
1581
1582 spin_lock_irq(&mcbsp->lock);
1583 memset(st_data->taps, 0, sizeof(st_data->taps));
1584 st_data->nr_taps = 0;
1585
1586 do {
1587 status = sscanf(buf, "%d%n", &val, &tmp);
1588 if (status < 0 || status == 0) {
1589 size = -EINVAL;
1590 goto out;
1591 }
1592 if (val < -32768 || val > 32767) {
1593 size = -EINVAL;
1594 goto out;
1595 }
1596 st_data->taps[i++] = val;
1597 buf += tmp;
1598 if (*buf != ',')
1599 break;
1600 buf++;
1601 } while (1);
1602
1603 st_data->nr_taps = i;
1604
1605out:
1606 spin_unlock_irq(&mcbsp->lock);
1607
1608 return size;
1609}
1610
1611static DEVICE_ATTR(st_taps, 0644, st_taps_show, st_taps_store);
1612
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001613static const struct attribute *additional_attrs[] = {
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001614 &dev_attr_max_tx_thres.attr,
1615 &dev_attr_max_rx_thres.attr,
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001616 &dev_attr_dma_op_mode.attr,
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001617 NULL,
1618};
1619
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001620static const struct attribute_group additional_attr_group = {
1621 .attrs = (struct attribute **)additional_attrs,
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001622};
1623
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001624static inline int __devinit omap_additional_add(struct device *dev)
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001625{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001626 return sysfs_create_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001627}
1628
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001629static inline void __devexit omap_additional_remove(struct device *dev)
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001630{
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001631 sysfs_remove_group(&dev->kobj, &additional_attr_group);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001632}
1633
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001634static const struct attribute *sidetone_attrs[] = {
1635 &dev_attr_st_taps.attr,
1636 NULL,
1637};
1638
1639static const struct attribute_group sidetone_attr_group = {
1640 .attrs = (struct attribute **)sidetone_attrs,
1641};
1642
Manjunath Kondaiah Gb0a330d2010-10-08 10:00:19 -07001643static int __devinit omap_st_add(struct omap_mcbsp *mcbsp)
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001644{
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001645 struct platform_device *pdev;
1646 struct resource *res;
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001647 struct omap_mcbsp_st_data *st_data;
1648 int err;
1649
1650 st_data = kzalloc(sizeof(*mcbsp->st_data), GFP_KERNEL);
1651 if (!st_data) {
1652 err = -ENOMEM;
1653 goto err1;
1654 }
1655
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001656 pdev = container_of(mcbsp->dev, struct platform_device, dev);
1657
1658 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone");
1659 st_data->io_base_st = ioremap(res->start, resource_size(res));
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001660 if (!st_data->io_base_st) {
1661 err = -ENOMEM;
1662 goto err2;
1663 }
1664
1665 err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
1666 if (err)
1667 goto err3;
1668
1669 mcbsp->st_data = st_data;
1670 return 0;
1671
1672err3:
1673 iounmap(st_data->io_base_st);
1674err2:
1675 kfree(st_data);
1676err1:
1677 return err;
1678
1679}
1680
1681static void __devexit omap_st_remove(struct omap_mcbsp *mcbsp)
1682{
1683 struct omap_mcbsp_st_data *st_data = mcbsp->st_data;
1684
1685 if (st_data) {
1686 sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
1687 iounmap(st_data->io_base_st);
1688 kfree(st_data);
1689 }
1690}
1691
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001692static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp)
1693{
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001694 mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT;
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001695 if (cpu_is_omap34xx()) {
Peter Ujfalusi451fd822010-06-03 07:39:33 +03001696 /*
1697 * Initially configure the maximum thresholds to a safe value.
1698 * The McBSP FIFO usage with these values should not go under
1699 * 16 locations.
1700 * If the whole FIFO without safety buffer is used, than there
1701 * is a possibility that the DMA will be not able to push the
1702 * new data on time, causing channel shifts in runtime.
1703 */
1704 mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
1705 mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
Peter Ujfalusi98cb20e2009-08-20 16:18:14 +03001706 /*
1707 * REVISIT: Set dmap_op_mode to THRESHOLD as default
1708 * for mcbsp2 instances.
1709 */
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001710 if (omap_additional_add(mcbsp->dev))
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001711 dev_warn(mcbsp->dev,
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001712 "Unable to create additional controls\n");
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001713
1714 if (mcbsp->id == 2 || mcbsp->id == 3)
1715 if (omap_st_add(mcbsp))
1716 dev_warn(mcbsp->dev,
1717 "Unable to create sidetone controls\n");
1718
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001719 } else {
1720 mcbsp->max_tx_thres = -EINVAL;
1721 mcbsp->max_rx_thres = -EINVAL;
1722 }
1723}
1724
1725static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp)
1726{
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001727 if (cpu_is_omap34xx()) {
Eduardo Valentin4c8200a2009-08-20 16:18:13 +03001728 omap_additional_remove(mcbsp->dev);
Eero Nurkkalad912fa92010-02-22 12:21:11 +00001729
1730 if (mcbsp->id == 2 || mcbsp->id == 3)
1731 omap_st_remove(mcbsp);
1732 }
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001733}
1734#else
1735static inline void __devinit omap34xx_device_init(struct omap_mcbsp *mcbsp) {}
1736static inline void __devexit omap34xx_device_exit(struct omap_mcbsp *mcbsp) {}
Tony Lindgrena8eb7ca2010-02-12 12:26:48 -08001737#endif /* CONFIG_ARCH_OMAP3 */
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001738
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001739/*
1740 * McBSP1 and McBSP3 are directly mapped on 1610 and 1510.
1741 * 730 has only 2 McBSP, and both of them are MPU peripherals.
1742 */
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001743static int __devinit omap_mcbsp_probe(struct platform_device *pdev)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001744{
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001745 struct omap_mcbsp_platform_data *pdata = pdev->dev.platform_data;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001746 struct omap_mcbsp *mcbsp;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001747 int id = pdev->id - 1;
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001748 struct resource *res;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001749 int ret = 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001750
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001751 if (!pdata) {
1752 dev_err(&pdev->dev, "McBSP device initialized without"
1753 "platform data\n");
1754 ret = -EINVAL;
1755 goto exit;
1756 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001757
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001758 dev_dbg(&pdev->dev, "Initializing OMAP McBSP (%d).\n", pdev->id);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001759
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001760 if (id >= omap_mcbsp_count) {
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001761 dev_err(&pdev->dev, "Invalid McBSP device id (%d)\n", id);
1762 ret = -EINVAL;
1763 goto exit;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001764 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001765
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001766 mcbsp = kzalloc(sizeof(struct omap_mcbsp), GFP_KERNEL);
1767 if (!mcbsp) {
1768 ret = -ENOMEM;
1769 goto exit;
1770 }
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001771
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001772 spin_lock_init(&mcbsp->lock);
1773 mcbsp->id = id + 1;
Shubhrajyoti D6722a722010-12-07 16:25:41 -08001774 mcbsp->free = true;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001775 mcbsp->dma_tx_lch = -1;
1776 mcbsp->dma_rx_lch = -1;
1777
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001778 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
1779 if (!res) {
1780 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1781 if (!res) {
1782 dev_err(&pdev->dev, "%s:mcbsp%d has invalid memory"
1783 "resource\n", __func__, pdev->id);
1784 ret = -ENOMEM;
1785 goto exit;
1786 }
1787 }
1788 mcbsp->phys_base = res->start;
1789 omap_mcbsp_cache_size = resource_size(res);
1790 mcbsp->io_base = ioremap(res->start, resource_size(res));
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001791 if (!mcbsp->io_base) {
Russell Kingd592dd12008-09-04 14:25:42 +01001792 ret = -ENOMEM;
1793 goto err_ioremap;
1794 }
1795
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001796 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
1797 if (!res)
1798 mcbsp->phys_dma_base = mcbsp->phys_base;
1799 else
1800 mcbsp->phys_dma_base = res->start;
1801
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001802 /* Default I/O is IRQ based */
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001803 mcbsp->io_type = OMAP_MCBSP_IRQ_IO;
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001804
1805 mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
1806 mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
1807
Kishon Vijay Abraham Icb7e9de2011-02-24 15:16:50 +05301808 /* From OMAP4 there will be a single irq line */
1809 if (mcbsp->tx_irq == -ENXIO)
1810 mcbsp->tx_irq = platform_get_irq(pdev, 0);
1811
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001812 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
1813 if (!res) {
1814 dev_err(&pdev->dev, "%s:mcbsp%d has invalid rx DMA channel\n",
1815 __func__, pdev->id);
1816 ret = -ENODEV;
1817 goto err_res;
1818 }
1819 mcbsp->dma_rx_sync = res->start;
1820
1821 res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
1822 if (!res) {
1823 dev_err(&pdev->dev, "%s:mcbsp%d has invalid tx DMA channel\n",
1824 __func__, pdev->id);
1825 ret = -ENODEV;
1826 goto err_res;
1827 }
1828 mcbsp->dma_tx_sync = res->start;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001829
Russell Kingb820ce42009-01-23 10:26:46 +00001830 mcbsp->iclk = clk_get(&pdev->dev, "ick");
1831 if (IS_ERR(mcbsp->iclk)) {
1832 ret = PTR_ERR(mcbsp->iclk);
1833 dev_err(&pdev->dev, "unable to get ick: %d\n", ret);
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001834 goto err_res;
Russell Kingb820ce42009-01-23 10:26:46 +00001835 }
Stanley.Miao06151152009-01-29 08:57:12 -08001836
Russell Kingb820ce42009-01-23 10:26:46 +00001837 mcbsp->fclk = clk_get(&pdev->dev, "fck");
1838 if (IS_ERR(mcbsp->fclk)) {
1839 ret = PTR_ERR(mcbsp->fclk);
1840 dev_err(&pdev->dev, "unable to get fck: %d\n", ret);
1841 goto err_fclk;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001842 }
1843
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001844 mcbsp->pdata = pdata;
1845 mcbsp->dev = &pdev->dev;
Russell Kingb820ce42009-01-23 10:26:46 +00001846 mcbsp_ptr[id] = mcbsp;
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001847 platform_set_drvdata(pdev, mcbsp);
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001848
1849 /* Initialize mcbsp properties for OMAP34XX if needed / applicable */
1850 omap34xx_device_init(mcbsp);
1851
Russell Kingd592dd12008-09-04 14:25:42 +01001852 return 0;
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001853
Russell Kingb820ce42009-01-23 10:26:46 +00001854err_fclk:
1855 clk_put(mcbsp->iclk);
Kishon Vijay Abraham I3cf32bb2011-02-24 12:51:45 -08001856err_res:
Chandra Shekharb4b58f52008-10-08 10:01:39 +03001857 iounmap(mcbsp->io_base);
Russell Kingd592dd12008-09-04 14:25:42 +01001858err_ioremap:
Russell Kingb820ce42009-01-23 10:26:46 +00001859 kfree(mcbsp);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001860exit:
1861 return ret;
1862}
1863
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001864static int __devexit omap_mcbsp_remove(struct platform_device *pdev)
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001865{
1866 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
1867
1868 platform_set_drvdata(pdev, NULL);
1869 if (mcbsp) {
1870
1871 if (mcbsp->pdata && mcbsp->pdata->ops &&
1872 mcbsp->pdata->ops->free)
1873 mcbsp->pdata->ops->free(mcbsp->id);
1874
Eduardo Valentina1a56f5f2009-08-20 16:18:11 +03001875 omap34xx_device_exit(mcbsp);
1876
Russell Kingb820ce42009-01-23 10:26:46 +00001877 clk_put(mcbsp->fclk);
1878 clk_put(mcbsp->iclk);
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001879
Russell Kingd592dd12008-09-04 14:25:42 +01001880 iounmap(mcbsp->io_base);
Jarkko Nikula5f3b7282010-12-07 16:25:40 -08001881 kfree(mcbsp);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001882 }
1883
1884 return 0;
1885}
1886
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001887static struct platform_driver omap_mcbsp_driver = {
1888 .probe = omap_mcbsp_probe,
Uwe Kleine-König25cef222008-10-08 10:01:39 +03001889 .remove = __devexit_p(omap_mcbsp_remove),
Eduardo Valentinbc5d0c82008-07-03 12:24:39 +03001890 .driver = {
1891 .name = "omap-mcbsp",
1892 },
1893};
1894
1895int __init omap_mcbsp_init(void)
1896{
1897 /* Register the McBSP driver */
1898 return platform_driver_register(&omap_mcbsp_driver);
1899}