Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Florian Meier | 2120 | 62.87% | 1 | 4.17% |
Matthias Reichl | 1063 | 31.52% | 8 | 33.33% |
Martin Sperl | 114 | 3.38% | 2 | 8.33% |
Mark Brown | 19 | 0.56% | 1 | 4.17% |
Kuninori Morimoto | 19 | 0.56% | 3 | 12.50% |
Charles Keepax | 16 | 0.47% | 3 | 12.50% |
Yang Yingliang | 7 | 0.21% | 1 | 4.17% |
Luis de Bethencourt | 7 | 0.21% | 1 | 4.17% |
Thomas Gleixner | 2 | 0.06% | 1 | 4.17% |
Seung-Woo Kim | 2 | 0.06% | 1 | 4.17% |
Yue haibing | 2 | 0.06% | 1 | 4.17% |
Lars-Peter Clausen | 1 | 0.03% | 1 | 4.17% |
Total | 3372 | 24 |
// SPDX-License-Identifier: GPL-2.0-only /* * ALSA SoC I2S Audio Layer for Broadcom BCM2835 SoC * * Author: Florian Meier <florian.meier@koalo.de> * Copyright 2013 * * Based on * Raspberry Pi PCM I2S ALSA Driver * Copyright (c) by Phil Poole 2013 * * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor * Vladimir Barinov, <vbarinov@embeddedalley.com> * Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com> * * OMAP ALSA SoC DAI driver using McBSP port * Copyright (C) 2008 Nokia Corporation * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> * Peter Ujfalusi <peter.ujfalusi@ti.com> * * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver * Author: Timur Tabi <timur@freescale.com> * Copyright 2007-2010 Freescale Semiconductor, Inc. */ #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/dmaengine_pcm.h> #include <sound/initval.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> /* I2S registers */ #define BCM2835_I2S_CS_A_REG 0x00 #define BCM2835_I2S_FIFO_A_REG 0x04 #define BCM2835_I2S_MODE_A_REG 0x08 #define BCM2835_I2S_RXC_A_REG 0x0c #define BCM2835_I2S_TXC_A_REG 0x10 #define BCM2835_I2S_DREQ_A_REG 0x14 #define BCM2835_I2S_INTEN_A_REG 0x18 #define BCM2835_I2S_INTSTC_A_REG 0x1c #define BCM2835_I2S_GRAY_REG 0x20 /* I2S register settings */ #define BCM2835_I2S_STBY BIT(25) #define BCM2835_I2S_SYNC BIT(24) #define BCM2835_I2S_RXSEX BIT(23) #define BCM2835_I2S_RXF BIT(22) #define BCM2835_I2S_TXE BIT(21) #define BCM2835_I2S_RXD BIT(20) #define BCM2835_I2S_TXD BIT(19) #define BCM2835_I2S_RXR BIT(18) #define BCM2835_I2S_TXW BIT(17) #define BCM2835_I2S_CS_RXERR BIT(16) #define BCM2835_I2S_CS_TXERR BIT(15) #define BCM2835_I2S_RXSYNC BIT(14) #define BCM2835_I2S_TXSYNC BIT(13) #define BCM2835_I2S_DMAEN BIT(9) #define BCM2835_I2S_RXTHR(v) ((v) << 7) #define BCM2835_I2S_TXTHR(v) ((v) << 5) #define BCM2835_I2S_RXCLR BIT(4) #define BCM2835_I2S_TXCLR BIT(3) #define BCM2835_I2S_TXON BIT(2) #define BCM2835_I2S_RXON BIT(1) #define BCM2835_I2S_EN (1) #define BCM2835_I2S_CLKDIS BIT(28) #define BCM2835_I2S_PDMN BIT(27) #define BCM2835_I2S_PDME BIT(26) #define BCM2835_I2S_FRXP BIT(25) #define BCM2835_I2S_FTXP BIT(24) #define BCM2835_I2S_CLKM BIT(23) #define BCM2835_I2S_CLKI BIT(22) #define BCM2835_I2S_FSM BIT(21) #define BCM2835_I2S_FSI BIT(20) #define BCM2835_I2S_FLEN(v) ((v) << 10) #define BCM2835_I2S_FSLEN(v) (v) #define BCM2835_I2S_CHWEX BIT(15) #define BCM2835_I2S_CHEN BIT(14) #define BCM2835_I2S_CHPOS(v) ((v) << 4) #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) #define BCM2835_I2S_TX(v) ((v) << 8) #define BCM2835_I2S_RX(v) (v) #define BCM2835_I2S_INT_RXERR BIT(3) #define BCM2835_I2S_INT_TXERR BIT(2) #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 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; bool clk_prepared; int clk_rate; }; static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev) { unsigned int provider = dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; if (dev->clk_prepared) return; switch (provider) { case SND_SOC_DAIFMT_BP_FP: case SND_SOC_DAIFMT_BP_FC: clk_prepare_enable(dev->clk); dev->clk_prepared = true; break; default: break; } } static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev) { if (dev->clk_prepared) clk_disable_unprepare(dev->clk); dev->clk_prepared = false; } static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev, bool tx, bool rx) { int timeout = 1000; uint32_t syncval; uint32_t csreg; uint32_t i2s_active_state; bool clk_was_prepared; uint32_t off; uint32_t clr; off = tx ? BCM2835_I2S_TXON : 0; off |= rx ? BCM2835_I2S_RXON : 0; clr = tx ? BCM2835_I2S_TXCLR : 0; clr |= rx ? BCM2835_I2S_RXCLR : 0; /* Backup the current state */ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON); /* Start clock if not running */ clk_was_prepared = dev->clk_prepared; if (!clk_was_prepared) bcm2835_i2s_start_clock(dev); /* Stop I2S module */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0); /* * Clear the FIFOs * Requires at least 2 PCM clock cycles to take effect */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, clr, clr); /* Wait for 2 PCM clock cycles */ /* * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back * FIXME: This does not seem to work for slave mode! */ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &syncval); syncval &= BCM2835_I2S_SYNC; regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, BCM2835_I2S_SYNC, ~syncval); /* Wait for the SYNC flag changing it's state */ while (--timeout) { regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); if ((csreg & BCM2835_I2S_SYNC) != syncval) break; } if (!timeout) dev_err(dev->dev, "I2S SYNC error!\n"); /* Stop clock if it was not running before */ if (!clk_was_prepared) bcm2835_i2s_stop_clock(dev); /* Restore I2S state */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, BCM2835_I2S_RXON | BCM2835_I2S_TXON, i2s_active_state); } static int bcm2835_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); dev->fmt = fmt; return 0; } 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); 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 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_provider = false; bool frame_sync_provider = false; bool frame_start_falling_edge = false; uint32_t csreg; int ret = 0; /* * If a stream is already enabled, * the registers are already set properly. */ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg); if (csreg & (BCM2835_I2S_TXON | BCM2835_I2S_RXON)) return 0; 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 provider */ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: case SND_SOC_DAIFMT_BP_FC: bit_clock_provider = true; break; case SND_SOC_DAIFMT_BC_FP: case SND_SOC_DAIFMT_BC_FC: bit_clock_provider = false; break; default: return -EINVAL; } /* Check if CPU is frame sync provider */ switch (dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_BP_FP: case SND_SOC_DAIFMT_BC_FP: frame_sync_provider = true; break; case SND_SOC_DAIFMT_BP_FC: case SND_SOC_DAIFMT_BC_FC: frame_sync_provider = false; break; default: return -EINVAL; } /* Clock should only be set up here if CPU is clock master */ if (bit_clock_provider && (!dev->clk_prepared || dev->clk_rate != bclk_rate)) { if (dev->clk_prepared) bcm2835_i2s_stop_clock(dev); if (dev->clk_rate != bclk_rate) { ret = clk_set_rate(dev->clk, bclk_rate); if (ret) return ret; dev->clk_rate = bclk_rate; } bcm2835_i2s_start_clock(dev); } /* Setup the frame format */ format = BCM2835_I2S_CHEN; if (data_length >= 24) format |= BCM2835_I2S_CHWEX; 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: /* I2S mode needs an even number of slots */ if (slots & 1) return -EINVAL; /* * Use I2S-style logical slot numbering: even slots * are in first half of frame, odd slots in second half. */ 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; /* 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: 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 provider. */ if ((!rx_ch1_pos || !tx_ch1_pos) && !frame_sync_provider) dev_warn(dev->dev, "Unstable consumer 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 | 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 */ if (data_length <= 16) { /* * Use frame packed mode (2 channels per 32 bit word) * We cannot set another frame length in the second stream * (and therefore word length) anyway, * so the format will be the same. */ mode |= BCM2835_I2S_FTXP | BCM2835_I2S_FRXP; } mode |= BCM2835_I2S_FLEN(frame_length - 1); mode |= BCM2835_I2S_FSLEN(framesync_length); /* CLKM selects bcm2835 clock slave mode */ if (!bit_clock_provider) mode |= BCM2835_I2S_CLKM; /* FSM selects bcm2835 frame sync slave mode */ if (!frame_sync_provider) 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_IB_NF: case SND_SOC_DAIFMT_IB_IF: break; default: return -EINVAL; } /* FSI selects frame start on falling edge */ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: case SND_SOC_DAIFMT_IB_NF: if (frame_start_falling_edge) mode |= BCM2835_I2S_FSI; break; case SND_SOC_DAIFMT_NB_IF: case SND_SOC_DAIFMT_IB_IF: if (!frame_start_falling_edge) mode |= BCM2835_I2S_FSI; break; default: return -EINVAL; } regmap_write(dev->i2s_regmap, BCM2835_I2S_MODE_A_REG, mode); /* Setup the DMA parameters */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, BCM2835_I2S_RXTHR(1) | BCM2835_I2S_TXTHR(1) | BCM2835_I2S_DMAEN, 0xffffffff); regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_DREQ_A_REG, BCM2835_I2S_TX_PANIC(0x10) | BCM2835_I2S_RX_PANIC(0x30) | BCM2835_I2S_TX(0x30) | BCM2835_I2S_RX(0x20), 0xffffffff); /* Clear FIFOs */ bcm2835_i2s_clear_fifos(dev, true, true); 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, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); uint32_t cs_reg; /* * Clear both FIFOs if the one that should be started * is not empty at the moment. This should only happen * after overrun. Otherwise, hw_params would have cleared * the FIFO. */ regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &cs_reg); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && !(cs_reg & BCM2835_I2S_TXE)) bcm2835_i2s_clear_fifos(dev, true, false); else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && (cs_reg & BCM2835_I2S_RXD)) bcm2835_i2s_clear_fifos(dev, false, true); return 0; } static void bcm2835_i2s_stop(struct bcm2835_i2s_dev *dev, struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { uint32_t mask; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) mask = BCM2835_I2S_RXON; else mask = BCM2835_I2S_TXON; regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, mask, 0); /* Stop also the clock when not SND_SOC_DAIFMT_CONT */ if (!snd_soc_dai_active(dai) && !(dev->fmt & SND_SOC_DAIFMT_CONT)) bcm2835_i2s_stop_clock(dev); } static int bcm2835_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); uint32_t mask; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: bcm2835_i2s_start_clock(dev); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) mask = BCM2835_I2S_RXON; else mask = BCM2835_I2S_TXON; regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, mask, mask); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: bcm2835_i2s_stop(dev, substream, dai); break; default: return -EINVAL; } return 0; } static int bcm2835_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); if (snd_soc_dai_active(dai)) return 0; /* Should this still be running stop it */ bcm2835_i2s_stop_clock(dev); /* Enable PCM block */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, BCM2835_I2S_EN, BCM2835_I2S_EN); /* * Disable STBY. * Requires at least 4 PCM clock cycles to take effect. */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, BCM2835_I2S_STBY, BCM2835_I2S_STBY); return 0; } static void bcm2835_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); bcm2835_i2s_stop(dev, substream, dai); /* If both streams are stopped, disable module and clock */ if (snd_soc_dai_active(dai)) return; /* Disable the module */ regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, BCM2835_I2S_EN, 0); /* * Stopping clock is necessary, because stop does * not stop the clock when SND_SOC_DAIFMT_CONT */ bcm2835_i2s_stop_clock(dev); } static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai) { struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); snd_soc_dai_init_dma_data(dai, &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK], &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]); return 0; } static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = { .probe = bcm2835_i2s_dai_probe, .startup = bcm2835_i2s_startup, .shutdown = bcm2835_i2s_shutdown, .prepare = bcm2835_i2s_prepare, .trigger = bcm2835_i2s_trigger, .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 struct snd_soc_dai_driver bcm2835_i2s_dai = { .name = "bcm2835-i2s", .playback = { .channels_min = 2, .channels_max = 2, .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 }, .capture = { .channels_min = 2, .channels_max = 2, .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_rate = 1, .symmetric_sample_bits = 1, }; static bool bcm2835_i2s_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case BCM2835_I2S_CS_A_REG: case BCM2835_I2S_FIFO_A_REG: case BCM2835_I2S_INTSTC_A_REG: case BCM2835_I2S_GRAY_REG: return true; default: return false; } } static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg) { switch (reg) { case BCM2835_I2S_FIFO_A_REG: return true; default: return false; } } static const struct regmap_config bcm2835_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .max_register = BCM2835_I2S_GRAY_REG, .precious_reg = bcm2835_i2s_precious_reg, .volatile_reg = bcm2835_i2s_volatile_reg, .cache_type = REGCACHE_RBTREE, }; static const struct snd_soc_component_driver bcm2835_i2s_component = { .name = "bcm2835-i2s-comp", .legacy_dai_naming = 1, }; static int bcm2835_i2s_probe(struct platform_device *pdev) { struct bcm2835_i2s_dev *dev; int ret; void __iomem *base; const __be32 *addr; dma_addr_t dma_base; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; /* get the clock */ dev->clk_prepared = false; dev->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk), "could not get clk\n"); /* Request ioarea */ base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base, &bcm2835_regmap_config); if (IS_ERR(dev->i2s_regmap)) return PTR_ERR(dev->i2s_regmap); /* Set the DMA address - we have to parse DT ourselves */ addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL); if (!addr) { dev_err(&pdev->dev, "could not get DMA-register address\n"); return -EINVAL; } dma_base = be32_to_cpup(addr); dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = dma_base + BCM2835_I2S_FIFO_A_REG; dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = dma_base + BCM2835_I2S_FIFO_A_REG; /* Set the bus width */ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; /* Set burst */ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2; dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2; /* * Set the PACK flag to enable S16_LE support (2 S16_LE values * packed into 32-bit transfers). */ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].flags = SND_DMAENGINE_PCM_DAI_FLAG_PACK; dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].flags = SND_DMAENGINE_PCM_DAI_FLAG_PACK; /* Store the pdev */ dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = devm_snd_soc_register_component(&pdev->dev, &bcm2835_i2s_component, &bcm2835_i2s_dai, 1); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); return ret; } ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (ret) { dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); return ret; } return 0; } static const struct of_device_id bcm2835_i2s_of_match[] = { { .compatible = "brcm,bcm2835-i2s", }, {}, }; MODULE_DEVICE_TABLE(of, bcm2835_i2s_of_match); static struct platform_driver bcm2835_i2s_driver = { .probe = bcm2835_i2s_probe, .driver = { .name = "bcm2835-i2s", .of_match_table = bcm2835_i2s_of_match, }, }; module_platform_driver(bcm2835_i2s_driver); MODULE_ALIAS("platform:bcm2835-i2s"); MODULE_DESCRIPTION("BCM2835 I2S interface"); MODULE_AUTHOR("Florian Meier <florian.meier@koalo.de>"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1