|
21 | 21 | #include <linux/platform_device.h> |
22 | 22 | #include <linux/pm_runtime.h> |
23 | 23 | #include <linux/reset.h> |
| 24 | +#include <linux/spinlock.h> |
| 25 | +#include <sound/asoundef.h> |
24 | 26 | #include <sound/dmaengine_pcm.h> |
25 | 27 | #include <sound/pcm_params.h> |
26 | 28 | #include <sound/soc.h> |
@@ -186,6 +188,7 @@ struct sun4i_spdif_dev { |
186 | 188 | struct regmap *regmap; |
187 | 189 | struct snd_dmaengine_dai_dma_data dma_params_tx; |
188 | 190 | const struct sun4i_spdif_quirks *quirks; |
| 191 | + spinlock_t lock; |
189 | 192 | }; |
190 | 193 |
|
191 | 194 | static void sun4i_spdif_configure(struct sun4i_spdif_dev *host) |
@@ -385,11 +388,122 @@ static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd, |
385 | 388 | return ret; |
386 | 389 | } |
387 | 390 |
|
| 391 | +static int sun4i_spdif_info(struct snd_kcontrol *kcontrol, |
| 392 | + struct snd_ctl_elem_info *uinfo) |
| 393 | +{ |
| 394 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; |
| 395 | + uinfo->count = 1; |
| 396 | + |
| 397 | + return 0; |
| 398 | +} |
| 399 | + |
| 400 | +static int sun4i_spdif_get_status_mask(struct snd_kcontrol *kcontrol, |
| 401 | + struct snd_ctl_elem_value *ucontrol) |
| 402 | +{ |
| 403 | + u8 *status = ucontrol->value.iec958.status; |
| 404 | + |
| 405 | + status[0] = 0xff; |
| 406 | + status[1] = 0xff; |
| 407 | + status[2] = 0xff; |
| 408 | + status[3] = 0xff; |
| 409 | + status[4] = 0xff; |
| 410 | + status[5] = 0x03; |
| 411 | + |
| 412 | + return 0; |
| 413 | +} |
| 414 | + |
| 415 | +static int sun4i_spdif_get_status(struct snd_kcontrol *kcontrol, |
| 416 | + struct snd_ctl_elem_value *ucontrol) |
| 417 | +{ |
| 418 | + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); |
| 419 | + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); |
| 420 | + u8 *status = ucontrol->value.iec958.status; |
| 421 | + unsigned long flags; |
| 422 | + unsigned int reg; |
| 423 | + |
| 424 | + spin_lock_irqsave(&host->lock, flags); |
| 425 | + |
| 426 | + regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA0, ®); |
| 427 | + |
| 428 | + status[0] = reg & 0xff; |
| 429 | + status[1] = (reg >> 8) & 0xff; |
| 430 | + status[2] = (reg >> 16) & 0xff; |
| 431 | + status[3] = (reg >> 24) & 0xff; |
| 432 | + |
| 433 | + regmap_read(host->regmap, SUN4I_SPDIF_TXCHSTA1, ®); |
| 434 | + |
| 435 | + status[4] = reg & 0xff; |
| 436 | + status[5] = (reg >> 8) & 0x3; |
| 437 | + |
| 438 | + spin_unlock_irqrestore(&host->lock, flags); |
| 439 | + |
| 440 | + return 0; |
| 441 | +} |
| 442 | + |
| 443 | +static int sun4i_spdif_set_status(struct snd_kcontrol *kcontrol, |
| 444 | + struct snd_ctl_elem_value *ucontrol) |
| 445 | +{ |
| 446 | + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); |
| 447 | + struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai); |
| 448 | + u8 *status = ucontrol->value.iec958.status; |
| 449 | + unsigned long flags; |
| 450 | + unsigned int reg; |
| 451 | + bool chg0, chg1; |
| 452 | + |
| 453 | + spin_lock_irqsave(&host->lock, flags); |
| 454 | + |
| 455 | + reg = (u32)status[3] << 24; |
| 456 | + reg |= (u32)status[2] << 16; |
| 457 | + reg |= (u32)status[1] << 8; |
| 458 | + reg |= (u32)status[0]; |
| 459 | + |
| 460 | + regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA0, |
| 461 | + GENMASK(31,0), reg, &chg0); |
| 462 | + |
| 463 | + reg = (u32)status[5] << 8; |
| 464 | + reg |= (u32)status[4]; |
| 465 | + |
| 466 | + regmap_update_bits_check(host->regmap, SUN4I_SPDIF_TXCHSTA1, |
| 467 | + GENMASK(9,0), reg, &chg1); |
| 468 | + |
| 469 | + reg = SUN4I_SPDIF_TXCFG_CHSTMODE; |
| 470 | + if (status[0] & IEC958_AES0_NONAUDIO) |
| 471 | + reg |= SUN4I_SPDIF_TXCFG_NONAUDIO; |
| 472 | + |
| 473 | + regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG, |
| 474 | + SUN4I_SPDIF_TXCFG_CHSTMODE | |
| 475 | + SUN4I_SPDIF_TXCFG_NONAUDIO, reg); |
| 476 | + |
| 477 | + spin_unlock_irqrestore(&host->lock, flags); |
| 478 | + |
| 479 | + return chg0 || chg1; |
| 480 | +} |
| 481 | + |
| 482 | +static struct snd_kcontrol_new sun4i_spdif_controls[] = { |
| 483 | + { |
| 484 | + .access = SNDRV_CTL_ELEM_ACCESS_READ, |
| 485 | + .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
| 486 | + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), |
| 487 | + .info = sun4i_spdif_info, |
| 488 | + .get = sun4i_spdif_get_status_mask |
| 489 | + }, |
| 490 | + { |
| 491 | + .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
| 492 | + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), |
| 493 | + .info = sun4i_spdif_info, |
| 494 | + .get = sun4i_spdif_get_status, |
| 495 | + .put = sun4i_spdif_set_status |
| 496 | + } |
| 497 | +}; |
| 498 | + |
388 | 499 | static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai) |
389 | 500 | { |
390 | 501 | struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai); |
391 | 502 |
|
392 | 503 | snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL); |
| 504 | + snd_soc_add_dai_controls(dai, sun4i_spdif_controls, |
| 505 | + ARRAY_SIZE(sun4i_spdif_controls)); |
| 506 | + |
393 | 507 | return 0; |
394 | 508 | } |
395 | 509 |
|
@@ -512,6 +626,7 @@ static int sun4i_spdif_probe(struct platform_device *pdev) |
512 | 626 | return -ENOMEM; |
513 | 627 |
|
514 | 628 | host->pdev = pdev; |
| 629 | + spin_lock_init(&host->lock); |
515 | 630 |
|
516 | 631 | /* Initialize this copy of the CPU DAI driver structure */ |
517 | 632 | memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai)); |
|
0 commit comments