aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.14/950-0116-ASoC-bcm2835-Support-left-right-justified-and-DSP-mo.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-4.14/950-0116-ASoC-bcm2835-Support-left-right-justified-and-DSP-mo.patch')
-rw-r--r--target/linux/brcm2708/patches-4.14/950-0116-ASoC-bcm2835-Support-left-right-justified-and-DSP-mo.patch246
1 files changed, 246 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-4.14/950-0116-ASoC-bcm2835-Support-left-right-justified-and-DSP-mo.patch b/target/linux/brcm2708/patches-4.14/950-0116-ASoC-bcm2835-Support-left-right-justified-and-DSP-mo.patch
new file mode 100644
index 0000000000..af002128fa
--- /dev/null
+++ b/target/linux/brcm2708/patches-4.14/950-0116-ASoC-bcm2835-Support-left-right-justified-and-DSP-mo.patch
@@ -0,0 +1,246 @@
+From d56239568d5f3b2a5519e9b08cba847c1354ad54 Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+Date: Sun, 7 May 2017 15:30:50 +0200
+Subject: [PATCH 116/454] ASoC: bcm2835: Support left/right justified and DSP
+ modes
+
+DSP modes and left/right justified modes can be supported
+on bcm2835 by configuring the frame sync polarity and
+frame sync length registers and by adjusting the
+channel data position registers.
+
+Clock and frame sync polarity handling in hw_params has
+been refactored to make the interaction between logical
+rising/falling edge frame start and physical configuration
+(changed by normal/inverted polarity modes) clearer.
+
+Modes where the first active data bit is transmitted immediately
+after frame start (eg DSP mode B with slot 0 active)
+only work reliable if bcm2835 is configured as frame master.
+In frame slave mode channel swap (or shift, this isn't quite
+clear yet) can occur.
+
+Currently the driver only warns if an unstable configuration
+is detected but doensn't prevent using them.
+
+Signed-off-by: Matthias Reichl <hias@horus.com>
+---
+ sound/soc/bcm/bcm2835-i2s.c | 152 +++++++++++++++++++++++-------------
+ 1 file changed, 99 insertions(+), 53 deletions(-)
+
+--- a/sound/soc/bcm/bcm2835-i2s.c
++++ b/sound/soc/bcm/bcm2835-i2s.c
+@@ -344,6 +344,9 @@ static int bcm2835_i2s_hw_params(struct
+ unsigned int rx_mask, tx_mask;
+ unsigned int rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos;
+ unsigned int mode, format;
++ bool bit_clock_master = false;
++ bool frame_sync_master = false;
++ bool frame_start_falling_edge = false;
+ uint32_t csreg;
+ int ret = 0;
+
+@@ -387,16 +390,39 @@ static int bcm2835_i2s_hw_params(struct
+ if (data_length > slot_width)
+ return -EINVAL;
+
+- /* Clock should only be set up here if CPU is clock master */
++ /* Check if CPU is bit clock master */
+ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBS_CFM:
+- ret = clk_set_rate(dev->clk, bclk_rate);
+- if (ret)
+- return ret;
++ bit_clock_master = true;
++ break;
++ case SND_SOC_DAIFMT_CBM_CFS:
++ case SND_SOC_DAIFMT_CBM_CFM:
++ bit_clock_master = false;
+ break;
+ default:
++ return -EINVAL;
++ }
++
++ /* Check if CPU is frame sync master */
++ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBS_CFS:
++ case SND_SOC_DAIFMT_CBM_CFS:
++ frame_sync_master = true;
++ break;
++ case SND_SOC_DAIFMT_CBS_CFM:
++ case SND_SOC_DAIFMT_CBM_CFM:
++ frame_sync_master = false;
+ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* Clock should only be set up here if CPU is clock master */
++ if (bit_clock_master) {
++ ret = clk_set_rate(dev->clk, bclk_rate);
++ if (ret)
++ return ret;
+ }
+
+ /* Setup the frame format */
+@@ -427,13 +453,41 @@ static int bcm2835_i2s_hw_params(struct
+
+ /* Setup frame sync signal for 50% duty cycle */
+ framesync_length = frame_length / 2;
++ frame_start_falling_edge = true;
++ break;
++ case SND_SOC_DAIFMT_LEFT_J:
++ if (slots & 1)
++ return -EINVAL;
++
++ odd_slot_offset = slots >> 1;
++ data_delay = 0;
++ framesync_length = frame_length / 2;
++ frame_start_falling_edge = false;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ if (slots & 1)
++ return -EINVAL;
++
++ /* Odd frame lengths aren't supported */
++ if (frame_length & 1)
++ return -EINVAL;
++
++ odd_slot_offset = slots >> 1;
++ data_delay = slot_width - data_length;
++ framesync_length = frame_length / 2;
++ frame_start_falling_edge = false;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ data_delay = 1;
++ framesync_length = 1;
++ frame_start_falling_edge = false;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ data_delay = 0;
++ framesync_length = 1;
++ frame_start_falling_edge = false;
+ break;
+ default:
+- /*
+- * TODO
+- * Others are possible but are not implemented at the moment.
+- */
+- dev_err(dev->dev, "%s:bad format\n", __func__);
+ return -EINVAL;
+ }
+
+@@ -443,6 +497,15 @@ static int bcm2835_i2s_hw_params(struct
+ tx_mask, slot_width, data_delay, odd_slot_offset);
+
+ /*
++ * Transmitting data immediately after frame start, eg
++ * in left-justified or DSP mode A, only works stable
++ * if bcm2835 is the frame clock master.
++ */
++ if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_master)
++ dev_warn(dev->dev,
++ "Unstable slave config detected, L/R may be swapped");
++
++ /*
+ * Set format for both streams.
+ * We cannot set another frame length
+ * (and therefore word length) anyway,
+@@ -472,62 +535,38 @@ static int bcm2835_i2s_hw_params(struct
+ mode |= BCM2835_I2S_FLEN(frame_length - 1);
+ mode |= BCM2835_I2S_FSLEN(framesync_length);
+
+- /* Master or slave? */
+- switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+- case SND_SOC_DAIFMT_CBS_CFS:
+- /* CPU is master */
+- break;
+- case SND_SOC_DAIFMT_CBM_CFS:
+- /*
+- * CODEC is bit clock master
+- * CPU is frame master
+- */
++ /* CLKM selects bcm2835 clock slave mode */
++ if (!bit_clock_master)
+ mode |= BCM2835_I2S_CLKM;
+- break;
+- case SND_SOC_DAIFMT_CBS_CFM:
+- /*
+- * CODEC is frame master
+- * CPU is bit clock master
+- */
++
++ /* FSM selects bcm2835 frame sync slave mode */
++ if (!frame_sync_master)
+ mode |= BCM2835_I2S_FSM;
++
++ /* CLKI selects normal clocking mode, sampling on rising edge */
++ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ case SND_SOC_DAIFMT_NB_IF:
++ mode |= BCM2835_I2S_CLKI;
+ break;
+- case SND_SOC_DAIFMT_CBM_CFM:
+- /* CODEC is master */
+- mode |= BCM2835_I2S_CLKM;
+- mode |= BCM2835_I2S_FSM;
++ case SND_SOC_DAIFMT_IB_NF:
++ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+- dev_err(dev->dev, "%s:bad master\n", __func__);
+ return -EINVAL;
+ }
+
+- /*
+- * Invert clocks?
+- *
+- * The BCM approach seems to be inverted to the classical I2S approach.
+- */
++ /* FSI selects frame start on falling edge */
+ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+- /* None. Therefore, both for BCM */
+- mode |= BCM2835_I2S_CLKI;
+- mode |= BCM2835_I2S_FSI;
+- break;
+- case SND_SOC_DAIFMT_IB_IF:
+- /* Both. Therefore, none for BCM */
++ case SND_SOC_DAIFMT_IB_NF:
++ if (frame_start_falling_edge)
++ mode |= BCM2835_I2S_FSI;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+- /*
+- * Invert only frame sync. Therefore,
+- * invert only bit clock for BCM
+- */
+- mode |= BCM2835_I2S_CLKI;
+- break;
+- case SND_SOC_DAIFMT_IB_NF:
+- /*
+- * Invert only bit clock. Therefore,
+- * invert only frame sync for BCM
+- */
+- mode |= BCM2835_I2S_FSI;
++ case SND_SOC_DAIFMT_IB_IF:
++ if (!frame_start_falling_edge)
++ mode |= BCM2835_I2S_FSI;
+ break;
+ default:
+ return -EINVAL;
+@@ -563,6 +602,13 @@ static int bcm2835_i2s_hw_params(struct
+ dev_dbg(dev->dev, "sampling rate: %d bclk rate: %d\n",
+ params_rate(params), bclk_rate);
+
++ dev_dbg(dev->dev, "CLKM: %d CLKI: %d FSM: %d FSI: %d frame start: %s edge\n",
++ !!(mode & BCM2835_I2S_CLKM),
++ !!(mode & BCM2835_I2S_CLKI),
++ !!(mode & BCM2835_I2S_FSM),
++ !!(mode & BCM2835_I2S_FSI),
++ (mode & BCM2835_I2S_FSI) ? "falling" : "rising");
++
+ return ret;
+ }
+