From e99961f4afa80a6bd34da1982f0ba370799407e7 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Sat, 25 Feb 2023 23:29:17 +0800 Subject: [PATCH 01/11] ASoC: rockchip: Add dmaengine for TRCM mode TRCM require TX/RX enabled at the same time, or need the one which provide clk enabled at first. In this situation, one direction's FIFO maybe XRUN which may break the channels order. This patch add dmaengine for this mode to handle this situation. Signed-off-by: Sugar Zhang Change-Id: I30b4ec134b06516d80de4c1d0ccaf916513294ab --- sound/soc/rockchip/Kconfig | 7 + sound/soc/rockchip/Makefile | 2 + sound/soc/rockchip/rockchip_trcm.c | 578 +++++++++++++++++++++++++++++ sound/soc/rockchip/rockchip_trcm.h | 32 ++ 4 files changed, 619 insertions(+) create mode 100644 sound/soc/rockchip/rockchip_trcm.c create mode 100644 sound/soc/rockchip/rockchip_trcm.h diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig index 623caceef234..5885779c7855 100644 --- a/sound/soc/rockchip/Kconfig +++ b/sound/soc/rockchip/Kconfig @@ -31,6 +31,7 @@ config SND_SOC_ROCKCHIP_I2S_TDM tristate "Rockchip I2S/TDM Device Driver" depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP select SND_SOC_GENERIC_DMAENGINE_PCM + select SND_SOC_ROCKCHIP_TRCM help Say Y or M if you want to add support for I2S/TDM driver for Rockchip I2S/TDM device. The device supports up to maximum of @@ -93,6 +94,12 @@ config SND_SOC_ROCKCHIP_SPDIFRX Say Y or M if you want to add support for SPDIFRX driver for Rockchip SPDIF receiver device. +config SND_SOC_ROCKCHIP_TRCM + tristate "Rockchip TRCM Pcm Driver" + depends on SND_SOC_ROCKCHIP + help + Say Y or M if you want to add support for TRCM driver. + config SND_SOC_ROCKCHIP_VAD tristate "Rockchip Voice Activity Detection Driver" depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile index ca8dfb54841c..6571779e1849 100644 --- a/sound/soc/rockchip/Makefile +++ b/sound/soc/rockchip/Makefile @@ -10,6 +10,7 @@ snd-soc-rockchip-pdm-objs := rockchip_pdm.o snd-soc-rockchip-sai-objs := rockchip_sai.o snd-soc-rockchip-spdif-objs := rockchip_spdif.o snd-soc-rockchip-spdifrx-objs := rockchip_spdifrx.o +snd-soc-rockchip-trcm-objs := rockchip_trcm.o snd-soc-rockchip-vad-objs := rockchip_vad.o ifdef CONFIG_THUMB2_KERNEL snd-soc-rockchip-vad-$(CONFIG_THUMB2_KERNEL) += vad_preprocess_thumb.o @@ -28,6 +29,7 @@ obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SAI) += snd-soc-rockchip-sai.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIFRX) += snd-soc-rockchip-spdifrx.o +obj-$(CONFIG_SND_SOC_ROCKCHIP_TRCM) += snd-soc-rockchip-trcm.o obj-$(CONFIG_SND_SOC_ROCKCHIP_VAD) += snd-soc-rockchip-vad.o snd-soc-rockchip-hdmi-objs := rockchip_hdmi.o diff --git a/sound/soc/rockchip/rockchip_trcm.c b/sound/soc/rockchip/rockchip_trcm.c new file mode 100644 index 000000000000..f9e9424a91f8 --- /dev/null +++ b/sound/soc/rockchip/rockchip_trcm.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Rockchip TRCM Pcm Driver + * + * Copyright (c) 2023 Rockchip Electronics Co. Ltd. + * Author: Sugar Zhang + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rockchip_trcm.h" + +#define DMA_GUARD_BUFFER_SIZE 64 + +static unsigned int prealloc_buffer_size_kbytes = 512; +module_param(prealloc_buffer_size_kbytes, uint, 0444); +MODULE_PARM_DESC(prealloc_buffer_size_kbytes, "Preallocate DMA buffer size (KB)."); + +struct dmaengine_dma_guard { + dma_addr_t dma_addr; + unsigned char *dma_area; +}; + +struct dmaengine_trcm { + struct device *dev; + struct dma_chan *chan[SNDRV_PCM_STREAM_LAST + 1]; + struct dmaengine_dma_guard guard[SNDRV_PCM_STREAM_LAST + 1]; + struct snd_soc_component component; +}; + +struct dmaengine_trcm_runtime_data { + struct dmaengine_trcm *parent; + struct dma_chan *dma_chan; + dma_cookie_t cookie; + + unsigned int frame_bytes; + unsigned int channels; + int stream; +}; + +static inline ssize_t trcm_channels_to_bytes(struct dmaengine_trcm_runtime_data *prtd, + int channels) +{ + return (prtd->frame_bytes / prtd->channels) * channels; +} + +static inline ssize_t trcm_frames_to_bytes(struct dmaengine_trcm_runtime_data *prtd, + snd_pcm_sframes_t size) +{ + return size * prtd->frame_bytes; +} + +static inline snd_pcm_sframes_t trcm_bytes_to_frames(struct dmaengine_trcm_runtime_data *prtd, + ssize_t size) +{ + return size / prtd->frame_bytes; +} + +static inline struct dmaengine_trcm *soc_component_to_trcm(struct snd_soc_component *p) +{ + return container_of(p, struct dmaengine_trcm, component); +} + +static inline struct dmaengine_trcm_runtime_data *substream_to_prtd( + const struct snd_pcm_substream *substream) +{ + if (!substream->runtime) + return NULL; + + return substream->runtime->private_data; +} + +static struct dma_chan *snd_dmaengine_trcm_get_chan(struct snd_pcm_substream *substream) +{ + struct dmaengine_trcm_runtime_data *prtd = substream_to_prtd(substream); + + return prtd->dma_chan; +} + +static struct device *dmaengine_dma_dev(struct dmaengine_trcm *trcm, + struct snd_pcm_substream *substream) +{ + if (!trcm->chan[substream->stream]) + return NULL; + + return trcm->chan[substream->stream]->device->dev; +} + +static int dmaengine_trcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct dmaengine_trcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_chan *chan = snd_dmaengine_trcm_get_chan(substream); + struct dma_slave_config slave_config; + int ret; + + memset(&slave_config, 0, sizeof(slave_config)); + + ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &slave_config); + if (ret) + return ret; + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) + return ret; + + prtd->frame_bytes = snd_pcm_format_size(params_format(params), + params_channels(params)); + prtd->channels = params_channels(params); + + return 0; +} + +static int +dmaengine_pcm_set_runtime_hwparams(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct dmaengine_trcm *trcm = soc_component_to_trcm(component); + struct device *dma_dev = dmaengine_dma_dev(trcm, substream); + struct dma_chan *chan = trcm->chan[substream->stream]; + struct snd_dmaengine_dai_dma_data *dma_data; + struct snd_pcm_hardware hw; + + if (rtd->num_cpus > 1) { + dev_err(rtd->dev, + "%s doesn't support Multi CPU yet\n", __func__); + return -EINVAL; + } + + dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream); + + memset(&hw, 0, sizeof(hw)); + hw.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED; + hw.periods_min = 2; + hw.periods_max = UINT_MAX; + hw.period_bytes_min = 256; + hw.period_bytes_max = dma_get_max_seg_size(dma_dev); + hw.buffer_bytes_max = SIZE_MAX; + hw.fifo_size = dma_data->fifo_size; + + snd_dmaengine_pcm_refine_runtime_hwparams(substream, + dma_data, + &hw, + chan); + + return snd_soc_set_runtime_hwparams(substream, &hw); +} + +static int dmaengine_trcm_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct dmaengine_trcm *trcm = soc_component_to_trcm(component); + struct dma_chan *chan = trcm->chan[substream->stream]; + struct dmaengine_trcm_runtime_data *prtd; + int ret; + + if (!chan) + return -ENXIO; + + ret = dmaengine_pcm_set_runtime_hwparams(component, substream); + if (ret) + return ret; + + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (!prtd) + return -ENOMEM; + + prtd->parent = trcm; + prtd->stream = substream->stream; + prtd->dma_chan = chan; + + substream->runtime->private_data = prtd; + + return 0; +} + +static int dmaengine_trcm_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct dmaengine_trcm_runtime_data *prtd = substream_to_prtd(substream); + + dmaengine_synchronize(prtd->dma_chan); + + kfree(prtd); + + return 0; +} + +static snd_pcm_uframes_t dmaengine_trcm_pointer( + struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct dmaengine_trcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_tx_state state; + unsigned int buf_size; + unsigned int pos = 0; + + dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state); + buf_size = snd_pcm_lib_buffer_bytes(substream); + if (state.residue > 0 && state.residue <= buf_size) + pos = buf_size - state.residue; + + return trcm_bytes_to_frames(prtd, pos); +} + +static void dmaengine_trcm_dma_complete(void *arg) +{ + struct snd_pcm_substream *substream = arg; + + if (!substream->runtime) + return; + + snd_pcm_period_elapsed(substream); +} + +static int dmaengine_trcm_prepare_and_submit(struct snd_pcm_substream *substream) +{ + struct dmaengine_trcm_runtime_data *prtd = substream_to_prtd(substream); + struct dma_chan *chan = prtd->dma_chan; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction direction; + unsigned long flags = DMA_CTRL_ACK; + + direction = snd_pcm_substream_to_dma_direction(substream); + + if (!substream->runtime->no_period_wakeup) + flags |= DMA_PREP_INTERRUPT; + + desc = dmaengine_prep_dma_cyclic(chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), direction, flags); + + if (!desc) + return -ENOMEM; + + desc->callback = dmaengine_trcm_dma_complete; + desc->callback_param = substream; + prtd->cookie = dmaengine_submit(desc); + + return 0; +} + +int dmaengine_trcm_dma_guard_ctrl(struct snd_soc_component *component, + int stream, bool en) +{ + struct dmaengine_trcm *trcm = soc_component_to_trcm(component); + struct dmaengine_dma_guard *guard = &trcm->guard[stream]; + struct dma_chan *chan = trcm->chan[stream]; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction direction; + + if (!chan) + return 0; + + if (!en) + return dmaengine_terminate_sync(chan); + + direction = stream ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + + desc = dmaengine_prep_dma_cyclic(chan, guard->dma_addr, + DMA_GUARD_BUFFER_SIZE, + DMA_GUARD_BUFFER_SIZE, + direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(component->dev, "Failed to get dma desc\n"); + return -ENOMEM; + } + + desc->callback = NULL; + desc->callback_param = NULL; + dmaengine_submit(desc); + dma_async_issue_pending(chan); + + return 0; +} +EXPORT_SYMBOL_GPL(dmaengine_trcm_dma_guard_ctrl); + +static int dmaengine_trcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct dmaengine_trcm_runtime_data *prtd = substream_to_prtd(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + dmaengine_terminate_sync(prtd->dma_chan); + ret = dmaengine_trcm_prepare_and_submit(substream); + if (ret) + return ret; + dma_async_issue_pending(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dmaengine_resume(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + if (runtime->info & SNDRV_PCM_INFO_PAUSE) + dmaengine_pause(prtd->dma_chan); + else + dmaengine_terminate_sync(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dmaengine_pause(prtd->dma_chan); + break; + case SNDRV_PCM_TRIGGER_STOP: + dmaengine_terminate_sync(prtd->dma_chan); + dmaengine_trcm_dma_guard_ctrl(component, substream->stream, 1); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int dmaengine_trcm_dma_guard_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct dmaengine_trcm *trcm = soc_component_to_trcm(component); + struct snd_dmaengine_dai_dma_data *dma_data; + struct snd_pcm_substream *substream; + struct dma_chan *chan; + struct dma_slave_config slave_config; + struct device *dev; + dma_addr_t dma_addr; + unsigned char *dma_area; + unsigned int i; + int ret; + + for_each_pcm_streams(i) { + substream = rtd->pcm->streams[i].substream; + dev = dmaengine_dma_dev(trcm, substream); + chan = trcm->chan[i]; + + dma_area = dma_alloc_coherent(dev, DMA_GUARD_BUFFER_SIZE, + &dma_addr, GFP_KERNEL); + if (!dma_area) + return -ENOMEM; + + memset(dma_area, 0x0, DMA_GUARD_BUFFER_SIZE); + + trcm->guard[i].dma_addr = dma_addr; + trcm->guard[i].dma_area = dma_area; + + memset(&slave_config, 0, sizeof(slave_config)); + + dma_data = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), + substream); + snd_dmaengine_pcm_set_config_from_dai_data(substream, dma_data, + &slave_config); + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) + return ret; + } + + return 0; +} + +static int dmaengine_trcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct dmaengine_trcm *trcm = soc_component_to_trcm(component); + struct snd_pcm_substream *substream; + size_t prealloc_buffer_size; + size_t max_buffer_size; + unsigned int i; + int ret; + + prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024; + max_buffer_size = SIZE_MAX; + + ret = dmaengine_trcm_dma_guard_new(component, rtd); + if (ret) + return ret; + + for_each_pcm_streams(i) { + substream = rtd->pcm->streams[i].substream; + if (!substream) + continue; + + if (!trcm->chan[i]) { + dev_err(component->dev, + "Missing dma channel for stream: %d\n", i); + return -EINVAL; + } + + snd_pcm_set_managed_buffer(substream, + SNDRV_DMA_TYPE_DEV_IRAM, + dmaengine_dma_dev(trcm, substream), + prealloc_buffer_size, + max_buffer_size); + + if (rtd->pcm->streams[i].pcm->name[0] == '\0') { + strscpy_pad(rtd->pcm->streams[i].pcm->name, + rtd->pcm->streams[i].pcm->id, + sizeof(rtd->pcm->streams[i].pcm->name)); + } + } + + return 0; +} + +static const struct snd_soc_component_driver dmaengine_trcm_component = { + .name = SND_DMAENGINE_TRCM_DRV_NAME, + .probe_order = SND_SOC_COMP_ORDER_LATE, + .open = dmaengine_trcm_open, + .close = dmaengine_trcm_close, + .hw_params = dmaengine_trcm_hw_params, + .trigger = dmaengine_trcm_trigger, + .pointer = dmaengine_trcm_pointer, + .pcm_construct = dmaengine_trcm_new, +}; + +static const char * const dmaengine_pcm_dma_channel_names[] = { + [SNDRV_PCM_STREAM_PLAYBACK] = "tx", + [SNDRV_PCM_STREAM_CAPTURE] = "rx", +}; + +static int dmaengine_pcm_request_chan_of(struct dmaengine_trcm *trcm, + struct device *dev, const struct snd_dmaengine_pcm_config *config) +{ + unsigned int i; + const char *name; + struct dma_chan *chan; + + for_each_pcm_streams(i) { + name = dmaengine_pcm_dma_channel_names[i]; + chan = dma_request_chan(dev, name); + if (IS_ERR(chan)) { + /* + * Only report probe deferral errors, channels + * might not be present for devices that + * support only TX or only RX. + */ + if (PTR_ERR(chan) == -EPROBE_DEFER) + return -EPROBE_DEFER; + trcm->chan[i] = NULL; + } else { + trcm->chan[i] = chan; + } + } + + return 0; +} + +static void dmaengine_pcm_release_chan(struct dmaengine_trcm *trcm) +{ + unsigned int i; + + for_each_pcm_streams(i) { + if (!trcm->chan[i]) + continue; + dma_release_channel(trcm->chan[i]); + } +} + +/** + * snd_dmaengine_trcm_register - Register a dmaengine based TRCM device + * @dev: The parent device for the TRCM device + */ +static int snd_dmaengine_trcm_register(struct device *dev) +{ + const struct snd_soc_component_driver *driver; + struct dmaengine_trcm *trcm; + int ret; + + trcm = kzalloc(sizeof(*trcm), GFP_KERNEL); + if (!trcm) + return -ENOMEM; + + trcm->dev = dev; + +#ifdef CONFIG_DEBUG_FS + trcm->component.debugfs_prefix = "dma"; +#endif + ret = dmaengine_pcm_request_chan_of(trcm, dev, NULL); + if (ret) + goto err_free_dma; + + driver = &dmaengine_trcm_component; + + ret = snd_soc_component_initialize(&trcm->component, driver, dev); + if (ret) + goto err_free_dma; + + ret = snd_soc_add_component(&trcm->component, NULL, 0); + if (ret) + goto err_free_dma; + + dev_info(dev, "Register PCM for TRCM mode\n"); + + return 0; + +err_free_dma: + dmaengine_pcm_release_chan(trcm); + kfree(trcm); + return ret; +} + +/** + * snd_dmaengine_trcm_unregister - Removes a dmaengine based TRCM device + * @dev: Parent device the TRCM was register with + * + * Removes a dmaengine based TRCM device previously registered with + * snd_dmaengine_trcm_register. + */ +static void snd_dmaengine_trcm_unregister(struct device *dev) +{ + struct snd_soc_component *component; + struct dmaengine_trcm *trcm; + + component = snd_soc_lookup_component(dev, SND_DMAENGINE_TRCM_DRV_NAME); + if (!component) + return; + + trcm = soc_component_to_trcm(component); + + snd_soc_unregister_component_by_driver(dev, component->driver); + dmaengine_pcm_release_chan(trcm); + kfree(trcm); +} + +static void devm_dmaengine_trcm_release(struct device *dev, void *res) +{ + snd_dmaengine_trcm_unregister(*(struct device **)res); +} + +/** + * devm_snd_dmaengine_trcm_register - resource managed dmaengine TRCM registration + * @dev: The parent device for the TRCM device + * + * Register a dmaengine based TRCM device with automatic unregistration when the + * device is unregistered. + */ +int devm_snd_dmaengine_trcm_register(struct device *dev) +{ + struct device **ptr; + int ret; + + ptr = devres_alloc(devm_dmaengine_trcm_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = snd_dmaengine_trcm_register(dev); + if (ret == 0) { + *ptr = dev; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_snd_dmaengine_trcm_register); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/rockchip/rockchip_trcm.h b/sound/soc/rockchip/rockchip_trcm.h new file mode 100644 index 000000000000..6135164dc0b5 --- /dev/null +++ b/sound/soc/rockchip/rockchip_trcm.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Rockchip TRCM Pcm driver + * + * Copyright (C) 2023 Rockchip Electronics Co., Ltd + * Author: Sugar Zhang + * + */ + +#ifndef _ROCKCHIP_TRCM_H +#define _ROCKCHIP_TRCM_H + +#define SND_DMAENGINE_TRCM_DRV_NAME "snd_dmaengine_trcm" + +#if IS_REACHABLE(CONFIG_SND_SOC_ROCKCHIP_TRCM) +int dmaengine_trcm_dma_guard_ctrl(struct snd_soc_component *component, + int stream, bool en); +int devm_snd_dmaengine_trcm_register(struct device *dev); +#else +static inline int dmaengine_trcm_dma_guard_ctrl(struct snd_soc_component *component, + int stream, bool en) +{ + return -ENOSYS; +} + +static inline int devm_snd_dmaengine_trcm_register(struct device *dev) +{ + return -ENOSYS; +} +#endif + +#endif From 7ae6fd2b0ea877a28d6086e0ce50898953a4eaf4 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Wed, 22 Feb 2023 17:49:39 +0800 Subject: [PATCH 02/11] ASoC: rockchip: i2s-tdm: Fix multi-channels order for TRCM TRCM on Master mode require TX/RX enabled at the same time, or need the one which provide clk enabled at first. In this situation, one direction's FIFO maybe XRUN which may break the channels order. This patch add dmaengine for this mode to handle this situation. Signed-off-by: Sugar Zhang Change-Id: I831aead0371228661620b54188048e4f3daa717e --- sound/soc/rockchip/rockchip_i2s_tdm.c | 221 ++++++++++++++++---------- 1 file changed, 141 insertions(+), 80 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 612c2639c52b..2d8e27b60ca6 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -30,6 +30,7 @@ #include "rockchip_i2s_tdm.h" #include "rockchip_dlp_pcm.h" #include "rockchip_utils.h" +#include "rockchip_trcm.h" #define DRV_NAME "rockchip-i2s-tdm" @@ -118,6 +119,7 @@ struct rk_i2s_tdm_dev { struct snd_dmaengine_dai_dma_data playback_dma_data; struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1]; unsigned int wait_time[SNDRV_PCM_STREAM_LAST + 1]; + struct snd_soc_component *pcm_comp; struct reset_control *tx_reset; struct reset_control *rx_reset; struct pinctrl *pinctrl; @@ -134,6 +136,7 @@ struct rk_i2s_tdm_dev { bool tdm_mode; bool tdm_fsync_half_frame; bool is_dma_active[SNDRV_PCM_STREAM_LAST + 1]; + bool dma_guard_initialized; unsigned int mclk_rx_freq; unsigned int mclk_tx_freq; unsigned int mclk_root0_freq; @@ -196,75 +199,6 @@ static int to_ch_num(unsigned int val) return chs; } -static int i2s_tdm_runtime_suspend(struct device *dev) -{ - struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); - - regcache_cache_only(i2s_tdm->regmap, true); - - clk_disable_unprepare(i2s_tdm->mclk_tx); - clk_disable_unprepare(i2s_tdm->mclk_rx); - - pinctrl_pm_select_idle_state(dev); - - return 0; -} - -static int rockchip_i2s_tdm_pinctrl_select_clk_state(struct device *dev) -{ - struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); - - if (IS_ERR_OR_NULL(i2s_tdm->pinctrl) || !i2s_tdm->clk_state) - return 0; - - pinctrl_select_state(i2s_tdm->pinctrl, i2s_tdm->clk_state); - - return 0; -} - -static int i2s_tdm_runtime_resume(struct device *dev) -{ - struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); - int ret; - - /* - * pinctrl default state is invoked by ASoC framework, so, - * we just handle clk state here if DT assigned. - */ - if (i2s_tdm->is_master_mode) - rockchip_i2s_tdm_pinctrl_select_clk_state(dev); - - ret = clk_prepare_enable(i2s_tdm->mclk_tx); - if (ret) - goto err_mclk_tx; - - ret = clk_prepare_enable(i2s_tdm->mclk_rx); - if (ret) - goto err_mclk_rx; - - regcache_cache_only(i2s_tdm->regmap, false); - regcache_mark_dirty(i2s_tdm->regmap); - ret = regcache_sync(i2s_tdm->regmap); - if (ret) - goto err_regmap; - - /* - * should be placed after regcache sync done to back - * to the slave mode and then enable clk state. - */ - if (!i2s_tdm->is_master_mode) - rockchip_i2s_tdm_pinctrl_select_clk_state(dev); - - return 0; - -err_regmap: - clk_disable_unprepare(i2s_tdm->mclk_rx); -err_mclk_rx: - clk_disable_unprepare(i2s_tdm->mclk_tx); -err_mclk_tx: - return ret; -} - static inline struct rk_i2s_tdm_dev *to_info(struct snd_soc_dai *dai) { return snd_soc_dai_get_drvdata(dai); @@ -932,23 +866,38 @@ static void rockchip_i2s_tdm_xfer_stop(struct rk_i2s_tdm_dev *i2s_tdm, rockchip_i2s_tdm_clear(i2s_tdm, clr); } -static void rockchip_i2s_tdm_xfer_trcm_start(struct rk_i2s_tdm_dev *i2s_tdm) +static void rockchip_i2s_tdm_xfer_trcm_start(struct rk_i2s_tdm_dev *i2s_tdm, + int stream) { + int bstream = SNDRV_PCM_STREAM_LAST - stream; unsigned long flags; + u32 val, en; spin_lock_irqsave(&i2s_tdm->lock, flags); - if (atomic_inc_return(&i2s_tdm->refcount) == 1) + if (atomic_inc_return(&i2s_tdm->refcount) == 1) { + if (i2s_tdm->dma_guard_initialized) { + regmap_read(i2s_tdm->regmap, I2S_DMACR, &val); + en = I2S_DMACR_RDE(1) | I2S_DMACR_TDE(1); + if ((val & en) != en) { + dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, bstream, 1); + rockchip_i2s_tdm_dma_ctrl(i2s_tdm, bstream, 1); + } + } rockchip_i2s_tdm_xfer_start(i2s_tdm, 0); + } spin_unlock_irqrestore(&i2s_tdm->lock, flags); } -static void rockchip_i2s_tdm_xfer_trcm_stop(struct rk_i2s_tdm_dev *i2s_tdm) +static void rockchip_i2s_tdm_xfer_trcm_stop(struct rk_i2s_tdm_dev *i2s_tdm, + int stream) { unsigned long flags; spin_lock_irqsave(&i2s_tdm->lock, flags); if (atomic_dec_and_test(&i2s_tdm->refcount)) rockchip_i2s_tdm_xfer_stop(i2s_tdm, 0, false); + if (i2s_tdm->dma_guard_initialized) + rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 1); spin_unlock_irqrestore(&i2s_tdm->lock, flags); } @@ -958,6 +907,9 @@ static void rockchip_i2s_tdm_trcm_pause(struct snd_pcm_substream *substream, int stream = substream->stream; int bstream = SNDRV_PCM_STREAM_LAST - stream; + if (i2s_tdm->pcm_comp) + dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, stream, 0); + /* store the current state, prepare for resume if necessary */ i2s_tdm->is_dma_active[bstream] = is_dma_active(i2s_tdm, bstream); @@ -970,14 +922,17 @@ static void rockchip_i2s_tdm_trcm_pause(struct snd_pcm_substream *substream, static void rockchip_i2s_tdm_trcm_resume(struct snd_pcm_substream *substream, struct rk_i2s_tdm_dev *i2s_tdm) { + int stream = substream->stream; int bstream = SNDRV_PCM_STREAM_LAST - substream->stream; - /* - * just resume bstream, because current stream will be - * startup in the trigger-cmd-START - */ + if (i2s_tdm->pcm_comp) { + dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, stream, 1); + rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 1); + } + if (i2s_tdm->is_dma_active[bstream]) rockchip_i2s_tdm_dma_ctrl(i2s_tdm, bstream, 1); + rockchip_i2s_tdm_xfer_start(i2s_tdm, bstream); } @@ -997,7 +952,7 @@ static void rockchip_i2s_tdm_start(struct rk_i2s_tdm_dev *i2s_tdm, int stream) rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 1); if (i2s_tdm->clk_trcm) - rockchip_i2s_tdm_xfer_trcm_start(i2s_tdm); + rockchip_i2s_tdm_xfer_trcm_start(i2s_tdm, stream); else rockchip_i2s_tdm_xfer_start(i2s_tdm, stream); } @@ -1007,7 +962,7 @@ static void rockchip_i2s_tdm_stop(struct rk_i2s_tdm_dev *i2s_tdm, int stream) rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 0); if (i2s_tdm->clk_trcm) - rockchip_i2s_tdm_xfer_trcm_stop(i2s_tdm); + rockchip_i2s_tdm_xfer_trcm_stop(i2s_tdm, stream); else rockchip_i2s_tdm_xfer_stop(i2s_tdm, stream, false); } @@ -1513,14 +1468,20 @@ static bool is_params_dirty(struct snd_pcm_substream *substream, } static int rockchip_i2s_tdm_params_trcm(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai, unsigned int div_bclk, unsigned int div_lrck, unsigned int fmt) { struct rk_i2s_tdm_dev *i2s_tdm = to_info(dai); + struct snd_soc_component *comp = i2s_tdm->pcm_comp; unsigned long flags; + /* Prepare params changes for trcm dma guard resume */ + if (comp && comp->driver->hw_params) + comp->driver->hw_params(comp, substream, params); + spin_lock_irqsave(&i2s_tdm->lock, flags); if (atomic_read(&i2s_tdm->refcount)) rockchip_i2s_tdm_trcm_pause(substream, i2s_tdm); @@ -1545,6 +1506,9 @@ static int rockchip_i2s_tdm_params_trcm(struct snd_pcm_substream *substream, rockchip_i2s_tdm_trcm_resume(substream, i2s_tdm); spin_unlock_irqrestore(&i2s_tdm->lock, flags); + if (comp && !i2s_tdm->dma_guard_initialized) + i2s_tdm->dma_guard_initialized = true; + return 0; } @@ -1767,7 +1731,7 @@ static int rockchip_i2s_tdm_hw_params(struct snd_pcm_substream *substream, return 0; if (i2s_tdm->clk_trcm) - rockchip_i2s_tdm_params_trcm(substream, dai, div_bclk, div_lrck, val); + rockchip_i2s_tdm_params_trcm(substream, params, dai, div_bclk, div_lrck, val); else rockchip_i2s_tdm_params(substream, dai, div_bclk, div_lrck, val); @@ -2661,7 +2625,7 @@ static int rockchip_i2s_tdm_keep_clk_always_on(struct rk_i2s_tdm_dev *i2s_tdm) I2S_CKR_RSD(div_lrck) | I2S_CKR_TSD(div_lrck)); if (i2s_tdm->clk_trcm) - rockchip_i2s_tdm_xfer_trcm_start(i2s_tdm); + rockchip_i2s_tdm_xfer_trcm_start(i2s_tdm, SNDRV_PCM_STREAM_PLAYBACK); else rockchip_i2s_tdm_xfer_start(i2s_tdm, SNDRV_PCM_STREAM_PLAYBACK); @@ -2675,6 +2639,8 @@ static int rockchip_i2s_tdm_keep_clk_always_on(struct rk_i2s_tdm_dev *i2s_tdm) static int rockchip_i2s_tdm_register_platform(struct device *dev) { + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + struct snd_soc_component *comp; int ret = 0; if (device_property_read_bool(dev, "rockchip,no-dmaengine")) { @@ -2689,6 +2655,25 @@ static int rockchip_i2s_tdm_register_platform(struct device *dev) return ret; } + if (i2s_tdm->clk_trcm) { + ret = devm_snd_dmaengine_trcm_register(dev); + if (ret) { + dev_err(dev, "Could not register TRCM PCM\n"); + return ret; + } + + comp = snd_soc_lookup_component(i2s_tdm->dev, + SND_DMAENGINE_TRCM_DRV_NAME); + if (!comp) { + dev_err(dev, "Could not find TRCM PCM\n"); + ret = -ENODEV; + } + + i2s_tdm->pcm_comp = comp; + + return ret; + } + ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0); if (ret) dev_err(dev, "Could not register PCM\n"); @@ -2696,6 +2681,82 @@ static int rockchip_i2s_tdm_register_platform(struct device *dev) return ret; } +static int i2s_tdm_runtime_suspend(struct device *dev) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + + if (i2s_tdm->pcm_comp && i2s_tdm->clk_trcm) { + rockchip_i2s_tdm_dma_ctrl(i2s_tdm, 0, 0); + rockchip_i2s_tdm_dma_ctrl(i2s_tdm, 1, 0); + dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, 0, 0); + dmaengine_trcm_dma_guard_ctrl(i2s_tdm->pcm_comp, 1, 0); + } + + regcache_cache_only(i2s_tdm->regmap, true); + + clk_disable_unprepare(i2s_tdm->mclk_tx); + clk_disable_unprepare(i2s_tdm->mclk_rx); + + pinctrl_pm_select_idle_state(dev); + + return 0; +} + +static int rockchip_i2s_tdm_pinctrl_select_clk_state(struct device *dev) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + + if (IS_ERR_OR_NULL(i2s_tdm->pinctrl) || !i2s_tdm->clk_state) + return 0; + + pinctrl_select_state(i2s_tdm->pinctrl, i2s_tdm->clk_state); + + return 0; +} + +static int i2s_tdm_runtime_resume(struct device *dev) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev); + int ret; + + /* + * pinctrl default state is invoked by ASoC framework, so, + * we just handle clk state here if DT assigned. + */ + if (i2s_tdm->is_master_mode) + rockchip_i2s_tdm_pinctrl_select_clk_state(dev); + + ret = clk_prepare_enable(i2s_tdm->mclk_tx); + if (ret) + goto err_mclk_tx; + + ret = clk_prepare_enable(i2s_tdm->mclk_rx); + if (ret) + goto err_mclk_rx; + + regcache_cache_only(i2s_tdm->regmap, false); + regcache_mark_dirty(i2s_tdm->regmap); + ret = regcache_sync(i2s_tdm->regmap); + if (ret) + goto err_regmap; + + /* + * should be placed after regcache sync done to back + * to the slave mode and then enable clk state. + */ + if (!i2s_tdm->is_master_mode) + rockchip_i2s_tdm_pinctrl_select_clk_state(dev); + + return 0; + +err_regmap: + clk_disable_unprepare(i2s_tdm->mclk_rx); +err_mclk_rx: + clk_disable_unprepare(i2s_tdm->mclk_tx); +err_mclk_tx: + return ret; +} + static int rockchip_i2s_tdm_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; From 5f7f65028ae94e70cd5b6972122c6ff4969b7a7c Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Wed, 15 Mar 2023 09:20:34 +0800 Subject: [PATCH 03/11] ASoC: rockchip: i2s-tdm: Simplify clk sync on TRCM mode There are clk sync issue on TRCM mode for RK3308/RK1808/PX30, and the previous soluation is to make TX/RX reset at the same time to fix that. Unfortunately, it fails on PX30-I2S0, because the reset-TX and reset-RX bits can't be addressed at the same time. Now, we use another way to fix it: reset_control_assert(i2s_tdm->tx_reset); reset_control_assert(i2s_tdm->rx_reset); | gate the i2s src clk | reset_control_deassert(i2s_tdm->tx_reset); reset_control_deassert(i2s_tdm->rx_reset); | ungate the i2s src clk as shown above, use the pair gate/ungate src clk to make the TX/RX reset at the same time. Change-Id: Ib320ad13350516100eb11c7717e1bd9005abaf39 Signed-off-by: Sugar Zhang --- sound/soc/rockchip/rockchip_i2s_tdm.c | 344 ++++++++++++-------------- sound/soc/rockchip/rockchip_i2s_tdm.h | 56 ++++- 2 files changed, 213 insertions(+), 187 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 2d8e27b60ca6..efaa378dc36b 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -85,6 +85,8 @@ struct txrx_config { u32 rxonly; }; +struct rk_i2s_tdm_dev; + struct rk_i2s_soc_data { u32 softrst_offset; u32 grf_reg_offset; @@ -92,6 +94,7 @@ struct rk_i2s_soc_data { int config_count; const struct txrx_config *configs; int (*init)(struct device *dev, u32 addr); + void (*src_clk_ctrl)(struct rk_i2s_tdm_dev *i2s_tdm, bool en); }; struct rk_i2s_tdm_dev { @@ -126,9 +129,8 @@ struct rk_i2s_tdm_dev { struct pinctrl_state *clk_state; const struct rk_i2s_soc_data *soc_data; #ifdef HAVE_SYNC_RESET + int id; void __iomem *cru_base; - int tx_reset_id; - int rx_reset_id; #endif bool is_master_mode; bool io_multiplex; @@ -229,148 +231,146 @@ static inline bool is_dma_active(struct rk_i2s_tdm_dev *i2s_tdm, int stream) } #ifdef HAVE_SYNC_RESET -#if defined(CONFIG_ARM) && !defined(writeq) -static inline void __raw_writeq(u64 val, volatile void __iomem *addr) +static void rockchip_i2s_tdm_src_clk_ctrl(struct rk_i2s_tdm_dev *i2s_tdm, bool en, + unsigned int gate_reg, unsigned int gate_val, + unsigned int sel_reg) { - asm volatile("strd %0, %H0, [%1]" : : "r" (val), "r" (addr)); + int val = readl(i2s_tdm->cru_base + sel_reg); + + if (!gate_reg || !sel_reg) + return; + + if (IS_I2S_CLK_SRC_MCLKIN(val) && en) + writel(I2S_CLK_SRC_MCLKIN, i2s_tdm->cru_base + sel_reg); + + writel(gate_val, i2s_tdm->cru_base + gate_reg); + + if (IS_I2S_CLK_SRC_MCLKIN(val) && !en) + writel(I2S_CLK_SRC_PLL, i2s_tdm->cru_base + sel_reg); +} + +static void rockchip_i2s_tdm_px30_src_clk_ctrl(struct rk_i2s_tdm_dev *i2s_tdm, bool en) +{ + unsigned int gate_reg = 0, gate_val = 0, sel_reg = 0; + + switch (i2s_tdm->clk_trcm) { + case I2S_CKR_TRCM_TXONLY: + sel_reg = PX30_CLKSEL_CON28_I2S0_TX; + gate_reg = PX30_CLKGATE_CON9; + gate_val = en ? PX30_CLKGATE_CON9_I2S0_TX_PLL_EN : + PX30_CLKGATE_CON9_I2S0_TX_PLL_DIS; + break; + case I2S_CKR_TRCM_RXONLY: + sel_reg = PX30_CLKSEL_CON58_I2S0_RX; + gate_reg = PX30_CLKGATE_CON17; + gate_val = en ? PX30_CLKGATE_CON17_I2S0_RX_PLL_EN : + PX30_CLKGATE_CON17_I2S0_RX_PLL_DIS; + break; + } + + rockchip_i2s_tdm_src_clk_ctrl(i2s_tdm, en, gate_reg, gate_val, sel_reg); +} + +static void rockchip_i2s_tdm_rk1808_src_clk_ctrl(struct rk_i2s_tdm_dev *i2s_tdm, bool en) +{ + unsigned int gate_reg = 0, gate_val = 0, sel_reg = 0; + + switch (i2s_tdm->clk_trcm) { + case I2S_CKR_TRCM_TXONLY: + sel_reg = RK1808_CLKSEL_CON32_I2S0_TX; + gate_reg = RK1808_CLKGATE_CON17; + gate_val = en ? RK1808_CLKGATE_CON17_I2S0_TX_PLL_EN : + RK1808_CLKGATE_CON17_I2S0_TX_PLL_DIS; + break; + case I2S_CKR_TRCM_RXONLY: + sel_reg = RK1808_CLKSEL_CON34_I2S0_RX; + gate_reg = RK1808_CLKGATE_CON18; + gate_val = en ? RK1808_CLKGATE_CON18_I2S0_RX_PLL_EN : + RK1808_CLKGATE_CON18_I2S0_RX_PLL_DIS; + break; + } + + rockchip_i2s_tdm_src_clk_ctrl(i2s_tdm, en, gate_reg, gate_val, sel_reg); +} + +static void rockchip_i2s_tdm_rk3308_src_clk_ctrl(struct rk_i2s_tdm_dev *i2s_tdm, bool en) +{ + unsigned int gate_reg = 0, gate_val = 0, sel_reg = 0; + + /* I2S_8CH_2 used for internal and TRCM-none mode default */ + switch (i2s_tdm->id) { + case 0: + switch (i2s_tdm->clk_trcm) { + case I2S_CKR_TRCM_TXONLY: + sel_reg = RK3308_CLKSEL_CON52_I2S0_TX; + gate_reg = RK3308_CLKGATE_CON10; + gate_val = en ? RK3308_CLKGATE_CON10_I2S0_TX_PLL_EN : + RK3308_CLKGATE_CON10_I2S0_TX_PLL_DIS; + break; + case I2S_CKR_TRCM_RXONLY: + sel_reg = RK3308_CLKSEL_CON54_I2S0_RX; + gate_reg = RK3308_CLKGATE_CON11; + gate_val = en ? RK3308_CLKGATE_CON11_I2S0_RX_PLL_EN : + RK3308_CLKGATE_CON11_I2S0_RX_PLL_DIS; + break; + } + break; + case 1: + switch (i2s_tdm->clk_trcm) { + case I2S_CKR_TRCM_TXONLY: + sel_reg = RK3308_CLKSEL_CON56_I2S1_TX; + gate_reg = RK3308_CLKGATE_CON11; + gate_val = en ? RK3308_CLKGATE_CON11_I2S1_TX_PLL_EN : + RK3308_CLKGATE_CON11_I2S1_TX_PLL_DIS; + break; + case I2S_CKR_TRCM_RXONLY: + sel_reg = RK3308_CLKSEL_CON58_I2S1_RX; + gate_reg = RK3308_CLKGATE_CON11; + gate_val = en ? RK3308_CLKGATE_CON11_I2S1_RX_PLL_EN : + RK3308_CLKGATE_CON11_I2S1_RX_PLL_DIS; + break; + } + break; + } + + rockchip_i2s_tdm_src_clk_ctrl(i2s_tdm, en, gate_reg, gate_val, sel_reg); } -#define writeq(v,c) ({ __iowmb(); __raw_writeq((__force u64) cpu_to_le64(v), c); }) -#endif static void rockchip_i2s_tdm_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm) { - int tx_bank, rx_bank, tx_offset, rx_offset, tx_id, rx_id; - void __iomem *cru_reset, *addr; - unsigned long flags; - u64 val; - if (!i2s_tdm->cru_base || !i2s_tdm->soc_data || !i2s_tdm->is_master_mode) return; - tx_id = i2s_tdm->tx_reset_id; - rx_id = i2s_tdm->rx_reset_id; - if (tx_id < 0 || rx_id < 0) + if (IS_ERR_OR_NULL(i2s_tdm->tx_reset) || IS_ERR_OR_NULL(i2s_tdm->rx_reset)) return; - tx_bank = tx_id / 16; - tx_offset = tx_id % 16; - rx_bank = rx_id / 16; - rx_offset = rx_id % 16; + reset_control_assert(i2s_tdm->tx_reset); + reset_control_assert(i2s_tdm->rx_reset); - dev_dbg(i2s_tdm->dev, - "tx_bank: %d, rx_bank: %d,tx_offset: %d, rx_offset: %d\n", - tx_bank, rx_bank, tx_offset, rx_offset); - - cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset; - - switch (abs(tx_bank - rx_bank)) { - case 0: - writel(BIT(tx_offset) | BIT(rx_offset) | - (BIT(tx_offset) << 16) | (BIT(rx_offset) << 16), - cru_reset + (tx_bank * 4)); - break; - case 1: - if (tx_bank < rx_bank) { - val = BIT(rx_offset) | (BIT(rx_offset) << 16); - val <<= 32; - val |= BIT(tx_offset) | (BIT(tx_offset) << 16); - addr = cru_reset + (tx_bank * 4); - } else { - val = BIT(tx_offset) | (BIT(tx_offset) << 16); - val <<= 32; - val |= BIT(rx_offset) | (BIT(rx_offset) << 16); - addr = cru_reset + (rx_bank * 4); - } - - if (IS_ALIGNED((uintptr_t)addr, 8)) { - writeq(val, addr); - break; - } - fallthrough; - default: - local_irq_save(flags); - writel(BIT(tx_offset) | (BIT(tx_offset) << 16), - cru_reset + (tx_bank * 4)); - writel(BIT(rx_offset) | (BIT(rx_offset) << 16), - cru_reset + (rx_bank * 4)); - local_irq_restore(flags); - break; - } /* delay for reset assert done */ udelay(10); } static void rockchip_i2s_tdm_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm) { - int tx_bank, rx_bank, tx_offset, rx_offset, tx_id, rx_id; - void __iomem *cru_reset, *addr; - unsigned long flags; - u64 val; - if (!i2s_tdm->cru_base || !i2s_tdm->soc_data || !i2s_tdm->is_master_mode) return; - tx_id = i2s_tdm->tx_reset_id; - rx_id = i2s_tdm->rx_reset_id; - if (tx_id < 0 || rx_id < 0) + if (IS_ERR_OR_NULL(i2s_tdm->tx_reset) || IS_ERR_OR_NULL(i2s_tdm->rx_reset)) return; - tx_bank = tx_id / 16; - tx_offset = tx_id % 16; - rx_bank = rx_id / 16; - rx_offset = rx_id % 16; + if (i2s_tdm->soc_data && i2s_tdm->soc_data->src_clk_ctrl) + i2s_tdm->soc_data->src_clk_ctrl(i2s_tdm, 0); - dev_dbg(i2s_tdm->dev, - "tx_bank: %d, rx_bank: %d,tx_offset: %d, rx_offset: %d\n", - tx_bank, rx_bank, tx_offset, rx_offset); + reset_control_deassert(i2s_tdm->tx_reset); + reset_control_deassert(i2s_tdm->rx_reset); - cru_reset = i2s_tdm->cru_base + i2s_tdm->soc_data->softrst_offset; + if (i2s_tdm->soc_data && i2s_tdm->soc_data->src_clk_ctrl) + i2s_tdm->soc_data->src_clk_ctrl(i2s_tdm, 1); - switch (abs(tx_bank - rx_bank)) { - case 0: - writel((BIT(tx_offset) << 16) | (BIT(rx_offset) << 16), - cru_reset + (tx_bank * 4)); - break; - case 1: - if (tx_bank < rx_bank) { - val = (BIT(rx_offset) << 16); - val <<= 32; - val |= (BIT(tx_offset) << 16); - addr = cru_reset + (tx_bank * 4); - } else { - val = (BIT(tx_offset) << 16); - val <<= 32; - val |= (BIT(rx_offset) << 16); - addr = cru_reset + (rx_bank * 4); - } - - if (IS_ALIGNED((uintptr_t)addr, 8)) { - writeq(val, addr); - break; - } - fallthrough; - default: - local_irq_save(flags); - writel((BIT(tx_offset) << 16), - cru_reset + (tx_bank * 4)); - writel((BIT(rx_offset) << 16), - cru_reset + (rx_bank * 4)); - local_irq_restore(flags); - break; - } /* delay for reset deassert done */ udelay(10); } - -/* - * make sure both tx and rx are reset at the same time for sync lrck - * when clk_trcm > 0 - */ -static void rockchip_i2s_tdm_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm) -{ - rockchip_i2s_tdm_reset_assert(i2s_tdm); - rockchip_i2s_tdm_reset_deassert(i2s_tdm); -} #else static inline void rockchip_i2s_tdm_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm) { @@ -378,44 +378,35 @@ static inline void rockchip_i2s_tdm_reset_assert(struct rk_i2s_tdm_dev *i2s_tdm) static inline void rockchip_i2s_tdm_reset_deassert(struct rk_i2s_tdm_dev *i2s_tdm) { } -static inline void rockchip_i2s_tdm_sync_reset(struct rk_i2s_tdm_dev *i2s_tdm) -{ -} #endif -static void rockchip_i2s_tdm_reset(struct reset_control *rc) +static void rockchip_i2s_tdm_reset(struct rk_i2s_tdm_dev *i2s_tdm, unsigned int clr) { - if (IS_ERR_OR_NULL(rc)) - return; + if ((clr & I2S_CLR_TXC) && !IS_ERR_OR_NULL(i2s_tdm->tx_reset)) { + reset_control_assert(i2s_tdm->tx_reset); + /* delay for reset assert done */ + udelay(10); + reset_control_deassert(i2s_tdm->tx_reset); + /* delay for reset deassert done */ + udelay(10); + } - reset_control_assert(rc); - /* delay for reset assert done */ - udelay(10); - reset_control_deassert(rc); - /* delay for reset deassert done */ - udelay(10); + if ((clr & I2S_CLR_RXC) && !IS_ERR_OR_NULL(i2s_tdm->rx_reset)) { + reset_control_assert(i2s_tdm->rx_reset); + /* delay for reset assert done */ + udelay(10); + reset_control_deassert(i2s_tdm->rx_reset); + /* delay for reset deassert done */ + udelay(10); + } } static int rockchip_i2s_tdm_clear(struct rk_i2s_tdm_dev *i2s_tdm, unsigned int clr) { - struct reset_control *rst = NULL; unsigned int val = 0; int ret = 0; - switch (clr) { - case I2S_CLR_TXC: - rst = i2s_tdm->tx_reset; - break; - case I2S_CLR_RXC: - rst = i2s_tdm->rx_reset; - break; - case I2S_CLR_TXC | I2S_CLR_RXC: - break; - default: - return -EINVAL; - } - regmap_update_bits(i2s_tdm->regmap, I2S_CLR, clr, clr); ret = regmap_read_poll_timeout_atomic(i2s_tdm->regmap, I2S_CLR, val, !(val & clr), 10, 100); @@ -453,10 +444,7 @@ static int rockchip_i2s_tdm_clear(struct rk_i2s_tdm_dev *i2s_tdm, return 0; reset: - if (i2s_tdm->clk_trcm) - rockchip_i2s_tdm_sync_reset(i2s_tdm); - else - rockchip_i2s_tdm_reset(rst); + rockchip_i2s_tdm_reset(i2s_tdm, clr); return 0; } @@ -2282,6 +2270,9 @@ static const struct rk_i2s_soc_data px30_i2s_soc_data = { .configs = px30_txrx_config, .config_count = ARRAY_SIZE(px30_txrx_config), .init = common_soc_init, +#ifdef HAVE_SYNC_RESET + .src_clk_ctrl = rockchip_i2s_tdm_px30_src_clk_ctrl, +#endif }; static const struct rk_i2s_soc_data rk1808_i2s_soc_data = { @@ -2289,6 +2280,9 @@ static const struct rk_i2s_soc_data rk1808_i2s_soc_data = { .configs = rk1808_txrx_config, .config_count = ARRAY_SIZE(rk1808_txrx_config), .init = common_soc_init, +#ifdef HAVE_SYNC_RESET + .src_clk_ctrl = rockchip_i2s_tdm_rk1808_src_clk_ctrl, +#endif }; static const struct rk_i2s_soc_data rk3308_i2s_soc_data = { @@ -2298,6 +2292,9 @@ static const struct rk_i2s_soc_data rk3308_i2s_soc_data = { .configs = rk3308_txrx_config, .config_count = ARRAY_SIZE(rk3308_txrx_config), .init = common_soc_init, +#ifdef HAVE_SYNC_RESET + .src_clk_ctrl = rockchip_i2s_tdm_rk3308_src_clk_ctrl, +#endif }; static const struct rk_i2s_soc_data rk3568_i2s_soc_data = { @@ -2339,26 +2336,6 @@ static const struct of_device_id rockchip_i2s_tdm_match[] = { {}, }; -#ifdef HAVE_SYNC_RESET -static int of_i2s_resetid_get(struct device_node *node, - const char *id) -{ - struct of_phandle_args args; - int index = 0; - int ret; - - if (id) - index = of_property_match_string(node, - "reset-names", id); - ret = of_parse_phandle_with_args(node, "resets", "#reset-cells", - index, &args); - if (ret) - return ret; - - return args.args[0]; -} -#endif - static int rockchip_i2s_tdm_dai_prepare(struct platform_device *pdev, struct snd_soc_dai_driver **soc_dai) { @@ -2878,24 +2855,6 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) } } -#ifdef HAVE_SYNC_RESET - sync = of_device_is_compatible(node, "rockchip,px30-i2s-tdm") || - of_device_is_compatible(node, "rockchip,rk1808-i2s-tdm") || - of_device_is_compatible(node, "rockchip,rk3308-i2s-tdm"); - - if (i2s_tdm->clk_trcm && sync) { - struct device_node *cru_node; - - cru_node = of_parse_phandle(node, "rockchip,cru", 0); - i2s_tdm->cru_base = of_iomap(cru_node, 0); - if (!i2s_tdm->cru_base) - return -ENOENT; - - i2s_tdm->tx_reset_id = of_i2s_resetid_get(node, "tx-m"); - i2s_tdm->rx_reset_id = of_i2s_resetid_get(node, "rx-m"); - } -#endif - i2s_tdm->tx_reset = devm_reset_control_get(&pdev->dev, "tx-m"); if (IS_ERR(i2s_tdm->tx_reset)) { ret = PTR_ERR(i2s_tdm->tx_reset); @@ -3017,6 +2976,23 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) return ret; } +#ifdef HAVE_SYNC_RESET + sync = of_device_is_compatible(node, "rockchip,px30-i2s-tdm") || + of_device_is_compatible(node, "rockchip,rk1808-i2s-tdm") || + of_device_is_compatible(node, "rockchip,rk3308-i2s-tdm"); + + if (i2s_tdm->clk_trcm && sync) { + struct device_node *cru_node; + + cru_node = of_parse_phandle(node, "rockchip,cru", 0); + i2s_tdm->cru_base = of_iomap(cru_node, 0); + if (!i2s_tdm->cru_base) + return -ENOENT; + + i2s_tdm->id = (res->start >> 16) & GENMASK(3, 0); + } +#endif + /* * MUST: after pm_runtime_enable step, any register R/W * should be wrapped with pm_runtime_get_sync/put. diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.h b/sound/soc/rockchip/rockchip_i2s_tdm.h index 904afe035fbd..f625cdf0ffaf 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.h +++ b/sound/soc/rockchip/rockchip_i2s_tdm.h @@ -324,7 +324,25 @@ enum { #define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16)) -/* PX30 GRF CONFIGS*/ +/* I2Sx CLK SRC Mux Common Define */ +#define I2S_CLK_SRC(v) (((v) & GENMASK(11, 10)) >> 10) +#define I2S_CLK_SRC_MCLKIN HIWORD_UPDATE(2, 11, 10) +#define I2S_CLK_SRC_PLL HIWORD_UPDATE(0, 11, 10) +#define IS_I2S_CLK_SRC_MCLKIN(v) (I2S_CLK_SRC(v) == 2) + +/* PX30 CRU CONFIGS */ +#define PX30_CLKSEL_CON28_I2S0_TX 0x170 +#define PX30_CLKSEL_CON58_I2S0_RX 0x1e8 + +#define PX30_CLKGATE_CON9 0x224 +#define PX30_CLKGATE_CON9_I2S0_TX_PLL_DIS HIWORD_UPDATE(1, 12, 12) +#define PX30_CLKGATE_CON9_I2S0_TX_PLL_EN HIWORD_UPDATE(0, 12, 12) + +#define PX30_CLKGATE_CON17 0x244 +#define PX30_CLKGATE_CON17_I2S0_RX_PLL_DIS HIWORD_UPDATE(1, 0, 0) +#define PX30_CLKGATE_CON17_I2S0_RX_PLL_EN HIWORD_UPDATE(0, 0, 0) + +/* PX30 GRF CONFIGS */ #define PX30_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 13, 12) #define PX30_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 13, 12) #define PX30_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5) @@ -336,7 +354,19 @@ enum { #define PX30_I2S0_CLK_RXONLY \ (PX30_I2S0_MCLK_OUT_SRC_FROM_RX | PX30_I2S0_CLK_IN_SRC_FROM_RX) -/* RK1808 GRF CONFIGS*/ +/* RK1808 CRU CONFIGS */ +#define RK1808_CLKSEL_CON32_I2S0_TX 0x180 +#define RK1808_CLKSEL_CON34_I2S0_RX 0x188 + +#define RK1808_CLKGATE_CON17 0x274 +#define RK1808_CLKGATE_CON17_I2S0_TX_PLL_DIS HIWORD_UPDATE(1, 12, 12) +#define RK1808_CLKGATE_CON17_I2S0_TX_PLL_EN HIWORD_UPDATE(0, 12, 12) + +#define RK1808_CLKGATE_CON18 0x278 +#define RK1808_CLKGATE_CON18_I2S0_RX_PLL_DIS HIWORD_UPDATE(1, 0, 0) +#define RK1808_CLKGATE_CON18_I2S0_RX_PLL_EN HIWORD_UPDATE(0, 0, 0) + +/* RK1808 GRF CONFIGS */ #define RK1808_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2) #define RK1808_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2) #define RK1808_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 1, 0) @@ -348,7 +378,27 @@ enum { #define RK1808_I2S0_CLK_RXONLY \ (RK1808_I2S0_MCLK_OUT_SRC_FROM_RX | RK1808_I2S0_CLK_IN_SRC_FROM_RX) -/* RK3308 GRF CONFIGS*/ +/* RK3308 CRU CONFIGS */ +#define RK3308_CLKSEL_CON52_I2S0_TX 0x1d0 +#define RK3308_CLKSEL_CON54_I2S0_RX 0x1d8 +#define RK3308_CLKSEL_CON56_I2S1_TX 0x1e0 +#define RK3308_CLKSEL_CON58_I2S1_RX 0x1e8 + +#define RK3308_CLKGATE_CON10 0x328 +#define RK3308_CLKGATE_CON10_I2S0_TX_PLL_DIS HIWORD_UPDATE(1, 12, 12) +#define RK3308_CLKGATE_CON10_I2S0_TX_PLL_EN HIWORD_UPDATE(0, 12, 12) + +#define RK3308_CLKGATE_CON11 0x32c +#define RK3308_CLKGATE_CON11_I2S0_RX_PLL_DIS HIWORD_UPDATE(1, 0, 0) +#define RK3308_CLKGATE_CON11_I2S0_RX_PLL_EN HIWORD_UPDATE(0, 0, 0) + +#define RK3308_CLKGATE_CON11_I2S1_TX_PLL_DIS HIWORD_UPDATE(1, 4, 4) +#define RK3308_CLKGATE_CON11_I2S1_TX_PLL_EN HIWORD_UPDATE(0, 4, 4) + +#define RK3308_CLKGATE_CON11_I2S1_RX_PLL_DIS HIWORD_UPDATE(1, 8, 8) +#define RK3308_CLKGATE_CON11_I2S1_RX_PLL_EN HIWORD_UPDATE(0, 8, 8) + +/* RK3308 GRF CONFIGS */ #define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 10, 10) #define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 10, 10) #define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 9, 9) From 13c446a390d3d7a9e0748a3ab5c951e2031ae27f Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 22 Dec 2023 10:00:45 +0800 Subject: [PATCH 04/11] ASoC: rockchip: i2s-tdm: Handle HCLK with runtime PM This patch handle HCLK with runtime PM to simplify code. Signed-off-by: Sugar Zhang Change-Id: I4a75b0998053e7ef1edb3ecf3bc50ab84eddce70 --- sound/soc/rockchip/rockchip_i2s_tdm.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index efaa378dc36b..c10b143fbd1d 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -2673,6 +2673,7 @@ static int i2s_tdm_runtime_suspend(struct device *dev) clk_disable_unprepare(i2s_tdm->mclk_tx); clk_disable_unprepare(i2s_tdm->mclk_rx); + clk_disable_unprepare(i2s_tdm->hclk); pinctrl_pm_select_idle_state(dev); @@ -2703,6 +2704,10 @@ static int i2s_tdm_runtime_resume(struct device *dev) if (i2s_tdm->is_master_mode) rockchip_i2s_tdm_pinctrl_select_clk_state(dev); + ret = clk_prepare_enable(i2s_tdm->hclk); + if (ret) + goto err_hclk; + ret = clk_prepare_enable(i2s_tdm->mclk_tx); if (ret) goto err_mclk_tx; @@ -2731,6 +2736,8 @@ err_regmap: err_mclk_rx: clk_disable_unprepare(i2s_tdm->mclk_tx); err_mclk_tx: + clk_disable_unprepare(i2s_tdm->hclk); +err_hclk: return ret; } @@ -2873,10 +2880,6 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) if (IS_ERR(i2s_tdm->hclk)) return PTR_ERR(i2s_tdm->hclk); - ret = clk_prepare_enable(i2s_tdm->hclk); - if (ret) - return ret; - i2s_tdm->mclk_tx = devm_clk_get(&pdev->dev, "mclk_tx"); if (IS_ERR(i2s_tdm->mclk_tx)) return PTR_ERR(i2s_tdm->mclk_tx); @@ -3035,16 +3038,10 @@ err_pm_disable: static int rockchip_i2s_tdm_remove(struct platform_device *pdev) { - struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&pdev->dev); - pm_runtime_disable(&pdev->dev); if (!pm_runtime_status_suspended(&pdev->dev)) i2s_tdm_runtime_suspend(&pdev->dev); - clk_disable_unprepare(i2s_tdm->mclk_tx); - clk_disable_unprepare(i2s_tdm->mclk_rx); - clk_disable_unprepare(i2s_tdm->hclk); - return 0; } From e1d8735a435b26ff49d10136b3e7f799e1baba32 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 22 Dec 2023 09:27:41 +0800 Subject: [PATCH 05/11] ASoC: rockchip: i2s-tdm: Fix warning This patch fix resources from of_iomap() not released. Signed-off-by: Sugar Zhang Change-Id: I46a7f4996bc751ade34071727a350137a313b446 --- sound/soc/rockchip/rockchip_i2s_tdm.c | 143 ++++++++++++++++---------- 1 file changed, 89 insertions(+), 54 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index c10b143fbd1d..b8e917e7b407 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -794,6 +794,64 @@ static int rockchip_i2s_tdm_multi_lanes_start(struct rk_i2s_tdm_dev *i2s_tdm, in return 0; } + +static int rockchip_i2s_tdm_multi_lanes_parse(struct rk_i2s_tdm_dev *i2s_tdm) +{ + struct device_node *clk_src_node = NULL; + unsigned int val; + int ret; + + i2s_tdm->is_tdm_multi_lanes = + device_property_read_bool(i2s_tdm->dev, "rockchip,tdm-multi-lanes"); + + if (!i2s_tdm->is_tdm_multi_lanes) + return 0; + + i2s_tdm->tx_lanes = 1; + i2s_tdm->rx_lanes = 1; + + if (!device_property_read_u32(i2s_tdm->dev, "rockchip,tdm-tx-lanes", &val)) { + if ((val >= 1) && (val <= 4)) + i2s_tdm->tx_lanes = val; + } + + if (!device_property_read_u32(i2s_tdm->dev, "rockchip,tdm-rx-lanes", &val)) { + if ((val >= 1) && (val <= 4)) + i2s_tdm->rx_lanes = val; + } + + i2s_tdm->i2s_lrck_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "i2s-lrck", GPIOD_IN); + if (IS_ERR(i2s_tdm->i2s_lrck_gpio)) { + ret = PTR_ERR(i2s_tdm->i2s_lrck_gpio); + dev_err(i2s_tdm->dev, "Failed to get i2s_lrck_gpio %d\n", ret); + return ret; + } + + i2s_tdm->tdm_fsync_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "tdm-fsync", GPIOD_IN); + if (IS_ERR(i2s_tdm->tdm_fsync_gpio)) { + ret = PTR_ERR(i2s_tdm->tdm_fsync_gpio); + dev_err(i2s_tdm->dev, "Failed to get tdm_fsync_gpio %d\n", ret); + return ret; + } + + /* It's optional, required when use soc clk src, such as: i2s2_2ch */ + clk_src_node = of_parse_phandle(i2s_tdm->dev->of_node, "rockchip,clk-src", 0); + if (clk_src_node) { + i2s_tdm->clk_src_dai = rockchip_i2s_tdm_find_dai(clk_src_node); + if (!i2s_tdm->clk_src_dai) + return -EPROBE_DEFER; + + i2s_tdm->clk_src_base = of_iomap(clk_src_node, 0); + if (!i2s_tdm->clk_src_base) + return -ENOENT; + + pm_runtime_forbid(i2s_tdm->clk_src_dai->dev); + } + + dev_info(i2s_tdm->dev, "Used as TDM_MULTI_LANES mode\n"); + + return 0; +} #endif static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm, @@ -2741,6 +2799,19 @@ err_hclk: return ret; } +static void __maybe_unused rockchip_i2s_tdm_unmap(struct rk_i2s_tdm_dev *i2s_tdm) +{ +#ifdef HAVE_SYNC_RESET + if (i2s_tdm->cru_base) + iounmap(i2s_tdm->cru_base); +#endif + +#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES + if (i2s_tdm->clk_src_base) + iounmap(i2s_tdm->clk_src_base); +#endif +} + static int rockchip_i2s_tdm_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -2769,58 +2840,6 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) if (!of_id) return -EINVAL; -#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES - i2s_tdm->is_tdm_multi_lanes = - device_property_read_bool(i2s_tdm->dev, "rockchip,tdm-multi-lanes"); - - if (i2s_tdm->is_tdm_multi_lanes) { - struct device_node *clk_src_node = NULL; - - i2s_tdm->tx_lanes = 1; - i2s_tdm->rx_lanes = 1; - - if (!device_property_read_u32(i2s_tdm->dev, "rockchip,tdm-tx-lanes", &val)) { - if ((val >= 1) && (val <= 4)) - i2s_tdm->tx_lanes = val; - } - - if (!device_property_read_u32(i2s_tdm->dev, "rockchip,tdm-rx-lanes", &val)) { - if ((val >= 1) && (val <= 4)) - i2s_tdm->rx_lanes = val; - } - - i2s_tdm->i2s_lrck_gpio = devm_gpiod_get_optional(&pdev->dev, "i2s-lrck", GPIOD_IN); - if (IS_ERR(i2s_tdm->i2s_lrck_gpio)) { - ret = PTR_ERR(i2s_tdm->i2s_lrck_gpio); - dev_err(&pdev->dev, "Failed to get i2s_lrck_gpio %d\n", ret); - return ret; - } - - i2s_tdm->tdm_fsync_gpio = devm_gpiod_get_optional(&pdev->dev, "tdm-fsync", GPIOD_IN); - if (IS_ERR(i2s_tdm->tdm_fsync_gpio)) { - ret = PTR_ERR(i2s_tdm->tdm_fsync_gpio); - dev_err(&pdev->dev, "Failed to get tdm_fsync_gpio %d\n", ret); - return ret; - } - - /* It's optional, required when use soc clk src, such as: i2s2_2ch */ - clk_src_node = of_parse_phandle(node, "rockchip,clk-src", 0); - if (clk_src_node) { - i2s_tdm->clk_src_base = of_iomap(clk_src_node, 0); - if (!i2s_tdm->clk_src_base) - return -ENOENT; - - i2s_tdm->clk_src_dai = rockchip_i2s_tdm_find_dai(clk_src_node); - if (!i2s_tdm->clk_src_dai) - return -EPROBE_DEFER; - - pm_runtime_forbid(i2s_tdm->clk_src_dai->dev); - } - - dev_info(&pdev->dev, "Used as TDM_MULTI_LANES mode\n"); - } -#endif - spin_lock_init(&i2s_tdm->lock); i2s_tdm->soc_data = (const struct rk_i2s_soc_data *)of_id->data; @@ -2979,6 +2998,12 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) return ret; } +#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES + ret = rockchip_i2s_tdm_multi_lanes_parse(i2s_tdm); + if (ret) + goto err_unmap; +#endif + #ifdef HAVE_SYNC_RESET sync = of_device_is_compatible(node, "rockchip,px30-i2s-tdm") || of_device_is_compatible(node, "rockchip,rk1808-i2s-tdm") || @@ -2989,8 +3014,10 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) cru_node = of_parse_phandle(node, "rockchip,cru", 0); i2s_tdm->cru_base = of_iomap(cru_node, 0); - if (!i2s_tdm->cru_base) - return -ENOENT; + if (!i2s_tdm->cru_base) { + ret = -ENOENT; + goto err_unmap; + } i2s_tdm->id = (res->start >> 16) & GENMASK(3, 0); } @@ -3033,6 +3060,11 @@ err_suspend: err_pm_disable: pm_runtime_disable(&pdev->dev); +#if defined(HAVE_SYNC_RESET) || defined(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES) +err_unmap: + rockchip_i2s_tdm_unmap(i2s_tdm); +#endif + return ret; } @@ -3042,6 +3074,9 @@ static int rockchip_i2s_tdm_remove(struct platform_device *pdev) if (!pm_runtime_status_suspended(&pdev->dev)) i2s_tdm_runtime_suspend(&pdev->dev); +#if defined(HAVE_SYNC_RESET) || defined(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES) + rockchip_i2s_tdm_unmap(dev_get_drvdata(&pdev->dev)); +#endif return 0; } From 16e6fedff09464453ee62f910ee1d4c791040638 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Wed, 27 Dec 2023 21:25:36 +0800 Subject: [PATCH 06/11] ASoC: rockchip: i2s-tdm: Use GPIOD_ASIS for TDM_MULTI_LANES Situation for 'i2s-lrck': Should use flag GPIOD_ASIS not to reclaim LRCK pin as GPIO function, because we use the same PIN and just read EXT_PORT value which show the pin status. Situation for 'tdm-fsync': A. when the pin is a generic gpio as the ref signal pin which is drived from external. should use flag GPIOD_IN to reclaim as GPIO_IN function. B. when the pin is the same pin from the 'clk-src' on the same SoC, we can use the 'clk-src' fsync out signal as the 'tdm-fsync' to query status. in this case, should use flag GPIOD_ASIS not to reclaim it as GPIO. Signed-off-by: Sugar Zhang Change-Id: I529d982a7e49cb90c02b7670a58759607f4e4f32 --- sound/soc/rockchip/rockchip_i2s_tdm.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index b8e917e7b407..9bc447110173 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -798,6 +798,7 @@ static int rockchip_i2s_tdm_multi_lanes_start(struct rk_i2s_tdm_dev *i2s_tdm, in static int rockchip_i2s_tdm_multi_lanes_parse(struct rk_i2s_tdm_dev *i2s_tdm) { struct device_node *clk_src_node = NULL; + enum gpiod_flags gpiod_flags; unsigned int val; int ret; @@ -820,22 +821,40 @@ static int rockchip_i2s_tdm_multi_lanes_parse(struct rk_i2s_tdm_dev *i2s_tdm) i2s_tdm->rx_lanes = val; } - i2s_tdm->i2s_lrck_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "i2s-lrck", GPIOD_IN); + /* + * Should use flag GPIOD_ASIS not to reclaim LRCK pin as GPIO function, + * because we use the same PIN and just read EXT_PORT value which show + * the pin status. + */ + i2s_tdm->i2s_lrck_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "i2s-lrck", + GPIOD_ASIS); if (IS_ERR(i2s_tdm->i2s_lrck_gpio)) { ret = PTR_ERR(i2s_tdm->i2s_lrck_gpio); dev_err(i2s_tdm->dev, "Failed to get i2s_lrck_gpio %d\n", ret); return ret; } - i2s_tdm->tdm_fsync_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "tdm-fsync", GPIOD_IN); + /* It's optional, required when use soc clk src, such as: i2s2_2ch */ + clk_src_node = of_parse_phandle(i2s_tdm->dev->of_node, "rockchip,clk-src", 0); + gpiod_flags = clk_src_node ? GPIOD_ASIS : GPIOD_IN; + /* + * Two situation for 'tdm-fsync': + * + * A. when the pin is a generic gpio as the ref signal pin which is drived from + * external. should use flag GPIOD_IN to reclaim as GPIO_IN function. + * + * B. when the pin is the same pin from the 'clk-src' on the same SoC, we can + * use the 'clk-src' fsync out signal as the 'tdm-fsync' to query status. + * in this case, should use flag GPIOD_ASIS not to reclaim it as GPIO. + */ + i2s_tdm->tdm_fsync_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "tdm-fsync", + gpiod_flags); if (IS_ERR(i2s_tdm->tdm_fsync_gpio)) { ret = PTR_ERR(i2s_tdm->tdm_fsync_gpio); dev_err(i2s_tdm->dev, "Failed to get tdm_fsync_gpio %d\n", ret); return ret; } - /* It's optional, required when use soc clk src, such as: i2s2_2ch */ - clk_src_node = of_parse_phandle(i2s_tdm->dev->of_node, "rockchip,clk-src", 0); if (clk_src_node) { i2s_tdm->clk_src_dai = rockchip_i2s_tdm_find_dai(clk_src_node); if (!i2s_tdm->clk_src_dai) From 9b3626c630780f4bb592f3402bc6769d8cc655cd Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 29 Dec 2023 17:06:00 +0800 Subject: [PATCH 07/11] ASoC: rockchip: i2s-tdm: Fix channels order for TDM_ONE_FRAME This patch fix multi-channels order for TDM_ONE_FRAME on SLAVE mode. Signed-off-by: Sugar Zhang Change-Id: Ib6543cf0740516d5df1570245abf07fcce9082c3 --- sound/soc/rockchip/rockchip_i2s_tdm.c | 268 +++++++++++++++----------- 1 file changed, 159 insertions(+), 109 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 9bc447110173..449ddd4d9276 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -56,8 +56,6 @@ * */ -#define CLK_MAX_COUNT 1000 -#define NSAMPLES 4 #define XFER_EN 0x3 #define XFER_DIS 0x0 #define CKR_V(m, r, t) ((m - 1) << 16 | (r - 1) << 8 | (t - 1) << 0) @@ -66,6 +64,8 @@ #define I2S_XCR_IBM_LSJM I2S_TXCR_IBM_LSJM #endif +#define CLK_MAX_COUNT 1000 +#define NSAMPLES 4 #define DEFAULT_MCLK_FS 256 #define DEFAULT_FS 48000 #define CH_GRP_MAX 4 /* The max channel 8 / 2 */ @@ -154,9 +154,9 @@ struct rk_i2s_tdm_dev { int clk_ppm; atomic_t refcount; spinlock_t lock; /* xfer lock */ + struct gpio_desc *i2s_lrck_gpio; #ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES struct snd_soc_dai *clk_src_dai; - struct gpio_desc *i2s_lrck_gpio; struct gpio_desc *tdm_fsync_gpio; unsigned int tx_lanes; unsigned int rx_lanes; @@ -522,6 +522,87 @@ static void rockchip_i2s_tdm_dma_ctrl(struct rk_i2s_tdm_dev *i2s_tdm, rockchip_i2s_tdm_fifo_xrun_detect(i2s_tdm, stream, 1); } +static inline int rockchip_i2s_tdm_clk_assert_h(const struct gpio_desc *desc) +{ + int cnt = CLK_MAX_COUNT; + + while (gpiod_get_raw_value(desc) && --cnt) + ; + + return cnt; +} + +static inline int rockchip_i2s_tdm_clk_assert_l(const struct gpio_desc *desc) +{ + int cnt = CLK_MAX_COUNT; + + while (!gpiod_get_raw_value(desc) && --cnt) + ; + + return cnt; +} + +static inline bool rockchip_i2s_tdm_clk_valid(struct rk_i2s_tdm_dev *i2s_tdm, + bool has_fsync) +{ + int dc_h = CLK_MAX_COUNT, dc_l = CLK_MAX_COUNT; + + /* + * TBD: optimize debounce and get value + * + * debounce at least one cycle found, otherwise, the clk ref maybe + * not on the fly. + */ + + /* check HIGH-Level */ + dc_h = rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + if (!dc_h) + return false; + + /* check LOW-Level */ + dc_l = rockchip_i2s_tdm_clk_assert_l(i2s_tdm->i2s_lrck_gpio); + if (!dc_l) + return false; + +#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES + if (!has_fsync) + return true; + + /* check HIGH-Level */ + dc_h = rockchip_i2s_tdm_clk_assert_h(i2s_tdm->tdm_fsync_gpio); + if (!dc_h) + return false; + + /* check LOW-Level */ + dc_l = rockchip_i2s_tdm_clk_assert_l(i2s_tdm->tdm_fsync_gpio); + if (!dc_l) + return false; +#endif + + return true; +} + +static void __maybe_unused rockchip_i2s_tdm_gpio_clk_meas(struct rk_i2s_tdm_dev *i2s_tdm, + const struct gpio_desc *desc, + const char *name) +{ + int h[NSAMPLES], l[NSAMPLES], i; + + dev_dbg(i2s_tdm->dev, "%s:\n", name); + + if (!rockchip_i2s_tdm_clk_valid(i2s_tdm, 1)) + return; + + for (i = 0; i < NSAMPLES; i++) { + h[i] = rockchip_i2s_tdm_clk_assert_h(desc); + l[i] = rockchip_i2s_tdm_clk_assert_l(desc); + } + + for (i = 0; i < NSAMPLES; i++) + dev_dbg(i2s_tdm->dev, "H[%d]: %2d, L[%d]: %2d\n", + i, CLK_MAX_COUNT - h[i], i, CLK_MAX_COUNT - l[i]); +} + #ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES static const char * const tx_lanes_text[] = { "Auto", "SDOx1", "SDOx2", "SDOx3", "SDOx4" }; static const char * const rx_lanes_text[] = { "Auto", "SDIx1", "SDIx2", "SDIx3", "SDIx4" }; @@ -639,81 +720,6 @@ static int rockchip_i2s_tdm_multi_lanes_set_clk(struct snd_pcm_substream *substr return 0; } -static inline int tdm_multi_lanes_clk_assert_h(const struct gpio_desc *desc) -{ - int cnt = CLK_MAX_COUNT; - - while (gpiod_get_raw_value(desc) && --cnt) - ; - - return cnt; -} - -static inline int tdm_multi_lanes_clk_assert_l(const struct gpio_desc *desc) -{ - int cnt = CLK_MAX_COUNT; - - while (!gpiod_get_raw_value(desc) && --cnt) - ; - - return cnt; -} - -static inline bool rockchip_i2s_tdm_clk_valid(struct rk_i2s_tdm_dev *i2s_tdm) -{ - int dc_h = CLK_MAX_COUNT, dc_l = CLK_MAX_COUNT; - - /* - * TBD: optimize debounce and get value - * - * debounce at least one cycle found, otherwise, the clk ref maybe - * not on the fly. - */ - - /* check HIGH-Level */ - dc_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio); - if (!dc_h) - return false; - - /* check LOW-Level */ - dc_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio); - if (!dc_l) - return false; - - /* check HIGH-Level */ - dc_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->tdm_fsync_gpio); - if (!dc_h) - return false; - - /* check LOW-Level */ - dc_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->tdm_fsync_gpio); - if (!dc_l) - return false; - - return true; -} - -static void __maybe_unused rockchip_i2s_tdm_gpio_clk_meas(struct rk_i2s_tdm_dev *i2s_tdm, - const struct gpio_desc *desc, - const char *name) -{ - int h[NSAMPLES], l[NSAMPLES], i; - - dev_dbg(i2s_tdm->dev, "%s:\n", name); - - if (!rockchip_i2s_tdm_clk_valid(i2s_tdm)) - return; - - for (i = 0; i < NSAMPLES; i++) { - h[i] = tdm_multi_lanes_clk_assert_h(desc); - l[i] = tdm_multi_lanes_clk_assert_l(desc); - } - - for (i = 0; i < NSAMPLES; i++) - dev_dbg(i2s_tdm->dev, "H[%d]: %2d, L[%d]: %2d\n", - i, CLK_MAX_COUNT - h[i], i, CLK_MAX_COUNT - l[i]); -} - static int rockchip_i2s_tdm_multi_lanes_start(struct rk_i2s_tdm_dev *i2s_tdm, int stream) { unsigned int tdm_h = 0, tdm_l = 0, i2s_h = 0, i2s_l = 0; @@ -741,7 +747,7 @@ static int rockchip_i2s_tdm_multi_lanes_start(struct rk_i2s_tdm_dev *i2s_tdm, in local_irq_save(flags); - if (!rockchip_i2s_tdm_clk_valid(i2s_tdm)) { + if (!rockchip_i2s_tdm_clk_valid(i2s_tdm, 1)) { local_irq_restore(flags); dev_err(i2s_tdm->dev, "Invalid LRCK / FSYNC measured by ref IO\n"); return -EINVAL; @@ -749,36 +755,36 @@ static int rockchip_i2s_tdm_multi_lanes_start(struct rk_i2s_tdm_dev *i2s_tdm, in switch (fmt) { case I2S_XCR_IBM_NORMAL: - tdm_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->tdm_fsync_gpio); - tdm_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->tdm_fsync_gpio); + tdm_h = rockchip_i2s_tdm_clk_assert_h(i2s_tdm->tdm_fsync_gpio); + tdm_l = rockchip_i2s_tdm_clk_assert_l(i2s_tdm->tdm_fsync_gpio); if (i2s_tdm->lrck_ratio == 8) { - tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio); - tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio); - tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio); - tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_l(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_l(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); } - i2s_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio); + i2s_l = rockchip_i2s_tdm_clk_assert_l(i2s_tdm->i2s_lrck_gpio); if (stream == SNDRV_PCM_STREAM_CAPTURE) - i2s_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + i2s_h = rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); break; case I2S_XCR_IBM_LSJM: - tdm_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->tdm_fsync_gpio); - tdm_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->tdm_fsync_gpio); + tdm_l = rockchip_i2s_tdm_clk_assert_l(i2s_tdm->tdm_fsync_gpio); + tdm_h = rockchip_i2s_tdm_clk_assert_h(i2s_tdm->tdm_fsync_gpio); if (i2s_tdm->lrck_ratio == 8) { - tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio); - tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio); - tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio); - tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_l(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_l(i2s_tdm->i2s_lrck_gpio); } - tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); - i2s_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio); - i2s_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + i2s_l = rockchip_i2s_tdm_clk_assert_l(i2s_tdm->i2s_lrck_gpio); + i2s_h = rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); break; default: local_irq_restore(flags); @@ -821,19 +827,6 @@ static int rockchip_i2s_tdm_multi_lanes_parse(struct rk_i2s_tdm_dev *i2s_tdm) i2s_tdm->rx_lanes = val; } - /* - * Should use flag GPIOD_ASIS not to reclaim LRCK pin as GPIO function, - * because we use the same PIN and just read EXT_PORT value which show - * the pin status. - */ - i2s_tdm->i2s_lrck_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "i2s-lrck", - GPIOD_ASIS); - if (IS_ERR(i2s_tdm->i2s_lrck_gpio)) { - ret = PTR_ERR(i2s_tdm->i2s_lrck_gpio); - dev_err(i2s_tdm->dev, "Failed to get i2s_lrck_gpio %d\n", ret); - return ret; - } - /* It's optional, required when use soc clk src, such as: i2s2_2ch */ clk_src_node = of_parse_phandle(i2s_tdm->dev->of_node, "rockchip,clk-src", 0); gpiod_flags = clk_src_node ? GPIOD_ASIS : GPIOD_IN; @@ -873,6 +866,47 @@ static int rockchip_i2s_tdm_multi_lanes_parse(struct rk_i2s_tdm_dev *i2s_tdm) } #endif +static int rockchip_i2s_tdm_slave_one_frame_start(struct rk_i2s_tdm_dev *i2s_tdm, + int stream) +{ + unsigned int msk, val, h; + unsigned long flags; + bool sof; + + sof = i2s_tdm->tdm_mode && !i2s_tdm->is_master_mode && + !i2s_tdm->tdm_fsync_half_frame; + + if (!sof) + return -ENOSYS; + + if (!i2s_tdm->i2s_lrck_gpio) { + dev_err(i2s_tdm->dev, "SOF: should assign 'i2s-lrck-gpio' the pin used in DT\n"); + return -EINVAL; + } + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + msk = I2S_XFER_TXS_MASK; + val = I2S_XFER_TXS_START; + } else { + msk = I2S_XFER_RXS_MASK; + val = I2S_XFER_RXS_START; + } + + local_irq_save(flags); + if (!rockchip_i2s_tdm_clk_valid(i2s_tdm, 0)) { + local_irq_restore(flags); + dev_err(i2s_tdm->dev, "SOF: invalid LRCK, please check 'i2s-lrck-gpio' in DT\n"); + return -EINVAL; + } + h = rockchip_i2s_tdm_clk_assert_h(i2s_tdm->i2s_lrck_gpio); + regmap_update_bits(i2s_tdm->regmap, I2S_XFER, msk, val); + local_irq_restore(flags); + + dev_dbg(i2s_tdm->dev, "STREAM[%d]: TDM-H: %d\n", stream, CLK_MAX_COUNT - h); + + return 0; +} + static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm, int stream) { @@ -882,6 +916,9 @@ static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm, return; } #endif + if (rockchip_i2s_tdm_slave_one_frame_start(i2s_tdm, stream) != -ENOSYS) + return; + if (i2s_tdm->clk_trcm) { rockchip_i2s_tdm_reset_assert(i2s_tdm); regmap_update_bits(i2s_tdm->regmap, I2S_XFER, @@ -2855,6 +2892,19 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) i2s_tdm->dev = &pdev->dev; i2s_tdm->lrck_ratio = 1; + /* + * Should use flag GPIOD_ASIS not to reclaim LRCK pin as GPIO function, + * because we use the same PIN and just read EXT_PORT value which show + * the pin status. + */ + i2s_tdm->i2s_lrck_gpio = devm_gpiod_get_optional(i2s_tdm->dev, "i2s-lrck", + GPIOD_ASIS); + if (IS_ERR(i2s_tdm->i2s_lrck_gpio)) { + ret = PTR_ERR(i2s_tdm->i2s_lrck_gpio); + dev_err(i2s_tdm->dev, "Failed to get i2s_lrck_gpio %d\n", ret); + return ret; + } + of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev); if (!of_id) return -EINVAL; From 9252b6c35b5c60a09e43ca809bb4026d4cb6bb0e Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 29 Dec 2023 17:06:02 +0800 Subject: [PATCH 08/11] arm64: dts: rockchip: rk3588: Assign 'i2s-lrck-gpio' for SOF This patch assign the LRCK pin for TDM_ONE_FRAME on Slave mode. Signed-off-by: Sugar Zhang Change-Id: I28bd554d64f9c63073d2c7f67b9e101c63266c8f --- arch/arm64/boot/dts/rockchip/rk3588s.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi index 1c24f35fb078..9ca2a0755193 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi @@ -4,9 +4,11 @@ */ #include +#include #include #include #include +#include #include #include #include @@ -5657,6 +5659,7 @@ resets = <&cru SRST_M_I2S0_8CH_TX>, <&cru SRST_M_I2S0_8CH_RX>; reset-names = "tx-m", "rx-m"; rockchip,clk-trcm = <1>; + i2s-lrck-gpio = <&gpio1 RK_PC5 GPIO_ACTIVE_HIGH>; /* i2s0_lrck */ pinctrl-names = "default", "idle", "clk"; pinctrl-0 = <&i2s0_sdi0 &i2s0_sdi1 @@ -5682,6 +5685,7 @@ resets = <&cru SRST_M_I2S1_8CH_TX>, <&cru SRST_M_I2S1_8CH_RX>; reset-names = "tx-m", "rx-m"; rockchip,clk-trcm = <1>; + i2s-lrck-gpio = <&gpio4 RK_PA2 GPIO_ACTIVE_HIGH>; /* i2s1m0_lrck */ pinctrl-names = "default"; pinctrl-0 = <&i2s1m0_lrck &i2s1m0_sclk From 618ed5561fcc91b3249d8f0d0f7d7e5d5d1bf492 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 29 Dec 2023 17:06:04 +0800 Subject: [PATCH 09/11] arm64: dts: rockchip: rk3568: Assign 'i2s-lrck-gpio' for SOF This patch assign the LRCK pin for TDM_ONE_FRAME on Slave mode. Signed-off-by: Sugar Zhang Change-Id: I0f0e1a1c778e1e42a5bb5471148358815f6d7b2a --- arch/arm64/boot/dts/rockchip/rk3568.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/arch/arm64/boot/dts/rockchip/rk3568.dtsi index e64cd290436e..17f63f65fab3 100644 --- a/arch/arm64/boot/dts/rockchip/rk3568.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3568.dtsi @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -2956,6 +2957,7 @@ reset-names = "tx-m", "rx-m"; rockchip,cru = <&cru>; rockchip,grf = <&grf>; + i2s-lrck-gpio = <&gpio1 RK_PA5 GPIO_ACTIVE_HIGH>; /* i2s1m0_lrcktx */ #sound-dai-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2s1m0_sclktx @@ -2984,6 +2986,7 @@ rockchip,cru = <&cru>; rockchip,grf = <&grf>; rockchip,clk-trcm = <1>; + i2s-lrck-gpio = <&gpio2 RK_PC3 GPIO_ACTIVE_HIGH>; /* i2s2m0_lrcktx */ #sound-dai-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2s2m0_sclktx @@ -3006,6 +3009,7 @@ rockchip,cru = <&cru>; rockchip,grf = <&grf>; rockchip,clk-trcm = <1>; + i2s-lrck-gpio = <&gpio3 RK_PA4 GPIO_ACTIVE_HIGH>; /* i2s3m0_lrck */ #sound-dai-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&i2s3m0_sclk From bba2c5a2200c9b4c845d04bb43d8e005d08ed102 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Fri, 29 Dec 2023 17:06:05 +0800 Subject: [PATCH 10/11] arm64: dts: rockchip: rk3308: Assign 'i2s-lrck-gpio' for SOF This patch assign the LRCK pin for TDM_ONE_FRAME on Slave mode. Signed-off-by: Sugar Zhang Change-Id: I2ef950170d6646f3d04115662099918942c3cf9d --- arch/arm64/boot/dts/rockchip/rk3308.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rk3308.dtsi b/arch/arm64/boot/dts/rockchip/rk3308.dtsi index fe3ab33ba1b1..4158f076412c 100644 --- a/arch/arm64/boot/dts/rockchip/rk3308.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3308.dtsi @@ -1039,6 +1039,7 @@ rockchip,cru = <&cru>; rockchip,grf = <&grf>; rockchip,mclk-calibrate; + i2s-lrck-gpio = <&gpio2 RK_PA7 GPIO_ACTIVE_HIGH>; /* i2s_8ch_0_lrcktx */ pinctrl-names = "default"; pinctrl-0 = <&i2s_8ch_0_sclktx &i2s_8ch_0_sclkrx @@ -1076,6 +1077,7 @@ rockchip,grf = <&grf>; rockchip,mclk-calibrate; rockchip,io-multiplex; + i2s-lrck-gpio = <&gpio1 RK_PA5 GPIO_ACTIVE_HIGH>; /* i2s_8ch_1_m0_lrcktx */ status = "disabled"; }; From 0d8cce351f927bad0e649c439c888821e72524a5 Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Thu, 18 Jan 2024 10:35:53 +0800 Subject: [PATCH 11/11] ASoC: rockchip: i2s-tdm: Fix stuck on probe Fixes: 13c446a390d3 ("ASoC: rockchip: i2s-tdm: Handle HCLK with runtime PM") Signed-off-by: Sugar Zhang Change-Id: I07ac58ecee8488d941cf2cbec8d4433db4b18702 --- sound/soc/rockchip/rockchip_i2s_tdm.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 449ddd4d9276..b98ba34c2227 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -3031,16 +3031,20 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) i2s_tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; i2s_tdm->capture_dma_data.maxburst = MAXBURST_PER_FIFO; + ret = clk_prepare_enable(i2s_tdm->hclk); + if (ret) + return ret; + ret = rockchip_i2s_tdm_tx_path_prepare(i2s_tdm, node); if (ret < 0) { dev_err(&pdev->dev, "I2S TX path prepare failed: %d\n", ret); - return ret; + goto err_disable_hclk; } ret = rockchip_i2s_tdm_rx_path_prepare(i2s_tdm, node); if (ret < 0) { dev_err(&pdev->dev, "I2S RX path prepare failed: %d\n", ret); - return ret; + goto err_disable_hclk; } atomic_set(&i2s_tdm->refcount, 0); @@ -3064,7 +3068,7 @@ static int rockchip_i2s_tdm_probe(struct platform_device *pdev) if (i2s_tdm->quirks & QUIRK_ALWAYS_ON) { ret = rockchip_i2s_tdm_keep_clk_always_on(i2s_tdm); if (ret) - return ret; + goto err_disable_hclk; } #ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES @@ -3134,6 +3138,9 @@ err_unmap: rockchip_i2s_tdm_unmap(i2s_tdm); #endif +err_disable_hclk: + clk_disable_unprepare(i2s_tdm->hclk); + return ret; }