202 lines
5.2 KiB
C
202 lines
5.2 KiB
C
/*
|
|
* 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)
|