Skip to content

Commit 7d29938

Browse files
codekipperbroonie
authored andcommitted
ASoC: sun4i-i2s: Add support for H3
The sun8i-h3 introduces a lot of changes to the i2s block such as different register locations, extended clock division and more operational modes. As we have to consider the earlier implementation then these changes need to be isolated. None of the new functionality has been implemented yet, the driver has just been expanded to allow it work on the H3 SoC. Signed-off-by: Marcus Cooper <[email protected]> Reviewed-by: Chen-Yu Tsai <[email protected]> Signed-off-by: Mark Brown <[email protected]>
1 parent 043b8da commit 7d29938

File tree

2 files changed

+176
-2
lines changed

2 files changed

+176
-2
lines changed

Documentation/devicetree/bindings/sound/sun4i-i2s.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Required properties:
88
- compatible: should be one of the following:
99
- "allwinner,sun4i-a10-i2s"
1010
- "allwinner,sun6i-a31-i2s"
11+
- "allwinner,sun8i-h3-i2s"
1112
- reg: physical base address of the controller and length of memory mapped
1213
region.
1314
- interrupts: should contain the I2S interrupt.
@@ -22,6 +23,7 @@ Required properties:
2223

2324
Required properties for the following compatibles:
2425
- "allwinner,sun6i-a31-i2s"
26+
- "allwinner,sun8i-h3-i2s"
2527
- resets: phandle to the reset line for this codec
2628

2729
Example:

sound/soc/sunxi/sun4i-i2s.c

Lines changed: 174 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,41 @@
9292
#define SUN4I_I2S_RX_CHAN_SEL_REG 0x38
9393
#define SUN4I_I2S_RX_CHAN_MAP_REG 0x3c
9494

95+
/* Defines required for sun8i-h3 support */
96+
#define SUN8I_I2S_CTRL_BCLK_OUT BIT(18)
97+
#define SUN8I_I2S_CTRL_LRCK_OUT BIT(17)
98+
99+
#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8)
100+
#define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
101+
102+
#define SUN8I_I2S_INT_STA_REG 0x0c
103+
#define SUN8I_I2S_FIFO_TX_REG 0x20
104+
105+
#define SUN8I_I2S_CHAN_CFG_REG 0x30
106+
#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(6, 4)
107+
#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(chan) (chan - 1)
108+
#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(2, 0)
109+
#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(chan) (chan - 1)
110+
111+
#define SUN8I_I2S_TX_CHAN_MAP_REG 0x44
112+
#define SUN8I_I2S_TX_CHAN_SEL_REG 0x34
113+
#define SUN8I_I2S_TX_CHAN_OFFSET_MASK GENMASK(13, 11)
114+
#define SUN8I_I2S_TX_CHAN_OFFSET(offset) (offset << 12)
115+
#define SUN8I_I2S_TX_CHAN_EN_MASK GENMASK(11, 4)
116+
#define SUN8I_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1) << 4)
117+
118+
#define SUN8I_I2S_RX_CHAN_SEL_REG 0x54
119+
#define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
120+
95121
/**
96122
* struct sun4i_i2s_quirks - Differences between SoC variants.
97123
*
98124
* @has_reset: SoC needs reset deasserted.
99125
* @has_slave_select_bit: SoC has a bit to enable slave mode.
126+
* @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
127+
* @has_chcfg: tx and rx slot number need to be set.
128+
* @has_chsel_tx_chen: SoC requires that the tx channels are enabled.
129+
* @has_chsel_offset: SoC uses offset for selecting dai operational mode.
100130
* @reg_offset_txdata: offset of the tx fifo.
101131
* @sun4i_i2s_regmap: regmap config to use.
102132
* @mclk_offset: Value by which mclkdiv needs to be adjusted.
@@ -116,6 +146,10 @@
116146
struct sun4i_i2s_quirks {
117147
bool has_reset;
118148
bool has_slave_select_bit;
149+
bool has_fmt_set_lrck_period;
150+
bool has_chcfg;
151+
bool has_chsel_tx_chen;
152+
bool has_chsel_offset;
119153
unsigned int reg_offset_txdata; /* TX FIFO */
120154
const struct regmap_config *sun4i_i2s_regmap;
121155
unsigned int mclk_offset;
@@ -173,6 +207,7 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
173207
{ .div = 8, .val = 3 },
174208
{ .div = 12, .val = 4 },
175209
{ .div = 16, .val = 5 },
210+
/* TODO - extend divide ratio supported by newer SoCs */
176211
};
177212

178213
static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
@@ -184,6 +219,7 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
184219
{ .div = 12, .val = 5 },
185220
{ .div = 16, .val = 6 },
186221
{ .div = 24, .val = 7 },
222+
/* TODO - extend divide ratio supported by newer SoCs */
187223
};
188224

189225
static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
@@ -295,6 +331,12 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
295331

296332
regmap_field_write(i2s->field_clkdiv_mclk_en, 1);
297333

334+
/* Set sync period */
335+
if (i2s->variant->has_fmt_set_lrck_period)
336+
regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
337+
SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
338+
SUN8I_I2S_FMT0_LRCK_PERIOD(32));
339+
298340
return 0;
299341
}
300342

@@ -303,12 +345,22 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
303345
struct snd_soc_dai *dai)
304346
{
305347
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
306-
int sr, wss;
348+
int sr, wss, channels;
307349
u32 width;
308350

309-
if (params_channels(params) != 2)
351+
channels = params_channels(params);
352+
if (channels != 2)
310353
return -EINVAL;
311354

355+
if (i2s->variant->has_chcfg) {
356+
regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
357+
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
358+
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
359+
regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
360+
SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
361+
SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
362+
}
363+
312364
/* Map the channels for playback and capture */
313365
regmap_field_write(i2s->field_txchanmap, 0x76543210);
314366
regmap_field_write(i2s->field_rxchanmap, 0x00003210);
@@ -320,6 +372,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
320372
regmap_field_write(i2s->field_rxchansel,
321373
SUN4I_I2S_CHAN_SEL(params_channels(params)));
322374

375+
if (i2s->variant->has_chsel_tx_chen)
376+
regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
377+
SUN8I_I2S_TX_CHAN_EN_MASK,
378+
SUN8I_I2S_TX_CHAN_EN(channels));
379+
323380
switch (params_physical_width(params)) {
324381
case 16:
325382
width = DMA_SLAVE_BUSWIDTH_2_BYTES;
@@ -352,13 +409,15 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
352409
{
353410
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
354411
u32 val;
412+
u32 offset = 0;
355413
u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
356414
u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
357415

358416
/* DAI Mode */
359417
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
360418
case SND_SOC_DAIFMT_I2S:
361419
val = SUN4I_I2S_FMT0_FMT_I2S;
420+
offset = 1;
362421
break;
363422
case SND_SOC_DAIFMT_LEFT_J:
364423
val = SUN4I_I2S_FMT0_FMT_LEFT_J;
@@ -370,6 +429,21 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
370429
return -EINVAL;
371430
}
372431

432+
if (i2s->variant->has_chsel_offset) {
433+
/*
434+
* offset being set indicates that we're connected to an i2s
435+
* device, however offset is only used on the sun8i block and
436+
* i2s shares the same setting with the LJ format. Increment
437+
* val so that the bit to value to write is correct.
438+
*/
439+
if (offset > 0)
440+
val++;
441+
/* blck offset determines whether i2s or LJ */
442+
regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
443+
SUN8I_I2S_TX_CHAN_OFFSET_MASK,
444+
SUN8I_I2S_TX_CHAN_OFFSET(offset));
445+
}
446+
373447
regmap_field_write(i2s->field_fmt_mode, val);
374448

375449
/* DAI clock polarity */
@@ -413,6 +487,29 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
413487
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
414488
SUN4I_I2S_CTRL_MODE_MASK,
415489
val);
490+
} else {
491+
/*
492+
* The newer i2s block does not have a slave select bit,
493+
* instead the clk pins are configured as inputs.
494+
*/
495+
/* DAI clock master masks */
496+
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
497+
case SND_SOC_DAIFMT_CBS_CFS:
498+
/* BCLK and LRCLK master */
499+
val = SUN8I_I2S_CTRL_BCLK_OUT |
500+
SUN8I_I2S_CTRL_LRCK_OUT;
501+
break;
502+
case SND_SOC_DAIFMT_CBM_CFM:
503+
/* BCLK and LRCLK slave */
504+
val = 0;
505+
break;
506+
default:
507+
return -EINVAL;
508+
}
509+
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
510+
SUN8I_I2S_CTRL_BCLK_OUT |
511+
SUN8I_I2S_CTRL_LRCK_OUT,
512+
val);
416513
}
417514

418515
/* Set significant bits in our FIFOs */
@@ -653,6 +750,27 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
653750
}
654751
}
655752

753+
static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg)
754+
{
755+
switch (reg) {
756+
case SUN8I_I2S_FIFO_TX_REG:
757+
return false;
758+
759+
default:
760+
return true;
761+
}
762+
}
763+
764+
static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg)
765+
{
766+
if (reg == SUN8I_I2S_INT_STA_REG)
767+
return true;
768+
if (reg == SUN8I_I2S_FIFO_TX_REG)
769+
return false;
770+
771+
return sun4i_i2s_volatile_reg(dev, reg);
772+
}
773+
656774
static const struct reg_default sun4i_i2s_reg_defaults[] = {
657775
{ SUN4I_I2S_CTRL_REG, 0x00000000 },
658776
{ SUN4I_I2S_FMT0_REG, 0x0000000c },
@@ -666,6 +784,20 @@ static const struct reg_default sun4i_i2s_reg_defaults[] = {
666784
{ SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210 },
667785
};
668786

787+
static const struct reg_default sun8i_i2s_reg_defaults[] = {
788+
{ SUN4I_I2S_CTRL_REG, 0x00060000 },
789+
{ SUN4I_I2S_FMT0_REG, 0x00000033 },
790+
{ SUN4I_I2S_FMT1_REG, 0x00000030 },
791+
{ SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
792+
{ SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
793+
{ SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
794+
{ SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
795+
{ SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
796+
{ SUN8I_I2S_TX_CHAN_MAP_REG, 0x00000000 },
797+
{ SUN8I_I2S_RX_CHAN_SEL_REG, 0x00000000 },
798+
{ SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 },
799+
};
800+
669801
static const struct regmap_config sun4i_i2s_regmap_config = {
670802
.reg_bits = 32,
671803
.reg_stride = 4,
@@ -680,6 +812,19 @@ static const struct regmap_config sun4i_i2s_regmap_config = {
680812
.volatile_reg = sun4i_i2s_volatile_reg,
681813
};
682814

815+
static const struct regmap_config sun8i_i2s_regmap_config = {
816+
.reg_bits = 32,
817+
.reg_stride = 4,
818+
.val_bits = 32,
819+
.max_register = SUN8I_I2S_RX_CHAN_MAP_REG,
820+
.cache_type = REGCACHE_FLAT,
821+
.reg_defaults = sun8i_i2s_reg_defaults,
822+
.num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults),
823+
.writeable_reg = sun4i_i2s_wr_reg,
824+
.readable_reg = sun8i_i2s_rd_reg,
825+
.volatile_reg = sun8i_i2s_volatile_reg,
826+
};
827+
683828
static int sun4i_i2s_runtime_resume(struct device *dev)
684829
{
685830
struct sun4i_i2s *i2s = dev_get_drvdata(dev);
@@ -752,6 +897,29 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
752897
.field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
753898
};
754899

900+
static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
901+
.has_reset = true,
902+
.reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
903+
.sun4i_i2s_regmap = &sun8i_i2s_regmap_config,
904+
.mclk_offset = 1,
905+
.bclk_offset = 2,
906+
.fmt_offset = 3,
907+
.has_fmt_set_lrck_period = true,
908+
.has_chcfg = true,
909+
.has_chsel_tx_chen = true,
910+
.has_chsel_offset = true,
911+
.field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
912+
.field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
913+
.field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
914+
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
915+
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
916+
.field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
917+
.field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
918+
.field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31),
919+
.field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2),
920+
.field_rxchansel = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2),
921+
};
922+
755923
static int sun4i_i2s_init_regmap_fields(struct device *dev,
756924
struct sun4i_i2s *i2s)
757925
{
@@ -952,6 +1120,10 @@ static const struct of_device_id sun4i_i2s_match[] = {
9521120
.compatible = "allwinner,sun6i-a31-i2s",
9531121
.data = &sun6i_a31_i2s_quirks,
9541122
},
1123+
{
1124+
.compatible = "allwinner,sun8i-h3-i2s",
1125+
.data = &sun8i_h3_i2s_quirks,
1126+
},
9551127
{}
9561128
};
9571129
MODULE_DEVICE_TABLE(of, sun4i_i2s_match);

0 commit comments

Comments
 (0)