17605eb5bSStephen Warren /* 27605eb5bSStephen Warren * tegra_pcm.c - Tegra PCM driver 37605eb5bSStephen Warren * 47605eb5bSStephen Warren * Author: Stephen Warren <swarren@nvidia.com> 5518de86bSStephen Warren * Copyright (C) 2010,2012 - NVIDIA, Inc. 67605eb5bSStephen Warren * 77605eb5bSStephen Warren * Based on code copyright/by: 87605eb5bSStephen Warren * 97605eb5bSStephen Warren * Copyright (c) 2009-2010, NVIDIA Corporation. 107605eb5bSStephen Warren * Scott Peterson <speterson@nvidia.com> 117605eb5bSStephen Warren * Vijay Mali <vmali@nvidia.com> 127605eb5bSStephen Warren * 137605eb5bSStephen Warren * Copyright (C) 2010 Google, Inc. 147605eb5bSStephen Warren * Iliyan Malchev <malchev@google.com> 157605eb5bSStephen Warren * 167605eb5bSStephen Warren * This program is free software; you can redistribute it and/or 177605eb5bSStephen Warren * modify it under the terms of the GNU General Public License 187605eb5bSStephen Warren * version 2 as published by the Free Software Foundation. 197605eb5bSStephen Warren * 207605eb5bSStephen Warren * This program is distributed in the hope that it will be useful, but 217605eb5bSStephen Warren * WITHOUT ANY WARRANTY; without even the implied warranty of 227605eb5bSStephen Warren * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 237605eb5bSStephen Warren * General Public License for more details. 247605eb5bSStephen Warren * 257605eb5bSStephen Warren * You should have received a copy of the GNU General Public License 267605eb5bSStephen Warren * along with this program; if not, write to the Free Software 277605eb5bSStephen Warren * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 287605eb5bSStephen Warren * 02110-1301 USA 297605eb5bSStephen Warren * 307605eb5bSStephen Warren */ 317605eb5bSStephen Warren 327605eb5bSStephen Warren #include <linux/dma-mapping.h> 33*7613c508SStephen Warren #include <linux/module.h> 347605eb5bSStephen Warren #include <linux/slab.h> 357605eb5bSStephen Warren #include <sound/core.h> 367605eb5bSStephen Warren #include <sound/pcm.h> 377605eb5bSStephen Warren #include <sound/pcm_params.h> 387605eb5bSStephen Warren #include <sound/soc.h> 397605eb5bSStephen Warren 407605eb5bSStephen Warren #include "tegra_pcm.h" 417605eb5bSStephen Warren 427605eb5bSStephen Warren static const struct snd_pcm_hardware tegra_pcm_hardware = { 437605eb5bSStephen Warren .info = SNDRV_PCM_INFO_MMAP | 447605eb5bSStephen Warren SNDRV_PCM_INFO_MMAP_VALID | 457605eb5bSStephen Warren SNDRV_PCM_INFO_PAUSE | 467605eb5bSStephen Warren SNDRV_PCM_INFO_RESUME | 477605eb5bSStephen Warren SNDRV_PCM_INFO_INTERLEAVED, 487605eb5bSStephen Warren .formats = SNDRV_PCM_FMTBIT_S16_LE, 497605eb5bSStephen Warren .channels_min = 2, 507605eb5bSStephen Warren .channels_max = 2, 517605eb5bSStephen Warren .period_bytes_min = 1024, 527605eb5bSStephen Warren .period_bytes_max = PAGE_SIZE, 537605eb5bSStephen Warren .periods_min = 2, 547605eb5bSStephen Warren .periods_max = 8, 557605eb5bSStephen Warren .buffer_bytes_max = PAGE_SIZE * 8, 567605eb5bSStephen Warren .fifo_size = 4, 577605eb5bSStephen Warren }; 587605eb5bSStephen Warren 597605eb5bSStephen Warren static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd) 607605eb5bSStephen Warren { 617605eb5bSStephen Warren struct snd_pcm_substream *substream = prtd->substream; 627605eb5bSStephen Warren struct snd_dma_buffer *buf = &substream->dma_buffer; 637605eb5bSStephen Warren struct tegra_dma_req *dma_req; 647605eb5bSStephen Warren unsigned long addr; 657605eb5bSStephen Warren 667605eb5bSStephen Warren dma_req = &prtd->dma_req[prtd->dma_req_idx]; 677605eb5bSStephen Warren prtd->dma_req_idx = 1 - prtd->dma_req_idx; 687605eb5bSStephen Warren 697605eb5bSStephen Warren addr = buf->addr + prtd->dma_pos; 707605eb5bSStephen Warren prtd->dma_pos += dma_req->size; 717605eb5bSStephen Warren if (prtd->dma_pos >= prtd->dma_pos_end) 727605eb5bSStephen Warren prtd->dma_pos = 0; 737605eb5bSStephen Warren 747605eb5bSStephen Warren if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 757605eb5bSStephen Warren dma_req->source_addr = addr; 767605eb5bSStephen Warren else 777605eb5bSStephen Warren dma_req->dest_addr = addr; 787605eb5bSStephen Warren 797605eb5bSStephen Warren tegra_dma_enqueue_req(prtd->dma_chan, dma_req); 807605eb5bSStephen Warren } 817605eb5bSStephen Warren 827605eb5bSStephen Warren static void dma_complete_callback(struct tegra_dma_req *req) 837605eb5bSStephen Warren { 847605eb5bSStephen Warren struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev; 857605eb5bSStephen Warren struct snd_pcm_substream *substream = prtd->substream; 867605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 877605eb5bSStephen Warren 887605eb5bSStephen Warren spin_lock(&prtd->lock); 897605eb5bSStephen Warren 907605eb5bSStephen Warren if (!prtd->running) { 917605eb5bSStephen Warren spin_unlock(&prtd->lock); 927605eb5bSStephen Warren return; 937605eb5bSStephen Warren } 947605eb5bSStephen Warren 957605eb5bSStephen Warren if (++prtd->period_index >= runtime->periods) 967605eb5bSStephen Warren prtd->period_index = 0; 977605eb5bSStephen Warren 987605eb5bSStephen Warren tegra_pcm_queue_dma(prtd); 997605eb5bSStephen Warren 1007605eb5bSStephen Warren spin_unlock(&prtd->lock); 1017605eb5bSStephen Warren 1027605eb5bSStephen Warren snd_pcm_period_elapsed(substream); 1037605eb5bSStephen Warren } 1047605eb5bSStephen Warren 1057605eb5bSStephen Warren static void setup_dma_tx_request(struct tegra_dma_req *req, 1067605eb5bSStephen Warren struct tegra_pcm_dma_params * dmap) 1077605eb5bSStephen Warren { 1087605eb5bSStephen Warren req->complete = dma_complete_callback; 1097605eb5bSStephen Warren req->to_memory = false; 1107605eb5bSStephen Warren req->dest_addr = dmap->addr; 1117605eb5bSStephen Warren req->dest_wrap = dmap->wrap; 1127605eb5bSStephen Warren req->source_bus_width = 32; 1137605eb5bSStephen Warren req->source_wrap = 0; 1147605eb5bSStephen Warren req->dest_bus_width = dmap->width; 1157605eb5bSStephen Warren req->req_sel = dmap->req_sel; 1167605eb5bSStephen Warren } 1177605eb5bSStephen Warren 1187605eb5bSStephen Warren static void setup_dma_rx_request(struct tegra_dma_req *req, 1197605eb5bSStephen Warren struct tegra_pcm_dma_params * dmap) 1207605eb5bSStephen Warren { 1217605eb5bSStephen Warren req->complete = dma_complete_callback; 1227605eb5bSStephen Warren req->to_memory = true; 1237605eb5bSStephen Warren req->source_addr = dmap->addr; 1247605eb5bSStephen Warren req->dest_wrap = 0; 1257605eb5bSStephen Warren req->source_bus_width = dmap->width; 1267605eb5bSStephen Warren req->source_wrap = dmap->wrap; 1277605eb5bSStephen Warren req->dest_bus_width = 32; 1287605eb5bSStephen Warren req->req_sel = dmap->req_sel; 1297605eb5bSStephen Warren } 1307605eb5bSStephen Warren 1317605eb5bSStephen Warren static int tegra_pcm_open(struct snd_pcm_substream *substream) 1327605eb5bSStephen Warren { 1337605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 1347605eb5bSStephen Warren struct tegra_runtime_data *prtd; 1357605eb5bSStephen Warren struct snd_soc_pcm_runtime *rtd = substream->private_data; 1367605eb5bSStephen Warren struct tegra_pcm_dma_params * dmap; 1377605eb5bSStephen Warren int ret = 0; 1387605eb5bSStephen Warren 1397605eb5bSStephen Warren prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL); 1407605eb5bSStephen Warren if (prtd == NULL) 1417605eb5bSStephen Warren return -ENOMEM; 1427605eb5bSStephen Warren 1437605eb5bSStephen Warren runtime->private_data = prtd; 1447605eb5bSStephen Warren prtd->substream = substream; 1457605eb5bSStephen Warren 1467605eb5bSStephen Warren spin_lock_init(&prtd->lock); 1477605eb5bSStephen Warren 1487605eb5bSStephen Warren if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 1497605eb5bSStephen Warren dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 1507605eb5bSStephen Warren setup_dma_tx_request(&prtd->dma_req[0], dmap); 1517605eb5bSStephen Warren setup_dma_tx_request(&prtd->dma_req[1], dmap); 1527605eb5bSStephen Warren } else { 1537605eb5bSStephen Warren dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 1547605eb5bSStephen Warren setup_dma_rx_request(&prtd->dma_req[0], dmap); 1557605eb5bSStephen Warren setup_dma_rx_request(&prtd->dma_req[1], dmap); 1567605eb5bSStephen Warren } 1577605eb5bSStephen Warren 1587605eb5bSStephen Warren prtd->dma_req[0].dev = prtd; 1597605eb5bSStephen Warren prtd->dma_req[1].dev = prtd; 1607605eb5bSStephen Warren 1617605eb5bSStephen Warren prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); 162e1412e63SStephen Warren if (prtd->dma_chan == NULL) { 163e1412e63SStephen Warren ret = -ENOMEM; 1647605eb5bSStephen Warren goto err; 1657605eb5bSStephen Warren } 1667605eb5bSStephen Warren 1677605eb5bSStephen Warren /* Set HW params now that initialization is complete */ 1687605eb5bSStephen Warren snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); 1697605eb5bSStephen Warren 1707605eb5bSStephen Warren /* Ensure that buffer size is a multiple of period size */ 1717605eb5bSStephen Warren ret = snd_pcm_hw_constraint_integer(runtime, 1727605eb5bSStephen Warren SNDRV_PCM_HW_PARAM_PERIODS); 1737605eb5bSStephen Warren if (ret < 0) 1747605eb5bSStephen Warren goto err; 1757605eb5bSStephen Warren 1767605eb5bSStephen Warren return 0; 1777605eb5bSStephen Warren 1787605eb5bSStephen Warren err: 1797605eb5bSStephen Warren if (prtd->dma_chan) { 1807605eb5bSStephen Warren tegra_dma_free_channel(prtd->dma_chan); 1817605eb5bSStephen Warren } 1827605eb5bSStephen Warren 1837605eb5bSStephen Warren kfree(prtd); 1847605eb5bSStephen Warren 1857605eb5bSStephen Warren return ret; 1867605eb5bSStephen Warren } 1877605eb5bSStephen Warren 1887605eb5bSStephen Warren static int tegra_pcm_close(struct snd_pcm_substream *substream) 1897605eb5bSStephen Warren { 1907605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 1917605eb5bSStephen Warren struct tegra_runtime_data *prtd = runtime->private_data; 1927605eb5bSStephen Warren 1937605eb5bSStephen Warren tegra_dma_free_channel(prtd->dma_chan); 1947605eb5bSStephen Warren 1957605eb5bSStephen Warren kfree(prtd); 1967605eb5bSStephen Warren 1977605eb5bSStephen Warren return 0; 1987605eb5bSStephen Warren } 1997605eb5bSStephen Warren 2007605eb5bSStephen Warren static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, 2017605eb5bSStephen Warren struct snd_pcm_hw_params *params) 2027605eb5bSStephen Warren { 2037605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 2047605eb5bSStephen Warren struct tegra_runtime_data *prtd = runtime->private_data; 2057605eb5bSStephen Warren 2067605eb5bSStephen Warren snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 2077605eb5bSStephen Warren 2087605eb5bSStephen Warren prtd->dma_req[0].size = params_period_bytes(params); 2097605eb5bSStephen Warren prtd->dma_req[1].size = prtd->dma_req[0].size; 2107605eb5bSStephen Warren 2117605eb5bSStephen Warren return 0; 2127605eb5bSStephen Warren } 2137605eb5bSStephen Warren 2147605eb5bSStephen Warren static int tegra_pcm_hw_free(struct snd_pcm_substream *substream) 2157605eb5bSStephen Warren { 2167605eb5bSStephen Warren snd_pcm_set_runtime_buffer(substream, NULL); 2177605eb5bSStephen Warren 2187605eb5bSStephen Warren return 0; 2197605eb5bSStephen Warren } 2207605eb5bSStephen Warren 2217605eb5bSStephen Warren static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 2227605eb5bSStephen Warren { 2237605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 2247605eb5bSStephen Warren struct tegra_runtime_data *prtd = runtime->private_data; 2257605eb5bSStephen Warren unsigned long flags; 2267605eb5bSStephen Warren 2277605eb5bSStephen Warren switch (cmd) { 2287605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_START: 2297605eb5bSStephen Warren prtd->dma_pos = 0; 2307605eb5bSStephen Warren prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size); 2317605eb5bSStephen Warren prtd->period_index = 0; 2327605eb5bSStephen Warren prtd->dma_req_idx = 0; 2337605eb5bSStephen Warren /* Fall-through */ 2347605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_RESUME: 2357605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2367605eb5bSStephen Warren spin_lock_irqsave(&prtd->lock, flags); 2377605eb5bSStephen Warren prtd->running = 1; 2387605eb5bSStephen Warren spin_unlock_irqrestore(&prtd->lock, flags); 2397605eb5bSStephen Warren tegra_pcm_queue_dma(prtd); 2407605eb5bSStephen Warren tegra_pcm_queue_dma(prtd); 2417605eb5bSStephen Warren break; 2427605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_STOP: 2437605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_SUSPEND: 2447605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2457605eb5bSStephen Warren spin_lock_irqsave(&prtd->lock, flags); 2467605eb5bSStephen Warren prtd->running = 0; 2477605eb5bSStephen Warren spin_unlock_irqrestore(&prtd->lock, flags); 2487605eb5bSStephen Warren tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]); 2497605eb5bSStephen Warren tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]); 2507605eb5bSStephen Warren break; 2517605eb5bSStephen Warren default: 2527605eb5bSStephen Warren return -EINVAL; 2537605eb5bSStephen Warren } 2547605eb5bSStephen Warren 2557605eb5bSStephen Warren return 0; 2567605eb5bSStephen Warren } 2577605eb5bSStephen Warren 2587605eb5bSStephen Warren static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream) 2597605eb5bSStephen Warren { 2607605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 2617605eb5bSStephen Warren struct tegra_runtime_data *prtd = runtime->private_data; 2627605eb5bSStephen Warren 2637605eb5bSStephen Warren return prtd->period_index * runtime->period_size; 2647605eb5bSStephen Warren } 2657605eb5bSStephen Warren 2667605eb5bSStephen Warren 2677605eb5bSStephen Warren static int tegra_pcm_mmap(struct snd_pcm_substream *substream, 2687605eb5bSStephen Warren struct vm_area_struct *vma) 2697605eb5bSStephen Warren { 2707605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 2717605eb5bSStephen Warren 2727605eb5bSStephen Warren return dma_mmap_writecombine(substream->pcm->card->dev, vma, 2737605eb5bSStephen Warren runtime->dma_area, 2747605eb5bSStephen Warren runtime->dma_addr, 2757605eb5bSStephen Warren runtime->dma_bytes); 2767605eb5bSStephen Warren } 2777605eb5bSStephen Warren 2787605eb5bSStephen Warren static struct snd_pcm_ops tegra_pcm_ops = { 2797605eb5bSStephen Warren .open = tegra_pcm_open, 2807605eb5bSStephen Warren .close = tegra_pcm_close, 2817605eb5bSStephen Warren .ioctl = snd_pcm_lib_ioctl, 2827605eb5bSStephen Warren .hw_params = tegra_pcm_hw_params, 2837605eb5bSStephen Warren .hw_free = tegra_pcm_hw_free, 2847605eb5bSStephen Warren .trigger = tegra_pcm_trigger, 2857605eb5bSStephen Warren .pointer = tegra_pcm_pointer, 2867605eb5bSStephen Warren .mmap = tegra_pcm_mmap, 2877605eb5bSStephen Warren }; 2887605eb5bSStephen Warren 2897605eb5bSStephen Warren static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 2907605eb5bSStephen Warren { 2917605eb5bSStephen Warren struct snd_pcm_substream *substream = pcm->streams[stream].substream; 2927605eb5bSStephen Warren struct snd_dma_buffer *buf = &substream->dma_buffer; 2937605eb5bSStephen Warren size_t size = tegra_pcm_hardware.buffer_bytes_max; 2947605eb5bSStephen Warren 2957605eb5bSStephen Warren buf->area = dma_alloc_writecombine(pcm->card->dev, size, 2967605eb5bSStephen Warren &buf->addr, GFP_KERNEL); 2977605eb5bSStephen Warren if (!buf->area) 2987605eb5bSStephen Warren return -ENOMEM; 2997605eb5bSStephen Warren 3007605eb5bSStephen Warren buf->dev.type = SNDRV_DMA_TYPE_DEV; 3017605eb5bSStephen Warren buf->dev.dev = pcm->card->dev; 3027605eb5bSStephen Warren buf->private_data = NULL; 3037605eb5bSStephen Warren buf->bytes = size; 3047605eb5bSStephen Warren 3057605eb5bSStephen Warren return 0; 3067605eb5bSStephen Warren } 3077605eb5bSStephen Warren 3087605eb5bSStephen Warren static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) 3097605eb5bSStephen Warren { 310a96edd59SStephen Warren struct snd_pcm_substream *substream; 311a96edd59SStephen Warren struct snd_dma_buffer *buf; 3127605eb5bSStephen Warren 313a96edd59SStephen Warren substream = pcm->streams[stream].substream; 314a96edd59SStephen Warren if (!substream) 315a96edd59SStephen Warren return; 316a96edd59SStephen Warren 317a96edd59SStephen Warren buf = &substream->dma_buffer; 3187605eb5bSStephen Warren if (!buf->area) 3197605eb5bSStephen Warren return; 3207605eb5bSStephen Warren 3217605eb5bSStephen Warren dma_free_writecombine(pcm->card->dev, buf->bytes, 3227605eb5bSStephen Warren buf->area, buf->addr); 3237605eb5bSStephen Warren buf->area = NULL; 3247605eb5bSStephen Warren } 3257605eb5bSStephen Warren 3267605eb5bSStephen Warren static u64 tegra_dma_mask = DMA_BIT_MASK(32); 3277605eb5bSStephen Warren 328552d1ef6SLiam Girdwood static int tegra_pcm_new(struct snd_soc_pcm_runtime *rtd) 3297605eb5bSStephen Warren { 330552d1ef6SLiam Girdwood struct snd_card *card = rtd->card->snd_card; 331552d1ef6SLiam Girdwood struct snd_pcm *pcm = rtd->pcm; 3327605eb5bSStephen Warren int ret = 0; 3337605eb5bSStephen Warren 3347605eb5bSStephen Warren if (!card->dev->dma_mask) 3357605eb5bSStephen Warren card->dev->dma_mask = &tegra_dma_mask; 3367605eb5bSStephen Warren if (!card->dev->coherent_dma_mask) 337350e16d5SJoachim Eastwood card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 3387605eb5bSStephen Warren 33925e9e756SJoachim Eastwood if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 3407605eb5bSStephen Warren ret = tegra_pcm_preallocate_dma_buffer(pcm, 3417605eb5bSStephen Warren SNDRV_PCM_STREAM_PLAYBACK); 3427605eb5bSStephen Warren if (ret) 3437605eb5bSStephen Warren goto err; 3447605eb5bSStephen Warren } 3457605eb5bSStephen Warren 34625e9e756SJoachim Eastwood if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 3477605eb5bSStephen Warren ret = tegra_pcm_preallocate_dma_buffer(pcm, 3487605eb5bSStephen Warren SNDRV_PCM_STREAM_CAPTURE); 3497605eb5bSStephen Warren if (ret) 3507605eb5bSStephen Warren goto err_free_play; 3517605eb5bSStephen Warren } 3527605eb5bSStephen Warren 3537605eb5bSStephen Warren return 0; 3547605eb5bSStephen Warren 3557605eb5bSStephen Warren err_free_play: 3567605eb5bSStephen Warren tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); 3577605eb5bSStephen Warren err: 3587605eb5bSStephen Warren return ret; 3597605eb5bSStephen Warren } 3607605eb5bSStephen Warren 3617605eb5bSStephen Warren static void tegra_pcm_free(struct snd_pcm *pcm) 3627605eb5bSStephen Warren { 3637605eb5bSStephen Warren tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); 3647605eb5bSStephen Warren tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); 3657605eb5bSStephen Warren } 3667605eb5bSStephen Warren 36701840bbeSOlof Johansson static struct snd_soc_platform_driver tegra_pcm_platform = { 3687605eb5bSStephen Warren .ops = &tegra_pcm_ops, 3697605eb5bSStephen Warren .pcm_new = tegra_pcm_new, 3707605eb5bSStephen Warren .pcm_free = tegra_pcm_free, 3717605eb5bSStephen Warren }; 3727605eb5bSStephen Warren 373518de86bSStephen Warren int __devinit tegra_pcm_platform_register(struct device *dev) 3747605eb5bSStephen Warren { 375518de86bSStephen Warren return snd_soc_register_platform(dev, &tegra_pcm_platform); 3767605eb5bSStephen Warren } 377518de86bSStephen Warren EXPORT_SYMBOL_GPL(tegra_pcm_platform_register); 3787605eb5bSStephen Warren 379518de86bSStephen Warren void __devexit tegra_pcm_platform_unregister(struct device *dev) 3807605eb5bSStephen Warren { 381518de86bSStephen Warren snd_soc_unregister_platform(dev); 3827605eb5bSStephen Warren } 383518de86bSStephen Warren EXPORT_SYMBOL_GPL(tegra_pcm_platform_unregister); 3847605eb5bSStephen Warren 3857605eb5bSStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 3867605eb5bSStephen Warren MODULE_DESCRIPTION("Tegra PCM ASoC driver"); 3877605eb5bSStephen Warren MODULE_LICENSE("GPL"); 388