1/* 2 * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port 3 * 4 * Copyright (C) 2009 Texas Instruments 5 * 6 * Author: Misael Lopez Cruz <x0052729@ti.com> 7 * Contact: Jorge Eduardo Candelaria <x0107209@ti.com> 8 * Margarita Olaya <magi.olaya@ti.com> 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License 12 * version 2 as published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 * 02110-1301 USA 23 * 24 */ 25 26#include <linux/init.h> 27#include <linux/module.h> 28#include <linux/device.h> 29#include <sound/core.h> 30#include <sound/pcm.h> 31#include <sound/pcm_params.h> 32#include <sound/initval.h> 33#include <sound/soc.h> 34 35#include <plat/control.h> 36#include <plat/dma.h> 37#include <plat/mcbsp.h> 38#include "mcpdm.h" 39#include "omap-mcpdm.h" 40#include "omap-pcm.h" 41 42struct omap_mcpdm_data { 43 struct omap_mcpdm_link *links; 44 int active; 45}; 46 47static struct omap_mcpdm_link omap_mcpdm_links[] = { 48 /* downlink */ 49 { 50 .irq_mask = MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL, 51 .threshold = 1, 52 .format = PDMOUTFORMAT_LJUST, 53 }, 54 /* uplink */ 55 { 56 .irq_mask = MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL, 57 .threshold = 1, 58 .format = PDMOUTFORMAT_LJUST, 59 }, 60}; 61 62static struct omap_mcpdm_data mcpdm_data = { 63 .links = omap_mcpdm_links, 64 .active = 0, 65}; 66 67/* 68 * Stream DMA parameters 69 */ 70static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = { 71 { 72 .name = "Audio playback", 73 .dma_req = OMAP44XX_DMA_MCPDM_DL, 74 .data_type = OMAP_DMA_DATA_TYPE_S32, 75 .sync_mode = OMAP_DMA_SYNC_PACKET, 76 .packet_size = 16, 77 .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_DN_DATA, 78 }, 79 { 80 .name = "Audio capture", 81 .dma_req = OMAP44XX_DMA_MCPDM_UP, 82 .data_type = OMAP_DMA_DATA_TYPE_S32, 83 .sync_mode = OMAP_DMA_SYNC_PACKET, 84 .packet_size = 16, 85 .port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_UP_DATA, 86 }, 87}; 88 89static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, 90 struct snd_soc_dai *dai) 91{ 92 struct snd_soc_pcm_runtime *rtd = substream->private_data; 93 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 94 int err = 0; 95 96 if (!cpu_dai->active) 97 err = omap_mcpdm_request(); 98 99 return err; 100} 101 102static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, 103 struct snd_soc_dai *dai) 104{ 105 struct snd_soc_pcm_runtime *rtd = substream->private_data; 106 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 107 108 if (!cpu_dai->active) 109 omap_mcpdm_free(); 110} 111 112static int omap_mcpdm_dai_trigger(struct snd_pcm_substream *substream, int cmd, 113 struct snd_soc_dai *dai) 114{ 115 struct snd_soc_pcm_runtime *rtd = substream->private_data; 116 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 117 struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; 118 int stream = substream->stream; 119 int err = 0; 120 121 switch (cmd) { 122 case SNDRV_PCM_TRIGGER_START: 123 case SNDRV_PCM_TRIGGER_RESUME: 124 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 125 if (!mcpdm_priv->active++) 126 omap_mcpdm_start(stream); 127 break; 128 129 case SNDRV_PCM_TRIGGER_STOP: 130 case SNDRV_PCM_TRIGGER_SUSPEND: 131 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 132 if (!--mcpdm_priv->active) 133 omap_mcpdm_stop(stream); 134 break; 135 default: 136 err = -EINVAL; 137 } 138 139 return err; 140} 141 142static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, 143 struct snd_pcm_hw_params *params, 144 struct snd_soc_dai *dai) 145{ 146 struct snd_soc_pcm_runtime *rtd = substream->private_data; 147 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 148 struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; 149 struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links; 150 int stream = substream->stream; 151 int channels, err, link_mask = 0; 152 153 snd_soc_dai_set_dma_data(cpu_dai, substream, 154 &omap_mcpdm_dai_dma_params[stream]); 155 156 channels = params_channels(params); 157 switch (channels) { 158 case 4: 159 if (stream == SNDRV_PCM_STREAM_CAPTURE) 160 /* up to 2 channels for capture */ 161 return -EINVAL; 162 link_mask |= 1 << 3; 163 case 3: 164 if (stream == SNDRV_PCM_STREAM_CAPTURE) 165 /* up to 2 channels for capture */ 166 return -EINVAL; 167 link_mask |= 1 << 2; 168 case 2: 169 link_mask |= 1 << 1; 170 case 1: 171 link_mask |= 1 << 0; 172 break; 173 default: 174 /* unsupported number of channels */ 175 return -EINVAL; 176 } 177 178 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 179 mcpdm_links[stream].channels = link_mask << 3; 180 err = omap_mcpdm_playback_open(&mcpdm_links[stream]); 181 } else { 182 mcpdm_links[stream].channels = link_mask << 0; 183 err = omap_mcpdm_capture_open(&mcpdm_links[stream]); 184 } 185 186 return err; 187} 188 189static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream, 190 struct snd_soc_dai *dai) 191{ 192 struct snd_soc_pcm_runtime *rtd = substream->private_data; 193 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; 194 struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data; 195 struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links; 196 int stream = substream->stream; 197 int err; 198 199 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 200 err = omap_mcpdm_playback_close(&mcpdm_links[stream]); 201 else 202 err = omap_mcpdm_capture_close(&mcpdm_links[stream]); 203 204 return err; 205} 206 207static struct snd_soc_dai_ops omap_mcpdm_dai_ops = { 208 .startup = omap_mcpdm_dai_startup, 209 .shutdown = omap_mcpdm_dai_shutdown, 210 .trigger = omap_mcpdm_dai_trigger, 211 .hw_params = omap_mcpdm_dai_hw_params, 212 .hw_free = omap_mcpdm_dai_hw_free, 213}; 214 215#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) 216#define OMAP_MCPDM_FORMATS (SNDRV_PCM_FMTBIT_S32_LE) 217 218struct snd_soc_dai omap_mcpdm_dai = { 219 .name = "omap-mcpdm", 220 .id = -1, 221 .playback = { 222 .channels_min = 1, 223 .channels_max = 4, 224 .rates = OMAP_MCPDM_RATES, 225 .formats = OMAP_MCPDM_FORMATS, 226 }, 227 .capture = { 228 .channels_min = 1, 229 .channels_max = 2, 230 .rates = OMAP_MCPDM_RATES, 231 .formats = OMAP_MCPDM_FORMATS, 232 }, 233 .ops = &omap_mcpdm_dai_ops, 234 .private_data = &mcpdm_data, 235}; 236EXPORT_SYMBOL_GPL(omap_mcpdm_dai); 237 238static int __init snd_omap_mcpdm_init(void) 239{ 240 return snd_soc_register_dai(&omap_mcpdm_dai); 241} 242module_init(snd_omap_mcpdm_init); 243 244static void __exit snd_omap_mcpdm_exit(void) 245{ 246 snd_soc_unregister_dai(&omap_mcpdm_dai); 247} 248module_exit(snd_omap_mcpdm_exit); 249 250MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>"); 251MODULE_DESCRIPTION("OMAP PDM SoC Interface"); 252MODULE_LICENSE("GPL"); 253