10ed275efSAlexander Shiyan /* 20ed275efSAlexander Shiyan * linux/sound/arm/ep93xx-pcm.c - EP93xx ALSA PCM interface 30ed275efSAlexander Shiyan * 40ed275efSAlexander Shiyan * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org> 50ed275efSAlexander Shiyan * Copyright (C) 2006 Applied Data Systems 60ed275efSAlexander Shiyan * 70ed275efSAlexander Shiyan * Rewritten for the SoC audio subsystem (Based on PXA2xx code): 80ed275efSAlexander Shiyan * Copyright (c) 2008 Ryan Mallon 90ed275efSAlexander Shiyan * 100ed275efSAlexander Shiyan * This program is free software; you can redistribute it and/or modify 110ed275efSAlexander Shiyan * it under the terms of the GNU General Public License version 2 as 120ed275efSAlexander Shiyan * published by the Free Software Foundation. 130ed275efSAlexander Shiyan */ 140ed275efSAlexander Shiyan 150ed275efSAlexander Shiyan #include <linux/module.h> 160ed275efSAlexander Shiyan #include <linux/init.h> 170ed275efSAlexander Shiyan #include <linux/device.h> 180ed275efSAlexander Shiyan #include <linux/slab.h> 190ed275efSAlexander Shiyan #include <linux/dmaengine.h> 200ed275efSAlexander Shiyan #include <linux/dma-mapping.h> 210ed275efSAlexander Shiyan 220ed275efSAlexander Shiyan #include <sound/core.h> 230ed275efSAlexander Shiyan #include <sound/pcm.h> 240ed275efSAlexander Shiyan #include <sound/pcm_params.h> 250ed275efSAlexander Shiyan #include <sound/soc.h> 260ed275efSAlexander Shiyan #include <sound/dmaengine_pcm.h> 270ed275efSAlexander Shiyan 28*f5a246eaSLinus Torvalds #include <linux/platform_data/dma-ep93xx.h> 290ed275efSAlexander Shiyan #include <mach/hardware.h> 300ed275efSAlexander Shiyan #include <mach/ep93xx-regs.h> 310ed275efSAlexander Shiyan 320ed275efSAlexander Shiyan #include "ep93xx-pcm.h" 330ed275efSAlexander Shiyan 340ed275efSAlexander Shiyan static const struct snd_pcm_hardware ep93xx_pcm_hardware = { 350ed275efSAlexander Shiyan .info = (SNDRV_PCM_INFO_MMAP | 360ed275efSAlexander Shiyan SNDRV_PCM_INFO_MMAP_VALID | 370ed275efSAlexander Shiyan SNDRV_PCM_INFO_INTERLEAVED | 380ed275efSAlexander Shiyan SNDRV_PCM_INFO_BLOCK_TRANSFER), 390ed275efSAlexander Shiyan 400ed275efSAlexander Shiyan .rates = SNDRV_PCM_RATE_8000_192000, 410ed275efSAlexander Shiyan .rate_min = SNDRV_PCM_RATE_8000, 420ed275efSAlexander Shiyan .rate_max = SNDRV_PCM_RATE_192000, 430ed275efSAlexander Shiyan 440ed275efSAlexander Shiyan .formats = (SNDRV_PCM_FMTBIT_S16_LE | 450ed275efSAlexander Shiyan SNDRV_PCM_FMTBIT_S24_LE | 460ed275efSAlexander Shiyan SNDRV_PCM_FMTBIT_S32_LE), 470ed275efSAlexander Shiyan 480ed275efSAlexander Shiyan .buffer_bytes_max = 131072, 490ed275efSAlexander Shiyan .period_bytes_min = 32, 500ed275efSAlexander Shiyan .period_bytes_max = 32768, 510ed275efSAlexander Shiyan .periods_min = 1, 520ed275efSAlexander Shiyan .periods_max = 32, 530ed275efSAlexander Shiyan .fifo_size = 32, 540ed275efSAlexander Shiyan }; 550ed275efSAlexander Shiyan 560ed275efSAlexander Shiyan static bool ep93xx_pcm_dma_filter(struct dma_chan *chan, void *filter_param) 570ed275efSAlexander Shiyan { 580ed275efSAlexander Shiyan struct ep93xx_dma_data *data = filter_param; 590ed275efSAlexander Shiyan 600ed275efSAlexander Shiyan if (data->direction == ep93xx_dma_chan_direction(chan)) { 610ed275efSAlexander Shiyan chan->private = data; 620ed275efSAlexander Shiyan return true; 630ed275efSAlexander Shiyan } 640ed275efSAlexander Shiyan 650ed275efSAlexander Shiyan return false; 660ed275efSAlexander Shiyan } 670ed275efSAlexander Shiyan 680ed275efSAlexander Shiyan static int ep93xx_pcm_open(struct snd_pcm_substream *substream) 690ed275efSAlexander Shiyan { 700ed275efSAlexander Shiyan struct snd_soc_pcm_runtime *rtd = substream->private_data; 710ed275efSAlexander Shiyan struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 720ed275efSAlexander Shiyan struct ep93xx_pcm_dma_params *dma_params; 730ed275efSAlexander Shiyan struct ep93xx_dma_data *dma_data; 740ed275efSAlexander Shiyan int ret; 750ed275efSAlexander Shiyan 760ed275efSAlexander Shiyan snd_soc_set_runtime_hwparams(substream, &ep93xx_pcm_hardware); 770ed275efSAlexander Shiyan 780ed275efSAlexander Shiyan dma_data = kmalloc(sizeof(*dma_data), GFP_KERNEL); 790ed275efSAlexander Shiyan if (!dma_data) 800ed275efSAlexander Shiyan return -ENOMEM; 810ed275efSAlexander Shiyan 820ed275efSAlexander Shiyan dma_params = snd_soc_dai_get_dma_data(cpu_dai, substream); 830ed275efSAlexander Shiyan dma_data->port = dma_params->dma_port; 840ed275efSAlexander Shiyan dma_data->name = dma_params->name; 850ed275efSAlexander Shiyan dma_data->direction = snd_pcm_substream_to_dma_direction(substream); 860ed275efSAlexander Shiyan 870ed275efSAlexander Shiyan ret = snd_dmaengine_pcm_open(substream, ep93xx_pcm_dma_filter, dma_data); 880ed275efSAlexander Shiyan if (ret) { 890ed275efSAlexander Shiyan kfree(dma_data); 900ed275efSAlexander Shiyan return ret; 910ed275efSAlexander Shiyan } 920ed275efSAlexander Shiyan 930ed275efSAlexander Shiyan snd_dmaengine_pcm_set_data(substream, dma_data); 940ed275efSAlexander Shiyan 950ed275efSAlexander Shiyan return 0; 960ed275efSAlexander Shiyan } 970ed275efSAlexander Shiyan 980ed275efSAlexander Shiyan static int ep93xx_pcm_close(struct snd_pcm_substream *substream) 990ed275efSAlexander Shiyan { 1000ed275efSAlexander Shiyan struct dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); 1010ed275efSAlexander Shiyan 1020ed275efSAlexander Shiyan snd_dmaengine_pcm_close(substream); 1030ed275efSAlexander Shiyan kfree(dma_data); 1040ed275efSAlexander Shiyan return 0; 1050ed275efSAlexander Shiyan } 1060ed275efSAlexander Shiyan 1070ed275efSAlexander Shiyan static int ep93xx_pcm_hw_params(struct snd_pcm_substream *substream, 1080ed275efSAlexander Shiyan struct snd_pcm_hw_params *params) 1090ed275efSAlexander Shiyan { 1100ed275efSAlexander Shiyan snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 1110ed275efSAlexander Shiyan 1120ed275efSAlexander Shiyan return 0; 1130ed275efSAlexander Shiyan } 1140ed275efSAlexander Shiyan 1150ed275efSAlexander Shiyan static int ep93xx_pcm_hw_free(struct snd_pcm_substream *substream) 1160ed275efSAlexander Shiyan { 1170ed275efSAlexander Shiyan snd_pcm_set_runtime_buffer(substream, NULL); 1180ed275efSAlexander Shiyan return 0; 1190ed275efSAlexander Shiyan } 1200ed275efSAlexander Shiyan 1210ed275efSAlexander Shiyan static int ep93xx_pcm_mmap(struct snd_pcm_substream *substream, 1220ed275efSAlexander Shiyan struct vm_area_struct *vma) 1230ed275efSAlexander Shiyan { 1240ed275efSAlexander Shiyan struct snd_pcm_runtime *runtime = substream->runtime; 1250ed275efSAlexander Shiyan 1260ed275efSAlexander Shiyan return dma_mmap_writecombine(substream->pcm->card->dev, vma, 1270ed275efSAlexander Shiyan runtime->dma_area, 1280ed275efSAlexander Shiyan runtime->dma_addr, 1290ed275efSAlexander Shiyan runtime->dma_bytes); 1300ed275efSAlexander Shiyan } 1310ed275efSAlexander Shiyan 1320ed275efSAlexander Shiyan static struct snd_pcm_ops ep93xx_pcm_ops = { 1330ed275efSAlexander Shiyan .open = ep93xx_pcm_open, 1340ed275efSAlexander Shiyan .close = ep93xx_pcm_close, 1350ed275efSAlexander Shiyan .ioctl = snd_pcm_lib_ioctl, 1360ed275efSAlexander Shiyan .hw_params = ep93xx_pcm_hw_params, 1370ed275efSAlexander Shiyan .hw_free = ep93xx_pcm_hw_free, 1380ed275efSAlexander Shiyan .trigger = snd_dmaengine_pcm_trigger, 1390ed275efSAlexander Shiyan .pointer = snd_dmaengine_pcm_pointer_no_residue, 1400ed275efSAlexander Shiyan .mmap = ep93xx_pcm_mmap, 1410ed275efSAlexander Shiyan }; 1420ed275efSAlexander Shiyan 1430ed275efSAlexander Shiyan static int ep93xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 1440ed275efSAlexander Shiyan { 1450ed275efSAlexander Shiyan struct snd_pcm_substream *substream = pcm->streams[stream].substream; 1460ed275efSAlexander Shiyan struct snd_dma_buffer *buf = &substream->dma_buffer; 1470ed275efSAlexander Shiyan size_t size = ep93xx_pcm_hardware.buffer_bytes_max; 1480ed275efSAlexander Shiyan 1490ed275efSAlexander Shiyan buf->dev.type = SNDRV_DMA_TYPE_DEV; 1500ed275efSAlexander Shiyan buf->dev.dev = pcm->card->dev; 1510ed275efSAlexander Shiyan buf->private_data = NULL; 1520ed275efSAlexander Shiyan buf->area = dma_alloc_writecombine(pcm->card->dev, size, 1530ed275efSAlexander Shiyan &buf->addr, GFP_KERNEL); 1540ed275efSAlexander Shiyan buf->bytes = size; 1550ed275efSAlexander Shiyan 1560ed275efSAlexander Shiyan return (buf->area == NULL) ? -ENOMEM : 0; 1570ed275efSAlexander Shiyan } 1580ed275efSAlexander Shiyan 1590ed275efSAlexander Shiyan static void ep93xx_pcm_free_dma_buffers(struct snd_pcm *pcm) 1600ed275efSAlexander Shiyan { 1610ed275efSAlexander Shiyan struct snd_pcm_substream *substream; 1620ed275efSAlexander Shiyan struct snd_dma_buffer *buf; 1630ed275efSAlexander Shiyan int stream; 1640ed275efSAlexander Shiyan 1650ed275efSAlexander Shiyan for (stream = 0; stream < 2; stream++) { 1660ed275efSAlexander Shiyan substream = pcm->streams[stream].substream; 1670ed275efSAlexander Shiyan if (!substream) 1680ed275efSAlexander Shiyan continue; 1690ed275efSAlexander Shiyan 1700ed275efSAlexander Shiyan buf = &substream->dma_buffer; 1710ed275efSAlexander Shiyan if (!buf->area) 1720ed275efSAlexander Shiyan continue; 1730ed275efSAlexander Shiyan 1740ed275efSAlexander Shiyan dma_free_writecombine(pcm->card->dev, buf->bytes, buf->area, 1750ed275efSAlexander Shiyan buf->addr); 1760ed275efSAlexander Shiyan buf->area = NULL; 1770ed275efSAlexander Shiyan } 1780ed275efSAlexander Shiyan } 1790ed275efSAlexander Shiyan 1800ed275efSAlexander Shiyan static u64 ep93xx_pcm_dmamask = DMA_BIT_MASK(32); 1810ed275efSAlexander Shiyan 1820ed275efSAlexander Shiyan static int ep93xx_pcm_new(struct snd_soc_pcm_runtime *rtd) 1830ed275efSAlexander Shiyan { 1840ed275efSAlexander Shiyan struct snd_card *card = rtd->card->snd_card; 1850ed275efSAlexander Shiyan struct snd_pcm *pcm = rtd->pcm; 1860ed275efSAlexander Shiyan int ret = 0; 1870ed275efSAlexander Shiyan 1880ed275efSAlexander Shiyan if (!card->dev->dma_mask) 1890ed275efSAlexander Shiyan card->dev->dma_mask = &ep93xx_pcm_dmamask; 1900ed275efSAlexander Shiyan if (!card->dev->coherent_dma_mask) 1910ed275efSAlexander Shiyan card->dev->coherent_dma_mask = DMA_BIT_MASK(32); 1920ed275efSAlexander Shiyan 1930ed275efSAlexander Shiyan if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { 1940ed275efSAlexander Shiyan ret = ep93xx_pcm_preallocate_dma_buffer(pcm, 1950ed275efSAlexander Shiyan SNDRV_PCM_STREAM_PLAYBACK); 1960ed275efSAlexander Shiyan if (ret) 1970ed275efSAlexander Shiyan return ret; 1980ed275efSAlexander Shiyan } 1990ed275efSAlexander Shiyan 2000ed275efSAlexander Shiyan if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { 2010ed275efSAlexander Shiyan ret = ep93xx_pcm_preallocate_dma_buffer(pcm, 2020ed275efSAlexander Shiyan SNDRV_PCM_STREAM_CAPTURE); 2030ed275efSAlexander Shiyan if (ret) 2040ed275efSAlexander Shiyan return ret; 2050ed275efSAlexander Shiyan } 2060ed275efSAlexander Shiyan 2070ed275efSAlexander Shiyan return 0; 2080ed275efSAlexander Shiyan } 2090ed275efSAlexander Shiyan 2100ed275efSAlexander Shiyan static struct snd_soc_platform_driver ep93xx_soc_platform = { 2110ed275efSAlexander Shiyan .ops = &ep93xx_pcm_ops, 2120ed275efSAlexander Shiyan .pcm_new = &ep93xx_pcm_new, 2130ed275efSAlexander Shiyan .pcm_free = &ep93xx_pcm_free_dma_buffers, 2140ed275efSAlexander Shiyan }; 2150ed275efSAlexander Shiyan 2160ed275efSAlexander Shiyan static int __devinit ep93xx_soc_platform_probe(struct platform_device *pdev) 2170ed275efSAlexander Shiyan { 2180ed275efSAlexander Shiyan return snd_soc_register_platform(&pdev->dev, &ep93xx_soc_platform); 2190ed275efSAlexander Shiyan } 2200ed275efSAlexander Shiyan 2210ed275efSAlexander Shiyan static int __devexit ep93xx_soc_platform_remove(struct platform_device *pdev) 2220ed275efSAlexander Shiyan { 2230ed275efSAlexander Shiyan snd_soc_unregister_platform(&pdev->dev); 2240ed275efSAlexander Shiyan return 0; 2250ed275efSAlexander Shiyan } 2260ed275efSAlexander Shiyan 2270ed275efSAlexander Shiyan static struct platform_driver ep93xx_pcm_driver = { 2280ed275efSAlexander Shiyan .driver = { 2290ed275efSAlexander Shiyan .name = "ep93xx-pcm-audio", 2300ed275efSAlexander Shiyan .owner = THIS_MODULE, 2310ed275efSAlexander Shiyan }, 2320ed275efSAlexander Shiyan 2330ed275efSAlexander Shiyan .probe = ep93xx_soc_platform_probe, 2340ed275efSAlexander Shiyan .remove = __devexit_p(ep93xx_soc_platform_remove), 2350ed275efSAlexander Shiyan }; 2360ed275efSAlexander Shiyan 2370ed275efSAlexander Shiyan module_platform_driver(ep93xx_pcm_driver); 2380ed275efSAlexander Shiyan 2390ed275efSAlexander Shiyan MODULE_AUTHOR("Ryan Mallon"); 2400ed275efSAlexander Shiyan MODULE_DESCRIPTION("EP93xx ALSA PCM interface"); 2410ed275efSAlexander Shiyan MODULE_LICENSE("GPL"); 2420ed275efSAlexander Shiyan MODULE_ALIAS("platform:ep93xx-pcm-audio"); 243