diff options
Diffstat (limited to 'sound/soc/sunxi')
-rw-r--r-- | sound/soc/sunxi/sun4i-i2s.c | 71 |
1 files changed, 55 insertions, 16 deletions
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index d5ec1a20499d..03696f880080 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -118,6 +118,8 @@ #define SUN8I_I2S_RX_CHAN_SEL_REG 0x54 #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58 +struct sun4i_i2s; + /** * struct sun4i_i2s_quirks - Differences between SoC variants. * @@ -131,7 +133,6 @@ * @sun4i_i2s_regmap: regmap config to use. * @mclk_offset: Value by which mclkdiv needs to be adjusted. * @bclk_offset: Value by which bclkdiv needs to be adjusted. - * @fmt_offset: Value by which wss and sr needs to be adjusted. * @field_clkdiv_mclk_en: regmap field to enable mclk output. * @field_fmt_wss: regmap field to set word select size. * @field_fmt_sr: regmap field to set sample resolution. @@ -154,7 +155,6 @@ struct sun4i_i2s_quirks { const struct regmap_config *sun4i_i2s_regmap; unsigned int mclk_offset; unsigned int bclk_offset; - unsigned int fmt_offset; /* Register fields for i2s */ struct reg_field field_clkdiv_mclk_en; @@ -167,6 +167,9 @@ struct sun4i_i2s_quirks { struct reg_field field_rxchanmap; struct reg_field field_txchansel; struct reg_field field_rxchansel; + + s8 (*get_sr)(const struct sun4i_i2s *, int); + s8 (*get_wss)(const struct sun4i_i2s *, int); }; struct sun4i_i2s { @@ -349,6 +352,39 @@ static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, return 0; } +static s8 sun4i_i2s_get_sr(const struct sun4i_i2s *i2s, int width) +{ + if (width < 16 || width > 24) + return -EINVAL; + + if (width % 4) + return -EINVAL; + + return (width - 16) / 4; +} + +static s8 sun4i_i2s_get_wss(const struct sun4i_i2s *i2s, int width) +{ + if (width < 16 || width > 32) + return -EINVAL; + + if (width % 4) + return -EINVAL; + + return (width - 16) / 4; +} + +static s8 sun8i_i2s_get_sr_wss(const struct sun4i_i2s *i2s, int width) +{ + if (width % 4) + return -EINVAL; + + if (width < 8 || width > 32) + return -EINVAL; + + return (width - 8) / 4 + 1; +} + static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -400,22 +436,16 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, } i2s->playback_dma_data.addr_width = width; - switch (params_width(params)) { - case 16: - sr = 0; - wss = 0; - break; + sr = i2s->variant->get_sr(i2s, params_width(params)); + if (sr < 0) + return -EINVAL; - default: - dev_err(dai->dev, "Unsupported sample width: %d\n", - params_width(params)); + wss = i2s->variant->get_wss(i2s, params_width(params)); + if (wss < 0) return -EINVAL; - } - regmap_field_write(i2s->field_fmt_wss, - wss + i2s->variant->fmt_offset); - regmap_field_write(i2s->field_fmt_sr, - sr + i2s->variant->fmt_offset); + regmap_field_write(i2s->field_fmt_wss, wss); + regmap_field_write(i2s->field_fmt_sr, sr); return sun4i_i2s_set_clk_rate(dai, params_rate(params), params_width(params)); @@ -891,6 +921,8 @@ static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2), .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), + .get_sr = sun4i_i2s_get_sr, + .get_wss = sun4i_i2s_get_wss, }; static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { @@ -908,6 +940,8 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2), .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), + .get_sr = sun4i_i2s_get_sr, + .get_wss = sun4i_i2s_get_wss, }; static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { @@ -925,6 +959,8 @@ static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2), .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), + .get_sr = sun8i_i2s_get_sr_wss, + .get_wss = sun8i_i2s_get_sr_wss, }; static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { @@ -933,7 +969,6 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .sun4i_i2s_regmap = &sun8i_i2s_regmap_config, .mclk_offset = 1, .bclk_offset = 2, - .fmt_offset = 3, .has_fmt_set_lrck_period = true, .has_chcfg = true, .has_chsel_tx_chen = true, @@ -948,6 +983,8 @@ static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { .field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31), .field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2), .field_rxchansel = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2), + .get_sr = sun8i_i2s_get_sr_wss, + .get_wss = sun8i_i2s_get_sr_wss, }; static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { @@ -965,6 +1002,8 @@ static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2), .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), + .get_sr = sun8i_i2s_get_sr_wss, + .get_wss = sun8i_i2s_get_sr_wss, }; static int sun4i_i2s_init_regmap_fields(struct device *dev, |