Unverified Commit 842860f4 authored by Mark Brown's avatar Mark Brown
Browse files

Merge series "Add I2S-MCC support for Microchip's SAMA7G5" from Codrin...

Merge series "Add I2S-MCC support for Microchip's SAMA7G5" from Codrin Ciubotariu <codrin.ciubotariu@microchip.com>:

SAMA7G5 includes an updated version of I2S-MCC, found previously on
SAM9X60. This controller includes 8 data pins, 4 for playback and 4 for
capture. For I2S and LEFT_J formats, these pins can be used to
send/receive up to 8 audio channels. For DSP_A, with TDM, any pins pair
(DIN/DOUT) from these 4 can be selected to send/receive data. This
version also includes 2 FIFOs (send and receive).
This patch set starts by moving the driver's bindings to yaml and
continues with adding a new compatible for the SAMA7G5 variant, followed
by the changes needed for I2S/LEFT_J support, TDM pin pair selection and
FIFO support, exclusively for SAMA7G5.

Changes in v2:
- moved DT binding conversion patch from the beginning to the end of the
  patch serieses
- patches that update the DT binding are modified to change .txt file
  instead of .yaml

Codrin Ciubotariu (7):
  dt-bindings: mchp,i2s-mcc: Add SAMA7G5 to binding
  ASoC: mchp-i2s-mcc: Add compatible for SAMA7G5
  ASoC: mchp-i2s-mcc: Add multi-channel support for I2S and LEFT_J
    formats
  dt-bindings: mchp,i2s-mcc: Add property to specify pin pair for TDM
  ASoC: mchp-i2s-mcc: Add support to select TDM pins
  ASoC: mchp-i2s-mcc: Add FIFOs support
  ASoC: convert Microchip I2SMCC binding to yaml

 .../bindings/sound/mchp,i2s-mcc.yaml          | 108 ++++++++++++
 .../bindings/sound/mchp-i2s-mcc.txt           |  43 -----
 sound/soc/atmel/Kconfig                       |   3 +
 sound/soc/atmel/mchp-i2s-mcc.c                | 161 +++++++++++++++---
 4 files changed, 252 insertions(+), 63 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/mchp,i2s-mcc.yaml
 delete mode 100644 Documentation/devicetree/bindings/sound/mchp-i2s-mcc.txt

--
2.27.0
parents e4438582 ead2a3e4
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
* Microchip I2S Multi-Channel Controller

Required properties:
- compatible:     Should be "microchip,sam9x60-i2smcc".
- compatible:     Should be "microchip,sam9x60-i2smcc" or
		  "microchip,sama7g5-i2smcc".
- reg:            Should be the physical base address of the controller and the
                  length of memory mapped region.
- interrupts:     Should contain the interrupt for the controller.
@@ -18,7 +19,12 @@ Required properties:
Optional properties:
- pinctrl-0:      Should specify pin control groups used for this controller.
- princtrl-names: Should contain only one value - "default".

- microchip,tdm-data-pair: 8 bit value that represents the DIN/DOUT pair pins
			   which are used to receive/send TDM data. It is optional
			   and it is only needed if the controller uses the TDM
			   mode. Not available for "microchip,sam9x60-i2smcc"
			   compatible. If it's not present, the default value is 0,
			   so the DIN/DOUT 0 pins are used.

(1) : Only the peripheral clock is required. The generated clock is optional
      and should be set mostly when Master Mode is required.
+3 −0
Original line number Diff line number Diff line
@@ -127,10 +127,13 @@ config SND_MCHP_SOC_I2S_MCC
	  Say Y or M if you want to add support for I2S Multi-Channel ASoC
	  driver on the following Microchip platforms:
	  - sam9x60
	  - sama7g5

	  The I2SMCC complies with the Inter-IC Sound (I2S) bus specification
	  and supports a Time Division Multiplexed (TDM) interface with
	  external multi-channel audio codecs.
	  Starting with sama7g5, I2S and Left-Justified multi-channel is
	  supported by using multiple data pins, output and input, without TDM.

config SND_MCHP_SOC_SPDIFTX
	tristate "Microchip ASoC driver for boards using S/PDIF TX"
+141 −20
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/lcm.h>
#include <linux/of_device.h>

#include <sound/core.h>
#include <sound/pcm.h>
@@ -99,6 +100,8 @@
#define MCHP_I2SMCC_MRA_DATALENGTH_8_BITS_COMPACT	(7 << 1)

#define MCHP_I2SMCC_MRA_WIRECFG_MASK		GENMASK(5, 4)
#define MCHP_I2SMCC_MRA_WIRECFG_TDM(pin)	(((pin) << 4) & \
						 MCHP_I2SMCC_MRA_WIRECFG_MASK)
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_1_TDM_0	(0 << 4)
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1	(1 << 4)
#define MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2	(2 << 4)
@@ -173,7 +176,7 @@
 */
#define MCHP_I2SMCC_MRB_CRAMODE_REGULAR		(1 << 0)

#define MCHP_I2SMCC_MRB_FIFOEN			BIT(1)
#define MCHP_I2SMCC_MRB_FIFOEN			BIT(4)

#define MCHP_I2SMCC_MRB_DMACHUNK_MASK		GENMASK(9, 8)
#define MCHP_I2SMCC_MRB_DMACHUNK(no_words) \
@@ -225,6 +228,11 @@ static const struct regmap_config mchp_i2s_mcc_regmap_config = {
	.max_register = MCHP_I2SMCC_VERSION,
};

struct mchp_i2s_mcc_soc_data {
	unsigned int	data_pin_pair_num;
	bool		has_fifo;
};

struct mchp_i2s_mcc_dev {
	struct wait_queue_head			wq_txrdy;
	struct wait_queue_head			wq_rxrdy;
@@ -232,6 +240,7 @@ struct mchp_i2s_mcc_dev {
	struct regmap				*regmap;
	struct clk				*pclk;
	struct clk				*gclk;
	const struct mchp_i2s_mcc_soc_data	*soc;
	struct snd_dmaengine_dai_dma_data	playback;
	struct snd_dmaengine_dai_dma_data	capture;
	unsigned int				fmt;
@@ -239,6 +248,7 @@ struct mchp_i2s_mcc_dev {
	unsigned int				frame_length;
	int					tdm_slots;
	int					channels;
	u8					tdm_data_pair;
	unsigned int				gclk_use:1;
	unsigned int				gclk_running:1;
	unsigned int				tx_rdy:1;
@@ -248,7 +258,7 @@ struct mchp_i2s_mcc_dev {
static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id)
{
	struct mchp_i2s_mcc_dev *dev = dev_id;
	u32 sra, imra, srb, imrb, pendinga, pendingb, idra = 0;
	u32 sra, imra, srb, imrb, pendinga, pendingb, idra = 0, idrb = 0;
	irqreturn_t ret = IRQ_NONE;

	regmap_read(dev->regmap, MCHP_I2SMCC_IMRA, &imra);
@@ -266,23 +276,35 @@ static irqreturn_t mchp_i2s_mcc_interrupt(int irq, void *dev_id)
	 * Tx/Rx ready interrupts are enabled when stopping only, to assure
	 * availability and to disable clocks if necessary
	 */
	if (dev->soc->has_fifo) {
		idrb |= pendingb & (MCHP_I2SMCC_INT_TXFFRDY |
				    MCHP_I2SMCC_INT_RXFFRDY);
	} else {
		idra |= pendinga & (MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels) |
				    MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
	if (idra)
	}
	if (idra || idrb)
		ret = IRQ_HANDLED;

	if ((imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
	if ((!dev->soc->has_fifo &&
	     (imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
	     (imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) ==
	    (idra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels))) {
	     (idra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels))) ||
	    (dev->soc->has_fifo && imrb & MCHP_I2SMCC_INT_TXFFRDY)) {
		dev->tx_rdy = 1;
		wake_up_interruptible(&dev->wq_txrdy);
	}
	if ((imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) &&
	if ((!dev->soc->has_fifo &&
	     (imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) &&
	     (imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) ==
	    (idra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels))) {
	     (idra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels))) ||
	    (dev->soc->has_fifo && imrb & MCHP_I2SMCC_INT_RXFFRDY)) {
		dev->rx_rdy = 1;
		wake_up_interruptible(&dev->wq_rxrdy);
	}
	if (dev->soc->has_fifo)
		regmap_write(dev->regmap, MCHP_I2SMCC_IDRB, idrb);
	else
		regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);

	return ret;
@@ -549,6 +571,17 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
	}

	if (dev->fmt & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
		/* for I2S and LEFT_J one pin is needed for every 2 channels */
		if (channels > dev->soc->data_pin_pair_num * 2) {
			dev_err(dev->dev,
				"unsupported number of audio channels: %d\n",
				channels);
			return -EINVAL;
		}

		/* enable for interleaved format */
		mrb |= MCHP_I2SMCC_MRB_CRAMODE_REGULAR;

		switch (channels) {
		case 1:
			if (is_playback)
@@ -558,6 +591,12 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
			break;
		case 2:
			break;
		case 4:
			mra |= MCHP_I2SMCC_MRA_WIRECFG_I2S_2_TDM_1;
			break;
		case 8:
			mra |= MCHP_I2SMCC_MRA_WIRECFG_I2S_4_TDM_2;
			break;
		default:
			dev_err(dev->dev, "unsupported number of audio channels\n");
			return -EINVAL;
@@ -566,6 +605,8 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
		if (!frame_length)
			frame_length = 2 * params_physical_width(params);
	} else if (dev->fmt & SND_SOC_DAIFMT_DSP_A) {
		mra |= MCHP_I2SMCC_MRA_WIRECFG_TDM(dev->tdm_data_pair);

		if (dev->tdm_slots) {
			if (channels % 2 && channels * 2 <= dev->tdm_slots) {
				/*
@@ -636,6 +677,10 @@ static int mchp_i2s_mcc_hw_params(struct snd_pcm_substream *substream,
		}
	}

	/* enable FIFO if available */
	if (dev->soc->has_fifo)
		mrb |= MCHP_I2SMCC_MRB_FIFOEN;

	/*
	 * If we are already running, the wanted setup must be
	 * the same with the one that's currently ongoing
@@ -698,8 +743,13 @@ static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
		if (err == 0) {
			dev_warn_once(dev->dev,
				      "Timeout waiting for Tx ready\n");
			if (dev->soc->has_fifo)
				regmap_write(dev->regmap, MCHP_I2SMCC_IDRB,
					     MCHP_I2SMCC_INT_TXFFRDY);
			else
				regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
					     MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels));

			dev->tx_rdy = 1;
		}
	} else {
@@ -709,6 +759,10 @@ static int mchp_i2s_mcc_hw_free(struct snd_pcm_substream *substream,
		if (err == 0) {
			dev_warn_once(dev->dev,
				      "Timeout waiting for Rx ready\n");
			if (dev->soc->has_fifo)
				regmap_write(dev->regmap, MCHP_I2SMCC_IDRB,
					     MCHP_I2SMCC_INT_RXFFRDY);
			else
				regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
					     MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
			dev->rx_rdy = 1;
@@ -737,7 +791,7 @@ static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
	struct mchp_i2s_mcc_dev *dev = snd_soc_dai_get_drvdata(dai);
	bool is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
	u32 cr = 0;
	u32 iera = 0;
	u32 iera = 0, ierb = 0;
	u32 sr;
	int err;

@@ -761,6 +815,9 @@ static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
			 * Enable Tx Ready interrupts on all channels
			 * to assure all data is sent
			 */
			if (dev->soc->has_fifo)
				ierb = MCHP_I2SMCC_INT_TXFFRDY;
			else
				iera = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
		} else if (!is_playback && (sr & MCHP_I2SMCC_SR_RXEN)) {
			cr = MCHP_I2SMCC_CR_RXDIS;
@@ -769,6 +826,9 @@ static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
			 * Enable Rx Ready interrupts on all channels
			 * to assure all data is received
			 */
			if (dev->soc->has_fifo)
				ierb = MCHP_I2SMCC_INT_RXFFRDY;
			else
				iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
		}
		break;
@@ -787,6 +847,9 @@ static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
		}
	}

	if (dev->soc->has_fifo)
		regmap_write(dev->regmap, MCHP_I2SMCC_IERB, ierb);
	else
		regmap_write(dev->regmap, MCHP_I2SMCC_IERA, iera);
	regmap_write(dev->regmap, MCHP_I2SMCC_CR, cr);

@@ -869,15 +932,68 @@ static const struct snd_soc_component_driver mchp_i2s_mcc_component = {
};

#ifdef CONFIG_OF
static struct mchp_i2s_mcc_soc_data mchp_i2s_mcc_sam9x60 = {
	.data_pin_pair_num = 1,
};

static struct mchp_i2s_mcc_soc_data mchp_i2s_mcc_sama7g5 = {
	.data_pin_pair_num = 4,
	.has_fifo = true,
};

static const struct of_device_id mchp_i2s_mcc_dt_ids[] = {
	{
		.compatible = "microchip,sam9x60-i2smcc",
		.data = &mchp_i2s_mcc_sam9x60,
	},
	{
		.compatible = "microchip,sama7g5-i2smcc",
		.data = &mchp_i2s_mcc_sama7g5,
	},
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mchp_i2s_mcc_dt_ids);
#endif

static int mchp_i2s_mcc_soc_data_parse(struct platform_device *pdev,
				       struct mchp_i2s_mcc_dev *dev)
{
	int err;

	if (!dev->soc) {
		dev_err(&pdev->dev, "failed to get soc data\n");
		return -ENODEV;
	}

	if (dev->soc->data_pin_pair_num == 1)
		return 0;

	err = of_property_read_u8(pdev->dev.of_node, "microchip,tdm-data-pair",
				  &dev->tdm_data_pair);
	if (err < 0 && err != -EINVAL) {
		dev_err(&pdev->dev,
			"bad property data for 'microchip,tdm-data-pair': %d",
			err);
		return err;
	}
	if (err == -EINVAL) {
		dev_info(&pdev->dev,
			 "'microchip,tdm-data-pair' not found; assuming DIN/DOUT 0 for TDM\n");
		dev->tdm_data_pair = 0;
	} else {
		if (dev->tdm_data_pair > dev->soc->data_pin_pair_num - 1) {
			dev_err(&pdev->dev,
				"invalid value for 'microchip,tdm-data-pair': %d\n",
				dev->tdm_data_pair);
			return -EINVAL;
		}
		dev_dbg(&pdev->dev, "TMD format on DIN/DOUT %d pins\n",
			dev->tdm_data_pair);
	}

	return 0;
}

static int mchp_i2s_mcc_probe(struct platform_device *pdev)
{
	struct mchp_i2s_mcc_dev *dev;
@@ -929,6 +1045,11 @@ static int mchp_i2s_mcc_probe(struct platform_device *pdev)
		dev->gclk = NULL;
	}

	dev->soc = of_device_get_match_data(&pdev->dev);
	err = mchp_i2s_mcc_soc_data_parse(pdev, dev);
	if (err < 0)
		return err;

	dev->dev = &pdev->dev;
	dev->regmap = regmap;
	platform_set_drvdata(pdev, dev);