Merge remote-tracking branches 'asoc/topic/simple', 'asoc/topic/spear', 'asoc/topic/sta32x', 'asoc/topic/stm32' and 'asoc/topic/sunxi' into asoc-next

This commit is contained in:
Mark Brown 2017-09-01 12:13:06 +01:00
18 changed files with 441 additions and 93 deletions

View file

@ -86,6 +86,9 @@ Optional CPU/CODEC subnodes properties:
in dai startup() and disabled with in dai startup() and disabled with
clk_disable_unprepare() in dai clk_disable_unprepare() in dai
shutdown(). shutdown().
- system-clock-direction-out : specifies clock direction as 'out' on
initialization. It is useful for some aCPUs with
fixed clocks.
Example 1 - single DAI link: Example 1 - single DAI link:

View file

@ -24,6 +24,7 @@ Optional subnode properties:
- simple-audio-card,convert-rate : platform specified sampling rate convert - simple-audio-card,convert-rate : platform specified sampling rate convert
- simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch) - simple-audio-card,convert-channels : platform specified converted channel size (2 - 8 ch)
- simple-audio-card,prefix : see routing - simple-audio-card,prefix : see routing
- simple-audio-card,widgets : Please refer to widgets.txt.
- simple-audio-card,routing : A list of the connections between audio components. - simple-audio-card,routing : A list of the connections between audio components.
Each entry is a pair of strings, the first being the connection's sink, Each entry is a pair of strings, the first being the connection's sink,
the second being the connection's source. Valid names for sources. the second being the connection's source. Valid names for sources.

View file

@ -8,6 +8,7 @@ Required properties:
- compatible: should be one of the following: - compatible: should be one of the following:
- "allwinner,sun4i-a10-i2s" - "allwinner,sun4i-a10-i2s"
- "allwinner,sun6i-a31-i2s" - "allwinner,sun6i-a31-i2s"
- "allwinner,sun8i-h3-i2s"
- reg: physical base address of the controller and length of memory mapped - reg: physical base address of the controller and length of memory mapped
region. region.
- interrupts: should contain the I2S interrupt. - interrupts: should contain the I2S interrupt.
@ -22,6 +23,7 @@ Required properties:
Required properties for the following compatibles: Required properties for the following compatibles:
- "allwinner,sun6i-a31-i2s" - "allwinner,sun6i-a31-i2s"
- "allwinner,sun8i-h3-i2s"
- resets: phandle to the reset line for this codec - resets: phandle to the reset line for this codec
Example: Example:

View file

@ -15,6 +15,7 @@
struct asoc_simple_dai { struct asoc_simple_dai {
const char *name; const char *name;
unsigned int sysclk; unsigned int sysclk;
int clk_direction;
int slots; int slots;
int slot_width; int slot_width;
unsigned int tx_slot_mask; unsigned int tx_slot_mask;

View file

@ -847,8 +847,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
msleep(300); msleep(300);
sta32x_watchdog_stop(sta32x); sta32x_watchdog_stop(sta32x);
if (sta32x->gpiod_nreset) gpiod_set_value(sta32x->gpiod_nreset, 0);
gpiod_set_value(sta32x->gpiod_nreset, 0);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies); sta32x->supplies);

View file

@ -325,6 +325,7 @@ MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
static struct platform_driver asoc_graph_card = { static struct platform_driver asoc_graph_card = {
.driver = { .driver = {
.name = "asoc-audio-graph-card", .name = "asoc-audio-graph-card",
.pm = &snd_soc_pm_ops,
.of_match_table = asoc_graph_of_match, .of_match_table = asoc_graph_of_match,
}, },
.probe = asoc_graph_card_probe, .probe = asoc_graph_card_probe,

View file

@ -401,6 +401,7 @@ MODULE_DEVICE_TABLE(of, asoc_graph_of_match);
static struct platform_driver asoc_graph_card = { static struct platform_driver asoc_graph_card = {
.driver = { .driver = {
.name = "asoc-audio-graph-scu-card", .name = "asoc-audio-graph-scu-card",
.pm = &snd_soc_pm_ops,
.of_match_table = asoc_graph_of_match, .of_match_table = asoc_graph_of_match,
}, },
.probe = asoc_graph_card_probe, .probe = asoc_graph_card_probe,

View file

@ -196,7 +196,11 @@ int asoc_simple_card_parse_clk(struct device *dev,
simple_dai->sysclk = clk_get_rate(clk); simple_dai->sysclk = clk_get_rate(clk);
} }
dev_dbg(dev, "%s : sysclk = %d\n", name, simple_dai->sysclk); if (of_property_read_bool(node, "system-clock-direction-out"))
simple_dai->clk_direction = SND_SOC_CLOCK_OUT;
dev_dbg(dev, "%s : sysclk = %d, direction %d\n", name,
simple_dai->sysclk, simple_dai->clk_direction);
return 0; return 0;
} }
@ -308,7 +312,8 @@ int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
int ret; int ret;
if (simple_dai->sysclk) { if (simple_dai->sysclk) {
ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 0); ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk,
simple_dai->clk_direction);
if (ret && ret != -ENOTSUPP) { if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "simple-card: set_sysclk error\n"); dev_err(dai->dev, "simple-card: set_sysclk error\n");
return ret; return ret;

View file

@ -191,6 +191,10 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv)
if (!node) if (!node)
return -EINVAL; return -EINVAL;
ret = asoc_simple_card_of_parse_widgets(card, PREFIX);
if (ret < 0)
return ret;
ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0); ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -296,6 +300,7 @@ MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
static struct platform_driver asoc_simple_card = { static struct platform_driver asoc_simple_card = {
.driver = { .driver = {
.name = "simple-scu-audio-card", .name = "simple-scu-audio-card",
.pm = &snd_soc_pm_ops,
.of_match_table = asoc_simple_of_match, .of_match_table = asoc_simple_of_match,
}, },
.probe = asoc_simple_card_probe, .probe = asoc_simple_card_probe,

View file

@ -151,7 +151,7 @@ static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
return ret; return ret;
} }
static struct snd_soc_dai_ops spdif_in_dai_ops = { static const struct snd_soc_dai_ops spdif_in_dai_ops = {
.shutdown = spdif_in_shutdown, .shutdown = spdif_in_shutdown,
.trigger = spdif_in_trigger, .trigger = spdif_in_trigger,
.hw_params = spdif_in_hw_params, .hw_params = spdif_in_hw_params,
@ -216,15 +216,15 @@ static int spdif_in_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) { if (!host)
dev_warn(&pdev->dev, "kzalloc fail\n");
return -ENOMEM; return -ENOMEM;
}
host->io_base = io_base; host->io_base = io_base;
host->irq = platform_get_irq(pdev, 0); host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) if (host->irq < 0) {
return -EINVAL; dev_warn(&pdev->dev, "failed to get IRQ: %d\n", host->irq);
return host->irq;
}
host->clk = devm_clk_get(&pdev->dev, NULL); host->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) if (IS_ERR(host->clk))

View file

@ -282,10 +282,8 @@ static int spdif_out_probe(struct platform_device *pdev)
int ret; int ret;
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) { if (!host)
dev_warn(&pdev->dev, "kzalloc fail\n");
return -ENOMEM; return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->io_base = devm_ioremap_resource(&pdev->dev, res); host->io_base = devm_ioremap_resource(&pdev->dev, res);

View file

@ -840,7 +840,7 @@ static int stm32_i2s_parse_dt(struct platform_device *pdev,
} }
/* Reset */ /* Reset */
rst = devm_reset_control_get(&pdev->dev, NULL); rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (!IS_ERR(rst)) { if (!IS_ERR(rst)) {
reset_control_assert(rst); reset_control_assert(rst);
udelay(2); udelay(2);

View file

@ -85,7 +85,7 @@ static int stm32_sai_probe(struct platform_device *pdev)
} }
/* reset */ /* reset */
rst = reset_control_get(&pdev->dev, NULL); rst = reset_control_get_exclusive(&pdev->dev, NULL);
if (!IS_ERR(rst)) { if (!IS_ERR(rst)) {
reset_control_assert(rst); reset_control_assert(rst);
udelay(2); udelay(2);

View file

@ -930,7 +930,7 @@ static int stm32_spdifrx_probe(struct platform_device *pdev)
return ret; return ret;
} }
rst = devm_reset_control_get(&pdev->dev, NULL); rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (!IS_ERR(rst)) { if (!IS_ERR(rst)) {
reset_control_assert(rst); reset_control_assert(rst);
udelay(2); udelay(2);

View file

@ -762,7 +762,7 @@ static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
{ "Mic1", NULL, "VMIC" }, { "Mic1", NULL, "VMIC" },
}; };
static struct snd_soc_codec_driver sun4i_codec_codec = { static const struct snd_soc_codec_driver sun4i_codec_codec = {
.component_driver = { .component_driver = {
.controls = sun4i_codec_controls, .controls = sun4i_codec_controls,
.num_controls = ARRAY_SIZE(sun4i_codec_controls), .num_controls = ARRAY_SIZE(sun4i_codec_controls),
@ -1068,7 +1068,7 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
{ "Right ADC", NULL, "Right ADC Mixer" }, { "Right ADC", NULL, "Right ADC Mixer" },
}; };
static struct snd_soc_codec_driver sun6i_codec_codec = { static const struct snd_soc_codec_driver sun6i_codec_codec = {
.component_driver = { .component_driver = {
.controls = sun6i_codec_codec_widgets, .controls = sun6i_codec_codec_widgets,
.num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets), .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets),
@ -1096,7 +1096,7 @@ static const struct snd_soc_dapm_widget sun8i_a23_codec_codec_widgets[] = {
}; };
static struct snd_soc_codec_driver sun8i_a23_codec_codec = { static const struct snd_soc_codec_driver sun8i_a23_codec_codec = {
.component_driver = { .component_driver = {
.controls = sun8i_a23_codec_codec_controls, .controls = sun8i_a23_codec_codec_controls,
.num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls), .num_controls = ARRAY_SIZE(sun8i_a23_codec_codec_controls),
@ -1171,9 +1171,8 @@ static int sun4i_codec_spk_event(struct snd_soc_dapm_widget *w,
{ {
struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card); struct sun4i_codec *scodec = snd_soc_card_get_drvdata(w->dapm->card);
if (scodec->gpio_pa) gpiod_set_value_cansleep(scodec->gpio_pa,
gpiod_set_value_cansleep(scodec->gpio_pa, !!SND_SOC_DAPM_EVENT_ON(event));
!!SND_SOC_DAPM_EVENT_ON(event));
return 0; return 0;
} }
@ -1574,7 +1573,8 @@ static int sun4i_codec_probe(struct platform_device *pdev)
} }
if (quirks->has_reset) { if (quirks->has_reset) {
scodec->rst = devm_reset_control_get(&pdev->dev, NULL); scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,
NULL);
if (IS_ERR(scodec->rst)) { if (IS_ERR(scodec->rst)) {
dev_err(&pdev->dev, "Failed to get reset control\n"); dev_err(&pdev->dev, "Failed to get reset control\n");
return PTR_ERR(scodec->rst); return PTR_ERR(scodec->rst);
@ -1655,7 +1655,6 @@ static int sun4i_codec_probe(struct platform_device *pdev)
goto err_unregister_codec; goto err_unregister_codec;
} }
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, scodec); snd_soc_card_set_drvdata(card, scodec);
ret = snd_soc_register_card(card); ret = snd_soc_register_card(card);

View file

@ -50,6 +50,8 @@
#define SUN4I_I2S_FMT0_FMT_RIGHT_J (2 << 0) #define SUN4I_I2S_FMT0_FMT_RIGHT_J (2 << 0)
#define SUN4I_I2S_FMT0_FMT_LEFT_J (1 << 0) #define SUN4I_I2S_FMT0_FMT_LEFT_J (1 << 0)
#define SUN4I_I2S_FMT0_FMT_I2S (0 << 0) #define SUN4I_I2S_FMT0_FMT_I2S (0 << 0)
#define SUN4I_I2S_FMT0_POLARITY_INVERTED (1)
#define SUN4I_I2S_FMT0_POLARITY_NORMAL (0)
#define SUN4I_I2S_FMT1_REG 0x08 #define SUN4I_I2S_FMT1_REG 0x08
#define SUN4I_I2S_FIFO_TX_REG 0x0c #define SUN4I_I2S_FIFO_TX_REG 0x0c
@ -82,7 +84,7 @@
#define SUN4I_I2S_TX_CNT_REG 0x2c #define SUN4I_I2S_TX_CNT_REG 0x2c
#define SUN4I_I2S_TX_CHAN_SEL_REG 0x30 #define SUN4I_I2S_TX_CHAN_SEL_REG 0x30
#define SUN4I_I2S_TX_CHAN_SEL(num_chan) (((num_chan) - 1) << 0) #define SUN4I_I2S_CHAN_SEL(num_chan) (((num_chan) - 1) << 0)
#define SUN4I_I2S_TX_CHAN_MAP_REG 0x34 #define SUN4I_I2S_TX_CHAN_MAP_REG 0x34
#define SUN4I_I2S_TX_CHAN_MAP(chan, sample) ((sample) << (chan << 2)) #define SUN4I_I2S_TX_CHAN_MAP(chan, sample) ((sample) << (chan << 2))
@ -90,6 +92,83 @@
#define SUN4I_I2S_RX_CHAN_SEL_REG 0x38 #define SUN4I_I2S_RX_CHAN_SEL_REG 0x38
#define SUN4I_I2S_RX_CHAN_MAP_REG 0x3c #define SUN4I_I2S_RX_CHAN_MAP_REG 0x3c
/* Defines required for sun8i-h3 support */
#define SUN8I_I2S_CTRL_BCLK_OUT BIT(18)
#define SUN8I_I2S_CTRL_LRCK_OUT BIT(17)
#define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8)
#define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8)
#define SUN8I_I2S_INT_STA_REG 0x0c
#define SUN8I_I2S_FIFO_TX_REG 0x20
#define SUN8I_I2S_CHAN_CFG_REG 0x30
#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK GENMASK(6, 4)
#define SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(chan) (chan - 1)
#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK GENMASK(2, 0)
#define SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(chan) (chan - 1)
#define SUN8I_I2S_TX_CHAN_MAP_REG 0x44
#define SUN8I_I2S_TX_CHAN_SEL_REG 0x34
#define SUN8I_I2S_TX_CHAN_OFFSET_MASK GENMASK(13, 11)
#define SUN8I_I2S_TX_CHAN_OFFSET(offset) (offset << 12)
#define SUN8I_I2S_TX_CHAN_EN_MASK GENMASK(11, 4)
#define SUN8I_I2S_TX_CHAN_EN(num_chan) (((1 << num_chan) - 1) << 4)
#define SUN8I_I2S_RX_CHAN_SEL_REG 0x54
#define SUN8I_I2S_RX_CHAN_MAP_REG 0x58
/**
* struct sun4i_i2s_quirks - Differences between SoC variants.
*
* @has_reset: SoC needs reset deasserted.
* @has_slave_select_bit: SoC has a bit to enable slave mode.
* @has_fmt_set_lrck_period: SoC requires lrclk period to be set.
* @has_chcfg: tx and rx slot number need to be set.
* @has_chsel_tx_chen: SoC requires that the tx channels are enabled.
* @has_chsel_offset: SoC uses offset for selecting dai operational mode.
* @reg_offset_txdata: offset of the tx fifo.
* @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.
* @field_fmt_bclk: regmap field to set clk polarity.
* @field_fmt_lrclk: regmap field to set frame polarity.
* @field_fmt_mode: regmap field to set the operational mode.
* @field_txchanmap: location of the tx channel mapping register.
* @field_rxchanmap: location of the rx channel mapping register.
* @field_txchansel: location of the tx channel select bit fields.
* @field_rxchansel: location of the rx channel select bit fields.
*/
struct sun4i_i2s_quirks {
bool has_reset;
bool has_slave_select_bit;
bool has_fmt_set_lrck_period;
bool has_chcfg;
bool has_chsel_tx_chen;
bool has_chsel_offset;
unsigned int reg_offset_txdata; /* TX FIFO */
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;
struct reg_field field_fmt_wss;
struct reg_field field_fmt_sr;
struct reg_field field_fmt_bclk;
struct reg_field field_fmt_lrclk;
struct reg_field field_fmt_mode;
struct reg_field field_txchanmap;
struct reg_field field_rxchanmap;
struct reg_field field_txchansel;
struct reg_field field_rxchansel;
};
struct sun4i_i2s { struct sun4i_i2s {
struct clk *bus_clk; struct clk *bus_clk;
struct clk *mod_clk; struct clk *mod_clk;
@ -100,6 +179,20 @@ struct sun4i_i2s {
struct snd_dmaengine_dai_dma_data capture_dma_data; struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data; struct snd_dmaengine_dai_dma_data playback_dma_data;
/* Register fields for i2s */
struct regmap_field *field_clkdiv_mclk_en;
struct regmap_field *field_fmt_wss;
struct regmap_field *field_fmt_sr;
struct regmap_field *field_fmt_bclk;
struct regmap_field *field_fmt_lrclk;
struct regmap_field *field_fmt_mode;
struct regmap_field *field_txchanmap;
struct regmap_field *field_rxchanmap;
struct regmap_field *field_txchansel;
struct regmap_field *field_rxchansel;
const struct sun4i_i2s_quirks *variant;
}; };
struct sun4i_i2s_clk_div { struct sun4i_i2s_clk_div {
@ -114,6 +207,7 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
{ .div = 8, .val = 3 }, { .div = 8, .val = 3 },
{ .div = 12, .val = 4 }, { .div = 12, .val = 4 },
{ .div = 16, .val = 5 }, { .div = 16, .val = 5 },
/* TODO - extend divide ratio supported by newer SoCs */
}; };
static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = { static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
@ -125,6 +219,7 @@ static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
{ .div = 12, .val = 5 }, { .div = 12, .val = 5 },
{ .div = 16, .val = 6 }, { .div = 16, .val = 6 },
{ .div = 24, .val = 7 }, { .div = 24, .val = 7 },
/* TODO - extend divide ratio supported by newer SoCs */
}; };
static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s, static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
@ -226,10 +321,21 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
if (mclk_div < 0) if (mclk_div < 0)
return -EINVAL; return -EINVAL;
/* Adjust the clock division values if needed */
bclk_div += i2s->variant->bclk_offset;
mclk_div += i2s->variant->mclk_offset;
regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG, regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
SUN4I_I2S_CLK_DIV_BCLK(bclk_div) | SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
SUN4I_I2S_CLK_DIV_MCLK(mclk_div) | SUN4I_I2S_CLK_DIV_MCLK(mclk_div));
SUN4I_I2S_CLK_DIV_MCLK_EN);
regmap_field_write(i2s->field_clkdiv_mclk_en, 1);
/* Set sync period */
if (i2s->variant->has_fmt_set_lrck_period)
regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
SUN8I_I2S_FMT0_LRCK_PERIOD_MASK,
SUN8I_I2S_FMT0_LRCK_PERIOD(32));
return 0; return 0;
} }
@ -239,12 +345,38 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
int sr, wss; int sr, wss, channels;
u32 width; u32 width;
if (params_channels(params) != 2) channels = params_channels(params);
if (channels != 2)
return -EINVAL; return -EINVAL;
if (i2s->variant->has_chcfg) {
regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK,
SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels));
regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK,
SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels));
}
/* Map the channels for playback and capture */
regmap_field_write(i2s->field_txchanmap, 0x76543210);
regmap_field_write(i2s->field_rxchanmap, 0x00003210);
/* Configure the channels */
regmap_field_write(i2s->field_txchansel,
SUN4I_I2S_CHAN_SEL(params_channels(params)));
regmap_field_write(i2s->field_rxchansel,
SUN4I_I2S_CHAN_SEL(params_channels(params)));
if (i2s->variant->has_chsel_tx_chen)
regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
SUN8I_I2S_TX_CHAN_EN_MASK,
SUN8I_I2S_TX_CHAN_EN(channels));
switch (params_physical_width(params)) { switch (params_physical_width(params)) {
case 16: case 16:
width = DMA_SLAVE_BUSWIDTH_2_BYTES; width = DMA_SLAVE_BUSWIDTH_2_BYTES;
@ -264,9 +396,10 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL; return -EINVAL;
} }
regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG, regmap_field_write(i2s->field_fmt_wss,
SUN4I_I2S_FMT0_WSS_MASK | SUN4I_I2S_FMT0_SR_MASK, wss + i2s->variant->fmt_offset);
SUN4I_I2S_FMT0_WSS(wss) | SUN4I_I2S_FMT0_SR(sr)); regmap_field_write(i2s->field_fmt_sr,
sr + i2s->variant->fmt_offset);
return sun4i_i2s_set_clk_rate(i2s, params_rate(params), return sun4i_i2s_set_clk_rate(i2s, params_rate(params),
params_width(params)); params_width(params));
@ -276,11 +409,15 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{ {
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
u32 val; u32 val;
u32 offset = 0;
u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL;
/* DAI Mode */ /* DAI Mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: case SND_SOC_DAIFMT_I2S:
val = SUN4I_I2S_FMT0_FMT_I2S; val = SUN4I_I2S_FMT0_FMT_I2S;
offset = 1;
break; break;
case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_LEFT_J:
val = SUN4I_I2S_FMT0_FMT_LEFT_J; val = SUN4I_I2S_FMT0_FMT_LEFT_J;
@ -292,59 +429,89 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL; return -EINVAL;
} }
regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG, if (i2s->variant->has_chsel_offset) {
SUN4I_I2S_FMT0_FMT_MASK, /*
val); * offset being set indicates that we're connected to an i2s
* device, however offset is only used on the sun8i block and
* i2s shares the same setting with the LJ format. Increment
* val so that the bit to value to write is correct.
*/
if (offset > 0)
val++;
/* blck offset determines whether i2s or LJ */
regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG,
SUN8I_I2S_TX_CHAN_OFFSET_MASK,
SUN8I_I2S_TX_CHAN_OFFSET(offset));
}
regmap_field_write(i2s->field_fmt_mode, val);
/* DAI clock polarity */ /* DAI clock polarity */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) { switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_IB_IF: case SND_SOC_DAIFMT_IB_IF:
/* Invert both clocks */ /* Invert both clocks */
val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED | bclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED;
SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED; lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED;
break; break;
case SND_SOC_DAIFMT_IB_NF: case SND_SOC_DAIFMT_IB_NF:
/* Invert bit clock */ /* Invert bit clock */
val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED | bclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED;
SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
break; break;
case SND_SOC_DAIFMT_NB_IF: case SND_SOC_DAIFMT_NB_IF:
/* Invert frame clock */ /* Invert frame clock */
val = SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED | lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED;
SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL;
break; break;
case SND_SOC_DAIFMT_NB_NF: case SND_SOC_DAIFMT_NB_NF:
/* Nothing to do for both normal cases */
val = SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL |
SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG, regmap_field_write(i2s->field_fmt_bclk, bclk_polarity);
SUN4I_I2S_FMT0_BCLK_POLARITY_MASK | regmap_field_write(i2s->field_fmt_lrclk, lrclk_polarity);
SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK,
val);
/* DAI clock master masks */ if (i2s->variant->has_slave_select_bit) {
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { /* DAI clock master masks */
case SND_SOC_DAIFMT_CBS_CFS: switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
/* BCLK and LRCLK master */ case SND_SOC_DAIFMT_CBS_CFS:
val = SUN4I_I2S_CTRL_MODE_MASTER; /* BCLK and LRCLK master */
break; val = SUN4I_I2S_CTRL_MODE_MASTER;
case SND_SOC_DAIFMT_CBM_CFM: break;
/* BCLK and LRCLK slave */ case SND_SOC_DAIFMT_CBM_CFM:
val = SUN4I_I2S_CTRL_MODE_SLAVE; /* BCLK and LRCLK slave */
break; val = SUN4I_I2S_CTRL_MODE_SLAVE;
default: break;
return -EINVAL; default:
return -EINVAL;
}
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
SUN4I_I2S_CTRL_MODE_MASK,
val);
} else {
/*
* The newer i2s block does not have a slave select bit,
* instead the clk pins are configured as inputs.
*/
/* DAI clock master masks */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
/* BCLK and LRCLK master */
val = SUN8I_I2S_CTRL_BCLK_OUT |
SUN8I_I2S_CTRL_LRCK_OUT;
break;
case SND_SOC_DAIFMT_CBM_CFM:
/* BCLK and LRCLK slave */
val = 0;
break;
default:
return -EINVAL;
}
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
SUN8I_I2S_CTRL_BCLK_OUT |
SUN8I_I2S_CTRL_LRCK_OUT,
val);
} }
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
SUN4I_I2S_CTRL_MODE_MASK,
val);
/* Set significant bits in our FIFOs */ /* Set significant bits in our FIFOs */
regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG, regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK | SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK |
@ -459,21 +626,14 @@ static int sun4i_i2s_startup(struct snd_pcm_substream *substream,
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
/* Enable the whole hardware block */ /* Enable the whole hardware block */
regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
SUN4I_I2S_CTRL_GL_EN); SUN4I_I2S_CTRL_GL_EN, SUN4I_I2S_CTRL_GL_EN);
/* Enable the first output line */ /* Enable the first output line */
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
SUN4I_I2S_CTRL_SDO_EN_MASK, SUN4I_I2S_CTRL_SDO_EN_MASK,
SUN4I_I2S_CTRL_SDO_EN(0)); SUN4I_I2S_CTRL_SDO_EN(0));
/* Enable the first two channels */
regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
SUN4I_I2S_TX_CHAN_SEL(2));
/* Map them to the two first samples coming in */
regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG,
SUN4I_I2S_TX_CHAN_MAP(0, 0) | SUN4I_I2S_TX_CHAN_MAP(1, 1));
return clk_prepare_enable(i2s->mod_clk); return clk_prepare_enable(i2s->mod_clk);
} }
@ -490,7 +650,8 @@ static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream,
SUN4I_I2S_CTRL_SDO_EN_MASK, 0); SUN4I_I2S_CTRL_SDO_EN_MASK, 0);
/* Disable the whole hardware block */ /* Disable the whole hardware block */
regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0); regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
SUN4I_I2S_CTRL_GL_EN, 0);
} }
static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
@ -589,6 +750,27 @@ static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
} }
} }
static bool sun8i_i2s_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SUN8I_I2S_FIFO_TX_REG:
return false;
default:
return true;
}
}
static bool sun8i_i2s_volatile_reg(struct device *dev, unsigned int reg)
{
if (reg == SUN8I_I2S_INT_STA_REG)
return true;
if (reg == SUN8I_I2S_FIFO_TX_REG)
return false;
return sun4i_i2s_volatile_reg(dev, reg);
}
static const struct reg_default sun4i_i2s_reg_defaults[] = { static const struct reg_default sun4i_i2s_reg_defaults[] = {
{ SUN4I_I2S_CTRL_REG, 0x00000000 }, { SUN4I_I2S_CTRL_REG, 0x00000000 },
{ SUN4I_I2S_FMT0_REG, 0x0000000c }, { SUN4I_I2S_FMT0_REG, 0x0000000c },
@ -602,6 +784,20 @@ static const struct reg_default sun4i_i2s_reg_defaults[] = {
{ SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210 }, { SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210 },
}; };
static const struct reg_default sun8i_i2s_reg_defaults[] = {
{ SUN4I_I2S_CTRL_REG, 0x00060000 },
{ SUN4I_I2S_FMT0_REG, 0x00000033 },
{ SUN4I_I2S_FMT1_REG, 0x00000030 },
{ SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
{ SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
{ SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
{ SUN8I_I2S_CHAN_CFG_REG, 0x00000000 },
{ SUN8I_I2S_TX_CHAN_SEL_REG, 0x00000000 },
{ SUN8I_I2S_TX_CHAN_MAP_REG, 0x00000000 },
{ SUN8I_I2S_RX_CHAN_SEL_REG, 0x00000000 },
{ SUN8I_I2S_RX_CHAN_MAP_REG, 0x00000000 },
};
static const struct regmap_config sun4i_i2s_regmap_config = { static const struct regmap_config sun4i_i2s_regmap_config = {
.reg_bits = 32, .reg_bits = 32,
.reg_stride = 4, .reg_stride = 4,
@ -616,6 +812,19 @@ static const struct regmap_config sun4i_i2s_regmap_config = {
.volatile_reg = sun4i_i2s_volatile_reg, .volatile_reg = sun4i_i2s_volatile_reg,
}; };
static const struct regmap_config sun8i_i2s_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = SUN8I_I2S_RX_CHAN_MAP_REG,
.cache_type = REGCACHE_FLAT,
.reg_defaults = sun8i_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sun8i_i2s_reg_defaults),
.writeable_reg = sun4i_i2s_wr_reg,
.readable_reg = sun8i_i2s_rd_reg,
.volatile_reg = sun8i_i2s_volatile_reg,
};
static int sun4i_i2s_runtime_resume(struct device *dev) static int sun4i_i2s_runtime_resume(struct device *dev)
{ {
struct sun4i_i2s *i2s = dev_get_drvdata(dev); struct sun4i_i2s *i2s = dev_get_drvdata(dev);
@ -654,22 +863,129 @@ static int sun4i_i2s_runtime_suspend(struct device *dev)
return 0; return 0;
} }
struct sun4i_i2s_quirks {
bool has_reset;
};
static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = { static const struct sun4i_i2s_quirks sun4i_a10_i2s_quirks = {
.has_reset = false, .has_reset = false,
.reg_offset_txdata = SUN4I_I2S_FIFO_TX_REG,
.sun4i_i2s_regmap = &sun4i_i2s_regmap_config,
.field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7),
.field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
.field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.has_slave_select_bit = true,
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.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),
}; };
static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
.has_reset = true, .has_reset = true,
.reg_offset_txdata = SUN4I_I2S_FIFO_TX_REG,
.sun4i_i2s_regmap = &sun4i_i2s_regmap_config,
.field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7),
.field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
.field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.has_slave_select_bit = true,
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.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),
}; };
static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.has_reset = true,
.reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
.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,
.has_chsel_offset = true,
.field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8),
.field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2),
.field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19),
.field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5),
.field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31),
.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),
};
static int sun4i_i2s_init_regmap_fields(struct device *dev,
struct sun4i_i2s *i2s)
{
i2s->field_clkdiv_mclk_en =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_clkdiv_mclk_en);
if (IS_ERR(i2s->field_clkdiv_mclk_en))
return PTR_ERR(i2s->field_clkdiv_mclk_en);
i2s->field_fmt_wss =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_fmt_wss);
if (IS_ERR(i2s->field_fmt_wss))
return PTR_ERR(i2s->field_fmt_wss);
i2s->field_fmt_sr =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_fmt_sr);
if (IS_ERR(i2s->field_fmt_sr))
return PTR_ERR(i2s->field_fmt_sr);
i2s->field_fmt_bclk =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_fmt_bclk);
if (IS_ERR(i2s->field_fmt_bclk))
return PTR_ERR(i2s->field_fmt_bclk);
i2s->field_fmt_lrclk =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_fmt_lrclk);
if (IS_ERR(i2s->field_fmt_lrclk))
return PTR_ERR(i2s->field_fmt_lrclk);
i2s->field_fmt_mode =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_fmt_mode);
if (IS_ERR(i2s->field_fmt_mode))
return PTR_ERR(i2s->field_fmt_mode);
i2s->field_txchanmap =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_txchanmap);
if (IS_ERR(i2s->field_txchanmap))
return PTR_ERR(i2s->field_txchanmap);
i2s->field_rxchanmap =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_rxchanmap);
if (IS_ERR(i2s->field_rxchanmap))
return PTR_ERR(i2s->field_rxchanmap);
i2s->field_txchansel =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_txchansel);
if (IS_ERR(i2s->field_txchansel))
return PTR_ERR(i2s->field_txchansel);
i2s->field_rxchansel =
devm_regmap_field_alloc(dev, i2s->regmap,
i2s->variant->field_rxchansel);
return PTR_ERR_OR_ZERO(i2s->field_rxchansel);
}
static int sun4i_i2s_probe(struct platform_device *pdev) static int sun4i_i2s_probe(struct platform_device *pdev)
{ {
struct sun4i_i2s *i2s; struct sun4i_i2s *i2s;
const struct sun4i_i2s_quirks *quirks;
struct resource *res; struct resource *res;
void __iomem *regs; void __iomem *regs;
int irq, ret; int irq, ret;
@ -690,8 +1006,8 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
return irq; return irq;
} }
quirks = of_device_get_match_data(&pdev->dev); i2s->variant = of_device_get_match_data(&pdev->dev);
if (!quirks) { if (!i2s->variant) {
dev_err(&pdev->dev, "Failed to determine the quirks to use\n"); dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
return -ENODEV; return -ENODEV;
} }
@ -703,7 +1019,7 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
} }
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs, i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&sun4i_i2s_regmap_config); i2s->variant->sun4i_i2s_regmap);
if (IS_ERR(i2s->regmap)) { if (IS_ERR(i2s->regmap)) {
dev_err(&pdev->dev, "Regmap initialisation failed\n"); dev_err(&pdev->dev, "Regmap initialisation failed\n");
return PTR_ERR(i2s->regmap); return PTR_ERR(i2s->regmap);
@ -715,8 +1031,8 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
return PTR_ERR(i2s->mod_clk); return PTR_ERR(i2s->mod_clk);
} }
if (quirks->has_reset) { if (i2s->variant->has_reset) {
i2s->rst = devm_reset_control_get(&pdev->dev, NULL); i2s->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(i2s->rst)) { if (IS_ERR(i2s->rst)) {
dev_err(&pdev->dev, "Failed to get reset control\n"); dev_err(&pdev->dev, "Failed to get reset control\n");
return PTR_ERR(i2s->rst); return PTR_ERR(i2s->rst);
@ -732,7 +1048,8 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
} }
} }
i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG; i2s->playback_dma_data.addr = res->start +
i2s->variant->reg_offset_txdata;
i2s->playback_dma_data.maxburst = 8; i2s->playback_dma_data.maxburst = 8;
i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG; i2s->capture_dma_data.addr = res->start + SUN4I_I2S_FIFO_RX_REG;
@ -759,6 +1076,12 @@ static int sun4i_i2s_probe(struct platform_device *pdev)
goto err_suspend; goto err_suspend;
} }
ret = sun4i_i2s_init_regmap_fields(&pdev->dev, i2s);
if (ret) {
dev_err(&pdev->dev, "Could not initialise regmap fields\n");
goto err_suspend;
}
return 0; return 0;
err_suspend: err_suspend:
@ -797,6 +1120,10 @@ static const struct of_device_id sun4i_i2s_match[] = {
.compatible = "allwinner,sun6i-a31-i2s", .compatible = "allwinner,sun6i-a31-i2s",
.data = &sun6i_a31_i2s_quirks, .data = &sun6i_a31_i2s_quirks,
}, },
{
.compatible = "allwinner,sun8i-h3-i2s",
.data = &sun8i_h3_i2s_quirks,
},
{} {}
}; };
MODULE_DEVICE_TABLE(of, sun4i_i2s_match); MODULE_DEVICE_TABLE(of, sun4i_i2s_match);

View file

@ -458,11 +458,16 @@ static int sun4i_spdif_runtime_suspend(struct device *dev)
static int sun4i_spdif_runtime_resume(struct device *dev) static int sun4i_spdif_runtime_resume(struct device *dev)
{ {
struct sun4i_spdif_dev *host = dev_get_drvdata(dev); struct sun4i_spdif_dev *host = dev_get_drvdata(dev);
int ret;
clk_prepare_enable(host->spdif_clk); ret = clk_prepare_enable(host->spdif_clk);
clk_prepare_enable(host->apb_clk); if (ret)
return ret;
ret = clk_prepare_enable(host->apb_clk);
if (ret)
clk_disable_unprepare(host->spdif_clk);
return 0; return ret;
} }
static int sun4i_spdif_probe(struct platform_device *pdev) static int sun4i_spdif_probe(struct platform_device *pdev)
@ -520,7 +525,8 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host); platform_set_drvdata(pdev, host);
if (quirks->has_reset) { if (quirks->has_reset) {
host->rst = devm_reset_control_get_optional(&pdev->dev, NULL); host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
NULL);
if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) { if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER; ret = -EPROBE_DEFER;
dev_err(&pdev->dev, "Failed to get reset: %d\n", ret); dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);

View file

@ -341,7 +341,7 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = {
"AIF1 Slot 0 Right"}, "AIF1 Slot 0 Right"},
}; };
static struct snd_soc_dai_ops sun8i_codec_dai_ops = { static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
.hw_params = sun8i_codec_hw_params, .hw_params = sun8i_codec_hw_params,
.set_fmt = sun8i_set_fmt, .set_fmt = sun8i_set_fmt,
}; };
@ -360,7 +360,7 @@ static struct snd_soc_dai_driver sun8i_codec_dai = {
.ops = &sun8i_codec_dai_ops, .ops = &sun8i_codec_dai_ops,
}; };
static struct snd_soc_codec_driver sun8i_soc_codec = { static const struct snd_soc_codec_driver sun8i_soc_codec = {
.component_driver = { .component_driver = {
.dapm_widgets = sun8i_codec_dapm_widgets, .dapm_widgets = sun8i_codec_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets), .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets),