1/* 2 * ALSA SoC Voice Codec Interface for TI DAVINCI processor 3 * 4 * Copyright (C) 2010 Texas Instruments. 5 * 6 * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23#include <linux/init.h> 24#include <linux/module.h> 25#include <linux/device.h> 26#include <linux/delay.h> 27#include <linux/slab.h> 28#include <linux/io.h> 29#include <linux/mfd/davinci_voicecodec.h> 30 31#include <sound/core.h> 32#include <sound/pcm.h> 33#include <sound/pcm_params.h> 34#include <sound/initval.h> 35#include <sound/soc.h> 36 37#include "davinci-pcm.h" 38#include "davinci-i2s.h" 39#include "davinci-vcif.h" 40 41#define MOD_REG_BIT(val, mask, set) do { \ 42 if (set) { \ 43 val |= mask; \ 44 } else { \ 45 val &= ~mask; \ 46 } \ 47} while (0) 48 49struct davinci_vcif_dev { 50 struct davinci_vc *davinci_vc; 51 struct davinci_pcm_dma_params dma_params[2]; 52}; 53 54static void davinci_vcif_start(struct snd_pcm_substream *substream) 55{ 56 struct snd_soc_pcm_runtime *rtd = substream->private_data; 57 struct davinci_vcif_dev *davinci_vcif_dev = 58 rtd->dai->cpu_dai->private_data; 59 struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; 60 u32 w; 61 62 /* Start the sample generator and enable transmitter/receiver */ 63 w = readl(davinci_vc->base + DAVINCI_VC_CTRL); 64 65 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 66 MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); 67 else 68 MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); 69 70 writel(w, davinci_vc->base + DAVINCI_VC_CTRL); 71} 72 73static void davinci_vcif_stop(struct snd_pcm_substream *substream) 74{ 75 struct snd_soc_pcm_runtime *rtd = substream->private_data; 76 struct davinci_vcif_dev *davinci_vcif_dev = 77 rtd->dai->cpu_dai->private_data; 78 struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; 79 u32 w; 80 81 /* Reset transmitter/receiver and sample rate/frame sync generators */ 82 w = readl(davinci_vc->base + DAVINCI_VC_CTRL); 83 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 84 MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); 85 else 86 MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); 87 88 writel(w, davinci_vc->base + DAVINCI_VC_CTRL); 89} 90 91static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, 92 struct snd_pcm_hw_params *params, 93 struct snd_soc_dai *dai) 94{ 95 struct davinci_vcif_dev *davinci_vcif_dev = dai->private_data; 96 struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; 97 struct davinci_pcm_dma_params *dma_params = 98 &davinci_vcif_dev->dma_params[substream->stream]; 99 u32 w; 100 101 /* Restart the codec before setup */ 102 davinci_vcif_stop(substream); 103 davinci_vcif_start(substream); 104 105 /* General line settings */ 106 writel(DAVINCI_VC_CTRL_MASK, davinci_vc->base + DAVINCI_VC_CTRL); 107 108 writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTCLR); 109 110 writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTEN); 111 112 w = readl(davinci_vc->base + DAVINCI_VC_CTRL); 113 114 /* Determine xfer data type */ 115 switch (params_format(params)) { 116 case SNDRV_PCM_FORMAT_U8: 117 dma_params->data_type = 0; 118 119 MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | 120 DAVINCI_VC_CTRL_RD_UNSIGNED | 121 DAVINCI_VC_CTRL_WD_BITS_8 | 122 DAVINCI_VC_CTRL_WD_UNSIGNED, 1); 123 break; 124 case SNDRV_PCM_FORMAT_S8: 125 dma_params->data_type = 1; 126 127 MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | 128 DAVINCI_VC_CTRL_WD_BITS_8, 1); 129 130 MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_UNSIGNED | 131 DAVINCI_VC_CTRL_WD_UNSIGNED, 0); 132 break; 133 case SNDRV_PCM_FORMAT_S16_LE: 134 dma_params->data_type = 2; 135 136 MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | 137 DAVINCI_VC_CTRL_RD_UNSIGNED | 138 DAVINCI_VC_CTRL_WD_BITS_8 | 139 DAVINCI_VC_CTRL_WD_UNSIGNED, 0); 140 break; 141 default: 142 printk(KERN_WARNING "davinci-vcif: unsupported PCM format"); 143 return -EINVAL; 144 } 145 146 dma_params->acnt = dma_params->data_type; 147 148 writel(w, davinci_vc->base + DAVINCI_VC_CTRL); 149 150 return 0; 151} 152 153static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, 154 struct snd_soc_dai *dai) 155{ 156 int ret = 0; 157 158 switch (cmd) { 159 case SNDRV_PCM_TRIGGER_START: 160 case SNDRV_PCM_TRIGGER_RESUME: 161 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 162 davinci_vcif_start(substream); 163 case SNDRV_PCM_TRIGGER_STOP: 164 case SNDRV_PCM_TRIGGER_SUSPEND: 165 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 166 davinci_vcif_stop(substream); 167 break; 168 default: 169 ret = -EINVAL; 170 } 171 172 return ret; 173} 174 175#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000 176 177static struct snd_soc_dai_ops davinci_vcif_dai_ops = { 178 .trigger = davinci_vcif_trigger, 179 .hw_params = davinci_vcif_hw_params, 180}; 181 182struct snd_soc_dai davinci_vcif_dai = { 183 .name = "davinci-vcif", 184 .playback = { 185 .channels_min = 1, 186 .channels_max = 2, 187 .rates = DAVINCI_VCIF_RATES, 188 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 189 .capture = { 190 .channels_min = 1, 191 .channels_max = 2, 192 .rates = DAVINCI_VCIF_RATES, 193 .formats = SNDRV_PCM_FMTBIT_S16_LE,}, 194 .ops = &davinci_vcif_dai_ops, 195 196}; 197EXPORT_SYMBOL_GPL(davinci_vcif_dai); 198 199static int davinci_vcif_probe(struct platform_device *pdev) 200{ 201 struct davinci_vc *davinci_vc = platform_get_drvdata(pdev); 202 struct davinci_vcif_dev *davinci_vcif_dev; 203 int ret; 204 205 davinci_vcif_dev = kzalloc(sizeof(struct davinci_vcif_dev), GFP_KERNEL); 206 if (!davinci_vcif_dev) { 207 dev_dbg(&pdev->dev, 208 "could not allocate memory for private data\n"); 209 return -ENOMEM; 210 } 211 212 /* DMA tx params */ 213 davinci_vcif_dev->davinci_vc = davinci_vc; 214 davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = 215 davinci_vc->davinci_vcif.dma_tx_channel; 216 davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = 217 davinci_vc->davinci_vcif.dma_tx_addr; 218 219 /* DMA rx params */ 220 davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = 221 davinci_vc->davinci_vcif.dma_rx_channel; 222 davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = 223 davinci_vc->davinci_vcif.dma_rx_addr; 224 225 davinci_vcif_dai.dev = &pdev->dev; 226 davinci_vcif_dai.capture.dma_data = davinci_vcif_dev->dma_params; 227 davinci_vcif_dai.playback.dma_data = davinci_vcif_dev->dma_params; 228 davinci_vcif_dai.private_data = davinci_vcif_dev; 229 230 ret = snd_soc_register_dai(&davinci_vcif_dai); 231 if (ret != 0) { 232 dev_err(&pdev->dev, "could not register dai\n"); 233 goto fail; 234 } 235 236 return 0; 237 238fail: 239 kfree(davinci_vcif_dev); 240 241 return ret; 242} 243 244static int davinci_vcif_remove(struct platform_device *pdev) 245{ 246 snd_soc_unregister_dai(&davinci_vcif_dai); 247 248 return 0; 249} 250 251static struct platform_driver davinci_vcif_driver = { 252 .probe = davinci_vcif_probe, 253 .remove = davinci_vcif_remove, 254 .driver = { 255 .name = "davinci_vcif", 256 .owner = THIS_MODULE, 257 }, 258}; 259 260static int __init davinci_vcif_init(void) 261{ 262 return platform_driver_probe(&davinci_vcif_driver, davinci_vcif_probe); 263} 264module_init(davinci_vcif_init); 265 266static void __exit davinci_vcif_exit(void) 267{ 268 platform_driver_unregister(&davinci_vcif_driver); 269} 270module_exit(davinci_vcif_exit); 271 272MODULE_AUTHOR("Miguel Aguilar"); 273MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC Voice Codec Interface"); 274MODULE_LICENSE("GPL"); 275