mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	ASoC: meson: aiu: add i2s and spdif support
Add support for the i2s and spdif audio outputs (AIU) found in the amlogic Gx SoC family Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> Link: https://lore.kernel.org/r/20200213155159.3235792-5-jbrunet@baylibre.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
		
							parent
							
								
									06b7282438
								
							
						
					
					
						commit
						6ae9ca9ce9
					
				
					 10 changed files with 1589 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -2,6 +2,13 @@
 | 
			
		|||
menu "ASoC support for Amlogic platforms"
 | 
			
		||||
	depends on ARCH_MESON || COMPILE_TEST
 | 
			
		||||
 | 
			
		||||
config SND_MESON_AIU
 | 
			
		||||
	tristate "Amlogic AIU"
 | 
			
		||||
	select SND_PCM_IEC958
 | 
			
		||||
	help
 | 
			
		||||
	  Select Y or M to add support for the Audio output subsystem found
 | 
			
		||||
	  in the Amlogic GX SoC family
 | 
			
		||||
 | 
			
		||||
config SND_MESON_AXG_FIFO
 | 
			
		||||
	tristate
 | 
			
		||||
	select REGMAP_MMIO
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,11 @@
 | 
			
		|||
# SPDX-License-Identifier: (GPL-2.0 OR MIT)
 | 
			
		||||
 | 
			
		||||
snd-soc-meson-aiu-objs := aiu.o
 | 
			
		||||
snd-soc-meson-aiu-objs += aiu-encoder-i2s.o
 | 
			
		||||
snd-soc-meson-aiu-objs += aiu-encoder-spdif.o
 | 
			
		||||
snd-soc-meson-aiu-objs += aiu-fifo.o
 | 
			
		||||
snd-soc-meson-aiu-objs += aiu-fifo-i2s.o
 | 
			
		||||
snd-soc-meson-aiu-objs += aiu-fifo-spdif.o
 | 
			
		||||
snd-soc-meson-axg-fifo-objs := axg-fifo.o
 | 
			
		||||
snd-soc-meson-axg-frddr-objs := axg-frddr.o
 | 
			
		||||
snd-soc-meson-axg-toddr-objs := axg-toddr.o
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +20,7 @@ snd-soc-meson-axg-pdm-objs := axg-pdm.o
 | 
			
		|||
snd-soc-meson-codec-glue-objs := meson-codec-glue.o
 | 
			
		||||
snd-soc-meson-g12a-tohdmitx-objs := g12a-tohdmitx.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_SND_MESON_AIU) += snd-soc-meson-aiu.o
 | 
			
		||||
obj-$(CONFIG_SND_MESON_AXG_FIFO) += snd-soc-meson-axg-fifo.o
 | 
			
		||||
obj-$(CONFIG_SND_MESON_AXG_FRDDR) += snd-soc-meson-axg-frddr.o
 | 
			
		||||
obj-$(CONFIG_SND_MESON_AXG_TODDR) += snd-soc-meson-axg-toddr.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										324
									
								
								sound/soc/meson/aiu-encoder-i2s.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								sound/soc/meson/aiu-encoder-i2s.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,324 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 BayLibre, SAS.
 | 
			
		||||
// Author: Jerome Brunet <jbrunet@baylibre.com>
 | 
			
		||||
 | 
			
		||||
#include <linux/bitfield.h>
 | 
			
		||||
#include <linux/clk.h>
 | 
			
		||||
#include <sound/pcm_params.h>
 | 
			
		||||
#include <sound/soc.h>
 | 
			
		||||
#include <sound/soc-dai.h>
 | 
			
		||||
 | 
			
		||||
#include "aiu.h"
 | 
			
		||||
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC_MODE_8CH	BIT(0)
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC_MODE_24BIT	BIT(5)
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC_MODE_32BIT	BIT(9)
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC_MODE_SPLIT	BIT(11)
 | 
			
		||||
#define AIU_RST_SOFT_I2S_FAST		BIT(0)
 | 
			
		||||
 | 
			
		||||
#define AIU_I2S_DAC_CFG_MSB_FIRST	BIT(2)
 | 
			
		||||
#define AIU_I2S_MISC_HOLD_EN		BIT(2)
 | 
			
		||||
#define AIU_CLK_CTRL_I2S_DIV_EN		BIT(0)
 | 
			
		||||
#define AIU_CLK_CTRL_I2S_DIV		GENMASK(3, 2)
 | 
			
		||||
#define AIU_CLK_CTRL_AOCLK_INVERT	BIT(6)
 | 
			
		||||
#define AIU_CLK_CTRL_LRCLK_INVERT	BIT(7)
 | 
			
		||||
#define AIU_CLK_CTRL_LRCLK_SKEW		GENMASK(9, 8)
 | 
			
		||||
#define AIU_CLK_CTRL_MORE_HDMI_AMCLK	BIT(6)
 | 
			
		||||
#define AIU_CLK_CTRL_MORE_I2S_DIV	GENMASK(5, 0)
 | 
			
		||||
#define AIU_CODEC_DAC_LRCLK_CTRL_DIV	GENMASK(11, 0)
 | 
			
		||||
 | 
			
		||||
struct aiu_encoder_i2s {
 | 
			
		||||
	struct clk *aoclk;
 | 
			
		||||
	struct clk *mclk;
 | 
			
		||||
	struct clk *mixer;
 | 
			
		||||
	struct clk *pclk;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void aiu_encoder_i2s_divider_enable(struct snd_soc_component *component,
 | 
			
		||||
					   bool enable)
 | 
			
		||||
{
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
 | 
			
		||||
				      AIU_CLK_CTRL_I2S_DIV_EN,
 | 
			
		||||
				      enable ? AIU_CLK_CTRL_I2S_DIV_EN : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void aiu_encoder_i2s_hold(struct snd_soc_component *component,
 | 
			
		||||
				 bool enable)
 | 
			
		||||
{
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_I2S_MISC,
 | 
			
		||||
				      AIU_I2S_MISC_HOLD_EN,
 | 
			
		||||
				      enable ? AIU_I2S_MISC_HOLD_EN : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 | 
			
		||||
				   struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_START:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_RESUME:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 | 
			
		||||
		aiu_encoder_i2s_hold(component, false);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_STOP:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_SUSPEND:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 | 
			
		||||
		aiu_encoder_i2s_hold(component, true);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_i2s_setup_desc(struct snd_soc_component *component,
 | 
			
		||||
				      struct snd_pcm_hw_params *params)
 | 
			
		||||
{
 | 
			
		||||
	/* Always operate in split (classic interleaved) mode */
 | 
			
		||||
	unsigned int desc = AIU_I2S_SOURCE_DESC_MODE_SPLIT;
 | 
			
		||||
	unsigned int val;
 | 
			
		||||
 | 
			
		||||
	/* Reset required to update the pipeline */
 | 
			
		||||
	snd_soc_component_write(component, AIU_RST_SOFT, AIU_RST_SOFT_I2S_FAST);
 | 
			
		||||
	snd_soc_component_read(component, AIU_I2S_SYNC, &val);
 | 
			
		||||
 | 
			
		||||
	switch (params_physical_width(params)) {
 | 
			
		||||
	case 16: /* Nothing to do */
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case 32:
 | 
			
		||||
		desc |= (AIU_I2S_SOURCE_DESC_MODE_24BIT |
 | 
			
		||||
			 AIU_I2S_SOURCE_DESC_MODE_32BIT);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch (params_channels(params)) {
 | 
			
		||||
	case 2: /* Nothing to do */
 | 
			
		||||
		break;
 | 
			
		||||
	case 8:
 | 
			
		||||
		desc |= AIU_I2S_SOURCE_DESC_MODE_8CH;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_I2S_SOURCE_DESC,
 | 
			
		||||
				      AIU_I2S_SOURCE_DESC_MODE_8CH |
 | 
			
		||||
				      AIU_I2S_SOURCE_DESC_MODE_24BIT |
 | 
			
		||||
				      AIU_I2S_SOURCE_DESC_MODE_32BIT |
 | 
			
		||||
				      AIU_I2S_SOURCE_DESC_MODE_SPLIT,
 | 
			
		||||
				      desc);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_i2s_set_clocks(struct snd_soc_component *component,
 | 
			
		||||
				      struct snd_pcm_hw_params *params)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(component);
 | 
			
		||||
	unsigned int srate = params_rate(params);
 | 
			
		||||
	unsigned int fs, bs;
 | 
			
		||||
 | 
			
		||||
	/* Get the oversampling factor */
 | 
			
		||||
	fs = DIV_ROUND_CLOSEST(clk_get_rate(aiu->i2s.clks[MCLK].clk), srate);
 | 
			
		||||
 | 
			
		||||
	if (fs % 64)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	/* Send data MSB first */
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_I2S_DAC_CFG,
 | 
			
		||||
				      AIU_I2S_DAC_CFG_MSB_FIRST,
 | 
			
		||||
				      AIU_I2S_DAC_CFG_MSB_FIRST);
 | 
			
		||||
 | 
			
		||||
	/* Set bclk to lrlck ratio */
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_CODEC_DAC_LRCLK_CTRL,
 | 
			
		||||
				      AIU_CODEC_DAC_LRCLK_CTRL_DIV,
 | 
			
		||||
				      FIELD_PREP(AIU_CODEC_DAC_LRCLK_CTRL_DIV,
 | 
			
		||||
						 64 - 1));
 | 
			
		||||
 | 
			
		||||
	/* Use CLK_MORE for mclk to bclk divider */
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
 | 
			
		||||
				      AIU_CLK_CTRL_I2S_DIV, 0);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * NOTE: this HW is odd.
 | 
			
		||||
	 * In most configuration, the i2s divider is 'mclk / blck'.
 | 
			
		||||
	 * However, in 16 bits - 8ch mode, this factor needs to be
 | 
			
		||||
	 * increased by 50% to get the correct output rate.
 | 
			
		||||
	 * No idea why !
 | 
			
		||||
	 */
 | 
			
		||||
	bs = fs / 64;
 | 
			
		||||
	if (params_width(params) == 16 && params_channels(params) == 8) {
 | 
			
		||||
		if (bs % 2) {
 | 
			
		||||
			dev_err(component->dev,
 | 
			
		||||
				"Cannot increase i2s divider by 50%%\n");
 | 
			
		||||
			return -EINVAL;
 | 
			
		||||
		}
 | 
			
		||||
		bs += bs / 2;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
 | 
			
		||||
				      AIU_CLK_CTRL_MORE_I2S_DIV,
 | 
			
		||||
				      FIELD_PREP(AIU_CLK_CTRL_MORE_I2S_DIV,
 | 
			
		||||
						 bs - 1));
 | 
			
		||||
 | 
			
		||||
	/* Make sure amclk is used for HDMI i2s as well */
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_CLK_CTRL_MORE,
 | 
			
		||||
				      AIU_CLK_CTRL_MORE_HDMI_AMCLK,
 | 
			
		||||
				      AIU_CLK_CTRL_MORE_HDMI_AMCLK);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_i2s_hw_params(struct snd_pcm_substream *substream,
 | 
			
		||||
				     struct snd_pcm_hw_params *params,
 | 
			
		||||
				     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/* Disable the clock while changing the settings */
 | 
			
		||||
	aiu_encoder_i2s_divider_enable(component, false);
 | 
			
		||||
 | 
			
		||||
	ret = aiu_encoder_i2s_setup_desc(component, params);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		dev_err(dai->dev, "setting i2s desc failed\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = aiu_encoder_i2s_set_clocks(component, params);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		dev_err(dai->dev, "setting i2s clocks failed\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aiu_encoder_i2s_divider_enable(component, true);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_i2s_hw_free(struct snd_pcm_substream *substream,
 | 
			
		||||
				   struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
 | 
			
		||||
	aiu_encoder_i2s_divider_enable(component, false);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	unsigned int inv = fmt & SND_SOC_DAIFMT_INV_MASK;
 | 
			
		||||
	unsigned int val = 0;
 | 
			
		||||
	unsigned int skew;
 | 
			
		||||
 | 
			
		||||
	/* Only CPU Master / Codec Slave supported ATM */
 | 
			
		||||
	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (inv == SND_SOC_DAIFMT_NB_IF ||
 | 
			
		||||
	    inv == SND_SOC_DAIFMT_IB_IF)
 | 
			
		||||
		val |= AIU_CLK_CTRL_LRCLK_INVERT;
 | 
			
		||||
 | 
			
		||||
	if (inv == SND_SOC_DAIFMT_IB_NF ||
 | 
			
		||||
	    inv == SND_SOC_DAIFMT_IB_IF)
 | 
			
		||||
		val |= AIU_CLK_CTRL_AOCLK_INVERT;
 | 
			
		||||
 | 
			
		||||
	/* Signal skew */
 | 
			
		||||
	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 | 
			
		||||
	case SND_SOC_DAIFMT_I2S:
 | 
			
		||||
		/* Invert sample clock for i2s */
 | 
			
		||||
		val ^= AIU_CLK_CTRL_LRCLK_INVERT;
 | 
			
		||||
		skew = 1;
 | 
			
		||||
		break;
 | 
			
		||||
	case SND_SOC_DAIFMT_LEFT_J:
 | 
			
		||||
		skew = 0;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	val |= FIELD_PREP(AIU_CLK_CTRL_LRCLK_SKEW, skew);
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
 | 
			
		||||
				      AIU_CLK_CTRL_LRCLK_INVERT |
 | 
			
		||||
				      AIU_CLK_CTRL_AOCLK_INVERT |
 | 
			
		||||
				      AIU_CLK_CTRL_LRCLK_SKEW,
 | 
			
		||||
				      val);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 | 
			
		||||
				      unsigned int freq, int dir)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	if (WARN_ON(clk_id != 0))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (dir == SND_SOC_CLOCK_IN)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	ret = clk_set_rate(aiu->i2s.clks[MCLK].clk, freq);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		dev_err(dai->dev, "Failed to set sysclk to %uHz", freq);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const unsigned int hw_channels[] = {2, 8};
 | 
			
		||||
static const struct snd_pcm_hw_constraint_list hw_channel_constraints = {
 | 
			
		||||
	.list = hw_channels,
 | 
			
		||||
	.count = ARRAY_SIZE(hw_channels),
 | 
			
		||||
	.mask = 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_i2s_startup(struct snd_pcm_substream *substream,
 | 
			
		||||
				   struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/* Make sure the encoder gets either 2 or 8 channels */
 | 
			
		||||
	ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
 | 
			
		||||
					 SNDRV_PCM_HW_PARAM_CHANNELS,
 | 
			
		||||
					 &hw_channel_constraints);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		dev_err(dai->dev, "adding channels constraints failed\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = clk_bulk_prepare_enable(aiu->i2s.clk_num, aiu->i2s.clks);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		dev_err(dai->dev, "failed to enable i2s clocks\n");
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void aiu_encoder_i2s_shutdown(struct snd_pcm_substream *substream,
 | 
			
		||||
				     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
 | 
			
		||||
 | 
			
		||||
	clk_bulk_disable_unprepare(aiu->i2s.clk_num, aiu->i2s.clks);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops = {
 | 
			
		||||
	.trigger	= aiu_encoder_i2s_trigger,
 | 
			
		||||
	.hw_params	= aiu_encoder_i2s_hw_params,
 | 
			
		||||
	.hw_free	= aiu_encoder_i2s_hw_free,
 | 
			
		||||
	.set_fmt	= aiu_encoder_i2s_set_fmt,
 | 
			
		||||
	.set_sysclk	= aiu_encoder_i2s_set_sysclk,
 | 
			
		||||
	.startup	= aiu_encoder_i2s_startup,
 | 
			
		||||
	.shutdown	= aiu_encoder_i2s_shutdown,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										209
									
								
								sound/soc/meson/aiu-encoder-spdif.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								sound/soc/meson/aiu-encoder-spdif.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,209 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 BayLibre, SAS.
 | 
			
		||||
// Author: Jerome Brunet <jbrunet@baylibre.com>
 | 
			
		||||
 | 
			
		||||
#include <linux/bitfield.h>
 | 
			
		||||
#include <linux/clk.h>
 | 
			
		||||
#include <sound/pcm_params.h>
 | 
			
		||||
#include <sound/pcm_iec958.h>
 | 
			
		||||
#include <sound/soc.h>
 | 
			
		||||
#include <sound/soc-dai.h>
 | 
			
		||||
 | 
			
		||||
#include "aiu.h"
 | 
			
		||||
 | 
			
		||||
#define AIU_958_MISC_NON_PCM		BIT(0)
 | 
			
		||||
#define AIU_958_MISC_MODE_16BITS	BIT(1)
 | 
			
		||||
#define AIU_958_MISC_16BITS_ALIGN	GENMASK(6, 5)
 | 
			
		||||
#define AIU_958_MISC_MODE_32BITS	BIT(7)
 | 
			
		||||
#define AIU_958_MISC_U_FROM_STREAM	BIT(12)
 | 
			
		||||
#define AIU_958_MISC_FORCE_LR		BIT(13)
 | 
			
		||||
#define AIU_958_CTRL_HOLD_EN		BIT(0)
 | 
			
		||||
#define AIU_CLK_CTRL_958_DIV_EN		BIT(1)
 | 
			
		||||
#define AIU_CLK_CTRL_958_DIV		GENMASK(5, 4)
 | 
			
		||||
#define AIU_CLK_CTRL_958_DIV_MORE	BIT(12)
 | 
			
		||||
 | 
			
		||||
#define AIU_CS_WORD_LEN			4
 | 
			
		||||
#define AIU_958_INTERNAL_DIV		2
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
aiu_encoder_spdif_divider_enable(struct snd_soc_component *component,
 | 
			
		||||
				 bool enable)
 | 
			
		||||
{
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
 | 
			
		||||
				      AIU_CLK_CTRL_958_DIV_EN,
 | 
			
		||||
				      enable ? AIU_CLK_CTRL_958_DIV_EN : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void aiu_encoder_spdif_hold(struct snd_soc_component *component,
 | 
			
		||||
				   bool enable)
 | 
			
		||||
{
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_958_CTRL,
 | 
			
		||||
				      AIU_958_CTRL_HOLD_EN,
 | 
			
		||||
				      enable ? AIU_958_CTRL_HOLD_EN : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
aiu_encoder_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
 | 
			
		||||
			  struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_START:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_RESUME:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 | 
			
		||||
		aiu_encoder_spdif_hold(component, false);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_STOP:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_SUSPEND:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 | 
			
		||||
		aiu_encoder_spdif_hold(component, true);
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_spdif_setup_cs_word(struct snd_soc_component *component,
 | 
			
		||||
					   struct snd_pcm_hw_params *params)
 | 
			
		||||
{
 | 
			
		||||
	u8 cs[AIU_CS_WORD_LEN];
 | 
			
		||||
	unsigned int val;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = snd_pcm_create_iec958_consumer_hw_params(params, cs,
 | 
			
		||||
						       AIU_CS_WORD_LEN);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	/* Write the 1st half word */
 | 
			
		||||
	val = cs[1] | cs[0] << 8;
 | 
			
		||||
	snd_soc_component_write(component, AIU_958_CHSTAT_L0, val);
 | 
			
		||||
	snd_soc_component_write(component, AIU_958_CHSTAT_R0, val);
 | 
			
		||||
 | 
			
		||||
	/* Write the 2nd half word */
 | 
			
		||||
	val = cs[3] | cs[2] << 8;
 | 
			
		||||
	snd_soc_component_write(component, AIU_958_CHSTAT_L1, val);
 | 
			
		||||
	snd_soc_component_write(component, AIU_958_CHSTAT_R1, val);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_spdif_hw_params(struct snd_pcm_substream *substream,
 | 
			
		||||
				       struct snd_pcm_hw_params *params,
 | 
			
		||||
				       struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(component);
 | 
			
		||||
	unsigned int val = 0, mrate;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/* Disable the clock while changing the settings */
 | 
			
		||||
	aiu_encoder_spdif_divider_enable(component, false);
 | 
			
		||||
 | 
			
		||||
	switch (params_physical_width(params)) {
 | 
			
		||||
	case 16:
 | 
			
		||||
		val |= AIU_958_MISC_MODE_16BITS;
 | 
			
		||||
		val |= FIELD_PREP(AIU_958_MISC_16BITS_ALIGN, 2);
 | 
			
		||||
		break;
 | 
			
		||||
	case 32:
 | 
			
		||||
		val |= AIU_958_MISC_MODE_32BITS;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		dev_err(dai->dev, "Unsupport physical width\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_958_MISC,
 | 
			
		||||
				      AIU_958_MISC_NON_PCM |
 | 
			
		||||
				      AIU_958_MISC_MODE_16BITS |
 | 
			
		||||
				      AIU_958_MISC_16BITS_ALIGN |
 | 
			
		||||
				      AIU_958_MISC_MODE_32BITS |
 | 
			
		||||
				      AIU_958_MISC_FORCE_LR |
 | 
			
		||||
				      AIU_958_MISC_U_FROM_STREAM,
 | 
			
		||||
				      val);
 | 
			
		||||
 | 
			
		||||
	/* Set the stream channel status word */
 | 
			
		||||
	ret = aiu_encoder_spdif_setup_cs_word(component, params);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		dev_err(dai->dev, "failed to set channel status word\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_CLK_CTRL,
 | 
			
		||||
				      AIU_CLK_CTRL_958_DIV |
 | 
			
		||||
				      AIU_CLK_CTRL_958_DIV_MORE,
 | 
			
		||||
				      FIELD_PREP(AIU_CLK_CTRL_958_DIV,
 | 
			
		||||
						 __ffs(AIU_958_INTERNAL_DIV)));
 | 
			
		||||
 | 
			
		||||
	/* 2 * 32bits per subframe * 2 channels = 128 */
 | 
			
		||||
	mrate = params_rate(params) * 128 * AIU_958_INTERNAL_DIV;
 | 
			
		||||
	ret = clk_set_rate(aiu->spdif.clks[MCLK].clk, mrate);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		dev_err(dai->dev, "failed to set mclk rate\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aiu_encoder_spdif_divider_enable(component, true);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_spdif_hw_free(struct snd_pcm_substream *substream,
 | 
			
		||||
				     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
 | 
			
		||||
	aiu_encoder_spdif_divider_enable(component, false);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_encoder_spdif_startup(struct snd_pcm_substream *substream,
 | 
			
		||||
				     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * NOTE: Make sure the spdif block is on its own divider.
 | 
			
		||||
	 *
 | 
			
		||||
	 * The spdif can be clocked by the i2s master clock or its own
 | 
			
		||||
	 * clock. We should (in theory) change the source depending on the
 | 
			
		||||
	 * origin of the data.
 | 
			
		||||
	 *
 | 
			
		||||
	 * However, considering the clocking scheme used on these platforms,
 | 
			
		||||
	 * the master clocks will pick the same PLL source when they are
 | 
			
		||||
	 * playing from the same FIFO. The clock should be in sync so, it
 | 
			
		||||
	 * should not be necessary to reparent the spdif master clock.
 | 
			
		||||
	 */
 | 
			
		||||
	ret = clk_set_parent(aiu->spdif.clks[MCLK].clk,
 | 
			
		||||
			     aiu->spdif_mclk);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = clk_bulk_prepare_enable(aiu->spdif.clk_num, aiu->spdif.clks);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		dev_err(dai->dev, "failed to enable spdif clocks\n");
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void aiu_encoder_spdif_shutdown(struct snd_pcm_substream *substream,
 | 
			
		||||
				       struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(dai->component);
 | 
			
		||||
 | 
			
		||||
	clk_bulk_disable_unprepare(aiu->spdif.clk_num, aiu->spdif.clks);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops = {
 | 
			
		||||
	.trigger	= aiu_encoder_spdif_trigger,
 | 
			
		||||
	.hw_params	= aiu_encoder_spdif_hw_params,
 | 
			
		||||
	.hw_free	= aiu_encoder_spdif_hw_free,
 | 
			
		||||
	.startup	= aiu_encoder_spdif_startup,
 | 
			
		||||
	.shutdown	= aiu_encoder_spdif_shutdown,
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										153
									
								
								sound/soc/meson/aiu-fifo-i2s.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								sound/soc/meson/aiu-fifo-i2s.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,153 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 BayLibre, SAS.
 | 
			
		||||
// Author: Jerome Brunet <jbrunet@baylibre.com>
 | 
			
		||||
 | 
			
		||||
#include <linux/bitfield.h>
 | 
			
		||||
#include <linux/clk.h>
 | 
			
		||||
#include <sound/pcm_params.h>
 | 
			
		||||
#include <sound/soc.h>
 | 
			
		||||
#include <sound/soc-dai.h>
 | 
			
		||||
 | 
			
		||||
#include "aiu.h"
 | 
			
		||||
#include "aiu-fifo.h"
 | 
			
		||||
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC_MODE_8CH	BIT(0)
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC_MODE_24BIT	BIT(5)
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC_MODE_32BIT	BIT(9)
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC_MODE_SPLIT	BIT(11)
 | 
			
		||||
#define AIU_MEM_I2S_MASKS_IRQ_BLOCK	GENMASK(31, 16)
 | 
			
		||||
#define AIU_MEM_I2S_CONTROL_MODE_16BIT	BIT(6)
 | 
			
		||||
#define AIU_MEM_I2S_BUF_CNTL_INIT	BIT(0)
 | 
			
		||||
#define AIU_RST_SOFT_I2S_FAST		BIT(0)
 | 
			
		||||
 | 
			
		||||
#define AIU_FIFO_I2S_BLOCK		256
 | 
			
		||||
 | 
			
		||||
static struct snd_pcm_hardware fifo_i2s_pcm = {
 | 
			
		||||
	.info = (SNDRV_PCM_INFO_INTERLEAVED |
 | 
			
		||||
		 SNDRV_PCM_INFO_MMAP |
 | 
			
		||||
		 SNDRV_PCM_INFO_MMAP_VALID |
 | 
			
		||||
		 SNDRV_PCM_INFO_PAUSE),
 | 
			
		||||
	.formats = AIU_FORMATS,
 | 
			
		||||
	.rate_min = 5512,
 | 
			
		||||
	.rate_max = 192000,
 | 
			
		||||
	.channels_min = 2,
 | 
			
		||||
	.channels_max = 8,
 | 
			
		||||
	.period_bytes_min = AIU_FIFO_I2S_BLOCK,
 | 
			
		||||
	.period_bytes_max = AIU_FIFO_I2S_BLOCK * USHRT_MAX,
 | 
			
		||||
	.periods_min = 2,
 | 
			
		||||
	.periods_max = UINT_MAX,
 | 
			
		||||
 | 
			
		||||
	/* No real justification for this */
 | 
			
		||||
	.buffer_bytes_max = 1 * 1024 * 1024,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int aiu_fifo_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 | 
			
		||||
				struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	unsigned int val;
 | 
			
		||||
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_START:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_RESUME:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 | 
			
		||||
		snd_soc_component_write(component, AIU_RST_SOFT,
 | 
			
		||||
					AIU_RST_SOFT_I2S_FAST);
 | 
			
		||||
		snd_soc_component_read(component, AIU_I2S_SYNC, &val);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return aiu_fifo_trigger(substream, cmd, dai);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_fifo_i2s_prepare(struct snd_pcm_substream *substream,
 | 
			
		||||
				struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = aiu_fifo_prepare(substream, dai);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component,
 | 
			
		||||
				      AIU_MEM_I2S_BUF_CNTL,
 | 
			
		||||
				      AIU_MEM_I2S_BUF_CNTL_INIT,
 | 
			
		||||
				      AIU_MEM_I2S_BUF_CNTL_INIT);
 | 
			
		||||
	snd_soc_component_update_bits(component,
 | 
			
		||||
				      AIU_MEM_I2S_BUF_CNTL,
 | 
			
		||||
				      AIU_MEM_I2S_BUF_CNTL_INIT, 0);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream,
 | 
			
		||||
				  struct snd_pcm_hw_params *params,
 | 
			
		||||
				  struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	struct aiu_fifo *fifo = dai->playback_dma_data;
 | 
			
		||||
	unsigned int val;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = aiu_fifo_hw_params(substream, params, dai);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	switch (params_physical_width(params)) {
 | 
			
		||||
	case 16:
 | 
			
		||||
		val = AIU_MEM_I2S_CONTROL_MODE_16BIT;
 | 
			
		||||
		break;
 | 
			
		||||
	case 32:
 | 
			
		||||
		val = 0;
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		dev_err(dai->dev, "Unsupported physical width %u\n",
 | 
			
		||||
			params_physical_width(params));
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_MEM_I2S_CONTROL,
 | 
			
		||||
				      AIU_MEM_I2S_CONTROL_MODE_16BIT,
 | 
			
		||||
				      val);
 | 
			
		||||
 | 
			
		||||
	/* Setup the irq periodicity */
 | 
			
		||||
	val = params_period_bytes(params) / fifo->fifo_block;
 | 
			
		||||
	val = FIELD_PREP(AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_MEM_I2S_MASKS,
 | 
			
		||||
				      AIU_MEM_I2S_MASKS_IRQ_BLOCK, val);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops = {
 | 
			
		||||
	.trigger	= aiu_fifo_i2s_trigger,
 | 
			
		||||
	.prepare	= aiu_fifo_i2s_prepare,
 | 
			
		||||
	.hw_params	= aiu_fifo_i2s_hw_params,
 | 
			
		||||
	.hw_free	= aiu_fifo_hw_free,
 | 
			
		||||
	.startup	= aiu_fifo_startup,
 | 
			
		||||
	.shutdown	= aiu_fifo_shutdown,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(component);
 | 
			
		||||
	struct aiu_fifo *fifo;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = aiu_fifo_dai_probe(dai);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	fifo = dai->playback_dma_data;
 | 
			
		||||
 | 
			
		||||
	fifo->pcm = &fifo_i2s_pcm;
 | 
			
		||||
	fifo->mem_offset = AIU_MEM_I2S_START;
 | 
			
		||||
	fifo->fifo_block = AIU_FIFO_I2S_BLOCK;
 | 
			
		||||
	fifo->pclk = aiu->i2s.clks[PCLK].clk;
 | 
			
		||||
	fifo->irq = aiu->i2s.irq;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										186
									
								
								sound/soc/meson/aiu-fifo-spdif.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								sound/soc/meson/aiu-fifo-spdif.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,186 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 BayLibre, SAS.
 | 
			
		||||
// Author: Jerome Brunet <jbrunet@baylibre.com>
 | 
			
		||||
 | 
			
		||||
#include <linux/clk.h>
 | 
			
		||||
#include <sound/pcm_params.h>
 | 
			
		||||
#include <sound/soc.h>
 | 
			
		||||
#include <sound/soc-dai.h>
 | 
			
		||||
 | 
			
		||||
#include "aiu.h"
 | 
			
		||||
#include "aiu-fifo.h"
 | 
			
		||||
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL_EN		BIT(0)
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE	BIT(1)
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL_IRQ_MODE		GENMASK(3, 2)
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL_IRQ_OUT_THD	BIT(2)
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ	BIT(3)
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN	BIT(4)
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL_BYTE_SEEK	BIT(5)
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL_CONTINUE		BIT(6)
 | 
			
		||||
#define AIU_MEM_IEC958_CONTROL_ENDIAN		GENMASK(5, 3)
 | 
			
		||||
#define AIU_MEM_IEC958_CONTROL_RD_DDR		BIT(6)
 | 
			
		||||
#define AIU_MEM_IEC958_CONTROL_MODE_16BIT	BIT(7)
 | 
			
		||||
#define AIU_MEM_IEC958_CONTROL_MODE_LINEAR	BIT(8)
 | 
			
		||||
#define AIU_MEM_IEC958_BUF_CNTL_INIT		BIT(0)
 | 
			
		||||
 | 
			
		||||
#define AIU_FIFO_SPDIF_BLOCK			8
 | 
			
		||||
 | 
			
		||||
static struct snd_pcm_hardware fifo_spdif_pcm = {
 | 
			
		||||
	.info = (SNDRV_PCM_INFO_INTERLEAVED |
 | 
			
		||||
		 SNDRV_PCM_INFO_MMAP |
 | 
			
		||||
		 SNDRV_PCM_INFO_MMAP_VALID |
 | 
			
		||||
		 SNDRV_PCM_INFO_PAUSE),
 | 
			
		||||
	.formats = AIU_FORMATS,
 | 
			
		||||
	.rate_min = 5512,
 | 
			
		||||
	.rate_max = 192000,
 | 
			
		||||
	.channels_min = 2,
 | 
			
		||||
	.channels_max = 2,
 | 
			
		||||
	.period_bytes_min = AIU_FIFO_SPDIF_BLOCK,
 | 
			
		||||
	.period_bytes_max = AIU_FIFO_SPDIF_BLOCK * USHRT_MAX,
 | 
			
		||||
	.periods_min = 2,
 | 
			
		||||
	.periods_max = UINT_MAX,
 | 
			
		||||
 | 
			
		||||
	/* No real justification for this */
 | 
			
		||||
	.buffer_bytes_max = 1 * 1024 * 1024,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void fifo_spdif_dcu_enable(struct snd_soc_component *component,
 | 
			
		||||
				  bool enable)
 | 
			
		||||
{
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,
 | 
			
		||||
				      AIU_IEC958_DCU_FF_CTRL_EN,
 | 
			
		||||
				      enable ? AIU_IEC958_DCU_FF_CTRL_EN : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fifo_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
 | 
			
		||||
			      struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = aiu_fifo_trigger(substream, cmd, dai);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_START:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_RESUME:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 | 
			
		||||
		fifo_spdif_dcu_enable(component, true);
 | 
			
		||||
		break;
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_SUSPEND:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_STOP:
 | 
			
		||||
		fifo_spdif_dcu_enable(component, false);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fifo_spdif_prepare(struct snd_pcm_substream *substream,
 | 
			
		||||
			      struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = aiu_fifo_prepare(substream, dai);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component,
 | 
			
		||||
				      AIU_MEM_IEC958_BUF_CNTL,
 | 
			
		||||
				      AIU_MEM_IEC958_BUF_CNTL_INIT,
 | 
			
		||||
				      AIU_MEM_IEC958_BUF_CNTL_INIT);
 | 
			
		||||
	snd_soc_component_update_bits(component,
 | 
			
		||||
				      AIU_MEM_IEC958_BUF_CNTL,
 | 
			
		||||
				      AIU_MEM_IEC958_BUF_CNTL_INIT, 0);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fifo_spdif_hw_params(struct snd_pcm_substream *substream,
 | 
			
		||||
				struct snd_pcm_hw_params *params,
 | 
			
		||||
				struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	unsigned int val;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = aiu_fifo_hw_params(substream, params, dai);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	val = AIU_MEM_IEC958_CONTROL_RD_DDR |
 | 
			
		||||
	      AIU_MEM_IEC958_CONTROL_MODE_LINEAR;
 | 
			
		||||
 | 
			
		||||
	switch (params_physical_width(params)) {
 | 
			
		||||
	case 16:
 | 
			
		||||
		val |= AIU_MEM_IEC958_CONTROL_MODE_16BIT;
 | 
			
		||||
		break;
 | 
			
		||||
	case 32:
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		dev_err(dai->dev, "Unsupported physical width %u\n",
 | 
			
		||||
			params_physical_width(params));
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_MEM_IEC958_CONTROL,
 | 
			
		||||
				      AIU_MEM_IEC958_CONTROL_ENDIAN |
 | 
			
		||||
				      AIU_MEM_IEC958_CONTROL_RD_DDR |
 | 
			
		||||
				      AIU_MEM_IEC958_CONTROL_MODE_LINEAR |
 | 
			
		||||
				      AIU_MEM_IEC958_CONTROL_MODE_16BIT,
 | 
			
		||||
				      val);
 | 
			
		||||
 | 
			
		||||
	/* Number bytes read by the FIFO between each IRQ */
 | 
			
		||||
	snd_soc_component_write(component, AIU_IEC958_BPF,
 | 
			
		||||
				params_period_bytes(params));
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * AUTO_DISABLE and SYNC_HEAD are enabled by default but
 | 
			
		||||
	 * this should be disabled in PCM (uncompressed) mode
 | 
			
		||||
	 */
 | 
			
		||||
	snd_soc_component_update_bits(component, AIU_IEC958_DCU_FF_CTRL,
 | 
			
		||||
				      AIU_IEC958_DCU_FF_CTRL_AUTO_DISABLE |
 | 
			
		||||
				      AIU_IEC958_DCU_FF_CTRL_IRQ_MODE |
 | 
			
		||||
				      AIU_IEC958_DCU_FF_CTRL_SYNC_HEAD_EN,
 | 
			
		||||
				      AIU_IEC958_DCU_FF_CTRL_IRQ_FRAME_READ);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops = {
 | 
			
		||||
	.trigger	= fifo_spdif_trigger,
 | 
			
		||||
	.prepare	= fifo_spdif_prepare,
 | 
			
		||||
	.hw_params	= fifo_spdif_hw_params,
 | 
			
		||||
	.hw_free	= aiu_fifo_hw_free,
 | 
			
		||||
	.startup	= aiu_fifo_startup,
 | 
			
		||||
	.shutdown	= aiu_fifo_shutdown,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(component);
 | 
			
		||||
	struct aiu_fifo *fifo;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = aiu_fifo_dai_probe(dai);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	fifo = dai->playback_dma_data;
 | 
			
		||||
 | 
			
		||||
	fifo->pcm = &fifo_spdif_pcm;
 | 
			
		||||
	fifo->mem_offset = AIU_MEM_IEC958_START;
 | 
			
		||||
	fifo->fifo_block = 1;
 | 
			
		||||
	fifo->pclk = aiu->spdif.clks[PCLK].clk;
 | 
			
		||||
	fifo->irq = aiu->spdif.irq;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										223
									
								
								sound/soc/meson/aiu-fifo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								sound/soc/meson/aiu-fifo.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,223 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 BayLibre, SAS.
 | 
			
		||||
// Author: Jerome Brunet <jbrunet@baylibre.com>
 | 
			
		||||
 | 
			
		||||
#include <linux/bitfield.h>
 | 
			
		||||
#include <linux/clk.h>
 | 
			
		||||
#include <sound/pcm_params.h>
 | 
			
		||||
#include <sound/soc.h>
 | 
			
		||||
#include <sound/soc-dai.h>
 | 
			
		||||
 | 
			
		||||
#include "aiu-fifo.h"
 | 
			
		||||
 | 
			
		||||
#define AIU_MEM_START	0x00
 | 
			
		||||
#define AIU_MEM_RD	0x04
 | 
			
		||||
#define AIU_MEM_END	0x08
 | 
			
		||||
#define AIU_MEM_MASKS	0x0c
 | 
			
		||||
#define  AIU_MEM_MASK_CH_RD GENMASK(7, 0)
 | 
			
		||||
#define  AIU_MEM_MASK_CH_MEM GENMASK(15, 8)
 | 
			
		||||
#define AIU_MEM_CONTROL	0x10
 | 
			
		||||
#define  AIU_MEM_CONTROL_INIT BIT(0)
 | 
			
		||||
#define  AIU_MEM_CONTROL_FILL_EN BIT(1)
 | 
			
		||||
#define  AIU_MEM_CONTROL_EMPTY_EN BIT(2)
 | 
			
		||||
 | 
			
		||||
static struct snd_soc_dai *aiu_fifo_dai(struct snd_pcm_substream *ss)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_pcm_runtime *rtd = ss->private_data;
 | 
			
		||||
 | 
			
		||||
	return rtd->cpu_dai;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
 | 
			
		||||
				   struct snd_pcm_substream *substream)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_dai *dai = aiu_fifo_dai(substream);
 | 
			
		||||
	struct aiu_fifo *fifo = dai->playback_dma_data;
 | 
			
		||||
	struct snd_pcm_runtime *runtime = substream->runtime;
 | 
			
		||||
	unsigned int addr;
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_read(component, fifo->mem_offset + AIU_MEM_RD,
 | 
			
		||||
			       &addr);
 | 
			
		||||
 | 
			
		||||
	return bytes_to_frames(runtime, addr - (unsigned int)runtime->dma_addr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	struct aiu_fifo *fifo = dai->playback_dma_data;
 | 
			
		||||
	unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN |
 | 
			
		||||
				AIU_MEM_CONTROL_EMPTY_EN);
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component,
 | 
			
		||||
				      fifo->mem_offset + AIU_MEM_CONTROL,
 | 
			
		||||
				      en_mask, enable ? en_mask : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
 | 
			
		||||
		     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	switch (cmd) {
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_START:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_RESUME:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 | 
			
		||||
		aiu_fifo_enable(dai, true);
 | 
			
		||||
		break;
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_SUSPEND:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 | 
			
		||||
	case SNDRV_PCM_TRIGGER_STOP:
 | 
			
		||||
		aiu_fifo_enable(dai, false);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_prepare(struct snd_pcm_substream *substream,
 | 
			
		||||
		     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	struct aiu_fifo *fifo = dai->playback_dma_data;
 | 
			
		||||
 | 
			
		||||
	snd_soc_component_update_bits(component,
 | 
			
		||||
				      fifo->mem_offset + AIU_MEM_CONTROL,
 | 
			
		||||
				      AIU_MEM_CONTROL_INIT,
 | 
			
		||||
				      AIU_MEM_CONTROL_INIT);
 | 
			
		||||
	snd_soc_component_update_bits(component,
 | 
			
		||||
				      fifo->mem_offset + AIU_MEM_CONTROL,
 | 
			
		||||
				      AIU_MEM_CONTROL_INIT, 0);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
 | 
			
		||||
		       struct snd_pcm_hw_params *params,
 | 
			
		||||
		       struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_pcm_runtime *runtime = substream->runtime;
 | 
			
		||||
	struct snd_soc_component *component = dai->component;
 | 
			
		||||
	struct aiu_fifo *fifo = dai->playback_dma_data;
 | 
			
		||||
	dma_addr_t end;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	/* Setup the fifo boundaries */
 | 
			
		||||
	end = runtime->dma_addr + runtime->dma_bytes - fifo->fifo_block;
 | 
			
		||||
	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_START,
 | 
			
		||||
				runtime->dma_addr);
 | 
			
		||||
	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_RD,
 | 
			
		||||
				runtime->dma_addr);
 | 
			
		||||
	snd_soc_component_write(component, fifo->mem_offset + AIU_MEM_END,
 | 
			
		||||
				end);
 | 
			
		||||
 | 
			
		||||
	/* Setup the fifo to read all the memory - no skip */
 | 
			
		||||
	snd_soc_component_update_bits(component,
 | 
			
		||||
				      fifo->mem_offset + AIU_MEM_MASKS,
 | 
			
		||||
				      AIU_MEM_MASK_CH_RD | AIU_MEM_MASK_CH_MEM,
 | 
			
		||||
				      FIELD_PREP(AIU_MEM_MASK_CH_RD, 0xff) |
 | 
			
		||||
				      FIELD_PREP(AIU_MEM_MASK_CH_MEM, 0xff));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_hw_free(struct snd_pcm_substream *substream,
 | 
			
		||||
		     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	return snd_pcm_lib_free_pages(substream);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static irqreturn_t aiu_fifo_isr(int irq, void *dev_id)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_pcm_substream *playback = dev_id;
 | 
			
		||||
 | 
			
		||||
	snd_pcm_period_elapsed(playback);
 | 
			
		||||
 | 
			
		||||
	return IRQ_HANDLED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_startup(struct snd_pcm_substream *substream,
 | 
			
		||||
		     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu_fifo *fifo = dai->playback_dma_data;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	snd_soc_set_runtime_hwparams(substream, fifo->pcm);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Make sure the buffer and period size are multiple of the fifo burst
 | 
			
		||||
	 * size
 | 
			
		||||
	 */
 | 
			
		||||
	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
 | 
			
		||||
					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
 | 
			
		||||
					 fifo->fifo_block);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
 | 
			
		||||
					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
 | 
			
		||||
					 fifo->fifo_block);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = clk_prepare_enable(fifo->pclk);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ret = request_irq(fifo->irq, aiu_fifo_isr, 0, dev_name(dai->dev),
 | 
			
		||||
			  substream);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		clk_disable_unprepare(fifo->pclk);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
 | 
			
		||||
		       struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu_fifo *fifo = dai->playback_dma_data;
 | 
			
		||||
 | 
			
		||||
	free_irq(fifo->irq, substream);
 | 
			
		||||
	clk_disable_unprepare(fifo->pclk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
 | 
			
		||||
		     struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_pcm_substream *substream =
 | 
			
		||||
		rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
 | 
			
		||||
	struct snd_card *card = rtd->card->snd_card;
 | 
			
		||||
	struct aiu_fifo *fifo = dai->playback_dma_data;
 | 
			
		||||
	size_t size = fifo->pcm->buffer_bytes_max;
 | 
			
		||||
 | 
			
		||||
	snd_pcm_lib_preallocate_pages(substream,
 | 
			
		||||
				      SNDRV_DMA_TYPE_DEV,
 | 
			
		||||
				      card->dev, size, size);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_dai_probe(struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu_fifo *fifo;
 | 
			
		||||
 | 
			
		||||
	fifo = kzalloc(sizeof(*fifo), GFP_KERNEL);
 | 
			
		||||
	if (!fifo)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	dai->playback_dma_data = fifo;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_dai_remove(struct snd_soc_dai *dai)
 | 
			
		||||
{
 | 
			
		||||
	kfree(dai->playback_dma_data);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								sound/soc/meson/aiu-fifo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								sound/soc/meson/aiu-fifo.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2020 BayLibre, SAS.
 | 
			
		||||
 * Author: Jerome Brunet <jbrunet@baylibre.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _MESON_AIU_FIFO_H
 | 
			
		||||
#define _MESON_AIU_FIFO_H
 | 
			
		||||
 | 
			
		||||
struct snd_pcm_hardware;
 | 
			
		||||
struct snd_soc_component_driver;
 | 
			
		||||
struct snd_soc_dai_driver;
 | 
			
		||||
struct clk;
 | 
			
		||||
struct snd_pcm_ops;
 | 
			
		||||
struct snd_pcm_substream;
 | 
			
		||||
struct snd_soc_dai;
 | 
			
		||||
struct snd_pcm_hw_params;
 | 
			
		||||
struct platform_device;
 | 
			
		||||
 | 
			
		||||
struct aiu_fifo {
 | 
			
		||||
	struct snd_pcm_hardware *pcm;
 | 
			
		||||
	unsigned int mem_offset;
 | 
			
		||||
	unsigned int fifo_block;
 | 
			
		||||
	struct clk *pclk;
 | 
			
		||||
	int irq;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_dai_probe(struct snd_soc_dai *dai);
 | 
			
		||||
int aiu_fifo_dai_remove(struct snd_soc_dai *dai);
 | 
			
		||||
 | 
			
		||||
snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
 | 
			
		||||
				   struct snd_pcm_substream *substream);
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_trigger(struct snd_pcm_substream *substream, int cmd,
 | 
			
		||||
		     struct snd_soc_dai *dai);
 | 
			
		||||
int aiu_fifo_prepare(struct snd_pcm_substream *substream,
 | 
			
		||||
		     struct snd_soc_dai *dai);
 | 
			
		||||
int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
 | 
			
		||||
		       struct snd_pcm_hw_params *params,
 | 
			
		||||
		       struct snd_soc_dai *dai);
 | 
			
		||||
int aiu_fifo_hw_free(struct snd_pcm_substream *substream,
 | 
			
		||||
		     struct snd_soc_dai *dai);
 | 
			
		||||
int aiu_fifo_startup(struct snd_pcm_substream *substream,
 | 
			
		||||
		     struct snd_soc_dai *dai);
 | 
			
		||||
void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
 | 
			
		||||
		       struct snd_soc_dai *dai);
 | 
			
		||||
int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
 | 
			
		||||
		     struct snd_soc_dai *dai);
 | 
			
		||||
 | 
			
		||||
#endif /* _MESON_AIU_FIFO_H */
 | 
			
		||||
							
								
								
									
										348
									
								
								sound/soc/meson/aiu.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										348
									
								
								sound/soc/meson/aiu.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,348 @@
 | 
			
		|||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
//
 | 
			
		||||
// Copyright (c) 2020 BayLibre, SAS.
 | 
			
		||||
// Author: Jerome Brunet <jbrunet@baylibre.com>
 | 
			
		||||
 | 
			
		||||
#include <linux/bitfield.h>
 | 
			
		||||
#include <linux/clk.h>
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/of_platform.h>
 | 
			
		||||
#include <linux/regmap.h>
 | 
			
		||||
#include <linux/reset.h>
 | 
			
		||||
#include <sound/soc.h>
 | 
			
		||||
#include <sound/soc-dai.h>
 | 
			
		||||
 | 
			
		||||
#include <dt-bindings/sound/meson-aiu.h>
 | 
			
		||||
#include "aiu.h"
 | 
			
		||||
#include "aiu-fifo.h"
 | 
			
		||||
 | 
			
		||||
#define AIU_I2S_MISC_958_SRC_SHIFT 3
 | 
			
		||||
 | 
			
		||||
static const char * const aiu_spdif_encode_sel_texts[] = {
 | 
			
		||||
	"SPDIF", "I2S",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static SOC_ENUM_SINGLE_DECL(aiu_spdif_encode_sel_enum, AIU_I2S_MISC,
 | 
			
		||||
			    AIU_I2S_MISC_958_SRC_SHIFT,
 | 
			
		||||
			    aiu_spdif_encode_sel_texts);
 | 
			
		||||
 | 
			
		||||
static const struct snd_kcontrol_new aiu_spdif_encode_mux =
 | 
			
		||||
	SOC_DAPM_ENUM("SPDIF Buffer Src", aiu_spdif_encode_sel_enum);
 | 
			
		||||
 | 
			
		||||
static const struct snd_soc_dapm_widget aiu_cpu_dapm_widgets[] = {
 | 
			
		||||
	SND_SOC_DAPM_MUX("SPDIF SRC SEL", SND_SOC_NOPM, 0, 0,
 | 
			
		||||
			 &aiu_spdif_encode_mux),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct snd_soc_dapm_route aiu_cpu_dapm_routes[] = {
 | 
			
		||||
	{ "I2S Encoder Playback", NULL, "I2S FIFO Playback" },
 | 
			
		||||
	{ "SPDIF SRC SEL", "SPDIF", "SPDIF FIFO Playback" },
 | 
			
		||||
	{ "SPDIF SRC SEL", "I2S", "I2S FIFO Playback" },
 | 
			
		||||
	{ "SPDIF Encoder Playback", NULL, "SPDIF SRC SEL" },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int aiu_of_xlate_dai_name(struct snd_soc_component *component,
 | 
			
		||||
			  struct of_phandle_args *args,
 | 
			
		||||
			  const char **dai_name,
 | 
			
		||||
			  unsigned int component_id)
 | 
			
		||||
{
 | 
			
		||||
	struct snd_soc_dai *dai;
 | 
			
		||||
	int id;
 | 
			
		||||
 | 
			
		||||
	if (args->args_count != 2)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if (args->args[0] != component_id)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	id = args->args[1];
 | 
			
		||||
 | 
			
		||||
	if (id < 0 || id >= component->num_dai)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	for_each_component_dais(component, dai) {
 | 
			
		||||
		if (id == 0)
 | 
			
		||||
			break;
 | 
			
		||||
		id--;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*dai_name = dai->driver->name;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_cpu_of_xlate_dai_name(struct snd_soc_component *component,
 | 
			
		||||
				     struct of_phandle_args *args,
 | 
			
		||||
				     const char **dai_name)
 | 
			
		||||
{
 | 
			
		||||
	return aiu_of_xlate_dai_name(component, args, dai_name, AIU_CPU);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_cpu_component_probe(struct snd_soc_component *component)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(component);
 | 
			
		||||
 | 
			
		||||
	/* Required for the SPDIF Source control operation */
 | 
			
		||||
	return clk_prepare_enable(aiu->i2s.clks[PCLK].clk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void aiu_cpu_component_remove(struct snd_soc_component *component)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = snd_soc_component_get_drvdata(component);
 | 
			
		||||
 | 
			
		||||
	clk_disable_unprepare(aiu->i2s.clks[PCLK].clk);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct snd_soc_component_driver aiu_cpu_component = {
 | 
			
		||||
	.name			= "AIU CPU",
 | 
			
		||||
	.dapm_widgets		= aiu_cpu_dapm_widgets,
 | 
			
		||||
	.num_dapm_widgets	= ARRAY_SIZE(aiu_cpu_dapm_widgets),
 | 
			
		||||
	.dapm_routes		= aiu_cpu_dapm_routes,
 | 
			
		||||
	.num_dapm_routes	= ARRAY_SIZE(aiu_cpu_dapm_routes),
 | 
			
		||||
	.of_xlate_dai_name	= aiu_cpu_of_xlate_dai_name,
 | 
			
		||||
	.pointer		= aiu_fifo_pointer,
 | 
			
		||||
	.probe			= aiu_cpu_component_probe,
 | 
			
		||||
	.remove			= aiu_cpu_component_remove,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct snd_soc_dai_driver aiu_cpu_dai_drv[] = {
 | 
			
		||||
	[CPU_I2S_FIFO] = {
 | 
			
		||||
		.name = "I2S FIFO",
 | 
			
		||||
		.playback = {
 | 
			
		||||
			.stream_name	= "I2S FIFO Playback",
 | 
			
		||||
			.channels_min	= 2,
 | 
			
		||||
			.channels_max	= 8,
 | 
			
		||||
			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
 | 
			
		||||
			.rate_min	= 5512,
 | 
			
		||||
			.rate_max	= 192000,
 | 
			
		||||
			.formats	= AIU_FORMATS,
 | 
			
		||||
		},
 | 
			
		||||
		.ops		= &aiu_fifo_i2s_dai_ops,
 | 
			
		||||
		.pcm_new	= aiu_fifo_pcm_new,
 | 
			
		||||
		.probe		= aiu_fifo_i2s_dai_probe,
 | 
			
		||||
		.remove		= aiu_fifo_dai_remove,
 | 
			
		||||
	},
 | 
			
		||||
	[CPU_SPDIF_FIFO] = {
 | 
			
		||||
		.name = "SPDIF FIFO",
 | 
			
		||||
		.playback = {
 | 
			
		||||
			.stream_name	= "SPDIF FIFO Playback",
 | 
			
		||||
			.channels_min	= 2,
 | 
			
		||||
			.channels_max	= 2,
 | 
			
		||||
			.rates		= SNDRV_PCM_RATE_CONTINUOUS,
 | 
			
		||||
			.rate_min	= 5512,
 | 
			
		||||
			.rate_max	= 192000,
 | 
			
		||||
			.formats	= AIU_FORMATS,
 | 
			
		||||
		},
 | 
			
		||||
		.ops		= &aiu_fifo_spdif_dai_ops,
 | 
			
		||||
		.pcm_new	= aiu_fifo_pcm_new,
 | 
			
		||||
		.probe		= aiu_fifo_spdif_dai_probe,
 | 
			
		||||
		.remove		= aiu_fifo_dai_remove,
 | 
			
		||||
	},
 | 
			
		||||
	[CPU_I2S_ENCODER] = {
 | 
			
		||||
		.name = "I2S Encoder",
 | 
			
		||||
		.playback = {
 | 
			
		||||
			.stream_name = "I2S Encoder Playback",
 | 
			
		||||
			.channels_min = 2,
 | 
			
		||||
			.channels_max = 8,
 | 
			
		||||
			.rates = SNDRV_PCM_RATE_8000_192000,
 | 
			
		||||
			.formats = AIU_FORMATS,
 | 
			
		||||
		},
 | 
			
		||||
		.ops = &aiu_encoder_i2s_dai_ops,
 | 
			
		||||
	},
 | 
			
		||||
	[CPU_SPDIF_ENCODER] = {
 | 
			
		||||
		.name = "SPDIF Encoder",
 | 
			
		||||
		.playback = {
 | 
			
		||||
			.stream_name = "SPDIF Encoder Playback",
 | 
			
		||||
			.channels_min = 2,
 | 
			
		||||
			.channels_max = 2,
 | 
			
		||||
			.rates = (SNDRV_PCM_RATE_32000  |
 | 
			
		||||
				  SNDRV_PCM_RATE_44100  |
 | 
			
		||||
				  SNDRV_PCM_RATE_48000  |
 | 
			
		||||
				  SNDRV_PCM_RATE_88200  |
 | 
			
		||||
				  SNDRV_PCM_RATE_96000  |
 | 
			
		||||
				  SNDRV_PCM_RATE_176400 |
 | 
			
		||||
				  SNDRV_PCM_RATE_192000),
 | 
			
		||||
			.formats = AIU_FORMATS,
 | 
			
		||||
		},
 | 
			
		||||
		.ops = &aiu_encoder_spdif_dai_ops,
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct regmap_config aiu_regmap_cfg = {
 | 
			
		||||
	.reg_bits	= 32,
 | 
			
		||||
	.val_bits	= 32,
 | 
			
		||||
	.reg_stride	= 4,
 | 
			
		||||
	.max_register	= 0x2ac,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int aiu_clk_bulk_get(struct device *dev,
 | 
			
		||||
			    const char * const *ids,
 | 
			
		||||
			    unsigned int num,
 | 
			
		||||
			    struct aiu_interface *interface)
 | 
			
		||||
{
 | 
			
		||||
	struct clk_bulk_data *clks;
 | 
			
		||||
	int i, ret;
 | 
			
		||||
 | 
			
		||||
	clks = devm_kcalloc(dev, num, sizeof(clks), GFP_KERNEL);
 | 
			
		||||
	if (!clks)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < num; i++)
 | 
			
		||||
		clks[i].id = ids[i];
 | 
			
		||||
 | 
			
		||||
	ret = devm_clk_bulk_get(dev, num, clks);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	interface->clks = clks;
 | 
			
		||||
	interface->clk_num = num;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char * const aiu_i2s_ids[] = {
 | 
			
		||||
	[PCLK]	= "i2s_pclk",
 | 
			
		||||
	[AOCLK]	= "i2s_aoclk",
 | 
			
		||||
	[MCLK]	= "i2s_mclk",
 | 
			
		||||
	[MIXER]	= "i2s_mixer",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const char * const aiu_spdif_ids[] = {
 | 
			
		||||
	[PCLK]	= "spdif_pclk",
 | 
			
		||||
	[AOCLK]	= "spdif_aoclk",
 | 
			
		||||
	[MCLK]	= "spdif_mclk_sel"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int aiu_clk_get(struct device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct aiu *aiu = dev_get_drvdata(dev);
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	aiu->pclk = devm_clk_get(dev, "pclk");
 | 
			
		||||
	if (IS_ERR(aiu->pclk)) {
 | 
			
		||||
		if (PTR_ERR(aiu->pclk) != -EPROBE_DEFER)
 | 
			
		||||
			dev_err(dev, "Can't get the aiu pclk\n");
 | 
			
		||||
		return PTR_ERR(aiu->pclk);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aiu->spdif_mclk = devm_clk_get(dev, "spdif_mclk");
 | 
			
		||||
	if (IS_ERR(aiu->spdif_mclk)) {
 | 
			
		||||
		if (PTR_ERR(aiu->spdif_mclk) != -EPROBE_DEFER)
 | 
			
		||||
			dev_err(dev, "Can't get the aiu spdif master clock\n");
 | 
			
		||||
		return PTR_ERR(aiu->spdif_mclk);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = aiu_clk_bulk_get(dev, aiu_i2s_ids, ARRAY_SIZE(aiu_i2s_ids),
 | 
			
		||||
			       &aiu->i2s);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		if (ret != -EPROBE_DEFER)
 | 
			
		||||
			dev_err(dev, "Can't get the i2s clocks\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = aiu_clk_bulk_get(dev, aiu_spdif_ids, ARRAY_SIZE(aiu_spdif_ids),
 | 
			
		||||
			       &aiu->spdif);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		if (ret != -EPROBE_DEFER)
 | 
			
		||||
			dev_err(dev, "Can't get the spdif clocks\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = clk_prepare_enable(aiu->pclk);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		dev_err(dev, "peripheral clock enable failed\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = devm_add_action_or_reset(dev,
 | 
			
		||||
				       (void(*)(void *))clk_disable_unprepare,
 | 
			
		||||
				       aiu->pclk);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		dev_err(dev, "failed to add reset action on pclk");
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_probe(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	struct device *dev = &pdev->dev;
 | 
			
		||||
	void __iomem *regs;
 | 
			
		||||
	struct regmap *map;
 | 
			
		||||
	struct aiu *aiu;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	aiu = devm_kzalloc(dev, sizeof(*aiu), GFP_KERNEL);
 | 
			
		||||
	if (!aiu)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	platform_set_drvdata(pdev, aiu);
 | 
			
		||||
 | 
			
		||||
	ret = device_reset(dev);
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		if (ret != -EPROBE_DEFER)
 | 
			
		||||
			dev_err(dev, "Failed to reset device\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	regs = devm_platform_ioremap_resource(pdev, 0);
 | 
			
		||||
	if (IS_ERR(regs))
 | 
			
		||||
		return PTR_ERR(regs);
 | 
			
		||||
 | 
			
		||||
	map = devm_regmap_init_mmio(dev, regs, &aiu_regmap_cfg);
 | 
			
		||||
	if (IS_ERR(map)) {
 | 
			
		||||
		dev_err(dev, "failed to init regmap: %ld\n",
 | 
			
		||||
			PTR_ERR(map));
 | 
			
		||||
		return PTR_ERR(map);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aiu->i2s.irq = platform_get_irq_byname(pdev, "i2s");
 | 
			
		||||
	if (aiu->i2s.irq < 0) {
 | 
			
		||||
		dev_err(dev, "Can't get i2s irq\n");
 | 
			
		||||
		return aiu->i2s.irq;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	aiu->spdif.irq = platform_get_irq_byname(pdev, "spdif");
 | 
			
		||||
	if (aiu->spdif.irq < 0) {
 | 
			
		||||
		dev_err(dev, "Can't get spdif irq\n");
 | 
			
		||||
		return aiu->spdif.irq;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = aiu_clk_get(dev);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	/* Register the cpu component of the aiu */
 | 
			
		||||
	ret = snd_soc_register_component(dev, &aiu_cpu_component,
 | 
			
		||||
					 aiu_cpu_dai_drv,
 | 
			
		||||
					 ARRAY_SIZE(aiu_cpu_dai_drv));
 | 
			
		||||
	if (ret)
 | 
			
		||||
		dev_err(dev, "Failed to register cpu component\n");
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int aiu_remove(struct platform_device *pdev)
 | 
			
		||||
{
 | 
			
		||||
	snd_soc_unregister_component(&pdev->dev);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct of_device_id aiu_of_match[] = {
 | 
			
		||||
	{ .compatible = "amlogic,aiu-gxbb", },
 | 
			
		||||
	{ .compatible = "amlogic,aiu-gxl", },
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(of, aiu_of_match);
 | 
			
		||||
 | 
			
		||||
static struct platform_driver aiu_pdrv = {
 | 
			
		||||
	.probe = aiu_probe,
 | 
			
		||||
	.remove = aiu_remove,
 | 
			
		||||
	.driver = {
 | 
			
		||||
		.name = "meson-aiu",
 | 
			
		||||
		.of_match_table = aiu_of_match,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
module_platform_driver(aiu_pdrv);
 | 
			
		||||
 | 
			
		||||
MODULE_DESCRIPTION("Meson AIU Driver");
 | 
			
		||||
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
 | 
			
		||||
MODULE_LICENSE("GPL v2");
 | 
			
		||||
							
								
								
									
										82
									
								
								sound/soc/meson/aiu.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								sound/soc/meson/aiu.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2018 BayLibre, SAS.
 | 
			
		||||
 * Author: Jerome Brunet <jbrunet@baylibre.com>
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _MESON_AIU_H
 | 
			
		||||
#define _MESON_AIU_H
 | 
			
		||||
 | 
			
		||||
struct clk;
 | 
			
		||||
struct clk_bulk_data;
 | 
			
		||||
struct device;
 | 
			
		||||
struct of_phandle_args;
 | 
			
		||||
struct snd_soc_component_driver;
 | 
			
		||||
struct snd_soc_dai;
 | 
			
		||||
struct snd_soc_dai_driver;
 | 
			
		||||
struct snd_soc_dai_ops;
 | 
			
		||||
 | 
			
		||||
enum aiu_clk_ids {
 | 
			
		||||
	PCLK = 0,
 | 
			
		||||
	AOCLK,
 | 
			
		||||
	MCLK,
 | 
			
		||||
	MIXER
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct aiu_interface {
 | 
			
		||||
	struct clk_bulk_data *clks;
 | 
			
		||||
	unsigned int clk_num;
 | 
			
		||||
	unsigned int irq;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct aiu {
 | 
			
		||||
	struct clk *pclk;
 | 
			
		||||
	struct clk *spdif_mclk;
 | 
			
		||||
	struct aiu_interface i2s;
 | 
			
		||||
	struct aiu_interface spdif;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define AIU_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |	\
 | 
			
		||||
		     SNDRV_PCM_FMTBIT_S20_LE |	\
 | 
			
		||||
		     SNDRV_PCM_FMTBIT_S24_LE)
 | 
			
		||||
 | 
			
		||||
int aiu_of_xlate_dai_name(struct snd_soc_component *component,
 | 
			
		||||
			  struct of_phandle_args *args,
 | 
			
		||||
			  const char **dai_name,
 | 
			
		||||
			  unsigned int component_id);
 | 
			
		||||
 | 
			
		||||
int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai);
 | 
			
		||||
int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai);
 | 
			
		||||
 | 
			
		||||
extern const struct snd_soc_dai_ops aiu_fifo_i2s_dai_ops;
 | 
			
		||||
extern const struct snd_soc_dai_ops aiu_fifo_spdif_dai_ops;
 | 
			
		||||
extern const struct snd_soc_dai_ops aiu_encoder_i2s_dai_ops;
 | 
			
		||||
extern const struct snd_soc_dai_ops aiu_encoder_spdif_dai_ops;
 | 
			
		||||
 | 
			
		||||
#define AIU_IEC958_BPF			0x000
 | 
			
		||||
#define AIU_958_MISC			0x010
 | 
			
		||||
#define AIU_IEC958_DCU_FF_CTRL		0x01c
 | 
			
		||||
#define AIU_958_CHSTAT_L0		0x020
 | 
			
		||||
#define AIU_958_CHSTAT_L1		0x024
 | 
			
		||||
#define AIU_958_CTRL			0x028
 | 
			
		||||
#define AIU_I2S_SOURCE_DESC		0x034
 | 
			
		||||
#define AIU_I2S_DAC_CFG			0x040
 | 
			
		||||
#define AIU_I2S_SYNC			0x044
 | 
			
		||||
#define AIU_I2S_MISC			0x048
 | 
			
		||||
#define AIU_RST_SOFT			0x054
 | 
			
		||||
#define AIU_CLK_CTRL			0x058
 | 
			
		||||
#define AIU_CLK_CTRL_MORE		0x064
 | 
			
		||||
#define AIU_CODEC_DAC_LRCLK_CTRL	0x0a0
 | 
			
		||||
#define AIU_HDMI_CLK_DATA_CTRL		0x0a8
 | 
			
		||||
#define AIU_ACODEC_CTRL			0x0b0
 | 
			
		||||
#define AIU_958_CHSTAT_R0		0x0c0
 | 
			
		||||
#define AIU_958_CHSTAT_R1		0x0c4
 | 
			
		||||
#define AIU_MEM_I2S_START		0x180
 | 
			
		||||
#define AIU_MEM_I2S_MASKS		0x18c
 | 
			
		||||
#define AIU_MEM_I2S_CONTROL		0x190
 | 
			
		||||
#define AIU_MEM_IEC958_START		0x194
 | 
			
		||||
#define AIU_MEM_IEC958_CONTROL		0x1a4
 | 
			
		||||
#define AIU_MEM_I2S_BUF_CNTL		0x1d8
 | 
			
		||||
#define AIU_MEM_IEC958_BUF_CNTL		0x1fc
 | 
			
		||||
 | 
			
		||||
#endif /* _MESON_AIU_H */
 | 
			
		||||
		Loading…
	
		Reference in a new issue