diff options
| -rw-r--r-- | sound/soc/samsung/i2s.c | 81 |
1 files changed, 51 insertions, 30 deletions
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index 20cc51fc76ae..05fc2f0e91ca 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c | |||
| @@ -472,17 +472,22 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
| 472 | { | 472 | { |
| 473 | struct i2s_dai *i2s = to_info(dai); | 473 | struct i2s_dai *i2s = to_info(dai); |
| 474 | struct i2s_dai *other = get_other_dai(i2s); | 474 | struct i2s_dai *other = get_other_dai(i2s); |
| 475 | u32 mod = readl(i2s->addr + I2SMOD); | ||
| 476 | const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; | 475 | const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; |
| 477 | unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; | 476 | unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; |
| 478 | unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; | 477 | unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; |
| 478 | u32 mod, mask, val = 0; | ||
| 479 | |||
| 480 | spin_lock(i2s->lock); | ||
| 481 | mod = readl(i2s->addr + I2SMOD); | ||
| 482 | spin_unlock(i2s->lock); | ||
| 479 | 483 | ||
| 480 | switch (clk_id) { | 484 | switch (clk_id) { |
| 481 | case SAMSUNG_I2S_OPCLK: | 485 | case SAMSUNG_I2S_OPCLK: |
| 482 | mod &= ~MOD_OPCLK_MASK; | 486 | mask = MOD_OPCLK_MASK; |
| 483 | mod |= dir; | 487 | val = dir; |
| 484 | break; | 488 | break; |
| 485 | case SAMSUNG_I2S_CDCLK: | 489 | case SAMSUNG_I2S_CDCLK: |
| 490 | mask = 1 << i2s_regs->cdclkcon_off; | ||
| 486 | /* Shouldn't matter in GATING(CLOCK_IN) mode */ | 491 | /* Shouldn't matter in GATING(CLOCK_IN) mode */ |
| 487 | if (dir == SND_SOC_CLOCK_IN) | 492 | if (dir == SND_SOC_CLOCK_IN) |
| 488 | rfs = 0; | 493 | rfs = 0; |
| @@ -499,15 +504,15 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
| 499 | } | 504 | } |
| 500 | 505 | ||
| 501 | if (dir == SND_SOC_CLOCK_IN) | 506 | if (dir == SND_SOC_CLOCK_IN) |
| 502 | mod |= 1 << i2s_regs->cdclkcon_off; | 507 | val = 1 << i2s_regs->cdclkcon_off; |
| 503 | else | ||
| 504 | mod &= ~(1 << i2s_regs->cdclkcon_off); | ||
| 505 | 508 | ||
| 506 | i2s->rfs = rfs; | 509 | i2s->rfs = rfs; |
| 507 | break; | 510 | break; |
| 508 | 511 | ||
| 509 | case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ | 512 | case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ |
| 510 | case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ | 513 | case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ |
| 514 | mask = 1 << i2s_regs->rclksrc_off; | ||
| 515 | |||
| 511 | if ((i2s->quirks & QUIRK_NO_MUXPSR) | 516 | if ((i2s->quirks & QUIRK_NO_MUXPSR) |
| 512 | || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) | 517 | || (clk_id == SAMSUNG_I2S_RCLKSRC_0)) |
| 513 | clk_id = 0; | 518 | clk_id = 0; |
| @@ -557,18 +562,19 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai, | |||
| 557 | return 0; | 562 | return 0; |
| 558 | } | 563 | } |
| 559 | 564 | ||
| 560 | if (clk_id == 0) | 565 | if (clk_id == 1) |
| 561 | mod &= ~(1 << i2s_regs->rclksrc_off); | 566 | val = 1 << i2s_regs->rclksrc_off; |
| 562 | else | ||
| 563 | mod |= 1 << i2s_regs->rclksrc_off; | ||
| 564 | |||
| 565 | break; | 567 | break; |
| 566 | default: | 568 | default: |
| 567 | dev_err(&i2s->pdev->dev, "We don't serve that!\n"); | 569 | dev_err(&i2s->pdev->dev, "We don't serve that!\n"); |
| 568 | return -EINVAL; | 570 | return -EINVAL; |
| 569 | } | 571 | } |
| 570 | 572 | ||
| 573 | spin_lock(i2s->lock); | ||
| 574 | mod = readl(i2s->addr + I2SMOD); | ||
| 575 | mod = (mod & ~mask) | val; | ||
| 571 | writel(mod, i2s->addr + I2SMOD); | 576 | writel(mod, i2s->addr + I2SMOD); |
| 577 | spin_unlock(i2s->lock); | ||
| 572 | 578 | ||
| 573 | return 0; | 579 | return 0; |
| 574 | } | 580 | } |
| @@ -577,9 +583,8 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
| 577 | unsigned int fmt) | 583 | unsigned int fmt) |
| 578 | { | 584 | { |
| 579 | struct i2s_dai *i2s = to_info(dai); | 585 | struct i2s_dai *i2s = to_info(dai); |
| 580 | u32 mod = readl(i2s->addr + I2SMOD); | ||
| 581 | int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; | 586 | int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; |
| 582 | u32 tmp = 0; | 587 | u32 mod, tmp = 0; |
| 583 | 588 | ||
| 584 | lrp_shift = i2s->variant_regs->lrp_off; | 589 | lrp_shift = i2s->variant_regs->lrp_off; |
| 585 | sdf_shift = i2s->variant_regs->sdf_off; | 590 | sdf_shift = i2s->variant_regs->sdf_off; |
| @@ -639,12 +644,15 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
| 639 | return -EINVAL; | 644 | return -EINVAL; |
| 640 | } | 645 | } |
| 641 | 646 | ||
| 647 | spin_lock(i2s->lock); | ||
| 648 | mod = readl(i2s->addr + I2SMOD); | ||
| 642 | /* | 649 | /* |
| 643 | * Don't change the I2S mode if any controller is active on this | 650 | * Don't change the I2S mode if any controller is active on this |
| 644 | * channel. | 651 | * channel. |
| 645 | */ | 652 | */ |
| 646 | if (any_active(i2s) && | 653 | if (any_active(i2s) && |
| 647 | ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { | 654 | ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { |
| 655 | spin_unlock(i2s->lock); | ||
| 648 | dev_err(&i2s->pdev->dev, | 656 | dev_err(&i2s->pdev->dev, |
| 649 | "%s:%d Other DAI busy\n", __func__, __LINE__); | 657 | "%s:%d Other DAI busy\n", __func__, __LINE__); |
| 650 | return -EAGAIN; | 658 | return -EAGAIN; |
| @@ -653,6 +661,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai, | |||
| 653 | mod &= ~(sdf_mask | lrp_rlow | mod_slave); | 661 | mod &= ~(sdf_mask | lrp_rlow | mod_slave); |
| 654 | mod |= tmp; | 662 | mod |= tmp; |
| 655 | writel(mod, i2s->addr + I2SMOD); | 663 | writel(mod, i2s->addr + I2SMOD); |
| 664 | spin_unlock(i2s->lock); | ||
| 656 | 665 | ||
| 657 | return 0; | 666 | return 0; |
| 658 | } | 667 | } |
| @@ -661,16 +670,16 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, | |||
| 661 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | 670 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) |
| 662 | { | 671 | { |
| 663 | struct i2s_dai *i2s = to_info(dai); | 672 | struct i2s_dai *i2s = to_info(dai); |
| 664 | u32 mod = readl(i2s->addr + I2SMOD); | 673 | u32 mod, mask = 0, val = 0; |
| 665 | 674 | ||
| 666 | if (!is_secondary(i2s)) | 675 | if (!is_secondary(i2s)) |
| 667 | mod &= ~(MOD_DC2_EN | MOD_DC1_EN); | 676 | mask |= (MOD_DC2_EN | MOD_DC1_EN); |
| 668 | 677 | ||
| 669 | switch (params_channels(params)) { | 678 | switch (params_channels(params)) { |
| 670 | case 6: | 679 | case 6: |
| 671 | mod |= MOD_DC2_EN; | 680 | val |= MOD_DC2_EN; |
| 672 | case 4: | 681 | case 4: |
| 673 | mod |= MOD_DC1_EN; | 682 | val |= MOD_DC1_EN; |
| 674 | break; | 683 | break; |
| 675 | case 2: | 684 | case 2: |
| 676 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 685 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| @@ -692,44 +701,49 @@ static int i2s_hw_params(struct snd_pcm_substream *substream, | |||
| 692 | } | 701 | } |
| 693 | 702 | ||
| 694 | if (is_secondary(i2s)) | 703 | if (is_secondary(i2s)) |
| 695 | mod &= ~MOD_BLCS_MASK; | 704 | mask |= MOD_BLCS_MASK; |
| 696 | else | 705 | else |
| 697 | mod &= ~MOD_BLCP_MASK; | 706 | mask |= MOD_BLCP_MASK; |
| 698 | 707 | ||
| 699 | if (is_manager(i2s)) | 708 | if (is_manager(i2s)) |
| 700 | mod &= ~MOD_BLC_MASK; | 709 | mask |= MOD_BLC_MASK; |
| 701 | 710 | ||
| 702 | switch (params_width(params)) { | 711 | switch (params_width(params)) { |
| 703 | case 8: | 712 | case 8: |
| 704 | if (is_secondary(i2s)) | 713 | if (is_secondary(i2s)) |
| 705 | mod |= MOD_BLCS_8BIT; | 714 | val |= MOD_BLCS_8BIT; |
| 706 | else | 715 | else |
| 707 | mod |= MOD_BLCP_8BIT; | 716 | val |= MOD_BLCP_8BIT; |
| 708 | if (is_manager(i2s)) | 717 | if (is_manager(i2s)) |
| 709 | mod |= MOD_BLC_8BIT; | 718 | val |= MOD_BLC_8BIT; |
| 710 | break; | 719 | break; |
| 711 | case 16: | 720 | case 16: |
| 712 | if (is_secondary(i2s)) | 721 | if (is_secondary(i2s)) |
| 713 | mod |= MOD_BLCS_16BIT; | 722 | val |= MOD_BLCS_16BIT; |
| 714 | else | 723 | else |
| 715 | mod |= MOD_BLCP_16BIT; | 724 | val |= MOD_BLCP_16BIT; |
| 716 | if (is_manager(i2s)) | 725 | if (is_manager(i2s)) |
| 717 | mod |= MOD_BLC_16BIT; | 726 | val |= MOD_BLC_16BIT; |
| 718 | break; | 727 | break; |
| 719 | case 24: | 728 | case 24: |
| 720 | if (is_secondary(i2s)) | 729 | if (is_secondary(i2s)) |
| 721 | mod |= MOD_BLCS_24BIT; | 730 | val |= MOD_BLCS_24BIT; |
| 722 | else | 731 | else |
| 723 | mod |= MOD_BLCP_24BIT; | 732 | val |= MOD_BLCP_24BIT; |
| 724 | if (is_manager(i2s)) | 733 | if (is_manager(i2s)) |
| 725 | mod |= MOD_BLC_24BIT; | 734 | val |= MOD_BLC_24BIT; |
| 726 | break; | 735 | break; |
| 727 | default: | 736 | default: |
| 728 | dev_err(&i2s->pdev->dev, "Format(%d) not supported\n", | 737 | dev_err(&i2s->pdev->dev, "Format(%d) not supported\n", |
| 729 | params_format(params)); | 738 | params_format(params)); |
| 730 | return -EINVAL; | 739 | return -EINVAL; |
| 731 | } | 740 | } |
| 741 | |||
| 742 | spin_lock(i2s->lock); | ||
| 743 | mod = readl(i2s->addr + I2SMOD); | ||
| 744 | mod = (mod & ~mask) | val; | ||
| 732 | writel(mod, i2s->addr + I2SMOD); | 745 | writel(mod, i2s->addr + I2SMOD); |
| 746 | spin_unlock(i2s->lock); | ||
| 733 | 747 | ||
| 734 | samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); | 748 | samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); |
| 735 | 749 | ||
| @@ -979,6 +993,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) | |||
| 979 | { | 993 | { |
| 980 | struct i2s_dai *i2s = to_info(dai); | 994 | struct i2s_dai *i2s = to_info(dai); |
| 981 | struct i2s_dai *other = get_other_dai(i2s); | 995 | struct i2s_dai *other = get_other_dai(i2s); |
| 996 | unsigned long flags; | ||
| 982 | 997 | ||
| 983 | if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ | 998 | if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */ |
| 984 | samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, | 999 | samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, |
| @@ -999,11 +1014,14 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) | |||
| 999 | i2s->rfs = 0; | 1014 | i2s->rfs = 0; |
| 1000 | i2s->bfs = 0; | 1015 | i2s->bfs = 0; |
| 1001 | i2s->rclk_srcrate = 0; | 1016 | i2s->rclk_srcrate = 0; |
| 1017 | |||
| 1018 | spin_lock_irqsave(i2s->lock, flags); | ||
| 1002 | i2s_txctrl(i2s, 0); | 1019 | i2s_txctrl(i2s, 0); |
| 1003 | i2s_rxctrl(i2s, 0); | 1020 | i2s_rxctrl(i2s, 0); |
| 1004 | i2s_fifo(i2s, FIC_TXFLUSH); | 1021 | i2s_fifo(i2s, FIC_TXFLUSH); |
| 1005 | i2s_fifo(other, FIC_TXFLUSH); | 1022 | i2s_fifo(other, FIC_TXFLUSH); |
| 1006 | i2s_fifo(i2s, FIC_RXFLUSH); | 1023 | i2s_fifo(i2s, FIC_RXFLUSH); |
| 1024 | spin_unlock_irqrestore(i2s->lock, flags); | ||
| 1007 | 1025 | ||
| 1008 | /* Gate CDCLK by default */ | 1026 | /* Gate CDCLK by default */ |
| 1009 | if (!is_opened(other)) | 1027 | if (!is_opened(other)) |
| @@ -1018,8 +1036,11 @@ static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) | |||
| 1018 | struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); | 1036 | struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); |
| 1019 | 1037 | ||
| 1020 | if (!is_secondary(i2s)) { | 1038 | if (!is_secondary(i2s)) { |
| 1021 | if (i2s->quirks & QUIRK_NEED_RSTCLR) | 1039 | if (i2s->quirks & QUIRK_NEED_RSTCLR) { |
| 1040 | spin_lock(i2s->lock); | ||
| 1022 | writel(0, i2s->addr + I2SCON); | 1041 | writel(0, i2s->addr + I2SCON); |
| 1042 | spin_unlock(i2s->lock); | ||
| 1043 | } | ||
| 1023 | } | 1044 | } |
| 1024 | 1045 | ||
| 1025 | return 0; | 1046 | return 0; |
