1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * tegra20_spdif.c - Tegra20 SPDIF driver 4 * 5 * Author: Stephen Warren <swarren@nvidia.com> 6 * Copyright (C) 2011-2012 - NVIDIA, Inc. 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/device.h> 11 #include <linux/io.h> 12 #include <linux/module.h> 13 #include <linux/platform_device.h> 14 #include <linux/pm_runtime.h> 15 #include <linux/regmap.h> 16 #include <linux/slab.h> 17 #include <sound/core.h> 18 #include <sound/pcm.h> 19 #include <sound/pcm_params.h> 20 #include <sound/soc.h> 21 #include <sound/dmaengine_pcm.h> 22 23 #include "tegra20_spdif.h" 24 25 #define DRV_NAME "tegra20-spdif" 26 27 static int tegra20_spdif_runtime_suspend(struct device *dev) 28 { 29 struct tegra20_spdif *spdif = dev_get_drvdata(dev); 30 31 clk_disable_unprepare(spdif->clk_spdif_out); 32 33 return 0; 34 } 35 36 static int tegra20_spdif_runtime_resume(struct device *dev) 37 { 38 struct tegra20_spdif *spdif = dev_get_drvdata(dev); 39 int ret; 40 41 ret = clk_prepare_enable(spdif->clk_spdif_out); 42 if (ret) { 43 dev_err(dev, "clk_enable failed: %d\n", ret); 44 return ret; 45 } 46 47 return 0; 48 } 49 50 static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, 51 struct snd_pcm_hw_params *params, 52 struct snd_soc_dai *dai) 53 { 54 struct device *dev = dai->dev; 55 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); 56 unsigned int mask = 0, val = 0; 57 int ret, spdifclock; 58 59 mask |= TEGRA20_SPDIF_CTRL_PACK | 60 TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; 61 switch (params_format(params)) { 62 case SNDRV_PCM_FORMAT_S16_LE: 63 val |= TEGRA20_SPDIF_CTRL_PACK | 64 TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT; 65 break; 66 default: 67 return -EINVAL; 68 } 69 70 regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, mask, val); 71 72 switch (params_rate(params)) { 73 case 32000: 74 spdifclock = 4096000; 75 break; 76 case 44100: 77 spdifclock = 5644800; 78 break; 79 case 48000: 80 spdifclock = 6144000; 81 break; 82 case 88200: 83 spdifclock = 11289600; 84 break; 85 case 96000: 86 spdifclock = 12288000; 87 break; 88 case 176400: 89 spdifclock = 22579200; 90 break; 91 case 192000: 92 spdifclock = 24576000; 93 break; 94 default: 95 return -EINVAL; 96 } 97 98 ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); 99 if (ret) { 100 dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); 101 return ret; 102 } 103 104 return 0; 105 } 106 107 static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif) 108 { 109 regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, 110 TEGRA20_SPDIF_CTRL_TX_EN, 111 TEGRA20_SPDIF_CTRL_TX_EN); 112 } 113 114 static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) 115 { 116 regmap_update_bits(spdif->regmap, TEGRA20_SPDIF_CTRL, 117 TEGRA20_SPDIF_CTRL_TX_EN, 0); 118 } 119 120 static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, 121 struct snd_soc_dai *dai) 122 { 123 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); 124 125 switch (cmd) { 126 case SNDRV_PCM_TRIGGER_START: 127 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 128 case SNDRV_PCM_TRIGGER_RESUME: 129 tegra20_spdif_start_playback(spdif); 130 break; 131 case SNDRV_PCM_TRIGGER_STOP: 132 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 133 case SNDRV_PCM_TRIGGER_SUSPEND: 134 tegra20_spdif_stop_playback(spdif); 135 break; 136 default: 137 return -EINVAL; 138 } 139 140 return 0; 141 } 142 143 static int tegra20_spdif_probe(struct snd_soc_dai *dai) 144 { 145 struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); 146 147 dai->capture_dma_data = NULL; 148 dai->playback_dma_data = &spdif->playback_dma_data; 149 150 return 0; 151 } 152 153 static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { 154 .hw_params = tegra20_spdif_hw_params, 155 .trigger = tegra20_spdif_trigger, 156 }; 157 158 static struct snd_soc_dai_driver tegra20_spdif_dai = { 159 .name = DRV_NAME, 160 .probe = tegra20_spdif_probe, 161 .playback = { 162 .stream_name = "Playback", 163 .channels_min = 2, 164 .channels_max = 2, 165 .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 166 SNDRV_PCM_RATE_48000, 167 .formats = SNDRV_PCM_FMTBIT_S16_LE, 168 }, 169 .ops = &tegra20_spdif_dai_ops, 170 }; 171 172 static const struct snd_soc_component_driver tegra20_spdif_component = { 173 .name = DRV_NAME, 174 }; 175 176 static bool tegra20_spdif_wr_rd_reg(struct device *dev, unsigned int reg) 177 { 178 switch (reg) { 179 case TEGRA20_SPDIF_CTRL: 180 case TEGRA20_SPDIF_STATUS: 181 case TEGRA20_SPDIF_STROBE_CTRL: 182 case TEGRA20_SPDIF_DATA_FIFO_CSR: 183 case TEGRA20_SPDIF_DATA_OUT: 184 case TEGRA20_SPDIF_DATA_IN: 185 case TEGRA20_SPDIF_CH_STA_RX_A: 186 case TEGRA20_SPDIF_CH_STA_RX_B: 187 case TEGRA20_SPDIF_CH_STA_RX_C: 188 case TEGRA20_SPDIF_CH_STA_RX_D: 189 case TEGRA20_SPDIF_CH_STA_RX_E: 190 case TEGRA20_SPDIF_CH_STA_RX_F: 191 case TEGRA20_SPDIF_CH_STA_TX_A: 192 case TEGRA20_SPDIF_CH_STA_TX_B: 193 case TEGRA20_SPDIF_CH_STA_TX_C: 194 case TEGRA20_SPDIF_CH_STA_TX_D: 195 case TEGRA20_SPDIF_CH_STA_TX_E: 196 case TEGRA20_SPDIF_CH_STA_TX_F: 197 case TEGRA20_SPDIF_USR_STA_RX_A: 198 case TEGRA20_SPDIF_USR_DAT_TX_A: 199 return true; 200 default: 201 return false; 202 } 203 } 204 205 static bool tegra20_spdif_volatile_reg(struct device *dev, unsigned int reg) 206 { 207 switch (reg) { 208 case TEGRA20_SPDIF_STATUS: 209 case TEGRA20_SPDIF_DATA_FIFO_CSR: 210 case TEGRA20_SPDIF_DATA_OUT: 211 case TEGRA20_SPDIF_DATA_IN: 212 case TEGRA20_SPDIF_CH_STA_RX_A: 213 case TEGRA20_SPDIF_CH_STA_RX_B: 214 case TEGRA20_SPDIF_CH_STA_RX_C: 215 case TEGRA20_SPDIF_CH_STA_RX_D: 216 case TEGRA20_SPDIF_CH_STA_RX_E: 217 case TEGRA20_SPDIF_CH_STA_RX_F: 218 case TEGRA20_SPDIF_USR_STA_RX_A: 219 case TEGRA20_SPDIF_USR_DAT_TX_A: 220 return true; 221 default: 222 return false; 223 } 224 } 225 226 static bool tegra20_spdif_precious_reg(struct device *dev, unsigned int reg) 227 { 228 switch (reg) { 229 case TEGRA20_SPDIF_DATA_OUT: 230 case TEGRA20_SPDIF_DATA_IN: 231 case TEGRA20_SPDIF_USR_STA_RX_A: 232 case TEGRA20_SPDIF_USR_DAT_TX_A: 233 return true; 234 default: 235 return false; 236 } 237 } 238 239 static const struct regmap_config tegra20_spdif_regmap_config = { 240 .reg_bits = 32, 241 .reg_stride = 4, 242 .val_bits = 32, 243 .max_register = TEGRA20_SPDIF_USR_DAT_TX_A, 244 .writeable_reg = tegra20_spdif_wr_rd_reg, 245 .readable_reg = tegra20_spdif_wr_rd_reg, 246 .volatile_reg = tegra20_spdif_volatile_reg, 247 .precious_reg = tegra20_spdif_precious_reg, 248 .cache_type = REGCACHE_FLAT, 249 }; 250 251 static int tegra20_spdif_platform_probe(struct platform_device *pdev) 252 { 253 struct tegra20_spdif *spdif; 254 struct resource *mem, *dmareq; 255 void __iomem *regs; 256 int ret; 257 258 spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif), 259 GFP_KERNEL); 260 if (!spdif) 261 return -ENOMEM; 262 263 dev_set_drvdata(&pdev->dev, spdif); 264 265 spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out"); 266 if (IS_ERR(spdif->clk_spdif_out)) { 267 pr_err("Can't retrieve spdif clock\n"); 268 ret = PTR_ERR(spdif->clk_spdif_out); 269 return ret; 270 } 271 272 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 273 regs = devm_ioremap_resource(&pdev->dev, mem); 274 if (IS_ERR(regs)) 275 return PTR_ERR(regs); 276 277 dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); 278 if (!dmareq) { 279 dev_err(&pdev->dev, "No DMA resource\n"); 280 return -ENODEV; 281 } 282 283 spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs, 284 &tegra20_spdif_regmap_config); 285 if (IS_ERR(spdif->regmap)) { 286 dev_err(&pdev->dev, "regmap init failed\n"); 287 ret = PTR_ERR(spdif->regmap); 288 return ret; 289 } 290 291 spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; 292 spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 293 spdif->playback_dma_data.maxburst = 4; 294 spdif->playback_dma_data.slave_id = dmareq->start; 295 296 pm_runtime_enable(&pdev->dev); 297 if (!pm_runtime_enabled(&pdev->dev)) { 298 ret = tegra20_spdif_runtime_resume(&pdev->dev); 299 if (ret) 300 goto err_pm_disable; 301 } 302 303 ret = snd_soc_register_component(&pdev->dev, &tegra20_spdif_component, 304 &tegra20_spdif_dai, 1); 305 if (ret) { 306 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); 307 ret = -ENOMEM; 308 goto err_suspend; 309 } 310 311 ret = tegra_pcm_platform_register(&pdev->dev); 312 if (ret) { 313 dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); 314 goto err_unregister_component; 315 } 316 317 return 0; 318 319 err_unregister_component: 320 snd_soc_unregister_component(&pdev->dev); 321 err_suspend: 322 if (!pm_runtime_status_suspended(&pdev->dev)) 323 tegra20_spdif_runtime_suspend(&pdev->dev); 324 err_pm_disable: 325 pm_runtime_disable(&pdev->dev); 326 327 return ret; 328 } 329 330 static int tegra20_spdif_platform_remove(struct platform_device *pdev) 331 { 332 pm_runtime_disable(&pdev->dev); 333 if (!pm_runtime_status_suspended(&pdev->dev)) 334 tegra20_spdif_runtime_suspend(&pdev->dev); 335 336 tegra_pcm_platform_unregister(&pdev->dev); 337 snd_soc_unregister_component(&pdev->dev); 338 339 return 0; 340 } 341 342 static const struct dev_pm_ops tegra20_spdif_pm_ops = { 343 SET_RUNTIME_PM_OPS(tegra20_spdif_runtime_suspend, 344 tegra20_spdif_runtime_resume, NULL) 345 }; 346 347 static struct platform_driver tegra20_spdif_driver = { 348 .driver = { 349 .name = DRV_NAME, 350 .pm = &tegra20_spdif_pm_ops, 351 }, 352 .probe = tegra20_spdif_platform_probe, 353 .remove = tegra20_spdif_platform_remove, 354 }; 355 356 module_platform_driver(tegra20_spdif_driver); 357 358 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 359 MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); 360 MODULE_LICENSE("GPL"); 361 MODULE_ALIAS("platform:" DRV_NAME); 362