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
This commit is contained in:
Mark Brown 2021-03-02 14:50:44 +00:00
commit 842860f45d
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 152 additions and 22 deletions

View File

@ -1,7 +1,8 @@
* 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.

View File

@ -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"

View File

@ -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,24 +276,36 @@ 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
*/
idra |= pendinga & (MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels) |
MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
if (idra)
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 || idrb)
ret = IRQ_HANDLED;
if ((imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) &&
(imra & MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels)) ==
(idra & 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))) ||
(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)) &&
(imra & MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels)) ==
(idra & 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))) ||
(dev->soc->has_fifo && imrb & MCHP_I2SMCC_INT_RXFFRDY)) {
dev->rx_rdy = 1;
wake_up_interruptible(&dev->wq_rxrdy);
}
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA, idra);
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");
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels));
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,8 +759,12 @@ 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");
regmap_write(dev->regmap, MCHP_I2SMCC_IDRA,
MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels));
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,7 +815,10 @@ 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
*/
iera = MCHP_I2SMCC_INT_TXRDY_MASK(dev->channels);
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;
dev->rx_rdy = 0;
@ -769,7 +826,10 @@ 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
*/
iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
if (dev->soc->has_fifo)
ierb = MCHP_I2SMCC_INT_RXFFRDY;
else
iera = MCHP_I2SMCC_INT_RXRDY_MASK(dev->channels);
}
break;
default:
@ -787,7 +847,10 @@ static int mchp_i2s_mcc_trigger(struct snd_pcm_substream *substream, int cmd,
}
}
regmap_write(dev->regmap, MCHP_I2SMCC_IERA, iera);
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);
return 0;
@ -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);