Unverified Commit 94e19f49 authored by Mark Brown's avatar Mark Brown
Browse files

ASoC: cleanups and improvements for jz4740-i2s

Merge series from Aidan MacDonald <aidanmacdonald.0x0@gmail.com>:

This series is a preparatory cleanup of the jz4740-i2s driver before
adding support for a new SoC. The two improvements are lifting
unnecessary restrictions on sample rates and formats -- the existing
ones appear to be derived from the limitations of the JZ4740's internal
codec and don't reflect the actual capabilities of the I2S controller.

I'm unable to test the series on any JZ47xx SoCs, but I have tested
on an X1000 (which is the SoC I'll be adding in a followup series).
parents 07b16192 4e02fd62
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ config SND_JZ4740_SOC_I2S
	tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
	depends on MIPS || COMPILE_TEST
	depends on HAS_IOMEM
	select REGMAP_MMIO
	select SND_SOC_GENERIC_DMAENGINE_PCM
	help
	  Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
+242 −213
Original line number Diff line number Diff line
@@ -3,19 +3,19 @@
 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
 */

#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>

#include <linux/clk.h>
#include <linux/delay.h>

#include <linux/dma-mapping.h>

#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -33,8 +33,6 @@
#define JZ_REG_AIC_CLK_DIV	0x30
#define JZ_REG_AIC_FIFO		0x34

#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_MASK (0xf << 12)
#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_MASK (0xf <<  8)
#define JZ_AIC_CONF_OVERFLOW_PLAY_LAST	BIT(6)
#define JZ_AIC_CONF_INTERNAL_CODEC	BIT(5)
#define JZ_AIC_CONF_I2S			BIT(4)
@@ -43,19 +41,15 @@
#define JZ_AIC_CONF_SYNC_CLK_MASTER	BIT(1)
#define JZ_AIC_CONF_ENABLE		BIT(0)

#define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12
#define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8
#define JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24
#define JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16

#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19)
#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16)
#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE	GENMASK(21, 19)
#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE	GENMASK(18, 16)
#define JZ_AIC_CTRL_ENABLE_RX_DMA	BIT(15)
#define JZ_AIC_CTRL_ENABLE_TX_DMA	BIT(14)
#define JZ_AIC_CTRL_MONO_TO_STEREO	BIT(11)
#define JZ_AIC_CTRL_SWITCH_ENDIANNESS	BIT(10)
#define JZ_AIC_CTRL_SIGNED_TO_UNSIGNED	BIT(9)
#define JZ_AIC_CTRL_FLUSH		BIT(8)
#define JZ_AIC_CTRL_TFLUSH		BIT(8)
#define JZ_AIC_CTRL_RFLUSH		BIT(7)
#define JZ_AIC_CTRL_ENABLE_ROR_INT	BIT(6)
#define JZ_AIC_CTRL_ENABLE_TUR_INT	BIT(5)
#define JZ_AIC_CTRL_ENABLE_RFS_INT	BIT(4)
@@ -64,9 +58,6 @@
#define JZ_AIC_CTRL_ENABLE_PLAYBACK	BIT(1)
#define JZ_AIC_CTRL_ENABLE_CAPTURE	BIT(0)

#define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET 19
#define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET  16

#define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK	BIT(12)
#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK	BIT(13)
#define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK	BIT(4)
@@ -74,26 +65,24 @@

#define JZ_AIC_I2S_STATUS_BUSY		BIT(2)

#define JZ_AIC_CLK_DIV_MASK 0xf
#define I2SDIV_DV_SHIFT 0
#define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT)
#define I2SDIV_IDV_SHIFT 8
#define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT)

enum jz47xx_i2s_version {
	JZ_I2S_JZ4740,
	JZ_I2S_JZ4760,
	JZ_I2S_JZ4770,
	JZ_I2S_JZ4780,
};

struct i2s_soc_info {
	enum jz47xx_i2s_version version;
	struct snd_soc_dai_driver *dai;

	struct reg_field field_rx_fifo_thresh;
	struct reg_field field_tx_fifo_thresh;
	struct reg_field field_i2sdiv_capture;
	struct reg_field field_i2sdiv_playback;

	bool shared_fifo_flush;
};

struct jz4740_i2s {
	void __iomem *base;
	struct regmap *regmap;

	struct regmap_field *field_rx_fifo_thresh;
	struct regmap_field *field_tx_fifo_thresh;
	struct regmap_field *field_i2sdiv_capture;
	struct regmap_field *field_i2sdiv_playback;

	struct clk *clk_aic;
	struct clk *clk_i2s;
@@ -104,40 +93,41 @@ struct jz4740_i2s {
	const struct i2s_soc_info *soc_info;
};

static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
	unsigned int reg)
{
	return readl(i2s->base + reg);
}

static inline void jz4740_i2s_write(const struct jz4740_i2s *i2s,
	unsigned int reg, uint32_t value)
{
	writel(value, i2s->base + reg);
}

static int jz4740_i2s_startup(struct snd_pcm_substream *substream,
	struct snd_soc_dai *dai)
{
	struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
	uint32_t conf, ctrl;
	int ret;

	/*
	 * When we can flush FIFOs independently, only flush the FIFO
	 * that is starting up. We can do this when the DAI is active
	 * because it does not disturb other active substreams.
	 */
	if (!i2s->soc_info->shared_fifo_flush) {
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
			regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH);
		else
			regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_RFLUSH);
	}

	if (snd_soc_dai_active(dai))
		return 0;

	ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
	ctrl |= JZ_AIC_CTRL_FLUSH;
	jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
	/*
	 * When there is a shared flush bit for both FIFOs, the TFLUSH
	 * bit flushes both FIFOs. Flushing while the DAI is active would
	 * cause FIFO underruns in other active substreams so we have to
	 * guard this behind the snd_soc_dai_active() check.
	 */
	if (i2s->soc_info->shared_fifo_flush)
		regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, JZ_AIC_CTRL_TFLUSH);

	ret = clk_prepare_enable(i2s->clk_i2s);
	if (ret)
		return ret;

	conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
	conf |= JZ_AIC_CONF_ENABLE;
	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);

	regmap_set_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE);
	return 0;
}

@@ -145,14 +135,11 @@ static void jz4740_i2s_shutdown(struct snd_pcm_substream *substream,
	struct snd_soc_dai *dai)
{
	struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
	uint32_t conf;

	if (snd_soc_dai_active(dai))
		return;

	conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
	conf &= ~JZ_AIC_CONF_ENABLE;
	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
	regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE);

	clk_disable_unprepare(i2s->clk_i2s);
}
@@ -161,8 +148,6 @@ static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
	struct snd_soc_dai *dai)
{
	struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);

	uint32_t ctrl;
	uint32_t mask;

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -170,38 +155,30 @@ static int jz4740_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
	else
		mask = JZ_AIC_CTRL_ENABLE_CAPTURE | JZ_AIC_CTRL_ENABLE_RX_DMA;

	ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		ctrl |= mask;
		regmap_set_bits(i2s->regmap, JZ_REG_AIC_CTRL, mask);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		ctrl &= ~mask;
		regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CTRL, mask);
		break;
	default:
		return -EINVAL;
	}

	jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);

	return 0;
}

static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
	struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);

	uint32_t format = 0;
	uint32_t conf;

	conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);

	conf &= ~(JZ_AIC_CONF_BIT_CLK_MASTER | JZ_AIC_CONF_SYNC_CLK_MASTER);
	const unsigned int conf_mask = JZ_AIC_CONF_BIT_CLK_MASTER |
				       JZ_AIC_CONF_SYNC_CLK_MASTER;
	unsigned int conf = 0, format = 0;

	switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
	case SND_SOC_DAIFMT_BP_FP:
@@ -237,8 +214,8 @@ static int jz4740_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
		return -EINVAL;
	}

	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
	jz4740_i2s_write(i2s, JZ_REG_AIC_I2S_FMT, format);
	regmap_update_bits(i2s->regmap, JZ_REG_AIC_CONF, conf_mask, conf);
	regmap_write(i2s->regmap, JZ_REG_AIC_I2S_FMT, format);

	return 0;
}
@@ -247,51 +224,51 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
	struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
	struct regmap_field *div_field;
	unsigned int sample_size;
	uint32_t ctrl, div_reg;
	uint32_t ctrl;
	int div;

	ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
	regmap_read(i2s->regmap, JZ_REG_AIC_CTRL, &ctrl);

	div_reg = jz4740_i2s_read(i2s, JZ_REG_AIC_CLK_DIV);
	div = clk_get_rate(i2s->clk_i2s) / (64 * params_rate(params));

	switch (params_format(params)) {
	case SNDRV_PCM_FORMAT_S8:
		sample_size = 0;
		break;
	case SNDRV_PCM_FORMAT_S16:
	case SNDRV_PCM_FORMAT_S16_LE:
		sample_size = 1;
		break;
	case SNDRV_PCM_FORMAT_S20_LE:
		sample_size = 3;
		break;
	case SNDRV_PCM_FORMAT_S24_LE:
		sample_size = 4;
		break;
	default:
		return -EINVAL;
	}

	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
		ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK;
		ctrl |= sample_size << JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_OFFSET;
		ctrl &= ~JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE;
		ctrl |= FIELD_PREP(JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE, sample_size);

		if (params_channels(params) == 1)
			ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
		else
			ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;

		div_reg &= ~I2SDIV_DV_MASK;
		div_reg |= (div - 1) << I2SDIV_DV_SHIFT;
		div_field = i2s->field_i2sdiv_playback;
	} else {
		ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
		ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
		ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE;
		ctrl |= FIELD_PREP(JZ_AIC_CTRL_INPUT_SAMPLE_SIZE, sample_size);

		if (i2s->soc_info->version >= JZ_I2S_JZ4770) {
			div_reg &= ~I2SDIV_IDV_MASK;
			div_reg |= (div - 1) << I2SDIV_IDV_SHIFT;
		} else {
			div_reg &= ~I2SDIV_DV_MASK;
			div_reg |= (div - 1) << I2SDIV_DV_SHIFT;
		}
		div_field = i2s->field_i2sdiv_capture;
	}

	jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
	jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div_reg);
	regmap_write(i2s->regmap, JZ_REG_AIC_CTRL, ctrl);
	regmap_field_write(div_field, div - 1);

	return 0;
}
@@ -325,87 +302,13 @@ static int jz4740_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
	return ret;
}

static int jz4740_i2s_suspend(struct snd_soc_component *component)
{
	struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);
	uint32_t conf;

	if (snd_soc_component_active(component)) {
		conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
		conf &= ~JZ_AIC_CONF_ENABLE;
		jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);

		clk_disable_unprepare(i2s->clk_i2s);
	}

	clk_disable_unprepare(i2s->clk_aic);

	return 0;
}

static int jz4740_i2s_resume(struct snd_soc_component *component)
{
	struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);
	uint32_t conf;
	int ret;

	ret = clk_prepare_enable(i2s->clk_aic);
	if (ret)
		return ret;

	if (snd_soc_component_active(component)) {
		ret = clk_prepare_enable(i2s->clk_i2s);
		if (ret) {
			clk_disable_unprepare(i2s->clk_aic);
			return ret;
		}

		conf = jz4740_i2s_read(i2s, JZ_REG_AIC_CONF);
		conf |= JZ_AIC_CONF_ENABLE;
		jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);
	}

	return 0;
}

static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
{
	struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
	uint32_t conf;
	int ret;

	ret = clk_prepare_enable(i2s->clk_aic);
	if (ret)
		return ret;

	snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
		&i2s->capture_dma_data);

	if (i2s->soc_info->version >= JZ_I2S_JZ4760) {
		conf = (7 << JZ4760_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
			(8 << JZ4760_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
			JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
			JZ_AIC_CONF_I2S |
			JZ_AIC_CONF_INTERNAL_CODEC;
	} else {
		conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
			(8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
			JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
			JZ_AIC_CONF_I2S |
			JZ_AIC_CONF_INTERNAL_CODEC;
	}

	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);
	jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf);

	return 0;
}

static int jz4740_i2s_dai_remove(struct snd_soc_dai *dai)
{
	struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);

	clk_disable_unprepare(i2s->clk_aic);
	return 0;
}

@@ -419,21 +322,22 @@ static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
};

#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
		SNDRV_PCM_FMTBIT_S16_LE)
			 SNDRV_PCM_FMTBIT_S16_LE | \
			 SNDRV_PCM_FMTBIT_S20_LE | \
			 SNDRV_PCM_FMTBIT_S24_LE)

static struct snd_soc_dai_driver jz4740_i2s_dai = {
	.probe = jz4740_i2s_dai_probe,
	.remove = jz4740_i2s_dai_remove,
	.playback = {
		.channels_min = 1,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_8000_48000,
		.rates = SNDRV_PCM_RATE_CONTINUOUS,
		.formats = JZ4740_I2S_FMTS,
	},
	.capture = {
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_8000_48000,
		.rates = SNDRV_PCM_RATE_CONTINUOUS,
		.formats = JZ4740_I2S_FMTS,
	},
	.symmetric_rate = 1,
@@ -441,45 +345,123 @@ static struct snd_soc_dai_driver jz4740_i2s_dai = {
};

static const struct i2s_soc_info jz4740_i2s_soc_info = {
	.version = JZ_I2S_JZ4740,
	.dai			= &jz4740_i2s_dai,
	.field_rx_fifo_thresh	= REG_FIELD(JZ_REG_AIC_CONF, 12, 15),
	.field_tx_fifo_thresh	= REG_FIELD(JZ_REG_AIC_CONF, 8, 11),
	.field_i2sdiv_capture	= REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
	.field_i2sdiv_playback	= REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
	.shared_fifo_flush	= true,
};

static const struct i2s_soc_info jz4760_i2s_soc_info = {
	.version = JZ_I2S_JZ4760,
	.dai			= &jz4740_i2s_dai,
	.field_rx_fifo_thresh	= REG_FIELD(JZ_REG_AIC_CONF, 24, 27),
	.field_tx_fifo_thresh	= REG_FIELD(JZ_REG_AIC_CONF, 16, 20),
	.field_i2sdiv_capture	= REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
	.field_i2sdiv_playback	= REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
};

static struct snd_soc_dai_driver jz4770_i2s_dai = {
	.probe = jz4740_i2s_dai_probe,
	.remove = jz4740_i2s_dai_remove,
	.playback = {
		.channels_min = 1,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_8000_48000,
		.rates = SNDRV_PCM_RATE_CONTINUOUS,
		.formats = JZ4740_I2S_FMTS,
	},
	.capture = {
		.channels_min = 2,
		.channels_max = 2,
		.rates = SNDRV_PCM_RATE_8000_48000,
		.rates = SNDRV_PCM_RATE_CONTINUOUS,
		.formats = JZ4740_I2S_FMTS,
	},
	.ops = &jz4740_i2s_dai_ops,
};

static const struct i2s_soc_info jz4770_i2s_soc_info = {
	.version = JZ_I2S_JZ4770,
	.dai			= &jz4770_i2s_dai,
	.field_rx_fifo_thresh	= REG_FIELD(JZ_REG_AIC_CONF, 24, 27),
	.field_tx_fifo_thresh	= REG_FIELD(JZ_REG_AIC_CONF, 16, 20),
	.field_i2sdiv_capture	= REG_FIELD(JZ_REG_AIC_CLK_DIV, 8, 11),
	.field_i2sdiv_playback	= REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
};

static const struct i2s_soc_info jz4780_i2s_soc_info = {
	.version = JZ_I2S_JZ4780,
	.dai			= &jz4770_i2s_dai,
	.field_rx_fifo_thresh	= REG_FIELD(JZ_REG_AIC_CONF, 24, 27),
	.field_tx_fifo_thresh	= REG_FIELD(JZ_REG_AIC_CONF, 16, 20),
	.field_i2sdiv_capture	= REG_FIELD(JZ_REG_AIC_CLK_DIV, 8, 11),
	.field_i2sdiv_playback	= REG_FIELD(JZ_REG_AIC_CLK_DIV, 0, 3),
};

static int jz4740_i2s_suspend(struct snd_soc_component *component)
{
	struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);

	if (snd_soc_component_active(component)) {
		regmap_clear_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE);
		clk_disable_unprepare(i2s->clk_i2s);
	}

	clk_disable_unprepare(i2s->clk_aic);

	return 0;
}

static int jz4740_i2s_resume(struct snd_soc_component *component)
{
	struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);
	int ret;

	ret = clk_prepare_enable(i2s->clk_aic);
	if (ret)
		return ret;

	if (snd_soc_component_active(component)) {
		ret = clk_prepare_enable(i2s->clk_i2s);
		if (ret) {
			clk_disable_unprepare(i2s->clk_aic);
			return ret;
		}

		regmap_set_bits(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_ENABLE);
	}

	return 0;
}

static int jz4740_i2s_probe(struct snd_soc_component *component)
{
	struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);
	int ret;

	ret = clk_prepare_enable(i2s->clk_aic);
	if (ret)
		return ret;

	regmap_write(i2s->regmap, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET);

	regmap_write(i2s->regmap, JZ_REG_AIC_CONF,
		     JZ_AIC_CONF_OVERFLOW_PLAY_LAST |
		     JZ_AIC_CONF_I2S | JZ_AIC_CONF_INTERNAL_CODEC);

	regmap_field_write(i2s->field_rx_fifo_thresh, 7);
	regmap_field_write(i2s->field_tx_fifo_thresh, 8);

	return 0;
}

static void jz4740_i2s_remove(struct snd_soc_component *component)
{
	struct jz4740_i2s *i2s = snd_soc_component_get_drvdata(component);

	clk_disable_unprepare(i2s->clk_aic);
}

static const struct snd_soc_component_driver jz4740_i2s_component = {
	.name			= "jz4740-i2s",
	.probe			= jz4740_i2s_probe,
	.remove			= jz4740_i2s_remove,
	.suspend		= jz4740_i2s_suspend,
	.resume			= jz4740_i2s_resume,
	.legacy_dai_naming	= 1,
@@ -494,11 +476,49 @@ static const struct of_device_id jz4740_of_matches[] = {
};
MODULE_DEVICE_TABLE(of, jz4740_of_matches);

static int jz4740_i2s_init_regmap_fields(struct device *dev,
					 struct jz4740_i2s *i2s)
{
	i2s->field_rx_fifo_thresh =
		devm_regmap_field_alloc(dev, i2s->regmap,
					i2s->soc_info->field_rx_fifo_thresh);
	if (IS_ERR(i2s->field_rx_fifo_thresh))
		return PTR_ERR(i2s->field_rx_fifo_thresh);

	i2s->field_tx_fifo_thresh =
		devm_regmap_field_alloc(dev, i2s->regmap,
					i2s->soc_info->field_tx_fifo_thresh);
	if (IS_ERR(i2s->field_tx_fifo_thresh))
		return PTR_ERR(i2s->field_tx_fifo_thresh);

	i2s->field_i2sdiv_capture =
		devm_regmap_field_alloc(dev, i2s->regmap,
					i2s->soc_info->field_i2sdiv_capture);
	if (IS_ERR(i2s->field_i2sdiv_capture))
		return PTR_ERR(i2s->field_i2sdiv_capture);

	i2s->field_i2sdiv_playback =
		devm_regmap_field_alloc(dev, i2s->regmap,
					i2s->soc_info->field_i2sdiv_playback);
	if (IS_ERR(i2s->field_i2sdiv_playback))
		return PTR_ERR(i2s->field_i2sdiv_playback);

	return 0;
}

static const struct regmap_config jz4740_i2s_regmap_config = {
	.reg_bits	= 32,
	.reg_stride	= 4,
	.val_bits	= 32,
	.max_register	= JZ_REG_AIC_FIFO,
};

static int jz4740_i2s_dev_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct jz4740_i2s *i2s;
	struct resource *mem;
	void __iomem *regs;
	int ret;

	i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
@@ -507,9 +527,9 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev)

	i2s->soc_info = device_get_match_data(dev);

	i2s->base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
	if (IS_ERR(i2s->base))
		return PTR_ERR(i2s->base);
	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
	if (IS_ERR(regs))
		return PTR_ERR(regs);

	i2s->playback_dma_data.maxburst = 16;
	i2s->playback_dma_data.addr = mem->start + JZ_REG_AIC_FIFO;
@@ -525,6 +545,15 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev)
	if (IS_ERR(i2s->clk_i2s))
		return PTR_ERR(i2s->clk_i2s);

	i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
					    &jz4740_i2s_regmap_config);
	if (IS_ERR(i2s->regmap))
		return PTR_ERR(i2s->regmap);

	ret = jz4740_i2s_init_regmap_fields(dev, i2s);
	if (ret)
		return ret;

	platform_set_drvdata(pdev, i2s);

	ret = devm_snd_soc_register_component(dev, &jz4740_i2s_component,