1/* 2 * File: sound/soc/blackfin/bf5xx-i2s.c 3 * Author: Cliff Cai <Cliff.Cai@analog.com> 4 * 5 * Created: Tue June 06 2008 6 * Description: Blackfin I2S CPU DAI driver 7 * 8 * Modified: 9 * Copyright 2008 Analog Devices Inc. 10 * 11 * Bugs: Enter bugs at http://blackfin.uclinux.org/ 12 * 13 * This program is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This program is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, see the file COPYING, or write 25 * to the Free Software Foundation, Inc., 26 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 27 */ 28 29#include <linux/init.h> 30#include <linux/module.h> 31#include <linux/device.h> 32#include <linux/delay.h> 33#include <sound/core.h> 34#include <sound/pcm.h> 35#include <sound/pcm_params.h> 36#include <sound/initval.h> 37#include <sound/soc.h> 38 39#include <asm/irq.h> 40#include <asm/portmux.h> 41#include <linux/mutex.h> 42#include <linux/gpio.h> 43 44#include "bf5xx-sport.h" 45#include "bf5xx-i2s-pcm.h" 46 47struct bf5xx_i2s_port { 48 u16 tcr1; 49 u16 rcr1; 50 u16 tcr2; 51 u16 rcr2; 52 int configured; 53 54 unsigned int slots; 55 unsigned int tx_mask; 56 unsigned int rx_mask; 57 58 struct bf5xx_i2s_pcm_data tx_dma_data; 59 struct bf5xx_i2s_pcm_data rx_dma_data; 60}; 61 62static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, 63 unsigned int fmt) 64{ 65 struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); 66 struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; 67 int ret = 0; 68 69 /* interface format:support I2S,slave mode */ 70 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 71 case SND_SOC_DAIFMT_I2S: 72 bf5xx_i2s->tcr1 |= TFSR | TCKFE; 73 bf5xx_i2s->rcr1 |= RFSR | RCKFE; 74 bf5xx_i2s->tcr2 |= TSFSE; 75 bf5xx_i2s->rcr2 |= RSFSE; 76 break; 77 case SND_SOC_DAIFMT_DSP_A: 78 bf5xx_i2s->tcr1 |= TFSR; 79 bf5xx_i2s->rcr1 |= RFSR; 80 break; 81 case SND_SOC_DAIFMT_LEFT_J: 82 ret = -EINVAL; 83 break; 84 default: 85 dev_err(cpu_dai->dev, "%s: Unknown DAI format type\n", 86 __func__); 87 ret = -EINVAL; 88 break; 89 } 90 91 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 92 case SND_SOC_DAIFMT_CBM_CFM: 93 break; 94 case SND_SOC_DAIFMT_CBS_CFS: 95 case SND_SOC_DAIFMT_CBM_CFS: 96 case SND_SOC_DAIFMT_CBS_CFM: 97 ret = -EINVAL; 98 break; 99 default: 100 dev_err(cpu_dai->dev, "%s: Unknown DAI master type\n", 101 __func__); 102 ret = -EINVAL; 103 break; 104 } 105 106 return ret; 107} 108 109static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, 110 struct snd_pcm_hw_params *params, 111 struct snd_soc_dai *dai) 112{ 113 struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); 114 struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; 115 int ret = 0; 116 117 bf5xx_i2s->tcr2 &= ~0x1f; 118 bf5xx_i2s->rcr2 &= ~0x1f; 119 switch (params_format(params)) { 120 case SNDRV_PCM_FORMAT_S8: 121 bf5xx_i2s->tcr2 |= 7; 122 bf5xx_i2s->rcr2 |= 7; 123 sport_handle->wdsize = 1; 124 break; 125 case SNDRV_PCM_FORMAT_S16_LE: 126 bf5xx_i2s->tcr2 |= 15; 127 bf5xx_i2s->rcr2 |= 15; 128 sport_handle->wdsize = 2; 129 break; 130 case SNDRV_PCM_FORMAT_S24_LE: 131 bf5xx_i2s->tcr2 |= 23; 132 bf5xx_i2s->rcr2 |= 23; 133 sport_handle->wdsize = 3; 134 break; 135 case SNDRV_PCM_FORMAT_S32_LE: 136 bf5xx_i2s->tcr2 |= 31; 137 bf5xx_i2s->rcr2 |= 31; 138 sport_handle->wdsize = 4; 139 break; 140 } 141 142 if (!bf5xx_i2s->configured) { 143 /* 144 * TX and RX are not independent,they are enabled at the 145 * same time, even if only one side is running. So, we 146 * need to configure both of them at the time when the first 147 * stream is opened. 148 * 149 * CPU DAI:slave mode. 150 */ 151 bf5xx_i2s->configured = 1; 152 ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, 153 bf5xx_i2s->rcr2, 0, 0); 154 if (ret) { 155 dev_err(dai->dev, "SPORT is busy!\n"); 156 return -EBUSY; 157 } 158 159 ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, 160 bf5xx_i2s->tcr2, 0, 0); 161 if (ret) { 162 dev_err(dai->dev, "SPORT is busy!\n"); 163 return -EBUSY; 164 } 165 } 166 167 return 0; 168} 169 170static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream, 171 struct snd_soc_dai *dai) 172{ 173 struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); 174 struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; 175 176 dev_dbg(dai->dev, "%s enter\n", __func__); 177 /* No active stream, SPORT is allowed to be configured again. */ 178 if (!dai->active) 179 bf5xx_i2s->configured = 0; 180} 181 182static int bf5xx_i2s_set_channel_map(struct snd_soc_dai *dai, 183 unsigned int tx_num, unsigned int *tx_slot, 184 unsigned int rx_num, unsigned int *rx_slot) 185{ 186 struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); 187 struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; 188 unsigned int tx_mapped = 0, rx_mapped = 0; 189 unsigned int slot; 190 int i; 191 192 if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) || 193 (rx_num > BFIN_TDM_DAI_MAX_SLOTS)) 194 return -EINVAL; 195 196 for (i = 0; i < tx_num; i++) { 197 slot = tx_slot[i]; 198 if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && 199 (!(tx_mapped & (1 << slot)))) { 200 bf5xx_i2s->tx_dma_data.map[i] = slot; 201 tx_mapped |= 1 << slot; 202 } else 203 return -EINVAL; 204 } 205 for (i = 0; i < rx_num; i++) { 206 slot = rx_slot[i]; 207 if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && 208 (!(rx_mapped & (1 << slot)))) { 209 bf5xx_i2s->rx_dma_data.map[i] = slot; 210 rx_mapped |= 1 << slot; 211 } else 212 return -EINVAL; 213 } 214 215 return 0; 216} 217 218static int bf5xx_i2s_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, 219 unsigned int rx_mask, int slots, int width) 220{ 221 struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); 222 struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; 223 224 if (slots % 8 != 0 || slots > 8) 225 return -EINVAL; 226 227 if (width != 32) 228 return -EINVAL; 229 230 bf5xx_i2s->slots = slots; 231 bf5xx_i2s->tx_mask = tx_mask; 232 bf5xx_i2s->rx_mask = rx_mask; 233 234 bf5xx_i2s->tx_dma_data.tdm_mode = slots != 0; 235 bf5xx_i2s->rx_dma_data.tdm_mode = slots != 0; 236 237 return sport_set_multichannel(sport_handle, slots, tx_mask, rx_mask, 0); 238} 239 240#ifdef CONFIG_PM 241static int bf5xx_i2s_suspend(struct snd_soc_dai *dai) 242{ 243 struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); 244 245 dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id); 246 247 if (dai->capture_active) 248 sport_rx_stop(sport_handle); 249 if (dai->playback_active) 250 sport_tx_stop(sport_handle); 251 return 0; 252} 253 254static int bf5xx_i2s_resume(struct snd_soc_dai *dai) 255{ 256 struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); 257 struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; 258 int ret; 259 260 dev_dbg(dai->dev, "%s : sport %d\n", __func__, dai->id); 261 262 ret = sport_config_rx(sport_handle, bf5xx_i2s->rcr1, 263 bf5xx_i2s->rcr2, 0, 0); 264 if (ret) { 265 dev_err(dai->dev, "SPORT is busy!\n"); 266 return -EBUSY; 267 } 268 269 ret = sport_config_tx(sport_handle, bf5xx_i2s->tcr1, 270 bf5xx_i2s->tcr2, 0, 0); 271 if (ret) { 272 dev_err(dai->dev, "SPORT is busy!\n"); 273 return -EBUSY; 274 } 275 276 return sport_set_multichannel(sport_handle, bf5xx_i2s->slots, 277 bf5xx_i2s->tx_mask, bf5xx_i2s->rx_mask, 0); 278} 279 280#else 281#define bf5xx_i2s_suspend NULL 282#define bf5xx_i2s_resume NULL 283#endif 284 285static int bf5xx_i2s_dai_probe(struct snd_soc_dai *dai) 286{ 287 struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); 288 struct bf5xx_i2s_port *bf5xx_i2s = sport_handle->private_data; 289 unsigned int i; 290 291 for (i = 0; i < BFIN_TDM_DAI_MAX_SLOTS; i++) { 292 bf5xx_i2s->tx_dma_data.map[i] = i; 293 bf5xx_i2s->rx_dma_data.map[i] = i; 294 } 295 296 dai->playback_dma_data = &bf5xx_i2s->tx_dma_data; 297 dai->capture_dma_data = &bf5xx_i2s->rx_dma_data; 298 299 return 0; 300} 301 302#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\ 303 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ 304 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ 305 SNDRV_PCM_RATE_96000) 306 307#define BF5XX_I2S_FORMATS \ 308 (SNDRV_PCM_FMTBIT_S8 | \ 309 SNDRV_PCM_FMTBIT_S16_LE | \ 310 SNDRV_PCM_FMTBIT_S24_LE | \ 311 SNDRV_PCM_FMTBIT_S32_LE) 312 313static const struct snd_soc_dai_ops bf5xx_i2s_dai_ops = { 314 .shutdown = bf5xx_i2s_shutdown, 315 .hw_params = bf5xx_i2s_hw_params, 316 .set_fmt = bf5xx_i2s_set_dai_fmt, 317 .set_tdm_slot = bf5xx_i2s_set_tdm_slot, 318 .set_channel_map = bf5xx_i2s_set_channel_map, 319}; 320 321static struct snd_soc_dai_driver bf5xx_i2s_dai = { 322 .probe = bf5xx_i2s_dai_probe, 323 .suspend = bf5xx_i2s_suspend, 324 .resume = bf5xx_i2s_resume, 325 .playback = { 326 .channels_min = 2, 327 .channels_max = 8, 328 .rates = BF5XX_I2S_RATES, 329 .formats = BF5XX_I2S_FORMATS,}, 330 .capture = { 331 .channels_min = 2, 332 .channels_max = 8, 333 .rates = BF5XX_I2S_RATES, 334 .formats = BF5XX_I2S_FORMATS,}, 335 .ops = &bf5xx_i2s_dai_ops, 336}; 337 338static const struct snd_soc_component_driver bf5xx_i2s_component = { 339 .name = "bf5xx-i2s", 340}; 341 342static int bf5xx_i2s_probe(struct platform_device *pdev) 343{ 344 struct sport_device *sport_handle; 345 int ret; 346 347 /* configure SPORT for I2S */ 348 sport_handle = sport_init(pdev, 4, 8 * sizeof(u32), 349 sizeof(struct bf5xx_i2s_port)); 350 if (!sport_handle) 351 return -ENODEV; 352 353 /* register with the ASoC layers */ 354 ret = snd_soc_register_component(&pdev->dev, &bf5xx_i2s_component, 355 &bf5xx_i2s_dai, 1); 356 if (ret) { 357 dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret); 358 sport_done(sport_handle); 359 return ret; 360 } 361 362 return 0; 363} 364 365static int bf5xx_i2s_remove(struct platform_device *pdev) 366{ 367 struct sport_device *sport_handle = platform_get_drvdata(pdev); 368 369 dev_dbg(&pdev->dev, "%s enter\n", __func__); 370 371 snd_soc_unregister_component(&pdev->dev); 372 sport_done(sport_handle); 373 374 return 0; 375} 376 377static struct platform_driver bfin_i2s_driver = { 378 .probe = bf5xx_i2s_probe, 379 .remove = bf5xx_i2s_remove, 380 .driver = { 381 .name = "bfin-i2s", 382 }, 383}; 384 385module_platform_driver(bfin_i2s_driver); 386 387/* Module information */ 388MODULE_AUTHOR("Cliff Cai"); 389MODULE_DESCRIPTION("I2S driver for ADI Blackfin"); 390MODULE_LICENSE("GPL"); 391 392