diff options
75 files changed, 6307 insertions, 475 deletions
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml new file mode 100644 index 000000000000..35eef7d818a2 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/awinic,aw88395.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Awinic AW88395 Smart Audio Amplifier + +maintainers: + - Weidong Wang <[email protected]> + +description: + The Awinic AW88395 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with an integrated 10.25V + smart boost convert. + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + const: awinic,aw88395 + + reg: + maxItems: 1 + + '#sound-dai-cells': + const: 0 + + reset-gpios: + maxItems: 1 + +required: + - compatible + - reg + - '#sound-dai-cells' + - reset-gpios + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + audio-codec@34 { + compatible = "awinic,aw88395"; + reg = <0x34>; + #sound-dai-cells = <0>; + reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml index eb87374cc812..162c52606635 100644 --- a/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml +++ b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml @@ -19,14 +19,13 @@ required: - compatible - reg -additionalProperties: true +additionalProperties: false examples: - | - #include <dt-bindings/gpio/gpio.h> i2c_bus { - sma1303_amp: sma1303@1e { - compatible = "irondevice,sma1303"; - reg = <0x1e>; - }; - }; + amplifier@1e { + compatible = "irondevice,sma1303"; + reg = <0x1e>; + }; + }; diff --git a/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml b/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml new file mode 100644 index 000000000000..a2b92dba5529 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/renesas,idt821034.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas IDT821034 codec device + +maintainers: + - Herve Codina <[email protected]> + +description: | + The IDT821034 codec is a four channel PCM codec with onchip filters and + programmable gain setting. + + The time-slots used by the codec must be set and so, the properties + 'dai-tdm-slot-num', 'dai-tdm-slot-width', 'dai-tdm-slot-tx-mask' and + 'dai-tdm-slot-rx-mask' must be present in the ALSA sound card node for + sub-nodes that involve the codec. The codec uses one 8bit time-slot per + channel. + 'dai-tdm-tdm-slot-with' must be set to 8. + + The IDT821034 codec also supports 5 gpios (SLIC signals) per channel. + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - $ref: dai-common.yaml# + +properties: + compatible: + const: renesas,idt821034 + + reg: + description: + SPI device address. + maxItems: 1 + + spi-max-frequency: + maximum: 8192000 + + spi-cpha: true + + '#sound-dai-cells': + const: 0 + + '#gpio-cells': + const: 2 + + gpio-controller: true + +required: + - compatible + - reg + - spi-cpha + - '#sound-dai-cells' + - gpio-controller + - '#gpio-cells' + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + audio-codec@0 { + compatible = "renesas,idt821034"; + reg = <0>; + spi-max-frequency = <8192000>; + spi-cpha; + #sound-dai-cells = <0>; + gpio-controller; + #gpio-cells = <2>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index f61eb221415b..3732dc391160 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10920,6 +10920,13 @@ M: David Sterba <[email protected]> S: Odd Fixes F: drivers/tty/ipwireless/ +IRON DEVICE AUDIO CODEC DRIVERS +M: Kiseok Jo <[email protected]> +L: [email protected] (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/sound/irondevice,* +F: sound/soc/codecs/sma* + IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY) M: Marc Zyngier <[email protected]> S: Maintained @@ -17783,6 +17790,13 @@ F: Documentation/devicetree/bindings/net/renesas,*.yaml F: drivers/net/ethernet/renesas/ F: include/linux/sh_eth.h +RENESAS IDT821034 ASoC CODEC +M: Herve Codina <[email protected]> +L: [email protected] (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/sound/renesas,idt821034.yaml +F: sound/soc/codecs/idt821034.c + RENESAS R-CAR GYROADC DRIVER M: Marek Vasut <[email protected]> diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index ea7509672086..e3906ecda740 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -423,6 +423,16 @@ struct snd_soc_dai_driver { int remove_order; }; +/* for Playback/Capture */ +struct snd_soc_dai_stream { + struct snd_soc_dapm_widget *widget; + + unsigned int active; /* usage count */ + unsigned int tdm_mask; /* CODEC TDM slot masks and params (for fixup) */ + + void *dma_data; /* DAI DMA data */ +}; + /* * Digital Audio Interface runtime data. * @@ -437,14 +447,7 @@ struct snd_soc_dai { struct snd_soc_dai_driver *driver; /* DAI runtime info */ - unsigned int stream_active[SNDRV_PCM_STREAM_LAST + 1]; /* usage count */ - - struct snd_soc_dapm_widget *playback_widget; - struct snd_soc_dapm_widget *capture_widget; - - /* DAI DMA data */ - void *playback_dma_data; - void *capture_dma_data; + struct snd_soc_dai_stream stream[SNDRV_PCM_STREAM_LAST + 1]; /* Symmetry data - only valid if symmetry is being enforced */ unsigned int rate; @@ -454,10 +457,6 @@ struct snd_soc_dai { /* parent platform/codec */ struct snd_soc_component *component; - /* CODEC TDM slot masks and params (for fixup) */ - unsigned int tx_mask; - unsigned int rx_mask; - struct list_head list; /* function mark */ @@ -477,36 +476,59 @@ snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream) &dai->driver->playback : &dai->driver->capture; } +#define snd_soc_dai_get_widget_playback(dai) snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_PLAYBACK) +#define snd_soc_dai_get_widget_capture(dai) snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_CAPTURE) static inline -struct snd_soc_dapm_widget *snd_soc_dai_get_widget( - struct snd_soc_dai *dai, int stream) +struct snd_soc_dapm_widget *snd_soc_dai_get_widget(struct snd_soc_dai *dai, int stream) { - return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? - dai->playback_widget : dai->capture_widget; + return dai->stream[stream].widget; } -static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, - const struct snd_pcm_substream *ss) +#define snd_soc_dai_set_widget_playback(dai, widget) snd_soc_dai_set_widget(dai, SNDRV_PCM_STREAM_PLAYBACK, widget) +#define snd_soc_dai_set_widget_capture(dai, widget) snd_soc_dai_set_widget(dai, SNDRV_PCM_STREAM_CAPTURE, widget) +static inline +void snd_soc_dai_set_widget(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget *widget) { - return (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - dai->playback_dma_data : dai->capture_dma_data; + dai->stream[stream].widget = widget; } -static inline void snd_soc_dai_set_dma_data(struct snd_soc_dai *dai, - const struct snd_pcm_substream *ss, - void *data) +#define snd_soc_dai_dma_data_get_playback(dai) snd_soc_dai_dma_data_get(dai, SNDRV_PCM_STREAM_PLAYBACK) +#define snd_soc_dai_dma_data_get_capture(dai) snd_soc_dai_dma_data_get(dai, SNDRV_PCM_STREAM_CAPTURE) +#define snd_soc_dai_get_dma_data(dai, ss) snd_soc_dai_dma_data_get(dai, ss->stream) +static inline void *snd_soc_dai_dma_data_get(const struct snd_soc_dai *dai, int stream) { - if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = data; - else - dai->capture_dma_data = data; + return dai->stream[stream].dma_data; +} + +#define snd_soc_dai_dma_data_set_playback(dai, data) snd_soc_dai_dma_data_set(dai, SNDRV_PCM_STREAM_PLAYBACK, data) +#define snd_soc_dai_dma_data_set_capture(dai, data) snd_soc_dai_dma_data_set(dai, SNDRV_PCM_STREAM_CAPTURE, data) +#define snd_soc_dai_set_dma_data(dai, ss, data) snd_soc_dai_dma_data_set(dai, ss->stream, data) +static inline void snd_soc_dai_dma_data_set(struct snd_soc_dai *dai, int stream, void *data) +{ + dai->stream[stream].dma_data = data; } -static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, - void *playback, void *capture) +static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, void *playback, void *capture) { - dai->playback_dma_data = playback; - dai->capture_dma_data = capture; + snd_soc_dai_dma_data_set_playback(dai, playback); + snd_soc_dai_dma_data_set_capture(dai, capture); +} + +static inline unsigned int snd_soc_dai_tdm_mask_get(struct snd_soc_dai *dai, int stream) +{ + return dai->stream[stream].tdm_mask; +} + +static inline void snd_soc_dai_tdm_mask_set(struct snd_soc_dai *dai, int stream, + unsigned int tdm_mask) +{ + dai->stream[stream].tdm_mask = tdm_mask; +} + +static inline unsigned int snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream) +{ + /* see snd_soc_dai_action() for setup */ + return dai->stream[stream].active; } static inline void snd_soc_dai_set_drvdata(struct snd_soc_dai *dai, @@ -561,10 +583,4 @@ static inline void *snd_soc_dai_get_stream(struct snd_soc_dai *dai, return ERR_PTR(-ENOTSUPP); } -static inline unsigned int -snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream) -{ - return dai->stream_active[stream]; -} - #endif diff --git a/include/sound/soc.h b/include/sound/soc.h index 37bbfc8b45cb..e58b43b5da7c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1052,6 +1052,12 @@ struct snd_soc_card { #define for_each_card_widgets_safe(card, w, _w) \ list_for_each_entry_safe(w, _w, &card->widgets, list) + +static inline int snd_soc_card_is_instantiated(struct snd_soc_card *card) +{ + return card && card->instantiated; +} + /* SoC machine DAI configuration, glues a codec and cpu DAI together */ struct snd_soc_pcm_runtime { struct device *dev; diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index ec0705cc40fa..526dd3a17471 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/spinlock.h> @@ -192,6 +193,43 @@ static bool mchp_spdifrx_precious_reg(struct device *dev, unsigned int reg) } } +static bool mchp_spdifrx_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIFRX_IMR: + case SPDIFRX_ISR: + case SPDIFRX_RSR: + case SPDIFRX_CHSR(0, 0): + case SPDIFRX_CHSR(0, 1): + case SPDIFRX_CHSR(0, 2): + case SPDIFRX_CHSR(0, 3): + case SPDIFRX_CHSR(0, 4): + case SPDIFRX_CHSR(0, 5): + case SPDIFRX_CHUD(0, 0): + case SPDIFRX_CHUD(0, 1): + case SPDIFRX_CHUD(0, 2): + case SPDIFRX_CHUD(0, 3): + case SPDIFRX_CHUD(0, 4): + case SPDIFRX_CHUD(0, 5): + case SPDIFRX_CHSR(1, 0): + case SPDIFRX_CHSR(1, 1): + case SPDIFRX_CHSR(1, 2): + case SPDIFRX_CHSR(1, 3): + case SPDIFRX_CHSR(1, 4): + case SPDIFRX_CHSR(1, 5): + case SPDIFRX_CHUD(1, 0): + case SPDIFRX_CHUD(1, 1): + case SPDIFRX_CHUD(1, 2): + case SPDIFRX_CHUD(1, 3): + case SPDIFRX_CHUD(1, 4): + case SPDIFRX_CHUD(1, 5): + case SPDIFRX_VERSION: + return true; + default: + return false; + } +} + static const struct regmap_config mchp_spdifrx_regmap_config = { .reg_bits = 32, .reg_stride = 4, @@ -200,6 +238,8 @@ static const struct regmap_config mchp_spdifrx_regmap_config = { .readable_reg = mchp_spdifrx_readable_reg, .writeable_reg = mchp_spdifrx_writeable_reg, .precious_reg = mchp_spdifrx_precious_reg, + .volatile_reg = mchp_spdifrx_volatile_reg, + .cache_type = REGCACHE_FLAT, }; #define SPDIFRX_GCLK_RATIO_MIN (12 * 64) @@ -209,17 +249,34 @@ static const struct regmap_config mchp_spdifrx_regmap_config = { #define SPDIFRX_CHANNELS 2 +/** + * struct mchp_spdifrx_ch_stat: MCHP SPDIFRX channel status + * @data: channel status bits + * @done: completion to signal channel status bits acquisition done + */ struct mchp_spdifrx_ch_stat { unsigned char data[SPDIFRX_CS_BITS / 8]; struct completion done; }; +/** + * struct mchp_spdifrx_user_data: MCHP SPDIFRX user data + * @data: user data bits + * @done: completion to signal user data bits acquisition done + */ struct mchp_spdifrx_user_data { unsigned char data[SPDIFRX_UD_BITS / 8]; struct completion done; - spinlock_t lock; /* protect access to user data */ }; +/** + * struct mchp_spdifrx_mixer_control: MCHP SPDIFRX mixer control data structure + * @ch_stat: array of channel statuses + * @user_data: array of user data + * @ulock: ulock bit status + * @badf: badf bit status + * @signal: signal bit status + */ struct mchp_spdifrx_mixer_control { struct mchp_spdifrx_ch_stat ch_stat[SPDIFRX_CHANNELS]; struct mchp_spdifrx_user_data user_data[SPDIFRX_CHANNELS]; @@ -228,17 +285,26 @@ struct mchp_spdifrx_mixer_control { bool signal; }; +/** + * struct mchp_spdifrx_dev: MCHP SPDIFRX device data structure + * @capture: DAI DMA configuration data + * @control: mixer controls + * @mlock: mutex to protect concurency b/w configuration and control APIs + * @dev: struct device + * @regmap: regmap for this device + * @pclk: peripheral clock + * @gclk: generic clock + * @trigger_enabled: true if enabled though trigger() ops + */ struct mchp_spdifrx_dev { struct snd_dmaengine_dai_dma_data capture; struct mchp_spdifrx_mixer_control control; - spinlock_t blockend_lock; /* protect access to blockend_refcount */ - int blockend_refcount; + struct mutex mlock; struct device *dev; struct regmap *regmap; struct clk *pclk; struct clk *gclk; - unsigned int fmt; - unsigned int gclk_enabled:1; + unsigned int trigger_enabled; }; static void mchp_spdifrx_channel_status_read(struct mchp_spdifrx_dev *dev, @@ -275,37 +341,11 @@ static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev, } } -/* called from non-atomic context only */ -static void mchp_spdifrx_isr_blockend_en(struct mchp_spdifrx_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->blockend_lock, flags); - dev->blockend_refcount++; - /* don't enable BLOCKEND interrupt if it's already enabled */ - if (dev->blockend_refcount == 1) - regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND); - spin_unlock_irqrestore(&dev->blockend_lock, flags); -} - -/* called from atomic/non-atomic context */ -static void mchp_spdifrx_isr_blockend_dis(struct mchp_spdifrx_dev *dev) -{ - unsigned long flags; - - spin_lock_irqsave(&dev->blockend_lock, flags); - dev->blockend_refcount--; - /* don't enable BLOCKEND interrupt if it's already enabled */ - if (dev->blockend_refcount == 0) - regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); - spin_unlock_irqrestore(&dev->blockend_lock, flags); -} - static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) { struct mchp_spdifrx_dev *dev = dev_id; struct mchp_spdifrx_mixer_control *ctrl = &dev->control; - u32 sr, imr, pending, idr = 0; + u32 sr, imr, pending; irqreturn_t ret = IRQ_NONE; int ch; @@ -320,13 +360,10 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) if (pending & SPDIFRX_IR_BLOCKEND) { for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { - spin_lock(&ctrl->user_data[ch].lock); mchp_spdifrx_channel_user_data_read(dev, ch); - spin_unlock(&ctrl->user_data[ch].lock); - complete(&ctrl->user_data[ch].done); } - mchp_spdifrx_isr_blockend_dis(dev); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); ret = IRQ_HANDLED; } @@ -334,7 +371,7 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) if (pending & SPDIFRX_IR_CSC(ch)) { mchp_spdifrx_channel_status_read(dev, ch); complete(&ctrl->ch_stat[ch].done); - idr |= SPDIFRX_IR_CSC(ch); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(ch)); ret = IRQ_HANDLED; } } @@ -344,8 +381,6 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) ret = IRQ_HANDLED; } - regmap_write(dev->regmap, SPDIFRX_IDR, idr); - return ret; } @@ -353,47 +388,40 @@ static int mchp_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); - u32 mr; - int running; - int ret; - - regmap_read(dev->regmap, SPDIFRX_MR, &mr); - running = !!(mr & SPDIFRX_MR_RXEN_ENABLE); + int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!running) { - mr &= ~SPDIFRX_MR_RXEN_MASK; - mr |= SPDIFRX_MR_RXEN_ENABLE; - /* enable overrun interrupts */ - regmap_write(dev->regmap, SPDIFRX_IER, - SPDIFRX_IR_OVERRUN); - } + mutex_lock(&dev->mlock); + /* Enable overrun interrupts */ + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_OVERRUN); + + /* Enable receiver. */ + regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK, + SPDIFRX_MR_RXEN_ENABLE); + dev->trigger_enabled = true; + mutex_unlock(&dev->mlock); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (running) { - mr &= ~SPDIFRX_MR_RXEN_MASK; - mr |= SPDIFRX_MR_RXEN_DISABLE; - /* disable overrun interrupts */ - regmap_write(dev->regmap, SPDIFRX_IDR, - SPDIFRX_IR_OVERRUN); - } + mutex_lock(&dev->mlock); + /* Disable overrun interrupts */ + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_OVERRUN); + + /* Disable receiver. */ + regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK, + SPDIFRX_MR_RXEN_DISABLE); + dev->trigger_enabled = false; + mutex_unlock(&dev->mlock); break; default: - return -EINVAL; + ret = -EINVAL; } - ret = regmap_write(dev->regmap, SPDIFRX_MR, mr); - if (ret) { - dev_err(dev->dev, "unable to enable/disable RX: %d\n", ret); - return ret; - } - - return 0; + return ret; } static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream, @@ -413,13 +441,6 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - regmap_read(dev->regmap, SPDIFRX_MR, &mr); - - if (mr & SPDIFRX_MR_RXEN_ENABLE) { - dev_err(dev->dev, "PCM already running\n"); - return -EBUSY; - } - if (params_channels(params) != SPDIFRX_CHANNELS) { dev_err(dev->dev, "unsupported number of channels: %d\n", params_channels(params)); @@ -445,47 +466,46 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (dev->gclk_enabled) { - clk_disable_unprepare(dev->gclk); - dev->gclk_enabled = 0; + mutex_lock(&dev->mlock); + if (dev->trigger_enabled) { + dev_err(dev->dev, "PCM already running\n"); + ret = -EBUSY; + goto unlock; } + + /* GCLK is enabled by runtime PM. */ + clk_disable_unprepare(dev->gclk); + ret = clk_set_min_rate(dev->gclk, params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1); if (ret) { dev_err(dev->dev, "unable to set gclk min rate: rate %u * ratio %u + 1\n", params_rate(params), SPDIFRX_GCLK_RATIO_MIN); - return ret; + /* Restore runtime PM state. */ + clk_prepare_enable(dev->gclk); + goto unlock; } ret = clk_prepare_enable(dev->gclk); if (ret) { dev_err(dev->dev, "unable to enable gclk: %d\n", ret); - return ret; + goto unlock; } - dev->gclk_enabled = 1; dev_dbg(dev->dev, "GCLK range min set to %d\n", params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1); - return regmap_write(dev->regmap, SPDIFRX_MR, mr); -} + ret = regmap_write(dev->regmap, SPDIFRX_MR, mr); -static int mchp_spdifrx_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); +unlock: + mutex_unlock(&dev->mlock); - if (dev->gclk_enabled) { - clk_disable_unprepare(dev->gclk); - dev->gclk_enabled = 0; - } - return 0; + return ret; } static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = { .trigger = mchp_spdifrx_trigger, .hw_params = mchp_spdifrx_hw_params, - .hw_free = mchp_spdifrx_hw_free, }; #define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000 @@ -515,22 +535,58 @@ static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev, { struct mchp_spdifrx_mixer_control *ctrl = &dev->control; struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel]; - int ret; - - regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel)); - /* check for new data available */ - ret = wait_for_completion_interruptible_timeout(&ch_stat->done, - msecs_to_jiffies(100)); - /* IP might not be started or valid stream might not be present */ - if (ret < 0) { - dev_dbg(dev->dev, "channel status for channel %d timeout\n", - channel); + int ret = 0; + + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * We may reach this point with both clocks enabled but the receiver + * still disabled. To void waiting for completion and return with + * timeout check the dev->trigger_enabled. + * + * To retrieve data: + * - if the receiver is enabled CSC IRQ will update the data in software + * caches (ch_stat->data) + * - otherwise we just update it here the software caches with latest + * available information and return it; in this case we don't need + * spin locking as the IRQ is disabled and will not be raised from + * anywhere else. + */ + + if (dev->trigger_enabled) { + reinit_completion(&ch_stat->done); + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel)); + /* Check for new data available */ + ret = wait_for_completion_interruptible_timeout(&ch_stat->done, + msecs_to_jiffies(100)); + /* Valid stream might not be present */ + if (ret <= 0) { + dev_dbg(dev->dev, "channel status for channel %d timeout\n", + channel); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(channel)); + ret = ret ? : -ETIMEDOUT; + goto pm_runtime_put; + } else { + ret = 0; + } + } else { + /* Update software cache with latest channel status. */ + mchp_spdifrx_channel_status_read(dev, channel); } memcpy(uvalue->value.iec958.status, ch_stat->data, sizeof(ch_stat->data)); - return 0; +pm_runtime_put: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + return ret; } static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol, @@ -564,29 +620,56 @@ static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev, int channel, struct snd_ctl_elem_value *uvalue) { - unsigned long flags; struct mchp_spdifrx_mixer_control *ctrl = &dev->control; struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel]; - int ret; - - reinit_completion(&user_data->done); - mchp_spdifrx_isr_blockend_en(dev); - ret = wait_for_completion_interruptible_timeout(&user_data->done, - msecs_to_jiffies(100)); - /* IP might not be started or valid stream might not be present */ - if (ret <= 0) { - dev_dbg(dev->dev, "user data for channel %d timeout\n", - channel); - mchp_spdifrx_isr_blockend_dis(dev); - return ret; + int ret = 0; + + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * We may reach this point with both clocks enabled but the receiver + * still disabled. To void waiting for completion to just timeout we + * check here the dev->trigger_enabled flag. + * + * To retrieve data: + * - if the receiver is enabled we need to wait for blockend IRQ to read + * data to and update it for us in software caches + * - otherwise reading the SPDIFRX_CHUD() registers is enough. + */ + + if (dev->trigger_enabled) { + reinit_completion(&user_data->done); + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND); + ret = wait_for_completion_interruptible_timeout(&user_data->done, + msecs_to_jiffies(100)); + /* Valid stream might not be present. */ + if (ret <= 0) { + dev_dbg(dev->dev, "user data for channel %d timeout\n", + channel); + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); + ret = ret ? : -ETIMEDOUT; + goto pm_runtime_put; + } else { + ret = 0; + } + } else { + /* Update software cache with last available data. */ + mchp_spdifrx_channel_user_data_read(dev, channel); } - spin_lock_irqsave(&user_data->lock, flags); memcpy(uvalue->value.iec958.subcode, user_data->data, sizeof(user_data->data)); - spin_unlock_irqrestore(&user_data->lock, flags); - return 0; +pm_runtime_put: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + return ret; } static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol, @@ -625,12 +708,34 @@ static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol, struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); struct mchp_spdifrx_mixer_control *ctrl = &dev->control; u32 val; + int ret; bool ulock_old = ctrl->ulock; - regmap_read(dev->regmap, SPDIFRX_RSR, &val); - ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK); + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * The RSR.ULOCK has wrong value if both pclk and gclk are enabled + * and the receiver is disabled. Thus we take into account the + * dev->trigger_enabled here to return a real status. + */ + if (dev->trigger_enabled) { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK); + } else { + ctrl->ulock = 0; + } + uvalue->value.integer.value[0] = ctrl->ulock; + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + return ulock_old != ctrl->ulock; } @@ -641,10 +746,32 @@ static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol, struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); struct mchp_spdifrx_mixer_control *ctrl = &dev->control; u32 val; + int ret; bool badf_old = ctrl->badf; - regmap_read(dev->regmap, SPDIFRX_RSR, &val); - ctrl->badf = !!(val & SPDIFRX_RSR_BADF); + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * The RSR.ULOCK has wrong value if both pclk and gclk are enabled + * and the receiver is disabled. Thus we take into account the + * dev->trigger_enabled here to return a real status. + */ + if (dev->trigger_enabled) { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + ctrl->badf = !!(val & SPDIFRX_RSR_BADF); + } else { + ctrl->badf = 0; + } + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + uvalue->value.integer.value[0] = ctrl->badf; return badf_old != ctrl->badf; @@ -656,11 +783,49 @@ static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol, struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); struct mchp_spdifrx_mixer_control *ctrl = &dev->control; - u32 val; + u32 val = ~0U, loops = 10; + int ret; bool signal_old = ctrl->signal; - regmap_read(dev->regmap, SPDIFRX_RSR, &val); - ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL); + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * To get the signal we need to have receiver enabled. This + * could be enabled also from trigger() function thus we need to + * take care of not disabling the receiver when it runs. + */ + if (!dev->trigger_enabled) { + regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK, + SPDIFRX_MR_RXEN_ENABLE); + + /* Wait for RSR.ULOCK bit. */ + while (--loops) { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + if (!(val & SPDIFRX_RSR_ULOCK)) + break; + usleep_range(100, 150); + } + + regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK, + SPDIFRX_MR_RXEN_DISABLE); + } else { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + } + + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + +unlock: + mutex_unlock(&dev->mlock); + + if (!(val & SPDIFRX_RSR_ULOCK)) + ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL); + else + ctrl->signal = 0; uvalue->value.integer.value[0] = ctrl->signal; return signal_old != ctrl->signal; @@ -682,22 +847,44 @@ static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol, { struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + unsigned long rate; u32 val; - int rate; - - regmap_read(dev->regmap, SPDIFRX_RSR, &val); + int ret; - /* if the receiver is not locked, ISF data is invalid */ - if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) { + mutex_lock(&dev->mlock); + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret < 0) + goto unlock; + + /* + * The RSR.ULOCK has wrong value if both pclk and gclk are enabled + * and the receiver is disabled. Thus we take into account the + * dev->trigger_enabled here to return a real status. + */ + if (dev->trigger_enabled) { + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + /* If the receiver is not locked, ISF data is invalid. */ + if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) { + ucontrol->value.integer.value[0] = 0; + goto pm_runtime_put; + } + } else { + /* Reveicer is not locked, IFS data is invalid. */ ucontrol->value.integer.value[0] = 0; - return 0; + goto pm_runtime_put; } rate = clk_get_rate(dev->gclk); ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val)); - return 0; +pm_runtime_put: + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); +unlock: + mutex_unlock(&dev->mlock); + return ret; } static struct snd_kcontrol_new mchp_spdifrx_ctrls[] = { @@ -787,14 +974,6 @@ static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai) struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); struct mchp_spdifrx_mixer_control *ctrl = &dev->control; int ch; - int err; - - err = clk_prepare_enable(dev->pclk); - if (err) { - dev_err(dev->dev, - "failed to enable the peripheral clock: %d\n", err); - return err; - } snd_soc_dai_init_dma_data(dai, NULL, &dev->capture); @@ -808,11 +987,9 @@ static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai) SPDIFRX_MR_AUTORST_NOACTION | SPDIFRX_MR_PACK_DISABLED); - dev->blockend_refcount = 0; for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { init_completion(&ctrl->ch_stat[ch].done); init_completion(&ctrl->user_data[ch].done); - spin_lock_init(&ctrl->user_data[ch].lock); } /* Add controls */ @@ -827,9 +1004,7 @@ static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai) struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); /* Disable interrupts */ - regmap_write(dev->regmap, SPDIFRX_IDR, 0xFF); - - clk_disable_unprepare(dev->pclk); + regmap_write(dev->regmap, SPDIFRX_IDR, GENMASK(14, 0)); return 0; } @@ -861,6 +1036,48 @@ static const struct of_device_id mchp_spdifrx_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, mchp_spdifrx_dt_ids); +static int mchp_spdifrx_runtime_suspend(struct device *dev) +{ + struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev); + + regcache_cache_only(spdifrx->regmap, true); + clk_disable_unprepare(spdifrx->gclk); + clk_disable_unprepare(spdifrx->pclk); + + return 0; +} + +static int mchp_spdifrx_runtime_resume(struct device *dev) +{ + struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(spdifrx->pclk); + if (ret) + return ret; + + ret = clk_prepare_enable(spdifrx->gclk); + if (ret) + goto disable_pclk; + + regcache_cache_only(spdifrx->regmap, false); + regcache_mark_dirty(spdifrx->regmap); + ret = regcache_sync(spdifrx->regmap); + if (ret) { + regcache_cache_only(spdifrx->regmap, true); + clk_disable_unprepare(spdifrx->gclk); +disable_pclk: + clk_disable_unprepare(spdifrx->pclk); + } + + return ret; +} + +static const struct dev_pm_ops mchp_spdifrx_pm_ops = { + RUNTIME_PM_OPS(mchp_spdifrx_runtime_suspend, mchp_spdifrx_runtime_resume, + NULL) +}; + static int mchp_spdifrx_probe(struct platform_device *pdev) { struct mchp_spdifrx_dev *dev; @@ -913,19 +1130,36 @@ static int mchp_spdifrx_probe(struct platform_device *pdev) "failed to get the PMC generated clock: %d\n", err); return err; } - spin_lock_init(&dev->blockend_lock); + + /* + * Signal control need a valid rate on gclk. hw_params() configures + * it propertly but requesting signal before any hw_params() has been + * called lead to invalid value returned for signal. Thus, configure + * gclk at a valid rate, here, in initialization, to simplify the + * control path. + */ + clk_set_min_rate(dev->gclk, 48000 * SPDIFRX_GCLK_RATIO_MIN + 1); + + mutex_init(&dev->mlock); dev->dev = &pdev->dev; dev->regmap = regmap; platform_set_drvdata(pdev, dev); + pm_runtime_enable(dev->dev); + if (!pm_runtime_enabled(dev->dev)) { + err = mchp_spdifrx_runtime_resume(dev->dev); + if (err) + goto pm_runtime_disable; + } + dev->capture.addr = (dma_addr_t)mem->start + SPDIFRX_RHR; dev->capture.maxburst = 1; err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); if (err) { dev_err(&pdev->dev, "failed to register PCM: %d\n", err); - return err; + goto pm_runtime_suspend; } err = devm_snd_soc_register_component(&pdev->dev, @@ -933,20 +1167,40 @@ static int mchp_spdifrx_probe(struct platform_device *pdev) &mchp_spdifrx_dai, 1); if (err) { dev_err(&pdev->dev, "fail to register dai\n"); - return err; + goto pm_runtime_suspend; } regmap_read(regmap, SPDIFRX_VERSION, &vers); dev_info(&pdev->dev, "hw version: %#lx\n", vers & SPDIFRX_VERSION_MASK); return 0; + +pm_runtime_suspend: + if (!pm_runtime_status_suspended(dev->dev)) + mchp_spdifrx_runtime_suspend(dev->dev); +pm_runtime_disable: + pm_runtime_disable(dev->dev); + return err; +} + +static int mchp_spdifrx_remove(struct platform_device *pdev) +{ + struct mchp_spdifrx_dev *dev = platform_get_drvdata(pdev); + + pm_runtime_disable(dev->dev); + if (!pm_runtime_status_suspended(dev->dev)) + mchp_spdifrx_runtime_suspend(dev->dev); + + return 0; } static struct platform_driver mchp_spdifrx_driver = { .probe = mchp_spdifrx_probe, + .remove = mchp_spdifrx_remove, .driver = { .name = "mchp_spdifrx", .of_match_table = of_match_ptr(mchp_spdifrx_dt_ids), + .pm = pm_ptr(&mchp_spdifrx_pm_ops), }, }; diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c index 37593abe6053..cec067c91a0f 100644 --- a/sound/soc/cirrus/ep93xx-ac97.c +++ b/sound/soc/cirrus/ep93xx-ac97.c @@ -323,8 +323,8 @@ static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai) info->dma_params_tx.filter_data = &ep93xx_ac97_pcm_out; info->dma_params_rx.filter_data = &ep93xx_ac97_pcm_in; - dai->playback_dma_data = &info->dma_params_tx; - dai->capture_dma_data = &info->dma_params_rx; + snd_soc_dai_init_dma_data(dai, &info->dma_params_tx, + &info->dma_params_rx); return 0; } diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c index 982151330c89..8265173a7932 100644 --- a/sound/soc/cirrus/ep93xx-i2s.c +++ b/sound/soc/cirrus/ep93xx-i2s.c @@ -202,8 +202,8 @@ static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai) info->dma_params_rx.filter_data = &ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE]; - dai->playback_dma_data = &info->dma_params_tx; - dai->capture_dma_data = &info->dma_params_rx; + snd_soc_dai_init_dma_data(dai, &info->dma_params_tx, + &info->dma_params_rx); return 0; } diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index de290c8daeac..5028d67cfd8e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -54,6 +54,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ALC5623 imply SND_SOC_ALC5632 imply SND_SOC_AW8738 + imply SND_SOC_AW88395 imply SND_SOC_BT_SCO imply SND_SOC_BD28623 imply SND_SOC_CQ0093VC @@ -108,6 +109,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_HDAC_HDMI imply SND_SOC_HDAC_HDA imply SND_SOC_ICS43432 + imply SND_SOC_IDT821034 imply SND_SOC_INNO_RK3036 imply SND_SOC_ISABELLE imply SND_SOC_JZ4740_CODEC @@ -601,6 +603,22 @@ config SND_SOC_AW8738 SND_SOC_SIMPLE_AMPLIFIER, but additionally allows setting the operation mode using the Awinic-specific one-wire pulse control. +config SND_SOC_AW88395_LIB + tristate + +config SND_SOC_AW88395 + tristate "Soc Audio for awinic aw88395" + depends on I2C + select CRC8 + select CRC32 + select REGMAP_I2C + select SND_SOC_AW88395_LIB + help + this option enables support for aw88395 Smart PA. + The Awinic AW88395 is an I2S/TDM input, high efficiency + digital Smart K audio amplifier with an integrated 10V + smart boost convert. + config SND_SOC_BD28623 tristate "ROHM BD28623 CODEC" help @@ -981,6 +999,16 @@ config SND_SOC_HDA config SND_SOC_ICS43432 tristate "ICS43423 and compatible i2s microphones" +config SND_SOC_IDT821034 + tristate "Renesas IDT821034 quad PCM codec" + depends on SPI + help + Enable support for the Renesas IDT821034 quad PCM with + programmable gain codec. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-idt821034. + config SND_SOC_INNO_RK3036 tristate "Inno codec driver for RK3036 SoC" select REGMAP_MMIO diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 90c93f9442f2..e88cb8b5baec 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -46,6 +46,9 @@ snd-soc-ak5386-objs := ak5386.o snd-soc-ak5558-objs := ak5558.o snd-soc-arizona-objs := arizona.o arizona-jack.o snd-soc-aw8738-objs := aw8738.o +snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o +snd-soc-aw88395-objs := aw88395/aw88395.o \ + aw88395/aw88395_device.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-cpcap-objs := cpcap.o @@ -112,6 +115,7 @@ snd-soc-hdac-hdmi-objs := hdac_hdmi.o snd-soc-hdac-hda-objs := hdac_hda.o snd-soc-hda-codec-objs := hda.o hda-dai.o snd-soc-ics43432-objs := ics43432.o +snd-soc-idt821034-objs := idt821034.o snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o @@ -409,6 +413,8 @@ obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o +obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o +obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o @@ -475,6 +481,7 @@ obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o obj-$(CONFIG_SND_SOC_HDA) += snd-soc-hda-codec.o obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o +obj-$(CONFIG_SND_SOC_IDT821034) += snd-soc-idt821034.o obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c new file mode 100644 index 000000000000..bce403744c1a --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -0,0 +1,580 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395.c -- ALSA SoC AW88395 codec support +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <[email protected]> +// Author: Weidong Wang <[email protected]> +// + +#include <linux/i2c.h> +#include <linux/firmware.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "aw88395.h" +#include "aw88395_device.h" +#include "aw88395_lib.h" +#include "aw88395_reg.h" + +static const struct regmap_config aw88395_remap_config = { + .val_bits = 16, + .reg_bits = 8, + .max_register = AW88395_REG_MAX - 1, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_BIG, +}; + +static void aw88395_start_pa(struct aw88395 *aw88395) +{ + int ret, i; + + for (i = 0; i < AW88395_START_RETRIES; i++) { + ret = aw88395_dev_start(aw88395->aw_pa); + if (ret) { + dev_err(aw88395->aw_pa->dev, "aw88395 device start failed. retry = %d", i); + ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_ON, true); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "fw update failed"); + continue; + } + } else { + dev_info(aw88395->aw_pa->dev, "start success\n"); + break; + } + } +} + +static void aw88395_startup_work(struct work_struct *work) +{ + struct aw88395 *aw88395 = + container_of(work, struct aw88395, start_work.work); + + mutex_lock(&aw88395->lock); + aw88395_start_pa(aw88395); + mutex_unlock(&aw88395->lock); +} + +static void aw88395_start(struct aw88395 *aw88395, bool sync_start) +{ + int ret; + + if (aw88395->aw_pa->fw_status != AW88395_DEV_FW_OK) + return; + + if (aw88395->aw_pa->status == AW88395_DEV_PW_ON) + return; + + ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_OFF, true); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "fw update failed."); + return; + } + + if (sync_start == AW88395_SYNC_START) + aw88395_start_pa(aw88395); + else + queue_delayed_work(system_wq, + &aw88395->start_work, + AW88395_START_WORK_DELAY_MS); +} + +static struct snd_soc_dai_driver aw88395_dai[] = { + { + .name = "aw88395-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW88395_RATES, + .formats = AW88395_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW88395_RATES, + .formats = AW88395_FORMATS, + }, + }, +}; + +static int aw88395_get_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + + return 0; +} + +static int aw88395_set_fade_in_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_in_time) { + aw_dev->fade_in_time = time; + return 1; + } + + return 0; +} + +static int aw88395_get_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + + return 0; +} + +static int aw88395_set_fade_out_time(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int time; + + time = ucontrol->value.integer.value[0]; + if (time < mc->min || time > mc->max) + return -EINVAL; + + if (time != aw_dev->fade_out_time) { + aw_dev->fade_out_time = time; + return 1; + } + + return 0; +} + +static int aw88395_profile_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + const char *prof_name; + char *name; + int count; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + count = aw88395_dev_get_profile_count(aw88395->aw_pa); + if (count <= 0) { + uinfo->value.enumerated.items = 0; + return 0; + } + + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + name = uinfo->value.enumerated.name; + count = uinfo->value.enumerated.item; + + prof_name = aw88395_dev_get_prof_name(aw88395->aw_pa, count); + if (!prof_name) { + strscpy(uinfo->value.enumerated.name, "null", + strlen("null") + 1); + return 0; + } + + strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name)); + + return 0; +} + +static int aw88395_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88395_dev_get_profile_index(aw88395->aw_pa); + + return 0; +} + +static int aw88395_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + int ret; + + /* pa stop or stopping just set profile */ + mutex_lock(&aw88395->lock); + ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]); + if (ret < 0) { + dev_dbg(codec->dev, "profile index does not change"); + mutex_unlock(&aw88395->lock); + return 0; + } + + if (aw88395->aw_pa->status) { + aw88395_dev_stop(aw88395->aw_pa); + aw88395_start(aw88395, AW88395_SYNC_START); + } + + mutex_unlock(&aw88395->lock); + + return 1; +} + +static int aw88395_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc; + + ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + + return 0; +} + +static int aw88395_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc; + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (vol_desc->ctl_volume != value) { + vol_desc->ctl_volume = value; + aw88395_dev_set_volume(aw88395->aw_pa, vol_desc->ctl_volume); + + return 1; + } + + return 0; +} + +static int aw88395_get_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = aw88395->aw_pa->fade_step; + + return 0; +} + +static int aw88395_set_fade_step(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw88395->aw_pa->fade_step != value) { + aw88395->aw_pa->fade_step = value; + return 1; + } + + return 0; +} + +static int aw88395_re_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct aw_device *aw_dev = aw88395->aw_pa; + + ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re; + + return 0; +} + +static int aw88395_re_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct aw_device *aw_dev = aw88395->aw_pa; + int value; + + value = ucontrol->value.integer.value[0]; + if (value < mc->min || value > mc->max) + return -EINVAL; + + if (aw_dev->cali_desc.cali_re != value) { + aw_dev->cali_desc.cali_re = value; + return 1; + } + + return 0; +} + +static const struct snd_kcontrol_new aw88395_controls[] = { + SOC_SINGLE_EXT("PCM Playback Volume", AW88395_SYSCTRL2_REG, + 6, AW88395_MUTE_VOL, 0, aw88395_volume_get, + aw88395_volume_set), + SOC_SINGLE_EXT("Fade Step", 0, 0, AW88395_MUTE_VOL, 0, + aw88395_get_fade_step, aw88395_set_fade_step), + SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88395_get_fade_in_time, aw88395_set_fade_in_time), + SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, + aw88395_get_fade_out_time, aw88395_set_fade_out_time), + SOC_SINGLE_EXT("Calib", 0, 0, 100, 0, + aw88395_re_get, aw88395_re_set), + AW88395_PROFILE_EXT("Profile Set", aw88395_profile_info, + aw88395_profile_get, aw88395_profile_set), +}; + +static int aw88395_plack_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + + mutex_lock(&aw88395->lock); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + aw88395_start(aw88395, AW88395_ASYNC_START); + break; + case SND_SOC_DAPM_POST_PMD: + aw88395_dev_stop(aw88395->aw_pa); + break; + default: + break; + } + mutex_unlock(&aw88395->lock); + + return 0; +} + +static const struct snd_soc_dapm_widget aw88395_dapm_widgets[] = { + /* playback */ + SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0, + aw88395_plack_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("DAC Output"), + + /* capture */ + SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("ADC Input"), +}; + +static const struct snd_soc_dapm_route aw88395_audio_map[] = { + {"DAC Output", NULL, "AIF_RX"}, + {"AIF_TX", NULL, "ADC Input"}, +}; + +static int aw88395_codec_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component); + int ret; + + INIT_DELAYED_WORK(&aw88395->start_work, aw88395_startup_work); + + /* add widgets */ + ret = snd_soc_dapm_new_controls(dapm, aw88395_dapm_widgets, + ARRAY_SIZE(aw88395_dapm_widgets)); + if (ret < 0) + return ret; + + /* add route */ + ret = snd_soc_dapm_add_routes(dapm, aw88395_audio_map, + ARRAY_SIZE(aw88395_audio_map)); + if (ret < 0) + return ret; + + ret = snd_soc_add_component_controls(component, aw88395_controls, + ARRAY_SIZE(aw88395_controls)); + + return ret; +} + +static void aw88395_codec_remove(struct snd_soc_component *aw_codec) +{ + struct aw88395 *aw88395 = snd_soc_component_get_drvdata(aw_codec); + + cancel_delayed_work_sync(&aw88395->start_work); +} + +static const struct snd_soc_component_driver soc_codec_dev_aw88395 = { + .probe = aw88395_codec_probe, + .remove = aw88395_codec_remove, +}; + +static struct aw88395 *aw88395_malloc_init(struct i2c_client *i2c) +{ + struct aw88395 *aw88395 = devm_kzalloc(&i2c->dev, + sizeof(struct aw88395), GFP_KERNEL); + if (!aw88395) + return NULL; + + mutex_init(&aw88395->lock); + + return aw88395; +} + +static void aw88395_hw_reset(struct aw88395 *aw88395) +{ + if (aw88395->reset_gpio) { + gpiod_set_value_cansleep(aw88395->reset_gpio, 0); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + gpiod_set_value_cansleep(aw88395->reset_gpio, 1); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + } else { + dev_err(aw88395->aw_pa->dev, "%s failed", __func__); + } +} + +static int aw88395_request_firmware_file(struct aw88395 *aw88395) +{ + const struct firmware *cont = NULL; + int ret; + + aw88395->aw_pa->fw_status = AW88395_DEV_FW_FAILED; + + ret = request_firmware(&cont, AW88395_ACF_FILE, aw88395->aw_pa->dev); + if ((ret < 0) || (!cont)) { + dev_err(aw88395->aw_pa->dev, "load [%s] failed!", AW88395_ACF_FILE); + return ret; + } + + dev_info(aw88395->aw_pa->dev, "loaded %s - size: %zu\n", + AW88395_ACF_FILE, cont ? cont->size : 0); + + aw88395->aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); + if (!aw88395->aw_cfg) { + release_firmware(cont); + return -ENOMEM; + } + aw88395->aw_cfg->len = (int)cont->size; + memcpy(aw88395->aw_cfg->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw88395_dev_load_acf_check(aw88395->aw_pa, aw88395->aw_cfg); + if (ret < 0) { + dev_err(aw88395->aw_pa->dev, "Load [%s] failed ....!", AW88395_ACF_FILE); + return ret; + } + + dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__); + + mutex_lock(&aw88395->lock); + /* aw device init */ + ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg); + if (ret < 0) + dev_err(aw88395->aw_pa->dev, "dev init failed"); + mutex_unlock(&aw88395->lock); + + return ret; +} + +static int aw88395_i2c_probe(struct i2c_client *i2c) +{ + struct aw88395 *aw88395; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed"); + return -EIO; + } + + aw88395 = aw88395_malloc_init(i2c); + if (!aw88395) { + dev_err(&i2c->dev, "malloc aw88395 failed"); + return -ENOMEM; + } + i2c_set_clientdata(i2c, aw88395); + + aw88395->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(aw88395->reset_gpio)) + dev_info(&i2c->dev, "reset gpio not defined\n"); + + /* hardware reset */ + aw88395_hw_reset(aw88395); + + aw88395->regmap = devm_regmap_init_i2c(i2c, &aw88395_remap_config); + if (IS_ERR(aw88395->regmap)) { + ret = PTR_ERR(aw88395->regmap); + dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret); + return ret; + } + + /* aw pa init */ + ret = aw88395_init(&aw88395->aw_pa, i2c, aw88395->regmap); + if (ret < 0) + return ret; + + ret = aw88395_request_firmware_file(aw88395); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed\n", __func__); + return ret; + } + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_aw88395, + aw88395_dai, ARRAY_SIZE(aw88395_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "failed to register aw88395: %d", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id aw88395_i2c_id[] = { + { AW88395_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id); + +static struct i2c_driver aw88395_i2c_driver = { + .driver = { + .name = AW88395_I2C_NAME, + .owner = THIS_MODULE, + }, + .probe_new = aw88395_i2c_probe, + .id_table = aw88395_i2c_id, +}; +module_i2c_driver(aw88395_i2c_driver); + +MODULE_DESCRIPTION("ASoC AW88395 Smart PA Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395.h b/sound/soc/codecs/aw88395/aw88395.h new file mode 100644 index 000000000000..8036ba27f68d --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395.h -- ALSA SoC AW88395 codec support +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <[email protected]> +// + +#ifndef __AW88395_H__ +#define __AW88395_H__ + +#define AW88395_CHIP_ID_REG (0x00) +#define AW88395_START_RETRIES (5) +#define AW88395_START_WORK_DELAY_MS (0) + +#define AW88395_DSP_16_DATA_MASK (0x0000ffff) + +#define AW88395_I2C_NAME "aw88395_smartpa" + +#define AW88395_RATES (SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_96000) +#define AW88395_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define FADE_TIME_MAX 100000 +#define FADE_TIME_MIN 0 + +#define AW88395_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = profile_info, \ + .get = profile_get, \ + .put = profile_set, \ +} + +enum { + AW88395_SYNC_START = 0, + AW88395_ASYNC_START, +}; + +enum { + AW88395_STREAM_CLOSE = 0, + AW88395_STREAM_OPEN, +}; + +struct aw88395 { + struct aw_device *aw_pa; + struct mutex lock; + struct gpio_desc *reset_gpio; + struct delayed_work start_work; + struct regmap *regmap; + struct aw_container *aw_cfg; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_data_type.h b/sound/soc/codecs/aw88395/aw88395_data_type.h new file mode 100644 index 000000000000..e7aa56178b36 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_data_type.h @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw883_data_type.h -- The data type of the AW88395 chip +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <[email protected]> +// + +#ifndef __AW88395_DATA_TYPE_H__ +#define __AW88395_DATA_TYPE_H__ + +#define PROJECT_NAME_MAX (24) +#define CUSTOMER_NAME_MAX (16) +#define CFG_VERSION_MAX (4) +#define DEV_NAME_MAX (16) +#define PROFILE_STR_MAX (32) + +#define ACF_FILE_ID (0xa15f908) + +enum aw_cfg_hdr_version { + AW88395_CFG_HDR_VER = 0x00000001, + AW88395_CFG_HDR_VER_V1 = 0x01000000, +}; + +enum aw_cfg_dde_type { + AW88395_DEV_NONE_TYPE_ID = 0xFFFFFFFF, + AW88395_DEV_TYPE_ID = 0x00000000, + AW88395_SKT_TYPE_ID = 0x00000001, + AW88395_DEV_DEFAULT_TYPE_ID = 0x00000002, +}; + +enum aw_sec_type { + ACF_SEC_TYPE_REG = 0, + ACF_SEC_TYPE_DSP, + ACF_SEC_TYPE_DSP_CFG, + ACF_SEC_TYPE_DSP_FW, + ACF_SEC_TYPE_HDR_REG, + ACF_SEC_TYPE_HDR_DSP_CFG, + ACF_SEC_TYPE_HDR_DSP_FW, + ACF_SEC_TYPE_MULTIPLE_BIN, + ACF_SEC_TYPE_SKT_PROJECT, + ACF_SEC_TYPE_DSP_PROJECT, + ACF_SEC_TYPE_MONITOR, + ACF_SEC_TYPE_MAX, +}; + +enum profile_data_type { + AW88395_DATA_TYPE_REG = 0, + AW88395_DATA_TYPE_DSP_CFG, + AW88395_DATA_TYPE_DSP_FW, + AW88395_DATA_TYPE_MAX, +}; + +enum aw_prof_type { + AW88395_PROFILE_MUSIC = 0, + AW88395_PROFILE_VOICE, + AW88395_PROFILE_VOIP, + AW88395_PROFILE_RINGTONE, + AW88395_PROFILE_RINGTONE_HS, + AW88395_PROFILE_LOWPOWER, + AW88395_PROFILE_BYPASS, + AW88395_PROFILE_MMI, + AW88395_PROFILE_FM, + AW88395_PROFILE_NOTIFICATION, + AW88395_PROFILE_RECEIVER, + AW88395_PROFILE_MAX, +}; + +enum aw_profile_status { + AW88395_PROFILE_WAIT = 0, + AW88395_PROFILE_OK, +}; + +struct aw_cfg_hdr { + u32 id; + char project[PROJECT_NAME_MAX]; + char custom[CUSTOMER_NAME_MAX]; + char version[CFG_VERSION_MAX]; + u32 author_id; + u32 ddt_size; + u32 ddt_num; + u32 hdr_offset; + u32 hdr_version; + u32 reserved[3]; +}; + +struct aw_cfg_dde { + u32 type; + char dev_name[DEV_NAME_MAX]; + u16 dev_index; + u16 dev_bus; + u16 dev_addr; + u16 dev_profile; + u32 data_type; + u32 data_size; + u32 data_offset; + u32 data_crc; + u32 reserved[5]; +}; + +struct aw_cfg_dde_v1 { + u32 type; + char dev_name[DEV_NAME_MAX]; + u16 dev_index; + u16 dev_bus; + u16 dev_addr; + u16 dev_profile; + u32 data_type; + u32 data_size; + u32 data_offset; + u32 data_crc; + char dev_profile_str[PROFILE_STR_MAX]; + u32 chip_id; + u32 reserved[4]; +}; + +struct aw_sec_data_desc { + u32 len; + u8 *data; +}; + +struct aw_prof_desc { + u32 id; + u32 prof_st; + char *prf_str; + u32 fw_ver; + struct aw_sec_data_desc sec_desc[AW88395_DATA_TYPE_MAX]; +}; + +struct aw_all_prof_info { + struct aw_prof_desc prof_desc[AW88395_PROFILE_MAX]; +}; + +struct aw_prof_info { + int count; + int prof_type; + char **prof_name_list; + struct aw_prof_desc *prof_desc; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c new file mode 100644 index 000000000000..33eda3741464 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_device.c @@ -0,0 +1,1748 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_device.c -- AW88395 function for ALSA Audio Driver +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <[email protected]> +// Author: Ben Yi <[email protected]> +// + +#include <linux/crc32.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include "aw88395_device.h" +#include "aw88395_reg.h" + +static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)dsp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write data error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data) +{ + u16 temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data & AW88395_DSP_16_DATA_MASK; + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret); + return ret; + } + + temp_data = dsp_data >> 16; + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret); + return ret; + } + + return 0; +} + +static int aw_dev_dsp_write(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88395_DSP_16_DATA: + ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + (u32)dsp_addr, dsp_data); + break; + case AW88395_DSP_32_DATA: + ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed", + (u32)dsp_addr, dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + +static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data = temp_data; + + return 0; +} + +static int aw_dev_dsp_read_32bit(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data) +{ + unsigned int temp_data; + int ret; + + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr); + if (ret) { + dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret); + return ret; + } + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data = temp_data; + + ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data); + if (ret) { + dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret); + return ret; + } + *dsp_data |= (temp_data << 16); + + return 0; +} + +static int aw_dev_dsp_read(struct aw_device *aw_dev, + unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type) +{ + u32 reg_value; + int ret; + + mutex_lock(&aw_dev->dsp_lock); + switch (data_type) { + case AW88395_DSP_16_DATA: + ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed", + (u32)dsp_addr, *dsp_data); + break; + case AW88395_DSP_32_DATA: + ret = aw_dev_dsp_read_32bit(aw_dev, dsp_addr, dsp_data); + if (ret) + dev_err(aw_dev->dev, "read dsp_addr[0x%x] 32r-bit dsp_data[0x%x] failed", + (u32)dsp_addr, *dsp_data); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + ret = -EINVAL; + break; + } + + /* clear dsp chip select state*/ + if (regmap_read(aw_dev->regmap, AW88395_ID_REG, ®_value)) + dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret); + mutex_unlock(&aw_dev->dsp_lock); + + return ret; +} + + +static int aw_dev_read_chipid(struct aw_device *aw_dev, u16 *chip_id) +{ + int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_CHIP_ID_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "%s read chipid error. ret = %d", __func__, ret); + return ret; + } + + dev_info(aw_dev->dev, "chip id = %x\n", reg_val); + *chip_id = reg_val; + + return 0; +} + +static unsigned int reg_val_to_db(unsigned int value) +{ + return (((value >> AW88395_VOL_6DB_START) * AW88395_VOLUME_STEP_DB) + + ((value & 0x3f) % AW88395_VOLUME_STEP_DB)); +} + +static unsigned short db_to_reg_val(unsigned short value) +{ + return (((value / AW88395_VOLUME_STEP_DB) << AW88395_VOL_6DB_START) + + (value % AW88395_VOLUME_STEP_DB)); +} + +static int aw_dev_dsp_fw_check(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_fw_desc; + struct aw_prof_desc *set_prof_desc; + u16 base_addr = AW88395_DSP_FW_ADDR; + u16 addr = base_addr; + u32 dsp_val; + u16 bin_val; + int ret, i; + + ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_cur, &set_prof_desc); + if (ret) + return ret; + + /* update reg */ + dsp_fw_desc = &set_prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW]; + + for (i = 0; i < AW88395_FW_CHECK_PART; i++) { + ret = aw_dev_dsp_read(aw_dev, addr, &dsp_val, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "dsp read failed"); + return ret; + } + + bin_val = be16_to_cpup((void *)&dsp_fw_desc->data[2 * (addr - base_addr)]); + + if (dsp_val != bin_val) { + dev_err(aw_dev->dev, "fw check failed, addr[0x%x], read[0x%x] != bindata[0x%x]", + addr, dsp_val, bin_val); + return -EINVAL; + } + + addr += (dsp_fw_desc->len / 2) / AW88395_FW_CHECK_PART; + if ((addr - base_addr) > dsp_fw_desc->len) { + dev_err(aw_dev->dev, "fw check failed, addr[0x%x] too large", addr); + return -EINVAL; + } + } + + return 0; +} + +static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int reg_value; + u16 real_value, volume; + int ret; + + volume = min((value + vol_desc->init_volume), (unsigned int)AW88395_MUTE_VOL); + real_value = db_to_reg_val(volume); + + /* cal real value */ + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL2_REG, ®_value); + if (ret) + return ret; + + dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value); + + /* [15 : 6] volume */ + real_value = (real_value << AW88395_VOL_START_BIT) | (reg_value & AW88395_VOL_MASK); + + /* write value */ + ret = regmap_write(aw_dev->regmap, AW88395_SYSCTRL2_REG, real_value); + + return ret; +} + +void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol) +{ + int ret; + + ret = aw_dev_set_volume(aw_dev, set_vol); + if (ret) + dev_dbg(aw_dev->dev, "set volume failed"); +} +EXPORT_SYMBOL_GPL(aw88395_dev_set_volume); + +static void aw_dev_fade_in(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + u16 fade_in_vol = desc->ctl_volume; + int fade_step = aw_dev->fade_step; + int i; + + if (!aw_dev->fade_en) + return; + + if (fade_step == 0 || aw_dev->fade_in_time == 0) { + aw_dev_set_volume(aw_dev, fade_in_vol); + return; + } + + for (i = AW88395_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10); + } + + if (i != fade_in_vol) + aw_dev_set_volume(aw_dev, fade_in_vol); +} + +static void aw_dev_fade_out(struct aw_device *aw_dev) +{ + struct aw_volume_desc *desc = &aw_dev->volume_desc; + int fade_step = aw_dev->fade_step; + int i; + + if (!aw_dev->fade_en) + return; + + if (fade_step == 0 || aw_dev->fade_out_time == 0) { + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + return; + } + + for (i = desc->ctl_volume; i <= AW88395_MUTE_VOL; i += fade_step) { + aw_dev_set_volume(aw_dev, i); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } + + if (i != AW88395_MUTE_VOL) { + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); + } +} + +static int aw_dev_modify_dsp_cfg(struct aw_device *aw_dev, + unsigned int addr, unsigned int dsp_data, unsigned char data_type) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + unsigned int addr_offset; + __le16 data1; + __le32 data2; + + dev_dbg(aw_dev->dev, "addr:0x%x, dsp_data:0x%x", addr, dsp_data); + + addr_offset = (addr - AW88395_DSP_CFG_ADDR) * 2; + if (addr_offset > crc_dsp_cfg->len) { + dev_err(aw_dev->dev, "addr_offset[%d] > crc_dsp_cfg->len[%d]", + addr_offset, crc_dsp_cfg->len); + return -EINVAL; + } + switch (data_type) { + case AW88395_DSP_16_DATA: + data1 = cpu_to_le16((u16)dsp_data); + memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data1, 2); + break; + case AW88395_DSP_32_DATA: + data2 = cpu_to_le32(dsp_data); + memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data2, 4); + break; + default: + dev_err(aw_dev->dev, "data type[%d] unsupported", data_type); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_dsp_set_cali_re(struct aw_device *aw_dev) +{ + u32 cali_re; + int ret; + + cali_re = AW88395_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re + + aw_dev->cali_desc.ra), AW88395_DSP_RE_SHIFT); + + /* set cali re to device */ + ret = aw_dev_dsp_write(aw_dev, + AW88395_DSP_REG_CFG_ADPZ_RE, cali_re, AW88395_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "set cali re error"); + return ret; + } + + ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RE, + cali_re, AW88395_DSP_32_DATA); + if (ret) + dev_err(aw_dev->dev, "modify dsp cfg failed"); + + return ret; +} + +static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG, + ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG, + ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_DISABLE_VALUE); + } + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_dsp_set_crc32(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + u32 crc_value, crc_data_len; + + /* get crc data len */ + crc_data_len = (AW88395_DSP_REG_CRC_ADDR - AW88395_DSP_CFG_ADDR) * 2; + if (crc_data_len > crc_dsp_cfg->len) { + dev_err(aw_dev->dev, "crc data len :%d > cfg_data len:%d", + crc_data_len, crc_dsp_cfg->len); + return -EINVAL; + } + + if (crc_data_len & 0x11) { + dev_err(aw_dev->dev, "The crc data len :%d unsupport", crc_data_len); + return -EINVAL; + } + + crc_value = __crc32c_le(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ 0xFFFFFFFF; + + return aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_CRC_ADDR, crc_value, + AW88395_DSP_32_DATA); +} + +static void aw_dev_dsp_check_crc_enable(struct aw_device *aw_dev, bool flag) +{ + int ret; + + if (flag) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG, + ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG, + ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_DISABLE_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static int aw_dev_dsp_check_st(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + int i; + + for (i = 0; i < AW88395_DSP_ST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) { + dev_err(aw_dev->dev, "read reg0x%x failed", AW88395_SYSST_REG); + continue; + } + + if ((reg_val & (~AW88395_DSPS_MASK)) != AW88395_DSPS_NORMAL_VALUE) { + dev_err(aw_dev->dev, "check dsp st fail,reg_val:0x%04x", reg_val); + ret = -EPERM; + continue; + } else { + dev_dbg(aw_dev->dev, "dsp st check ok, reg_val:0x%04x", reg_val); + return 0; + } + } + + return ret; +} + +static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable) +{ + int ret; + + if (is_enable) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_DSPBY_MASK, AW88395_DSPBY_WORKING_VALUE); + if (ret) + dev_dbg(aw_dev->dev, "enable dsp failed"); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_DSPBY_MASK, AW88395_DSPBY_BYPASS_VALUE); + if (ret) + dev_dbg(aw_dev->dev, "disable dsp failed"); + } +} + +static int aw_dev_dsp_check_crc32(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_BYPASS) { + dev_info(aw_dev->dev, "dsp bypass"); + return 0; + } + + ret = aw_dev_dsp_set_crc32(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "set dsp crc32 failed"); + return ret; + } + + aw_dev_dsp_check_crc_enable(aw_dev, true); + + /* dsp enable */ + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88395_5000_US, AW88395_5000_US + 100); + + ret = aw_dev_dsp_check_st(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check crc32 fail"); + } else { + aw_dev_dsp_check_crc_enable(aw_dev, false); + aw_dev->dsp_crc_st = AW88395_DSP_CRC_OK; + } + + return ret; +} + +static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd) +{ + int ret; + + if (pwd) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_PWDN_MASK, AW88395_PWDN_POWER_DOWN_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_PWDN_MASK, AW88395_PWDN_WORKING_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd) +{ + int ret; + + if (amppd) { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_AMPPD_MASK, AW88395_AMPPD_POWER_DOWN_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_AMPPD_MASK, AW88395_AMPPD_WORKING_VALUE); + } + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} + +void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute) +{ + int ret; + + if (is_mute) { + aw_dev_fade_out(aw_dev); + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_HMUTE_MASK, AW88395_HMUTE_ENABLE_VALUE); + } else { + ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG, + ~AW88395_HMUTE_MASK, AW88395_HMUTE_DISABLE_VALUE); + aw_dev_fade_in(aw_dev); + } + + if (ret) + dev_dbg(aw_dev->dev, "%s failed", __func__); +} +EXPORT_SYMBOL_GPL(aw88395_dev_mute); + +static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk) +{ + unsigned int reg_val; + u16 reg_icalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, ®_val); + if (ret) + return ret; + + reg_icalk = reg_val & (~AW88395_EF_ISN_GESLP_MASK); + + if (reg_icalk & (~AW88395_EF_ISN_GESLP_SIGN_MASK)) + reg_icalk = reg_icalk | AW88395_EF_ISN_GESLP_SIGN_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk) +{ + unsigned int reg_val; + u16 reg_vcalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRH_REG, ®_val); + if (ret) + return ret; + + reg_val = reg_val >> AW88395_EF_VSENSE_GAIN_SHIFT; + + reg_vcalk = (u16)reg_val & (~AW88395_EF_VSN_GESLP_MASK); + + if (reg_vcalk & (~AW88395_EF_VSN_GESLP_SIGN_MASK)) + reg_vcalk = reg_vcalk | AW88395_EF_VSN_GESLP_SIGN_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_get_vcalk_dac(struct aw_device *aw_dev, int16_t *vcalk) +{ + unsigned int reg_val; + u16 reg_vcalk; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, ®_val); + if (ret) + return ret; + + reg_vcalk = reg_val >> AW88395_EF_DAC_GESLP_SHIFT; + + if (reg_vcalk & AW88395_EF_DAC_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | AW88395_EF_DAC_GESLP_SIGN_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw_dev_vsense_select(struct aw_device *aw_dev, int *vsense_select) +{ + unsigned int vsense_reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_I2SCFG3_REG, &vsense_reg_val); + if (ret) { + dev_err(aw_dev->dev, "read vsense_reg_val failed"); + return ret; + } + dev_dbg(aw_dev->dev, "vsense_reg = 0x%x", vsense_reg_val); + + if (vsense_reg_val & (~AW88395_VDSEL_MASK)) { + *vsense_select = AW88395_DEV_VDSEL_VSENSE; + dev_dbg(aw_dev->dev, "vsense outside"); + } else { + *vsense_select = AW88395_DEV_VDSEL_DAC; + dev_dbg(aw_dev->dev, "vsense inside"); + } + + return 0; +} + +static int aw_dev_set_vcalb(struct aw_device *aw_dev) +{ + int16_t icalk_val, vcalk_val; + int icalk, vsense_select; + u32 vcalb_adj, reg_val; + int vcalb, vcalk; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VCALB, &vcalb_adj, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "read vcalb_adj failed"); + return ret; + } + + ret = aw_dev_vsense_select(aw_dev, &vsense_select); + if (ret) + return ret; + dev_dbg(aw_dev->dev, "vsense_select = %d", vsense_select); + + ret = aw_dev_get_icalk(aw_dev, &icalk_val); + if (ret) + return ret; + icalk = AW88395_CABL_BASE_VALUE + AW88395_ICABLK_FACTOR * icalk_val; + + switch (vsense_select) { + case AW88395_DEV_VDSEL_VSENSE: + ret = aw_dev_get_vcalk(aw_dev, &vcalk_val); + if (ret) + return ret; + vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR * vcalk_val; + vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR / + AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_factor=%d, vscal_factor=%d, icalk=%d, vcalk=%d", + AW88395_VCABLK_FACTOR, AW88395_VSCAL_FACTOR, icalk, vcalk); + break; + case AW88395_DEV_VDSEL_DAC: + ret = aw_dev_get_vcalk_dac(aw_dev, &vcalk_val); + if (ret) + return ret; + vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR_DAC * vcalk_val; + vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR_DAC / + AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj; + + dev_dbg(aw_dev->dev, "vcalk_dac_factor=%d, vscal_dac_factor=%d, icalk=%d, vcalk=%d", + AW88395_VCABLK_FACTOR_DAC, + AW88395_VSCAL_FACTOR_DAC, icalk, vcalk); + break; + default: + dev_err(aw_dev->dev, "unsupport vsense status"); + return -EINVAL; + } + + if ((vcalk == 0) || (AW88395_ISCAL_FACTOR == 0)) { + dev_err(aw_dev->dev, "vcalk:%d or desc->iscal_factor:%d unsupported", + vcalk, AW88395_ISCAL_FACTOR); + return -EINVAL; + } + + vcalb = vcalb >> AW88395_VCALB_ADJ_FACTOR; + reg_val = (u32)vcalb; + + dev_dbg(aw_dev->dev, "vcalb=%d, reg_val=0x%x, vcalb_adj =0x%x", + vcalb, reg_val, vcalb_adj); + + ret = aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_VCALB, reg_val, AW88395_DSP_16_DATA); + if (ret) { + dev_err(aw_dev->dev, "write vcalb failed"); + return ret; + } + + ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_VCALB, + (u32)reg_val, AW88395_DSP_16_DATA); + if (ret) + dev_err(aw_dev->dev, "modify dsp cfg failed"); + + return ret; +} + +static int aw_dev_get_cali_f0_delay(struct aw_device *aw_dev) +{ + struct aw_cali_delay_desc *desc = &aw_dev->cali_delay_desc; + u32 cali_delay; + int ret; + + ret = aw_dev_dsp_read(aw_dev, + AW88395_DSP_CALI_F0_DELAY, &cali_delay, AW88395_DSP_16_DATA); + if (ret) + dev_err(aw_dev->dev, "read cali delay failed, ret=%d", ret); + else + desc->delay = AW88395_CALI_DELAY_CACL(cali_delay); + + dev_dbg(aw_dev->dev, "read cali delay: %d ms", desc->delay); + + return ret; +} + +static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSINT_REG, ®_val); + if (ret) + dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret); + else + *int_status = reg_val; + + dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status); +} + +static void aw_dev_clear_int_status(struct aw_device *aw_dev) +{ + u16 int_status; + + /* read int status and clear */ + aw_dev_get_int_status(aw_dev, &int_status); + /* make sure int status is clear */ + aw_dev_get_int_status(aw_dev, &int_status); + if (int_status) + dev_info(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status); +} + +static int aw_dev_get_iis_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) + return -EIO; + if ((reg_val & AW88395_BIT_PLL_CHECK) != AW88395_BIT_PLL_CHECK) { + dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_check_mode1_pll(struct aw_device *aw_dev) +{ + int ret, i; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode1 iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_mode2_pll(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret, i; + + ret = regmap_read(aw_dev->regmap, AW88395_PLLCTRL1_REG, ®_val); + if (ret) + return ret; + + reg_val &= (~AW88395_CCO_MUX_MASK); + if (reg_val == AW88395_CCO_MUX_DIVIDED_VALUE) { + dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); + return -EPERM; + } + + /* change mode2 */ + ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG, + ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_DIVIDED_VALUE); + if (ret) + return ret; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_get_iis_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + break; + } + } + + /* change mode1 */ + ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG, + ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_BYPASS_VALUE); + if (ret == 0) { + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + break; + } + } + } + + return ret; +} + +static int aw_dev_check_syspll(struct aw_device *aw_dev) +{ + int ret; + + ret = aw_dev_check_mode1_pll(aw_dev); + if (ret) { + dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); + ret = aw_dev_check_mode2_pll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "mode2 check iis failed"); + return ret; + } + } + + return ret; +} + +static int aw_dev_check_sysst(struct aw_device *aw_dev) +{ + unsigned int check_val; + unsigned int reg_val; + int ret, i; + + for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) { + ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, ®_val); + if (ret) + return ret; + + check_val = reg_val & (~AW88395_BIT_SYSST_CHECK_MASK) + & AW88395_BIT_SYSST_CHECK; + if (check_val != AW88395_BIT_SYSST_CHECK) { + dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x", + i, reg_val, AW88395_BIT_SYSST_CHECK); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } else { + return 0; + } + } + + return -EPERM; +} + +static int aw_dev_check_sysint(struct aw_device *aw_dev) +{ + u16 reg_val; + + aw_dev_get_int_status(aw_dev, ®_val); + + if (reg_val & AW88395_BIT_SYSINT_CHECK) { + dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val); + return -EINVAL; + } + + return 0; +} + +static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev) +{ + struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc; + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if ((reg_val & (~AW88395_RCV_MODE_MASK)) == AW88395_RCV_MODE_RECEIVER_VALUE) + profctrl_desc->cur_mode = AW88395_RCV_MODE; + else + profctrl_desc->cur_mode = AW88395_NOT_RCV_MODE; +} + +static void aw_dev_get_dsp_config(struct aw_device *aw_dev, unsigned char *dsp_cfg) +{ + unsigned int reg_val = 0; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, ®_val); + if (ret) { + dev_dbg(aw_dev->dev, "%s failed", __func__); + return; + } + if (reg_val & (~AW88395_DSPBY_MASK)) + *dsp_cfg = AW88395_DEV_DSP_BYPASS; + else + *dsp_cfg = AW88395_DEV_DSP_WORK; +} + +static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag) +{ + int ret; + + switch (flag) { + case AW88395_DEV_MEMCLK_PLL: + ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG, + ~AW88395_MEM_CLKSEL_MASK, + AW88395_MEM_CLKSEL_DAP_HCLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select pll failed"); + break; + case AW88395_DEV_MEMCLK_OSC: + ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG, + ~AW88395_MEM_CLKSEL_MASK, + AW88395_MEM_CLKSEL_OSC_CLK_VALUE); + if (ret) + dev_err(aw_dev->dev, "memclk select OSC failed"); + break; + default: + dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag); + break; + } +} + +static int aw_dev_get_dsp_status(struct aw_device *aw_dev) +{ + unsigned int reg_val; + int ret; + + ret = regmap_read(aw_dev->regmap, AW88395_WDT_REG, ®_val); + if (ret) + return ret; + if (!(reg_val & (~AW88395_WDT_CNT_MASK))) + ret = -EPERM; + + return ret; +} + +static int aw_dev_get_vmax(struct aw_device *aw_dev, unsigned int *vmax) +{ + return aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VMAX, vmax, AW88395_DSP_16_DATA); +} + +static int aw_dev_update_reg_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; + unsigned int read_val; + int16_t *reg_data; + int data_len; + u16 read_vol; + u16 reg_val; + u8 reg_addr; + int i, ret; + + reg_data = (int16_t *)data; + data_len = len >> 1; + + if (data_len & 0x1) { + dev_err(aw_dev->dev, "data len:%d unsupported", data_len); + return -EINVAL; + } + + for (i = 0; i < data_len; i += 2) { + reg_addr = reg_data[i]; + reg_val = reg_data[i + 1]; + + if (reg_addr == AW88395_SYSCTRL_REG) { + ret = regmap_read(aw_dev->regmap, reg_addr, &read_val); + if (ret) + break; + read_val &= (~AW88395_HMUTE_MASK); + reg_val &= AW88395_HMUTE_MASK; + reg_val |= read_val; + } + if (reg_addr == AW88395_HAGCCFG7_REG) + reg_val &= AW88395_AGC_DSP_CTL_MASK; + + if (reg_addr == AW88395_I2SCFG1_REG) { + /* close tx */ + reg_val &= AW88395_I2STXEN_MASK; + reg_val |= AW88395_I2STXEN_DISABLE_VALUE; + } + + if (reg_addr == AW88395_SYSCTRL2_REG) { + read_vol = (reg_val & (~AW88395_VOL_MASK)) >> + AW88395_VOL_START_BIT; + aw_dev->volume_desc.init_volume = + reg_val_to_db(read_vol); + } + ret = regmap_write(aw_dev->regmap, reg_addr, reg_val); + if (ret) + break; + + } + + aw_dev_get_cur_mode_st(aw_dev); + + if (aw_dev->prof_cur != aw_dev->prof_index) { + /* clear control volume when PA change profile */ + vol_desc->ctl_volume = 0; + } else { + /* keep control volume when PA start with sync mode */ + aw_dev_set_volume(aw_dev, vol_desc->ctl_volume); + } + + /* keep min volume */ + if (aw_dev->fade_en) + aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL); + + aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg); + + return ret; +} + +static int aw_dev_reg_update(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + if (!len || !data) { + dev_err(aw_dev->dev, "reg data is null or len is 0"); + return -EINVAL; + } + + ret = aw_dev_update_reg_container(aw_dev, data, len); + if (ret) { + dev_err(aw_dev->dev, "reg update failed"); + return ret; + } + + return 0; +} + +static int aw_dev_get_ra(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + u32 dsp_ra; + int ret; + + ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RA, + &dsp_ra, AW88395_DSP_32_DATA); + if (ret) { + dev_err(aw_dev->dev, "read ra error"); + return ret; + } + + cali_desc->ra = AW88395_DSP_RE_TO_SHOW_RE(dsp_ra, + AW88395_DSP_RE_SHIFT); + + return ret; +} + +static int aw_dev_dsp_update_container(struct aw_device *aw_dev, + unsigned char *data, unsigned int len, unsigned short base) +{ + int i, ret; + +#ifdef AW88395_DSP_I2C_WRITES + u32 tmp_len; + + mutex_lock(&aw_dev->dsp_lock); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) + goto error_operation; + + for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) { + if ((len - i) < AW88395_MAX_RAM_WRITE_BYTE_SIZE) + tmp_len = len - i; + else + tmp_len = AW88395_MAX_RAM_WRITE_BYTE_SIZE; + + ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + &data[i], tmp_len); + if (ret) + goto error_operation; + } + mutex_unlock(&aw_dev->dsp_lock); +#else + __be16 reg_val; + + mutex_lock(&aw_dev->dsp_lock); + /* i2c write */ + ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base); + if (ret) + goto error_operation; + for (i = 0; i < len; i += 2) { + reg_val = cpu_to_be16p((u16 *)(data + i)); + ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, + (u16)reg_val); + if (ret) + goto error_operation; + } + mutex_unlock(&aw_dev->dsp_lock); +#endif + + return 0; + +error_operation: + mutex_unlock(&aw_dev->dsp_lock); + return ret; +} + +static int aw_dev_dsp_update_fw(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + + dev_dbg(aw_dev->dev, "dsp firmware len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp firmware data is null or len is 0"); + return -EINVAL; + } + aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_FW_ADDR); + aw_dev->dsp_fw_len = len; + + return 0; +} + +static int aw_dev_copy_to_crc_dsp_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int size) +{ + struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg; + + if (!crc_dsp_cfg->data) { + crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) + return -ENOMEM; + crc_dsp_cfg->len = size; + } else if (crc_dsp_cfg->len < size) { + devm_kfree(aw_dev->dev, crc_dsp_cfg->data); + crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL); + if (!crc_dsp_cfg->data) + return -ENOMEM; + crc_dsp_cfg->len = size; + } + memcpy(crc_dsp_cfg->data, data, size); + swab16_array((u16 *)crc_dsp_cfg->data, size >> 1); + + return 0; +} + +static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev, + unsigned char *data, unsigned int len) +{ + int ret; + + dev_dbg(aw_dev->dev, "dsp config len:%d", len); + + if (!len || !data) { + dev_err(aw_dev->dev, "dsp config data is null or len is 0"); + return -EINVAL; + } + + aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_CFG_ADDR); + aw_dev->dsp_cfg_len = len; + + ret = aw_dev_copy_to_crc_dsp_cfg(aw_dev, data, len); + if (ret) + return ret; + + ret = aw_dev_set_vcalb(aw_dev); + if (ret) + return ret; + ret = aw_dev_get_ra(&aw_dev->cali_desc); + if (ret) + return ret; + ret = aw_dev_get_cali_f0_delay(aw_dev); + if (ret) + return ret; + + ret = aw_dev_get_vmax(aw_dev, &aw_dev->vmax_desc.init_vmax); + if (ret) { + dev_err(aw_dev->dev, "get vmax failed"); + return ret; + } + dev_dbg(aw_dev->dev, "get init vmax:0x%x", aw_dev->vmax_desc.init_vmax); + aw_dev->dsp_crc_st = AW88395_DSP_CRC_NA; + + return 0; +} + +static int aw_dev_check_sram(struct aw_device *aw_dev) +{ + unsigned int reg_val; + + mutex_lock(&aw_dev->dsp_lock); + /* check the odd bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST); + regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); + goto error; + } + + /* check the even bits of reg 0x40 */ + regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_EVEN_NUM_BIT_TEST); + regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, ®_val); + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /* check dsp_fw_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_FW_ADDR, AW88395_DSP_EVEN_NUM_BIT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_FW_ADDR, ®_val); + if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST); + goto error; + } + + /* check dsp_cfg_base_addr */ + aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_CFG_ADDR, AW88395_DSP_ODD_NUM_BIT_TEST); + aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_CFG_ADDR, ®_val); + if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) { + dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]", + reg_val, AW88395_DSP_ODD_NUM_BIT_TEST); + goto error; + } + mutex_unlock(&aw_dev->dsp_lock); + + return 0; + +error: + mutex_unlock(&aw_dev->dsp_lock); + return -EPERM; +} + +int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en) +{ + struct aw_prof_desc *prof_index_desc; + struct aw_sec_data_desc *sec_desc; + char *prof_name; + int ret; + + if ((aw_dev->prof_cur == aw_dev->prof_index) && + (force_up_en == AW88395_FORCE_UPDATE_OFF)) { + dev_dbg(aw_dev->dev, "scene no change, not update"); + return 0; + } + + if (aw_dev->fw_status == AW88395_DEV_FW_FAILED) { + dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status); + return -EPERM; + } + + prof_name = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index); + + dev_dbg(aw_dev->dev, "start update %s", prof_name); + + ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc); + if (ret) + return ret; + + /* update reg */ + sec_desc = prof_index_desc->sec_desc; + ret = aw_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].data, + sec_desc[AW88395_DATA_TYPE_REG].len); + if (ret) { + dev_err(aw_dev->dev, "update reg failed"); + return ret; + } + + aw88395_dev_mute(aw_dev, true); + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) + aw_dev_dsp_enable(aw_dev, false); + + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC); + + if (up_dsp_fw_en) { + ret = aw_dev_check_sram(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "check sram failed"); + goto error; + } + + /* update dsp firmware */ + dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver); + ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data, + sec_desc[AW88395_DATA_TYPE_DSP_FW].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp fw failed"); + goto error; + } + } + + /* update dsp config */ + ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data, + sec_desc[AW88395_DATA_TYPE_DSP_CFG].len); + if (ret) { + dev_err(aw_dev->dev, "update dsp cfg failed"); + goto error; + } + + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + + aw_dev->prof_cur = aw_dev->prof_index; + + return 0; + +error: + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_dev_fw_update); + +static int aw_dev_dsp_check(struct aw_device *aw_dev) +{ + int ret, i; + + switch (aw_dev->dsp_cfg) { + case AW88395_DEV_DSP_BYPASS: + dev_dbg(aw_dev->dev, "dsp bypass"); + ret = 0; + break; + case AW88395_DEV_DSP_WORK: + aw_dev_dsp_enable(aw_dev, false); + aw_dev_dsp_enable(aw_dev, true); + usleep_range(AW88395_1000_US, AW88395_1000_US + 10); + for (i = 0; i < AW88395_DEV_DSP_CHECK_MAX; i++) { + ret = aw_dev_get_dsp_status(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp wdt status error=%d", ret); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + } + } + break; + default: + dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg); + ret = -EINVAL; + break; + } + + return ret; +} + +static void aw_dev_update_cali_re(struct aw_cali_desc *cali_desc) +{ + struct aw_device *aw_dev = + container_of(cali_desc, struct aw_device, cali_desc); + int ret; + + if ((aw_dev->cali_desc.cali_re < AW88395_CALI_RE_MAX) && + (aw_dev->cali_desc.cali_re > AW88395_CALI_RE_MIN)) { + + ret = aw_dev_dsp_set_cali_re(aw_dev); + if (ret) + dev_err(aw_dev->dev, "set cali re failed"); + } +} + +int aw88395_dev_start(struct aw_device *aw_dev) +{ + int ret; + + if (aw_dev->status == AW88395_DEV_PW_ON) { + dev_info(aw_dev->dev, "already power on"); + return 0; + } + /* power on */ + aw_dev_pwd(aw_dev, false); + usleep_range(AW88395_2000_US, AW88395_2000_US + 10); + + ret = aw_dev_check_syspll(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "pll check failed cannot start"); + goto pll_check_fail; + } + + /* amppd on */ + aw_dev_amppd(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 50); + + /* check i2s status */ + ret = aw_dev_check_sysst(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "sysst check failed"); + goto sysst_check_fail; + } + + if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) { + /* dsp bypass */ + aw_dev_dsp_enable(aw_dev, false); + ret = aw_dev_dsp_fw_check(aw_dev); + if (ret) + goto dev_dsp_fw_check_fail; + + aw_dev_update_cali_re(&aw_dev->cali_desc); + + if (aw_dev->dsp_crc_st != AW88395_DSP_CRC_OK) { + ret = aw_dev_dsp_check_crc32(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp crc check failed"); + goto crc_check_fail; + } + } + + ret = aw_dev_dsp_check(aw_dev); + if (ret) { + dev_err(aw_dev->dev, "dsp status check failed"); + goto dsp_check_fail; + } + } else { + dev_dbg(aw_dev->dev, "start pa with dsp bypass"); + } + + /* enable tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, true); + + /* close mute */ + aw88395_dev_mute(aw_dev, false); + /* clear inturrupt */ + aw_dev_clear_int_status(aw_dev); + aw_dev->status = AW88395_DEV_PW_ON; + + return 0; + +dsp_check_fail: +crc_check_fail: + aw_dev_dsp_enable(aw_dev, false); +dev_dsp_fw_check_fail: +sysst_check_fail: + aw_dev_clear_int_status(aw_dev); + aw_dev_amppd(aw_dev, true); +pll_check_fail: + aw_dev_pwd(aw_dev, true); + aw_dev->status = AW88395_DEV_PW_OFF; + + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_dev_start); + +int aw88395_dev_stop(struct aw_device *aw_dev) +{ + struct aw_sec_data_desc *dsp_cfg = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG]; + struct aw_sec_data_desc *dsp_fw = + &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW]; + int int_st = 0; + int ret; + + if (aw_dev->status == AW88395_DEV_PW_OFF) { + dev_info(aw_dev->dev, "already power off"); + return 0; + } + + aw_dev->status = AW88395_DEV_PW_OFF; + + /* set mute */ + aw88395_dev_mute(aw_dev, true); + usleep_range(AW88395_4000_US, AW88395_4000_US + 100); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 100); + + /* check sysint state */ + int_st = aw_dev_check_sysint(aw_dev); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + + if (int_st < 0) { + /* system status anomaly */ + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC); + ret = aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len); + if (ret) + dev_err(aw_dev->dev, "update dsp fw failed"); + ret = aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len); + if (ret) + dev_err(aw_dev->dev, "update dsp cfg failed"); + aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL); + } + + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_stop); + +int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + int ret; + + if ((!aw_dev) || (!aw_cfg)) { + pr_err("aw_dev is NULL or aw_cfg is NULL"); + return -ENOMEM; + } + ret = aw88395_dev_cfg_load(aw_dev, aw_cfg); + if (ret) { + dev_err(aw_dev->dev, "aw_dev acf parse failed"); + return -EINVAL; + } + aw_dev->fade_in_time = AW88395_1000_US / 10; + aw_dev->fade_out_time = AW88395_1000_US >> 1; + aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id; + aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id; + + ret = aw88395_dev_fw_update(aw_dev, AW88395_FORCE_UPDATE_ON, AW88395_DSP_FW_UPDATE_ON); + if (ret) { + dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret); + return ret; + } + + /* set mute */ + aw88395_dev_mute(aw_dev, true); + usleep_range(AW88395_4000_US, AW88395_4000_US + 100); + + /* close tx feedback */ + aw_dev_i2s_tx_enable(aw_dev, false); + usleep_range(AW88395_1000_US, AW88395_1000_US + 100); + + /* close dsp */ + aw_dev_dsp_enable(aw_dev, false); + /* enable amppd */ + aw_dev_amppd(aw_dev, true); + /* set power down */ + aw_dev_pwd(aw_dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_init); + +static void aw88395_parse_channel_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 channel_value; + int ret; + + ret = of_property_read_u32(np, "sound-channel", &channel_value); + if (ret) { + dev_dbg(aw_dev->dev, + "read sound-channel failed,use default 0"); + aw_dev->channel = AW88395_DEV_DEFAULT_CH; + return; + } + + dev_dbg(aw_dev->dev, "read sound-channel value is: %d", + channel_value); + aw_dev->channel = channel_value; +} + +static void aw88395_parse_fade_enable_dt(struct aw_device *aw_dev) +{ + struct device_node *np = aw_dev->dev->of_node; + u32 fade_en; + int ret; + + ret = of_property_read_u32(np, "fade-enable", &fade_en); + if (ret) { + dev_dbg(aw_dev->dev, + "read fade-enable failed, close fade_in_out"); + fade_en = AW88395_FADE_IN_OUT_DEFAULT; + } + + dev_dbg(aw_dev->dev, "read fade-enable value is: %d", fade_en); + + aw_dev->fade_en = fade_en; +} + +static int aw_dev_init(struct aw_device *aw_dev) +{ + aw_dev->chip_id = AW88395_CHIP_ID; + /* call aw device init func */ + aw_dev->acf = NULL; + aw_dev->prof_info.prof_desc = NULL; + aw_dev->prof_info.count = 0; + aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; + aw_dev->channel = 0; + aw_dev->fw_status = AW88395_DEV_FW_FAILED; + + aw_dev->fade_step = AW88395_VOLUME_STEP_DB; + aw_dev->volume_desc.ctl_volume = AW88395_VOL_DEFAULT_VALUE; + aw88395_parse_channel_dt(aw_dev); + aw88395_parse_fade_enable_dt(aw_dev); + + return 0; +} + +int aw88395_dev_get_profile_count(struct aw_device *aw_dev) +{ + return aw_dev->prof_info.count; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_count); + +int aw88395_dev_get_profile_index(struct aw_device *aw_dev) +{ + return aw_dev->prof_index; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_index); + +int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index) +{ + /* check the index whether is valid */ + if ((index >= aw_dev->prof_info.count) || (index < 0)) + return -EINVAL; + /* check the index whether change */ + if (aw_dev->prof_index == index) + return -EINVAL; + + aw_dev->prof_index = index; + dev_dbg(aw_dev->dev, "set prof[%s]", + aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]); + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_set_profile_index); + +char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc; + + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "index[%d] overflow count[%d]", + index, aw_dev->prof_info.count); + return NULL; + } + + prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return prof_info->prof_name_list[prof_desc->id]; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_name); + +int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc) +{ + if ((index >= aw_dev->prof_info.count) || (index < 0)) { + dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n", + __func__, index, aw_dev->prof_info.count); + return -EINVAL; + } + + *prof_desc = &aw_dev->prof_info.prof_desc[index]; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_data); + +int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap) +{ + u16 chip_id; + int ret; + + if (*aw_dev) { + dev_info(&i2c->dev, "it should be initialized here.\n"); + } else { + *aw_dev = devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL); + if (!(*aw_dev)) + return -ENOMEM; + } + + (*aw_dev)->i2c = i2c; + (*aw_dev)->dev = &i2c->dev; + (*aw_dev)->regmap = regmap; + mutex_init(&(*aw_dev)->dsp_lock); + + /* read chip id */ + ret = aw_dev_read_chipid((*aw_dev), &chip_id); + if (ret) { + dev_err(&i2c->dev, "dev_read_chipid failed ret=%d", ret); + return ret; + } + + switch (chip_id) { + case AW88395_CHIP_ID: + ret = aw_dev_init((*aw_dev)); + break; + default: + ret = -EINVAL; + dev_err((*aw_dev)->dev, "unsupported device"); + break; + } + + return ret; +} +EXPORT_SYMBOL_GPL(aw88395_init); + +MODULE_DESCRIPTION("AW88395 device lib"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h new file mode 100644 index 000000000000..caf730753167 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_device.h @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_device.h -- AW88395 function for ALSA Audio Driver +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <[email protected]> +// + +#ifndef __AW88395_DEVICE_FILE_H__ +#define __AW88395_DEVICE_FILE_H__ + +#include "aw88395.h" +#include "aw88395_data_type.h" +#include "aw88395_lib.h" + +#define AW88395_DEV_DEFAULT_CH (0) +#define AW88395_DEV_DSP_CHECK_MAX (5) +#define AW88395_DSP_I2C_WRITES +#define AW88395_MAX_RAM_WRITE_BYTE_SIZE (128) +#define AW88395_DSP_ODD_NUM_BIT_TEST (0x5555) +#define AW88395_DSP_EVEN_NUM_BIT_TEST (0xAAAA) +#define AW88395_DSP_ST_CHECK_MAX (2) +#define AW88395_FADE_IN_OUT_DEFAULT (0) +#define AW88395_CALI_RE_MAX (15000) +#define AW88395_CALI_RE_MIN (4000) +#define AW88395_CALI_DELAY_CACL(value) ((value * 32) / 48) + +#define AW88395_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift)) +#define AW88395_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000)) + +#define AW88395_ACF_FILE "aw88395_acf.bin" +#define AW88395_DEV_SYSST_CHECK_MAX (10) + +enum { + AW88395_DEV_VDSEL_DAC = 0, + AW88395_DEV_VDSEL_VSENSE = 1, +}; + +enum { + AW88395_DSP_CRC_NA = 0, + AW88395_DSP_CRC_OK = 1, +}; + +enum { + AW88395_DSP_FW_UPDATE_OFF = 0, + AW88395_DSP_FW_UPDATE_ON = 1, +}; + +enum { + AW88395_FORCE_UPDATE_OFF = 0, + AW88395_FORCE_UPDATE_ON = 1, +}; + +enum { + AW88395_1000_US = 1000, + AW88395_2000_US = 2000, + AW88395_3000_US = 3000, + AW88395_4000_US = 4000, + AW88395_5000_US = 5000, + AW88395_10000_US = 10000, + AW88395_100000_US = 100000, +}; + +enum { + AW88395_DEV_TYPE_OK = 0, + AW88395_DEV_TYPE_NONE = 1, +}; + + +enum AW88395_DEV_STATUS { + AW88395_DEV_PW_OFF = 0, + AW88395_DEV_PW_ON, +}; + +enum AW88395_DEV_FW_STATUS { + AW88395_DEV_FW_FAILED = 0, + AW88395_DEV_FW_OK, +}; + +enum AW88395_DEV_MEMCLK { + AW88395_DEV_MEMCLK_OSC = 0, + AW88395_DEV_MEMCLK_PLL = 1, +}; + +enum AW88395_DEV_DSP_CFG { + AW88395_DEV_DSP_WORK = 0, + AW88395_DEV_DSP_BYPASS = 1, +}; + +enum { + AW88395_DSP_16_DATA = 0, + AW88395_DSP_32_DATA = 1, +}; + +enum { + AW88395_NOT_RCV_MODE = 0, + AW88395_RCV_MODE = 1, +}; + +struct aw_profctrl_desc { + unsigned int cur_mode; +}; + +struct aw_volume_desc { + unsigned int init_volume; + unsigned int mute_volume; + unsigned int ctl_volume; + unsigned int max_volume; +}; + +struct aw_dsp_mem_desc { + unsigned int dsp_madd_reg; + unsigned int dsp_mdat_reg; + unsigned int dsp_fw_base_addr; + unsigned int dsp_cfg_base_addr; +}; + +struct aw_vmax_desc { + unsigned int init_vmax; +}; + +struct aw_cali_delay_desc { + unsigned int delay; +}; + +struct aw_cali_desc { + u32 cali_re; + u32 ra; +}; + +struct aw_container { + int len; + u8 data[]; +}; + +struct aw_device { + int status; + struct mutex dsp_lock; + + unsigned char prof_cur; + unsigned char prof_index; + unsigned char dsp_crc_st; + u16 chip_id; + + unsigned int channel; + unsigned int fade_step; + + struct i2c_client *i2c; + struct device *dev; + struct regmap *regmap; + char *acf; + + u32 fade_en; + unsigned char dsp_cfg; + + u32 dsp_fw_len; + u32 dsp_cfg_len; + u8 platform; + u8 fw_status; + + unsigned int fade_in_time; + unsigned int fade_out_time; + + struct aw_prof_info prof_info; + struct aw_sec_data_desc crc_dsp_cfg; + struct aw_profctrl_desc profctrl_desc; + struct aw_volume_desc volume_desc; + struct aw_dsp_mem_desc dsp_mem_desc; + struct aw_vmax_desc vmax_desc; + + struct aw_cali_delay_desc cali_delay_desc; + struct aw_cali_desc cali_desc; + +}; + +int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap); +int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw88395_dev_start(struct aw_device *aw_dev); +int aw88395_dev_stop(struct aw_device *aw_dev); +int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en); + +void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol); +int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index, + struct aw_prof_desc **prof_desc); +char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index); +int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index); +int aw88395_dev_get_profile_index(struct aw_device *aw_dev); +int aw88395_dev_get_profile_count(struct aw_device *aw_dev); +int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg); +int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg); +void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute); + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c new file mode 100644 index 000000000000..34ae405bb43d --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_lib.c @@ -0,0 +1,1066 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_lib.c -- ACF bin parsing and check library file for aw88395 +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <[email protected]> +// + +#include <linux/crc8.h> +#include <linux/i2c.h> +#include "aw88395_lib.h" +#include "aw88395_device.h" + +#define AW88395_CRC8_POLYNOMIAL 0x8C +DECLARE_CRC8_TABLE(aw_crc8_table); + +static char *profile_name[AW88395_PROFILE_MAX] = { + "Music", "Voice", "Voip", "Ringtone", + "Ringtone_hs", "Lowpower", "Bypass", + "Mmi", "Fm", "Notification", "Receiver" +}; + +static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin); + +static int aw_check_sum(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + unsigned char *p_check_sum; + unsigned int sum_data = 0; + unsigned int check_sum; + unsigned int i, len; + + p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + len = bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num].header_len; + check_sum = le32_to_cpup((void *)p_check_sum); + + for (i = 4; i < len; i++) + sum_data += *(p_check_sum + i); + + dev_dbg(aw_dev->dev, "%s -- check_sum = %p, check_sum = 0x%x, sum_data = 0x%x", + __func__, p_check_sum, check_sum, sum_data); + if (sum_data != check_sum) { + dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData:0x%x", + __func__, bin_num, check_sum, sum_data); + return -EINVAL; + } + + return 0; +} + +static int aw_check_data_version(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + if (bin->header_info[bin_num].bin_data_ver < DATA_VERSION_V1 || + bin->header_info[bin_num].bin_data_ver > DATA_VERSION_MAX) { + dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin data version\n"); + return -EINVAL; + } + + return 0; +} + +static int aw_check_register_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_register_num, parse_register_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + parse_register_num = le32_to_cpup((void *)p_check_sum); + check_register_num = (bin->header_info[bin_num].bin_data_len - CHECK_REGISTER_NUM_OFFSET) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + dev_dbg(aw_dev->dev, "%s,parse_register_num = 0x%x,check_register_num = 0x%x\n", + __func__, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + dev_err(aw_dev->dev, "%s parse_register_num = 0x%x,check_register_num = 0x%x\n", + __func__, parse_register_num, check_register_num); + return -EINVAL; + } + + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + VALID_DATA_ADDR; + + return 0; +} + +static int aw_check_dsp_reg_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_dsp_reg_num, parse_dsp_reg_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_NUM)); + bin->header_info[bin_num].reg_data_byte_len = + le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN)); + check_dsp_reg_num = (bin->header_info[bin_num].bin_data_len - CHECK_DSP_REG_NUM) / + bin->header_info[bin_num].reg_data_byte_len; + dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", + __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n"); + dev_err(aw_dev->dev, "%s parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", + __func__, check_dsp_reg_num, check_dsp_reg_num); + return -EINVAL; + } + + bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - DSP_VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + + DSP_VALID_DATA_ADDR; + + return 0; +} + +static int aw_check_soc_app_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) +{ + struct bin_header_info temp_info = bin->header_info[bin_num]; + unsigned int check_soc_app_num, parse_soc_app_num; + unsigned char *p_check_sum; + + p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); + + bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum); + parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_NUM)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - CHECK_SOC_APP_NUM; + dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", + __func__, bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + dev_err(aw_dev->dev, "%s parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", + __func__, parse_soc_app_num, check_soc_app_num); + return -EINVAL; + } + + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = le32_to_cpup((void *)(p_check_sum + + APP_DOWNLOAD_ADDR)); + bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - APP_VALID_DATA_LEN; + bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + + APP_VALID_DATA_ADDR; + + return 0; +} + +static void aw_get_single_bin_header(struct aw_bin *bin) +{ + memcpy((void *)&bin->header_info[bin->all_bin_parse_num], bin->p_addr, DATA_LEN); + + bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN; + bin->all_bin_parse_num += 1; +} + +static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned int bin_num, + int bin_serial_num, struct aw_bin *bin) +{ + struct bin_header_info aw_bin_header_info; + unsigned int bin_start_addr; + unsigned int valid_data_len; + + if (bin->info.len < sizeof(struct bin_header_info)) { + dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + aw_bin_header_info = bin->header_info[bin->all_bin_parse_num - 1]; + if (!bin_serial_num) { + bin_start_addr = le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET)); + bin->p_addr += (HEADER_LEN + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num + + VALID_DATA_ADDR_OFFSET; + } else { + valid_data_len = aw_bin_header_info.bin_data_len; + bin->p_addr += (HDADER_LEN + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len + + VALID_DATA_ADDR_OFFSET; + } + + return aw_parse_bin_header(aw_dev, bin); +} + +static int aw_get_multi_bin_header(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int bin_num, i; + int ret; + + bin_num = le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET)); + if (bin->multi_bin_parse_num == 1) + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + VALID_DATA_ADDR_OFFSET; + + aw_get_single_bin_header(bin); + + for (i = 0; i < bin_num; i++) { + dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_one_of_multi_bins(aw_dev, bin_num, i, bin); + if (ret < 0) + return ret; + } + + return 0; +} + +static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int bin_data_type; + + if (bin->info.len < sizeof(struct bin_header_info)) { + dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", + (int)sizeof(struct bin_header_info), bin->info.len); + return -EINVAL; + } + + bin_data_type = le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSET)); + dev_dbg(aw_dev->dev, "aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + bin->single_bin_parse_num += 1; + dev_dbg(aw_dev->dev, "%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + VALID_DATA_ADDR_OFFSET; + aw_get_single_bin_header(bin); + return 0; + case DATA_TYPE_MULTI_BINS: + bin->multi_bin_parse_num += 1; + dev_dbg(aw_dev->dev, "%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + return aw_get_multi_bin_header(aw_dev, bin); + default: + dev_dbg(aw_dev->dev, "%s There is no corresponding type\n", __func__); + return 0; + } +} + +static int aw_check_bin_header_version(struct aw_device *aw_dev, struct aw_bin *bin) +{ + unsigned int header_version; + + header_version = le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFFSET)); + dev_dbg(aw_dev->dev, "aw_bin_parse header_version 0x%x\n", header_version); + + switch (header_version) { + case HEADER_VERSION_V1: + return aw_parse_bin_header(aw_dev, bin); + default: + dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin header version\n"); + return -EINVAL; + } +} + +static int aw_parsing_bin_file(struct aw_device *aw_dev, struct aw_bin *bin) +{ + int ret = -EINVAL; + int i; + + if (!bin) { + dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n"); + return ret; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + ret = aw_check_bin_header_version(aw_dev, bin); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check bin header version error\n"); + return ret; + } + + for (i = 0; i < bin->all_bin_parse_num; i++) { + ret = aw_check_sum(aw_dev, bin, i); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check sum data error\n"); + return ret; + } + ret = aw_check_data_version(aw_dev, bin, i); + if (ret < 0) { + dev_err(aw_dev->dev, "aw_bin_parse check data version error\n"); + return ret; + } + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + switch (bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + ret = aw_check_register_num(aw_dev, bin, i); + break; + case DATA_TYPE_DSP_REG: + ret = aw_check_dsp_reg_num(aw_dev, bin, i); + break; + case DATA_TYPE_SOC_APP: + ret = aw_check_soc_app_num(aw_dev, bin, i); + break; + default: + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + ret = 0; + break; + } + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_cfg(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + if (data_len & 0x01) + return -EINVAL; + + swab16_array((u16 *)data, data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len, + struct aw_prof_desc *prof_desc) +{ + if (data_len & 0x01) + return -EINVAL; + + swab16_array((u16 *)data, data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = data; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = data_len; + + prof_desc->prof_st = AW88395_PROFILE_OK; + + return 0; +} + +static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data, + unsigned int data_len, struct aw_prof_desc *prof_desc) +{ + struct aw_bin *aw_bin; + int ret; + int i; + + aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL); + if (!aw_bin) + return -ENOMEM; + + aw_bin->info.len = data_len; + memcpy(aw_bin->info.data, data, data_len); + + ret = aw_parsing_bin_file(aw_dev, aw_bin); + if (ret < 0) { + dev_err(aw_dev->dev, "parse bin failed"); + goto parse_bin_failed; + } + + for (i = 0; i < aw_bin->all_bin_parse_num; i++) { + switch (aw_bin->header_info[i].bin_data_type) { + case DATA_TYPE_REGISTER: + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_REG: + if (aw_bin->header_info[i].valid_data_len & 0x01) { + ret = -EINVAL; + goto parse_bin_failed; + } + + swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), + aw_bin->header_info[i].valid_data_len >> 1); + + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + case DATA_TYPE_DSP_FW: + case DATA_TYPE_SOC_APP: + if (aw_bin->header_info[i].valid_data_len & 0x01) { + ret = -EINVAL; + goto parse_bin_failed; + } + + swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), + aw_bin->header_info[i].valid_data_len >> 1); + + prof_desc->fw_ver = aw_bin->header_info[i].app_version; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = + aw_bin->header_info[i].valid_data_len; + prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = + data + aw_bin->header_info[i].valid_data_addr; + break; + default: + dev_dbg(aw_dev->dev, "bin_data_type not found"); + break; + } + } + prof_desc->prof_st = AW88395_PROFILE_OK; + ret = 0; + +parse_bin_failed: + devm_kfree(aw_dev->dev, aw_bin); + return ret; +} + +static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, + struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc) +{ + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_REG: + return aw_dev_parse_raw_reg((u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_DSP_CFG: + return aw_dev_parse_raw_dsp_cfg((u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_DSP_FW: + return aw_dev_parse_raw_dsp_fw( + (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + case ACF_SEC_TYPE_MULTIPLE_BIN: + return aw_dev_prof_parse_multi_bin( + aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, + cfg_dde->data_size, scene_prof_desc); + default: + dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type); + break; + } + + return 0; +} + +static int aw_dev_parse_dev_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); + int sec_num = 0; + int ret, i; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (cfg_dde[i].type == AW88395_DEV_TYPE_ID) && + (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) { + if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_num); + return AW88395_DEV_TYPE_NONE; + } + + return AW88395_DEV_TYPE_OK; +} + +static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) +{ + struct aw_cfg_dde *cfg_dde = + (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); + int sec_num = 0; + int ret, i; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->channel == cfg_dde[i].dev_index) && + (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) && + (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) { + if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) { + dev_err(aw_dev->dev, "dev_profile [%d] overflow", + cfg_dde[i].dev_profile); + return -EINVAL; + } + ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], + &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + sec_num++; + } + } + + if (sec_num == 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev, + struct aw_all_prof_info all_prof_info) +{ + struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_sec_data_desc *sec_desc; + int num = 0; + int i; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + sec_desc = prof_desc[i].sec_desc; + if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_REG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) + prof_info->count++; + } + } + + dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); + + if (!prof_info->count) { + dev_err(aw_dev->dev, "no profile data"); + return -EPERM; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + for (i = 0; i < AW88395_PROFILE_MAX; i++) { + if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { + sec_desc = prof_desc[i].sec_desc; + if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_REG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) && + (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) { + if (num >= prof_info->count) { + dev_err(aw_dev->dev, "overflow count[%d]", + prof_info->count); + return -EINVAL; + } + prof_info->prof_desc[num] = prof_desc[i]; + prof_info->prof_desc[num].id = i; + num++; + } + } + } + + return 0; +} + +static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_all_prof_info *all_prof_info; + int ret; + + all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL); + if (!all_prof_info) + return -ENOMEM; + + ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) { + goto exit; + } else if (ret == AW88395_DEV_TYPE_NONE) { + dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev"); + ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); + if (ret < 0) + goto exit; + } + + ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); + if (ret < 0) + goto exit; + + aw_dev->prof_info.prof_name_list = profile_name; + +exit: + devm_kfree(aw_dev->dev, all_prof_info); + return ret; +} + +static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + struct aw_prof_desc *prof_desc = prof_info->prof_desc; + int i; + + if (!prof_desc) { + dev_err(aw_dev->dev, "prof_desc is NULL"); + return -EINVAL; + } + + prof_info->prof_name_list = devm_kzalloc(aw_dev->dev, + prof_info->count * PROFILE_STR_MAX, + GFP_KERNEL); + if (!prof_info->prof_name_list) + return -ENOMEM; + + for (i = 0; i < prof_info->count; i++) { + prof_desc[i].id = i; + prof_info->prof_name_list[i] = prof_desc[i].prf_str; + dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]); + } + + return 0; +} + +static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + int default_num = 0; + int dev_num = 0; + unsigned int i; + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + if (cfg_dde[i].type == AW88395_DEV_TYPE_ID) + dev_num++; + + if (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) + default_num++; + } + + if (dev_num != 0) { + aw_dev->prof_info.prof_type = AW88395_DEV_TYPE_ID; + } else if (default_num != 0) { + aw_dev->prof_info.prof_type = AW88395_DEV_DEFAULT_TYPE_ID; + } else { + dev_err(aw_dev->dev, "can't find scene"); + return -EINVAL; + } + + return 0; +} + +static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + unsigned int i; + + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) + (*scene_num)++; + } + + return 0; +} + +static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *scene_num) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); + unsigned int i; + + for (i = 0; i < cfg_hdr->ddt_num; ++i) { + if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && + (aw_dev->chip_id == cfg_dde[i].chip_id) && + (aw_dev->channel == cfg_dde[i].dev_index)) + (*scene_num)++; + } + + return 0; +} + +static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg, + unsigned int *count) +{ + int ret; + + ret = aw_get_dde_type_info(aw_dev, aw_cfg); + if (ret < 0) + return ret; + + switch (aw_dev->prof_info.prof_type) { + case AW88395_DEV_TYPE_ID: + ret = aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count); + break; + case AW88395_DEV_DEFAULT_TYPE_ID: + ret = aw_get_default_scene_count_v1(aw_dev, aw_cfg, count); + break; + default: + dev_err(aw_dev->dev, "unsupported prof_type[%x]", aw_dev->prof_info.prof_type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr, + struct aw_cfg_dde_v1 *cfg_dde, + int *cur_scene_id) +{ + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int ret; + + switch (cfg_dde->data_type) { + case ACF_SEC_TYPE_MULTIPLE_BIN: + ret = aw_dev_prof_parse_multi_bin(aw_dev, (u8 *)prof_hdr + cfg_dde->data_offset, + cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); + if (ret < 0) { + dev_err(aw_dev->dev, "parse multi bin failed"); + return ret; + } + prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; + prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; + (*cur_scene_id)++; + break; + default: + dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_dev_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); + int cur_scene_id; + unsigned int i; + int ret; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && + (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && + (aw_dev->chip_id == cfg_dde[i].chip_id)) { + ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr, + &cfg_dde[i], &cur_scene_id); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + } + } + + if (cur_scene_id == 0) { + dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_default_type_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *prof_hdr) +{ + struct aw_cfg_dde_v1 *cfg_dde = + (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); + int cur_scene_id = 0; + unsigned int i; + int ret; + + for (i = 0; i < prof_hdr->ddt_num; i++) { + if ((aw_dev->channel == cfg_dde[i].dev_index) && + (aw_dev->chip_id == cfg_dde[i].chip_id)) { + ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr, + &cfg_dde[i], &cur_scene_id); + if (ret < 0) { + dev_err(aw_dev->dev, "parse failed"); + return ret; + } + } + } + + if (cur_scene_id == 0) { + dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id); + return -EINVAL; + } + + return 0; +} + +static int aw_dev_parse_by_hdr_v1(struct aw_device *aw_dev, + struct aw_cfg_hdr *cfg_hdr) +{ + int ret; + + switch (aw_dev->prof_info.prof_type) { + case AW88395_DEV_TYPE_ID: + ret = aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr); + break; + case AW88395_DEV_DEFAULT_TYPE_ID: + ret = aw_dev_parse_default_type_v1(aw_dev, cfg_hdr); + break; + default: + dev_err(aw_dev->dev, "prof type matched failed, get num[%d]", + aw_dev->prof_info.prof_type); + ret = -EINVAL; + break; + } + + return ret; +} + +static int aw_dev_load_cfg_by_hdr_v1(struct aw_device *aw_dev, + struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + struct aw_prof_info *prof_info = &aw_dev->prof_info; + int ret; + + ret = aw_dev_parse_scene_count_v1(aw_dev, aw_cfg, &prof_info->count); + if (ret < 0) { + dev_err(aw_dev->dev, "get scene count failed"); + return ret; + } + + prof_info->prof_desc = devm_kcalloc(aw_dev->dev, + prof_info->count, sizeof(struct aw_prof_desc), + GFP_KERNEL); + if (!prof_info->prof_desc) + return -ENOMEM; + + ret = aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, "parse hdr failed"); + return ret; + } + + ret = aw_dev_create_prof_name_list_v1(aw_dev); + if (ret < 0) { + dev_err(aw_dev->dev, "create prof name list failed"); + return ret; + } + + return 0; +} + +int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr; + int ret; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + switch (cfg_hdr->hdr_version) { + case AW88395_CFG_HDR_VER: + ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->hdr_version); + return ret; + } + break; + case AW88395_CFG_HDR_VER_V1: + ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg); + if (ret < 0) { + dev_err(aw_dev->dev, "hdr_cersion[0x%x] parse failed", + cfg_hdr->hdr_version); + return ret; + } + break; + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version); + return -EINVAL; + } + aw_dev->fw_status = AW88395_DEV_FW_OK; + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_cfg_load); + +static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + unsigned int end_data_offset; + struct aw_cfg_hdr *cfg_hdr; + struct aw_cfg_dde *cfg_dde; + unsigned int act_data = 0; + unsigned int hdr_ddt_len; + unsigned int i; + u8 act_crc8; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + /* check file type id is awinic acf file */ + if (cfg_hdr->id != ACF_FILE_ID) { + dev_err(aw_dev->dev, "not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /* check data size */ + cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, + cfg_dde[i].data_size, 0); + if (act_crc8 != cfg_dde[i].data_crc) { + dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc:0x%x", + i, (u32)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + return 0; +} + +static int aw_dev_check_acf_by_hdr_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_dde_v1 *cfg_dde; + unsigned int end_data_offset; + struct aw_cfg_hdr *cfg_hdr; + unsigned int act_data = 0; + unsigned int hdr_ddt_len; + u8 act_crc8; + int i; + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + + /* check file type id is awinic acf file */ + if (cfg_hdr->id != ACF_FILE_ID) { + dev_err(aw_dev->dev, "not acf type file"); + return -EINVAL; + } + + hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size; + if (hdr_ddt_len > aw_cfg->len) { + dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]", + cfg_hdr->hdr_offset, aw_cfg->len); + return -EINVAL; + } + + /* check data size */ + cfg_dde = (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_offset); + act_data += hdr_ddt_len; + for (i = 0; i < cfg_hdr->ddt_num; i++) + act_data += cfg_dde[i].data_size; + + if (act_data != aw_cfg->len) { + dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", + act_data, aw_cfg->len); + return -EINVAL; + } + + for (i = 0; i < cfg_hdr->ddt_num; i++) { + /* data check */ + end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; + if (end_data_offset > aw_cfg->len) { + dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]", + i, end_data_offset, aw_cfg->len); + return -EINVAL; + } + + /* crc check */ + act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, + cfg_dde[i].data_size, 0); + if (act_crc8 != cfg_dde[i].data_crc) { + dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc 0x%x", + i, (u32)act_crc8, cfg_dde[i].data_crc); + return -EINVAL; + } + } + + return 0; +} + +int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg) +{ + struct aw_cfg_hdr *cfg_hdr; + + if (!aw_cfg) { + dev_err(aw_dev->dev, "aw_prof is NULL"); + return -EINVAL; + } + + if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { + dev_err(aw_dev->dev, "cfg hdr size[%d] overflow file size[%d]", + aw_cfg->len, (int)sizeof(struct aw_cfg_hdr)); + return -EINVAL; + } + + crc8_populate_lsb(aw_crc8_table, AW88395_CRC8_POLYNOMIAL); + + cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; + switch (cfg_hdr->hdr_version) { + case AW88395_CFG_HDR_VER: + return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg); + case AW88395_CFG_HDR_VER_V1: + return aw_dev_check_acf_by_hdr_v1(aw_dev, aw_cfg); + default: + dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(aw88395_dev_load_acf_check); + +MODULE_DESCRIPTION("AW88395 ACF File Parsing Lib"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/aw88395/aw88395_lib.h b/sound/soc/codecs/aw88395/aw88395_lib.h new file mode 100644 index 000000000000..8a620920d8bd --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_lib.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_lib.h -- ACF bin parsing and check library file for aw88395 +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <[email protected]> +// + +#ifndef __AW88395_LIB_H__ +#define __AW88395_LIB_H__ + +#define CHECK_REGISTER_NUM_OFFSET (4) +#define VALID_DATA_LEN (4) +#define VALID_DATA_ADDR (4) +#define PARSE_DSP_REG_NUM (4) +#define REG_DATA_BYTP_LEN (8) +#define CHECK_DSP_REG_NUM (12) +#define DSP_VALID_DATA_LEN (12) +#define DSP_VALID_DATA_ADDR (12) +#define PARSE_SOC_APP_NUM (8) +#define CHECK_SOC_APP_NUM (12) +#define APP_DOWNLOAD_ADDR (4) +#define APP_VALID_DATA_LEN (12) +#define APP_VALID_DATA_ADDR (12) +#define BIN_NUM_MAX (100) +#define HEADER_LEN (60) +#define BIN_DATA_TYPE_OFFSET (8) +#define DATA_LEN (44) +#define VALID_DATA_ADDR_OFFSET (60) +#define START_ADDR_OFFSET (64) + +#define AW88395_FW_CHECK_PART (10) +#define HDADER_LEN (60) + +#define HEADER_VERSION_OFFSET (4) + +enum bin_header_version_enum { + HEADER_VERSION_V1 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_DSP_FW = 0x00000022, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0x00000001, + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int check_sum; + unsigned int header_ver; + unsigned int bin_data_type; + unsigned int bin_data_ver; + unsigned int bin_data_len; + unsigned int ui_ver; + unsigned char chip_type[8]; + unsigned int reg_byte_len; + unsigned int data_byte_len; + unsigned int device_addr; + unsigned int valid_data_len; + unsigned int valid_data_addr; + + unsigned int reg_num; + unsigned int reg_data_byte_len; + unsigned int download_addr; + unsigned int app_version; + unsigned int header_len; +}; + +struct bin_container { + unsigned int len; + unsigned char data[]; +}; + +struct aw_bin { + unsigned char *p_addr; + unsigned int all_bin_parse_num; + unsigned int multi_bin_parse_num; + unsigned int single_bin_parse_num; + struct bin_header_info header_info[BIN_NUM_MAX]; + struct bin_container info; +}; + +#endif diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h new file mode 100644 index 000000000000..e64f24e97150 --- /dev/null +++ b/sound/soc/codecs/aw88395/aw88395_reg.h @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// aw88395_reg.h -- AW88395 chip register file +// +// Copyright (c) 2022-2023 AWINIC Technology CO., LTD +// +// Author: Bruce zhao <[email protected]> +// + +#ifndef __AW88395_REG_H__ +#define __AW88395_REG_H__ + +#define AW88395_ID_REG (0x00) +#define AW88395_SYSST_REG (0x01) +#define AW88395_SYSINT_REG (0x02) +#define AW88395_SYSINTM_REG (0x03) +#define AW88395_SYSCTRL_REG (0x04) +#define AW88395_SYSCTRL2_REG (0x05) +#define AW88395_I2SCTRL_REG (0x06) +#define AW88395_I2SCFG1_REG (0x07) +#define AW88395_I2SCFG2_REG (0x08) +#define AW88395_HAGCCFG1_REG (0x09) +#define AW88395_HAGCCFG2_REG (0x0A) +#define AW88395_HAGCCFG3_REG (0x0B) +#define AW88395_HAGCCFG4_REG (0x0C) +#define AW88395_HAGCCFG5_REG (0x0D) +#define AW88395_HAGCCFG6_REG (0x0E) +#define AW88395_HAGCCFG7_REG (0x0F) +#define AW88395_MPDCFG_REG (0x10) +#define AW88395_PWMCTRL_REG (0x11) +#define AW88395_I2SCFG3_REG (0x12) +#define AW88395_DBGCTRL_REG (0x13) +#define AW88395_HAGCST_REG (0x20) +#define AW88395_VBAT_REG (0x21) +#define AW88395_TEMP_REG (0x22) +#define AW88395_PVDD_REG (0x23) +#define AW88395_ISNDAT_REG (0x24) +#define AW88395_VSNDAT_REG (0x25) +#define AW88395_I2SINT_REG (0x26) +#define AW88395_I2SCAPCNT_REG (0x27) +#define AW88395_ANASTA1_REG (0x28) +#define AW88395_ANASTA2_REG (0x29) +#define AW88395_ANASTA3_REG (0x2A) +#define AW88395_ANASTA4_REG (0x2B) +#define AW88395_TESTDET_REG (0x2C) +#define AW88395_TESTIN_REG (0x38) +#define AW88395_TESTOUT_REG (0x39) +#define AW88395_DSPMADD_REG (0x40) +#define AW88395_DSPMDAT_REG (0x41) +#define AW88395_WDT_REG (0x42) +#define AW88395_ACR1_REG (0x43) +#define AW88395_ACR2_REG (0x44) +#define AW88395_ASR1_REG (0x45) +#define AW88395_ASR2_REG (0x46) +#define AW88395_DSPCFG_REG (0x47) +#define AW88395_ASR3_REG (0x48) +#define AW88395_ASR4_REG (0x49) +#define AW88395_VSNCTRL1_REG (0x50) +#define AW88395_ISNCTRL1_REG (0x51) +#define AW88395_PLLCTRL1_REG (0x52) +#define AW88395_PLLCTRL2_REG (0x53) +#define AW88395_PLLCTRL3_REG (0x54) +#define AW88395_CDACTRL1_REG (0x55) +#define AW88395_CDACTRL2_REG (0x56) +#define AW88395_SADCCTRL1_REG (0x57) +#define AW88395_SADCCTRL2_REG (0x58) +#define AW88395_CPCTRL1_REG (0x59) +#define AW88395_BSTCTRL1_REG (0x60) +#define AW88395_BSTCTRL2_REG (0x61) +#define AW88395_BSTCTRL3_REG (0x62) +#define AW88395_BSTCTRL4_REG (0x63) +#define AW88395_BSTCTRL5_REG (0x64) +#define AW88395_BSTCTRL6_REG (0x65) +#define AW88395_BSTCTRL7_REG (0x66) +#define AW88395_DSMCFG1_REG (0x67) +#define AW88395_DSMCFG2_REG (0x68) +#define AW88395_DSMCFG3_REG (0x69) +#define AW88395_DSMCFG4_REG (0x6A) +#define AW88395_DSMCFG5_REG (0x6B) +#define AW88395_DSMCFG6_REG (0x6C) +#define AW88395_DSMCFG7_REG (0x6D) +#define AW88395_DSMCFG8_REG (0x6E) +#define AW88395_TESTCTRL1_REG (0x70) +#define AW88395_TESTCTRL2_REG (0x71) +#define AW88395_EFCTRL1_REG (0x72) +#define AW88395_EFCTRL2_REG (0x73) +#define AW88395_EFWH_REG (0x74) +#define AW88395_EFWM2_REG (0x75) +#define AW88395_EFWM1_REG (0x76) +#define AW88395_EFWL_REG (0x77) +#define AW88395_EFRH_REG (0x78) +#define AW88395_EFRM2_REG (0x79) +#define AW88395_EFRM1_REG (0x7A) +#define AW88395_EFRL_REG (0x7B) +#define AW88395_TM_REG (0x7C) + +enum aw88395_id { + AW88395_CHIP_ID = 0x2049, +}; + +#define AW88395_REG_MAX (0x7D) + +#define AW88395_VOLUME_STEP_DB (6 * 8) + +#define AW88395_UVLS_START_BIT (14) +#define AW88395_UVLS_NORMAL (0) +#define AW88395_UVLS_NORMAL_VALUE \ + (AW88395_UVLS_NORMAL << AW88395_UVLS_START_BIT) + +#define AW88395_DSPS_START_BIT (12) +#define AW88395_DSPS_BITS_LEN (1) +#define AW88395_DSPS_MASK \ + (~(((1<<AW88395_DSPS_BITS_LEN)-1) << AW88395_DSPS_START_BIT)) + +#define AW88395_DSPS_NORMAL (0) +#define AW88395_DSPS_NORMAL_VALUE \ + (AW88395_DSPS_NORMAL << AW88395_DSPS_START_BIT) + +#define AW88395_BSTOCS_START_BIT (11) +#define AW88395_BSTOCS_OVER_CURRENT (1) +#define AW88395_BSTOCS_OVER_CURRENT_VALUE \ + (AW88395_BSTOCS_OVER_CURRENT << AW88395_BSTOCS_START_BIT) + +#define AW88395_BSTS_START_BIT (9) +#define AW88395_BSTS_FINISHED (1) +#define AW88395_BSTS_FINISHED_VALUE \ + (AW88395_BSTS_FINISHED << AW88395_BSTS_START_BIT) + +#define AW88395_SWS_START_BIT (8) +#define AW88395_SWS_SWITCHING (1) +#define AW88395_SWS_SWITCHING_VALUE \ + (AW88395_SWS_SWITCHING << AW88395_SWS_START_BIT) + +#define AW88395_NOCLKS_START_BIT (5) +#define AW88395_NOCLKS_NO_CLOCK (1) +#define AW88395_NOCLKS_NO_CLOCK_VALUE \ + (AW88395_NOCLKS_NO_CLOCK << AW88395_NOCLKS_START_BIT) + +#define AW88395_CLKS_START_BIT (4) +#define AW88395_CLKS_STABLE (1) +#define AW88395_CLKS_STABLE_VALUE \ + (AW88395_CLKS_STABLE << AW88395_CLKS_START_BIT) + +#define AW88395_OCDS_START_BIT (3) +#define AW88395_OCDS_OC (1) +#define AW88395_OCDS_OC_VALUE \ + (AW88395_OCDS_OC << AW88395_OCDS_START_BIT) + +#define AW88395_OTHS_START_BIT (1) +#define AW88395_OTHS_OT (1) +#define AW88395_OTHS_OT_VALUE \ + (AW88395_OTHS_OT << AW88395_OTHS_START_BIT) + +#define AW88395_PLLS_START_BIT (0) +#define AW88395_PLLS_LOCKED (1) +#define AW88395_PLLS_LOCKED_VALUE \ + (AW88395_PLLS_LOCKED << AW88395_PLLS_START_BIT) + +#define AW88395_BIT_PLL_CHECK \ + (AW88395_CLKS_STABLE_VALUE | \ + AW88395_PLLS_LOCKED_VALUE) + +#define AW88395_BIT_SYSST_CHECK_MASK \ + (~(AW88395_UVLS_NORMAL_VALUE | \ + AW88395_BSTOCS_OVER_CURRENT_VALUE | \ + AW88395_BSTS_FINISHED_VALUE | \ + AW88395_SWS_SWITCHING_VALUE | \ + AW88395_NOCLKS_NO_CLOCK_VALUE | \ + AW88395_CLKS_STABLE_VALUE | \ + AW88395_OCDS_OC_VALUE | \ + AW88395_OTHS_OT_VALUE | \ + AW88395_PLLS_LOCKED_VALUE)) + +#define AW88395_BIT_SYSST_CHECK \ + (AW88395_BSTS_FINISHED_VALUE | \ + AW88395_SWS_SWITCHING_VALUE | \ + AW88395_CLKS_STABLE_VALUE | \ + AW88395_PLLS_LOCKED_VALUE) + +#define AW88395_WDI_START_BIT (6) +#define AW88395_WDI_INT_VALUE (1) +#define AW88395_WDI_INTERRUPT \ + (AW88395_WDI_INT_VALUE << AW88395_WDI_START_BIT) + +#define AW88395_NOCLKI_START_BIT (5) +#define AW88395_NOCLKI_INT_VALUE (1) +#define AW88395_NOCLKI_INTERRUPT \ + (AW88395_NOCLKI_INT_VALUE << AW88395_NOCLKI_START_BIT) + +#define AW88395_CLKI_START_BIT (4) +#define AW88395_CLKI_INT_VALUE (1) +#define AW88395_CLKI_INTERRUPT \ + (AW88395_CLKI_INT_VALUE << AW88395_CLKI_START_BIT) + +#define AW88395_PLLI_START_BIT (0) +#define AW88395_PLLI_INT_VALUE (1) +#define AW88395_PLLI_INTERRUPT \ + (AW88395_PLLI_INT_VALUE << AW88395_PLLI_START_BIT) + +#define AW88395_BIT_SYSINT_CHECK \ + (AW88395_WDI_INTERRUPT | \ + AW88395_CLKI_INTERRUPT | \ + AW88395_NOCLKI_INTERRUPT | \ + AW88395_PLLI_INTERRUPT) + +#define AW88395_HMUTE_START_BIT (8) +#define AW88395_HMUTE_BITS_LEN (1) +#define AW88395_HMUTE_MASK \ + (~(((1<<AW88395_HMUTE_BITS_LEN)-1) << AW88395_HMUTE_START_BIT)) + +#define AW88395_HMUTE_DISABLE (0) +#define AW88395_HMUTE_DISABLE_VALUE \ + (AW88395_HMUTE_DISABLE << AW88395_HMUTE_START_BIT) + +#define AW88395_HMUTE_ENABLE (1) +#define AW88395_HMUTE_ENABLE_VALUE \ + (AW88395_HMUTE_ENABLE << AW88395_HMUTE_START_BIT) + +#define AW88395_RCV_MODE_START_BIT (7) +#define AW88395_RCV_MODE_BITS_LEN (1) +#define AW88395_RCV_MODE_MASK \ + (~(((1<<AW88395_RCV_MODE_BITS_LEN)-1) << AW88395_RCV_MODE_START_BIT)) + +#define AW88395_RCV_MODE_RECEIVER (1) +#define AW88395_RCV_MODE_RECEIVER_VALUE \ + (AW88395_RCV_MODE_RECEIVER << AW88395_RCV_MODE_START_BIT) + +#define AW88395_DSPBY_START_BIT (2) +#define AW88395_DSPBY_BITS_LEN (1) +#define AW88395_DSPBY_MASK \ + (~(((1<<AW88395_DSPBY_BITS_LEN)-1) << AW88395_DSPBY_START_BIT)) + +#define AW88395_DSPBY_WORKING (0) +#define AW88395_DSPBY_WORKING_VALUE \ + (AW88395_DSPBY_WORKING << AW88395_DSPBY_START_BIT) + +#define AW88395_DSPBY_BYPASS (1) +#define AW88395_DSPBY_BYPASS_VALUE \ + (AW88395_DSPBY_BYPASS << AW88395_DSPBY_START_BIT) + +#define AW88395_AMPPD_START_BIT (1) +#define AW88395_AMPPD_BITS_LEN (1) +#define AW88395_AMPPD_MASK \ + (~(((1<<AW88395_AMPPD_BITS_LEN)-1) << AW88395_AMPPD_START_BIT)) + +#define AW88395_AMPPD_WORKING (0) +#define AW88395_AMPPD_WORKING_VALUE \ + (AW88395_AMPPD_WORKING << AW88395_AMPPD_START_BIT) + +#define AW88395_AMPPD_POWER_DOWN (1) +#define AW88395_AMPPD_POWER_DOWN_VALUE \ + (AW88395_AMPPD_POWER_DOWN << AW88395_AMPPD_START_BIT) + +#define AW88395_PWDN_START_BIT (0) +#define AW88395_PWDN_BITS_LEN (1) +#define AW88395_PWDN_MASK \ + (~(((1<<AW88395_PWDN_BITS_LEN)-1) << AW88395_PWDN_START_BIT)) + +#define AW88395_PWDN_WORKING (0) +#define AW88395_PWDN_WORKING_VALUE \ + (AW88395_PWDN_WORKING << AW88395_PWDN_START_BIT) + +#define AW88395_PWDN_POWER_DOWN (1) +#define AW88395_PWDN_POWER_DOWN_VALUE \ + (AW88395_PWDN_POWER_DOWN << AW88395_PWDN_START_BIT) + +#define AW88395_MUTE_VOL (90 * 8) +#define AW88395_VOLUME_STEP_DB (6 * 8) + +#define AW88395_VOL_6DB_START (6) +#define AW88395_VOL_START_BIT (6) +#define AW88395_VOL_BITS_LEN (10) +#define AW88395_VOL_MASK \ + (~(((1<<AW88395_VOL_BITS_LEN)-1) << AW88395_VOL_START_BIT)) + +#define AW88395_VOL_DEFAULT_VALUE (0) + +#define AW88395_I2STXEN_START_BIT (0) +#define AW88395_I2STXEN_BITS_LEN (1) +#define AW88395_I2STXEN_MASK \ + (~(((1<<AW88395_I2STXEN_BITS_LEN)-1) << AW88395_I2STXEN_START_BIT)) + +#define AW88395_I2STXEN_DISABLE (0) +#define AW88395_I2STXEN_DISABLE_VALUE \ + (AW88395_I2STXEN_DISABLE << AW88395_I2STXEN_START_BIT) + +#define AW88395_I2STXEN_ENABLE (1) +#define AW88395_I2STXEN_ENABLE_VALUE \ + (AW88395_I2STXEN_ENABLE << AW88395_I2STXEN_START_BIT) + +#define AW88395_AGC_DSP_CTL_START_BIT (15) +#define AW88395_AGC_DSP_CTL_BITS_LEN (1) +#define AW88395_AGC_DSP_CTL_MASK \ + (~(((1<<AW88395_AGC_DSP_CTL_BITS_LEN)-1) << AW88395_AGC_DSP_CTL_START_BIT)) + +#define AW88395_AGC_DSP_CTL_DISABLE (0) +#define AW88395_AGC_DSP_CTL_DISABLE_VALUE \ + (AW88395_AGC_DSP_CTL_DISABLE << AW88395_AGC_DSP_CTL_START_BIT) + +#define AW88395_AGC_DSP_CTL_ENABLE (1) +#define AW88395_AGC_DSP_CTL_ENABLE_VALUE \ + (AW88395_AGC_DSP_CTL_ENABLE << AW88395_AGC_DSP_CTL_START_BIT) + +#define AW88395_VDSEL_START_BIT (0) +#define AW88395_VDSEL_BITS_LEN (1) +#define AW88395_VDSEL_MASK \ + (~(((1<<AW88395_VDSEL_BITS_LEN)-1) << AW88395_VDSEL_START_BIT)) + +#define AW88395_MEM_CLKSEL_START_BIT (3) +#define AW88395_MEM_CLKSEL_BITS_LEN (1) +#define AW88395_MEM_CLKSEL_MASK \ + (~(((1<<AW88395_MEM_CLKSEL_BITS_LEN)-1) << AW88395_MEM_CLKSEL_START_BIT)) + +#define AW88395_MEM_CLKSEL_OSC_CLK (0) +#define AW88395_MEM_CLKSEL_OSC_CLK_VALUE \ + (AW88395_MEM_CLKSEL_OSC_CLK << AW88395_MEM_CLKSEL_START_BIT) + +#define AW88395_MEM_CLKSEL_DAP_HCLK (1) +#define AW88395_MEM_CLKSEL_DAP_HCLK_VALUE \ + (AW88395_MEM_CLKSEL_DAP_HCLK << AW88395_MEM_CLKSEL_START_BIT) + +#define AW88395_CCO_MUX_START_BIT (14) +#define AW88395_CCO_MUX_BITS_LEN (1) +#define AW88395_CCO_MUX_MASK \ + (~(((1<<AW88395_CCO_MUX_BITS_LEN)-1) << AW88395_CCO_MUX_START_BIT)) + +#define AW88395_CCO_MUX_DIVIDED (0) +#define AW88395_CCO_MUX_DIVIDED_VALUE \ + (AW88395_CCO_MUX_DIVIDED << AW88395_CCO_MUX_START_BIT) + +#define AW88395_CCO_MUX_BYPASS (1) +#define AW88395_CCO_MUX_BYPASS_VALUE \ + (AW88395_CCO_MUX_BYPASS << AW88395_CCO_MUX_START_BIT) + +#define AW88395_EF_VSN_GESLP_START_BIT (0) +#define AW88395_EF_VSN_GESLP_BITS_LEN (10) +#define AW88395_EF_VSN_GESLP_MASK \ + (~(((1<<AW88395_EF_VSN_GESLP_BITS_LEN)-1) << AW88395_EF_VSN_GESLP_START_BIT)) + +#define AW88395_EF_VSN_GESLP_SIGN_MASK (~(1 << 9)) +#define AW88395_EF_VSN_GESLP_SIGN_NEG (0xfe00) + +#define AW88395_EF_ISN_GESLP_START_BIT (0) +#define AW88395_EF_ISN_GESLP_BITS_LEN (10) +#define AW88395_EF_ISN_GESLP_MASK \ + (~(((1<<AW88395_EF_ISN_GESLP_BITS_LEN)-1) << AW88395_EF_ISN_GESLP_START_BIT)) + +#define AW88395_EF_ISN_GESLP_SIGN_MASK (~(1 << 9)) +#define AW88395_EF_ISN_GESLP_SIGN_NEG (0xfe00) + +#define AW88395_CABL_BASE_VALUE (1000) +#define AW88395_ICABLK_FACTOR (1) +#define AW88395_VCABLK_FACTOR (1) +#define AW88395_VCAL_FACTOR (1 << 12) +#define AW88395_VSCAL_FACTOR (16500) +#define AW88395_ISCAL_FACTOR (3667) +#define AW88395_EF_VSENSE_GAIN_SHIFT (0) + +#define AW88395_VCABLK_FACTOR_DAC (2) +#define AW88395_VSCAL_FACTOR_DAC (11790) +#define AW88395_EF_DAC_GESLP_SHIFT (10) +#define AW88395_EF_DAC_GESLP_SIGN_MASK (1 << 5) +#define AW88395_EF_DAC_GESLP_SIGN_NEG (0xffc0) + +#define AW88395_VCALB_ADJ_FACTOR (12) + +#define AW88395_WDT_CNT_START_BIT (0) +#define AW88395_WDT_CNT_BITS_LEN (8) +#define AW88395_WDT_CNT_MASK \ + (~(((1<<AW88395_WDT_CNT_BITS_LEN)-1) << AW88395_WDT_CNT_START_BIT)) + +#define AW88395_DSP_CFG_ADDR (0x9C80) +#define AW88395_DSP_FW_ADDR (0x8C00) +#define AW88395_DSP_REG_VMAX (0x9C94) +#define AW88395_DSP_REG_CFG_ADPZ_RE (0x9D00) +#define AW88395_DSP_REG_VCALB (0x9CF7) +#define AW88395_DSP_RE_SHIFT (12) + +#define AW88395_DSP_REG_CFG_ADPZ_RA (0x9D02) +#define AW88395_DSP_REG_CRC_ADDR (0x9F42) +#define AW88395_DSP_CALI_F0_DELAY (0x9CFD) + +#endif diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c index 4b8ec6f77337..d57b043d6bfe 100644 --- a/sound/soc/codecs/hda.c +++ b/sound/soc/codecs/hda.c @@ -126,12 +126,15 @@ static void hda_codec_unregister_dais(struct hda_codec *codec, struct hda_pcm *pcm; for_each_component_dais_safe(component, dai, save) { + int stream; + list_for_each_entry(pcm, &codec->pcm_list_head, list) { if (strcmp(dai->driver->name, pcm->name)) continue; - snd_soc_dapm_free_widget(dai->playback_widget); - snd_soc_dapm_free_widget(dai->capture_widget); + for_each_pcm_streams(stream) + snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream)); + snd_soc_unregister_dai(dai); break; } diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 74cbbe16f9ae..01e8ffda2a4b 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -518,7 +518,7 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - struct hdmi_codec_daifmt *cf = dai->playback_dma_data; + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); struct hdmi_codec_params hp = { .iec = { .status = { 0 }, @@ -562,7 +562,7 @@ static int hdmi_codec_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - struct hdmi_codec_daifmt *cf = dai->playback_dma_data; + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int channels = runtime->channels; unsigned int width = snd_pcm_format_width(runtime->format); @@ -597,7 +597,7 @@ static int hdmi_codec_prepare(struct snd_pcm_substream *substream, static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct hdmi_codec_daifmt *cf = dai->playback_dma_data; + struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai); /* Reset daifmt */ memset(cf, 0, sizeof(*cf)); @@ -834,7 +834,8 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai) if (!daifmt) return -ENOMEM; - dai->playback_dma_data = daifmt; + snd_soc_dai_dma_data_set_playback(dai, daifmt); + return 0; } @@ -891,7 +892,7 @@ static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) if (ret) return ret; - cf = dai->playback_dma_data; + cf = snd_soc_dai_dma_data_get_playback(dai); cf->fmt = HDMI_SPDIF; return 0; diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c new file mode 100644 index 000000000000..5d01787b1c1f --- /dev/null +++ b/sound/soc/codecs/idt821034.c @@ -0,0 +1,1180 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// IDT821034 ALSA SoC driver +// +// Copyright 2022 CS GROUP France +// +// Author: Herve Codina <[email protected]> + +#include <linux/bitrev.h> +#include <linux/gpio/driver.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define IDT821034_NB_CHANNEL 4 + +struct idt821034_amp { + u16 gain; + bool is_muted; +}; + +struct idt821034 { + struct spi_device *spi; + struct mutex mutex; + u8 spi_tx_buf; /* Cannot use stack area for SPI (dma-safe memory) */ + u8 spi_rx_buf; /* Cannot use stack area for SPI (dma-safe memory) */ + struct { + u8 codec_conf; + struct { + u8 power; + u8 tx_slot; + u8 rx_slot; + u8 slic_conf; + u8 slic_control; + } ch[IDT821034_NB_CHANNEL]; + } cache; + struct { + struct { + struct idt821034_amp amp_out; + struct idt821034_amp amp_in; + } ch[IDT821034_NB_CHANNEL]; + } amps; + int max_ch_playback; + int max_ch_capture; + struct gpio_chip gpio_chip; +}; + +static int idt821034_8bit_write(struct idt821034 *idt821034, u8 val) +{ + struct spi_transfer xfer[] = { + { + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + }, { + .cs_off = 1, + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + } + }; + + idt821034->spi_tx_buf = val; + + dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x\n", val); + + return spi_sync_transfer(idt821034->spi, xfer, 2); +} + +static int idt821034_2x8bit_write(struct idt821034 *idt821034, u8 val1, u8 val2) +{ + int ret; + + ret = idt821034_8bit_write(idt821034, val1); + if (ret) + return ret; + return idt821034_8bit_write(idt821034, val2); +} + +static int idt821034_8bit_read(struct idt821034 *idt821034, u8 valw, u8 *valr) +{ + struct spi_transfer xfer[] = { + { + .tx_buf = &idt821034->spi_tx_buf, + .rx_buf = &idt821034->spi_rx_buf, + .len = 1, + }, { + .cs_off = 1, + .tx_buf = &idt821034->spi_tx_buf, + .len = 1, + } + }; + int ret; + + idt821034->spi_tx_buf = valw; + + ret = spi_sync_transfer(idt821034->spi, xfer, 2); + if (ret) + return ret; + + *valr = idt821034->spi_rx_buf; + + dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x, rd 0x%x\n", + valw, *valr); + + return 0; +} + +/* Available mode for the programming sequence */ +#define IDT821034_MODE_CODEC(_ch) (0x80 | ((_ch) << 2)) +#define IDT821034_MODE_SLIC(_ch) (0xD0 | ((_ch) << 2)) +#define IDT821034_MODE_GAIN(_ch) (0xC0 | ((_ch) << 2)) + +/* Power values that can be used in 'power' (can be ORed) */ +#define IDT821034_CONF_PWRUP_TX BIT(1) /* from analog input to PCM */ +#define IDT821034_CONF_PWRUP_RX BIT(0) /* from PCM to analog output */ + +static int idt821034_set_channel_power(struct idt821034 *idt821034, u8 ch, u8 power) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_channel_power(%u, 0x%x)\n", ch, power); + + conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf; + + if (power & IDT821034_CONF_PWRUP_RX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_RX, + idt821034->cache.ch[ch].rx_slot); + if (ret) + return ret; + } + if (power & IDT821034_CONF_PWRUP_TX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_TX, + idt821034->cache.ch[ch].tx_slot); + if (ret) + return ret; + } + if (!(power & (IDT821034_CONF_PWRUP_TX | IDT821034_CONF_PWRUP_RX))) { + ret = idt821034_2x8bit_write(idt821034, conf, 0); + if (ret) + return ret; + } + + idt821034->cache.ch[ch].power = power; + + return 0; +} + +static u8 idt821034_get_channel_power(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].power; +} + +/* Codec configuration values that can be used in 'codec_conf' (can be ORed) */ +#define IDT821034_CONF_ALAW_MODE BIT(5) +#define IDT821034_CONF_DELAY_MODE BIT(4) + +static int idt821034_set_codec_conf(struct idt821034 *idt821034, u8 codec_conf) +{ + u8 conf; + u8 ts; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_codec_conf(0x%x)\n", codec_conf); + + /* codec conf fields are common to all channel. + * Arbitrary use of channel 0 for this configuration. + */ + + /* Set Configuration Register */ + conf = IDT821034_MODE_CODEC(0) | codec_conf; + + /* Update conf value and timeslot register value according + * to cache values + */ + if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_RX) { + conf |= IDT821034_CONF_PWRUP_RX; + ts = idt821034->cache.ch[0].rx_slot; + } else if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_TX) { + conf |= IDT821034_CONF_PWRUP_TX; + ts = idt821034->cache.ch[0].tx_slot; + } else { + ts = 0x00; + } + + /* Write configuration register and time-slot register */ + ret = idt821034_2x8bit_write(idt821034, conf, ts); + if (ret) + return ret; + + idt821034->cache.codec_conf = codec_conf; + return 0; +} + +static u8 idt821034_get_codec_conf(struct idt821034 *idt821034) +{ + return idt821034->cache.codec_conf; +} + +/* Channel direction values that can be used in 'ch_dir' (can be ORed) */ +#define IDT821034_CH_RX BIT(0) /* from PCM to analog output */ +#define IDT821034_CH_TX BIT(1) /* from analog input to PCM */ + +static int idt821034_set_channel_ts(struct idt821034 *idt821034, u8 ch, u8 ch_dir, u8 ts_num) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_channel_ts(%u, 0x%x, %d)\n", ch, ch_dir, ts_num); + + conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf; + + if (ch_dir & IDT821034_CH_RX) { + if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_RX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_RX, + ts_num); + if (ret) + return ret; + } + idt821034->cache.ch[ch].rx_slot = ts_num; + } + if (ch_dir & IDT821034_CH_TX) { + if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_TX) { + ret = idt821034_2x8bit_write(idt821034, + conf | IDT821034_CONF_PWRUP_TX, + ts_num); + if (ret) + return ret; + } + idt821034->cache.ch[ch].tx_slot = ts_num; + } + + return 0; +} + +/* SLIC direction values that can be used in 'slic_dir' (can be ORed) */ +#define IDT821034_SLIC_IO1_IN BIT(1) +#define IDT821034_SLIC_IO0_IN BIT(0) + +static int idt821034_set_slic_conf(struct idt821034 *idt821034, u8 ch, u8 slic_dir) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_slic_conf(%u, 0x%x)\n", ch, slic_dir); + + conf = IDT821034_MODE_SLIC(ch) | slic_dir; + ret = idt821034_2x8bit_write(idt821034, conf, idt821034->cache.ch[ch].slic_control); + if (ret) + return ret; + + idt821034->cache.ch[ch].slic_conf = slic_dir; + + return 0; +} + +static u8 idt821034_get_slic_conf(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].slic_conf; +} + +static int idt821034_write_slic_raw(struct idt821034 *idt821034, u8 ch, u8 slic_raw) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "write_slic_raw(%u, 0x%x)\n", ch, slic_raw); + + /* + * On write, slic_raw is mapped as follow : + * b4: O_4 + * b3: O_3 + * b2: O_2 + * b1: I/O_1 + * b0: I/O_0 + */ + + conf = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf; + ret = idt821034_2x8bit_write(idt821034, conf, slic_raw); + if (ret) + return ret; + + idt821034->cache.ch[ch].slic_control = slic_raw; + return 0; +} + +static u8 idt821034_get_written_slic_raw(struct idt821034 *idt821034, u8 ch) +{ + return idt821034->cache.ch[ch].slic_control; +} + +static int idt821034_read_slic_raw(struct idt821034 *idt821034, u8 ch, u8 *slic_raw) +{ + u8 val; + int ret; + + /* + * On read, slic_raw is mapped as follow : + * b7: I/O_0 + * b6: I/O_1 + * b5: O_2 + * b4: O_3 + * b3: O_4 + * b2: I/O1_0, I/O_0 from channel 1 (no matter ch value) + * b1: I/O2_0, I/O_0 from channel 2 (no matter ch value) + * b2: I/O3_0, I/O_0 from channel 3 (no matter ch value) + */ + + val = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf; + ret = idt821034_8bit_write(idt821034, val); + if (ret) + return ret; + + ret = idt821034_8bit_read(idt821034, idt821034->cache.ch[ch].slic_control, slic_raw); + if (ret) + return ret; + + dev_dbg(&idt821034->spi->dev, "read_slic_raw(%i) 0x%x\n", ch, *slic_raw); + + return 0; +} + +/* Gain type values that can be used in 'gain_type' (cannot be ORed) */ +#define IDT821034_GAIN_RX (0 << 1) /* from PCM to analog output */ +#define IDT821034_GAIN_TX (1 << 1) /* from analog input to PCM */ + +static int idt821034_set_gain_channel(struct idt821034 *idt821034, u8 ch, + u8 gain_type, u16 gain_val) +{ + u8 conf; + int ret; + + dev_dbg(&idt821034->spi->dev, "set_gain_channel(%u, 0x%x, 0x%x-%d)\n", + ch, gain_type, gain_val, gain_val); + + /* + * The gain programming coefficients should be calculated as: + * Transmit : Coeff_X = round [ gain_X0dB × gain_X ] + * Receive: Coeff_R = round [ gain_R0dB × gain_R ] + * where: + * gain_X0dB = 1820; + * gain_X is the target gain; + * Coeff_X should be in the range of 0 to 8192. + * gain_R0dB = 2506; + * gain_R is the target gain; + * Coeff_R should be in the range of 0 to 8192. + * + * A gain programming coefficient is 14-bit wide and in binary format. + * The 7 Most Significant Bits of the coefficient is called + * GA_MSB_Transmit for transmit path, or is called GA_MSB_Receive for + * receive path; The 7 Least Significant Bits of the coefficient is + * called GA_LSB_ Transmit for transmit path, or is called + * GA_LSB_Receive for receive path. + * + * An example is given below to clarify the calculation of the + * coefficient. To program a +3 dB gain in transmit path and a -3.5 dB + * gain in receive path: + * + * Linear Code of +3dB = 10^(3/20)= 1.412537545 + * Coeff_X = round (1820 × 1.412537545) = 2571 + * = 0b001010_00001011 + * GA_MSB_Transmit = 0b0010100 + * GA_LSB_Transmit = 0b0001011 + * + * Linear Code of -3.5dB = 10^(-3.5/20) = 0.668343917 + * Coeff_R= round (2506 × 0.668343917) = 1675 + * = 0b0001101_0001011 + * GA_MSB_Receive = 0b0001101 + * GA_LSB_Receive = 0b0001011 + */ + + conf = IDT821034_MODE_GAIN(ch) | gain_type; + + ret = idt821034_2x8bit_write(idt821034, conf | 0x00, gain_val & 0x007F); + if (ret) + return ret; + + ret = idt821034_2x8bit_write(idt821034, conf | 0x01, (gain_val >> 7) & 0x7F); + if (ret) + return ret; + + return 0; +} + +/* Id helpers used in controls and dapm */ +#define IDT821034_DIR_OUT (1 << 3) +#define IDT821034_DIR_IN (0 << 3) +#define IDT821034_ID(_ch, _dir) (((_ch) & 0x03) | (_dir)) +#define IDT821034_ID_OUT(_ch) IDT821034_ID(_ch, IDT821034_DIR_OUT) +#define IDT821034_ID_IN(_ch) IDT821034_ID(_ch, IDT821034_DIR_IN) + +#define IDT821034_ID_GET_CHAN(_id) ((_id) & 0x03) +#define IDT821034_ID_GET_DIR(_id) ((_id) & (1 << 3)) +#define IDT821034_ID_IS_OUT(_id) (IDT821034_ID_GET_DIR(_id) == IDT821034_DIR_OUT) + +static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + int val; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(mc->reg); + + mutex_lock(&idt821034->mutex); + if (IDT821034_ID_IS_OUT(mc->reg)) + val = idt821034->amps.ch[ch].amp_out.gain; + else + val = idt821034->amps.ch[ch].amp_in.gain; + mutex_unlock(&idt821034->mutex); + + ucontrol->value.integer.value[0] = val & mask; + if (invert) + ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0]; + else + ucontrol->value.integer.value[0] = ucontrol->value.integer.value[0] - min; + + return 0; +} + +static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + struct idt821034_amp *amp; + int min = mc->min; + int max = mc->max; + unsigned int mask = (1 << fls(max)) - 1; + unsigned int invert = mc->invert; + unsigned int val; + int ret; + u8 gain_type; + u8 ch; + + val = ucontrol->value.integer.value[0]; + if (val < 0) + return -EINVAL; + if (val > max - min) + return -EINVAL; + + if (invert) + val = (max - val) & mask; + else + val = (val + min) & mask; + + ch = IDT821034_ID_GET_CHAN(mc->reg); + + mutex_lock(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(mc->reg)) { + amp = &idt821034->amps.ch[ch].amp_out; + gain_type = IDT821034_GAIN_RX; + } else { + amp = &idt821034->amps.ch[ch].amp_in; + gain_type = IDT821034_GAIN_TX; + } + + if (amp->gain == val) { + ret = 0; + goto end; + } + + if (!amp->is_muted) { + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val); + if (ret) + goto end; + } + + amp->gain = val; + ret = 1; /* The value changed */ +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int id = kcontrol->private_value; + bool is_muted; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + + mutex_lock(&idt821034->mutex); + is_muted = IDT821034_ID_IS_OUT(id) ? + idt821034->amps.ch[ch].amp_out.is_muted : + idt821034->amps.ch[ch].amp_in.is_muted; + mutex_unlock(&idt821034->mutex); + + ucontrol->value.integer.value[0] = !is_muted; + + return 0; +} + +static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int id = kcontrol->private_value; + struct idt821034_amp *amp; + bool is_mute; + u8 gain_type; + int ret; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + is_mute = !ucontrol->value.integer.value[0]; + + mutex_lock(&idt821034->mutex); + + if (IDT821034_ID_IS_OUT(id)) { + amp = &idt821034->amps.ch[ch].amp_out; + gain_type = IDT821034_GAIN_RX; + } else { + amp = &idt821034->amps.ch[ch].amp_in; + gain_type = IDT821034_GAIN_TX; + } + + if (amp->is_muted == is_mute) { + ret = 0; + goto end; + } + + ret = idt821034_set_gain_channel(idt821034, ch, gain_type, + is_mute ? 0 : amp->gain); + if (ret) + goto end; + + amp->is_muted = is_mute; + ret = 1; /* The value changed */ +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -6520, 1306); +#define IDT821034_GAIN_IN_MIN_RAW 1 /* -65.20 dB -> 10^(-65.2/20.0) * 1820 = 1 */ +#define IDT821034_GAIN_IN_MAX_RAW 8191 /* 13.06 dB -> 10^(13.06/20.0) * 1820 = 8191 */ +#define IDT821034_GAIN_IN_INIT_RAW 1820 /* 0dB -> 10^(0/20) * 1820 = 1820 */ + +static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -6798, 1029); +#define IDT821034_GAIN_OUT_MIN_RAW 1 /* -67.98 dB -> 10^(-67.98/20.0) * 2506 = 1*/ +#define IDT821034_GAIN_OUT_MAX_RAW 8191 /* 10.29 dB -> 10^(10.29/20.0) * 2506 = 8191 */ +#define IDT821034_GAIN_OUT_INIT_RAW 2506 /* 0dB -> 10^(0/20) * 2506 = 2506 */ + +static const struct snd_kcontrol_new idt821034_controls[] = { + /* DAC volume control */ + SOC_SINGLE_RANGE_EXT_TLV("DAC0 Playback Volume", IDT821034_ID_OUT(0), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC1 Playback Volume", IDT821034_ID_OUT(1), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC2 Playback Volume", IDT821034_ID_OUT(2), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + SOC_SINGLE_RANGE_EXT_TLV("DAC3 Playback Volume", IDT821034_ID_OUT(3), 0, + IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_out), + + /* DAC mute control */ + SOC_SINGLE_BOOL_EXT("DAC0 Playback Switch", IDT821034_ID_OUT(0), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC1 Playback Switch", IDT821034_ID_OUT(1), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC2 Playback Switch", IDT821034_ID_OUT(2), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("DAC3 Playback Switch", IDT821034_ID_OUT(3), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + + /* ADC volume control */ + SOC_SINGLE_RANGE_EXT_TLV("ADC0 Capture Volume", IDT821034_ID_IN(0), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC1 Capture Volume", IDT821034_ID_IN(1), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC2 Capture Volume", IDT821034_ID_IN(2), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + SOC_SINGLE_RANGE_EXT_TLV("ADC3 Capture Volume", IDT821034_ID_IN(3), 0, + IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW, + 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put, + idt821034_gain_in), + + /* ADC mute control */ + SOC_SINGLE_BOOL_EXT("ADC0 Capture Switch", IDT821034_ID_IN(0), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC1 Capture Switch", IDT821034_ID_IN(1), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC2 Capture Switch", IDT821034_ID_IN(2), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), + SOC_SINGLE_BOOL_EXT("ADC3 Capture Switch", IDT821034_ID_IN(3), + idt821034_kctrl_mute_get, idt821034_kctrl_mute_put), +}; + +static int idt821034_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + unsigned int id = w->shift; + u8 power, mask; + int ret; + u8 ch; + + ch = IDT821034_ID_GET_CHAN(id); + mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX; + + mutex_lock(&idt821034->mutex); + + power = idt821034_get_channel_power(idt821034, ch); + if (SND_SOC_DAPM_EVENT_ON(event)) + power |= mask; + else + power &= ~mask; + ret = idt821034_set_channel_power(idt821034, ch, power); + + mutex_unlock(&idt821034->mutex); + + return ret; +} + +static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("DAC0", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(0), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC1", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(1), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC2", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(2), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DAC3", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(3), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("OUT0"), + SND_SOC_DAPM_OUTPUT("OUT1"), + SND_SOC_DAPM_OUTPUT("OUT2"), + SND_SOC_DAPM_OUTPUT("OUT3"), + + SND_SOC_DAPM_DAC_E("ADC0", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(0), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC1", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(1), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC2", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(2), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("ADC3", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(3), 0, + idt821034_power_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("IN0"), + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3"), +}; + +static const struct snd_soc_dapm_route idt821034_dapm_routes[] = { + { "OUT0", NULL, "DAC0" }, + { "OUT1", NULL, "DAC1" }, + { "OUT2", NULL, "DAC2" }, + { "OUT3", NULL, "DAC3" }, + + { "ADC0", NULL, "IN0" }, + { "ADC1", NULL, "IN1" }, + { "ADC2", NULL, "IN2" }, + { "ADC3", NULL, "IN3" }, +}; + +static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int width) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + unsigned int mask; + u8 slot; + int ret; + u8 ch; + + switch (width) { + case 0: /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + mask = tx_mask; + slot = 0; + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { + mutex_lock(&idt821034->mutex); + ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n", + ch, ret); + return ret; + } + ch++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n", + tx_mask, IDT821034_NB_CHANNEL); + return -EINVAL; + } + idt821034->max_ch_playback = ch; + + mask = rx_mask; + slot = 0; + ch = 0; + while (mask && ch < IDT821034_NB_CHANNEL) { + if (mask & 0x1) { + mutex_lock(&idt821034->mutex); + ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n", + ch, ret); + return ret; + } + ch++; + } + mask >>= 1; + slot++; + } + if (mask) { + dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n", + rx_mask, IDT821034_NB_CHANNEL); + return -EINVAL; + } + idt821034->max_ch_capture = ch; + + return 0; +} + +static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; + int ret; + + mutex_lock(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + conf |= IDT821034_CONF_DELAY_MODE; + break; + case SND_SOC_DAIFMT_DSP_B: + conf &= ~IDT821034_CONF_DELAY_MODE; + break; + default: + dev_err(dai->dev, "Unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + ret = -EINVAL; + goto end; + } + ret = idt821034_set_codec_conf(idt821034, conf); +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + u8 conf; + int ret; + + mutex_lock(&idt821034->mutex); + + conf = idt821034_get_codec_conf(idt821034); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_A_LAW: + conf |= IDT821034_CONF_ALAW_MODE; + break; + case SNDRV_PCM_FORMAT_MU_LAW: + conf &= ~IDT821034_CONF_ALAW_MODE; + break; + default: + dev_err(dai->dev, "Unsupported PCM format 0x%x\n", + params_format(params)); + ret = -EINVAL; + goto end; + } + ret = idt821034_set_codec_conf(idt821034, conf); +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static const unsigned int idt821034_sample_bits[] = {8}; + +static struct snd_pcm_hw_constraint_list idt821034_sample_bits_constr = { + .list = idt821034_sample_bits, + .count = ARRAY_SIZE(idt821034_sample_bits), +}; + +static int idt821034_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component); + unsigned int max_ch = 0; + int ret; + + max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + idt821034->max_ch_playback : idt821034->max_ch_capture; + + /* + * Disable stream support (min = 0, max = 0) if no timeslots were + * configured otherwise, limit the number of channels to those + * configured. + */ + ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, + max_ch ? 1 : 0, max_ch); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &idt821034_sample_bits_constr); + if (ret) + return ret; + + return 0; +} + +static u64 idt821034_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_A | + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops idt821034_dai_ops = { + .startup = idt821034_dai_startup, + .hw_params = idt821034_dai_hw_params, + .set_tdm_slot = idt821034_dai_set_tdm_slot, + .set_fmt = idt821034_dai_set_fmt, + .auto_selectable_formats = idt821034_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats), +}; + +static struct snd_soc_dai_driver idt821034_dai_driver = { + .name = "idt821034", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = IDT821034_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = IDT821034_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW, + }, + .ops = &idt821034_dai_ops, +}; + +static int idt821034_reset_audio(struct idt821034 *idt821034) +{ + int ret; + u8 i; + + mutex_lock(&idt821034->mutex); + + ret = idt821034_set_codec_conf(idt821034, 0); + if (ret) + goto end; + + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW; + idt821034->amps.ch[i].amp_out.is_muted = false; + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX, + idt821034->amps.ch[i].amp_out.gain); + if (ret) + goto end; + + idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW; + idt821034->amps.ch[i].amp_in.is_muted = false; + ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX, + idt821034->amps.ch[i].amp_in.gain); + if (ret) + goto end; + + ret = idt821034_set_channel_power(idt821034, i, 0); + if (ret) + goto end; + } + + ret = 0; +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_component_probe(struct snd_soc_component *component) +{ + struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component); + int ret; + + /* reset idt821034 audio part*/ + ret = idt821034_reset_audio(idt821034); + if (ret) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver idt821034_component_driver = { + .probe = idt821034_component_probe, + .controls = idt821034_controls, + .num_controls = ARRAY_SIZE(idt821034_controls), + .dapm_widgets = idt821034_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(idt821034_dapm_widgets), + .dapm_routes = idt821034_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(idt821034_dapm_routes), + .endianness = 1, +}; + +#define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4) +#define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5) + +static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_raw; + int ret; + + mutex_lock(&idt821034->mutex); + + slic_raw = idt821034_get_written_slic_raw(idt821034, ch); + if (val) + slic_raw |= mask; + else + slic_raw &= ~mask; + ret = idt821034_write_slic_raw(idt821034, ch, slic_raw); + if (ret) { + dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); +} + +static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_raw; + int ret; + + mutex_lock(&idt821034->mutex); + ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw); + mutex_unlock(&idt821034->mutex); + if (ret) { + dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + return ret; + } + + /* + * SLIC IOs are read in reverse order compared to write. + * Reverse the read value here in order to have IO0 at lsb (ie same + * order as write) + */ + return !!(bitrev8(slic_raw) & mask); +} + +static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_dir; + + mutex_lock(&idt821034->mutex); + slic_dir = idt821034_get_slic_conf(idt821034, ch); + mutex_unlock(&idt821034->mutex); + + return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT; +} + +static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offset) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_conf; + int ret; + + /* Only IO0 and IO1 can be set as input */ + if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN)) + return -EPERM; + + mutex_lock(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask; + + ret = idt821034_set_slic_conf(idt821034, ch, slic_conf); + if (ret) { + dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val) +{ + u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset); + u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset); + struct idt821034 *idt821034 = gpiochip_get_data(c); + u8 slic_conf; + int ret; + + idt821034_chip_gpio_set(c, offset, val); + + mutex_lock(&idt821034->mutex); + + slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask; + + ret = idt821034_set_slic_conf(idt821034, ch, slic_conf); + if (ret) { + dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n", + offset, ch, mask, ret); + } + + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_reset_gpio(struct idt821034 *idt821034) +{ + int ret; + u8 i; + + mutex_lock(&idt821034->mutex); + + /* IO0 and IO1 as input for all channels and output IO set to 0 */ + for (i = 0; i < IDT821034_NB_CHANNEL; i++) { + ret = idt821034_set_slic_conf(idt821034, i, + IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN); + if (ret) + goto end; + + ret = idt821034_write_slic_raw(idt821034, i, 0); + if (ret) + goto end; + + } + ret = 0; +end: + mutex_unlock(&idt821034->mutex); + return ret; +} + +static int idt821034_gpio_init(struct idt821034 *idt821034) +{ + int ret; + + ret = idt821034_reset_gpio(idt821034); + if (ret) + return ret; + + idt821034->gpio_chip.owner = THIS_MODULE; + idt821034->gpio_chip.label = dev_name(&idt821034->spi->dev); + idt821034->gpio_chip.parent = &idt821034->spi->dev; + idt821034->gpio_chip.base = -1; + idt821034->gpio_chip.ngpio = 5 * 4; /* 5 GPIOs on 4 channels */ + idt821034->gpio_chip.get_direction = idt821034_chip_get_direction; + idt821034->gpio_chip.direction_input = idt821034_chip_direction_input; + idt821034->gpio_chip.direction_output = idt821034_chip_direction_output; + idt821034->gpio_chip.get = idt821034_chip_gpio_get; + idt821034->gpio_chip.set = idt821034_chip_gpio_set; + idt821034->gpio_chip.can_sleep = true; + + return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip, + idt821034); +} + +static int idt821034_spi_probe(struct spi_device *spi) +{ + struct idt821034 *idt821034; + int ret; + + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + idt821034 = devm_kzalloc(&spi->dev, sizeof(*idt821034), GFP_KERNEL); + if (!idt821034) + return -ENOMEM; + + idt821034->spi = spi; + + mutex_init(&idt821034->mutex); + + spi_set_drvdata(spi, idt821034); + + ret = devm_snd_soc_register_component(&spi->dev, &idt821034_component_driver, + &idt821034_dai_driver, 1); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_GPIOLIB)) + return idt821034_gpio_init(idt821034); + + return 0; +} + +static const struct of_device_id idt821034_of_match[] = { + { .compatible = "renesas,idt821034", }, + { } +}; +MODULE_DEVICE_TABLE(of, idt821034_of_match); + +static const struct spi_device_id idt821034_id_table[] = { + { "idt821034", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, idt821034_id_table); + +static struct spi_driver idt821034_spi_driver = { + .driver = { + .name = "idt821034", + .of_match_table = idt821034_of_match, + }, + .id_table = idt821034_id_table, + .probe = idt821034_spi_probe, +}; + +module_spi_driver(idt821034_spi_driver); + +MODULE_AUTHOR("Herve Codina <[email protected]>"); +MODULE_DESCRIPTION("IDT821034 ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 3cd1be743d9e..c9a2d4dabd3c 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -689,10 +689,7 @@ static int max98373_set_sdw_stream(struct snd_soc_dai *dai, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index ca2790d63b71..45544b530d3d 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -508,10 +508,7 @@ static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 45a3eff31915..91edfd5eee08 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -507,10 +507,7 @@ static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index c6ec86e97a6e..c34975f958c4 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -575,10 +575,7 @@ static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 620ecbfa4a7a..7c7cbb6362ea 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3157,7 +3157,7 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse snd_soc_dapm_force_enable_pin(dapm, "LDO2"); snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power"); snd_soc_dapm_sync(dapm); - if (!dapm->card->instantiated) { + if (!snd_soc_card_is_instantiated(dapm->card)) { /* Power up necessary bits for JD if dapm is not ready yet */ regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1, diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 6e66cc218fa8..17afaef85c77 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -1298,7 +1298,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work) usleep_range(10000, 15000); } - while (!rt5665->component->card->instantiated) { + while (!snd_soc_card_is_instantiated(rt5665->component->card)) { pr_debug("%s\n", __func__); usleep_range(10000, 15000); } @@ -4748,7 +4748,7 @@ static void rt5665_calibrate_handler(struct work_struct *work) struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv, calibrate_work.work); - while (!rt5665->component->card->instantiated) { + while (!snd_soc_card_is_instantiated(rt5665->component->card)) { pr_debug("%s\n", __func__); usleep_range(10000, 15000); } diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index beb0951ff680..ecf3b0527dbe 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -1022,8 +1022,8 @@ static void rt5668_jack_detect_handler(struct work_struct *work) container_of(work, struct rt5668_priv, jack_detect_work.work); int val, btn_type; - if (!rt5668->component || !rt5668->component->card || - !rt5668->component->card->instantiated) { + if (!rt5668->component || + !snd_soc_card_is_instantiated(rt5668->component->card)) { /* card not yet ready, try later */ mod_delayed_work(system_power_efficient_wq, &rt5668->jack_detect_work, msecs_to_jiffies(15)); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index d8a573dcb771..5f80a5d59b65 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -107,10 +107,7 @@ static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 7e3eb65afe16..f6c798b65c08 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -1094,8 +1094,8 @@ void rt5682_jack_detect_handler(struct work_struct *work) struct snd_soc_dapm_context *dapm; int val, btn_type; - if (!rt5682->component || !rt5682->component->card || - !rt5682->component->card->instantiated) { + if (!rt5682->component || + !snd_soc_card_is_instantiated(rt5682->component->card)) { /* card not yet ready, try later */ mod_delayed_work(system_power_efficient_wq, &rt5682->jack_detect_work, msecs_to_jiffies(15)); diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index f5e5dbc3b0f0..77dc62219bb8 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -834,8 +834,8 @@ static void rt5682s_jack_detect_handler(struct work_struct *work) struct snd_soc_dapm_context *dapm; int val, btn_type; - if (!rt5682s->component || !rt5682s->component->card || - !rt5682s->component->card->instantiated) { + if (!rt5682s->component || + !snd_soc_card_is_instantiated(rt5682s->component->card)) { /* card not yet ready, try later */ mod_delayed_work(system_power_efficient_wq, &rt5682s->jack_detect_work, msecs_to_jiffies(15)); diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c index 6534c9b51442..659ce26e9f3b 100644 --- a/sound/soc/codecs/rt700.c +++ b/sound/soc/codecs/rt700.c @@ -163,7 +163,7 @@ static void rt700_jack_detect_handler(struct work_struct *work) if (!rt700->hs_jack) return; - if (!rt700->component->card || !rt700->component->card->instantiated) + if (!snd_soc_card_is_instantiated(rt700->component->card)) return; reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT; @@ -887,10 +887,7 @@ static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c index 7cdf184d380b..c65abe812a4c 100644 --- a/sound/soc/codecs/rt711-sdca.c +++ b/sound/soc/codecs/rt711-sdca.c @@ -295,7 +295,7 @@ static void rt711_sdca_jack_detect_handler(struct work_struct *work) if (!rt711->hs_jack) return; - if (!rt711->component->card || !rt711->component->card->instantiated) + if (!snd_soc_card_is_instantiated(rt711->component->card)) return; /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */ @@ -1249,10 +1249,7 @@ static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c index 78e1da9b0738..862f50950565 100644 --- a/sound/soc/codecs/rt711.c +++ b/sound/soc/codecs/rt711.c @@ -243,7 +243,7 @@ static void rt711_jack_detect_handler(struct work_struct *work) if (!rt711->hs_jack) return; - if (!rt711->component->card || !rt711->component->card->instantiated) + if (!snd_soc_card_is_instantiated(rt711->component->card)) return; if (pm_runtime_status_suspended(rt711->slave->dev.parent)) { @@ -976,10 +976,7 @@ static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c index 1fca7a3f46ea..920510365fd7 100644 --- a/sound/soc/codecs/rt715-sdca.c +++ b/sound/soc/codecs/rt715-sdca.c @@ -793,10 +793,7 @@ static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 1a2036ccfbac..c6dd9df7be45 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -777,10 +777,7 @@ static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c index af52f2728854..62b02d764f09 100644 --- a/sound/soc/codecs/sdw-mockup.c +++ b/sound/soc/codecs/sdw-mockup.c @@ -57,10 +57,7 @@ static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, stream->sdw_stream = sdw_stream; /* Use tx_mask or rx_mask to configure stream tag and set dma_data */ - if (direction == SNDRV_PCM_STREAM_PLAYBACK) - dai->playback_dma_data = stream; - else - dai->capture_dma_data = stream; + snd_soc_dai_dma_data_set(dai, direction, stream); return 0; } diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c index 3d8e3900f5c3..fbedba574ff4 100644 --- a/sound/soc/codecs/sma1303.c +++ b/sound/soc/codecs/sma1303.c @@ -1597,8 +1597,7 @@ static struct attribute_group sma1303_attr_group = { .attrs = sma1303_attr, }; -static int sma1303_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sma1303_i2c_probe(struct i2c_client *client) { struct sma1303_priv *sma1303; struct device_node *np = client->dev.of_node; @@ -1791,7 +1790,7 @@ static struct i2c_driver sma1303_i2c_driver = { .name = "sma1303", .of_match_table = sma1303_of_match, }, - .probe = sma1303_i2c_probe, + .probe_new = sma1303_i2c_probe, .remove = sma1303_i2c_remove, .id_table = sma1303_i2c_id, }; diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index fd59b35a62ba..38116c758717 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -1327,15 +1327,13 @@ static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w) int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) { struct sst_data *drv = snd_soc_dai_get_drvdata(dai); - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); struct snd_soc_dapm_path *p; dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream); + dev_dbg(dai->dev, "Stream name=%s\n", w->name); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - dev_dbg(dai->dev, "Stream name=%s\n", - dai->playback_widget->name); - w = dai->playback_widget; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connected && !p->connected(w, p->sink)) continue; @@ -1352,9 +1350,6 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute) } } } else { - dev_dbg(dai->dev, "Stream name=%s\n", - dai->capture_widget->name); - w = dai->capture_widget; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connected && !p->connected(w, p->source)) continue; diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c index 6731d8a49076..b31fa931ba8b 100644 --- a/sound/soc/intel/avs/boards/nau8825.c +++ b/sound/soc/intel/avs/boards/nau8825.c @@ -258,14 +258,15 @@ static int avs_card_resume_post(struct snd_soc_card *card) { struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI); struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card); + int stream = SNDRV_PCM_STREAM_PLAYBACK; if (!codec_dai) { dev_err(card->dev, "Codec dai not found\n"); return -EINVAL; } - if (codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK] && - codec_dai->playback_widget->active) + if (snd_soc_dai_stream_active(codec_dai, stream) && + snd_soc_dai_get_widget(codec_dai, stream)->active) snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0, SND_SOC_CLOCK_IN); return snd_soc_component_set_jack(codec_dai->component, jack, NULL); diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index c4376c9c35ef..211edd51a896 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -35,15 +35,13 @@ struct avs_dma_data { static struct avs_tplg_path_template * avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction) { - struct snd_soc_dapm_widget *dw; + struct snd_soc_dapm_widget *dw = snd_soc_dai_get_widget(dai, direction); struct snd_soc_dapm_path *dp; enum snd_soc_dapm_direction dir; if (direction == SNDRV_PCM_STREAM_CAPTURE) { - dw = dai->capture_widget; dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN; } else { - dw = dai->playback_widget; dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT; } @@ -929,7 +927,7 @@ static int avs_component_pm_op(struct snd_soc_component *component, bool be, int ret; for_each_component_dais(component, dai) { - data = dai->playback_dma_data; + data = snd_soc_dai_dma_data_get_playback(dai); if (data) { rtd = asoc_substream_to_rtd(data->substream); if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { @@ -942,7 +940,7 @@ static int avs_component_pm_op(struct snd_soc_component *component, bool be, } } - data = dai->capture_dma_data; + data = snd_soc_dai_dma_data_get_capture(dai); if (data) { rtd = asoc_substream_to_rtd(data->substream); if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) { @@ -1291,11 +1289,14 @@ static void avs_component_hda_unregister_dais(struct snd_soc_component *componen sprintf(name, "%s-cpu", dev_name(&codec->core.dev)); for_each_component_dais_safe(component, dai, save) { + int stream; + if (!strstr(dai->driver->name, name)) continue; - snd_soc_dapm_free_widget(dai->playback_widget); - snd_soc_dapm_free_widget(dai->capture_widget); + for_each_pcm_streams(stream) + snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream)); + snd_soc_unregister_dai(dai); } } diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b20643b83401..96cfebded072 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -1663,11 +1663,10 @@ int skl_tplg_update_pipe_params(struct device *dev, struct skl_module_cfg * skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) { - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); struct snd_soc_dapm_path *p = NULL; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - w = dai->playback_widget; snd_soc_dapm_widget_for_each_sink_path(w, p) { if (p->connect && p->sink->power && !is_skl_dsp_widget_type(p->sink, dai->dev)) @@ -1680,7 +1679,6 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream) } } } else { - w = dai->capture_widget; snd_soc_dapm_widget_for_each_source_path(w, p) { if (p->connect && p->source->power && !is_skl_dsp_widget_type(p->source, dai->dev)) @@ -1744,14 +1742,12 @@ static struct skl_module_cfg *skl_get_mconfig_cap_cpr( struct skl_module_cfg * skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream) { - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); struct skl_module_cfg *mconfig; if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - w = dai->playback_widget; mconfig = skl_get_mconfig_pb_cpr(dai, w); } else { - w = dai->capture_widget; mconfig = skl_get_mconfig_cap_cpr(dai, w); } return mconfig; @@ -1905,20 +1901,13 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, int skl_tplg_be_update_params(struct snd_soc_dai *dai, struct skl_pipe_params *params) { - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, params->stream); if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) { - w = dai->playback_widget; - return skl_tplg_be_set_src_pipe_params(dai, w, params); - } else { - w = dai->capture_widget; - return skl_tplg_be_set_sink_pipe_params(dai, w, params); } - - return 0; } static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = { @@ -2978,7 +2967,7 @@ void skl_cleanup_resources(struct skl_dev *skl) return; card = soc_component->card; - if (!card || !card->instantiated) + if (!snd_soc_card_is_instantiated(card)) return; list_for_each_entry(w, &card->widgets, list) { diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c index 8b1b623207be..6fef16306f74 100644 --- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c +++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c @@ -32,7 +32,7 @@ int mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, continue; for_each_rtd_cpu_dais(runtime, j, cpu_dai) { - if (cpu_dai->stream_active[conn->stream_dir] > 0) { + if (snd_soc_dai_stream_active(cpu_dai, conn->stream_dir) > 0) { sof_dai_link = runtime->dai_link; break; } @@ -111,21 +111,17 @@ int mtk_sof_card_late_probe(struct snd_soc_card *card) for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) { struct snd_soc_dapm_route route; struct snd_soc_dapm_path *p = NULL; - struct snd_soc_dapm_widget *play_widget = - cpu_dai->playback_widget; - struct snd_soc_dapm_widget *cap_widget = - cpu_dai->capture_widget; + struct snd_soc_dapm_widget *widget = snd_soc_dai_get_widget(cpu_dai, conn->stream_dir); + memset(&route, 0, sizeof(route)); - if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && - cap_widget) { - snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) { + if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && widget) { + snd_soc_dapm_widget_for_each_sink_path(widget, p) { route.source = conn->sof_dma; route.sink = p->sink->name; snd_soc_dapm_add_routes(&card->dapm, &route, 1); } - } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && - play_widget) { - snd_soc_dapm_widget_for_each_source_path(play_widget, p) { + } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && widget) { + snd_soc_dapm_widget_for_each_source_path(widget, p) { route.source = p->source->name; route.sink = conn->sof_dma; snd_soc_dapm_add_routes(&card->dapm, &route, 1); diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c index 51f736f319e4..8a309b0734f7 100644 --- a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c @@ -183,6 +183,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); unsigned int rate = params_rate(params); unsigned int rate_reg = mt6797_rate_transform(afe->dev, rate, dai->id); unsigned int pcm_con = 0; @@ -193,10 +195,10 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, substream->stream, rate, rate_reg, - dai->playback_widget->active, - dai->capture_widget->active); + p->active, + c->active); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; switch (dai->id) { diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c index 38ce0e36cdb4..4e25287fc0e4 100644 --- a/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c +++ b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c @@ -183,6 +183,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); unsigned int rate = params_rate(params); unsigned int rate_reg = mt8183_rate_transform(afe->dev, rate, dai->id); unsigned int pcm_con = 0; @@ -193,10 +195,9 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, substream->stream, rate, rate_reg, - dai->playback_widget->active, - dai->capture_widget->active); + p->active, c->active); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; switch (dai->id) { diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c b/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c index 41221a66111c..a50aa294960b 100644 --- a/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c +++ b/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c @@ -218,6 +218,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); struct mt8186_afe_private *afe_priv = afe->platform_priv; + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); int pcm_id = dai->id; struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[pcm_id]; unsigned int rate = params_rate(params); @@ -230,12 +232,11 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, unsigned int pcm_con = 0; dev_dbg(afe->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n", - __func__, dai->id, substream->stream, dai->playback_widget->active, - dai->capture_widget->active); + __func__, dai->id, substream->stream, p->active, c->active); dev_dbg(afe->dev, "%s(), rate %d, rate_reg %d, data_width %d, wlen_width %d\n", __func__, rate, rate_reg, data_width, wlen_width); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; switch (dai->id) { diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c b/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c index 3f1696dcf81c..5bc854a8f3df 100644 --- a/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c +++ b/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c @@ -227,7 +227,8 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - if (dai->playback_widget->active || dai->capture_widget->active) + if (snd_soc_dai_get_widget_playback(dai)->active || + snd_soc_dai_get_widget_capture(dai)->active) return 0; return mtk_dai_pcm_configure(substream, dai); diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c b/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c index 239e3f5b53d3..2847a2e747be 100644 --- a/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c +++ b/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c @@ -273,6 +273,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai); + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); unsigned int rate = params_rate(params); unsigned int rate_reg = mt8192_rate_transform(afe->dev, rate, dai->id); unsigned int pcm_con = 0; @@ -283,10 +285,10 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream, substream->stream, rate, rate_reg, - dai->playback_widget->active, - dai->capture_widget->active); + p->active, + c->active); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; switch (dai->id) { diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c index caceb0deb467..051433689ff5 100644 --- a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c +++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c @@ -213,11 +213,14 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream, static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { + struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai); + struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai); + dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n", __func__, dai->id, substream->stream, - dai->playback_widget->active, dai->capture_widget->active); + p->active, c->active); - if (dai->playback_widget->active || dai->capture_widget->active) + if (p->active || c->active) return 0; return mtk_dai_pcm_configure(substream, dai); diff --git a/sound/soc/meson/aiu-fifo-i2s.c b/sound/soc/meson/aiu-fifo-i2s.c index 57e6e7160d2f..59e00a74b5f8 100644 --- a/sound/soc/meson/aiu-fifo-i2s.c +++ b/sound/soc/meson/aiu-fifo-i2s.c @@ -88,7 +88,7 @@ static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); unsigned int val; int ret; @@ -158,7 +158,7 @@ int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai) if (ret) return ret; - fifo = dai->playback_dma_data; + fifo = snd_soc_dai_dma_data_get_playback(dai); fifo->pcm = &fifo_i2s_pcm; fifo->mem_offset = AIU_MEM_I2S_START; diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c index 2fb30f89bf7a..ddbd2fc40185 100644 --- a/sound/soc/meson/aiu-fifo-spdif.c +++ b/sound/soc/meson/aiu-fifo-spdif.c @@ -173,7 +173,7 @@ int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai) if (ret) return ret; - fifo = dai->playback_dma_data; + fifo = snd_soc_dai_dma_data_get_playback(dai); fifo->pcm = &fifo_spdif_pcm; fifo->mem_offset = AIU_MEM_IEC958_START; diff --git a/sound/soc/meson/aiu-fifo.c b/sound/soc/meson/aiu-fifo.c index d67ff4cdabd5..543d41856c12 100644 --- a/sound/soc/meson/aiu-fifo.c +++ b/sound/soc/meson/aiu-fifo.c @@ -34,7 +34,7 @@ snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_dai *dai = aiu_fifo_dai(substream); - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int addr; @@ -46,7 +46,7 @@ snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component, static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable) { struct snd_soc_component *component = dai->component; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN | AIU_MEM_CONTROL_EMPTY_EN); @@ -80,7 +80,7 @@ int aiu_fifo_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); snd_soc_component_update_bits(component, fifo->mem_offset + AIU_MEM_CONTROL, @@ -98,7 +98,7 @@ int aiu_fifo_hw_params(struct snd_pcm_substream *substream, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_component *component = dai->component; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); dma_addr_t end; /* Setup the fifo boundaries */ @@ -132,7 +132,7 @@ static irqreturn_t aiu_fifo_isr(int irq, void *dev_id) int aiu_fifo_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); int ret; snd_soc_set_runtime_hwparams(substream, fifo->pcm); @@ -168,7 +168,7 @@ int aiu_fifo_startup(struct snd_pcm_substream *substream, void aiu_fifo_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); free_irq(fifo->irq, substream); clk_disable_unprepare(fifo->pclk); @@ -178,7 +178,7 @@ int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { struct snd_card *card = rtd->card->snd_card; - struct aiu_fifo *fifo = dai->playback_dma_data; + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); size_t size = fifo->pcm->buffer_bytes_max; int ret; @@ -200,15 +200,16 @@ int aiu_fifo_dai_probe(struct snd_soc_dai *dai) if (!fifo) return -ENOMEM; - dai->playback_dma_data = fifo; + snd_soc_dai_dma_data_set_playback(dai, fifo); return 0; } int aiu_fifo_dai_remove(struct snd_soc_dai *dai) { - kfree(dai->playback_dma_data); + struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai); + + kfree(fifo); return 0; } - diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c index c040c83637e0..7624aafe9009 100644 --- a/sound/soc/meson/axg-tdm-interface.c +++ b/sound/soc/meson/axg-tdm-interface.c @@ -37,10 +37,8 @@ int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask, unsigned int slot_width) { struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); - struct axg_tdm_stream *tx = (struct axg_tdm_stream *) - dai->playback_dma_data; - struct axg_tdm_stream *rx = (struct axg_tdm_stream *) - dai->capture_dma_data; + struct axg_tdm_stream *tx = snd_soc_dai_dma_data_get_playback(dai); + struct axg_tdm_stream *rx = snd_soc_dai_dma_data_get_capture(dai); unsigned int tx_slots, rx_slots; unsigned int fmt = 0; @@ -362,11 +360,14 @@ static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream, static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) { - if (dai->capture_dma_data) - axg_tdm_stream_free(dai->capture_dma_data); + int stream; - if (dai->playback_dma_data) - axg_tdm_stream_free(dai->playback_dma_data); + for_each_pcm_streams(stream) { + struct axg_tdm_stream *ts = snd_soc_dai_dma_data_get(dai, stream); + + if (ts) + axg_tdm_stream_free(ts); + } return 0; } @@ -374,19 +375,20 @@ static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai) static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai) { struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai); + int stream; - if (dai->capture_widget) { - dai->capture_dma_data = axg_tdm_stream_alloc(iface); - if (!dai->capture_dma_data) - return -ENOMEM; - } + for_each_pcm_streams(stream) { + struct axg_tdm_stream *ts; + + if (!snd_soc_dai_get_widget(dai, stream)) + continue; - if (dai->playback_widget) { - dai->playback_dma_data = axg_tdm_stream_alloc(iface); - if (!dai->playback_dma_data) { + ts = axg_tdm_stream_alloc(iface); + if (!ts) { axg_tdm_iface_remove_dai(dai); return -ENOMEM; } + snd_soc_dai_dma_data_set(dai, stream, ts); } return 0; diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c index 49b613a1faf2..c8f6ea24ae78 100644 --- a/sound/soc/meson/axg-tdmin.c +++ b/sound/soc/meson/axg-tdmin.c @@ -83,7 +83,7 @@ axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w) if (!be) return NULL; - return be->capture_dma_data; + return snd_soc_dai_dma_data_get_capture(be); } static void axg_tdmin_enable(struct regmap *map) diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c index 22d519fc07b2..c4039e4f0847 100644 --- a/sound/soc/meson/axg-tdmout.c +++ b/sound/soc/meson/axg-tdmout.c @@ -81,7 +81,7 @@ axg_tdmout_get_tdm_stream(struct snd_soc_dapm_widget *w) if (!be) return NULL; - return be->playback_dma_data; + return snd_soc_dai_dma_data_get_playback(be); } static void axg_tdmout_enable(struct regmap *map) diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c index 80c5ed196961..5913486c43ab 100644 --- a/sound/soc/meson/meson-codec-glue.c +++ b/sound/soc/meson/meson-codec-glue.c @@ -39,13 +39,13 @@ meson_codec_glue_get_input(struct snd_soc_dapm_widget *w) static void meson_codec_glue_input_set_data(struct snd_soc_dai *dai, struct meson_codec_glue_input *data) { - dai->playback_dma_data = data; + snd_soc_dai_dma_data_set_playback(dai, data); } struct meson_codec_glue_input * meson_codec_glue_input_get_data(struct snd_soc_dai *dai) { - return dai->playback_dma_data; + return snd_soc_dai_dma_data_get_playback(dai); } EXPORT_SYMBOL_GPL(meson_codec_glue_input_get_data); @@ -99,8 +99,8 @@ int meson_codec_glue_output_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct meson_codec_glue_input *in_data = - meson_codec_glue_output_get_input_data(dai->capture_widget); + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget_capture(dai); + struct meson_codec_glue_input *in_data = meson_codec_glue_output_get_input_data(w); if (!in_data) return -ENODEV; diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 08b90ec5cc80..166257c6ae14 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -1070,9 +1070,9 @@ static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai) struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai); if (i2s_tdm->has_capture) - dai->capture_dma_data = &i2s_tdm->capture_dma_data; + snd_soc_dai_dma_data_set_capture(dai, &i2s_tdm->capture_dma_data); if (i2s_tdm->has_playback) - dai->playback_dma_data = &i2s_tdm->playback_dma_data; + snd_soc_dai_dma_data_set_playback(dai, &i2s_tdm->playback_dma_data); if (i2s_tdm->mclk_calibrate) snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1); diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index 5b1e47bdc376..6ce92b1db790 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -373,7 +373,7 @@ static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai) { struct rk_pdm_dev *pdm = to_info(dai); - dai->capture_dma_data = &pdm->capture_dma_data; + snd_soc_dai_dma_data_set_capture(dai, &pdm->capture_dma_data); return 0; } diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 5b4f00457587..2d937fcf357d 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -196,7 +196,7 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai) { struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai); - dai->playback_dma_data = &spdif->playback_dma_data; + snd_soc_dai_dma_data_set_playback(dai, &spdif->playback_dma_data); return 0; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index e613698824fd..71b022f7edfd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -553,7 +553,7 @@ int snd_soc_suspend(struct device *dev) int i; /* If the card is not initialized yet there is nothing to do */ - if (!card->instantiated) + if (!snd_soc_card_is_instantiated(card)) return 0; /* @@ -695,7 +695,7 @@ int snd_soc_resume(struct device *dev) struct snd_soc_component *component; /* If the card is not initialized yet there is nothing to do */ - if (!card->instantiated) + if (!snd_soc_card_is_instantiated(card)) return 0; /* activate pins from sleep state */ @@ -1915,7 +1915,7 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card) static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister) { - if (card->instantiated) { + if (snd_soc_card_is_instantiated(card)) { card->instantiated = false; snd_soc_flush_all_delayed_work(card); @@ -2126,7 +2126,7 @@ int snd_soc_poweroff(struct device *dev) struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_component *component; - if (!card->instantiated) + if (!snd_soc_card_is_instantiated(card)) return 0; /* diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 29a75fdf90e0..0119afbd01fc 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -267,6 +267,11 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { int ret = -ENOTSUPP; + int stream; + unsigned int *tdm_mask[] = { + &tx_mask, + &rx_mask, + }; if (dai->driver->ops && dai->driver->ops->xlate_tdm_slot_mask) @@ -275,8 +280,8 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, else snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask); - dai->tx_mask = tx_mask; - dai->rx_mask = rx_mask; + for_each_pcm_streams(stream) + snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]); if (dai->driver->ops && dai->driver->ops->set_tdm_slot) @@ -509,7 +514,7 @@ void snd_soc_dai_action(struct snd_soc_dai *dai, int stream, int action) { /* see snd_soc_dai_stream_active() */ - dai->stream_active[stream] += action; + dai->stream[stream].active += action; /* see snd_soc_component_active() */ dai->component->active += action; @@ -522,7 +527,7 @@ int snd_soc_dai_active(struct snd_soc_dai *dai) active = 0; for_each_pcm_streams(stream) - active += dai->stream_active[stream]; + active += dai->stream[stream].active; return active; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f61c8633e7eb..5d9a671e50f1 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -149,7 +149,7 @@ static int dapm_down_seq[] = { static void dapm_assert_locked(struct snd_soc_dapm_context *dapm) { - if (dapm->card && dapm->card->instantiated) + if (snd_soc_card_is_instantiated(dapm->card)) lockdep_assert_held(&dapm->card->dapm_mutex); } @@ -1297,7 +1297,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, enum snd_soc_dapm_direction)) { struct snd_soc_card *card = dai->component->card; - struct snd_soc_dapm_widget *w; + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream); LIST_HEAD(widgets); int paths; int ret; @@ -1305,12 +1305,10 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - w = dai->playback_widget; invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT); paths = is_connected_output_ep(w, &widgets, custom_stop_condition); } else { - w = dai->capture_widget; invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN); paths = is_connected_input_ep(w, &widgets, custom_stop_condition); @@ -2614,7 +2612,7 @@ int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm) * Suppress early reports (eg, jacks syncing their state) to avoid * silly DAPM runs during card startup. */ - if (!dapm->card || !dapm->card->instantiated) + if (!snd_soc_card_is_instantiated(dapm->card)) return 0; return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP); @@ -2908,7 +2906,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm, dapm_mark_dirty(path->node[dir], "Route added"); } - if (dapm->card->instantiated && path->connect) + if (snd_soc_card_is_instantiated(dapm->card) && path->connect) dapm_path_invalidate(path); return 0; @@ -4229,7 +4227,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, return PTR_ERR(w); w->priv = dai; - dai->playback_widget = w; + snd_soc_dai_set_widget_playback(dai, w); } if (dai->driver->capture.stream_name) { @@ -4245,7 +4243,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, return PTR_ERR(w); w->priv = dai; - dai->capture_widget = w; + snd_soc_dai_set_widget_capture(dai, w); } return 0; @@ -4339,16 +4337,16 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, int stream; if (dai_link->params) { - playback_cpu = cpu_dai->capture_widget; - capture_cpu = cpu_dai->playback_widget; + playback_cpu = snd_soc_dai_get_widget_capture(cpu_dai); + capture_cpu = snd_soc_dai_get_widget_playback(cpu_dai); } else { - playback_cpu = cpu_dai->playback_widget; - capture_cpu = cpu_dai->capture_widget; + playback_cpu = snd_soc_dai_get_widget_playback(cpu_dai); + capture_cpu = snd_soc_dai_get_widget_capture(cpu_dai); } /* connect BE DAI playback if widgets are valid */ stream = SNDRV_PCM_STREAM_PLAYBACK; - codec = codec_dai->playback_widget; + codec = snd_soc_dai_get_widget(codec_dai, stream); if (playback_cpu && codec) { if (dai_link->params && !rtd->c2c_widget[stream]) { @@ -4367,7 +4365,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card, capture: /* connect BE DAI capture if widgets are valid */ stream = SNDRV_PCM_STREAM_CAPTURE; - codec = codec_dai->capture_widget; + codec = snd_soc_dai_get_widget(codec_dai, stream); if (codec && capture_cpu) { if (dai_link->params && !rtd->c2c_widget[stream]) { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index f6caa55ef322..005b179a770a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1012,6 +1012,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, for_each_rtd_codec_dais(rtd, i, codec_dai) { struct snd_pcm_hw_params codec_params; + unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream); /* * Skip CODECs which don't support the current stream type, @@ -1034,15 +1035,8 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd, codec_params = *params; /* fixup params based on TDM slot masks */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - codec_dai->tx_mask) - soc_pcm_codec_params_fixup(&codec_params, - codec_dai->tx_mask); - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && - codec_dai->rx_mask) - soc_pcm_codec_params_fixup(&codec_params, - codec_dai->rx_mask); + if (tdm_mask) + soc_pcm_codec_params_fixup(&codec_params, tdm_mask); ret = snd_soc_dai_hw_params(codec_dai, substream, &codec_params); diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index e66b0d9e387a..caf547816ea7 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -1436,7 +1436,7 @@ widget: /* card dapm mutex is held by the core if we are loading topology * data during sound card init. */ - if (card->instantiated) + if (snd_soc_card_is_instantiated(card)) widget = snd_soc_dapm_new_control(dapm, &template); else widget = snd_soc_dapm_new_control_unlocked(dapm, &template); @@ -1525,7 +1525,7 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg) /* Card might not have been registered at this point. * If so, just return success. */ - if (!card || !card->instantiated) { + if (!snd_soc_card_is_instantiated(card)) { dev_warn(tplg->dev, "ASoC: Parent card not yet available," " widget card binding deferred\n"); return 0; diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index e2f8cd9e278e..3cfdf782afca 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1055,6 +1055,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *cpu_dai; + int stream; int i; if (!w->sname) { @@ -1062,62 +1063,41 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, return -EINVAL; } + if (w->id == snd_soc_dapm_dai_out) + stream = SNDRV_PCM_STREAM_CAPTURE; + if (w->id == snd_soc_dapm_dai_in) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + goto end; + list_for_each_entry(rtd, &card->rtd_list, list) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || strcmp(w->sname, rtd->dai_link->stream_name)) continue; - switch (w->id) { - case snd_soc_dapm_dai_out: - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - /* - * Please create DAI widget in the right order - * to ensure BE will connect to the right DAI - * widget. - */ - if (!cpu_dai->capture_widget) { - cpu_dai->capture_widget = w; - break; - } - } - if (i == rtd->dai_link->num_cpus) { - dev_err(scomp->dev, "error: can't find BE for DAI %s\n", - w->name); - - return -EINVAL; - } - dai->name = rtd->dai_link->name; - dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", - w->name, rtd->dai_link->name); - break; - case snd_soc_dapm_dai_in: - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - /* - * Please create DAI widget in the right order - * to ensure BE will connect to the right DAI - * widget. - */ - if (!cpu_dai->playback_widget) { - cpu_dai->playback_widget = w; - break; - } + for_each_rtd_cpu_dais(rtd, i, cpu_dai) { + /* + * Please create DAI widget in the right order + * to ensure BE will connect to the right DAI + * widget. + */ + if (!snd_soc_dai_get_widget(cpu_dai, stream)) { + snd_soc_dai_set_widget(cpu_dai, stream, w); + break; } - if (i == rtd->dai_link->num_cpus) { - dev_err(scomp->dev, "error: can't find BE for DAI %s\n", - w->name); + } + if (i == rtd->dai_link->num_cpus) { + dev_err(scomp->dev, "error: can't find BE for DAI %s\n", w->name); - return -EINVAL; - } - dai->name = rtd->dai_link->name; - dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", - w->name, rtd->dai_link->name); - break; - default: - break; + return -EINVAL; } - } + dai->name = rtd->dai_link->name; + dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n", + w->name, rtd->dai_link->name); + } +end: /* check we have a connection */ if (!dai->name) { dev_err(scomp->dev, "error: can't connect DAI %s stream %s\n", @@ -1134,37 +1114,29 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_card *card = scomp->card; struct snd_soc_pcm_runtime *rtd; struct snd_soc_dai *cpu_dai; - int i; + int i, stream; if (!w->sname) return; + if (w->id == snd_soc_dapm_dai_out) + stream = SNDRV_PCM_STREAM_CAPTURE; + else if (w->id == snd_soc_dapm_dai_in) + stream = SNDRV_PCM_STREAM_PLAYBACK; + else + return; + list_for_each_entry(rtd, &card->rtd_list, list) { /* does stream match DAI link ? */ if (!rtd->dai_link->stream_name || strcmp(w->sname, rtd->dai_link->stream_name)) continue; - switch (w->id) { - case snd_soc_dapm_dai_out: - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - if (cpu_dai->capture_widget == w) { - cpu_dai->capture_widget = NULL; - break; - } + for_each_rtd_cpu_dais(rtd, i, cpu_dai) + if (snd_soc_dai_get_widget(cpu_dai, stream) == w) { + snd_soc_dai_set_widget(cpu_dai, stream, NULL); + break; } - break; - case snd_soc_dapm_dai_in: - for_each_rtd_cpu_dais(rtd, i, cpu_dai) { - if (cpu_dai->playback_widget == w) { - cpu_dai->playback_widget = NULL; - break; - } - } - break; - default: - break; - } } } diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c index fb107c5790ad..469373d1bb41 100644 --- a/sound/soc/spear/spdif_out.c +++ b/sound/soc/spear/spdif_out.c @@ -244,7 +244,8 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai) struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai); host->dma_params_tx.filter_data = &host->dma_params; - dai->playback_dma_data = &host->dma_params_tx; + + snd_soc_dai_dma_data_set_playback(dai, &host->dma_params_tx); return snd_soc_add_dai_controls(dai, spdif_out_controls, ARRAY_SIZE(spdif_out_controls)); diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c index 87facfbcdd11..d23d88a10899 100644 --- a/sound/soc/tegra/tegra20_ac97.c +++ b/sound/soc/tegra/tegra20_ac97.c @@ -212,8 +212,8 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai) { struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); - dai->capture_dma_data = &ac97->capture_dma_data; - dai->playback_dma_data = &ac97->playback_dma_data; + snd_soc_dai_init_dma_data(dai, &ac97->playback_dma_data, + &ac97->capture_dma_data); return 0; } diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index fff0cd6588f5..d37a9f2603e8 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -256,8 +256,8 @@ static int tegra20_i2s_probe(struct snd_soc_dai *dai) { struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); - dai->capture_dma_data = &i2s->capture_dma_data; - dai->playback_dma_data = &i2s->playback_dma_data; + snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, + &i2s->capture_dma_data); return 0; } diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index ca7b222e07d0..86bef54dfdf2 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -236,8 +236,7 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai) { struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev); - dai->capture_dma_data = NULL; - dai->playback_dma_data = &spdif->playback_dma_data; + snd_soc_dai_init_dma_data(dai, &spdif->playback_dma_data, NULL); return 0; } diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c index 1a2e868a6220..100a2b6e6063 100644 --- a/sound/soc/tegra/tegra210_admaif.c +++ b/sound/soc/tegra/tegra210_admaif.c @@ -544,8 +544,8 @@ static int tegra_admaif_dai_probe(struct snd_soc_dai *dai) { struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); - dai->capture_dma_data = &admaif->capture_dma_data[dai->id]; - dai->playback_dma_data = &admaif->playback_dma_data[dai->id]; + snd_soc_dai_init_dma_data(dai, &admaif->playback_dma_data[dai->id], + &admaif->capture_dma_data[dai->id]); return 0; } diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 10cd37096fb3..c26f960c6afd 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -297,8 +297,8 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai) { struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); - dai->capture_dma_data = &i2s->capture_dma_data; - dai->playback_dma_data = &i2s->playback_dma_data; + snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, + &i2s->capture_dma_data); return 0; } diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c index e6e77a5f3c1e..3ccd0cfca008 100644 --- a/sound/soc/ti/davinci-i2s.c +++ b/sound/soc/ti/davinci-i2s.c @@ -614,9 +614,10 @@ static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + int stream; - dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + for_each_pcm_streams(stream) + snd_soc_dai_dma_data_set(dai, stream, &dev->dma_data[stream]); return 0; } diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index f5ac2ab77f5b..578254549d2d 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -1699,9 +1699,10 @@ static void davinci_mcasp_init_iec958_status(struct davinci_mcasp *mcasp) static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + int stream; - dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + for_each_pcm_streams(stream) + snd_soc_dai_dma_data_set(dai, stream, &mcasp->dma_data[stream]); if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) { davinci_mcasp_init_iec958_status(mcasp); diff --git a/sound/soc/ti/davinci-vcif.c b/sound/soc/ti/davinci-vcif.c index 36fa97e2b9e2..fc16b3b8f71a 100644 --- a/sound/soc/ti/davinci-vcif.c +++ b/sound/soc/ti/davinci-vcif.c @@ -161,9 +161,10 @@ static const struct snd_soc_dai_ops davinci_vcif_dai_ops = { static int davinci_vcif_dai_probe(struct snd_soc_dai *dai) { struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai); + int stream; - dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + for_each_pcm_streams(stream) + snd_soc_dai_dma_data_set(dai, stream, &dev->dma_data[stream]); return 0; } |