From: mpa@pengutronix.de (Markus Pargmann)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3 12/15] ASoC: fsl-ssi: imx ac97 support
Date: Sun, 14 Apr 2013 13:42:56 +0200 [thread overview]
Message-ID: <1365939779-4507-13-git-send-email-mpa@pengutronix.de> (raw)
In-Reply-To: <1365939779-4507-1-git-send-email-mpa@pengutronix.de>
This patch copies some parts from imx-ssi to support AC97 on
imx27-pca100 and imx27-pcm043. This is a implementation of the
ac97-slave mode.
For ac97, the registers have to be setup earlier than for other ssi
modes because there is some communication with the external device
before actual streaming. So this patch introduces a fsl_ssi_setup
function to setup the registers at different times.
This patch was tested with imx27-pca100.
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---
Notes:
Changes in v3:
- Cleanup ac97 code by adding a fsl_ssi_setup function for initial register
setup. For ac97 the registers have to be setup earlier than in normal
mode.
sound/soc/fsl/fsl_ssi.c | 412 +++++++++++++++++++++++++++++++++++++++---------
1 file changed, 340 insertions(+), 72 deletions(-)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 1ab66f8..0ffb898 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -121,6 +121,7 @@ struct fsl_ssi_private {
bool new_binding;
bool ssi_on_imx;
+ bool imx_ac97;
bool use_dma;
struct clk *clk;
struct platform_device *imx_pcm_pdev;
@@ -130,6 +131,9 @@ struct fsl_ssi_private {
struct imx_dma_data filter_data_rx;
struct imx_pcm_fiq_params fiq_params;
+ void (*ac97_reset) (struct snd_ac97 *ac97);
+ void (*ac97_warm_reset)(struct snd_ac97 *ac97);
+
struct {
unsigned int rfrc;
unsigned int tfrc;
@@ -301,6 +305,124 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
return ret;
}
+static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
+{
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+ u8 i2s_mode;
+ u8 wm;
+ int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
+
+ if (ssi_private->imx_ac97)
+ i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
+ else
+ i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+
+ /*
+ * Section 16.5 of the MPC8610 reference manual says that the SSI needs
+ * to be disabled before updating the registers we set here.
+ */
+ write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
+
+ /*
+ * Program the SSI into I2S Slave Non-Network Synchronous mode. Also
+ * enable the transmit and receive FIFO.
+ *
+ * FIXME: Little-endian samples require a different shift dir
+ */
+ write_ssi_mask(&ssi->scr,
+ CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
+ CCSR_SSI_SCR_TFR_CLK_DIS |
+ i2s_mode |
+ (synchronous ? CCSR_SSI_SCR_SYN : 0));
+
+ write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
+ CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
+ CCSR_SSI_STCR_TSCKP, &ssi->stcr);
+
+ write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
+ CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
+ CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
+ /*
+ * The DC and PM bits are only used if the SSI is the clock master.
+ */
+
+ /*
+ * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
+ * use FIFO 1. We program the transmit water to signal a DMA transfer
+ * if there are only two (or fewer) elements left in the FIFO. Two
+ * elements equals one frame (left channel, right channel). This value,
+ * however, depends on the depth of the transmit buffer.
+ *
+ * We set the watermark on the same level as the DMA burstsize. For
+ * fiq it is probably better to use the biggest possible watermark
+ * size.
+ */
+ if (ssi_private->use_dma)
+ wm = ssi_private->fifo_depth - 2;
+ else
+ wm = ssi_private->fifo_depth;
+
+ write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
+ CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
+ &ssi->sfcsr);
+
+ /*
+ * For non-ac97 setups, we keep the SSI disabled because if we enable
+ * it, then the DMA controller will start. It's not supposed to start
+ * until the SCR.TE (or SCR.RE) bit is set, but it does anyway. The DMA
+ * controller will transfer one "BWC" of data (i.e. the amount of data
+ * that the MR.BWC bits are set to). The reason this is bad is because
+ * at this point, the PCM driver has not finished initializing the DMA
+ * controller.
+ */
+
+
+ /*
+ * For ac97 interrupts are enabled with the startup of the substream
+ * because it is also running without an active substream. Normally SSI
+ * is only enabled when there is a substream.
+ */
+ if (!ssi_private->imx_ac97) {
+ /* Enable the interrupts and DMA requests */
+ if (ssi_private->use_dma)
+ write_ssi(SIER_FLAGS, &ssi->sier);
+ else
+ write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN |
+ CCSR_SSI_SIER_RIE |
+ CCSR_SSI_SIER_RFF0_EN, &ssi->sier);
+ } else {
+ /*
+ * Setup the clock control register
+ */
+ write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
+ &ssi->stccr);
+ write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
+ &ssi->srccr);
+
+ /*
+ * Enable AC97 mode and startup the SSI
+ */
+ write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV,
+ &ssi->sacnt);
+ write_ssi(0xff, &ssi->saccdis);
+ write_ssi(0x300, &ssi->saccen);
+
+ /*
+ * Enable SSI
+ */
+ write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN);
+ write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
+
+ /*
+ * Enable Transmit and Receive
+ */
+ write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
+ }
+
+ return 0;
+}
+
+
/**
* fsl_ssi_startup: create a new substream
*
@@ -322,75 +444,14 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
* and initialize the SSI registers.
*/
if (!ssi_private->first_stream) {
- struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-
ssi_private->first_stream = substream;
/*
- * Section 16.5 of the MPC8610 reference manual says that the
- * SSI needs to be disabled before updating the registers we set
- * here.
- */
- write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
-
- /*
- * Program the SSI into I2S Slave Non-Network Synchronous mode.
- * Also enable the transmit and receive FIFO.
- *
- * FIXME: Little-endian samples require a different shift dir
- */
- write_ssi_mask(&ssi->scr,
- CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
- CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
- | (synchronous ? CCSR_SSI_SCR_SYN : 0));
-
- write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
- CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
- CCSR_SSI_STCR_TSCKP, &ssi->stcr);
-
- write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
- CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
- CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
-
- /*
- * The DC and PM bits are only used if the SSI is the clock
- * master.
- */
-
- /* Enable the interrupts and DMA requests */
- if (ssi_private->use_dma)
- write_ssi(SIER_FLAGS, &ssi->sier);
- else
- write_ssi(CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN |
- CCSR_SSI_SIER_RIE |
- CCSR_SSI_SIER_RFF0_EN, &ssi->sier);
-
- /*
- * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
- * don't use FIFO 1. We program the transmit water to signal a
- * DMA transfer if there are only two (or fewer) elements left
- * in the FIFO. Two elements equals one frame (left channel,
- * right channel). This value, however, depends on the depth of
- * the transmit buffer.
- *
- * We program the receive FIFO to notify us if at least two
- * elements (one frame) have been written to the FIFO. We could
- * make this value larger (and maybe we should), but this way
- * data will be written to memory as soon as it's available.
- */
- write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
- CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
- &ssi->sfcsr);
-
- /*
- * We keep the SSI disabled because if we enable it, then the
- * DMA controller will start. It's not supposed to start until
- * the SCR.TE (or SCR.RE) bit is set, but it does anyway. The
- * DMA controller will transfer one "BWC" of data (i.e. the
- * amount of data that the MR.BWC bits are set to). The reason
- * this is bad is because at this point, the PCM driver has not
- * finished initializing the DMA controller.
+ * fsl_ssi_setup was already called by ac97_init earlier if
+ * the driver is in ac97 mode.
*/
+ if (!ssi_private->imx_ac97)
+ fsl_ssi_setup(ssi_private);
} else {
if (synchronous) {
struct snd_pcm_runtime *first_runtime =
@@ -545,8 +606,9 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
/*
* If this is the last active substream, disable the SSI.
+ * If AC97 is active, we don't want to disable SSI.
*/
- if (!ssi_private->first_stream) {
+ if (!ssi_private->first_stream && !ssi_private->imx_ac97) {
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
@@ -595,6 +657,176 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
.name = "fsl-ssi",
};
+/**
+ * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the
+ * transfer of data.
+ */
+static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+ struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE |
+ CCSR_SSI_SIER_TFE0_EN);
+ else
+ write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE |
+ CCSR_SSI_SIER_RFF0_EN);
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE |
+ CCSR_SSI_SIER_TFE0_EN, 0);
+ else
+ write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE |
+ CCSR_SSI_SIER_RFF0_EN, 0);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
+ else
+ write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = {
+ .startup = fsl_ssi_startup,
+ .shutdown = fsl_ssi_shutdown,
+ .trigger = fsl_ssi_ac97_trigger,
+};
+
+static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
+ .ac97_control = 1,
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &fsl_ssi_ac97_dai_ops,
+};
+
+
+static struct fsl_ssi_private *fsl_ac97_data;
+
+static void fsl_ssi_ac97_init(void)
+{
+ fsl_ssi_setup(fsl_ac97_data);
+}
+
+static void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+ unsigned short val)
+{
+ struct ccsr_ssi *ssi = fsl_ac97_data->ssi;
+ unsigned int lreg;
+ unsigned int lval;
+
+ if (reg > 0x7f)
+ return;
+
+
+ lreg = reg << 12;
+ write_ssi(lreg, &ssi->sacadd);
+
+ lval = val << 4;
+ write_ssi(lval , &ssi->sacdat);
+
+ write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK,
+ CCSR_SSI_SACNT_WR);
+ udelay(100);
+}
+
+static unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
+ unsigned short reg)
+{
+ struct ccsr_ssi *ssi = fsl_ac97_data->ssi;
+
+ unsigned short val = -1;
+ unsigned int lreg;
+
+ lreg = (reg & 0x7f) << 12 ;
+ write_ssi(lreg, &ssi->sacadd);
+ write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK,
+ CCSR_SSI_SACNT_RD);
+
+ udelay(100);
+
+ val = (read_ssi(&ssi->sacdat) >> 4) & 0xffff;
+
+ return val;
+}
+
+static void fsl_ssi_ac97_reset(struct snd_ac97 *ac97)
+{
+ struct fsl_ssi_private *ssi_private = fsl_ac97_data;
+
+ if (ssi_private->ac97_reset)
+ ssi_private->ac97_reset(ac97);
+ /* First read sometimes fails, do a dummy read */
+ fsl_ssi_ac97_read(ac97, 0);
+}
+
+static void fsl_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct fsl_ssi_private *ssi_private = fsl_ac97_data;
+
+ if (ssi_private->ac97_warm_reset)
+ ssi_private->ac97_warm_reset(ac97);
+
+ /* First read sometimes fails, do a dummy read */
+ fsl_ssi_ac97_read(ac97, 0);
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+ .read = fsl_ssi_ac97_read,
+ .write = fsl_ssi_ac97_write,
+ .reset = fsl_ssi_ac97_reset,
+ .warm_reset = fsl_ssi_ac97_warm_reset
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+/*
+ * Pointer to AC97 reset functions for specific boards
+ */
+#if IS_ENABLED(CONFIG_MACH_PCA100)
+extern void pca100_ac97_cold_reset(struct snd_ac97 *ac97);
+extern void pca100_ac97_warm_reset(struct snd_ac97 *ac97);
+#else
+void pca100_ac97_cold_reset(struct snd_ac97 *ac97) { }
+void pca100_ac97_warm_reset(struct snd_ac97 *ac97) { }
+#endif
+
+#if IS_ENABLED(CONFIG_MACH_PCM043)
+extern void pcm043_ac97_cold_reset(struct snd_ac97 *ac97);
+extern void pcm043_ac97_warm_reset(struct snd_ac97 *ac97);
+#else
+void pcm043_ac97_cold_reset(struct snd_ac97 *ac97) { }
+void pcm043_ac97_warm_reset(struct snd_ac97 *ac97) { }
+#endif
+
+
/* Show the statistics of a flag only if its interrupt is enabled. The
* compiler will optimze this code to a no-op if the interrupt is not
* enabled.
@@ -671,6 +903,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
struct resource res;
char name[64];
bool shared;
+ bool ac97 = false;
/* SSIs that are not connected on the board should have a
* status = "disabled"
@@ -681,7 +914,13 @@ static int fsl_ssi_probe(struct platform_device *pdev)
/* We only support the SSI in "I2S Slave" mode */
sprop = of_get_property(np, "fsl,mode", NULL);
- if (!sprop || strcmp(sprop, "i2s-slave")) {
+ if (!sprop) {
+ dev_err(&pdev->dev, "fsl,mode property is necessary\n");
+ return -EINVAL;
+ }
+ if (!strcmp(sprop, "ac97-slave")) {
+ ac97 = true;
+ } else if (strcmp(sprop, "i2s-slave")) {
dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
return -ENODEV;
}
@@ -697,13 +936,39 @@ static int fsl_ssi_probe(struct platform_device *pdev)
strcpy(ssi_private->name, p);
- /* Initialize this copy of the CPU DAI driver structure */
- memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
- sizeof(fsl_ssi_dai_template));
- ssi_private->cpu_dai_drv.name = ssi_private->name;
-
ssi_private->use_dma = !of_property_read_bool(np, "fsl,imx-fiq");
+ if (ac97) {
+ sprop = of_get_property(of_find_node_by_path("/"), "compatible",
+ NULL);
+ p = strrchr(sprop, ',');
+ if (p)
+ sprop = p + 1;
+
+ if (!strcmp(sprop, "imx27-pca100")) {
+ ssi_private->ac97_reset = pca100_ac97_cold_reset;
+ ssi_private->ac97_warm_reset = pca100_ac97_warm_reset;
+ } else if (!strcmp(sprop, "imx27-pcm043")) {
+ ssi_private->ac97_reset = pcm043_ac97_cold_reset;
+ ssi_private->ac97_warm_reset = pcm043_ac97_warm_reset;
+ } else {
+ dev_err(&pdev->dev, "Failed to enable ssi AC97 mode, unknown board.\n");
+ ret = -EINVAL;
+ goto error_kmalloc;
+ }
+
+ memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
+ sizeof(fsl_ssi_ac97_dai));
+
+ fsl_ac97_data = ssi_private;
+ ssi_private->imx_ac97 = true;
+ } else {
+ /* Initialize this copy of the CPU DAI driver structure */
+ memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
+ sizeof(fsl_ssi_dai_template));
+ }
+ ssi_private->cpu_dai_drv.name = ssi_private->name;
+
/* Get the addresses and IRQ */
ret = of_address_to_resource(np, 0, &res);
if (ret) {
@@ -894,6 +1159,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
}
done:
+ if (ssi_private->imx_ac97)
+ fsl_ssi_ac97_init();
+
return 0;
error_dai:
--
1.8.1.5
next prev parent reply other threads:[~2013-04-14 11:42 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-04-14 11:42 [PATCH v3 00/15] ASoC: fsl-ssi: ac97-slave support Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 01/15] ASoC: imx-ssi: Fix includes Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 02/15] ASoC: dmaengine-pcm: Replace of specific helper function Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 03/15] ASoC: dmaengine-pcm: Fix function name Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 04/15] ASoC: dmaengine-pcm: Fix pcm_request_chan " Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 05/15] ASoC: phycore-ac97: Add DT support Markus Pargmann
2013-04-16 11:59 ` Mark Brown
2013-04-16 15:23 ` Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 06/15] ASoC: imx-pcm-dma: " Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 07/15] ASoC: imx-pcm-fiq: Introduce pcm-fiq-params Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 08/15] ASoC: fsl-ssi: Add SACNT definitions Markus Pargmann
2013-04-16 12:03 ` Mark Brown
2013-04-14 11:42 ` [PATCH v3 09/15] ASoC: fsl-ssi: Add support for imx-pcm-fiq Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 10/15] ASoC: fsl-ssi: Use generic DMA bindings if possible Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 11/15] ARM: imx: Export ac97 reset functions Markus Pargmann
2013-04-14 11:42 ` Markus Pargmann [this message]
2013-04-14 11:42 ` [PATCH v3 13/15] ASoC: fsl: Kconfig: Use fsl-ssi for phycore-ac97 Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 14/15] ASoC: fsl: Move fsl-ssi binding doc to sound/ Markus Pargmann
2013-04-14 11:42 ` [PATCH v3 15/15] ASoC: fsl: Update fsl-ssi binding doc Markus Pargmann
2013-04-14 12:13 ` [PATCH v3 00/15] ASoC: fsl-ssi: ac97-slave support Lars-Peter Clausen
2013-04-16 0:25 ` Timur Tabi
2013-04-16 8:06 ` Lars-Peter Clausen
2013-04-17 14:12 ` Mark Brown
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1365939779-4507-13-git-send-email-mpa@pengutronix.de \
--to=mpa@pengutronix.de \
--cc=linux-arm-kernel@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).