OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * tegra_spdif.c - Tegra SPDIF driver |
| 3 * |
| 4 * Author: Stephen Warren <swarren@nvidia.com> |
| 5 * Copyright (C) 2011 - NVIDIA, Inc. |
| 6 * |
| 7 * This program is free software; you can redistribute it and/or |
| 8 * modify it under the terms of the GNU General Public License |
| 9 * version 2 as published by the Free Software Foundation. |
| 10 * |
| 11 * This program is distributed in the hope that it will be useful, but |
| 12 * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 * General Public License for more details. |
| 15 * |
| 16 * You should have received a copy of the GNU General Public License |
| 17 * along with this program; if not, write to the Free Software |
| 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
| 19 * 02110-1301 USA |
| 20 * |
| 21 */ |
| 22 |
| 23 #include <linux/clk.h> |
| 24 #include <linux/module.h> |
| 25 #include <linux/debugfs.h> |
| 26 #include <linux/device.h> |
| 27 #include <linux/platform_device.h> |
| 28 #include <linux/seq_file.h> |
| 29 #include <linux/slab.h> |
| 30 #include <linux/io.h> |
| 31 #include <mach/iomap.h> |
| 32 #include <mach/dc.h> |
| 33 #include <sound/core.h> |
| 34 #include <sound/pcm.h> |
| 35 #include <sound/pcm_params.h> |
| 36 #include <sound/soc.h> |
| 37 |
| 38 #include "tegra_spdif.h" |
| 39 |
| 40 #define DRV_NAME "tegra-spdif" |
| 41 |
| 42 static inline void tegra_spdif_write(struct tegra_spdif *spdif, u32 reg, |
| 43 u32 val) |
| 44 { |
| 45 __raw_writel(val, spdif->regs + reg); |
| 46 } |
| 47 |
| 48 static inline u32 tegra_spdif_read(struct tegra_spdif *spdif, u32 reg) |
| 49 { |
| 50 return __raw_readl(spdif->regs + reg); |
| 51 } |
| 52 |
| 53 #ifdef CONFIG_DEBUG_FS |
| 54 static int tegra_spdif_show(struct seq_file *s, void *unused) |
| 55 { |
| 56 #define REG(r) { r, #r } |
| 57 static const struct { |
| 58 int offset; |
| 59 const char *name; |
| 60 } regs[] = { |
| 61 REG(TEGRA_SPDIF_CTRL), |
| 62 REG(TEGRA_SPDIF_STATUS), |
| 63 REG(TEGRA_SPDIF_STROBE_CTRL), |
| 64 REG(TEGRA_SPDIF_DATA_FIFO_CSR), |
| 65 REG(TEGRA_SPDIF_CH_STA_RX_A), |
| 66 REG(TEGRA_SPDIF_CH_STA_RX_B), |
| 67 REG(TEGRA_SPDIF_CH_STA_RX_C), |
| 68 REG(TEGRA_SPDIF_CH_STA_RX_D), |
| 69 REG(TEGRA_SPDIF_CH_STA_RX_E), |
| 70 REG(TEGRA_SPDIF_CH_STA_RX_F), |
| 71 REG(TEGRA_SPDIF_CH_STA_TX_A), |
| 72 REG(TEGRA_SPDIF_CH_STA_TX_B), |
| 73 REG(TEGRA_SPDIF_CH_STA_TX_C), |
| 74 REG(TEGRA_SPDIF_CH_STA_TX_D), |
| 75 REG(TEGRA_SPDIF_CH_STA_TX_E), |
| 76 REG(TEGRA_SPDIF_CH_STA_TX_F), |
| 77 }; |
| 78 #undef REG |
| 79 |
| 80 struct tegra_spdif *spdif = s->private; |
| 81 int i; |
| 82 |
| 83 for (i = 0; i < ARRAY_SIZE(regs); i++) { |
| 84 u32 val = tegra_spdif_read(spdif, regs[i].offset); |
| 85 seq_printf(s, "%s = %08x\n", regs[i].name, val); |
| 86 } |
| 87 |
| 88 return 0; |
| 89 } |
| 90 |
| 91 static int tegra_spdif_debug_open(struct inode *inode, struct file *file) |
| 92 { |
| 93 return single_open(file, tegra_spdif_show, inode->i_private); |
| 94 } |
| 95 |
| 96 static const struct file_operations tegra_spdif_debug_fops = { |
| 97 .open = tegra_spdif_debug_open, |
| 98 .read = seq_read, |
| 99 .llseek = seq_lseek, |
| 100 .release = single_release, |
| 101 }; |
| 102 |
| 103 static void tegra_spdif_debug_add(struct tegra_spdif *spdif, int id) |
| 104 { |
| 105 spdif->debug = debugfs_create_file(DRV_NAME, S_IRUGO, |
| 106 snd_soc_debugfs_root, spdif, |
| 107 &tegra_spdif_debug_fops); |
| 108 } |
| 109 |
| 110 static void tegra_spdif_debug_remove(struct tegra_spdif *spdif) |
| 111 { |
| 112 if (spdif->debug) |
| 113 debugfs_remove(spdif->debug); |
| 114 } |
| 115 #else |
| 116 static inline void tegra_spdif_debug_add(struct tegra_spdif *spdif) |
| 117 { |
| 118 } |
| 119 |
| 120 static inline void tegra_spdif_debug_remove(struct tegra_spdif *spdif) |
| 121 { |
| 122 } |
| 123 #endif |
| 124 |
| 125 static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, |
| 126 struct snd_pcm_hw_params *params, |
| 127 struct snd_soc_dai *dai) |
| 128 { |
| 129 struct device *dev = substream->pcm->card->dev; |
| 130 struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); |
| 131 int ret, srate, spdifclock; |
| 132 |
| 133 spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_PACK; |
| 134 spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_BIT_MODE_MASK; |
| 135 switch (params_format(params)) { |
| 136 case SNDRV_PCM_FORMAT_S16_LE: |
| 137 spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_PACK; |
| 138 spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_BIT_MODE_16BIT; |
| 139 break; |
| 140 default: |
| 141 return -EINVAL; |
| 142 } |
| 143 |
| 144 srate = params_rate(params); |
| 145 switch (params_rate(params)) { |
| 146 case 32000: |
| 147 spdifclock = 4096000; |
| 148 break; |
| 149 case 44100: |
| 150 spdifclock = 5644800; |
| 151 break; |
| 152 case 48000: |
| 153 spdifclock = 6144000; |
| 154 break; |
| 155 case 88200: |
| 156 spdifclock = 11289600; |
| 157 break; |
| 158 case 96000: |
| 159 spdifclock = 12288000; |
| 160 break; |
| 161 case 176400: |
| 162 spdifclock = 22579200; |
| 163 break; |
| 164 case 192000: |
| 165 spdifclock = 24576000; |
| 166 break; |
| 167 default: |
| 168 return -EINVAL; |
| 169 } |
| 170 |
| 171 ret = clk_set_rate(spdif->clk_spdif_out, spdifclock); |
| 172 if (ret) { |
| 173 dev_err(dev, "Can't set SPDIF clock rate: %d\n", ret); |
| 174 return ret; |
| 175 } |
| 176 |
| 177 /* FIXME: Should this be in a codec driver? */ |
| 178 ret = tegra_dc_hdmi_set_audio_sample_rate(srate); |
| 179 if (ret) { |
| 180 dev_err(dev, "Can't configure HDMI controller: %d\n", ret); |
| 181 return ret; |
| 182 } |
| 183 |
| 184 return 0; |
| 185 } |
| 186 |
| 187 static void tegra_spdif_start_playback(struct tegra_spdif *spdif) |
| 188 { |
| 189 spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_TX_EN; |
| 190 tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl); |
| 191 } |
| 192 |
| 193 static void tegra_spdif_stop_playback(struct tegra_spdif *spdif) |
| 194 { |
| 195 spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_TX_EN; |
| 196 tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl); |
| 197 } |
| 198 |
| 199 static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd, |
| 200 struct snd_soc_dai *dai) |
| 201 { |
| 202 struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); |
| 203 |
| 204 switch (cmd) { |
| 205 case SNDRV_PCM_TRIGGER_START: |
| 206 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
| 207 case SNDRV_PCM_TRIGGER_RESUME: |
| 208 if (!spdif->clk_refs) |
| 209 clk_enable(spdif->clk_spdif_out); |
| 210 spdif->clk_refs++; |
| 211 tegra_spdif_start_playback(spdif); |
| 212 break; |
| 213 case SNDRV_PCM_TRIGGER_STOP: |
| 214 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
| 215 case SNDRV_PCM_TRIGGER_SUSPEND: |
| 216 tegra_spdif_stop_playback(spdif); |
| 217 spdif->clk_refs--; |
| 218 if (!spdif->clk_refs) |
| 219 clk_disable(spdif->clk_spdif_out); |
| 220 break; |
| 221 default: |
| 222 return -EINVAL; |
| 223 } |
| 224 |
| 225 return 0; |
| 226 } |
| 227 |
| 228 static int tegra_spdif_probe(struct snd_soc_dai *dai) |
| 229 { |
| 230 struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); |
| 231 |
| 232 dai->capture_dma_data = NULL; |
| 233 dai->playback_dma_data = &spdif->playback_dma_data; |
| 234 |
| 235 return 0; |
| 236 } |
| 237 |
| 238 static struct snd_soc_dai_ops tegra_spdif_dai_ops = { |
| 239 .hw_params = tegra_spdif_hw_params, |
| 240 .trigger = tegra_spdif_trigger, |
| 241 }; |
| 242 |
| 243 struct snd_soc_dai_driver tegra_spdif_dai = { |
| 244 .name = DRV_NAME ".0", |
| 245 .probe = tegra_spdif_probe, |
| 246 .playback = { |
| 247 .channels_min = 2, |
| 248 .channels_max = 2, |
| 249 .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | |
| 250 SNDRV_PCM_RATE_48000, |
| 251 .formats = SNDRV_PCM_FMTBIT_S16_LE, |
| 252 }, |
| 253 .ops = &tegra_spdif_dai_ops, |
| 254 }; |
| 255 |
| 256 static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev) |
| 257 { |
| 258 struct tegra_spdif *spdif; |
| 259 struct resource *mem, *memregion, *dmareq; |
| 260 int ret; |
| 261 |
| 262 spdif = kzalloc(sizeof(struct tegra_spdif), GFP_KERNEL); |
| 263 if (!spdif) { |
| 264 dev_err(&pdev->dev, "Can't allocate tegra_spdif\n"); |
| 265 ret = -ENOMEM; |
| 266 goto exit; |
| 267 } |
| 268 dev_set_drvdata(&pdev->dev, spdif); |
| 269 |
| 270 spdif->clk_spdif_out = clk_get_sys("spdif_out", NULL); |
| 271 if (IS_ERR(spdif->clk_spdif_out)) { |
| 272 pr_err("Can't retrieve spdif clock\n"); |
| 273 ret = PTR_ERR(spdif->clk_spdif_out); |
| 274 goto err_free; |
| 275 } |
| 276 |
| 277 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 278 if (!mem) { |
| 279 dev_err(&pdev->dev, "No memory resource\n"); |
| 280 ret = -ENODEV; |
| 281 goto err_clk_put; |
| 282 } |
| 283 |
| 284 dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0); |
| 285 if (!dmareq) { |
| 286 dev_err(&pdev->dev, "No DMA resource\n"); |
| 287 ret = -ENODEV; |
| 288 goto err_clk_put; |
| 289 } |
| 290 |
| 291 memregion = request_mem_region(mem->start, resource_size(mem), |
| 292 DRV_NAME); |
| 293 if (!memregion) { |
| 294 dev_err(&pdev->dev, "Memory region already claimed\n"); |
| 295 ret = -EBUSY; |
| 296 goto err_clk_put; |
| 297 } |
| 298 |
| 299 spdif->regs = ioremap(mem->start, resource_size(mem)); |
| 300 if (!spdif->regs) { |
| 301 dev_err(&pdev->dev, "ioremap failed\n"); |
| 302 ret = -ENOMEM; |
| 303 goto err_release; |
| 304 } |
| 305 |
| 306 spdif->playback_dma_data.addr = mem->start + TEGRA_SPDIF_DATA_OUT; |
| 307 spdif->playback_dma_data.wrap = 4; |
| 308 spdif->playback_dma_data.width = 32; |
| 309 spdif->playback_dma_data.req_sel = dmareq->start; |
| 310 |
| 311 ret = snd_soc_register_dai(&pdev->dev, &tegra_spdif_dai); |
| 312 if (ret) { |
| 313 dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); |
| 314 ret = -ENOMEM; |
| 315 goto err_unmap; |
| 316 } |
| 317 |
| 318 tegra_spdif_debug_add(spdif, pdev->id); |
| 319 |
| 320 return 0; |
| 321 |
| 322 err_unmap: |
| 323 iounmap(spdif->regs); |
| 324 err_release: |
| 325 release_mem_region(mem->start, resource_size(mem)); |
| 326 err_clk_put: |
| 327 clk_put(spdif->clk_spdif_out); |
| 328 err_free: |
| 329 kfree(spdif); |
| 330 exit: |
| 331 return ret; |
| 332 } |
| 333 |
| 334 static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev) |
| 335 { |
| 336 struct tegra_spdif *spdif = dev_get_drvdata(&pdev->dev); |
| 337 struct resource *res; |
| 338 |
| 339 snd_soc_unregister_dai(&pdev->dev); |
| 340 |
| 341 tegra_spdif_debug_remove(spdif); |
| 342 |
| 343 iounmap(spdif->regs); |
| 344 |
| 345 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 346 release_mem_region(res->start, resource_size(res)); |
| 347 |
| 348 clk_put(spdif->clk_spdif_out); |
| 349 |
| 350 kfree(spdif); |
| 351 |
| 352 return 0; |
| 353 } |
| 354 |
| 355 static struct platform_driver tegra_spdif_driver = { |
| 356 .driver = { |
| 357 .name = DRV_NAME, |
| 358 .owner = THIS_MODULE, |
| 359 }, |
| 360 .probe = tegra_spdif_platform_probe, |
| 361 .remove = __devexit_p(tegra_spdif_platform_remove), |
| 362 }; |
| 363 |
| 364 static int __init snd_tegra_spdif_init(void) |
| 365 { |
| 366 return platform_driver_register(&tegra_spdif_driver); |
| 367 } |
| 368 module_init(snd_tegra_spdif_init); |
| 369 |
| 370 static void __exit snd_tegra_spdif_exit(void) |
| 371 { |
| 372 platform_driver_unregister(&tegra_spdif_driver); |
| 373 } |
| 374 module_exit(snd_tegra_spdif_exit); |
| 375 |
| 376 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); |
| 377 MODULE_DESCRIPTION("Tegra SPDIF ASoC driver"); |
| 378 MODULE_LICENSE("GPL"); |
OLD | NEW |