From 638cfbbdbf95901086a4ead5475cc1b4c5dc2bc2 Mon Sep 17 00:00:00 2001 From: Adrian Bonislawski Date: Mon, 25 Apr 2022 09:53:18 +0200 Subject: [PATCH] drivers: dai: add ALH dai driver The ALH is an intermediary device, which acts as a hub and provides an abstracted support for numerous sound interfaces (e.g. SoundWire). Signed-off-by: Adrian Bonislawski --- drivers/dai/CMakeLists.txt | 1 + drivers/dai/Kconfig | 1 + drivers/dai/intel/alh/CMakeLists.txt | 5 + drivers/dai/intel/alh/Kconfig.alh | 24 ++++ drivers/dai/intel/alh/alh.c | 201 +++++++++++++++++++++++++++ drivers/dai/intel/alh/alh.h | 110 +++++++++++++++ drivers/dai/intel/alh/alh_map.h | 82 +++++++++++ dts/bindings/alh/intel,alh-dai.yaml | 13 ++ dts/xtensa/intel/intel_cavs25.dtsi | 15 ++ 9 files changed, 452 insertions(+) create mode 100644 drivers/dai/intel/alh/CMakeLists.txt create mode 100644 drivers/dai/intel/alh/Kconfig.alh create mode 100644 drivers/dai/intel/alh/alh.c create mode 100644 drivers/dai/intel/alh/alh.h create mode 100644 drivers/dai/intel/alh/alh_map.h create mode 100644 dts/bindings/alh/intel,alh-dai.yaml diff --git a/drivers/dai/CMakeLists.txt b/drivers/dai/CMakeLists.txt index 4ccea582c28..dd622d69c0d 100644 --- a/drivers/dai/CMakeLists.txt +++ b/drivers/dai/CMakeLists.txt @@ -1,3 +1,4 @@ # SPDX-License-Identifier: Apache-2.0 add_subdirectory_ifdef(CONFIG_DAI_INTEL_SSP intel/ssp) +add_subdirectory_ifdef(CONFIG_DAI_INTEL_ALH intel/alh) diff --git a/drivers/dai/Kconfig b/drivers/dai/Kconfig index 719e4b60bc6..24489662d93 100644 --- a/drivers/dai/Kconfig +++ b/drivers/dai/Kconfig @@ -26,5 +26,6 @@ source "subsys/logging/Kconfig.template.log_config" comment "Device Drivers" source "drivers/dai/intel/ssp/Kconfig.ssp" +source "drivers/dai/intel/alh/Kconfig.alh" endif # DAI diff --git a/drivers/dai/intel/alh/CMakeLists.txt b/drivers/dai/intel/alh/CMakeLists.txt new file mode 100644 index 00000000000..bb38a9e9be5 --- /dev/null +++ b/drivers/dai/intel/alh/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(alh.c) +zephyr_library_compile_options(-std=gnu99) diff --git a/drivers/dai/intel/alh/Kconfig.alh b/drivers/dai/intel/alh/Kconfig.alh new file mode 100644 index 00000000000..2a064798517 --- /dev/null +++ b/drivers/dai/intel/alh/Kconfig.alh @@ -0,0 +1,24 @@ +# SOF ALH configuration options + +# Copyright (c) 2022 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +DT_COMPAT_INTEL_ALH_DAI := intel,alh-dai + +config DAI_INTEL_ALH + bool "Intel ALH driver for Dai interface" + default $(dt_compat_enabled,$(DT_COMPAT_INTEL_ALH_DAI)) + select DMA + help + Select this to enable Intel ALH driver. + The ALH is an intermediary device, which acts as a hub and provides an + abstracted support for numerous sound interfaces (e.g. SoundWire). + +if DAI_INTEL_ALH + +config DAI_ALH_HAS_OWNERSHIP + bool "Intel ALH driver has ownership" + help + Select this to enable programming HW ownership + +endif diff --git a/drivers/dai/intel/alh/alh.c b/drivers/dai/intel/alh/alh.c new file mode 100644 index 00000000000..de720095e56 --- /dev/null +++ b/drivers/dai/intel/alh/alh.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2022 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#define DT_DRV_COMPAT intel_alh_dai +#define LOG_DOMAIN dai_intel_alh + +LOG_MODULE_REGISTER(LOG_DOMAIN); + +#include "alh.h" + +/* Digital Audio interface formatting */ +static int dai_alh_set_config_tplg(struct dai_intel_alh *dp, const void *spec_config) +{ + struct dai_intel_alh_pdata *alh = dai_get_drvdata(dp); + const struct dai_intel_ipc3_alh_params *config = spec_config; + + if (config->channels && config->rate) { + alh->params.channels = config->channels; + alh->params.rate = config->rate; + LOG_INF("%s channels %d rate %d", __func__, config->channels, config->rate); + } + + alh->params.stream_id = config->stream_id; + + return 0; +} + +static int dai_alh_set_config_blob(struct dai_intel_alh *dp, const void *spec_config) +{ + struct dai_intel_alh_pdata *alh = dai_get_drvdata(dp); + const struct dai_intel_ipc4_alh_configuration_blob *blob = spec_config; + const struct ipc4_alh_multi_gtw_cfg *alh_cfg = &blob->alh_cfg; + + alh->params.channels = ALH_CHANNELS_DEFAULT; + alh->params.rate = ALH_RATE_DEFAULT; + /* the LSB 8bits are for stream id */ + alh->params.stream_id = alh_cfg->mapping[0].alh_id & 0xff; + + return 0; +} + +static int dai_alh_trigger(const struct device *dev, enum dai_dir dir, + enum dai_trigger_cmd cmd) +{ + LOG_DBG("cmd %d", cmd); + + return 0; +} + +static void alh_claim_ownership(void) +{ +#if CONFIG_DAI_ALH_HAS_OWNERSHIP + uint32_t ALHASCTL = DT_INST_PROP_BY_IDX(0, reg, 0); + uint32_t ALHCSCTL = DT_INST_PROP_BY_IDX(0, reg, 1); + + sys_write32(sys_read32(ALHASCTL) | ALHASCTL_OSEL(0x3), ALHASCTL); + sys_write32(sys_read32(ALHCSCTL) | ALHASCTL_OSEL(0x3), ALHCSCTL); +#endif +} + +static void alh_release_ownership(void) +{ +#if CONFIG_DAI_ALH_HAS_OWNERSHIP + uint32_t ALHASCTL = DT_INST_PROP_BY_IDX(0, reg, 0); + uint32_t ALHCSCTL = DT_INST_PROP_BY_IDX(0, reg, 1); + + sys_write32(sys_read32(ALHASCTL) | ALHASCTL_OSEL(0), ALHASCTL); + sys_write32(sys_read32(ALHCSCTL) | ALHASCTL_OSEL(0), ALHCSCTL); +#endif +} + + +static const struct dai_config *dai_alh_config_get(const struct device *dev, enum dai_dir dir) +{ + struct dai_config *params = (struct dai_config *)dev->config; + struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data; + struct dai_intel_alh_pdata *alh = dai_get_drvdata(dp); + + params->rate = alh->params.rate; + params->channels = alh->params.channels; + params->word_size = ALH_WORD_SIZE_DEFAULT; + + return params; +} + +static int dai_alh_config_set(const struct device *dev, const struct dai_config *cfg, + const void *bespoke_cfg) +{ + struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data; + + LOG_DBG("%s", __func__); + + if (cfg->type == DAI_INTEL_ALH) { + return dai_alh_set_config_tplg(dp, bespoke_cfg); + } else { + return dai_alh_set_config_blob(dp, bespoke_cfg); + } +} + +static const struct dai_properties *dai_alh_get_properties(const struct device *dev, + enum dai_dir dir, int stream_id) +{ + struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data; + struct dai_intel_alh_pdata *alh = dai_get_drvdata(dp); + struct dai_properties *prop = &alh->props; + uint32_t offset = dir == DAI_DIR_PLAYBACK ? + ALH_TXDA_OFFSET : ALH_RXDA_OFFSET; + + prop->fifo_address = dai_base(dp) + offset + ALH_STREAM_OFFSET * stream_id; + prop->dma_hs_id = alh_handshake_map[stream_id]; + prop->stream_id = alh->params.stream_id; + + LOG_DBG("dai_index %u", dp->index); + LOG_DBG("fifo %u", prop->fifo_address); + LOG_DBG("handshake %u", prop->dma_hs_id); + + return prop; +} + +static int dai_alh_probe(const struct device *dev) +{ + struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data; + k_spinlock_key_t key; + + LOG_DBG("%s", __func__); + + key = k_spin_lock(&dp->lock); + + if (dp->sref == 0) { + alh_claim_ownership(); + } + + dp->sref++; + + k_spin_unlock(&dp->lock, key); + + return 0; +} + +static int dai_alh_remove(const struct device *dev) +{ + struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data; + k_spinlock_key_t key; + + LOG_DBG("%s", __func__); + + key = k_spin_lock(&dp->lock); + + if (--dp->sref == 0) { + alh_release_ownership(); + } + + k_spin_unlock(&dp->lock, key); + + return 0; +} + +static int alh_init(const struct device *dev) +{ + return 0; +} + +static const struct dai_driver_api dai_intel_alh_api_funcs = { + .probe = dai_alh_probe, + .remove = dai_alh_remove, + .config_set = dai_alh_config_set, + .config_get = dai_alh_config_get, + .trigger = dai_alh_trigger, + .get_properties = dai_alh_get_properties, +}; + +#define DAI_INTEL_ALH_DEVICE_INIT(n) \ + static struct dai_config dai_intel_alh_config_##n; \ + static struct dai_intel_alh dai_intel_alh_data_##n = { \ + .index = (n / DAI_NUM_ALH_BI_DIR_LINKS_GROUP) << 8 | \ + (n % DAI_NUM_ALH_BI_DIR_LINKS_GROUP), \ + .plat_data = { \ + .base = DT_INST_PROP_BY_IDX(n, reg, 0), \ + .fifo_depth[DAI_DIR_PLAYBACK] = ALH_GPDMA_BURST_LENGTH, \ + .fifo_depth[DAI_DIR_CAPTURE] = ALH_GPDMA_BURST_LENGTH, \ + }, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, \ + alh_init, NULL, \ + &dai_intel_alh_data_##n, \ + &dai_intel_alh_config_##n, \ + POST_KERNEL, 32, \ + &dai_intel_alh_api_funcs); + +DT_INST_FOREACH_STATUS_OKAY(DAI_INTEL_ALH_DEVICE_INIT) diff --git a/drivers/dai/intel/alh/alh.h b/drivers/dai/intel/alh/alh.h new file mode 100644 index 00000000000..85e432e8013 --- /dev/null +++ b/drivers/dai/intel/alh/alh.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __INTEL_DAI_DRIVER_ALH_H__ +#define __INTEL_DAI_DRIVER_ALH_H__ + +#include +#include + +#include "alh_map.h" + +#define DAI_NUM_ALH_BI_DIR_LINKS_GROUP 4 + +#define ALH_STREAM_OFFSET 0x4 + +#define IPC4_ALH_MAX_NUMBER_OF_GTW 16 +#define IPC4_ALH_DAI_INDEX_OFFSET 7 + +#define ALH_GPDMA_BURST_LENGTH 4 + +#define ALH_SET_BITS(b_hi, b_lo, x) \ + (((x) & ((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL)) << (b_lo)) +#define ALHASCTL_OSEL(x) ALH_SET_BITS(25, 24, x) + +#define dai_get_drvdata(dai) &dai->priv_data +#define dai_base(dai) dai->plat_data.base + +#define DAI_DIR_PLAYBACK 0 +#define DAI_DIR_CAPTURE 1 + +#define ALH_CHANNELS_DEFAULT 2 +#define ALH_RATE_DEFAULT 48000 +#define ALH_WORD_SIZE_DEFAULT 32 + +#if CONFIG_INTEL_ADSP_CAVS +#define ALH_TXDA_OFFSET 0x400 +#define ALH_RXDA_OFFSET 0x500 +#else +#define ALH_TXDA_OFFSET 0 +#define ALH_RXDA_OFFSET 0x100 +#endif + +union dai_intel_ipc4_gateway_attributes { + /**< Raw value */ + uint32_t dw; + + /**< Access to the fields */ + struct { + /**< Gateway data requested in low power memory. */ + uint32_t lp_buffer_alloc : 1; + + /**< Gateway data requested in register file memory. */ + uint32_t alloc_from_reg_file : 1; + + /**< Reserved field */ + uint32_t _rsvd : 30; + } bits; /**<< Bits */ +} __packed; + +/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */ +struct dai_intel_ipc3_alh_params { + uint32_t reserved0; + uint32_t stream_id; + uint32_t rate; + uint32_t channels; + + /* reserved for future use */ + uint32_t reserved[13]; +} __packed; + +struct ipc4_alh_multi_gtw_cfg { + /* Number of single channels (valid items in mapping array). */ + uint32_t count; + /* Single to multi aggregation mapping item. */ + struct { + /* Vindex of a single ALH channel aggregated. */ + uint32_t alh_id; + /* Channel mask */ + uint32_t channel_mask; + } mapping[IPC4_ALH_MAX_NUMBER_OF_GTW]; /* < Mapping items */ +} __packed; + +struct dai_intel_ipc4_alh_configuration_blob { + union dai_intel_ipc4_gateway_attributes gtw_attributes; + struct ipc4_alh_multi_gtw_cfg alh_cfg; +} __packed; + +struct dai_intel_alh_plat_data { + uint32_t base; + uint32_t fifo_depth[2]; +}; + +struct dai_intel_alh_pdata { + struct dai_config config; + struct dai_properties props; + struct dai_intel_ipc3_alh_params params; +}; + +struct dai_intel_alh { + uint32_t index; /**< index */ + struct k_spinlock lock; /**< locking mechanism */ + int sref; /**< simple ref counter, guarded by lock */ + struct dai_intel_alh_plat_data plat_data; + struct dai_intel_alh_pdata priv_data; +}; + +#endif diff --git a/drivers/dai/intel/alh/alh_map.h b/drivers/dai/intel/alh/alh_map.h new file mode 100644 index 00000000000..64fd3e0ec78 --- /dev/null +++ b/drivers/dai/intel/alh/alh_map.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __INTEL_DAI_DRIVER_ALH_MAP_H__ +#define __INTEL_DAI_DRIVER_ALH_MAP_H__ + +/** + * \brief ALH Handshakes for audio I/O + * Stream ID -> DMA Handshake map + * -1 identifies invalid handshakes/streams + */ +static const uint8_t alh_handshake_map[64] = { + -1, /* 0 - INVALID */ + -1, /* 1 - INVALID */ + -1, /* 2 - INVALID */ + -1, /* 3 - INVALID */ + -1, /* 4 - INVALID */ + -1, /* 5 - INVALID */ + -1, /* 6 - INVALID */ + 22, /* 7 - BIDIRECTIONAL */ + 23, /* 8 - BIDIRECTIONAL */ + 24, /* 9 - BIDIRECTIONAL */ + 25, /* 10 - BIDIRECTIONAL */ + 26, /* 11 - BIDIRECTIONAL */ + 27, /* 12 - BIDIRECTIONAL */ + -1, /* 13 - INVALID */ + -1, /* 14 - INVALID */ + -1, /* 15 - INVALID */ + -1, /* 16 - INVALID */ + -1, /* 17 - INVALID */ + -1, /* 18 - INVALID */ + -1, /* 19 - INVALID */ + -1, /* 20 - INVALID */ + -1, /* 21 - INVALID */ + -1, /* 22 - INVALID */ + 32, /* 23 - BIDIRECTIONAL */ + 33, /* 24 - BIDIRECTIONAL */ + 34, /* 25 - BIDIRECTIONAL */ + 35, /* 26 - BIDIRECTIONAL */ + 36, /* 27 - BIDIRECTIONAL */ + 37, /* 28 - BIDIRECTIONAL */ + -1, /* 29 - INVALID */ + -1, /* 30 - INVALID */ + -1, /* 31 - INVALID */ + -1, /* 32 - INVALID */ + -1, /* 33 - INVALID */ + -1, /* 34 - INVALID */ + -1, /* 35 - INVALID */ + -1, /* 36 - INVALID */ + -1, /* 37 - INVALID */ + -1, /* 38 - INVALID */ + 42, /* 39 - BIDIRECTIONAL */ + 43, /* 40 - BIDIRECTIONAL */ + 44, /* 41 - BIDIRECTIONAL */ + 45, /* 42 - BIDIRECTIONAL */ + 46, /* 43 - BIDIRECTIONAL */ + 47, /* 44 - BIDIRECTIONAL */ + -1, /* 45 - INVALID */ + -1, /* 46 - INVALID */ + -1, /* 47 - INVALID */ + -1, /* 48 - INVALID */ + -1, /* 49 - INVALID */ + -1, /* 50 - INVALID */ + -1, /* 51 - INVALID */ + -1, /* 52 - INVALID */ + -1, /* 53 - INVALID */ + -1, /* 54 - INVALID */ + 52, /* 55 - BIDIRECTIONAL */ + 53, /* 56 - BIDIRECTIONAL */ + 54, /* 57 - BIDIRECTIONAL */ + 55, /* 58 - BIDIRECTIONAL */ + 56, /* 59 - BIDIRECTIONAL */ + 57, /* 60 - BIDIRECTIONAL */ + -1, /* 61 - INVALID */ + -1, /* 62 - INVALID */ + -1, /* 63 - INVALID */ +}; + +#endif diff --git a/dts/bindings/alh/intel,alh-dai.yaml b/dts/bindings/alh/intel,alh-dai.yaml new file mode 100644 index 00000000000..0eabe3cfe37 --- /dev/null +++ b/dts/bindings/alh/intel,alh-dai.yaml @@ -0,0 +1,13 @@ +# Copyright (c) 2022 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +description: Intel ALH DAI controller + +compatible: "intel,alh-dai" + +include: base.yaml + +properties: + reg: + required: true diff --git a/dts/xtensa/intel/intel_cavs25.dtsi b/dts/xtensa/intel/intel_cavs25.dtsi index e6257bca099..274ce09ac4c 100644 --- a/dts/xtensa/intel/intel_cavs25.dtsi +++ b/dts/xtensa/intel/intel_cavs25.dtsi @@ -251,5 +251,20 @@ status = "okay"; }; + alh0:alh@24400 { + compatible = "intel,alh-dai"; + reg = <0x00024400 0x00024600>; + label = "ALH_0"; + + status = "okay"; + }; + + alh1:alh@24400 { + compatible = "intel,alh-dai"; + reg = <0x00024400 0x00024600>; + label = "ALH_1"; + + status = "okay"; + }; }; };