diff options
| -rw-r--r-- | sound/soc/stm/stm32_sai.c | 2 | ||||
| -rw-r--r-- | sound/soc/stm/stm32_sai.h | 2 | ||||
| -rw-r--r-- | sound/soc/stm/stm32_sai_sub.c | 153 | 
3 files changed, 133 insertions, 24 deletions
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index d743b7dd52fb..f22654253c43 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -30,10 +30,12 @@  static const struct stm32_sai_conf stm32_sai_conf_f4 = {  	.version = SAI_STM32F4, +	.has_spdif = false,  };  static const struct stm32_sai_conf stm32_sai_conf_h7 = {  	.version = SAI_STM32H7, +	.has_spdif = true,  };  static const struct of_device_id stm32_sai_ids[] = { diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h index bb062e70de63..f25422174909 100644 --- a/sound/soc/stm/stm32_sai.h +++ b/sound/soc/stm/stm32_sai.h @@ -248,9 +248,11 @@ enum stm32_sai_version {  /**   * struct stm32_sai_conf - SAI configuration   * @version: SAI version + * @has_spdif: SAI S/PDIF support flag   */  struct stm32_sai_conf {  	int version; +	bool has_spdif;  };  /** diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 08583b958430..cfeb219e1d78 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -23,6 +23,7 @@  #include <linux/of_platform.h>  #include <linux/regmap.h> +#include <sound/asoundef.h>  #include <sound/core.h>  #include <sound/dmaengine_pcm.h>  #include <sound/pcm_params.h> @@ -30,6 +31,7 @@  #include "stm32_sai.h"  #define SAI_FREE_PROTOCOL	0x0 +#define SAI_SPDIF_PROTOCOL	0x1  #define SAI_SLOT_SIZE_AUTO	0x0  #define SAI_SLOT_SIZE_16	0x1 @@ -59,8 +61,13 @@  #define SAI_SYNC_INTERNAL	0x1  #define SAI_SYNC_EXTERNAL	0x2 +#define STM_SAI_PROTOCOL_IS_SPDIF(ip)	((ip)->spdif) +#define STM_SAI_HAS_SPDIF(x)	((x)->pdata->conf->has_spdif)  #define STM_SAI_HAS_EXT_SYNC(x) (!STM_SAI_IS_F4(sai->pdata)) +#define SAI_IEC60958_BLOCK_FRAMES	192 +#define SAI_IEC60958_STATUS_BYTES	24 +  /**   * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)   * @pdev: device data pointer @@ -78,6 +85,7 @@   * @id: SAI sub block id corresponding to sub-block A or B   * @dir: SAI block direction (playback or capture). set at init   * @master: SAI block mode flag. (true=master, false=slave) set at init + * @spdif: SAI S/PDIF iec60958 mode flag. set at init   * @fmt: SAI block format. relevant only for custom protocols. set at init   * @sync: SAI block synchronization mode. (none, internal or external)   * @synco: SAI block ext sync source (provider setting). (none, sub-block A/B) @@ -87,6 +95,8 @@   * @slot_width: rx or tx slot width in bits   * @slot_mask: rx or tx active slots mask. set at init or at runtime   * @data_size: PCM data width. corresponds to PCM substream width. + * @spdif_frm_cnt: S/PDIF playback frame counter + * @spdif_status_bits: S/PDIF status bits   */  struct stm32_sai_sub_data {  	struct platform_device *pdev; @@ -104,6 +114,7 @@ struct stm32_sai_sub_data {  	unsigned int id;  	int dir;  	bool master; +	bool spdif;  	int fmt;  	int sync;  	int synco; @@ -113,6 +124,8 @@ struct stm32_sai_sub_data {  	int slot_width;  	int slot_mask;  	int data_size; +	unsigned int spdif_frm_cnt; +	unsigned char spdif_status_bits[SAI_IEC60958_STATUS_BYTES];  };  enum stm32_sai_fifo_th { @@ -171,6 +184,10 @@ static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)  	}  } +static const unsigned char default_status_bits[SAI_IEC60958_STATUS_BYTES] = { +	0, 0, 0, IEC958_AES3_CON_FS_48000, +}; +  static const struct regmap_config stm32_sai_sub_regmap_config_f4 = {  	.reg_bits = 32,  	.reg_stride = 4, @@ -277,6 +294,11 @@ static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,  	struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);  	int slotr, slotr_mask, slot_size; +	if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { +		dev_warn(cpu_dai->dev, "Slot setting relevant only for TDM\n"); +		return 0; +	} +  	dev_dbg(cpu_dai->dev, "Masks tx/rx:%#x/%#x, slots:%d, width:%d\n",  		tx_mask, rx_mask, slots, slot_width); @@ -326,8 +348,17 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)  	dev_dbg(cpu_dai->dev, "fmt %x\n", fmt); -	cr1_mask = SAI_XCR1_PRTCFG_MASK; -	cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL); +	/* Do not generate master by default */ +	cr1 = SAI_XCR1_NODIV; +	cr1_mask = SAI_XCR1_NODIV; + +	cr1_mask |= SAI_XCR1_PRTCFG_MASK; +	if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { +		cr1 |= SAI_XCR1_PRTCFG_SET(SAI_SPDIF_PROTOCOL); +		goto conf_update; +	} + +	cr1 |= SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL);  	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {  	/* SCK active high for all protocols */ @@ -409,10 +440,7 @@ static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)  	cr1_mask |= SAI_XCR1_SLAVE; -	/* do not generate master by default */ -	cr1 |= SAI_XCR1_NODIV; -	cr1_mask |= SAI_XCR1_NODIV; - +conf_update:  	ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);  	if (ret < 0) {  		dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); @@ -478,6 +506,12 @@ static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,  			   SAI_XCR2_FFLUSH |  			   SAI_XCR2_FTH_SET(STM_SAI_FIFO_TH_HALF)); +	/* DS bits in CR1 not set for SPDIF (size forced to 24 bits).*/ +	if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { +		sai->spdif_frm_cnt = 0; +		return 0; +	} +  	/* Mode, data format and channel config */  	cr1_mask = SAI_XCR1_DS_MASK;  	switch (params_format(params)) { @@ -592,13 +626,14 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,  	int cr1, mask, div = 0;  	int sai_clk_rate, mclk_ratio, den, ret;  	int version = sai->pdata->conf->version; +	unsigned int rate = params_rate(params);  	if (!sai->mclk_rate) {  		dev_err(cpu_dai->dev, "Mclk rate is null\n");  		return -EINVAL;  	} -	if (!(params_rate(params) % 11025)) +	if (!(rate % 11025))  		clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);  	else  		clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k); @@ -623,24 +658,28 @@ static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,  		 *      MCKDIV = sai_ck / (frl x ws)	(NOMCK=1)  		 * Note: NOMCK/NODIV correspond to same bit.  		 */ -		if (sai->mclk_rate) { -			mclk_ratio = sai->mclk_rate / params_rate(params); -			if (mclk_ratio != 256) { +		if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) { +			div = DIV_ROUND_CLOSEST(sai_clk_rate, +						(params_rate(params) * 128)); +		} else { +			if (sai->mclk_rate) { +				mclk_ratio = sai->mclk_rate / rate;  				if (mclk_ratio == 512) {  					mask = SAI_XCR1_OSR;  					cr1 = SAI_XCR1_OSR; -				} else { +				} else if (mclk_ratio != 256) {  					dev_err(cpu_dai->dev,  						"Wrong mclk ratio %d\n",  						mclk_ratio);  					return -EINVAL;  				} +				div = DIV_ROUND_CLOSEST(sai_clk_rate, +							sai->mclk_rate); +			} else { +				/* mclk-fs not set, master clock not active */ +				den = sai->fs_length * params_rate(params); +				div = DIV_ROUND_CLOSEST(sai_clk_rate, den);  			} -			div = DIV_ROUND_CLOSEST(sai_clk_rate, sai->mclk_rate); -		} else { -			/* mclk-fs not set, master clock not active. NOMCK=1 */ -			den = sai->fs_length * params_rate(params); -			div = DIV_ROUND_CLOSEST(sai_clk_rate, den);  		}  	} @@ -670,10 +709,12 @@ static int stm32_sai_hw_params(struct snd_pcm_substream *substream,  	sai->data_size = params_width(params); -	ret = stm32_sai_set_slots(cpu_dai); -	if (ret < 0) -		return ret; -	stm32_sai_set_frame(cpu_dai); +	if (!STM_SAI_PROTOCOL_IS_SPDIF(sai)) { +		ret = stm32_sai_set_slots(cpu_dai); +		if (ret < 0) +			return ret; +		stm32_sai_set_frame(cpu_dai); +	}  	ret = stm32_sai_set_config(cpu_dai, substream, params);  	if (ret) @@ -723,6 +764,9 @@ static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd,  					 (unsigned int)~SAI_XCR1_DMAEN);  		if (ret < 0)  			dev_err(cpu_dai->dev, "Failed to update CR1 register\n"); + +		if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) +			sai->spdif_frm_cnt = 0;  		break;  	default:  		return -EINVAL; @@ -776,6 +820,10 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)  				     sai->synco, sai->synci);  	} +	if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) +		memcpy(sai->spdif_status_bits, default_status_bits, +		       sizeof(default_status_bits)); +  	cr1_mask |= SAI_XCR1_SYNCEN_MASK;  	cr1 |= SAI_XCR1_SYNCEN_SET(sai->sync); @@ -792,6 +840,42 @@ static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = {  	.shutdown	= stm32_sai_shutdown,  }; +static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream, +				       int channel, unsigned long hwoff, +				       void *buf, unsigned long bytes) +{ +	struct snd_pcm_runtime *runtime = substream->runtime; +	struct snd_soc_pcm_runtime *rtd = substream->private_data; +	struct snd_soc_dai *cpu_dai = rtd->cpu_dai; +	struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev); +	int *ptr = (int *)(runtime->dma_area + hwoff + +			   channel * (runtime->dma_bytes / runtime->channels)); +	ssize_t cnt = bytes_to_samples(runtime, bytes); +	unsigned int frm_cnt = sai->spdif_frm_cnt; +	unsigned int byte; +	unsigned int mask; + +	do { +		*ptr = ((*ptr >> 8) & 0x00ffffff); + +		/* Set channel status bit */ +		byte = frm_cnt >> 3; +		mask = 1 << (frm_cnt - (byte << 3)); +		if (sai->spdif_status_bits[byte] & mask) +			*ptr |= 0x04000000; +		ptr++; + +		if (!(cnt % 2)) +			frm_cnt++; + +		if (frm_cnt == SAI_IEC60958_BLOCK_FRAMES) +			frm_cnt = 0; +	} while (--cnt); +	sai->spdif_frm_cnt = frm_cnt; + +	return 0; +} +  static const struct snd_pcm_hardware stm32_sai_pcm_hw = {  	.info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,  	.buffer_bytes_max = 8 * PAGE_SIZE, @@ -842,8 +926,14 @@ static struct snd_soc_dai_driver stm32_sai_capture_dai[] = {  };  static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = { -	.pcm_hardware	= &stm32_sai_pcm_hw, -	.prepare_slave_config	= snd_dmaengine_pcm_prepare_slave_config, +	.pcm_hardware = &stm32_sai_pcm_hw, +	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +}; + +static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config_spdif = { +	.pcm_hardware = &stm32_sai_pcm_hw, +	.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, +	.process = stm32_sai_pcm_process_spdif,  };  static const struct snd_soc_component_driver stm32_component = { @@ -900,6 +990,18 @@ static int stm32_sai_sub_parse_of(struct platform_device *pdev,  		return -EINVAL;  	} +	/* Get spdif iec60958 property */ +	sai->spdif = false; +	if (of_get_property(np, "st,iec60958", NULL)) { +		if (!STM_SAI_HAS_SPDIF(sai) || +		    sai->dir == SNDRV_PCM_STREAM_CAPTURE) { +			dev_err(&pdev->dev, "S/PDIF IEC60958 not supported\n"); +			return -EINVAL; +		} +		sai->spdif = true; +		sai->master = true; +	} +  	/* Get synchronization property */  	args.np = NULL;  	ret = of_parse_phandle_with_fixed_args(np, "st,sync", 1, 0, &args); @@ -999,6 +1101,7 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)  {  	struct stm32_sai_sub_data *sai;  	const struct of_device_id *of_id; +	const struct snd_dmaengine_pcm_config *conf = &stm32_sai_pcm_config;  	int ret;  	sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); @@ -1039,8 +1142,10 @@ static int stm32_sai_sub_probe(struct platform_device *pdev)  	if (ret)  		return ret; -	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, -					      &stm32_sai_pcm_config, 0); +	if (STM_SAI_PROTOCOL_IS_SPDIF(sai)) +		conf = &stm32_sai_pcm_config_spdif; + +	ret = devm_snd_dmaengine_pcm_register(&pdev->dev, conf, 0);  	if (ret) {  		dev_err(&pdev->dev, "Could not register pcm dma\n");  		return ret;  | 
