tegra210_adx.c (15174B)
1// SPDX-License-Identifier: GPL-2.0-only 2// 3// tegra210_adx.c - Tegra210 ADX driver 4// 5// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved. 6 7#include <linux/clk.h> 8#include <linux/device.h> 9#include <linux/io.h> 10#include <linux/module.h> 11#include <linux/of.h> 12#include <linux/of_device.h> 13#include <linux/platform_device.h> 14#include <linux/pm_runtime.h> 15#include <linux/regmap.h> 16#include <sound/core.h> 17#include <sound/pcm.h> 18#include <sound/pcm_params.h> 19#include <sound/soc.h> 20 21#include "tegra210_adx.h" 22#include "tegra_cif.h" 23 24static const struct reg_default tegra210_adx_reg_defaults[] = { 25 { TEGRA210_ADX_RX_INT_MASK, 0x00000001}, 26 { TEGRA210_ADX_RX_CIF_CTRL, 0x00007000}, 27 { TEGRA210_ADX_TX_INT_MASK, 0x0000000f }, 28 { TEGRA210_ADX_TX1_CIF_CTRL, 0x00007000}, 29 { TEGRA210_ADX_TX2_CIF_CTRL, 0x00007000}, 30 { TEGRA210_ADX_TX3_CIF_CTRL, 0x00007000}, 31 { TEGRA210_ADX_TX4_CIF_CTRL, 0x00007000}, 32 { TEGRA210_ADX_CG, 0x1}, 33 { TEGRA210_ADX_CFG_RAM_CTRL, 0x00004000}, 34}; 35 36static void tegra210_adx_write_map_ram(struct tegra210_adx *adx) 37{ 38 int i; 39 40 regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL, 41 TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN | 42 TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN | 43 TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE); 44 45 for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++) 46 regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA, 47 adx->map[i]); 48 49 regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, adx->byte_mask[0]); 50 regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, adx->byte_mask[1]); 51} 52 53static int tegra210_adx_startup(struct snd_pcm_substream *substream, 54 struct snd_soc_dai *dai) 55{ 56 struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); 57 unsigned int val; 58 int err; 59 60 /* Ensure if ADX status is disabled */ 61 err = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS, 62 val, !(val & 0x1), 10, 10000); 63 if (err < 0) { 64 dev_err(dai->dev, "failed to stop ADX, err = %d\n", err); 65 return err; 66 } 67 68 /* 69 * Soft Reset: Below performs module soft reset which clears 70 * all FSM logic, flushes flow control of FIFO and resets the 71 * state register. It also brings module back to disabled 72 * state (without flushing the data in the pipe). 73 */ 74 regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET, 75 TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK, 76 TEGRA210_ADX_SOFT_RESET_SOFT_EN); 77 78 err = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_SOFT_RESET, 79 val, !(val & 0x1), 10, 10000); 80 if (err < 0) { 81 dev_err(dai->dev, "failed to reset ADX, err = %d\n", err); 82 return err; 83 } 84 85 return 0; 86} 87 88static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev) 89{ 90 struct tegra210_adx *adx = dev_get_drvdata(dev); 91 92 regcache_cache_only(adx->regmap, true); 93 regcache_mark_dirty(adx->regmap); 94 95 return 0; 96} 97 98static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev) 99{ 100 struct tegra210_adx *adx = dev_get_drvdata(dev); 101 102 regcache_cache_only(adx->regmap, false); 103 regcache_sync(adx->regmap); 104 105 tegra210_adx_write_map_ram(adx); 106 107 return 0; 108} 109 110static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai, 111 unsigned int channels, 112 unsigned int format, 113 unsigned int reg) 114{ 115 struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai); 116 struct tegra_cif_conf cif_conf; 117 int audio_bits; 118 119 memset(&cif_conf, 0, sizeof(struct tegra_cif_conf)); 120 121 if (channels < 1 || channels > 16) 122 return -EINVAL; 123 124 switch (format) { 125 case SNDRV_PCM_FORMAT_S8: 126 audio_bits = TEGRA_ACIF_BITS_8; 127 break; 128 case SNDRV_PCM_FORMAT_S16_LE: 129 audio_bits = TEGRA_ACIF_BITS_16; 130 break; 131 case SNDRV_PCM_FORMAT_S32_LE: 132 audio_bits = TEGRA_ACIF_BITS_32; 133 break; 134 default: 135 return -EINVAL; 136 } 137 138 cif_conf.audio_ch = channels; 139 cif_conf.client_ch = channels; 140 cif_conf.audio_bits = audio_bits; 141 cif_conf.client_bits = audio_bits; 142 143 tegra_set_cif(adx->regmap, reg, &cif_conf); 144 145 return 0; 146} 147 148static int tegra210_adx_out_hw_params(struct snd_pcm_substream *substream, 149 struct snd_pcm_hw_params *params, 150 struct snd_soc_dai *dai) 151{ 152 return tegra210_adx_set_audio_cif(dai, params_channels(params), 153 params_format(params), 154 TEGRA210_ADX_TX1_CIF_CTRL + ((dai->id - 1) * TEGRA210_ADX_AUDIOCIF_CH_STRIDE)); 155} 156 157static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream, 158 struct snd_pcm_hw_params *params, 159 struct snd_soc_dai *dai) 160{ 161 return tegra210_adx_set_audio_cif(dai, params_channels(params), 162 params_format(params), 163 TEGRA210_ADX_RX_CIF_CTRL); 164} 165 166static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol, 167 struct snd_ctl_elem_value *ucontrol) 168{ 169 struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 170 struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt); 171 struct soc_mixer_control *mc; 172 unsigned char *bytes_map = (unsigned char *)&adx->map; 173 int enabled; 174 175 mc = (struct soc_mixer_control *)kcontrol->private_value; 176 enabled = adx->byte_mask[mc->reg / 32] & (1 << (mc->reg % 32)); 177 178 if (enabled) 179 ucontrol->value.integer.value[0] = bytes_map[mc->reg]; 180 else 181 ucontrol->value.integer.value[0] = 0; 182 183 return 0; 184} 185 186static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol, 187 struct snd_ctl_elem_value *ucontrol) 188{ 189 struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); 190 struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt); 191 unsigned char *bytes_map = (unsigned char *)&adx->map; 192 int value = ucontrol->value.integer.value[0]; 193 struct soc_mixer_control *mc = 194 (struct soc_mixer_control *)kcontrol->private_value;; 195 196 if (value == bytes_map[mc->reg]) 197 return 0; 198 199 if (value >= 0 && value <= 255) { 200 /* update byte map and enable slot */ 201 bytes_map[mc->reg] = value; 202 adx->byte_mask[mc->reg / 32] |= (1 << (mc->reg % 32)); 203 } else { 204 /* reset byte map and disable slot */ 205 bytes_map[mc->reg] = 0; 206 adx->byte_mask[mc->reg / 32] &= ~(1 << (mc->reg % 32)); 207 } 208 209 return 1; 210} 211 212static const struct snd_soc_dai_ops tegra210_adx_in_dai_ops = { 213 .hw_params = tegra210_adx_in_hw_params, 214 .startup = tegra210_adx_startup, 215}; 216 217static const struct snd_soc_dai_ops tegra210_adx_out_dai_ops = { 218 .hw_params = tegra210_adx_out_hw_params, 219}; 220 221#define IN_DAI \ 222 { \ 223 .name = "ADX-RX-CIF", \ 224 .playback = { \ 225 .stream_name = "RX-CIF-Playback", \ 226 .channels_min = 1, \ 227 .channels_max = 16, \ 228 .rates = SNDRV_PCM_RATE_8000_192000, \ 229 .formats = SNDRV_PCM_FMTBIT_S8 | \ 230 SNDRV_PCM_FMTBIT_S16_LE | \ 231 SNDRV_PCM_FMTBIT_S32_LE, \ 232 }, \ 233 .capture = { \ 234 .stream_name = "RX-CIF-Capture", \ 235 .channels_min = 1, \ 236 .channels_max = 16, \ 237 .rates = SNDRV_PCM_RATE_8000_192000, \ 238 .formats = SNDRV_PCM_FMTBIT_S8 | \ 239 SNDRV_PCM_FMTBIT_S16_LE | \ 240 SNDRV_PCM_FMTBIT_S32_LE, \ 241 }, \ 242 .ops = &tegra210_adx_in_dai_ops, \ 243 } 244 245#define OUT_DAI(id) \ 246 { \ 247 .name = "ADX-TX" #id "-CIF", \ 248 .playback = { \ 249 .stream_name = "TX" #id "-CIF-Playback",\ 250 .channels_min = 1, \ 251 .channels_max = 16, \ 252 .rates = SNDRV_PCM_RATE_8000_192000, \ 253 .formats = SNDRV_PCM_FMTBIT_S8 | \ 254 SNDRV_PCM_FMTBIT_S16_LE | \ 255 SNDRV_PCM_FMTBIT_S32_LE, \ 256 }, \ 257 .capture = { \ 258 .stream_name = "TX" #id "-CIF-Capture", \ 259 .channels_min = 1, \ 260 .channels_max = 16, \ 261 .rates = SNDRV_PCM_RATE_8000_192000, \ 262 .formats = SNDRV_PCM_FMTBIT_S8 | \ 263 SNDRV_PCM_FMTBIT_S16_LE | \ 264 SNDRV_PCM_FMTBIT_S32_LE, \ 265 }, \ 266 .ops = &tegra210_adx_out_dai_ops, \ 267 } 268 269static struct snd_soc_dai_driver tegra210_adx_dais[] = { 270 IN_DAI, 271 OUT_DAI(1), 272 OUT_DAI(2), 273 OUT_DAI(3), 274 OUT_DAI(4), 275}; 276 277static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = { 278 SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA210_ADX_ENABLE, 279 TEGRA210_ADX_ENABLE_SHIFT, 0), 280 SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0), 281 SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0), 282 SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0), 283 SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0), 284}; 285 286#define STREAM_ROUTES(id, sname) \ 287 { "XBAR-" sname, NULL, "XBAR-TX" }, \ 288 { "RX-CIF-" sname, NULL, "XBAR-" sname }, \ 289 { "RX", NULL, "RX-CIF-" sname }, \ 290 { "TX" #id, NULL, "RX" }, \ 291 { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ 292 { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ 293 { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } 294 295#define ADX_ROUTES(id) \ 296 STREAM_ROUTES(id, "Playback"), \ 297 STREAM_ROUTES(id, "Capture") 298 299#define STREAM_ROUTES(id, sname) \ 300 { "XBAR-" sname, NULL, "XBAR-TX" }, \ 301 { "RX-CIF-" sname, NULL, "XBAR-" sname }, \ 302 { "RX", NULL, "RX-CIF-" sname }, \ 303 { "TX" #id, NULL, "RX" }, \ 304 { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \ 305 { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \ 306 { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname } 307 308#define ADX_ROUTES(id) \ 309 STREAM_ROUTES(id, "Playback"), \ 310 STREAM_ROUTES(id, "Capture") 311 312static const struct snd_soc_dapm_route tegra210_adx_routes[] = { 313 ADX_ROUTES(1), 314 ADX_ROUTES(2), 315 ADX_ROUTES(3), 316 ADX_ROUTES(4), 317}; 318 319#define TEGRA210_ADX_BYTE_MAP_CTRL(reg) \ 320 SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \ 321 tegra210_adx_get_byte_map, \ 322 tegra210_adx_put_byte_map) 323 324static struct snd_kcontrol_new tegra210_adx_controls[] = { 325 TEGRA210_ADX_BYTE_MAP_CTRL(0), 326 TEGRA210_ADX_BYTE_MAP_CTRL(1), 327 TEGRA210_ADX_BYTE_MAP_CTRL(2), 328 TEGRA210_ADX_BYTE_MAP_CTRL(3), 329 TEGRA210_ADX_BYTE_MAP_CTRL(4), 330 TEGRA210_ADX_BYTE_MAP_CTRL(5), 331 TEGRA210_ADX_BYTE_MAP_CTRL(6), 332 TEGRA210_ADX_BYTE_MAP_CTRL(7), 333 TEGRA210_ADX_BYTE_MAP_CTRL(8), 334 TEGRA210_ADX_BYTE_MAP_CTRL(9), 335 TEGRA210_ADX_BYTE_MAP_CTRL(10), 336 TEGRA210_ADX_BYTE_MAP_CTRL(11), 337 TEGRA210_ADX_BYTE_MAP_CTRL(12), 338 TEGRA210_ADX_BYTE_MAP_CTRL(13), 339 TEGRA210_ADX_BYTE_MAP_CTRL(14), 340 TEGRA210_ADX_BYTE_MAP_CTRL(15), 341 TEGRA210_ADX_BYTE_MAP_CTRL(16), 342 TEGRA210_ADX_BYTE_MAP_CTRL(17), 343 TEGRA210_ADX_BYTE_MAP_CTRL(18), 344 TEGRA210_ADX_BYTE_MAP_CTRL(19), 345 TEGRA210_ADX_BYTE_MAP_CTRL(20), 346 TEGRA210_ADX_BYTE_MAP_CTRL(21), 347 TEGRA210_ADX_BYTE_MAP_CTRL(22), 348 TEGRA210_ADX_BYTE_MAP_CTRL(23), 349 TEGRA210_ADX_BYTE_MAP_CTRL(24), 350 TEGRA210_ADX_BYTE_MAP_CTRL(25), 351 TEGRA210_ADX_BYTE_MAP_CTRL(26), 352 TEGRA210_ADX_BYTE_MAP_CTRL(27), 353 TEGRA210_ADX_BYTE_MAP_CTRL(28), 354 TEGRA210_ADX_BYTE_MAP_CTRL(29), 355 TEGRA210_ADX_BYTE_MAP_CTRL(30), 356 TEGRA210_ADX_BYTE_MAP_CTRL(31), 357 TEGRA210_ADX_BYTE_MAP_CTRL(32), 358 TEGRA210_ADX_BYTE_MAP_CTRL(33), 359 TEGRA210_ADX_BYTE_MAP_CTRL(34), 360 TEGRA210_ADX_BYTE_MAP_CTRL(35), 361 TEGRA210_ADX_BYTE_MAP_CTRL(36), 362 TEGRA210_ADX_BYTE_MAP_CTRL(37), 363 TEGRA210_ADX_BYTE_MAP_CTRL(38), 364 TEGRA210_ADX_BYTE_MAP_CTRL(39), 365 TEGRA210_ADX_BYTE_MAP_CTRL(40), 366 TEGRA210_ADX_BYTE_MAP_CTRL(41), 367 TEGRA210_ADX_BYTE_MAP_CTRL(42), 368 TEGRA210_ADX_BYTE_MAP_CTRL(43), 369 TEGRA210_ADX_BYTE_MAP_CTRL(44), 370 TEGRA210_ADX_BYTE_MAP_CTRL(45), 371 TEGRA210_ADX_BYTE_MAP_CTRL(46), 372 TEGRA210_ADX_BYTE_MAP_CTRL(47), 373 TEGRA210_ADX_BYTE_MAP_CTRL(48), 374 TEGRA210_ADX_BYTE_MAP_CTRL(49), 375 TEGRA210_ADX_BYTE_MAP_CTRL(50), 376 TEGRA210_ADX_BYTE_MAP_CTRL(51), 377 TEGRA210_ADX_BYTE_MAP_CTRL(52), 378 TEGRA210_ADX_BYTE_MAP_CTRL(53), 379 TEGRA210_ADX_BYTE_MAP_CTRL(54), 380 TEGRA210_ADX_BYTE_MAP_CTRL(55), 381 TEGRA210_ADX_BYTE_MAP_CTRL(56), 382 TEGRA210_ADX_BYTE_MAP_CTRL(57), 383 TEGRA210_ADX_BYTE_MAP_CTRL(58), 384 TEGRA210_ADX_BYTE_MAP_CTRL(59), 385 TEGRA210_ADX_BYTE_MAP_CTRL(60), 386 TEGRA210_ADX_BYTE_MAP_CTRL(61), 387 TEGRA210_ADX_BYTE_MAP_CTRL(62), 388 TEGRA210_ADX_BYTE_MAP_CTRL(63), 389}; 390 391static const struct snd_soc_component_driver tegra210_adx_cmpnt = { 392 .dapm_widgets = tegra210_adx_widgets, 393 .num_dapm_widgets = ARRAY_SIZE(tegra210_adx_widgets), 394 .dapm_routes = tegra210_adx_routes, 395 .num_dapm_routes = ARRAY_SIZE(tegra210_adx_routes), 396 .controls = tegra210_adx_controls, 397 .num_controls = ARRAY_SIZE(tegra210_adx_controls), 398}; 399 400static bool tegra210_adx_wr_reg(struct device *dev, 401 unsigned int reg) 402{ 403 switch (reg) { 404 case TEGRA210_ADX_TX_INT_MASK ... TEGRA210_ADX_TX4_CIF_CTRL: 405 case TEGRA210_ADX_RX_INT_MASK ... TEGRA210_ADX_RX_CIF_CTRL: 406 case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_CG: 407 case TEGRA210_ADX_CTRL ... TEGRA210_ADX_IN_BYTE_EN1: 408 case TEGRA210_ADX_CFG_RAM_CTRL ... TEGRA210_ADX_CFG_RAM_DATA: 409 return true; 410 default: 411 return false; 412 } 413} 414 415static bool tegra210_adx_rd_reg(struct device *dev, 416 unsigned int reg) 417{ 418 switch (reg) { 419 case TEGRA210_ADX_RX_STATUS ... TEGRA210_ADX_CFG_RAM_DATA: 420 return true; 421 default: 422 return false; 423 } 424} 425 426static bool tegra210_adx_volatile_reg(struct device *dev, 427 unsigned int reg) 428{ 429 switch (reg) { 430 case TEGRA210_ADX_RX_STATUS: 431 case TEGRA210_ADX_RX_INT_STATUS: 432 case TEGRA210_ADX_RX_INT_SET: 433 case TEGRA210_ADX_TX_STATUS: 434 case TEGRA210_ADX_TX_INT_STATUS: 435 case TEGRA210_ADX_TX_INT_SET: 436 case TEGRA210_ADX_SOFT_RESET: 437 case TEGRA210_ADX_STATUS: 438 case TEGRA210_ADX_INT_STATUS: 439 case TEGRA210_ADX_CFG_RAM_CTRL: 440 case TEGRA210_ADX_CFG_RAM_DATA: 441 return true; 442 default: 443 break; 444 } 445 446 return false; 447} 448 449static const struct regmap_config tegra210_adx_regmap_config = { 450 .reg_bits = 32, 451 .reg_stride = 4, 452 .val_bits = 32, 453 .max_register = TEGRA210_ADX_CFG_RAM_DATA, 454 .writeable_reg = tegra210_adx_wr_reg, 455 .readable_reg = tegra210_adx_rd_reg, 456 .volatile_reg = tegra210_adx_volatile_reg, 457 .reg_defaults = tegra210_adx_reg_defaults, 458 .num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults), 459 .cache_type = REGCACHE_FLAT, 460}; 461 462static const struct of_device_id tegra210_adx_of_match[] = { 463 { .compatible = "nvidia,tegra210-adx" }, 464 {}, 465}; 466MODULE_DEVICE_TABLE(of, tegra210_adx_of_match); 467 468static int tegra210_adx_platform_probe(struct platform_device *pdev) 469{ 470 struct device *dev = &pdev->dev; 471 struct tegra210_adx *adx; 472 void __iomem *regs; 473 int err; 474 475 adx = devm_kzalloc(dev, sizeof(*adx), GFP_KERNEL); 476 if (!adx) 477 return -ENOMEM; 478 479 dev_set_drvdata(dev, adx); 480 481 regs = devm_platform_ioremap_resource(pdev, 0); 482 if (IS_ERR(regs)) 483 return PTR_ERR(regs); 484 485 adx->regmap = devm_regmap_init_mmio(dev, regs, 486 &tegra210_adx_regmap_config); 487 if (IS_ERR(adx->regmap)) { 488 dev_err(dev, "regmap init failed\n"); 489 return PTR_ERR(adx->regmap); 490 } 491 492 regcache_cache_only(adx->regmap, true); 493 494 err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt, 495 tegra210_adx_dais, 496 ARRAY_SIZE(tegra210_adx_dais)); 497 if (err) { 498 dev_err(dev, "can't register ADX component, err: %d\n", err); 499 return err; 500 } 501 502 pm_runtime_enable(dev); 503 504 return 0; 505} 506 507static int tegra210_adx_platform_remove(struct platform_device *pdev) 508{ 509 pm_runtime_disable(&pdev->dev); 510 511 return 0; 512} 513 514static const struct dev_pm_ops tegra210_adx_pm_ops = { 515 SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend, 516 tegra210_adx_runtime_resume, NULL) 517 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 518 pm_runtime_force_resume) 519}; 520 521static struct platform_driver tegra210_adx_driver = { 522 .driver = { 523 .name = "tegra210-adx", 524 .of_match_table = tegra210_adx_of_match, 525 .pm = &tegra210_adx_pm_ops, 526 }, 527 .probe = tegra210_adx_platform_probe, 528 .remove = tegra210_adx_platform_remove, 529}; 530module_platform_driver(tegra210_adx_driver); 531 532MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>"); 533MODULE_DESCRIPTION("Tegra210 ADX ASoC driver"); 534MODULE_LICENSE("GPL v2");