Loading Documentation/devicetree/bindings/sound/cs42l56.txt +1 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,7 @@ Example: codec: codec@4b { compatible = "cirrus,cs42l56"; reg = <0x4b>; gpio-reset = <&gpio 10 0>; cirrus,gpio-nreset = <&gpio 10 0>; cirrus,chgfreq-divisor = <0x05>; cirrus.ain1_ref_cfg; cirrus,micbias-lvl = <5>; Loading sound/soc/bcm/bcm2835-i2s.c +290 −101 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ * General Public License for more details. */ #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> Loading Loading @@ -99,6 +100,8 @@ #define BCM2835_I2S_CHWID(v) (v) #define BCM2835_I2S_CH1(v) ((v) << 16) #define BCM2835_I2S_CH2(v) (v) #define BCM2835_I2S_CH1_POS(v) BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(v)) #define BCM2835_I2S_CH2_POS(v) BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(v)) #define BCM2835_I2S_TX_PANIC(v) ((v) << 24) #define BCM2835_I2S_RX_PANIC(v) ((v) << 16) Loading @@ -110,12 +113,19 @@ #define BCM2835_I2S_INT_RXR BIT(1) #define BCM2835_I2S_INT_TXW BIT(0) /* Frame length register is 10 bit, maximum length 1024 */ #define BCM2835_I2S_MAX_FRAME_LENGTH 1024 /* General device struct */ struct bcm2835_i2s_dev { struct device *dev; struct snd_dmaengine_dai_dma_data dma_data[2]; unsigned int fmt; unsigned int bclk_ratio; unsigned int tdm_slots; unsigned int rx_mask; unsigned int tx_mask; unsigned int slot_width; unsigned int frame_length; struct regmap *i2s_regmap; struct clk *clk; Loading Loading @@ -225,19 +235,120 @@ static int bcm2835_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); dev->bclk_ratio = ratio; if (!ratio) { dev->tdm_slots = 0; return 0; } if (ratio > BCM2835_I2S_MAX_FRAME_LENGTH) return -EINVAL; dev->tdm_slots = 2; dev->rx_mask = 0x03; dev->tx_mask = 0x03; dev->slot_width = ratio / 2; dev->frame_length = ratio; return 0; } static int bcm2835_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int width) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (slots) { if (slots < 0 || width < 0) return -EINVAL; /* Limit masks to available slots */ rx_mask &= GENMASK(slots - 1, 0); tx_mask &= GENMASK(slots - 1, 0); /* * The driver is limited to 2-channel setups. * Check that exactly 2 bits are set in the masks. */ if (hweight_long((unsigned long) rx_mask) != 2 || hweight_long((unsigned long) tx_mask) != 2) return -EINVAL; if (slots * width > BCM2835_I2S_MAX_FRAME_LENGTH) return -EINVAL; } dev->tdm_slots = slots; dev->rx_mask = rx_mask; dev->tx_mask = tx_mask; dev->slot_width = width; dev->frame_length = slots * width; return 0; } /* * Convert logical slot number into physical slot number. * * If odd_offset is 0 sequential number is identical to logical number. * This is used for DSP modes with slot numbering 0 1 2 3 ... * * Otherwise odd_offset defines the physical offset for odd numbered * slots. This is used for I2S and left/right justified modes to * translate from logical slot numbers 0 1 2 3 ... into physical slot * numbers 0 2 ... 3 4 ... */ static int bcm2835_i2s_convert_slot(unsigned int slot, unsigned int odd_offset) { if (!odd_offset) return slot; if (slot & 1) return (slot >> 1) + odd_offset; return slot >> 1; } /* * Calculate channel position from mask and slot width. * * Mask must contain exactly 2 set bits. * Lowest set bit is channel 1 position, highest set bit channel 2. * The constant offset is added to both channel positions. * * If odd_offset is > 0 slot positions are translated to * I2S-style TDM slot numbering ( 0 2 ... 3 4 ...) with odd * logical slot numbers starting at physical slot odd_offset. */ static void bcm2835_i2s_calc_channel_pos( unsigned int *ch1_pos, unsigned int *ch2_pos, unsigned int mask, unsigned int width, unsigned int bit_offset, unsigned int odd_offset) { *ch1_pos = bcm2835_i2s_convert_slot((ffs(mask) - 1), odd_offset) * width + bit_offset; *ch2_pos = bcm2835_i2s_convert_slot((fls(mask) - 1), odd_offset) * width + bit_offset; } static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); unsigned int sampling_rate = params_rate(params); unsigned int data_length, data_delay, bclk_ratio; unsigned int ch1pos, ch2pos, mode, format; unsigned int data_length, data_delay, framesync_length; unsigned int slots, slot_width, odd_slot_offset; int frame_length, bclk_rate; 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; /* * If a stream is already enabled, Loading @@ -248,42 +359,70 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON)) return 0; /* * Adjust the data length according to the format. * We prefill the half frame length with an integer * divider of 2400 as explained at the clock settings. * Maybe it is overwritten there, if the Integer mode * does not apply. */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: data_length = 16; break; case SNDRV_PCM_FORMAT_S24_LE: data_length = 24; data_length = params_width(params); data_delay = 0; odd_slot_offset = 0; mode = 0; if (dev->tdm_slots) { slots = dev->tdm_slots; slot_width = dev->slot_width; frame_length = dev->frame_length; rx_mask = dev->rx_mask; tx_mask = dev->tx_mask; bclk_rate = dev->frame_length * params_rate(params); } else { slots = 2; slot_width = params_width(params); rx_mask = 0x03; tx_mask = 0x03; frame_length = snd_soc_params_to_frame_size(params); if (frame_length < 0) return frame_length; bclk_rate = snd_soc_params_to_bclk(params); if (bclk_rate < 0) return bclk_rate; } /* Check if data fits into slots */ if (data_length > slot_width) return -EINVAL; /* 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: bit_clock_master = true; break; case SNDRV_PCM_FORMAT_S32_LE: data_length = 32; case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBM_CFM: bit_clock_master = false; break; default: return -EINVAL; } /* If bclk_ratio already set, use that one. */ if (dev->bclk_ratio) bclk_ratio = dev->bclk_ratio; else /* otherwise calculate a fitting block ratio */ bclk_ratio = 2 * data_length; /* Clock should only be set up here if CPU is clock master */ /* 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: clk_set_rate(dev->clk, sampling_rate * bclk_ratio); case SND_SOC_DAIFMT_CBM_CFM: frame_sync_master = false; break; default: break; 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 */ Loading @@ -294,43 +433,94 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, format |= BCM2835_I2S_CHWID((data_length-8)&0xf); /* CH2 format is the same as for CH1 */ format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: data_delay = 1; break; default: /* I2S mode needs an even number of slots */ if (slots & 1) return -EINVAL; /* * TODO * Others are possible but are not implemented at the moment. * Use I2S-style logical slot numbering: even slots * are in first half of frame, odd slots in second half. */ dev_err(dev->dev, "%s:bad format\n", __func__); odd_slot_offset = slots >> 1; /* MSB starts one cycle after frame start */ data_delay = 1; /* 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; } ch1pos = data_delay; ch2pos = bclk_ratio / 2 + data_delay; /* Odd frame lengths aren't supported */ if (frame_length & 1) return -EINVAL; switch (params_channels(params)) { case 2: format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); format |= BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(ch1pos)); format |= BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(ch2pos)); 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: return -EINVAL; } bcm2835_i2s_calc_channel_pos(&rx_ch1_pos, &rx_ch2_pos, rx_mask, slot_width, data_delay, odd_slot_offset); bcm2835_i2s_calc_channel_pos(&tx_ch1_pos, &tx_ch2_pos, 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, * so the format will be the same. */ regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format); regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format); regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format | BCM2835_I2S_CH1_POS(rx_ch1_pos) | BCM2835_I2S_CH2_POS(rx_ch2_pos)); regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format | BCM2835_I2S_CH1_POS(tx_ch1_pos) | BCM2835_I2S_CH2_POS(tx_ch2_pos)); /* Setup the I2S mode */ mode = 0; if (data_length <= 16) { /* Loading @@ -342,64 +532,40 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP; } mode |= BCM2835_I2S_FLEN(bclk_ratio - 1); mode |= BCM2835_I2S_FSLEN(bclk_ratio / 2); 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; case SND_SOC_DAIFMT_IB_NF: if (frame_start_falling_edge) mode |= BCM2835_I2S_FSI; break; case SND_SOC_DAIFMT_IB_IF: /* Both. Therefore, none for BCM */ 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 */ case SND_SOC_DAIFMT_IB_IF: if (!frame_start_falling_edge) mode |= BCM2835_I2S_FSI; break; default: Loading @@ -423,7 +589,27 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* Clear FIFOs */ bcm2835_i2s_clear_fifos(dev, true, true); return 0; dev_dbg(dev->dev, "slots: %d width: %d rx mask: 0x%02x tx_mask: 0x%02x\n", slots, slot_width, rx_mask, tx_mask); dev_dbg(dev->dev, "frame len: %d sync len: %d data len: %d\n", frame_length, framesync_length, data_length); dev_dbg(dev->dev, "rx pos: %d,%d tx pos: %d,%d\n", rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos); 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; } static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream, Loading Loading @@ -559,6 +745,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .hw_params = bcm2835_i2s_hw_params, .set_fmt = bcm2835_i2s_set_dai_fmt, .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, }; static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) Loading @@ -578,7 +765,9 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .playback = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE Loading @@ -586,13 +775,16 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .capture = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE }, .ops = &bcm2835_i2s_dai_ops, .symmetric_rates = 1 .symmetric_rates = 1, .symmetric_samplebits = 1, }; static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg) Loading Loading @@ -699,9 +891,6 @@ static int bcm2835_i2s_probe(struct platform_device *pdev) dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags = SND_DMAENGINE_PCM_DAI_FLAG_PACK; /* BCLK ratio - use default */ dev->bclk_ratio = 0; /* Store the pdev */ dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); Loading sound/soc/codecs/cs43130.c +14 −2 Original line number Diff line number Diff line Loading @@ -909,6 +909,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP << CS43130_DSD_SRC_SHIFT); break; } if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM) Loading Loading @@ -1039,6 +1040,7 @@ static int cs43130_pcm_ch_put(struct snd_kcontrol *kcontrol, else regmap_multi_reg_write(cs43130->regmap, pcm_ch_dis_seq, ARRAY_SIZE(pcm_ch_dis_seq)); break; } return snd_soc_put_enum_double(kcontrol, ucontrol); Loading Loading @@ -1152,6 +1154,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, dsd_seq, ARRAY_SIZE(dsd_seq)); break; } break; case SND_SOC_DAPM_POST_PMU: Loading @@ -1162,6 +1165,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, unmute_seq, ARRAY_SIZE(unmute_seq)); break; } break; case SND_SOC_DAPM_PRE_PMD: Loading @@ -1184,6 +1188,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_1, CS43130_MUTE_MASK, CS43130_MUTE_EN); break; } break; default: Loading @@ -1206,6 +1211,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, pcm_seq, ARRAY_SIZE(pcm_seq)); break; } break; case SND_SOC_DAPM_POST_PMU: Loading @@ -1216,6 +1222,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, unmute_seq, ARRAY_SIZE(unmute_seq)); break; } break; case SND_SOC_DAPM_PRE_PMD: Loading @@ -1238,6 +1245,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1, CS43130_MUTE_MASK, CS43130_MUTE_EN); break; } break; default: Loading Loading @@ -1277,6 +1285,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS43198_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, pop_free_seq2, ARRAY_SIZE(pop_free_seq2)); break; } break; case SND_SOC_DAPM_POST_PMU: Loading @@ -1301,6 +1310,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS43198_CHIP_ID: usleep_range(12000, 12010); regmap_write(cs43130->regmap, CS43130_DXD13, 0); break; } regmap_write(cs43130->regmap, CS43130_DXD1, 0); Loading @@ -1311,6 +1321,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, dac_postpmd_seq, ARRAY_SIZE(dac_postpmd_seq)); break; } break; default: Loading Loading @@ -2133,6 +2144,7 @@ static void cs43130_imp_meas(struct work_struct *wk) cs43130_hpload_proc(cs43130, hp_dis_cal_seq2, ARRAY_SIZE(hp_dis_cal_seq2), CS43130_HPLOAD_OFF_INT, ac_idx); break; } regmap_multi_reg_write(cs43130->regmap, hp_cln_seq, Loading Loading @@ -2543,6 +2555,7 @@ static int cs43130_i2c_probe(struct i2c_client *client, digital_hp_routes; soc_codec_dev_cs43130.component_driver.num_dapm_routes = ARRAY_SIZE(digital_hp_routes); break; } ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_cs43130, Loading Loading @@ -2586,7 +2599,6 @@ static int cs43130_i2c_remove(struct i2c_client *client) device_remove_file(&client->dev, &dev_attr_hpload_ac_r); } if (cs43130->reset_gpio) gpiod_set_value_cansleep(cs43130->reset_gpio, 0); pm_runtime_disable(&client->dev); Loading Loading
Documentation/devicetree/bindings/sound/cs42l56.txt +1 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,7 @@ Example: codec: codec@4b { compatible = "cirrus,cs42l56"; reg = <0x4b>; gpio-reset = <&gpio 10 0>; cirrus,gpio-nreset = <&gpio 10 0>; cirrus,chgfreq-divisor = <0x05>; cirrus.ain1_ref_cfg; cirrus,micbias-lvl = <5>; Loading
sound/soc/bcm/bcm2835-i2s.c +290 −101 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ * General Public License for more details. */ #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> Loading Loading @@ -99,6 +100,8 @@ #define BCM2835_I2S_CHWID(v) (v) #define BCM2835_I2S_CH1(v) ((v) << 16) #define BCM2835_I2S_CH2(v) (v) #define BCM2835_I2S_CH1_POS(v) BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(v)) #define BCM2835_I2S_CH2_POS(v) BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(v)) #define BCM2835_I2S_TX_PANIC(v) ((v) << 24) #define BCM2835_I2S_RX_PANIC(v) ((v) << 16) Loading @@ -110,12 +113,19 @@ #define BCM2835_I2S_INT_RXR BIT(1) #define BCM2835_I2S_INT_TXW BIT(0) /* Frame length register is 10 bit, maximum length 1024 */ #define BCM2835_I2S_MAX_FRAME_LENGTH 1024 /* General device struct */ struct bcm2835_i2s_dev { struct device *dev; struct snd_dmaengine_dai_dma_data dma_data[2]; unsigned int fmt; unsigned int bclk_ratio; unsigned int tdm_slots; unsigned int rx_mask; unsigned int tx_mask; unsigned int slot_width; unsigned int frame_length; struct regmap *i2s_regmap; struct clk *clk; Loading Loading @@ -225,19 +235,120 @@ static int bcm2835_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); dev->bclk_ratio = ratio; if (!ratio) { dev->tdm_slots = 0; return 0; } if (ratio > BCM2835_I2S_MAX_FRAME_LENGTH) return -EINVAL; dev->tdm_slots = 2; dev->rx_mask = 0x03; dev->tx_mask = 0x03; dev->slot_width = ratio / 2; dev->frame_length = ratio; return 0; } static int bcm2835_i2s_set_dai_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int width) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (slots) { if (slots < 0 || width < 0) return -EINVAL; /* Limit masks to available slots */ rx_mask &= GENMASK(slots - 1, 0); tx_mask &= GENMASK(slots - 1, 0); /* * The driver is limited to 2-channel setups. * Check that exactly 2 bits are set in the masks. */ if (hweight_long((unsigned long) rx_mask) != 2 || hweight_long((unsigned long) tx_mask) != 2) return -EINVAL; if (slots * width > BCM2835_I2S_MAX_FRAME_LENGTH) return -EINVAL; } dev->tdm_slots = slots; dev->rx_mask = rx_mask; dev->tx_mask = tx_mask; dev->slot_width = width; dev->frame_length = slots * width; return 0; } /* * Convert logical slot number into physical slot number. * * If odd_offset is 0 sequential number is identical to logical number. * This is used for DSP modes with slot numbering 0 1 2 3 ... * * Otherwise odd_offset defines the physical offset for odd numbered * slots. This is used for I2S and left/right justified modes to * translate from logical slot numbers 0 1 2 3 ... into physical slot * numbers 0 2 ... 3 4 ... */ static int bcm2835_i2s_convert_slot(unsigned int slot, unsigned int odd_offset) { if (!odd_offset) return slot; if (slot & 1) return (slot >> 1) + odd_offset; return slot >> 1; } /* * Calculate channel position from mask and slot width. * * Mask must contain exactly 2 set bits. * Lowest set bit is channel 1 position, highest set bit channel 2. * The constant offset is added to both channel positions. * * If odd_offset is > 0 slot positions are translated to * I2S-style TDM slot numbering ( 0 2 ... 3 4 ...) with odd * logical slot numbers starting at physical slot odd_offset. */ static void bcm2835_i2s_calc_channel_pos( unsigned int *ch1_pos, unsigned int *ch2_pos, unsigned int mask, unsigned int width, unsigned int bit_offset, unsigned int odd_offset) { *ch1_pos = bcm2835_i2s_convert_slot((ffs(mask) - 1), odd_offset) * width + bit_offset; *ch2_pos = bcm2835_i2s_convert_slot((fls(mask) - 1), odd_offset) * width + bit_offset; } static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); unsigned int sampling_rate = params_rate(params); unsigned int data_length, data_delay, bclk_ratio; unsigned int ch1pos, ch2pos, mode, format; unsigned int data_length, data_delay, framesync_length; unsigned int slots, slot_width, odd_slot_offset; int frame_length, bclk_rate; 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; /* * If a stream is already enabled, Loading @@ -248,42 +359,70 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON)) return 0; /* * Adjust the data length according to the format. * We prefill the half frame length with an integer * divider of 2400 as explained at the clock settings. * Maybe it is overwritten there, if the Integer mode * does not apply. */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: data_length = 16; break; case SNDRV_PCM_FORMAT_S24_LE: data_length = 24; data_length = params_width(params); data_delay = 0; odd_slot_offset = 0; mode = 0; if (dev->tdm_slots) { slots = dev->tdm_slots; slot_width = dev->slot_width; frame_length = dev->frame_length; rx_mask = dev->rx_mask; tx_mask = dev->tx_mask; bclk_rate = dev->frame_length * params_rate(params); } else { slots = 2; slot_width = params_width(params); rx_mask = 0x03; tx_mask = 0x03; frame_length = snd_soc_params_to_frame_size(params); if (frame_length < 0) return frame_length; bclk_rate = snd_soc_params_to_bclk(params); if (bclk_rate < 0) return bclk_rate; } /* Check if data fits into slots */ if (data_length > slot_width) return -EINVAL; /* 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: bit_clock_master = true; break; case SNDRV_PCM_FORMAT_S32_LE: data_length = 32; case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBM_CFM: bit_clock_master = false; break; default: return -EINVAL; } /* If bclk_ratio already set, use that one. */ if (dev->bclk_ratio) bclk_ratio = dev->bclk_ratio; else /* otherwise calculate a fitting block ratio */ bclk_ratio = 2 * data_length; /* Clock should only be set up here if CPU is clock master */ /* 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: clk_set_rate(dev->clk, sampling_rate * bclk_ratio); case SND_SOC_DAIFMT_CBM_CFM: frame_sync_master = false; break; default: break; 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 */ Loading @@ -294,43 +433,94 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, format |= BCM2835_I2S_CHWID((data_length-8)&0xf); /* CH2 format is the same as for CH1 */ format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: data_delay = 1; break; default: /* I2S mode needs an even number of slots */ if (slots & 1) return -EINVAL; /* * TODO * Others are possible but are not implemented at the moment. * Use I2S-style logical slot numbering: even slots * are in first half of frame, odd slots in second half. */ dev_err(dev->dev, "%s:bad format\n", __func__); odd_slot_offset = slots >> 1; /* MSB starts one cycle after frame start */ data_delay = 1; /* 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; } ch1pos = data_delay; ch2pos = bclk_ratio / 2 + data_delay; /* Odd frame lengths aren't supported */ if (frame_length & 1) return -EINVAL; switch (params_channels(params)) { case 2: format = BCM2835_I2S_CH1(format) | BCM2835_I2S_CH2(format); format |= BCM2835_I2S_CH1(BCM2835_I2S_CHPOS(ch1pos)); format |= BCM2835_I2S_CH2(BCM2835_I2S_CHPOS(ch2pos)); 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: return -EINVAL; } bcm2835_i2s_calc_channel_pos(&rx_ch1_pos, &rx_ch2_pos, rx_mask, slot_width, data_delay, odd_slot_offset); bcm2835_i2s_calc_channel_pos(&tx_ch1_pos, &tx_ch2_pos, 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, * so the format will be the same. */ regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format); regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format); regmap_write(dev->i2s_regmap, BCM2835_I2S_RXC_A_REG, format | BCM2835_I2S_CH1_POS(rx_ch1_pos) | BCM2835_I2S_CH2_POS(rx_ch2_pos)); regmap_write(dev->i2s_regmap, BCM2835_I2S_TXC_A_REG, format | BCM2835_I2S_CH1_POS(tx_ch1_pos) | BCM2835_I2S_CH2_POS(tx_ch2_pos)); /* Setup the I2S mode */ mode = 0; if (data_length <= 16) { /* Loading @@ -342,64 +532,40 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP; } mode |= BCM2835_I2S_FLEN(bclk_ratio - 1); mode |= BCM2835_I2S_FSLEN(bclk_ratio / 2); 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; case SND_SOC_DAIFMT_IB_NF: if (frame_start_falling_edge) mode |= BCM2835_I2S_FSI; break; case SND_SOC_DAIFMT_IB_IF: /* Both. Therefore, none for BCM */ 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 */ case SND_SOC_DAIFMT_IB_IF: if (!frame_start_falling_edge) mode |= BCM2835_I2S_FSI; break; default: Loading @@ -423,7 +589,27 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream, /* Clear FIFOs */ bcm2835_i2s_clear_fifos(dev, true, true); return 0; dev_dbg(dev->dev, "slots: %d width: %d rx mask: 0x%02x tx_mask: 0x%02x\n", slots, slot_width, rx_mask, tx_mask); dev_dbg(dev->dev, "frame len: %d sync len: %d data len: %d\n", frame_length, framesync_length, data_length); dev_dbg(dev->dev, "rx pos: %d,%d tx pos: %d,%d\n", rx_ch1_pos, rx_ch2_pos, tx_ch1_pos, tx_ch2_pos); 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; } static int bcm2835_i2s_prepare(struct snd_pcm_substream *substream, Loading Loading @@ -559,6 +745,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .hw_params = bcm2835_i2s_hw_params, .set_fmt = bcm2835_i2s_set_dai_fmt, .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio, .set_tdm_slot = bcm2835_i2s_set_dai_tdm_slot, }; static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) Loading @@ -578,7 +765,9 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .playback = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE Loading @@ -586,13 +775,16 @@ static struct snd_soc_dai_driver bcm2835_i2s_dai = { .capture = { .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_192000, .rates = SNDRV_PCM_RATE_CONTINUOUS, .rate_min = 8000, .rate_max = 384000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE }, .ops = &bcm2835_i2s_dai_ops, .symmetric_rates = 1 .symmetric_rates = 1, .symmetric_samplebits = 1, }; static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg) Loading Loading @@ -699,9 +891,6 @@ static int bcm2835_i2s_probe(struct platform_device *pdev) dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags = SND_DMAENGINE_PCM_DAI_FLAG_PACK; /* BCLK ratio - use default */ dev->bclk_ratio = 0; /* Store the pdev */ dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); Loading
sound/soc/codecs/cs43130.c +14 −2 Original line number Diff line number Diff line Loading @@ -909,6 +909,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_2, CS43130_DSD_SRC_MASK, CS43130_DSD_SRC_XSP << CS43130_DSD_SRC_SHIFT); break; } if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBM_CFM) Loading Loading @@ -1039,6 +1040,7 @@ static int cs43130_pcm_ch_put(struct snd_kcontrol *kcontrol, else regmap_multi_reg_write(cs43130->regmap, pcm_ch_dis_seq, ARRAY_SIZE(pcm_ch_dis_seq)); break; } return snd_soc_put_enum_double(kcontrol, ucontrol); Loading Loading @@ -1152,6 +1154,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, dsd_seq, ARRAY_SIZE(dsd_seq)); break; } break; case SND_SOC_DAPM_POST_PMU: Loading @@ -1162,6 +1165,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, unmute_seq, ARRAY_SIZE(unmute_seq)); break; } break; case SND_SOC_DAPM_PRE_PMD: Loading @@ -1184,6 +1188,7 @@ static int cs43130_dsd_event(struct snd_soc_dapm_widget *w, regmap_update_bits(cs43130->regmap, CS43130_DSD_PATH_CTL_1, CS43130_MUTE_MASK, CS43130_MUTE_EN); break; } break; default: Loading @@ -1206,6 +1211,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, pcm_seq, ARRAY_SIZE(pcm_seq)); break; } break; case SND_SOC_DAPM_POST_PMU: Loading @@ -1216,6 +1222,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, unmute_seq, ARRAY_SIZE(unmute_seq)); break; } break; case SND_SOC_DAPM_PRE_PMD: Loading @@ -1238,6 +1245,7 @@ static int cs43130_pcm_event(struct snd_soc_dapm_widget *w, regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1, CS43130_MUTE_MASK, CS43130_MUTE_EN); break; } break; default: Loading Loading @@ -1277,6 +1285,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS43198_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, pop_free_seq2, ARRAY_SIZE(pop_free_seq2)); break; } break; case SND_SOC_DAPM_POST_PMU: Loading @@ -1301,6 +1310,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS43198_CHIP_ID: usleep_range(12000, 12010); regmap_write(cs43130->regmap, CS43130_DXD13, 0); break; } regmap_write(cs43130->regmap, CS43130_DXD1, 0); Loading @@ -1311,6 +1321,7 @@ static int cs43130_dac_event(struct snd_soc_dapm_widget *w, case CS4399_CHIP_ID: regmap_multi_reg_write(cs43130->regmap, dac_postpmd_seq, ARRAY_SIZE(dac_postpmd_seq)); break; } break; default: Loading Loading @@ -2133,6 +2144,7 @@ static void cs43130_imp_meas(struct work_struct *wk) cs43130_hpload_proc(cs43130, hp_dis_cal_seq2, ARRAY_SIZE(hp_dis_cal_seq2), CS43130_HPLOAD_OFF_INT, ac_idx); break; } regmap_multi_reg_write(cs43130->regmap, hp_cln_seq, Loading Loading @@ -2543,6 +2555,7 @@ static int cs43130_i2c_probe(struct i2c_client *client, digital_hp_routes; soc_codec_dev_cs43130.component_driver.num_dapm_routes = ARRAY_SIZE(digital_hp_routes); break; } ret = snd_soc_register_codec(&client->dev, &soc_codec_dev_cs43130, Loading Loading @@ -2586,7 +2599,6 @@ static int cs43130_i2c_remove(struct i2c_client *client) device_remove_file(&client->dev, &dev_attr_hpload_ac_r); } if (cs43130->reset_gpio) gpiod_set_value_cansleep(cs43130->reset_gpio, 0); pm_runtime_disable(&client->dev); Loading