Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.7 455 lines 11 kB view raw
1/* 2 * ALSA SoC Synopsys I2S Audio Layer 3 * 4 * sound/soc/spear/designware_i2s.c 5 * 6 * Copyright (C) 2010 ST Microelectronics 7 * Rajeev Kumar <rajeev-dlh.kumar@st.com> 8 * 9 * This file is licensed under the terms of the GNU General Public 10 * License version 2. This program is licensed "as is" without any 11 * warranty of any kind, whether express or implied. 12 */ 13 14#include <linux/clk.h> 15#include <linux/device.h> 16#include <linux/init.h> 17#include <linux/io.h> 18#include <linux/interrupt.h> 19#include <linux/module.h> 20#include <linux/slab.h> 21#include <sound/designware_i2s.h> 22#include <sound/pcm.h> 23#include <sound/pcm_params.h> 24#include <sound/soc.h> 25 26/* common register for all channel */ 27#define IER 0x000 28#define IRER 0x004 29#define ITER 0x008 30#define CER 0x00C 31#define CCR 0x010 32#define RXFFR 0x014 33#define TXFFR 0x018 34 35/* I2STxRxRegisters for all channels */ 36#define LRBR_LTHR(x) (0x40 * x + 0x020) 37#define RRBR_RTHR(x) (0x40 * x + 0x024) 38#define RER(x) (0x40 * x + 0x028) 39#define TER(x) (0x40 * x + 0x02C) 40#define RCR(x) (0x40 * x + 0x030) 41#define TCR(x) (0x40 * x + 0x034) 42#define ISR(x) (0x40 * x + 0x038) 43#define IMR(x) (0x40 * x + 0x03C) 44#define ROR(x) (0x40 * x + 0x040) 45#define TOR(x) (0x40 * x + 0x044) 46#define RFCR(x) (0x40 * x + 0x048) 47#define TFCR(x) (0x40 * x + 0x04C) 48#define RFF(x) (0x40 * x + 0x050) 49#define TFF(x) (0x40 * x + 0x054) 50 51/* I2SCOMPRegisters */ 52#define I2S_COMP_PARAM_2 0x01F0 53#define I2S_COMP_PARAM_1 0x01F4 54#define I2S_COMP_VERSION 0x01F8 55#define I2S_COMP_TYPE 0x01FC 56 57#define MAX_CHANNEL_NUM 8 58#define MIN_CHANNEL_NUM 2 59 60struct dw_i2s_dev { 61 void __iomem *i2s_base; 62 struct clk *clk; 63 int active; 64 unsigned int capability; 65 struct device *dev; 66 67 /* data related to DMA transfers b/w i2s and DMAC */ 68 struct i2s_dma_data play_dma_data; 69 struct i2s_dma_data capture_dma_data; 70 struct i2s_clk_config_data config; 71 int (*i2s_clk_cfg)(struct i2s_clk_config_data *config); 72}; 73 74static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val) 75{ 76 writel(val, io_base + reg); 77} 78 79static inline u32 i2s_read_reg(void __iomem *io_base, int reg) 80{ 81 return readl(io_base + reg); 82} 83 84static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream) 85{ 86 u32 i = 0; 87 88 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 89 for (i = 0; i < 4; i++) 90 i2s_write_reg(dev->i2s_base, TER(i), 0); 91 } else { 92 for (i = 0; i < 4; i++) 93 i2s_write_reg(dev->i2s_base, RER(i), 0); 94 } 95} 96 97static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream) 98{ 99 u32 i = 0; 100 101 if (stream == SNDRV_PCM_STREAM_PLAYBACK) { 102 for (i = 0; i < 4; i++) 103 i2s_write_reg(dev->i2s_base, TOR(i), 0); 104 } else { 105 for (i = 0; i < 4; i++) 106 i2s_write_reg(dev->i2s_base, ROR(i), 0); 107 } 108} 109 110static void i2s_start(struct dw_i2s_dev *dev, 111 struct snd_pcm_substream *substream) 112{ 113 114 i2s_write_reg(dev->i2s_base, IER, 1); 115 116 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 117 i2s_write_reg(dev->i2s_base, ITER, 1); 118 else 119 i2s_write_reg(dev->i2s_base, IRER, 1); 120 121 i2s_write_reg(dev->i2s_base, CER, 1); 122} 123 124static void i2s_stop(struct dw_i2s_dev *dev, 125 struct snd_pcm_substream *substream) 126{ 127 u32 i = 0, irq; 128 129 i2s_clear_irqs(dev, substream->stream); 130 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 131 i2s_write_reg(dev->i2s_base, ITER, 0); 132 133 for (i = 0; i < 4; i++) { 134 irq = i2s_read_reg(dev->i2s_base, IMR(i)); 135 i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30); 136 } 137 } else { 138 i2s_write_reg(dev->i2s_base, IRER, 0); 139 140 for (i = 0; i < 4; i++) { 141 irq = i2s_read_reg(dev->i2s_base, IMR(i)); 142 i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03); 143 } 144 } 145 146 if (!dev->active) { 147 i2s_write_reg(dev->i2s_base, CER, 0); 148 i2s_write_reg(dev->i2s_base, IER, 0); 149 } 150} 151 152static int dw_i2s_startup(struct snd_pcm_substream *substream, 153 struct snd_soc_dai *cpu_dai) 154{ 155 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); 156 struct i2s_dma_data *dma_data = NULL; 157 158 if (!(dev->capability & DWC_I2S_RECORD) && 159 (substream->stream == SNDRV_PCM_STREAM_CAPTURE)) 160 return -EINVAL; 161 162 if (!(dev->capability & DWC_I2S_PLAY) && 163 (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)) 164 return -EINVAL; 165 166 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 167 dma_data = &dev->play_dma_data; 168 else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 169 dma_data = &dev->capture_dma_data; 170 171 snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data); 172 173 return 0; 174} 175 176static int dw_i2s_hw_params(struct snd_pcm_substream *substream, 177 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 178{ 179 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); 180 struct i2s_clk_config_data *config = &dev->config; 181 u32 ccr, xfer_resolution, ch_reg, irq; 182 int ret; 183 184 switch (params_format(params)) { 185 case SNDRV_PCM_FORMAT_S16_LE: 186 config->data_width = 16; 187 ccr = 0x00; 188 xfer_resolution = 0x02; 189 break; 190 191 case SNDRV_PCM_FORMAT_S24_LE: 192 config->data_width = 24; 193 ccr = 0x08; 194 xfer_resolution = 0x04; 195 break; 196 197 case SNDRV_PCM_FORMAT_S32_LE: 198 config->data_width = 32; 199 ccr = 0x10; 200 xfer_resolution = 0x05; 201 break; 202 203 default: 204 dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt"); 205 return -EINVAL; 206 } 207 208 config->chan_nr = params_channels(params); 209 210 switch (config->chan_nr) { 211 case EIGHT_CHANNEL_SUPPORT: 212 ch_reg = 3; 213 case SIX_CHANNEL_SUPPORT: 214 ch_reg = 2; 215 case FOUR_CHANNEL_SUPPORT: 216 ch_reg = 1; 217 case TWO_CHANNEL_SUPPORT: 218 ch_reg = 0; 219 break; 220 default: 221 dev_err(dev->dev, "channel not supported\n"); 222 } 223 224 i2s_disable_channels(dev, substream->stream); 225 226 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 227 i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution); 228 i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02); 229 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); 230 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30); 231 i2s_write_reg(dev->i2s_base, TER(ch_reg), 1); 232 } else { 233 i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution); 234 i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07); 235 irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg)); 236 i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03); 237 i2s_write_reg(dev->i2s_base, RER(ch_reg), 1); 238 } 239 240 i2s_write_reg(dev->i2s_base, CCR, ccr); 241 242 config->sample_rate = params_rate(params); 243 244 if (!dev->i2s_clk_cfg) 245 return -EINVAL; 246 247 ret = dev->i2s_clk_cfg(config); 248 if (ret < 0) { 249 dev_err(dev->dev, "runtime audio clk config fail\n"); 250 return ret; 251 } 252 253 return 0; 254} 255 256static void dw_i2s_shutdown(struct snd_pcm_substream *substream, 257 struct snd_soc_dai *dai) 258{ 259 snd_soc_dai_set_dma_data(dai, substream, NULL); 260} 261 262static int dw_i2s_trigger(struct snd_pcm_substream *substream, 263 int cmd, struct snd_soc_dai *dai) 264{ 265 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); 266 int ret = 0; 267 268 switch (cmd) { 269 case SNDRV_PCM_TRIGGER_START: 270 case SNDRV_PCM_TRIGGER_RESUME: 271 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 272 dev->active++; 273 i2s_start(dev, substream); 274 break; 275 276 case SNDRV_PCM_TRIGGER_STOP: 277 case SNDRV_PCM_TRIGGER_SUSPEND: 278 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 279 dev->active--; 280 i2s_stop(dev, substream); 281 break; 282 default: 283 ret = -EINVAL; 284 break; 285 } 286 return ret; 287} 288 289static struct snd_soc_dai_ops dw_i2s_dai_ops = { 290 .startup = dw_i2s_startup, 291 .shutdown = dw_i2s_shutdown, 292 .hw_params = dw_i2s_hw_params, 293 .trigger = dw_i2s_trigger, 294}; 295 296#ifdef CONFIG_PM 297 298static int dw_i2s_suspend(struct snd_soc_dai *dai) 299{ 300 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); 301 302 clk_disable(dev->clk); 303 return 0; 304} 305 306static int dw_i2s_resume(struct snd_soc_dai *dai) 307{ 308 struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); 309 310 clk_enable(dev->clk); 311 return 0; 312} 313 314#else 315#define dw_i2s_suspend NULL 316#define dw_i2s_resume NULL 317#endif 318 319static int dw_i2s_probe(struct platform_device *pdev) 320{ 321 const struct i2s_platform_data *pdata = pdev->dev.platform_data; 322 struct dw_i2s_dev *dev; 323 struct resource *res; 324 int ret; 325 unsigned int cap; 326 struct snd_soc_dai_driver *dw_i2s_dai; 327 328 if (!pdata) { 329 dev_err(&pdev->dev, "Invalid platform data\n"); 330 return -EINVAL; 331 } 332 333 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 334 if (!res) { 335 dev_err(&pdev->dev, "no i2s resource defined\n"); 336 return -ENODEV; 337 } 338 339 if (!devm_request_mem_region(&pdev->dev, res->start, 340 resource_size(res), pdev->name)) { 341 dev_err(&pdev->dev, "i2s region already claimed\n"); 342 return -EBUSY; 343 } 344 345 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); 346 if (!dev) { 347 dev_warn(&pdev->dev, "kzalloc fail\n"); 348 return -ENOMEM; 349 } 350 351 dev->i2s_base = devm_ioremap(&pdev->dev, res->start, 352 resource_size(res)); 353 if (!dev->i2s_base) { 354 dev_err(&pdev->dev, "ioremap fail for i2s_region\n"); 355 return -ENOMEM; 356 } 357 358 cap = pdata->cap; 359 dev->capability = cap; 360 dev->i2s_clk_cfg = pdata->i2s_clk_cfg; 361 362 /* Set DMA slaves info */ 363 364 dev->play_dma_data.data = pdata->play_dma_data; 365 dev->capture_dma_data.data = pdata->capture_dma_data; 366 dev->play_dma_data.addr = res->start + I2S_TXDMA; 367 dev->capture_dma_data.addr = res->start + I2S_RXDMA; 368 dev->play_dma_data.max_burst = 16; 369 dev->capture_dma_data.max_burst = 16; 370 dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 371 dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 372 dev->play_dma_data.filter = pdata->filter; 373 dev->capture_dma_data.filter = pdata->filter; 374 375 dev->clk = clk_get(&pdev->dev, NULL); 376 if (IS_ERR(dev->clk)) 377 return PTR_ERR(dev->clk); 378 379 ret = clk_enable(dev->clk); 380 if (ret < 0) 381 goto err_clk_put; 382 383 dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL); 384 if (!dw_i2s_dai) { 385 dev_err(&pdev->dev, "mem allocation failed for dai driver\n"); 386 ret = -ENOMEM; 387 goto err_clk_disable; 388 } 389 390 if (cap & DWC_I2S_PLAY) { 391 dev_dbg(&pdev->dev, " SPEAr: play supported\n"); 392 dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM; 393 dw_i2s_dai->playback.channels_max = pdata->channel; 394 dw_i2s_dai->playback.formats = pdata->snd_fmts; 395 dw_i2s_dai->playback.rates = pdata->snd_rates; 396 } 397 398 if (cap & DWC_I2S_RECORD) { 399 dev_dbg(&pdev->dev, "SPEAr: record supported\n"); 400 dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM; 401 dw_i2s_dai->capture.channels_max = pdata->channel; 402 dw_i2s_dai->capture.formats = pdata->snd_fmts; 403 dw_i2s_dai->capture.rates = pdata->snd_rates; 404 } 405 406 dw_i2s_dai->ops = &dw_i2s_dai_ops; 407 dw_i2s_dai->suspend = dw_i2s_suspend; 408 dw_i2s_dai->resume = dw_i2s_resume; 409 410 dev->dev = &pdev->dev; 411 dev_set_drvdata(&pdev->dev, dev); 412 ret = snd_soc_register_dai(&pdev->dev, dw_i2s_dai); 413 if (ret != 0) { 414 dev_err(&pdev->dev, "not able to register dai\n"); 415 goto err_set_drvdata; 416 } 417 418 return 0; 419 420err_set_drvdata: 421 dev_set_drvdata(&pdev->dev, NULL); 422err_clk_disable: 423 clk_disable(dev->clk); 424err_clk_put: 425 clk_put(dev->clk); 426 return ret; 427} 428 429static int dw_i2s_remove(struct platform_device *pdev) 430{ 431 struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); 432 433 snd_soc_unregister_dai(&pdev->dev); 434 dev_set_drvdata(&pdev->dev, NULL); 435 436 clk_put(dev->clk); 437 438 return 0; 439} 440 441static struct platform_driver dw_i2s_driver = { 442 .probe = dw_i2s_probe, 443 .remove = dw_i2s_remove, 444 .driver = { 445 .name = "designware-i2s", 446 .owner = THIS_MODULE, 447 }, 448}; 449 450module_platform_driver(dw_i2s_driver); 451 452MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>"); 453MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface"); 454MODULE_LICENSE("GPL"); 455MODULE_ALIAS("platform:designware_i2s");