1 /* 2 * tegra20_i2s.c - Tegra20 I2S driver 3 * 4 * Author: Stephen Warren <swarren@nvidia.com> 5 * Copyright (C) 2010,2012 - NVIDIA, Inc. 6 * 7 * Based on code copyright/by: 8 * 9 * Copyright (c) 2009-2010, NVIDIA Corporation. 10 * Scott Peterson <speterson@nvidia.com> 11 * 12 * Copyright (C) 2010 Google, Inc. 13 * Iliyan Malchev <malchev@google.com> 14 * 15 * This program is free software; you can redistribute it and/or 16 * modify it under the terms of the GNU General Public License 17 * version 2 as published by the Free Software Foundation. 18 * 19 * This program is distributed in the hope that it will be useful, but 20 * WITHOUT ANY WARRANTY; without even the implied warranty of 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 * General Public License for more details. 23 * 24 * You should have received a copy of the GNU General Public License 25 * along with this program; if not, write to the Free Software 26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 27 * 02110-1301 USA 28 * 29 */ 30 31 #include <linux/clk.h> 32 #include <linux/device.h> 33 #include <linux/io.h> 34 #include <linux/module.h> 35 #include <linux/of.h> 36 #include <linux/platform_device.h> 37 #include <linux/pm_runtime.h> 38 #include <linux/regmap.h> 39 #include <linux/slab.h> 40 #include <sound/core.h> 41 #include <sound/pcm.h> 42 #include <sound/pcm_params.h> 43 #include <sound/soc.h> 44 #include <sound/dmaengine_pcm.h> 45 46 #include "tegra20_i2s.h" 47 48 #define DRV_NAME "tegra20-i2s" 49 50 static int tegra20_i2s_runtime_suspend(struct device *dev) 51 { 52 struct tegra20_i2s *i2s = dev_get_drvdata(dev); 53 54 clk_disable_unprepare(i2s->clk_i2s); 55 56 return 0; 57 } 58 59 static int tegra20_i2s_runtime_resume(struct device *dev) 60 { 61 struct tegra20_i2s *i2s = dev_get_drvdata(dev); 62 int ret; 63 64 ret = clk_prepare_enable(i2s->clk_i2s); 65 if (ret) { 66 dev_err(dev, "clk_enable failed: %d\n", ret); 67 return ret; 68 } 69 70 return 0; 71 } 72 73 static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai, 74 unsigned int fmt) 75 { 76 struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); 77 unsigned int mask = 0, val = 0; 78 79 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 80 case SND_SOC_DAIFMT_NB_NF: 81 break; 82 default: 83 return -EINVAL; 84 } 85 86 mask |= TEGRA20_I2S_CTRL_MASTER_ENABLE; 87 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 88 case SND_SOC_DAIFMT_CBS_CFS: 89 val |= TEGRA20_I2S_CTRL_MASTER_ENABLE; 90 break; 91 case SND_SOC_DAIFMT_CBM_CFM: 92 break; 93 default: 94 return -EINVAL; 95 } 96 97 mask |= TEGRA20_I2S_CTRL_BIT_FORMAT_MASK | 98 TEGRA20_I2S_CTRL_LRCK_MASK; 99 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 100 case SND_SOC_DAIFMT_DSP_A: 101 val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP; 102 val |= TEGRA20_I2S_CTRL_LRCK_L_LOW; 103 break; 104 case SND_SOC_DAIFMT_DSP_B: 105 val |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP; 106 val |= TEGRA20_I2S_CTRL_LRCK_R_LOW; 107 break; 108 case SND_SOC_DAIFMT_I2S: 109 val |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S; 110 val |= TEGRA20_I2S_CTRL_LRCK_L_LOW; 111 break; 112 case SND_SOC_DAIFMT_RIGHT_J: 113 val |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM; 114 val |= TEGRA20_I2S_CTRL_LRCK_L_LOW; 115 break; 116 case SND_SOC_DAIFMT_LEFT_J: 117 val |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM; 118 val |= TEGRA20_I2S_CTRL_LRCK_L_LOW; 119 break; 120 default: 121 return -EINVAL; 122 } 123 124 regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, mask, val); 125 126 return 0; 127 } 128 129 static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream, 130 struct snd_pcm_hw_params *params, 131 struct snd_soc_dai *dai) 132 { 133 struct device *dev = dai->dev; 134 struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); 135 unsigned int mask, val; 136 int ret, sample_size, srate, i2sclock, bitcnt; 137 138 mask = TEGRA20_I2S_CTRL_BIT_SIZE_MASK; 139 switch (params_format(params)) { 140 case SNDRV_PCM_FORMAT_S16_LE: 141 val = TEGRA20_I2S_CTRL_BIT_SIZE_16; 142 sample_size = 16; 143 break; 144 case SNDRV_PCM_FORMAT_S24_LE: 145 val = TEGRA20_I2S_CTRL_BIT_SIZE_24; 146 sample_size = 24; 147 break; 148 case SNDRV_PCM_FORMAT_S32_LE: 149 val = TEGRA20_I2S_CTRL_BIT_SIZE_32; 150 sample_size = 32; 151 break; 152 default: 153 return -EINVAL; 154 } 155 156 mask |= TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK; 157 val |= TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED; 158 159 regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, mask, val); 160 161 srate = params_rate(params); 162 163 /* Final "* 2" required by Tegra hardware */ 164 i2sclock = srate * params_channels(params) * sample_size * 2; 165 166 ret = clk_set_rate(i2s->clk_i2s, i2sclock); 167 if (ret) { 168 dev_err(dev, "Can't set I2S clock rate: %d\n", ret); 169 return ret; 170 } 171 172 bitcnt = (i2sclock / (2 * srate)) - 1; 173 if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) 174 return -EINVAL; 175 val = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; 176 177 if (i2sclock % (2 * srate)) 178 val |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE; 179 180 regmap_write(i2s->regmap, TEGRA20_I2S_TIMING, val); 181 182 regmap_write(i2s->regmap, TEGRA20_I2S_FIFO_SCR, 183 TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | 184 TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); 185 186 return 0; 187 } 188 189 static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s) 190 { 191 regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, 192 TEGRA20_I2S_CTRL_FIFO1_ENABLE, 193 TEGRA20_I2S_CTRL_FIFO1_ENABLE); 194 } 195 196 static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s) 197 { 198 regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, 199 TEGRA20_I2S_CTRL_FIFO1_ENABLE, 0); 200 } 201 202 static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s) 203 { 204 regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, 205 TEGRA20_I2S_CTRL_FIFO2_ENABLE, 206 TEGRA20_I2S_CTRL_FIFO2_ENABLE); 207 } 208 209 static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s) 210 { 211 regmap_update_bits(i2s->regmap, TEGRA20_I2S_CTRL, 212 TEGRA20_I2S_CTRL_FIFO2_ENABLE, 0); 213 } 214 215 static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd, 216 struct snd_soc_dai *dai) 217 { 218 struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); 219 220 switch (cmd) { 221 case SNDRV_PCM_TRIGGER_START: 222 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 223 case SNDRV_PCM_TRIGGER_RESUME: 224 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 225 tegra20_i2s_start_playback(i2s); 226 else 227 tegra20_i2s_start_capture(i2s); 228 break; 229 case SNDRV_PCM_TRIGGER_STOP: 230 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 231 case SNDRV_PCM_TRIGGER_SUSPEND: 232 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 233 tegra20_i2s_stop_playback(i2s); 234 else 235 tegra20_i2s_stop_capture(i2s); 236 break; 237 default: 238 return -EINVAL; 239 } 240 241 return 0; 242 } 243 244 static int tegra20_i2s_probe(struct snd_soc_dai *dai) 245 { 246 struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); 247 248 dai->capture_dma_data = &i2s->capture_dma_data; 249 dai->playback_dma_data = &i2s->playback_dma_data; 250 251 return 0; 252 } 253 254 static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { 255 .set_fmt = tegra20_i2s_set_fmt, 256 .hw_params = tegra20_i2s_hw_params, 257 .trigger = tegra20_i2s_trigger, 258 }; 259 260 static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { 261 .probe = tegra20_i2s_probe, 262 .playback = { 263 .stream_name = "Playback", 264 .channels_min = 2, 265 .channels_max = 2, 266 .rates = SNDRV_PCM_RATE_8000_96000, 267 .formats = SNDRV_PCM_FMTBIT_S16_LE, 268 }, 269 .capture = { 270 .stream_name = "Capture", 271 .channels_min = 2, 272 .channels_max = 2, 273 .rates = SNDRV_PCM_RATE_8000_96000, 274 .formats = SNDRV_PCM_FMTBIT_S16_LE, 275 }, 276 .ops = &tegra20_i2s_dai_ops, 277 .symmetric_rates = 1, 278 }; 279 280 static const struct snd_soc_component_driver tegra20_i2s_component = { 281 .name = DRV_NAME, 282 }; 283 284 static bool tegra20_i2s_wr_rd_reg(struct device *dev, unsigned int reg) 285 { 286 switch (reg) { 287 case TEGRA20_I2S_CTRL: 288 case TEGRA20_I2S_STATUS: 289 case TEGRA20_I2S_TIMING: 290 case TEGRA20_I2S_FIFO_SCR: 291 case TEGRA20_I2S_PCM_CTRL: 292 case TEGRA20_I2S_NW_CTRL: 293 case TEGRA20_I2S_TDM_CTRL: 294 case TEGRA20_I2S_TDM_TX_RX_CTRL: 295 case TEGRA20_I2S_FIFO1: 296 case TEGRA20_I2S_FIFO2: 297 return true; 298 default: 299 return false; 300 } 301 } 302 303 static bool tegra20_i2s_volatile_reg(struct device *dev, unsigned int reg) 304 { 305 switch (reg) { 306 case TEGRA20_I2S_STATUS: 307 case TEGRA20_I2S_FIFO_SCR: 308 case TEGRA20_I2S_FIFO1: 309 case TEGRA20_I2S_FIFO2: 310 return true; 311 default: 312 return false; 313 } 314 } 315 316 static bool tegra20_i2s_precious_reg(struct device *dev, unsigned int reg) 317 { 318 switch (reg) { 319 case TEGRA20_I2S_FIFO1: 320 case TEGRA20_I2S_FIFO2: 321 return true; 322 default: 323 return false; 324 } 325 } 326 327 static const struct regmap_config tegra20_i2s_regmap_config = { 328 .reg_bits = 32, 329 .reg_stride = 4, 330 .val_bits = 32, 331 .max_register = TEGRA20_I2S_FIFO2, 332 .writeable_reg = tegra20_i2s_wr_rd_reg, 333 .readable_reg = tegra20_i2s_wr_rd_reg, 334 .volatile_reg = tegra20_i2s_volatile_reg, 335 .precious_reg = tegra20_i2s_precious_reg, 336 .cache_type = REGCACHE_FLAT, 337 }; 338 339 static int tegra20_i2s_platform_probe(struct platform_device *pdev) 340 { 341 struct tegra20_i2s *i2s; 342 struct resource *mem; 343 void __iomem *regs; 344 int ret; 345 346 i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL); 347 if (!i2s) { 348 ret = -ENOMEM; 349 goto err; 350 } 351 dev_set_drvdata(&pdev->dev, i2s); 352 353 i2s->dai = tegra20_i2s_dai_template; 354 i2s->dai.name = dev_name(&pdev->dev); 355 356 i2s->clk_i2s = clk_get(&pdev->dev, NULL); 357 if (IS_ERR(i2s->clk_i2s)) { 358 dev_err(&pdev->dev, "Can't retrieve i2s clock\n"); 359 ret = PTR_ERR(i2s->clk_i2s); 360 goto err; 361 } 362 363 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 364 regs = devm_ioremap_resource(&pdev->dev, mem); 365 if (IS_ERR(regs)) { 366 ret = PTR_ERR(regs); 367 goto err_clk_put; 368 } 369 370 i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 371 &tegra20_i2s_regmap_config); 372 if (IS_ERR(i2s->regmap)) { 373 dev_err(&pdev->dev, "regmap init failed\n"); 374 ret = PTR_ERR(i2s->regmap); 375 goto err_clk_put; 376 } 377 378 i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2; 379 i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 380 i2s->capture_dma_data.maxburst = 4; 381 382 i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1; 383 i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 384 i2s->playback_dma_data.maxburst = 4; 385 386 pm_runtime_enable(&pdev->dev); 387 if (!pm_runtime_enabled(&pdev->dev)) { 388 ret = tegra20_i2s_runtime_resume(&pdev->dev); 389 if (ret) 390 goto err_pm_disable; 391 } 392 393 ret = snd_soc_register_component(&pdev->dev, &tegra20_i2s_component, 394 &i2s->dai, 1); 395 if (ret) { 396 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); 397 ret = -ENOMEM; 398 goto err_suspend; 399 } 400 401 ret = tegra_pcm_platform_register(&pdev->dev); 402 if (ret) { 403 dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); 404 goto err_unregister_component; 405 } 406 407 return 0; 408 409 err_unregister_component: 410 snd_soc_unregister_component(&pdev->dev); 411 err_suspend: 412 if (!pm_runtime_status_suspended(&pdev->dev)) 413 tegra20_i2s_runtime_suspend(&pdev->dev); 414 err_pm_disable: 415 pm_runtime_disable(&pdev->dev); 416 err_clk_put: 417 clk_put(i2s->clk_i2s); 418 err: 419 return ret; 420 } 421 422 static int tegra20_i2s_platform_remove(struct platform_device *pdev) 423 { 424 struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev); 425 426 pm_runtime_disable(&pdev->dev); 427 if (!pm_runtime_status_suspended(&pdev->dev)) 428 tegra20_i2s_runtime_suspend(&pdev->dev); 429 430 tegra_pcm_platform_unregister(&pdev->dev); 431 snd_soc_unregister_component(&pdev->dev); 432 433 clk_put(i2s->clk_i2s); 434 435 return 0; 436 } 437 438 static const struct of_device_id tegra20_i2s_of_match[] = { 439 { .compatible = "nvidia,tegra20-i2s", }, 440 {}, 441 }; 442 443 static const struct dev_pm_ops tegra20_i2s_pm_ops = { 444 SET_RUNTIME_PM_OPS(tegra20_i2s_runtime_suspend, 445 tegra20_i2s_runtime_resume, NULL) 446 }; 447 448 static struct platform_driver tegra20_i2s_driver = { 449 .driver = { 450 .name = DRV_NAME, 451 .of_match_table = tegra20_i2s_of_match, 452 .pm = &tegra20_i2s_pm_ops, 453 }, 454 .probe = tegra20_i2s_platform_probe, 455 .remove = tegra20_i2s_platform_remove, 456 }; 457 module_platform_driver(tegra20_i2s_driver); 458 459 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 460 MODULE_DESCRIPTION("Tegra20 I2S ASoC driver"); 461 MODULE_LICENSE("GPL"); 462 MODULE_ALIAS("platform:" DRV_NAME); 463 MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match); 464