1 /* 2 * IMG SPDIF output controller driver 3 * 4 * Copyright (C) 2015 Imagination Technologies Ltd. 5 * 6 * Author: Damien Horsley <Damien.Horsley@imgtec.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms and conditions of the GNU General Public License, 10 * version 2, as published by the Free Software Foundation. 11 */ 12 13 #include <linux/clk.h> 14 #include <linux/init.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/of.h> 18 #include <linux/platform_device.h> 19 #include <linux/pm_runtime.h> 20 #include <linux/reset.h> 21 22 #include <sound/core.h> 23 #include <sound/dmaengine_pcm.h> 24 #include <sound/initval.h> 25 #include <sound/pcm.h> 26 #include <sound/pcm_params.h> 27 #include <sound/soc.h> 28 29 #define IMG_SPDIF_OUT_TX_FIFO 0x0 30 31 #define IMG_SPDIF_OUT_CTL 0x4 32 #define IMG_SPDIF_OUT_CTL_FS_MASK BIT(4) 33 #define IMG_SPDIF_OUT_CTL_CLK_MASK BIT(2) 34 #define IMG_SPDIF_OUT_CTL_SRT_MASK BIT(0) 35 36 #define IMG_SPDIF_OUT_CSL 0x14 37 38 #define IMG_SPDIF_OUT_CSH_UV 0x18 39 #define IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT 0 40 #define IMG_SPDIF_OUT_CSH_UV_CSH_MASK 0xff 41 42 struct img_spdif_out { 43 spinlock_t lock; 44 void __iomem *base; 45 struct clk *clk_sys; 46 struct clk *clk_ref; 47 struct snd_dmaengine_dai_dma_data dma_data; 48 struct device *dev; 49 struct reset_control *rst; 50 u32 suspend_ctl; 51 u32 suspend_csl; 52 u32 suspend_csh; 53 }; 54 55 static int img_spdif_out_runtime_suspend(struct device *dev) 56 { 57 struct img_spdif_out *spdif = dev_get_drvdata(dev); 58 59 clk_disable_unprepare(spdif->clk_ref); 60 clk_disable_unprepare(spdif->clk_sys); 61 62 return 0; 63 } 64 65 static int img_spdif_out_runtime_resume(struct device *dev) 66 { 67 struct img_spdif_out *spdif = dev_get_drvdata(dev); 68 int ret; 69 70 ret = clk_prepare_enable(spdif->clk_sys); 71 if (ret) { 72 dev_err(dev, "clk_enable failed: %d\n", ret); 73 return ret; 74 } 75 76 ret = clk_prepare_enable(spdif->clk_ref); 77 if (ret) { 78 dev_err(dev, "clk_enable failed: %d\n", ret); 79 clk_disable_unprepare(spdif->clk_sys); 80 return ret; 81 } 82 83 return 0; 84 } 85 86 static inline void img_spdif_out_writel(struct img_spdif_out *spdif, u32 val, 87 u32 reg) 88 { 89 writel(val, spdif->base + reg); 90 } 91 92 static inline u32 img_spdif_out_readl(struct img_spdif_out *spdif, u32 reg) 93 { 94 return readl(spdif->base + reg); 95 } 96 97 static void img_spdif_out_reset(struct img_spdif_out *spdif) 98 { 99 u32 ctl, status_low, status_high; 100 101 ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL) & 102 ~IMG_SPDIF_OUT_CTL_SRT_MASK; 103 status_low = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); 104 status_high = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); 105 106 reset_control_assert(spdif->rst); 107 reset_control_deassert(spdif->rst); 108 109 img_spdif_out_writel(spdif, ctl, IMG_SPDIF_OUT_CTL); 110 img_spdif_out_writel(spdif, status_low, IMG_SPDIF_OUT_CSL); 111 img_spdif_out_writel(spdif, status_high, IMG_SPDIF_OUT_CSH_UV); 112 } 113 114 static int img_spdif_out_info(struct snd_kcontrol *kcontrol, 115 struct snd_ctl_elem_info *uinfo) 116 { 117 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 118 uinfo->count = 1; 119 120 return 0; 121 } 122 123 static int img_spdif_out_get_status_mask(struct snd_kcontrol *kcontrol, 124 struct snd_ctl_elem_value *ucontrol) 125 { 126 ucontrol->value.iec958.status[0] = 0xff; 127 ucontrol->value.iec958.status[1] = 0xff; 128 ucontrol->value.iec958.status[2] = 0xff; 129 ucontrol->value.iec958.status[3] = 0xff; 130 ucontrol->value.iec958.status[4] = 0xff; 131 132 return 0; 133 } 134 135 static int img_spdif_out_get_status(struct snd_kcontrol *kcontrol, 136 struct snd_ctl_elem_value *ucontrol) 137 { 138 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); 139 struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai); 140 u32 reg; 141 unsigned long flags; 142 143 spin_lock_irqsave(&spdif->lock, flags); 144 145 reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); 146 ucontrol->value.iec958.status[0] = reg & 0xff; 147 ucontrol->value.iec958.status[1] = (reg >> 8) & 0xff; 148 ucontrol->value.iec958.status[2] = (reg >> 16) & 0xff; 149 ucontrol->value.iec958.status[3] = (reg >> 24) & 0xff; 150 151 reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); 152 ucontrol->value.iec958.status[4] = 153 (reg & IMG_SPDIF_OUT_CSH_UV_CSH_MASK) >> 154 IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT; 155 156 spin_unlock_irqrestore(&spdif->lock, flags); 157 158 return 0; 159 } 160 161 static int img_spdif_out_set_status(struct snd_kcontrol *kcontrol, 162 struct snd_ctl_elem_value *ucontrol) 163 { 164 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); 165 struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(cpu_dai); 166 u32 reg; 167 unsigned long flags; 168 169 reg = ((u32)ucontrol->value.iec958.status[3] << 24); 170 reg |= ((u32)ucontrol->value.iec958.status[2] << 16); 171 reg |= ((u32)ucontrol->value.iec958.status[1] << 8); 172 reg |= (u32)ucontrol->value.iec958.status[0]; 173 174 spin_lock_irqsave(&spdif->lock, flags); 175 176 img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSL); 177 178 reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); 179 reg &= ~IMG_SPDIF_OUT_CSH_UV_CSH_MASK; 180 reg |= (u32)ucontrol->value.iec958.status[4] << 181 IMG_SPDIF_OUT_CSH_UV_CSH_SHIFT; 182 img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CSH_UV); 183 184 spin_unlock_irqrestore(&spdif->lock, flags); 185 186 return 0; 187 } 188 189 static struct snd_kcontrol_new img_spdif_out_controls[] = { 190 { 191 .access = SNDRV_CTL_ELEM_ACCESS_READ, 192 .iface = SNDRV_CTL_ELEM_IFACE_PCM, 193 .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), 194 .info = img_spdif_out_info, 195 .get = img_spdif_out_get_status_mask 196 }, 197 { 198 .iface = SNDRV_CTL_ELEM_IFACE_PCM, 199 .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 200 .info = img_spdif_out_info, 201 .get = img_spdif_out_get_status, 202 .put = img_spdif_out_set_status 203 } 204 }; 205 206 static int img_spdif_out_trigger(struct snd_pcm_substream *substream, int cmd, 207 struct snd_soc_dai *dai) 208 { 209 struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); 210 u32 reg; 211 unsigned long flags; 212 213 switch (cmd) { 214 case SNDRV_PCM_TRIGGER_START: 215 case SNDRV_PCM_TRIGGER_RESUME: 216 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 217 reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); 218 reg |= IMG_SPDIF_OUT_CTL_SRT_MASK; 219 img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL); 220 break; 221 case SNDRV_PCM_TRIGGER_STOP: 222 case SNDRV_PCM_TRIGGER_SUSPEND: 223 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 224 spin_lock_irqsave(&spdif->lock, flags); 225 img_spdif_out_reset(spdif); 226 spin_unlock_irqrestore(&spdif->lock, flags); 227 break; 228 default: 229 return -EINVAL; 230 } 231 232 return 0; 233 } 234 235 static int img_spdif_out_hw_params(struct snd_pcm_substream *substream, 236 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 237 { 238 struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); 239 unsigned int channels; 240 long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate; 241 u32 reg; 242 snd_pcm_format_t format; 243 244 rate = params_rate(params); 245 format = params_format(params); 246 channels = params_channels(params); 247 248 dev_dbg(spdif->dev, "hw_params rate %ld channels %u format %u\n", 249 rate, channels, format); 250 251 if (format != SNDRV_PCM_FORMAT_S32_LE) 252 return -EINVAL; 253 254 if (channels != 2) 255 return -EINVAL; 256 257 pre_div_a = clk_round_rate(spdif->clk_ref, rate * 256); 258 if (pre_div_a < 0) 259 return pre_div_a; 260 pre_div_b = clk_round_rate(spdif->clk_ref, rate * 384); 261 if (pre_div_b < 0) 262 return pre_div_b; 263 264 diff_a = abs((pre_div_a / 256) - rate); 265 diff_b = abs((pre_div_b / 384) - rate); 266 267 /* If diffs are equal, use lower clock rate */ 268 if (diff_a > diff_b) 269 clk_set_rate(spdif->clk_ref, pre_div_b); 270 else 271 clk_set_rate(spdif->clk_ref, pre_div_a); 272 273 /* 274 * Another driver (eg machine driver) may have rejected the above 275 * change. Get the current rate and set the register bit according to 276 * the new min diff 277 */ 278 clk_rate = clk_get_rate(spdif->clk_ref); 279 280 diff_a = abs((clk_rate / 256) - rate); 281 diff_b = abs((clk_rate / 384) - rate); 282 283 reg = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); 284 if (diff_a <= diff_b) 285 reg &= ~IMG_SPDIF_OUT_CTL_CLK_MASK; 286 else 287 reg |= IMG_SPDIF_OUT_CTL_CLK_MASK; 288 img_spdif_out_writel(spdif, reg, IMG_SPDIF_OUT_CTL); 289 290 return 0; 291 } 292 293 static const struct snd_soc_dai_ops img_spdif_out_dai_ops = { 294 .trigger = img_spdif_out_trigger, 295 .hw_params = img_spdif_out_hw_params 296 }; 297 298 static int img_spdif_out_dai_probe(struct snd_soc_dai *dai) 299 { 300 struct img_spdif_out *spdif = snd_soc_dai_get_drvdata(dai); 301 302 snd_soc_dai_init_dma_data(dai, &spdif->dma_data, NULL); 303 304 snd_soc_add_dai_controls(dai, img_spdif_out_controls, 305 ARRAY_SIZE(img_spdif_out_controls)); 306 307 return 0; 308 } 309 310 static struct snd_soc_dai_driver img_spdif_out_dai = { 311 .probe = img_spdif_out_dai_probe, 312 .playback = { 313 .channels_min = 2, 314 .channels_max = 2, 315 .rates = SNDRV_PCM_RATE_8000_192000, 316 .formats = SNDRV_PCM_FMTBIT_S32_LE 317 }, 318 .ops = &img_spdif_out_dai_ops 319 }; 320 321 static const struct snd_soc_component_driver img_spdif_out_component = { 322 .name = "img-spdif-out" 323 }; 324 325 static int img_spdif_out_probe(struct platform_device *pdev) 326 { 327 struct img_spdif_out *spdif; 328 struct resource *res; 329 void __iomem *base; 330 int ret; 331 struct device *dev = &pdev->dev; 332 333 spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL); 334 if (!spdif) 335 return -ENOMEM; 336 337 platform_set_drvdata(pdev, spdif); 338 339 spdif->dev = &pdev->dev; 340 341 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 342 base = devm_ioremap_resource(&pdev->dev, res); 343 if (IS_ERR(base)) 344 return PTR_ERR(base); 345 346 spdif->base = base; 347 348 spdif->rst = devm_reset_control_get_exclusive(&pdev->dev, "rst"); 349 if (IS_ERR(spdif->rst)) { 350 if (PTR_ERR(spdif->rst) != -EPROBE_DEFER) 351 dev_err(&pdev->dev, "No top level reset found\n"); 352 return PTR_ERR(spdif->rst); 353 } 354 355 spdif->clk_sys = devm_clk_get(&pdev->dev, "sys"); 356 if (IS_ERR(spdif->clk_sys)) { 357 if (PTR_ERR(spdif->clk_sys) != -EPROBE_DEFER) 358 dev_err(dev, "Failed to acquire clock 'sys'\n"); 359 return PTR_ERR(spdif->clk_sys); 360 } 361 362 spdif->clk_ref = devm_clk_get(&pdev->dev, "ref"); 363 if (IS_ERR(spdif->clk_ref)) { 364 if (PTR_ERR(spdif->clk_ref) != -EPROBE_DEFER) 365 dev_err(dev, "Failed to acquire clock 'ref'\n"); 366 return PTR_ERR(spdif->clk_ref); 367 } 368 369 pm_runtime_enable(&pdev->dev); 370 if (!pm_runtime_enabled(&pdev->dev)) { 371 ret = img_spdif_out_runtime_resume(&pdev->dev); 372 if (ret) 373 goto err_pm_disable; 374 } 375 ret = pm_runtime_get_sync(&pdev->dev); 376 if (ret < 0) 377 goto err_suspend; 378 379 img_spdif_out_writel(spdif, IMG_SPDIF_OUT_CTL_FS_MASK, 380 IMG_SPDIF_OUT_CTL); 381 382 img_spdif_out_reset(spdif); 383 pm_runtime_put(&pdev->dev); 384 385 spin_lock_init(&spdif->lock); 386 387 spdif->dma_data.addr = res->start + IMG_SPDIF_OUT_TX_FIFO; 388 spdif->dma_data.addr_width = 4; 389 spdif->dma_data.maxburst = 4; 390 391 ret = devm_snd_soc_register_component(&pdev->dev, 392 &img_spdif_out_component, 393 &img_spdif_out_dai, 1); 394 if (ret) 395 goto err_suspend; 396 397 ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); 398 if (ret) 399 goto err_suspend; 400 401 dev_dbg(&pdev->dev, "Probe successful\n"); 402 403 return 0; 404 405 err_suspend: 406 if (!pm_runtime_status_suspended(&pdev->dev)) 407 img_spdif_out_runtime_suspend(&pdev->dev); 408 err_pm_disable: 409 pm_runtime_disable(&pdev->dev); 410 411 return ret; 412 } 413 414 static int img_spdif_out_dev_remove(struct platform_device *pdev) 415 { 416 pm_runtime_disable(&pdev->dev); 417 if (!pm_runtime_status_suspended(&pdev->dev)) 418 img_spdif_out_runtime_suspend(&pdev->dev); 419 420 return 0; 421 } 422 423 #ifdef CONFIG_PM_SLEEP 424 static int img_spdif_out_suspend(struct device *dev) 425 { 426 struct img_spdif_out *spdif = dev_get_drvdata(dev); 427 int ret; 428 429 if (pm_runtime_status_suspended(dev)) { 430 ret = img_spdif_out_runtime_resume(dev); 431 if (ret) 432 return ret; 433 } 434 435 spdif->suspend_ctl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CTL); 436 spdif->suspend_csl = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSL); 437 spdif->suspend_csh = img_spdif_out_readl(spdif, IMG_SPDIF_OUT_CSH_UV); 438 439 img_spdif_out_runtime_suspend(dev); 440 441 return 0; 442 } 443 444 static int img_spdif_out_resume(struct device *dev) 445 { 446 struct img_spdif_out *spdif = dev_get_drvdata(dev); 447 int ret; 448 449 ret = img_spdif_out_runtime_resume(dev); 450 if (ret) 451 return ret; 452 453 img_spdif_out_writel(spdif, spdif->suspend_ctl, IMG_SPDIF_OUT_CTL); 454 img_spdif_out_writel(spdif, spdif->suspend_csl, IMG_SPDIF_OUT_CSL); 455 img_spdif_out_writel(spdif, spdif->suspend_csh, IMG_SPDIF_OUT_CSH_UV); 456 457 if (pm_runtime_status_suspended(dev)) 458 img_spdif_out_runtime_suspend(dev); 459 460 return 0; 461 } 462 #endif 463 static const struct of_device_id img_spdif_out_of_match[] = { 464 { .compatible = "img,spdif-out" }, 465 {} 466 }; 467 MODULE_DEVICE_TABLE(of, img_spdif_out_of_match); 468 469 static const struct dev_pm_ops img_spdif_out_pm_ops = { 470 SET_RUNTIME_PM_OPS(img_spdif_out_runtime_suspend, 471 img_spdif_out_runtime_resume, NULL) 472 SET_SYSTEM_SLEEP_PM_OPS(img_spdif_out_suspend, img_spdif_out_resume) 473 }; 474 475 static struct platform_driver img_spdif_out_driver = { 476 .driver = { 477 .name = "img-spdif-out", 478 .of_match_table = img_spdif_out_of_match, 479 .pm = &img_spdif_out_pm_ops 480 }, 481 .probe = img_spdif_out_probe, 482 .remove = img_spdif_out_dev_remove 483 }; 484 module_platform_driver(img_spdif_out_driver); 485 486 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>"); 487 MODULE_DESCRIPTION("IMG SPDIF Output driver"); 488 MODULE_LICENSE("GPL v2"); 489