diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-09-17 17:03:43 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-09-17 17:03:43 +0200 |
commit | 2f27fce67173bbb05d5a0ee03dae5c021202c912 (patch) | |
tree | 7436f7da47c77cca422ac25ff3345dbe744b0ab2 /sound/soc/sh | |
parent | 194fcd20ebccbc34bba80d7d9b203920087bb01d (diff) | |
parent | 64c0ce555ad2d84f497f5f584ddd31e87ac690a2 (diff) |
Merge tag 'sound-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"A fairly big update at this time, both in core and driver sides.
The core received rewrites in PCM buffer allocation handling and
locking optimizations, PCM rate updates followed by lots of cleanups.
In ASoC side, the legacy Intel drivers have been deprecated by AVS
drivers which leaded to the significant amount of code reduction.
SoundWire driver updates and other cleanups contributed more code
reduction, too.
USB-audio driver received a large cleanup of its big quirk table, and
the old snd_print*() API usages in many legacy drivers are replaced
with the standard print API.
Here are some highlights:
Core:
- More optimized locking in ALSA control code
- Rewrites of memalloc helpers for better DMA API usage
- Drop of obsoleted vmalloc PCM buffer helper API
- Continued MIDI2 UMP updates
- Support of a new user-space driven timer instance
- Update for more PCM support rates and cleanups
- Xrun counter report in the proc files
ASoC:
- Continued simplification and cleanup works for ASoC
- Extensive cleanups and refactoring of the Soundwire drivers
- Removal of Intel machine support obsoleted by the AVS driver
- Lots of DT schema conversions
- Machine support for many AMD and Intel x86 platforms
- Support for AMD ACP 7.1, Mediatek MT6367 and MT8365, Realtek
RTL1320 SoundWire and rev C, and Texas Instruments TAS2563
USB-audio:
- Add support of multiple control interfaces
- A large rewrite of quirk table with macros
- Support for RME Digiface USB
HD-audio:
- Cleanup of quirk code for Samsung Galaxy laptops
- Clean up of detection of Cirrus codecs
- C-Media CM9825 HD-audio codec support
Others:
- Rewrites to standard print API in a lot of legacy drivers"
* tag 'sound-6.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (410 commits)
ASoC: topology: Fix redundant logical jump
ASoC: tas2781: Add Calibration Kcontrols for Chromebook
ASoC: amd: acp: refactor SoundWire machine driver code
ASoC: sdw_utils/intel: move soundwire endpoint parsing helper functions
ASoC: sdw_util/intel: move soundwire endpoint and dai link structures
ASoC: intel: sof_sdw: rename soundwire parsing helper functions
ASoC: intel: sof_sdw: rename soundwire endpoint and dailink structures
ASoC: atmel: mchp-pdmc: Retain Non-Runtime Controls
ALSA: hda/realtek: Add support for Galaxy Book2 Pro (NP950XEE)
ASoC: mediatek: mt7986-afe-pcm: Remove redundant error message
ALSA: memalloc: Use proper DMA mapping API for x86 S/G buffer allocations
ALSA: memalloc: Use proper DMA mapping API for x86 WC buffer allocations
ALSA: usb-audio: Add logitech Audio profile quirk
ASoc: mediatek: mt8365: Remove unneeded assignment
ASoC: Intel: ARL: Add entry for HDMI-In capture support to non-I2S codec boards.
ASoC: Intel: sof_rt5682: Add HDMI-In capture with rt5682 support for ARL.
ASoC: SOF: Intel: hda: remove common_hdmi_codec_drv
ASoC: Intel: sof_pcm512x: do not check common_hdmi_codec_drv
ASoC: Intel: ehl_rt5660: do not check common_hdmi_codec_drv
ASoC: Intel: skl_hda_dsp_generic: use common module for DAI links
...
Diffstat (limited to 'sound/soc/sh')
-rw-r--r-- | sound/soc/sh/Kconfig | 1 | ||||
-rw-r--r-- | sound/soc/sh/fsi.c | 2 | ||||
-rw-r--r-- | sound/soc/sh/hac.c | 2 | ||||
-rw-r--r-- | sound/soc/sh/rcar/adg.c | 4 | ||||
-rw-r--r-- | sound/soc/sh/rcar/core.c | 19 | ||||
-rw-r--r-- | sound/soc/sh/rcar/dma.c | 75 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 10 | ||||
-rw-r--r-- | sound/soc/sh/rcar/ssi.c | 2 | ||||
-rw-r--r-- | sound/soc/sh/rz-ssi.c | 259 | ||||
-rw-r--r-- | sound/soc/sh/siu_dai.c | 2 |
10 files changed, 198 insertions, 178 deletions
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 7bddfd5e38d6..426632996a0a 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -41,6 +41,7 @@ config SND_SOC_RCAR depends on COMMON_CLK depends on OF select SND_SIMPLE_CARD_UTILS + select SND_DMAENGINE_PCM select REGMAP_MMIO help This option enables R-Car SRU/SCU/SSIU/SSI sound support diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 087e379aa3bc..221ce91f1950 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -2107,7 +2107,7 @@ static struct platform_driver fsi_driver = { .of_match_table = fsi_of_match, }, .probe = fsi_probe, - .remove_new = fsi_remove, + .remove = fsi_remove, .id_table = fsi_id_table, }; diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c index cc200f45826c..db618c09d1e0 100644 --- a/sound/soc/sh/hac.c +++ b/sound/soc/sh/hac.c @@ -334,7 +334,7 @@ static struct platform_driver hac_pcm_driver = { }, .probe = hac_soc_platform_probe, - .remove_new = hac_soc_platform_remove, + .remove = hac_soc_platform_remove, }; module_platform_driver(hac_pcm_driver); diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index afd69c6eb654..0f190abf00e7 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -262,7 +262,7 @@ int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, int id = rsnd_mod_id(src_mod); int shift = (id % 2) ? 16 : 0; - rsnd_mod_confirm_src(src_mod); + rsnd_mod_make_sure(src_mod, RSND_MOD_SRC); rsnd_adg_get_timesel_ratio(priv, io, in_rate, out_rate, @@ -291,7 +291,7 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) int shift = (id % 4) * 8; u32 mask = 0xFF << shift; - rsnd_mod_confirm_ssi(ssi_mod); + rsnd_mod_make_sure(ssi_mod, RSND_MOD_SSI); val = val << shift; diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 63b3c8bf0fde..9784718a2b6f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -660,23 +660,6 @@ static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) return rsnd_rdai_get(priv, dai->id); } -/* - * rsnd_soc_dai functions - */ -void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io) -{ - struct snd_pcm_substream *substream = io->substream; - - /* - * this function should be called... - * - * - if rsnd_dai_pointer_update() returns true - * - without spin lock - */ - - snd_pcm_period_elapsed(substream); -} - static void rsnd_dai_stream_init(struct rsnd_dai_stream *io, struct snd_pcm_substream *substream) { @@ -2104,7 +2087,7 @@ static struct platform_driver rsnd_driver = { .of_match_table = rsnd_of_match, }, .probe = rsnd_probe, - .remove_new = rsnd_remove, + .remove = rsnd_remove, }; module_platform_driver(rsnd_driver); diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 7b499eee5080..2342bbb6fe92 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -7,6 +7,7 @@ #include <linux/delay.h> #include <linux/of_dma.h> +#include <sound/dmaengine_pcm.h> #include "rsnd.h" /* @@ -22,8 +23,6 @@ struct rsnd_dmaen { struct dma_chan *chan; - dma_cookie_t cookie; - unsigned int dma_len; }; struct rsnd_dmapp { @@ -66,20 +65,6 @@ static struct rsnd_mod mem = { /* * Audio DMAC */ -static void __rsnd_dmaen_complete(struct rsnd_mod *mod, - struct rsnd_dai_stream *io) -{ - if (rsnd_io_is_working(io)) - rsnd_dai_period_elapsed(io); -} - -static void rsnd_dmaen_complete(void *data) -{ - struct rsnd_mod *mod = data; - - rsnd_mod_interrupt(mod, __rsnd_dmaen_complete); -} - static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io, struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) @@ -98,13 +83,7 @@ static int rsnd_dmaen_stop(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - - if (dmaen->chan) - dmaengine_terminate_async(dmaen->chan); - - return 0; + return snd_dmaengine_pcm_trigger(io->substream, SNDRV_PCM_TRIGGER_STOP); } static int rsnd_dmaen_cleanup(struct rsnd_mod *mod, @@ -120,7 +99,7 @@ static int rsnd_dmaen_cleanup(struct rsnd_mod *mod, * Let's call it under prepare */ if (dmaen->chan) - dma_release_channel(dmaen->chan); + snd_dmaengine_pcm_close_release_chan(io->substream); dmaen->chan = NULL; @@ -153,7 +132,7 @@ static int rsnd_dmaen_prepare(struct rsnd_mod *mod, return -EIO; } - return 0; + return snd_dmaengine_pcm_open(io->substream, dmaen->chan); } static int rsnd_dmaen_start(struct rsnd_mod *mod, @@ -162,12 +141,9 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, { struct rsnd_dma *dma = rsnd_mod_to_dma(mod); struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - struct snd_pcm_substream *substream = io->substream; struct device *dev = rsnd_priv_to_dev(priv); - struct dma_async_tx_descriptor *desc; struct dma_slave_config cfg = {}; enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; - int is_play = rsnd_io_is_play(io); int ret; /* @@ -195,7 +171,7 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, } } - cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + cfg.direction = snd_pcm_substream_to_dma_direction(io->substream); cfg.src_addr = dma->src_addr; cfg.dst_addr = dma->dst_addr; cfg.src_addr_width = buswidth; @@ -209,32 +185,7 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, if (ret < 0) return ret; - desc = dmaengine_prep_dma_cyclic(dmaen->chan, - substream->runtime->dma_addr, - snd_pcm_lib_buffer_bytes(substream), - snd_pcm_lib_period_bytes(substream), - is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - - if (!desc) { - dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); - return -EIO; - } - - desc->callback = rsnd_dmaen_complete; - desc->callback_param = rsnd_mod_get(dma); - - dmaen->dma_len = snd_pcm_lib_buffer_bytes(substream); - - dmaen->cookie = dmaengine_submit(desc); - if (dmaen->cookie < 0) { - dev_err(dev, "dmaengine_submit() fail\n"); - return -EIO; - } - - dma_async_issue_pending(dmaen->chan); - - return 0; + return snd_dmaengine_pcm_trigger(io->substream, SNDRV_PCM_TRIGGER_START); } struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *name, @@ -307,19 +258,7 @@ static int rsnd_dmaen_pointer(struct rsnd_mod *mod, struct rsnd_dai_stream *io, snd_pcm_uframes_t *pointer) { - struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); - struct rsnd_dma *dma = rsnd_mod_to_dma(mod); - struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); - struct dma_tx_state state; - enum dma_status status; - unsigned int pos = 0; - - status = dmaengine_tx_status(dmaen->chan, dmaen->cookie, &state); - if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) { - if (state.residue > 0 && state.residue <= dmaen->dma_len) - pos = dmaen->dma_len - state.residue; - } - *pointer = bytes_to_frames(runtime, pos); + *pointer = snd_dmaengine_pcm_pointer(io->substream); return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index ff294aa2d640..3c164d8e3b16 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -576,7 +576,6 @@ int rsnd_rdai_ssi_lane_ctrl(struct rsnd_dai *rdai, #define rsnd_rdai_width_get(rdai) \ rsnd_rdai_width_ctrl(rdai, 0) int rsnd_rdai_width_ctrl(struct rsnd_dai *rdai, int width); -void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io); int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_dai_stream *io, enum rsnd_mod_type type); @@ -871,15 +870,6 @@ void rsnd_cmd_remove(struct rsnd_priv *priv); int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id); void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type); -#ifdef DEBUG -#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI) -#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC) -#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC) -#else -#define rsnd_mod_confirm_ssi(mssi) -#define rsnd_mod_confirm_src(msrc) -#define rsnd_mod_confirm_dvc(mdvc) -#endif /* * If you don't need interrupt status debug message, diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 8d2a86383ae0..b3d4e8ae07ef 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -706,7 +706,7 @@ rsnd_ssi_interrupt_out: spin_unlock(&priv->lock); if (elapsed) - rsnd_dai_period_elapsed(io); + snd_pcm_period_elapsed(io->substream); if (stop) snd_pcm_stop_xrun(io->substream); diff --git a/sound/soc/sh/rz-ssi.c b/sound/soc/sh/rz-ssi.c index 9d103646973a..040ce0431fd2 100644 --- a/sound/soc/sh/rz-ssi.c +++ b/sound/soc/sh/rz-ssi.c @@ -52,6 +52,7 @@ #define SSIFCR_RIE BIT(2) #define SSIFCR_TFRST BIT(1) #define SSIFCR_RFRST BIT(0) +#define SSIFCR_FIFO_RST (SSIFCR_TFRST | SSIFCR_RFRST) #define SSIFSR_TDC_MASK 0x3f #define SSIFSR_TDC_SHIFT 24 @@ -130,6 +131,14 @@ struct rz_ssi_priv { bool lrckp_fsync_fall; /* LR clock polarity (SSICR.LRCKP) */ bool bckp_rise; /* Bit clock polarity (SSICR.BCKP) */ bool dma_rt; + + /* Full duplex communication support */ + struct { + unsigned int rate; + unsigned int channels; + unsigned int sample_width; + unsigned int sample_bits; + } hw_params_cache; }; static void rz_ssi_dma_complete(void *data); @@ -208,6 +217,11 @@ static bool rz_ssi_stream_is_valid(struct rz_ssi_priv *ssi, return ret; } +static inline bool rz_ssi_is_stream_running(struct rz_ssi_stream *strm) +{ + return strm->substream && strm->running; +} + static void rz_ssi_stream_init(struct rz_ssi_stream *strm, struct snd_pcm_substream *substream) { @@ -303,13 +317,53 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate, return 0; } +static void rz_ssi_set_idle(struct rz_ssi_priv *ssi) +{ + int timeout; + + /* Disable irqs */ + rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TUIEN | SSICR_TOIEN | + SSICR_RUIEN | SSICR_ROIEN, 0); + rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_TIE | SSIFCR_RIE, 0); + + /* Clear all error flags */ + rz_ssi_reg_mask_setl(ssi, SSISR, + (SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ | + SSISR_RUIRQ), 0); + + /* Wait for idle */ + timeout = 100; + while (--timeout) { + if (rz_ssi_reg_readl(ssi, SSISR) & SSISR_IIRQ) + break; + udelay(1); + } + + if (!timeout) + dev_info(ssi->dev, "timeout waiting for SSI idle\n"); + + /* Hold FIFOs in reset */ + rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, + SSIFCR_TFRST | SSIFCR_RFRST); +} + static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { bool is_play = rz_ssi_stream_is_play(ssi, strm->substream); + bool is_full_duplex; u32 ssicr, ssifcr; + is_full_duplex = rz_ssi_is_stream_running(&ssi->playback) || + rz_ssi_is_stream_running(&ssi->capture); ssicr = rz_ssi_reg_readl(ssi, SSICR); - ssifcr = rz_ssi_reg_readl(ssi, SSIFCR) & ~0xF; + ssifcr = rz_ssi_reg_readl(ssi, SSIFCR); + if (!is_full_duplex) { + ssifcr &= ~0xF; + } else { + rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0); + rz_ssi_set_idle(ssi); + ssifcr &= ~SSIFCR_FIFO_RST; + } /* FIFO interrupt thresholds */ if (rz_ssi_is_dma_enabled(ssi)) @@ -322,10 +376,14 @@ static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) /* enable IRQ */ if (is_play) { ssicr |= SSICR_TUIEN | SSICR_TOIEN; - ssifcr |= SSIFCR_TIE | SSIFCR_RFRST; + ssifcr |= SSIFCR_TIE; + if (!is_full_duplex) + ssifcr |= SSIFCR_RFRST; } else { ssicr |= SSICR_RUIEN | SSICR_ROIEN; - ssifcr |= SSIFCR_RIE | SSIFCR_TFRST; + ssifcr |= SSIFCR_RIE; + if (!is_full_duplex) + ssifcr |= SSIFCR_TFRST; } rz_ssi_reg_writel(ssi, SSICR, ssicr); @@ -337,7 +395,11 @@ static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) SSISR_RUIRQ), 0); strm->running = 1; - ssicr |= is_play ? SSICR_TEN : SSICR_REN; + if (is_full_duplex) + ssicr |= SSICR_TEN | SSICR_REN; + else + ssicr |= is_play ? SSICR_TEN : SSICR_REN; + rz_ssi_reg_writel(ssi, SSICR, ssicr); return 0; @@ -345,10 +407,12 @@ static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) { - int timeout; - strm->running = 0; + if (rz_ssi_is_stream_running(&ssi->playback) || + rz_ssi_is_stream_running(&ssi->capture)) + return 0; + /* Disable TX/RX */ rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0); @@ -356,30 +420,7 @@ static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) if (rz_ssi_is_dma_enabled(ssi)) dmaengine_terminate_async(strm->dma_ch); - /* Disable irqs */ - rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TUIEN | SSICR_TOIEN | - SSICR_RUIEN | SSICR_ROIEN, 0); - rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_TIE | SSIFCR_RIE, 0); - - /* Clear all error flags */ - rz_ssi_reg_mask_setl(ssi, SSISR, - (SSISR_TOIRQ | SSISR_TUIRQ | SSISR_ROIRQ | - SSISR_RUIRQ), 0); - - /* Wait for idle */ - timeout = 100; - while (--timeout) { - if (rz_ssi_reg_readl(ssi, SSISR) & SSISR_IIRQ) - break; - udelay(1); - } - - if (!timeout) - dev_info(ssi->dev, "timeout waiting for SSI idle\n"); - - /* Hold FIFOs in reset */ - rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, - SSIFCR_TFRST | SSIFCR_RFRST); + rz_ssi_set_idle(ssi); return 0; } @@ -512,66 +553,90 @@ static int rz_ssi_pio_send(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm) static irqreturn_t rz_ssi_interrupt(int irq, void *data) { - struct rz_ssi_stream *strm = NULL; + struct rz_ssi_stream *strm_playback = NULL; + struct rz_ssi_stream *strm_capture = NULL; struct rz_ssi_priv *ssi = data; u32 ssisr = rz_ssi_reg_readl(ssi, SSISR); if (ssi->playback.substream) - strm = &ssi->playback; - else if (ssi->capture.substream) - strm = &ssi->capture; - else + strm_playback = &ssi->playback; + if (ssi->capture.substream) + strm_capture = &ssi->capture; + + if (!strm_playback && !strm_capture) return IRQ_HANDLED; /* Left over TX/RX interrupt */ if (irq == ssi->irq_int) { /* error or idle */ - if (ssisr & SSISR_TUIRQ) - strm->uerr_num++; - if (ssisr & SSISR_TOIRQ) - strm->oerr_num++; - if (ssisr & SSISR_RUIRQ) - strm->uerr_num++; - if (ssisr & SSISR_ROIRQ) - strm->oerr_num++; - - if (ssisr & (SSISR_TUIRQ | SSISR_TOIRQ | SSISR_RUIRQ | - SSISR_ROIRQ)) { - /* Error handling */ - /* You must reset (stop/restart) after each interrupt */ - rz_ssi_stop(ssi, strm); - - /* Clear all flags */ - rz_ssi_reg_mask_setl(ssi, SSISR, SSISR_TOIRQ | - SSISR_TUIRQ | SSISR_ROIRQ | - SSISR_RUIRQ, 0); - - /* Add/remove more data */ - strm->transfer(ssi, strm); - - /* Resume */ - rz_ssi_start(ssi, strm); + bool is_stopped = false; + int i, count; + + if (rz_ssi_is_dma_enabled(ssi)) + count = 4; + else + count = 1; + + if (ssisr & (SSISR_RUIRQ | SSISR_ROIRQ | SSISR_TUIRQ | SSISR_TOIRQ)) + is_stopped = true; + + if (ssi->capture.substream && is_stopped) { + if (ssisr & SSISR_RUIRQ) + strm_capture->uerr_num++; + if (ssisr & SSISR_ROIRQ) + strm_capture->oerr_num++; + + rz_ssi_stop(ssi, strm_capture); } + + if (ssi->playback.substream && is_stopped) { + if (ssisr & SSISR_TUIRQ) + strm_playback->uerr_num++; + if (ssisr & SSISR_TOIRQ) + strm_playback->oerr_num++; + + rz_ssi_stop(ssi, strm_playback); + } + + /* Clear all flags */ + rz_ssi_reg_mask_setl(ssi, SSISR, SSISR_TOIRQ | SSISR_TUIRQ | + SSISR_ROIRQ | SSISR_RUIRQ, 0); + + /* Add/remove more data */ + if (ssi->capture.substream && is_stopped) { + for (i = 0; i < count; i++) + strm_capture->transfer(ssi, strm_capture); + } + + if (ssi->playback.substream && is_stopped) { + for (i = 0; i < count; i++) + strm_playback->transfer(ssi, strm_playback); + } + + /* Resume */ + if (ssi->playback.substream && is_stopped) + rz_ssi_start(ssi, &ssi->playback); + if (ssi->capture.substream && is_stopped) + rz_ssi_start(ssi, &ssi->capture); } - if (!strm->running) + if (!rz_ssi_is_stream_running(&ssi->playback) && + !rz_ssi_is_stream_running(&ssi->capture)) return IRQ_HANDLED; /* tx data empty */ - if (irq == ssi->irq_tx) - strm->transfer(ssi, &ssi->playback); + if (irq == ssi->irq_tx && rz_ssi_is_stream_running(&ssi->playback)) + strm_playback->transfer(ssi, &ssi->playback); /* rx data full */ - if (irq == ssi->irq_rx) { - strm->transfer(ssi, &ssi->capture); + if (irq == ssi->irq_rx && rz_ssi_is_stream_running(&ssi->capture)) { + strm_capture->transfer(ssi, &ssi->capture); rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); } if (irq == ssi->irq_rt) { - struct snd_pcm_substream *substream = strm->substream; - - if (rz_ssi_stream_is_play(ssi, substream)) { - strm->transfer(ssi, &ssi->playback); + if (ssi->playback.substream) { + strm_playback->transfer(ssi, &ssi->playback); } else { - strm->transfer(ssi, &ssi->capture); + strm_capture->transfer(ssi, &ssi->capture); rz_ssi_reg_mask_setl(ssi, SSIFSR, SSIFSR_RDF, 0); } } @@ -731,9 +796,12 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd, switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* Soft Reset */ - rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST); - rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0); - udelay(5); + if (!rz_ssi_is_stream_running(&ssi->playback) && + !rz_ssi_is_stream_running(&ssi->capture)) { + rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST); + rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0); + udelay(5); + } rz_ssi_stream_init(strm, substream); @@ -824,14 +892,41 @@ static int rz_ssi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static bool rz_ssi_is_valid_hw_params(struct rz_ssi_priv *ssi, unsigned int rate, + unsigned int channels, + unsigned int sample_width, + unsigned int sample_bits) +{ + if (ssi->hw_params_cache.rate != rate || + ssi->hw_params_cache.channels != channels || + ssi->hw_params_cache.sample_width != sample_width || + ssi->hw_params_cache.sample_bits != sample_bits) + return false; + + return true; +} + +static void rz_ssi_cache_hw_params(struct rz_ssi_priv *ssi, unsigned int rate, + unsigned int channels, + unsigned int sample_width, + unsigned int sample_bits) +{ + ssi->hw_params_cache.rate = rate; + ssi->hw_params_cache.channels = channels; + ssi->hw_params_cache.sample_width = sample_width; + ssi->hw_params_cache.sample_bits = sample_bits; +} + static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai); + struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream); unsigned int sample_bits = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min; unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); if (sample_bits != 16) { dev_err(ssi->dev, "Unsupported sample width: %d\n", @@ -845,8 +940,20 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - return rz_ssi_clk_setup(ssi, params_rate(params), - params_channels(params)); + if (rz_ssi_is_stream_running(&ssi->playback) || + rz_ssi_is_stream_running(&ssi->capture)) { + if (rz_ssi_is_valid_hw_params(ssi, rate, channels, + strm->sample_width, sample_bits)) + return 0; + + dev_err(ssi->dev, "Full duplex needs same HW params\n"); + return -EINVAL; + } + + rz_ssi_cache_hw_params(ssi, rate, channels, strm->sample_width, + sample_bits); + + return rz_ssi_clk_setup(ssi, rate, channels); } static const struct snd_soc_dai_ops rz_ssi_dai_ops = { @@ -1097,7 +1204,7 @@ static struct platform_driver rz_ssi_driver = { .of_match_table = rz_ssi_of_match, }, .probe = rz_ssi_probe, - .remove_new = rz_ssi_remove, + .remove = rz_ssi_remove, }; module_platform_driver(rz_ssi_driver); diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c index d0b5c543fd2f..7e771a164a80 100644 --- a/sound/soc/sh/siu_dai.c +++ b/sound/soc/sh/siu_dai.c @@ -788,7 +788,7 @@ static struct platform_driver siu_driver = { .name = "siu-pcm-audio", }, .probe = siu_probe, - .remove_new = siu_remove, + .remove = siu_remove, }; module_platform_driver(siu_driver); |