diff options
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.patch | 246 |
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; + } + |