~ubuntu-branches/debian/wheezy/linux-2.6/wheezy

« back to all changes in this revision

Viewing changes to sound/soc/tegra/tegra_pcm.c

  • Committer: Bazaar Package Importer
  • Author(s): Ben Hutchings, Ben Hutchings, Aurelien Jarno
  • Date: 2011-06-07 12:14:05 UTC
  • mfrom: (43.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110607121405-i3h1rd7nrnd2b73h
Tags: 2.6.39-2
[ Ben Hutchings ]
* [x86] Enable BACKLIGHT_APPLE, replacing BACKLIGHT_MBP_NVIDIA
  (Closes: #627492)
* cgroups: Disable memory resource controller by default. Allow it
  to be enabled using kernel parameter 'cgroup_enable=memory'.
* rt2800usb: Enable support for more USB devices including
  Linksys WUSB600N (Closes: #596626) (this change was accidentally
  omitted from 2.6.39-1)
* [x86] Remove Celeron from list of processors supporting PAE. Most
  'Celeron M' models do not.
* Update debconf template translations:
  - Swedish (Martin Bagge) (Closes: #628932)
  - French (David Prévot) (Closes: #628191)
* aufs: Update for 2.6.39 (Closes: #627837)
* Add stable 2.6.39.1, including:
  - ext4: dont set PageUptodate in ext4_end_bio()
  - pata_cmd64x: fix boot crash on parisc (Closes: #622997, #622745)
  - ext3: Fix fs corruption when make_indexed_dir() fails
  - netfilter: nf_ct_sip: validate Content-Length in TCP SIP messages
  - sctp: fix race between sctp_bind_addr_free() and
    sctp_bind_addr_conflict()
  - sctp: fix memory leak of the ASCONF queue when free asoc
  - md/bitmap: fix saving of events_cleared and other state
  - cdc_acm: Fix oops when Droids MuIn LCD is connected
  - cx88: Fix conversion from BKL to fine-grained locks (Closes: #619827)
  - keys: Set cred->user_ns in key_replace_session_keyring (CVE-2011-2184)
  - tmpfs: fix race between truncate and writepage
  - nfs41: Correct offset for LAYOUTCOMMIT
  - xen/mmu: fix a race window causing leave_mm BUG()
  - ext4: fix possible use-after-free in ext4_remove_li_request()
  For the complete list of changes, see:
   http://www.kernel.org/pub/linux/kernel/v2.6/ChangeLog-2.6.39.1
* Bump ABI to 2
* netfilter: Enable IP_SET, IP_SET_BITMAP_IP, IP_SET_BITMAP_IPMAC,
  IP_SET_BITMAP_PORT, IP_SET_HASH_IP, IP_SET_HASH_IPPORT,
  IP_SET_HASH_IPPORTIP, IP_SET_HASH_IPPORTNET, IP_SET_HASH_NET,
  IP_SET_HASH_NETPORT, IP_SET_LIST_SET, NETFILTER_XT_SET as modules
  (Closes: #629401)

[ Aurelien Jarno ]
* [mipsel/loongson-2f] Disable_SCSI_LPFC to workaround GCC ICE.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * tegra_pcm.c - Tegra PCM driver
 
3
 *
 
4
 * Author: Stephen Warren <swarren@nvidia.com>
 
5
 * Copyright (C) 2010 - NVIDIA, Inc.
 
6
 *
 
7
 * Based on code copyright/by:
 
8
 *
 
9
 * Copyright (c) 2009-2010, NVIDIA Corporation.
 
10
 * Scott Peterson <speterson@nvidia.com>
 
11
 * Vijay Mali <vmali@nvidia.com>
 
12
 *
 
13
 * Copyright (C) 2010 Google, Inc.
 
14
 * Iliyan Malchev <malchev@google.com>
 
15
 *
 
16
 * This program is free software; you can redistribute it and/or
 
17
 * modify it under the terms of the GNU General Public License
 
18
 * version 2 as published by the Free Software Foundation.
 
19
 *
 
20
 * This program is distributed in the hope that it will be useful, but
 
21
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
23
 * General Public License for more details.
 
24
 *
 
25
 * You should have received a copy of the GNU General Public License
 
26
 * along with this program; if not, write to the Free Software
 
27
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 
28
 * 02110-1301 USA
 
29
 *
 
30
 */
 
31
 
 
32
#include <linux/module.h>
 
33
#include <linux/dma-mapping.h>
 
34
#include <linux/slab.h>
 
35
#include <sound/core.h>
 
36
#include <sound/pcm.h>
 
37
#include <sound/pcm_params.h>
 
38
#include <sound/soc.h>
 
39
 
 
40
#include "tegra_pcm.h"
 
41
 
 
42
#define DRV_NAME "tegra-pcm-audio"
 
43
 
 
44
static const struct snd_pcm_hardware tegra_pcm_hardware = {
 
45
        .info                   = SNDRV_PCM_INFO_MMAP |
 
46
                                  SNDRV_PCM_INFO_MMAP_VALID |
 
47
                                  SNDRV_PCM_INFO_PAUSE |
 
48
                                  SNDRV_PCM_INFO_RESUME |
 
49
                                  SNDRV_PCM_INFO_INTERLEAVED,
 
50
        .formats                = SNDRV_PCM_FMTBIT_S16_LE,
 
51
        .channels_min           = 2,
 
52
        .channels_max           = 2,
 
53
        .period_bytes_min       = 1024,
 
54
        .period_bytes_max       = PAGE_SIZE,
 
55
        .periods_min            = 2,
 
56
        .periods_max            = 8,
 
57
        .buffer_bytes_max       = PAGE_SIZE * 8,
 
58
        .fifo_size              = 4,
 
59
};
 
60
 
 
61
static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd)
 
62
{
 
63
        struct snd_pcm_substream *substream = prtd->substream;
 
64
        struct snd_dma_buffer *buf = &substream->dma_buffer;
 
65
        struct tegra_dma_req *dma_req;
 
66
        unsigned long addr;
 
67
 
 
68
        dma_req = &prtd->dma_req[prtd->dma_req_idx];
 
69
        prtd->dma_req_idx = 1 - prtd->dma_req_idx;
 
70
 
 
71
        addr = buf->addr + prtd->dma_pos;
 
72
        prtd->dma_pos += dma_req->size;
 
73
        if (prtd->dma_pos >= prtd->dma_pos_end)
 
74
                prtd->dma_pos = 0;
 
75
 
 
76
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 
77
                dma_req->source_addr = addr;
 
78
        else
 
79
                dma_req->dest_addr = addr;
 
80
 
 
81
        tegra_dma_enqueue_req(prtd->dma_chan, dma_req);
 
82
}
 
83
 
 
84
static void dma_complete_callback(struct tegra_dma_req *req)
 
85
{
 
86
        struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev;
 
87
        struct snd_pcm_substream *substream = prtd->substream;
 
88
        struct snd_pcm_runtime *runtime = substream->runtime;
 
89
 
 
90
        spin_lock(&prtd->lock);
 
91
 
 
92
        if (!prtd->running) {
 
93
                spin_unlock(&prtd->lock);
 
94
                return;
 
95
        }
 
96
 
 
97
        if (++prtd->period_index >= runtime->periods)
 
98
                prtd->period_index = 0;
 
99
 
 
100
        tegra_pcm_queue_dma(prtd);
 
101
 
 
102
        spin_unlock(&prtd->lock);
 
103
 
 
104
        snd_pcm_period_elapsed(substream);
 
105
}
 
106
 
 
107
static void setup_dma_tx_request(struct tegra_dma_req *req,
 
108
                                        struct tegra_pcm_dma_params * dmap)
 
109
{
 
110
        req->complete = dma_complete_callback;
 
111
        req->to_memory = false;
 
112
        req->dest_addr = dmap->addr;
 
113
        req->dest_wrap = dmap->wrap;
 
114
        req->source_bus_width = 32;
 
115
        req->source_wrap = 0;
 
116
        req->dest_bus_width = dmap->width;
 
117
        req->req_sel = dmap->req_sel;
 
118
}
 
119
 
 
120
static void setup_dma_rx_request(struct tegra_dma_req *req,
 
121
                                        struct tegra_pcm_dma_params * dmap)
 
122
{
 
123
        req->complete = dma_complete_callback;
 
124
        req->to_memory = true;
 
125
        req->source_addr = dmap->addr;
 
126
        req->dest_wrap = 0;
 
127
        req->source_bus_width = dmap->width;
 
128
        req->source_wrap = dmap->wrap;
 
129
        req->dest_bus_width = 32;
 
130
        req->req_sel = dmap->req_sel;
 
131
}
 
132
 
 
133
static int tegra_pcm_open(struct snd_pcm_substream *substream)
 
134
{
 
135
        struct snd_pcm_runtime *runtime = substream->runtime;
 
136
        struct tegra_runtime_data *prtd;
 
137
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 
138
        struct tegra_pcm_dma_params * dmap;
 
139
        int ret = 0;
 
140
 
 
141
        prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL);
 
142
        if (prtd == NULL)
 
143
                return -ENOMEM;
 
144
 
 
145
        runtime->private_data = prtd;
 
146
        prtd->substream = substream;
 
147
 
 
148
        spin_lock_init(&prtd->lock);
 
149
 
 
150
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 
151
                dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 
152
                setup_dma_tx_request(&prtd->dma_req[0], dmap);
 
153
                setup_dma_tx_request(&prtd->dma_req[1], dmap);
 
154
        } else {
 
155
                dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 
156
                setup_dma_rx_request(&prtd->dma_req[0], dmap);
 
157
                setup_dma_rx_request(&prtd->dma_req[1], dmap);
 
158
        }
 
159
 
 
160
        prtd->dma_req[0].dev = prtd;
 
161
        prtd->dma_req[1].dev = prtd;
 
162
 
 
163
        prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
 
164
        if (prtd->dma_chan == NULL) {
 
165
                ret = -ENOMEM;
 
166
                goto err;
 
167
        }
 
168
 
 
169
        /* Set HW params now that initialization is complete */
 
170
        snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
 
171
 
 
172
        /* Ensure that buffer size is a multiple of period size */
 
173
        ret = snd_pcm_hw_constraint_integer(runtime,
 
174
                                                SNDRV_PCM_HW_PARAM_PERIODS);
 
175
        if (ret < 0)
 
176
                goto err;
 
177
 
 
178
        return 0;
 
179
 
 
180
err:
 
181
        if (prtd->dma_chan) {
 
182
                tegra_dma_free_channel(prtd->dma_chan);
 
183
        }
 
184
 
 
185
        kfree(prtd);
 
186
 
 
187
        return ret;
 
188
}
 
189
 
 
190
static int tegra_pcm_close(struct snd_pcm_substream *substream)
 
191
{
 
192
        struct snd_pcm_runtime *runtime = substream->runtime;
 
193
        struct tegra_runtime_data *prtd = runtime->private_data;
 
194
 
 
195
        tegra_dma_free_channel(prtd->dma_chan);
 
196
 
 
197
        kfree(prtd);
 
198
 
 
199
        return 0;
 
200
}
 
201
 
 
202
static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
 
203
                                struct snd_pcm_hw_params *params)
 
204
{
 
205
        struct snd_pcm_runtime *runtime = substream->runtime;
 
206
        struct tegra_runtime_data *prtd = runtime->private_data;
 
207
 
 
208
        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 
209
 
 
210
        prtd->dma_req[0].size = params_period_bytes(params);
 
211
        prtd->dma_req[1].size = prtd->dma_req[0].size;
 
212
 
 
213
        return 0;
 
214
}
 
215
 
 
216
static int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
 
217
{
 
218
        snd_pcm_set_runtime_buffer(substream, NULL);
 
219
 
 
220
        return 0;
 
221
}
 
222
 
 
223
static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 
224
{
 
225
        struct snd_pcm_runtime *runtime = substream->runtime;
 
226
        struct tegra_runtime_data *prtd = runtime->private_data;
 
227
        unsigned long flags;
 
228
 
 
229
        switch (cmd) {
 
230
        case SNDRV_PCM_TRIGGER_START:
 
231
                prtd->dma_pos = 0;
 
232
                prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size);
 
233
                prtd->period_index = 0;
 
234
                prtd->dma_req_idx = 0;
 
235
                /* Fall-through */
 
236
        case SNDRV_PCM_TRIGGER_RESUME:
 
237
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 
238
                spin_lock_irqsave(&prtd->lock, flags);
 
239
                prtd->running = 1;
 
240
                spin_unlock_irqrestore(&prtd->lock, flags);
 
241
                tegra_pcm_queue_dma(prtd);
 
242
                tegra_pcm_queue_dma(prtd);
 
243
                break;
 
244
        case SNDRV_PCM_TRIGGER_STOP:
 
245
        case SNDRV_PCM_TRIGGER_SUSPEND:
 
246
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 
247
                spin_lock_irqsave(&prtd->lock, flags);
 
248
                prtd->running = 0;
 
249
                spin_unlock_irqrestore(&prtd->lock, flags);
 
250
                tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]);
 
251
                tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]);
 
252
                break;
 
253
        default:
 
254
                return -EINVAL;
 
255
        }
 
256
 
 
257
        return 0;
 
258
}
 
259
 
 
260
static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream)
 
261
{
 
262
        struct snd_pcm_runtime *runtime = substream->runtime;
 
263
        struct tegra_runtime_data *prtd = runtime->private_data;
 
264
 
 
265
        return prtd->period_index * runtime->period_size;
 
266
}
 
267
 
 
268
 
 
269
static int tegra_pcm_mmap(struct snd_pcm_substream *substream,
 
270
                                struct vm_area_struct *vma)
 
271
{
 
272
        struct snd_pcm_runtime *runtime = substream->runtime;
 
273
 
 
274
        return dma_mmap_writecombine(substream->pcm->card->dev, vma,
 
275
                                        runtime->dma_area,
 
276
                                        runtime->dma_addr,
 
277
                                        runtime->dma_bytes);
 
278
}
 
279
 
 
280
static struct snd_pcm_ops tegra_pcm_ops = {
 
281
        .open           = tegra_pcm_open,
 
282
        .close          = tegra_pcm_close,
 
283
        .ioctl          = snd_pcm_lib_ioctl,
 
284
        .hw_params      = tegra_pcm_hw_params,
 
285
        .hw_free        = tegra_pcm_hw_free,
 
286
        .trigger        = tegra_pcm_trigger,
 
287
        .pointer        = tegra_pcm_pointer,
 
288
        .mmap           = tegra_pcm_mmap,
 
289
};
 
290
 
 
291
static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 
292
{
 
293
        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 
294
        struct snd_dma_buffer *buf = &substream->dma_buffer;
 
295
        size_t size = tegra_pcm_hardware.buffer_bytes_max;
 
296
 
 
297
        buf->area = dma_alloc_writecombine(pcm->card->dev, size,
 
298
                                                &buf->addr, GFP_KERNEL);
 
299
        if (!buf->area)
 
300
                return -ENOMEM;
 
301
 
 
302
        buf->dev.type = SNDRV_DMA_TYPE_DEV;
 
303
        buf->dev.dev = pcm->card->dev;
 
304
        buf->private_data = NULL;
 
305
        buf->bytes = size;
 
306
 
 
307
        return 0;
 
308
}
 
309
 
 
310
static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 
311
{
 
312
        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 
313
        struct snd_dma_buffer *buf = &substream->dma_buffer;
 
314
 
 
315
        if (!buf->area)
 
316
                return;
 
317
 
 
318
        dma_free_writecombine(pcm->card->dev, buf->bytes,
 
319
                                buf->area, buf->addr);
 
320
        buf->area = NULL;
 
321
}
 
322
 
 
323
static u64 tegra_dma_mask = DMA_BIT_MASK(32);
 
324
 
 
325
static int tegra_pcm_new(struct snd_card *card,
 
326
                                struct snd_soc_dai *dai, struct snd_pcm *pcm)
 
327
{
 
328
        int ret = 0;
 
329
 
 
330
        if (!card->dev->dma_mask)
 
331
                card->dev->dma_mask = &tegra_dma_mask;
 
332
        if (!card->dev->coherent_dma_mask)
 
333
                card->dev->coherent_dma_mask = 0xffffffff;
 
334
 
 
335
        if (dai->driver->playback.channels_min) {
 
336
                ret = tegra_pcm_preallocate_dma_buffer(pcm,
 
337
                                                SNDRV_PCM_STREAM_PLAYBACK);
 
338
                if (ret)
 
339
                        goto err;
 
340
        }
 
341
 
 
342
        if (dai->driver->capture.channels_min) {
 
343
                ret = tegra_pcm_preallocate_dma_buffer(pcm,
 
344
                                                SNDRV_PCM_STREAM_CAPTURE);
 
345
                if (ret)
 
346
                        goto err_free_play;
 
347
        }
 
348
 
 
349
        return 0;
 
350
 
 
351
err_free_play:
 
352
        tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
 
353
err:
 
354
        return ret;
 
355
}
 
356
 
 
357
static void tegra_pcm_free(struct snd_pcm *pcm)
 
358
{
 
359
        tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
 
360
        tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
 
361
}
 
362
 
 
363
struct snd_soc_platform_driver tegra_pcm_platform = {
 
364
        .ops            = &tegra_pcm_ops,
 
365
        .pcm_new        = tegra_pcm_new,
 
366
        .pcm_free       = tegra_pcm_free,
 
367
};
 
368
 
 
369
static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev)
 
370
{
 
371
        return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform);
 
372
}
 
373
 
 
374
static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev)
 
375
{
 
376
        snd_soc_unregister_platform(&pdev->dev);
 
377
        return 0;
 
378
}
 
379
 
 
380
static struct platform_driver tegra_pcm_driver = {
 
381
        .driver = {
 
382
                .name = DRV_NAME,
 
383
                .owner = THIS_MODULE,
 
384
        },
 
385
        .probe = tegra_pcm_platform_probe,
 
386
        .remove = __devexit_p(tegra_pcm_platform_remove),
 
387
};
 
388
 
 
389
static int __init snd_tegra_pcm_init(void)
 
390
{
 
391
        return platform_driver_register(&tegra_pcm_driver);
 
392
}
 
393
module_init(snd_tegra_pcm_init);
 
394
 
 
395
static void __exit snd_tegra_pcm_exit(void)
 
396
{
 
397
        platform_driver_unregister(&tegra_pcm_driver);
 
398
}
 
399
module_exit(snd_tegra_pcm_exit);
 
400
 
 
401
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 
402
MODULE_DESCRIPTION("Tegra PCM ASoC driver");
 
403
MODULE_LICENSE("GPL");
 
404
MODULE_ALIAS("platform:" DRV_NAME);