-
Notifications
You must be signed in to change notification settings - Fork 8.3k
drivers: dai: add ALH dai driver #45104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| zephyr_library() | ||
| zephyr_library_sources(alh.c) | ||
| zephyr_library_compile_options(-std=gnu99) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| /* | ||
| * Copyright (c) 2022 Intel Corporation. | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <errno.h> | ||
| #include <stdbool.h> | ||
| #include <stdint.h> | ||
| #include <zephyr/spinlock.h> | ||
| #include <zephyr/devicetree.h> | ||
| #include <zephyr/logging/log.h> | ||
|
|
||
| #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) | ||
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -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__ | ||||||
|
||||||
| #define __INTEL_DAI_DRIVER_ALH_H__ | |
| #define __INTEL_DAI_DRIVER_ALH_H__ |
not following the pattern used on Zephyr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean? In ssp we have INTEL_DAI_DRIVER_SSP_H and here INTEL_DAI_DRIVER_ALH_H
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The majority of the drivers follow the ZEPHYR_DRIVERS_<name>_H__ pattern. Therefore, it could be ZEPHYR_DRIVERS_INTEL_DAI_DRIVER_ALH_H__. However, not every module and driver does it.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These might be better as devicetree properties
cc: @laurenmurphyx64
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing you have this struct (and others in this file) marked __packed as you're getting them over the wire from the SOF driver on the host? That seems fragile to me. If you really want unified config structs like that, we should arrange for some other layer to specify them. Baking them into the driver internals (this header isn't part of the Zephyr API!) seems like the wrong place.
Also, packed is a noop for a struct containing only a single field type anyway.
Also also, this struct has a size of ... 17 words? Not 16? Is there an off-by-one in the computation of that reserved[] array? Is the glitch symmetric with the SOF definition? This kind of thing is exactly why you want protocol details and driver layer separated. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now I can see in SOF we have exactly the same config with 17 words :) Need to check that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you add this before
config,and this inside
configThis will allow the driver to be enabled whenever a devicetree node has the compatible value is enabled. This will allow CI to build this driver for cAVS 2.5
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, we can do it later but for now @juimonen requested to not enabling this driver until sof can use it (changes in review)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it needs to be enabled somewhere before we can merge it. Otherwise, it will not be included in CI and we won't know if it even builds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@abonislawski @juimonen I think we can proceed with a) a test case, b) enable it for cAVS2.5 SOF build, c) add a new board file using this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dcpleung @MaureenHelm driver enabled again as you requested, hopefully it will pass CI :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @abonislawski