Skip to content

Commit 638cfbb

Browse files
abonislawskicarlescufi
authored andcommitted
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 <adrian.bonislawski@intel.com>
1 parent 45e1d16 commit 638cfbb

File tree

9 files changed

+452
-0
lines changed

9 files changed

+452
-0
lines changed

drivers/dai/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# SPDX-License-Identifier: Apache-2.0
22

33
add_subdirectory_ifdef(CONFIG_DAI_INTEL_SSP intel/ssp)
4+
add_subdirectory_ifdef(CONFIG_DAI_INTEL_ALH intel/alh)

drivers/dai/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@ source "subsys/logging/Kconfig.template.log_config"
2626
comment "Device Drivers"
2727

2828
source "drivers/dai/intel/ssp/Kconfig.ssp"
29+
source "drivers/dai/intel/alh/Kconfig.alh"
2930

3031
endif # DAI
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
zephyr_library()
4+
zephyr_library_sources(alh.c)
5+
zephyr_library_compile_options(-std=gnu99)

drivers/dai/intel/alh/Kconfig.alh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# SOF ALH configuration options
2+
3+
# Copyright (c) 2022 Intel Corporation
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
DT_COMPAT_INTEL_ALH_DAI := intel,alh-dai
7+
8+
config DAI_INTEL_ALH
9+
bool "Intel ALH driver for Dai interface"
10+
default $(dt_compat_enabled,$(DT_COMPAT_INTEL_ALH_DAI))
11+
select DMA
12+
help
13+
Select this to enable Intel ALH driver.
14+
The ALH is an intermediary device, which acts as a hub and provides an
15+
abstracted support for numerous sound interfaces (e.g. SoundWire).
16+
17+
if DAI_INTEL_ALH
18+
19+
config DAI_ALH_HAS_OWNERSHIP
20+
bool "Intel ALH driver has ownership"
21+
help
22+
Select this to enable programming HW ownership
23+
24+
endif

drivers/dai/intel/alh/alh.c

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* Copyright (c) 2022 Intel Corporation.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <errno.h>
8+
#include <stdbool.h>
9+
#include <stdint.h>
10+
#include <zephyr/spinlock.h>
11+
#include <zephyr/devicetree.h>
12+
#include <zephyr/logging/log.h>
13+
14+
#define DT_DRV_COMPAT intel_alh_dai
15+
#define LOG_DOMAIN dai_intel_alh
16+
17+
LOG_MODULE_REGISTER(LOG_DOMAIN);
18+
19+
#include "alh.h"
20+
21+
/* Digital Audio interface formatting */
22+
static int dai_alh_set_config_tplg(struct dai_intel_alh *dp, const void *spec_config)
23+
{
24+
struct dai_intel_alh_pdata *alh = dai_get_drvdata(dp);
25+
const struct dai_intel_ipc3_alh_params *config = spec_config;
26+
27+
if (config->channels && config->rate) {
28+
alh->params.channels = config->channels;
29+
alh->params.rate = config->rate;
30+
LOG_INF("%s channels %d rate %d", __func__, config->channels, config->rate);
31+
}
32+
33+
alh->params.stream_id = config->stream_id;
34+
35+
return 0;
36+
}
37+
38+
static int dai_alh_set_config_blob(struct dai_intel_alh *dp, const void *spec_config)
39+
{
40+
struct dai_intel_alh_pdata *alh = dai_get_drvdata(dp);
41+
const struct dai_intel_ipc4_alh_configuration_blob *blob = spec_config;
42+
const struct ipc4_alh_multi_gtw_cfg *alh_cfg = &blob->alh_cfg;
43+
44+
alh->params.channels = ALH_CHANNELS_DEFAULT;
45+
alh->params.rate = ALH_RATE_DEFAULT;
46+
/* the LSB 8bits are for stream id */
47+
alh->params.stream_id = alh_cfg->mapping[0].alh_id & 0xff;
48+
49+
return 0;
50+
}
51+
52+
static int dai_alh_trigger(const struct device *dev, enum dai_dir dir,
53+
enum dai_trigger_cmd cmd)
54+
{
55+
LOG_DBG("cmd %d", cmd);
56+
57+
return 0;
58+
}
59+
60+
static void alh_claim_ownership(void)
61+
{
62+
#if CONFIG_DAI_ALH_HAS_OWNERSHIP
63+
uint32_t ALHASCTL = DT_INST_PROP_BY_IDX(0, reg, 0);
64+
uint32_t ALHCSCTL = DT_INST_PROP_BY_IDX(0, reg, 1);
65+
66+
sys_write32(sys_read32(ALHASCTL) | ALHASCTL_OSEL(0x3), ALHASCTL);
67+
sys_write32(sys_read32(ALHCSCTL) | ALHASCTL_OSEL(0x3), ALHCSCTL);
68+
#endif
69+
}
70+
71+
static void alh_release_ownership(void)
72+
{
73+
#if CONFIG_DAI_ALH_HAS_OWNERSHIP
74+
uint32_t ALHASCTL = DT_INST_PROP_BY_IDX(0, reg, 0);
75+
uint32_t ALHCSCTL = DT_INST_PROP_BY_IDX(0, reg, 1);
76+
77+
sys_write32(sys_read32(ALHASCTL) | ALHASCTL_OSEL(0), ALHASCTL);
78+
sys_write32(sys_read32(ALHCSCTL) | ALHASCTL_OSEL(0), ALHCSCTL);
79+
#endif
80+
}
81+
82+
83+
static const struct dai_config *dai_alh_config_get(const struct device *dev, enum dai_dir dir)
84+
{
85+
struct dai_config *params = (struct dai_config *)dev->config;
86+
struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data;
87+
struct dai_intel_alh_pdata *alh = dai_get_drvdata(dp);
88+
89+
params->rate = alh->params.rate;
90+
params->channels = alh->params.channels;
91+
params->word_size = ALH_WORD_SIZE_DEFAULT;
92+
93+
return params;
94+
}
95+
96+
static int dai_alh_config_set(const struct device *dev, const struct dai_config *cfg,
97+
const void *bespoke_cfg)
98+
{
99+
struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data;
100+
101+
LOG_DBG("%s", __func__);
102+
103+
if (cfg->type == DAI_INTEL_ALH) {
104+
return dai_alh_set_config_tplg(dp, bespoke_cfg);
105+
} else {
106+
return dai_alh_set_config_blob(dp, bespoke_cfg);
107+
}
108+
}
109+
110+
static const struct dai_properties *dai_alh_get_properties(const struct device *dev,
111+
enum dai_dir dir, int stream_id)
112+
{
113+
struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data;
114+
struct dai_intel_alh_pdata *alh = dai_get_drvdata(dp);
115+
struct dai_properties *prop = &alh->props;
116+
uint32_t offset = dir == DAI_DIR_PLAYBACK ?
117+
ALH_TXDA_OFFSET : ALH_RXDA_OFFSET;
118+
119+
prop->fifo_address = dai_base(dp) + offset + ALH_STREAM_OFFSET * stream_id;
120+
prop->dma_hs_id = alh_handshake_map[stream_id];
121+
prop->stream_id = alh->params.stream_id;
122+
123+
LOG_DBG("dai_index %u", dp->index);
124+
LOG_DBG("fifo %u", prop->fifo_address);
125+
LOG_DBG("handshake %u", prop->dma_hs_id);
126+
127+
return prop;
128+
}
129+
130+
static int dai_alh_probe(const struct device *dev)
131+
{
132+
struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data;
133+
k_spinlock_key_t key;
134+
135+
LOG_DBG("%s", __func__);
136+
137+
key = k_spin_lock(&dp->lock);
138+
139+
if (dp->sref == 0) {
140+
alh_claim_ownership();
141+
}
142+
143+
dp->sref++;
144+
145+
k_spin_unlock(&dp->lock, key);
146+
147+
return 0;
148+
}
149+
150+
static int dai_alh_remove(const struct device *dev)
151+
{
152+
struct dai_intel_alh *dp = (struct dai_intel_alh *)dev->data;
153+
k_spinlock_key_t key;
154+
155+
LOG_DBG("%s", __func__);
156+
157+
key = k_spin_lock(&dp->lock);
158+
159+
if (--dp->sref == 0) {
160+
alh_release_ownership();
161+
}
162+
163+
k_spin_unlock(&dp->lock, key);
164+
165+
return 0;
166+
}
167+
168+
static int alh_init(const struct device *dev)
169+
{
170+
return 0;
171+
}
172+
173+
static const struct dai_driver_api dai_intel_alh_api_funcs = {
174+
.probe = dai_alh_probe,
175+
.remove = dai_alh_remove,
176+
.config_set = dai_alh_config_set,
177+
.config_get = dai_alh_config_get,
178+
.trigger = dai_alh_trigger,
179+
.get_properties = dai_alh_get_properties,
180+
};
181+
182+
#define DAI_INTEL_ALH_DEVICE_INIT(n) \
183+
static struct dai_config dai_intel_alh_config_##n; \
184+
static struct dai_intel_alh dai_intel_alh_data_##n = { \
185+
.index = (n / DAI_NUM_ALH_BI_DIR_LINKS_GROUP) << 8 | \
186+
(n % DAI_NUM_ALH_BI_DIR_LINKS_GROUP), \
187+
.plat_data = { \
188+
.base = DT_INST_PROP_BY_IDX(n, reg, 0), \
189+
.fifo_depth[DAI_DIR_PLAYBACK] = ALH_GPDMA_BURST_LENGTH, \
190+
.fifo_depth[DAI_DIR_CAPTURE] = ALH_GPDMA_BURST_LENGTH, \
191+
}, \
192+
}; \
193+
\
194+
DEVICE_DT_INST_DEFINE(n, \
195+
alh_init, NULL, \
196+
&dai_intel_alh_data_##n, \
197+
&dai_intel_alh_config_##n, \
198+
POST_KERNEL, 32, \
199+
&dai_intel_alh_api_funcs);
200+
201+
DT_INST_FOREACH_STATUS_OKAY(DAI_INTEL_ALH_DEVICE_INIT)

drivers/dai/intel/alh/alh.h

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (c) 2022 Intel Corporation.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#ifndef __INTEL_DAI_DRIVER_ALH_H__
8+
#define __INTEL_DAI_DRIVER_ALH_H__
9+
10+
#include <stdint.h>
11+
#include <zephyr/drivers/dai.h>
12+
13+
#include "alh_map.h"
14+
15+
#define DAI_NUM_ALH_BI_DIR_LINKS_GROUP 4
16+
17+
#define ALH_STREAM_OFFSET 0x4
18+
19+
#define IPC4_ALH_MAX_NUMBER_OF_GTW 16
20+
#define IPC4_ALH_DAI_INDEX_OFFSET 7
21+
22+
#define ALH_GPDMA_BURST_LENGTH 4
23+
24+
#define ALH_SET_BITS(b_hi, b_lo, x) \
25+
(((x) & ((1ULL << ((b_hi) - (b_lo) + 1ULL)) - 1ULL)) << (b_lo))
26+
#define ALHASCTL_OSEL(x) ALH_SET_BITS(25, 24, x)
27+
28+
#define dai_get_drvdata(dai) &dai->priv_data
29+
#define dai_base(dai) dai->plat_data.base
30+
31+
#define DAI_DIR_PLAYBACK 0
32+
#define DAI_DIR_CAPTURE 1
33+
34+
#define ALH_CHANNELS_DEFAULT 2
35+
#define ALH_RATE_DEFAULT 48000
36+
#define ALH_WORD_SIZE_DEFAULT 32
37+
38+
#if CONFIG_INTEL_ADSP_CAVS
39+
#define ALH_TXDA_OFFSET 0x400
40+
#define ALH_RXDA_OFFSET 0x500
41+
#else
42+
#define ALH_TXDA_OFFSET 0
43+
#define ALH_RXDA_OFFSET 0x100
44+
#endif
45+
46+
union dai_intel_ipc4_gateway_attributes {
47+
/**< Raw value */
48+
uint32_t dw;
49+
50+
/**< Access to the fields */
51+
struct {
52+
/**< Gateway data requested in low power memory. */
53+
uint32_t lp_buffer_alloc : 1;
54+
55+
/**< Gateway data requested in register file memory. */
56+
uint32_t alloc_from_reg_file : 1;
57+
58+
/**< Reserved field */
59+
uint32_t _rsvd : 30;
60+
} bits; /**<< Bits */
61+
} __packed;
62+
63+
/* ALH Configuration Request - SOF_IPC_DAI_ALH_CONFIG */
64+
struct dai_intel_ipc3_alh_params {
65+
uint32_t reserved0;
66+
uint32_t stream_id;
67+
uint32_t rate;
68+
uint32_t channels;
69+
70+
/* reserved for future use */
71+
uint32_t reserved[13];
72+
} __packed;
73+
74+
struct ipc4_alh_multi_gtw_cfg {
75+
/* Number of single channels (valid items in mapping array). */
76+
uint32_t count;
77+
/* Single to multi aggregation mapping item. */
78+
struct {
79+
/* Vindex of a single ALH channel aggregated. */
80+
uint32_t alh_id;
81+
/* Channel mask */
82+
uint32_t channel_mask;
83+
} mapping[IPC4_ALH_MAX_NUMBER_OF_GTW]; /* < Mapping items */
84+
} __packed;
85+
86+
struct dai_intel_ipc4_alh_configuration_blob {
87+
union dai_intel_ipc4_gateway_attributes gtw_attributes;
88+
struct ipc4_alh_multi_gtw_cfg alh_cfg;
89+
} __packed;
90+
91+
struct dai_intel_alh_plat_data {
92+
uint32_t base;
93+
uint32_t fifo_depth[2];
94+
};
95+
96+
struct dai_intel_alh_pdata {
97+
struct dai_config config;
98+
struct dai_properties props;
99+
struct dai_intel_ipc3_alh_params params;
100+
};
101+
102+
struct dai_intel_alh {
103+
uint32_t index; /**< index */
104+
struct k_spinlock lock; /**< locking mechanism */
105+
int sref; /**< simple ref counter, guarded by lock */
106+
struct dai_intel_alh_plat_data plat_data;
107+
struct dai_intel_alh_pdata priv_data;
108+
};
109+
110+
#endif

0 commit comments

Comments
 (0)