aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcus Cooper <codekipper@gmail.com>2017-08-19 08:48:32 -0400
committerMark Brown <broonie@kernel.org>2017-08-21 12:15:44 -0400
commit6eb4f27419d9250ac632df15d9dcf916d84b9944 (patch)
tree5068efbf51591c4506d2622935e25e52e54629de
parent827807549e690c84bba5aac99b58dfbd52fcef8c (diff)
ASoC: sun4i-i2s: Add regmap fields for channels
On the original i2s block the channel mapping and selection were configured for stereo audio by default: This is not the case with the newer SoCs and they are also located at different offsets. To support the newer SoC then regmap fields have been added to the quirks and these are initialised to their correct settings during probing. Signed-off-by: Marcus Cooper <codekipper@gmail.com> Reviewed-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c77
1 files changed, 69 insertions, 8 deletions
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index dfb794ffff92..87feb5a14cff 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -82,7 +82,7 @@
82#define SUN4I_I2S_TX_CNT_REG 0x2c 82#define SUN4I_I2S_TX_CNT_REG 0x2c
83 83
84#define SUN4I_I2S_TX_CHAN_SEL_REG 0x30 84#define SUN4I_I2S_TX_CHAN_SEL_REG 0x30
85#define SUN4I_I2S_TX_CHAN_SEL(num_chan) (((num_chan) - 1) << 0) 85#define SUN4I_I2S_CHAN_SEL(num_chan) (((num_chan) - 1) << 0)
86 86
87#define SUN4I_I2S_TX_CHAN_MAP_REG 0x34 87#define SUN4I_I2S_TX_CHAN_MAP_REG 0x34
88#define SUN4I_I2S_TX_CHAN_MAP(chan, sample) ((sample) << (chan << 2)) 88#define SUN4I_I2S_TX_CHAN_MAP(chan, sample) ((sample) << (chan << 2))
@@ -98,6 +98,10 @@
98 * @sun4i_i2s_regmap: regmap config to use. 98 * @sun4i_i2s_regmap: regmap config to use.
99 * @mclk_offset: Value by which mclkdiv needs to be adjusted. 99 * @mclk_offset: Value by which mclkdiv needs to be adjusted.
100 * @bclk_offset: Value by which bclkdiv needs to be adjusted. 100 * @bclk_offset: Value by which bclkdiv needs to be adjusted.
101 * @field_txchanmap: location of the tx channel mapping register.
102 * @field_rxchanmap: location of the rx channel mapping register.
103 * @field_txchansel: location of the tx channel select bit fields.
104 * @field_rxchansel: location of the rx channel select bit fields.
101 */ 105 */
102struct sun4i_i2s_quirks { 106struct sun4i_i2s_quirks {
103 bool has_reset; 107 bool has_reset;
@@ -105,6 +109,12 @@ struct sun4i_i2s_quirks {
105 const struct regmap_config *sun4i_i2s_regmap; 109 const struct regmap_config *sun4i_i2s_regmap;
106 unsigned int mclk_offset; 110 unsigned int mclk_offset;
107 unsigned int bclk_offset; 111 unsigned int bclk_offset;
112
113 /* Register fields for i2s */
114 struct reg_field field_txchanmap;
115 struct reg_field field_rxchanmap;
116 struct reg_field field_txchansel;
117 struct reg_field field_rxchansel;
108}; 118};
109 119
110struct sun4i_i2s { 120struct sun4i_i2s {
@@ -118,6 +128,12 @@ struct sun4i_i2s {
118 struct snd_dmaengine_dai_dma_data capture_dma_data; 128 struct snd_dmaengine_dai_dma_data capture_dma_data;
119 struct snd_dmaengine_dai_dma_data playback_dma_data; 129 struct snd_dmaengine_dai_dma_data playback_dma_data;
120 130
131 /* Register fields for i2s */
132 struct regmap_field *field_txchanmap;
133 struct regmap_field *field_rxchanmap;
134 struct regmap_field *field_txchansel;
135 struct regmap_field *field_rxchansel;
136
121 const struct sun4i_i2s_quirks *variant; 137 const struct sun4i_i2s_quirks *variant;
122}; 138};
123 139
@@ -268,6 +284,17 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
268 if (params_channels(params) != 2) 284 if (params_channels(params) != 2)
269 return -EINVAL; 285 return -EINVAL;
270 286
287 /* Map the channels for playback and capture */
288 regmap_field_write(i2s->field_txchanmap, 0x76543210);
289 regmap_field_write(i2s->field_rxchanmap, 0x00003210);
290
291 /* Configure the channels */
292 regmap_field_write(i2s->field_txchansel,
293 SUN4I_I2S_CHAN_SEL(params_channels(params)));
294
295 regmap_field_write(i2s->field_rxchansel,
296 SUN4I_I2S_CHAN_SEL(params_channels(params)));
297
271 switch (params_physical_width(params)) { 298 switch (params_physical_width(params)) {
272 case 16: 299 case 16:
273 width = DMA_SLAVE_BUSWIDTH_2_BYTES; 300 width = DMA_SLAVE_BUSWIDTH_2_BYTES;
@@ -490,13 +517,6 @@ static int sun4i_i2s_startup(struct snd_pcm_substream *substream,
490 SUN4I_I2S_CTRL_SDO_EN_MASK, 517 SUN4I_I2S_CTRL_SDO_EN_MASK,
491 SUN4I_I2S_CTRL_SDO_EN(0)); 518 SUN4I_I2S_CTRL_SDO_EN(0));
492 519
493 /* Enable the first two channels */
494 regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
495 SUN4I_I2S_TX_CHAN_SEL(2));
496
497 /* Map them to the two first samples coming in */
498 regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG,
499 SUN4I_I2S_TX_CHAN_MAP(0, 0) | SUN4I_I2S_TX_CHAN_MAP(1, 1));
500 520
501 return clk_prepare_enable(i2s->mod_clk); 521 return clk_prepare_enable(i2s->mod_clk);
502} 522}
@@ -681,14 +701,49 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
681 .has_reset = false, 701 .has_reset = false,
682 .reg_offset_txdata = SUN4I_I2S_FIFO_TX_REG, 702 .reg_offset_txdata = SUN4I_I2S_FIFO_TX_REG,
683 .sun4i_i2s_regmap = &sun4i_i2s_regmap_config, 703 .sun4i_i2s_regmap = &sun4i_i2s_regmap_config,
704 .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
705 .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
706 .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
707 .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
684}; 708};
685 709
686static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { 710static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
687 .has_reset = true, 711 .has_reset = true,
688 .reg_offset_txdata = SUN4I_I2S_FIFO_TX_REG, 712 .reg_offset_txdata = SUN4I_I2S_FIFO_TX_REG,
689 .sun4i_i2s_regmap = &sun4i_i2s_regmap_config, 713 .sun4i_i2s_regmap = &sun4i_i2s_regmap_config,
714 .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
715 .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
716 .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
717 .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
690}; 718};
691 719
720static int sun4i_i2s_init_regmap_fields(struct device *dev,
721 struct sun4i_i2s *i2s)
722{
723 i2s->field_txchanmap =
724 devm_regmap_field_alloc(dev, i2s->regmap,
725 i2s->variant->field_txchanmap);
726 if (IS_ERR(i2s->field_txchanmap))
727 return PTR_ERR(i2s->field_txchanmap);
728
729 i2s->field_rxchanmap =
730 devm_regmap_field_alloc(dev, i2s->regmap,
731 i2s->variant->field_rxchanmap);
732 if (IS_ERR(i2s->field_rxchanmap))
733 return PTR_ERR(i2s->field_rxchanmap);
734
735 i2s->field_txchansel =
736 devm_regmap_field_alloc(dev, i2s->regmap,
737 i2s->variant->field_txchansel);
738 if (IS_ERR(i2s->field_txchansel))
739 return PTR_ERR(i2s->field_txchansel);
740
741 i2s->field_rxchansel =
742 devm_regmap_field_alloc(dev, i2s->regmap,
743 i2s->variant->field_rxchansel);
744 return PTR_ERR_OR_ZERO(i2s->field_rxchansel);
745}
746
692static int sun4i_i2s_probe(struct platform_device *pdev) 747static int sun4i_i2s_probe(struct platform_device *pdev)
693{ 748{
694 struct sun4i_i2s *i2s; 749 struct sun4i_i2s *i2s;
@@ -782,6 +837,12 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
782 goto err_suspend; 837 goto err_suspend;
783 } 838 }
784 839
840 ret = sun4i_i2s_init_regmap_fields(&pdev->dev, i2s);
841 if (ret) {
842 dev_err(&pdev->dev, "Could not initialise regmap fields\n");
843 goto err_suspend;
844 }
845
785 return 0; 846 return 0;
786 847
787err_suspend: 848err_suspend: