17605eb5bSStephen Warren /* 27605eb5bSStephen Warren * tegra_pcm.c - Tegra PCM driver 37605eb5bSStephen Warren * 47605eb5bSStephen Warren * Author: Stephen Warren <swarren@nvidia.com> 57605eb5bSStephen Warren * Copyright (C) 2010 - 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/module.h> 337605eb5bSStephen Warren #include <linux/dma-mapping.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 42*8eb34207SStephen Warren #define DRV_NAME "tegra-pcm-audio" 43*8eb34207SStephen Warren 447605eb5bSStephen Warren static const struct snd_pcm_hardware tegra_pcm_hardware = { 457605eb5bSStephen Warren .info = SNDRV_PCM_INFO_MMAP | 467605eb5bSStephen Warren SNDRV_PCM_INFO_MMAP_VALID | 477605eb5bSStephen Warren SNDRV_PCM_INFO_PAUSE | 487605eb5bSStephen Warren SNDRV_PCM_INFO_RESUME | 497605eb5bSStephen Warren SNDRV_PCM_INFO_INTERLEAVED, 507605eb5bSStephen Warren .formats = SNDRV_PCM_FMTBIT_S16_LE, 517605eb5bSStephen Warren .channels_min = 2, 527605eb5bSStephen Warren .channels_max = 2, 537605eb5bSStephen Warren .period_bytes_min = 1024, 547605eb5bSStephen Warren .period_bytes_max = PAGE_SIZE, 557605eb5bSStephen Warren .periods_min = 2, 567605eb5bSStephen Warren .periods_max = 8, 577605eb5bSStephen Warren .buffer_bytes_max = PAGE_SIZE * 8, 587605eb5bSStephen Warren .fifo_size = 4, 597605eb5bSStephen Warren }; 607605eb5bSStephen Warren 617605eb5bSStephen Warren static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd) 627605eb5bSStephen Warren { 637605eb5bSStephen Warren struct snd_pcm_substream *substream = prtd->substream; 647605eb5bSStephen Warren struct snd_dma_buffer *buf = &substream->dma_buffer; 657605eb5bSStephen Warren struct tegra_dma_req *dma_req; 667605eb5bSStephen Warren unsigned long addr; 677605eb5bSStephen Warren 687605eb5bSStephen Warren dma_req = &prtd->dma_req[prtd->dma_req_idx]; 697605eb5bSStephen Warren prtd->dma_req_idx = 1 - prtd->dma_req_idx; 707605eb5bSStephen Warren 717605eb5bSStephen Warren addr = buf->addr + prtd->dma_pos; 727605eb5bSStephen Warren prtd->dma_pos += dma_req->size; 737605eb5bSStephen Warren if (prtd->dma_pos >= prtd->dma_pos_end) 747605eb5bSStephen Warren prtd->dma_pos = 0; 757605eb5bSStephen Warren 767605eb5bSStephen Warren if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 777605eb5bSStephen Warren dma_req->source_addr = addr; 787605eb5bSStephen Warren else 797605eb5bSStephen Warren dma_req->dest_addr = addr; 807605eb5bSStephen Warren 817605eb5bSStephen Warren tegra_dma_enqueue_req(prtd->dma_chan, dma_req); 827605eb5bSStephen Warren } 837605eb5bSStephen Warren 847605eb5bSStephen Warren static void dma_complete_callback(struct tegra_dma_req *req) 857605eb5bSStephen Warren { 867605eb5bSStephen Warren struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev; 877605eb5bSStephen Warren struct snd_pcm_substream *substream = prtd->substream; 887605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 897605eb5bSStephen Warren 907605eb5bSStephen Warren spin_lock(&prtd->lock); 917605eb5bSStephen Warren 927605eb5bSStephen Warren if (!prtd->running) { 937605eb5bSStephen Warren spin_unlock(&prtd->lock); 947605eb5bSStephen Warren return; 957605eb5bSStephen Warren } 967605eb5bSStephen Warren 977605eb5bSStephen Warren if (++prtd->period_index >= runtime->periods) 987605eb5bSStephen Warren prtd->period_index = 0; 997605eb5bSStephen Warren 1007605eb5bSStephen Warren tegra_pcm_queue_dma(prtd); 1017605eb5bSStephen Warren 1027605eb5bSStephen Warren spin_unlock(&prtd->lock); 1037605eb5bSStephen Warren 1047605eb5bSStephen Warren snd_pcm_period_elapsed(substream); 1057605eb5bSStephen Warren } 1067605eb5bSStephen Warren 1077605eb5bSStephen Warren static void setup_dma_tx_request(struct tegra_dma_req *req, 1087605eb5bSStephen Warren struct tegra_pcm_dma_params * dmap) 1097605eb5bSStephen Warren { 1107605eb5bSStephen Warren req->complete = dma_complete_callback; 1117605eb5bSStephen Warren req->to_memory = false; 1127605eb5bSStephen Warren req->dest_addr = dmap->addr; 1137605eb5bSStephen Warren req->dest_wrap = dmap->wrap; 1147605eb5bSStephen Warren req->source_bus_width = 32; 1157605eb5bSStephen Warren req->source_wrap = 0; 1167605eb5bSStephen Warren req->dest_bus_width = dmap->width; 1177605eb5bSStephen Warren req->req_sel = dmap->req_sel; 1187605eb5bSStephen Warren } 1197605eb5bSStephen Warren 1207605eb5bSStephen Warren static void setup_dma_rx_request(struct tegra_dma_req *req, 1217605eb5bSStephen Warren struct tegra_pcm_dma_params * dmap) 1227605eb5bSStephen Warren { 1237605eb5bSStephen Warren req->complete = dma_complete_callback; 1247605eb5bSStephen Warren req->to_memory = true; 1257605eb5bSStephen Warren req->source_addr = dmap->addr; 1267605eb5bSStephen Warren req->dest_wrap = 0; 1277605eb5bSStephen Warren req->source_bus_width = dmap->width; 1287605eb5bSStephen Warren req->source_wrap = dmap->wrap; 1297605eb5bSStephen Warren req->dest_bus_width = 32; 1307605eb5bSStephen Warren req->req_sel = dmap->req_sel; 1317605eb5bSStephen Warren } 1327605eb5bSStephen Warren 1337605eb5bSStephen Warren static int tegra_pcm_open(struct snd_pcm_substream *substream) 1347605eb5bSStephen Warren { 1357605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 1367605eb5bSStephen Warren struct tegra_runtime_data *prtd; 1377605eb5bSStephen Warren struct snd_soc_pcm_runtime *rtd = substream->private_data; 1387605eb5bSStephen Warren struct tegra_pcm_dma_params * dmap; 1397605eb5bSStephen Warren int ret = 0; 1407605eb5bSStephen Warren 1417605eb5bSStephen Warren prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL); 1427605eb5bSStephen Warren if (prtd == NULL) 1437605eb5bSStephen Warren return -ENOMEM; 1447605eb5bSStephen Warren 1457605eb5bSStephen Warren runtime->private_data = prtd; 1467605eb5bSStephen Warren prtd->substream = substream; 1477605eb5bSStephen Warren 1487605eb5bSStephen Warren spin_lock_init(&prtd->lock); 1497605eb5bSStephen Warren 1507605eb5bSStephen Warren if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 1517605eb5bSStephen Warren dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 1527605eb5bSStephen Warren setup_dma_tx_request(&prtd->dma_req[0], dmap); 1537605eb5bSStephen Warren setup_dma_tx_request(&prtd->dma_req[1], dmap); 1547605eb5bSStephen Warren } else { 1557605eb5bSStephen Warren dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 1567605eb5bSStephen Warren setup_dma_rx_request(&prtd->dma_req[0], dmap); 1577605eb5bSStephen Warren setup_dma_rx_request(&prtd->dma_req[1], dmap); 1587605eb5bSStephen Warren } 1597605eb5bSStephen Warren 1607605eb5bSStephen Warren prtd->dma_req[0].dev = prtd; 1617605eb5bSStephen Warren prtd->dma_req[1].dev = prtd; 1627605eb5bSStephen Warren 1637605eb5bSStephen Warren prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); 1647605eb5bSStephen Warren if (IS_ERR(prtd->dma_chan)) { 1657605eb5bSStephen Warren ret = PTR_ERR(prtd->dma_chan); 1667605eb5bSStephen Warren goto err; 1677605eb5bSStephen Warren } 1687605eb5bSStephen Warren 1697605eb5bSStephen Warren /* Set HW params now that initialization is complete */ 1707605eb5bSStephen Warren snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware); 1717605eb5bSStephen Warren 1727605eb5bSStephen Warren /* Ensure that buffer size is a multiple of period size */ 1737605eb5bSStephen Warren ret = snd_pcm_hw_constraint_integer(runtime, 1747605eb5bSStephen Warren SNDRV_PCM_HW_PARAM_PERIODS); 1757605eb5bSStephen Warren if (ret < 0) 1767605eb5bSStephen Warren goto err; 1777605eb5bSStephen Warren 1787605eb5bSStephen Warren return 0; 1797605eb5bSStephen Warren 1807605eb5bSStephen Warren err: 1817605eb5bSStephen Warren if (prtd->dma_chan) { 1827605eb5bSStephen Warren tegra_dma_free_channel(prtd->dma_chan); 1837605eb5bSStephen Warren } 1847605eb5bSStephen Warren 1857605eb5bSStephen Warren kfree(prtd); 1867605eb5bSStephen Warren 1877605eb5bSStephen Warren return ret; 1887605eb5bSStephen Warren } 1897605eb5bSStephen Warren 1907605eb5bSStephen Warren static int tegra_pcm_close(struct snd_pcm_substream *substream) 1917605eb5bSStephen Warren { 1927605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 1937605eb5bSStephen Warren struct tegra_runtime_data *prtd = runtime->private_data; 1947605eb5bSStephen Warren 1957605eb5bSStephen Warren tegra_dma_free_channel(prtd->dma_chan); 1967605eb5bSStephen Warren 1977605eb5bSStephen Warren kfree(prtd); 1987605eb5bSStephen Warren 1997605eb5bSStephen Warren return 0; 2007605eb5bSStephen Warren } 2017605eb5bSStephen Warren 2027605eb5bSStephen Warren static int tegra_pcm_hw_params(struct snd_pcm_substream *substream, 2037605eb5bSStephen Warren struct snd_pcm_hw_params *params) 2047605eb5bSStephen Warren { 2057605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 2067605eb5bSStephen Warren struct tegra_runtime_data *prtd = runtime->private_data; 2077605eb5bSStephen Warren 2087605eb5bSStephen Warren snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 2097605eb5bSStephen Warren 2107605eb5bSStephen Warren prtd->dma_req[0].size = params_period_bytes(params); 2117605eb5bSStephen Warren prtd->dma_req[1].size = prtd->dma_req[0].size; 2127605eb5bSStephen Warren 2137605eb5bSStephen Warren return 0; 2147605eb5bSStephen Warren } 2157605eb5bSStephen Warren 2167605eb5bSStephen Warren static int tegra_pcm_hw_free(struct snd_pcm_substream *substream) 2177605eb5bSStephen Warren { 2187605eb5bSStephen Warren snd_pcm_set_runtime_buffer(substream, NULL); 2197605eb5bSStephen Warren 2207605eb5bSStephen Warren return 0; 2217605eb5bSStephen Warren } 2227605eb5bSStephen Warren 2237605eb5bSStephen Warren static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 2247605eb5bSStephen Warren { 2257605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 2267605eb5bSStephen Warren struct tegra_runtime_data *prtd = runtime->private_data; 2277605eb5bSStephen Warren unsigned long flags; 2287605eb5bSStephen Warren 2297605eb5bSStephen Warren switch (cmd) { 2307605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_START: 2317605eb5bSStephen Warren prtd->dma_pos = 0; 2327605eb5bSStephen Warren prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size); 2337605eb5bSStephen Warren prtd->period_index = 0; 2347605eb5bSStephen Warren prtd->dma_req_idx = 0; 2357605eb5bSStephen Warren /* Fall-through */ 2367605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_RESUME: 2377605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 2387605eb5bSStephen Warren spin_lock_irqsave(&prtd->lock, flags); 2397605eb5bSStephen Warren prtd->running = 1; 2407605eb5bSStephen Warren spin_unlock_irqrestore(&prtd->lock, flags); 2417605eb5bSStephen Warren tegra_pcm_queue_dma(prtd); 2427605eb5bSStephen Warren tegra_pcm_queue_dma(prtd); 2437605eb5bSStephen Warren break; 2447605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_STOP: 2457605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_SUSPEND: 2467605eb5bSStephen Warren case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 2477605eb5bSStephen Warren spin_lock_irqsave(&prtd->lock, flags); 2487605eb5bSStephen Warren prtd->running = 0; 2497605eb5bSStephen Warren spin_unlock_irqrestore(&prtd->lock, flags); 2507605eb5bSStephen Warren tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]); 2517605eb5bSStephen Warren tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]); 2527605eb5bSStephen Warren break; 2537605eb5bSStephen Warren default: 2547605eb5bSStephen Warren return -EINVAL; 2557605eb5bSStephen Warren } 2567605eb5bSStephen Warren 2577605eb5bSStephen Warren return 0; 2587605eb5bSStephen Warren } 2597605eb5bSStephen Warren 2607605eb5bSStephen Warren static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream) 2617605eb5bSStephen Warren { 2627605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 2637605eb5bSStephen Warren struct tegra_runtime_data *prtd = runtime->private_data; 2647605eb5bSStephen Warren 2657605eb5bSStephen Warren return prtd->period_index * runtime->period_size; 2667605eb5bSStephen Warren } 2677605eb5bSStephen Warren 2687605eb5bSStephen Warren 2697605eb5bSStephen Warren static int tegra_pcm_mmap(struct snd_pcm_substream *substream, 2707605eb5bSStephen Warren struct vm_area_struct *vma) 2717605eb5bSStephen Warren { 2727605eb5bSStephen Warren struct snd_pcm_runtime *runtime = substream->runtime; 2737605eb5bSStephen Warren 2747605eb5bSStephen Warren return dma_mmap_writecombine(substream->pcm->card->dev, vma, 2757605eb5bSStephen Warren runtime->dma_area, 2767605eb5bSStephen Warren runtime->dma_addr, 2777605eb5bSStephen Warren runtime->dma_bytes); 2787605eb5bSStephen Warren } 2797605eb5bSStephen Warren 2807605eb5bSStephen Warren static struct snd_pcm_ops tegra_pcm_ops = { 2817605eb5bSStephen Warren .open = tegra_pcm_open, 2827605eb5bSStephen Warren .close = tegra_pcm_close, 2837605eb5bSStephen Warren .ioctl = snd_pcm_lib_ioctl, 2847605eb5bSStephen Warren .hw_params = tegra_pcm_hw_params, 2857605eb5bSStephen Warren .hw_free = tegra_pcm_hw_free, 2867605eb5bSStephen Warren .trigger = tegra_pcm_trigger, 2877605eb5bSStephen Warren .pointer = tegra_pcm_pointer, 2887605eb5bSStephen Warren .mmap = tegra_pcm_mmap, 2897605eb5bSStephen Warren }; 2907605eb5bSStephen Warren 2917605eb5bSStephen Warren static int tegra_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 2927605eb5bSStephen Warren { 2937605eb5bSStephen Warren struct snd_pcm_substream *substream = pcm->streams[stream].substream; 2947605eb5bSStephen Warren struct snd_dma_buffer *buf = &substream->dma_buffer; 2957605eb5bSStephen Warren size_t size = tegra_pcm_hardware.buffer_bytes_max; 2967605eb5bSStephen Warren 2977605eb5bSStephen Warren buf->area = dma_alloc_writecombine(pcm->card->dev, size, 2987605eb5bSStephen Warren &buf->addr, GFP_KERNEL); 2997605eb5bSStephen Warren if (!buf->area) 3007605eb5bSStephen Warren return -ENOMEM; 3017605eb5bSStephen Warren 3027605eb5bSStephen Warren buf->dev.type = SNDRV_DMA_TYPE_DEV; 3037605eb5bSStephen Warren buf->dev.dev = pcm->card->dev; 3047605eb5bSStephen Warren buf->private_data = NULL; 3057605eb5bSStephen Warren buf->bytes = size; 3067605eb5bSStephen Warren 3077605eb5bSStephen Warren return 0; 3087605eb5bSStephen Warren } 3097605eb5bSStephen Warren 3107605eb5bSStephen Warren static void tegra_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream) 3117605eb5bSStephen Warren { 3127605eb5bSStephen Warren struct snd_pcm_substream *substream = pcm->streams[stream].substream; 3137605eb5bSStephen Warren struct snd_dma_buffer *buf = &substream->dma_buffer; 3147605eb5bSStephen Warren 3157605eb5bSStephen Warren if (!buf->area) 3167605eb5bSStephen Warren return; 3177605eb5bSStephen Warren 3187605eb5bSStephen Warren dma_free_writecombine(pcm->card->dev, buf->bytes, 3197605eb5bSStephen Warren buf->area, buf->addr); 3207605eb5bSStephen Warren buf->area = NULL; 3217605eb5bSStephen Warren } 3227605eb5bSStephen Warren 3237605eb5bSStephen Warren static u64 tegra_dma_mask = DMA_BIT_MASK(32); 3247605eb5bSStephen Warren 3257605eb5bSStephen Warren static int tegra_pcm_new(struct snd_card *card, 3267605eb5bSStephen Warren struct snd_soc_dai *dai, struct snd_pcm *pcm) 3277605eb5bSStephen Warren { 3287605eb5bSStephen Warren int ret = 0; 3297605eb5bSStephen Warren 3307605eb5bSStephen Warren if (!card->dev->dma_mask) 3317605eb5bSStephen Warren card->dev->dma_mask = &tegra_dma_mask; 3327605eb5bSStephen Warren if (!card->dev->coherent_dma_mask) 3337605eb5bSStephen Warren card->dev->coherent_dma_mask = 0xffffffff; 3347605eb5bSStephen Warren 3357605eb5bSStephen Warren if (dai->driver->playback.channels_min) { 3367605eb5bSStephen Warren ret = tegra_pcm_preallocate_dma_buffer(pcm, 3377605eb5bSStephen Warren SNDRV_PCM_STREAM_PLAYBACK); 3387605eb5bSStephen Warren if (ret) 3397605eb5bSStephen Warren goto err; 3407605eb5bSStephen Warren } 3417605eb5bSStephen Warren 3427605eb5bSStephen Warren if (dai->driver->capture.channels_min) { 3437605eb5bSStephen Warren ret = tegra_pcm_preallocate_dma_buffer(pcm, 3447605eb5bSStephen Warren SNDRV_PCM_STREAM_CAPTURE); 3457605eb5bSStephen Warren if (ret) 3467605eb5bSStephen Warren goto err_free_play; 3477605eb5bSStephen Warren } 3487605eb5bSStephen Warren 3497605eb5bSStephen Warren return 0; 3507605eb5bSStephen Warren 3517605eb5bSStephen Warren err_free_play: 3527605eb5bSStephen Warren tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); 3537605eb5bSStephen Warren err: 3547605eb5bSStephen Warren return ret; 3557605eb5bSStephen Warren } 3567605eb5bSStephen Warren 3577605eb5bSStephen Warren static void tegra_pcm_free(struct snd_pcm *pcm) 3587605eb5bSStephen Warren { 3597605eb5bSStephen Warren tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); 3607605eb5bSStephen Warren tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); 3617605eb5bSStephen Warren } 3627605eb5bSStephen Warren 3637605eb5bSStephen Warren struct snd_soc_platform_driver tegra_pcm_platform = { 3647605eb5bSStephen Warren .ops = &tegra_pcm_ops, 3657605eb5bSStephen Warren .pcm_new = tegra_pcm_new, 3667605eb5bSStephen Warren .pcm_free = tegra_pcm_free, 3677605eb5bSStephen Warren }; 3687605eb5bSStephen Warren 3697605eb5bSStephen Warren static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev) 3707605eb5bSStephen Warren { 3717605eb5bSStephen Warren return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform); 3727605eb5bSStephen Warren } 3737605eb5bSStephen Warren 3747605eb5bSStephen Warren static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev) 3757605eb5bSStephen Warren { 3767605eb5bSStephen Warren snd_soc_unregister_platform(&pdev->dev); 3777605eb5bSStephen Warren return 0; 3787605eb5bSStephen Warren } 3797605eb5bSStephen Warren 3807605eb5bSStephen Warren static struct platform_driver tegra_pcm_driver = { 3817605eb5bSStephen Warren .driver = { 382*8eb34207SStephen Warren .name = DRV_NAME, 3837605eb5bSStephen Warren .owner = THIS_MODULE, 3847605eb5bSStephen Warren }, 3857605eb5bSStephen Warren .probe = tegra_pcm_platform_probe, 3867605eb5bSStephen Warren .remove = __devexit_p(tegra_pcm_platform_remove), 3877605eb5bSStephen Warren }; 3887605eb5bSStephen Warren 3897605eb5bSStephen Warren static int __init snd_tegra_pcm_init(void) 3907605eb5bSStephen Warren { 3917605eb5bSStephen Warren return platform_driver_register(&tegra_pcm_driver); 3927605eb5bSStephen Warren } 3937605eb5bSStephen Warren module_init(snd_tegra_pcm_init); 3947605eb5bSStephen Warren 3957605eb5bSStephen Warren static void __exit snd_tegra_pcm_exit(void) 3967605eb5bSStephen Warren { 3977605eb5bSStephen Warren platform_driver_unregister(&tegra_pcm_driver); 3987605eb5bSStephen Warren } 3997605eb5bSStephen Warren module_exit(snd_tegra_pcm_exit); 4007605eb5bSStephen Warren 4017605eb5bSStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 4027605eb5bSStephen Warren MODULE_DESCRIPTION("Tegra PCM ASoC driver"); 4037605eb5bSStephen Warren MODULE_LICENSE("GPL"); 404*8eb34207SStephen Warren MODULE_ALIAS("platform:" DRV_NAME); 405