2022-05-11 16:42:40 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2022 Intel Corporation.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT intel_dai_dmic
|
|
|
|
#define LOG_DOMAIN dai_intel_dmic
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(LOG_DOMAIN);
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stdint.h>
|
2022-10-12 15:18:33 +02:00
|
|
|
#include <zephyr/kernel.h>
|
2022-05-11 16:42:40 +02:00
|
|
|
#include <zephyr/spinlock.h>
|
|
|
|
#include <zephyr/devicetree.h>
|
2022-11-25 00:12:16 +01:00
|
|
|
#include <zephyr/pm/device.h>
|
|
|
|
#include <zephyr/pm/device_runtime.h>
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
#include <zephyr/drivers/dai.h>
|
2022-10-17 10:24:11 +02:00
|
|
|
#include <zephyr/irq.h>
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
#include "dmic.h"
|
2023-06-23 12:37:53 +00:00
|
|
|
#include <dmic_regs.h>
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
/* Base addresses (in PDM scope) of 2ch PDM controllers and coefficient RAM. */
|
2023-08-04 10:57:01 -07:00
|
|
|
static const uint32_t dmic_base[4] = {PDM0, PDM1, PDM2, PDM3};
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
/* global data shared between all dmic instances */
|
|
|
|
struct dai_dmic_global_shared dai_dmic_global;
|
|
|
|
|
|
|
|
/* Helper macro to read 64-bit data using two 32-bit data read */
|
|
|
|
#define sys_read64(addr) (((uint64_t)(sys_read32(addr + 4)) << 32) | \
|
|
|
|
sys_read32(addr))
|
|
|
|
|
|
|
|
int dai_dmic_set_config_nhlt(struct dai_intel_dmic *dmic, const void *spec_config);
|
|
|
|
|
2022-08-02 12:39:59 +03:00
|
|
|
/* Exponent function for small values of x. This function calculates
|
|
|
|
* fairly accurately exponent for x in range -2.0 .. +2.0. The iteration
|
|
|
|
* uses first 11 terms of Taylor series approximation for exponent
|
|
|
|
* function. With the current scaling the numerator just remains under
|
|
|
|
* 64 bits with the 11 terms.
|
|
|
|
*
|
|
|
|
* See https://en.wikipedia.org/wiki/Exponential_function#Computation
|
|
|
|
*
|
|
|
|
* The input is Q3.29
|
|
|
|
* The output is Q9.23
|
|
|
|
*/
|
|
|
|
static int32_t exp_small_fixed(int32_t x)
|
|
|
|
{
|
|
|
|
int64_t p;
|
|
|
|
int64_t num = Q_SHIFT_RND(x, 29, 23);
|
|
|
|
int32_t y = (int32_t)num;
|
|
|
|
int32_t den = 1;
|
|
|
|
int32_t inc;
|
|
|
|
int k;
|
|
|
|
|
|
|
|
/* Numerator is x^k, denominator is k! */
|
|
|
|
for (k = 2; k < 12; k++) {
|
|
|
|
p = num * x; /* Q9.23 x Q3.29 -> Q12.52 */
|
|
|
|
num = Q_SHIFT_RND(p, 52, 23);
|
|
|
|
den = den * k;
|
|
|
|
inc = (int32_t)(num / den);
|
|
|
|
y += inc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return y + ONE_Q23;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t exp_fixed(int32_t x)
|
|
|
|
{
|
|
|
|
int32_t xs;
|
|
|
|
int32_t y;
|
|
|
|
int32_t z;
|
|
|
|
int i;
|
|
|
|
int n = 0;
|
|
|
|
|
2024-08-16 13:26:13 +07:00
|
|
|
if (x < Q_CONVERT_FLOAT(-11.5, 27)) {
|
2022-08-02 12:39:59 +03:00
|
|
|
return 0;
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-08-02 12:39:59 +03:00
|
|
|
|
2024-08-16 13:26:13 +07:00
|
|
|
if (x > Q_CONVERT_FLOAT(7.6245, 27)) {
|
2022-08-02 12:39:59 +03:00
|
|
|
return INT32_MAX;
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-08-02 12:39:59 +03:00
|
|
|
|
|
|
|
/* x is Q5.27 */
|
|
|
|
xs = x;
|
|
|
|
while (xs >= TWO_Q27 || xs <= MINUS_TWO_Q27) {
|
|
|
|
xs >>= 1;
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* exp_small_fixed() input is Q3.29, while x1 is Q5.27
|
|
|
|
* exp_small_fixed() output is Q9.23, while z is Q12.20
|
|
|
|
*/
|
|
|
|
z = Q_SHIFT_RND(exp_small_fixed(Q_SHIFT_LEFT(xs, 27, 29)), 23, 20);
|
|
|
|
y = ONE_Q20;
|
2024-08-16 13:26:13 +07:00
|
|
|
for (i = 0; i < (1 << n); i++) {
|
2022-08-02 12:39:59 +03:00
|
|
|
y = (int32_t)Q_MULTSR_32X32((int64_t)y, z, 20, 20, 20);
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-08-02 12:39:59 +03:00
|
|
|
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int32_t db2lin_fixed(int32_t db)
|
|
|
|
{
|
|
|
|
int32_t arg;
|
|
|
|
|
2024-08-16 13:26:13 +07:00
|
|
|
if (db < Q_CONVERT_FLOAT(-100.0, 24)) {
|
2022-08-02 12:39:59 +03:00
|
|
|
return 0;
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-08-02 12:39:59 +03:00
|
|
|
|
|
|
|
/* Q8.24 x Q5.27, result needs to be Q5.27 */
|
|
|
|
arg = (int32_t)Q_MULTSR_32X32((int64_t)db, LOG10_DIV20_Q27, 24, 27, 27);
|
|
|
|
return exp_fixed(arg);
|
|
|
|
}
|
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
static void dai_dmic_update_bits(const struct dai_intel_dmic *dmic,
|
|
|
|
uint32_t reg, uint32_t mask, uint32_t val)
|
|
|
|
{
|
|
|
|
uint32_t dest = dmic->reg_base + reg;
|
|
|
|
|
|
|
|
sys_write32((sys_read32(dest) & (~mask)) | (val & mask), dest);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dai_dmic_write(const struct dai_intel_dmic *dmic,
|
|
|
|
uint32_t reg, uint32_t val)
|
|
|
|
{
|
|
|
|
sys_write32(val, dmic->reg_base + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t dai_dmic_read(const struct dai_intel_dmic *dmic,
|
|
|
|
uint32_t reg)
|
|
|
|
{
|
|
|
|
return sys_read32(dmic->reg_base + reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if CONFIG_DAI_DMIC_HAS_OWNERSHIP
|
|
|
|
static inline void dai_dmic_claim_ownership(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
/* DMIC Owner Select to DSP */
|
|
|
|
sys_write32(sys_read32(dmic->shim_base + DMICLCTL_OFFSET) |
|
2023-04-19 13:45:07 +02:00
|
|
|
FIELD_PREP(DMICLCTL_OSEL, 0x3), dmic->shim_base + DMICLCTL_OFFSET);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dai_dmic_release_ownership(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
/* DMIC Owner Select back to Host CPU + DSP */
|
|
|
|
sys_write32(sys_read32(dmic->shim_base + DMICLCTL_OFFSET) &
|
2023-04-19 13:45:07 +02:00
|
|
|
~DMICLCTL_OSEL, dmic->shim_base + DMICLCTL_OFFSET);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#else /* CONFIG_DAI_DMIC_HAS_OWNERSHIP */
|
|
|
|
|
|
|
|
static inline void dai_dmic_claim_ownership(const struct dai_intel_dmic *dmic) {}
|
|
|
|
static inline void dai_dmic_release_ownership(const struct dai_intel_dmic *dmic) {}
|
|
|
|
|
|
|
|
#endif /* CONFIG_DAI_DMIC_HAS_OWNERSHIP */
|
|
|
|
|
2022-10-21 09:26:09 +02:00
|
|
|
static inline uint32_t dai_dmic_base(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
2024-09-23 13:14:55 +02:00
|
|
|
#if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30)
|
2022-10-21 09:26:09 +02:00
|
|
|
return dmic->hdamldmic_base;
|
|
|
|
#else
|
|
|
|
return dmic->shim_base;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
#if CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC
|
|
|
|
static inline void dai_dmic_set_sync_period(uint32_t period, const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
uint32_t val = CONFIG_DAI_DMIC_HW_IOCLK / period - 1;
|
2022-10-21 09:26:09 +02:00
|
|
|
uint32_t base = dai_dmic_base(dmic);
|
2022-05-11 16:42:40 +02:00
|
|
|
/* DMIC Change sync period */
|
2024-09-23 13:14:55 +02:00
|
|
|
#if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30)
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) | FIELD_PREP(DMICSYNC_SYNCPRD, val),
|
2022-10-21 09:26:09 +02:00
|
|
|
base + DMICSYNC_OFFSET);
|
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) | DMICSYNC_SYNCPU,
|
|
|
|
base + DMICSYNC_OFFSET);
|
2023-06-13 16:03:35 +02:00
|
|
|
|
|
|
|
if (!WAIT_FOR((sys_read32(base + DMICSYNC_OFFSET) & DMICSYNC_SYNCPU) == 0, 1000,
|
|
|
|
k_sleep(K_USEC(100)))) {
|
|
|
|
LOG_ERR("poll timeout");
|
2022-10-21 09:26:09 +02:00
|
|
|
}
|
2023-06-13 16:03:35 +02:00
|
|
|
|
2022-10-21 09:26:09 +02:00
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) | DMICSYNC_CMDSYNC,
|
|
|
|
base + DMICSYNC_OFFSET);
|
|
|
|
#else /* All other CAVS and ACE platforms */
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) | FIELD_PREP(DMICSYNC_SYNCPRD, val),
|
2022-10-21 09:26:09 +02:00
|
|
|
base + DMICSYNC_OFFSET);
|
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) | DMICSYNC_CMDSYNC,
|
|
|
|
base + DMICSYNC_OFFSET);
|
|
|
|
#endif
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dai_dmic_clear_sync_period(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
2022-10-21 09:26:09 +02:00
|
|
|
uint32_t base = dai_dmic_base(dmic);
|
2022-05-11 16:42:40 +02:00
|
|
|
/* DMIC Clean sync period */
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) & ~DMICSYNC_SYNCPRD,
|
2022-10-21 09:26:09 +02:00
|
|
|
base + DMICSYNC_OFFSET);
|
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) & ~DMICSYNC_CMDSYNC,
|
|
|
|
base + DMICSYNC_OFFSET);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Preparing for command synchronization on multiple link segments */
|
|
|
|
static inline void dai_dmic_sync_prepare(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
2022-10-21 09:26:09 +02:00
|
|
|
uint32_t base = dai_dmic_base(dmic);
|
|
|
|
|
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) | DMICSYNC_CMDSYNC,
|
|
|
|
base + DMICSYNC_OFFSET);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Trigering synchronization of command execution */
|
|
|
|
static void dmic_sync_trigger(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
2022-10-21 09:26:09 +02:00
|
|
|
uint32_t base = dai_dmic_base(dmic);
|
2022-05-11 16:42:40 +02:00
|
|
|
|
2022-10-21 09:26:09 +02:00
|
|
|
__ASSERT_NO_MSG((sys_read32(base + DMICSYNC_OFFSET) & DMICSYNC_CMDSYNC) != 0);
|
|
|
|
|
|
|
|
sys_write32(sys_read32(base + DMICSYNC_OFFSET) |
|
|
|
|
DMICSYNC_SYNCGO, base + DMICSYNC_OFFSET);
|
2023-06-13 16:03:35 +02:00
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
/* waiting for CMDSYNC bit clearing */
|
2023-06-13 16:03:35 +02:00
|
|
|
if (!WAIT_FOR((sys_read32(base + DMICSYNC_OFFSET) & DMICSYNC_CMDSYNC) == 0,
|
|
|
|
1000, k_sleep(K_USEC(100)))) {
|
|
|
|
LOG_ERR("poll timeout");
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC */
|
|
|
|
|
|
|
|
static inline void dai_dmic_set_sync_period(uint32_t period, const struct dai_intel_dmic *dmic) {}
|
|
|
|
static inline void dai_dmic_clear_sync_period(const struct dai_intel_dmic *dmic) {}
|
|
|
|
static inline void dai_dmic_sync_prepare(const struct dai_intel_dmic *dmic) {}
|
|
|
|
static void dmic_sync_trigger(const struct dai_intel_dmic *dmic) {}
|
|
|
|
|
|
|
|
#endif /* CONFIG_DAI_DMIC_HAS_MULTIPLE_LINE_SYNC */
|
|
|
|
|
2023-06-13 16:30:07 +02:00
|
|
|
static void dai_dmic_start_fifo_packers(struct dai_intel_dmic *dmic, int fifo_index)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* Start FIFO packers and clear FIFO initialize bits */
|
|
|
|
dai_dmic_update_bits(dmic, fifo_index * PDM_CHANNEL_REGS_SIZE + OUTCONTROL,
|
|
|
|
OUTCONTROL_SIP | OUTCONTROL_FINIT,
|
|
|
|
OUTCONTROL_SIP);
|
|
|
|
}
|
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
static void dai_dmic_stop_fifo_packers(struct dai_intel_dmic *dmic,
|
|
|
|
int fifo_index)
|
|
|
|
{
|
|
|
|
/* Stop FIFO packers and set FIFO initialize bits */
|
2023-05-05 16:06:19 +02:00
|
|
|
dai_dmic_update_bits(dmic, fifo_index * PDM_CHANNEL_REGS_SIZE + OUTCONTROL,
|
|
|
|
OUTCONTROL_SIP | OUTCONTROL_FINIT,
|
|
|
|
OUTCONTROL_FINIT);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dai_dmic_dis_clk_gating(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
/* Disable DMIC clock gating */
|
2024-09-23 13:14:55 +02:00
|
|
|
#if (CONFIG_SOC_INTEL_ACE20_LNL || CONFIG_SOC_INTEL_ACE30)
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32((sys_read32(dmic->vshim_base + DMICLVSCTL_OFFSET) | DMICLVSCTL_DCGD),
|
|
|
|
dmic->vshim_base + DMICLVSCTL_OFFSET);
|
2022-10-21 09:26:09 +02:00
|
|
|
#else
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32((sys_read32(dmic->shim_base + DMICLCTL_OFFSET) | DMICLCTL_DCGD),
|
2022-10-21 09:26:09 +02:00
|
|
|
dmic->shim_base + DMICLCTL_OFFSET);
|
|
|
|
#endif
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void dai_dmic_en_clk_gating(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
/* Enable DMIC clock gating */
|
2024-09-23 13:14:55 +02:00
|
|
|
#if (CONFIG_SOC_INTEL_ACE20_LNL || CONFIG_SOC_INTEL_ACE30)
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32((sys_read32(dmic->vshim_base + DMICLVSCTL_OFFSET) & ~DMICLVSCTL_DCGD),
|
|
|
|
dmic->vshim_base + DMICLVSCTL_OFFSET);
|
2022-10-21 09:07:41 +02:00
|
|
|
#else /* All other CAVS and ACE platforms */
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32((sys_read32(dmic->shim_base + DMICLCTL_OFFSET) & ~DMICLCTL_DCGD),
|
2022-10-21 09:26:09 +02:00
|
|
|
dmic->shim_base + DMICLCTL_OFFSET);
|
|
|
|
#endif
|
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
2022-12-01 18:25:00 +01:00
|
|
|
static inline void dai_dmic_program_channel_map(const struct dai_intel_dmic *dmic,
|
|
|
|
const struct dai_config *cfg,
|
|
|
|
uint32_t index)
|
|
|
|
{
|
2024-09-23 13:14:55 +02:00
|
|
|
#if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30)
|
2022-12-01 18:25:00 +01:00
|
|
|
uint16_t pcmsycm = cfg->link_config;
|
|
|
|
uint32_t reg_add = dmic->shim_base + DMICXPCMSyCM_OFFSET + 0x0004*index;
|
|
|
|
|
|
|
|
sys_write16(pcmsycm, reg_add);
|
|
|
|
#else
|
|
|
|
ARG_UNUSED(dmic);
|
|
|
|
ARG_UNUSED(cfg);
|
|
|
|
ARG_UNUSED(index);
|
2024-09-23 13:14:55 +02:00
|
|
|
#endif /* defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30) */
|
2022-12-01 18:25:00 +01:00
|
|
|
}
|
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
static inline void dai_dmic_en_power(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
2022-10-21 09:26:09 +02:00
|
|
|
uint32_t base = dai_dmic_base(dmic);
|
2022-05-11 16:42:40 +02:00
|
|
|
/* Enable DMIC power */
|
2022-10-21 09:26:09 +02:00
|
|
|
sys_write32((sys_read32(base + DMICLCTL_OFFSET) | DMICLCTL_SPA),
|
|
|
|
base + DMICLCTL_OFFSET);
|
2023-03-17 13:06:41 -05:00
|
|
|
|
2024-09-23 13:14:55 +02:00
|
|
|
#if defined(CONFIG_SOC_INTEL_ACE20_LNL) || defined(CONFIG_SOC_INTEL_ACE30)
|
2022-10-21 09:26:09 +02:00
|
|
|
while (!(sys_read32(base + DMICLCTL_OFFSET) & DMICLCTL_CPA)) {
|
|
|
|
k_sleep(K_USEC(100));
|
|
|
|
}
|
2023-04-21 16:18:24 +03:00
|
|
|
#endif
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
2022-10-21 09:26:09 +02:00
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
static inline void dai_dmic_dis_power(const struct dai_intel_dmic *dmic)
|
|
|
|
{
|
2022-10-21 09:26:09 +02:00
|
|
|
uint32_t base = dai_dmic_base(dmic);
|
2022-05-11 16:42:40 +02:00
|
|
|
/* Disable DMIC power */
|
2022-10-21 09:26:09 +02:00
|
|
|
sys_write32((sys_read32(base + DMICLCTL_OFFSET) & (~DMICLCTL_SPA)),
|
|
|
|
base + DMICLCTL_OFFSET);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_dmic_probe(struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
LOG_INF("dmic_probe()");
|
|
|
|
|
|
|
|
/* Set state, note there is no playback direction support */
|
|
|
|
dmic->state = DAI_STATE_NOT_READY;
|
|
|
|
|
|
|
|
/* Enable DMIC power */
|
|
|
|
dai_dmic_en_power(dmic);
|
|
|
|
|
|
|
|
/* Disable dynamic clock gating for dmic before touching any reg */
|
|
|
|
dai_dmic_dis_clk_gating(dmic);
|
|
|
|
|
|
|
|
/* DMIC Change sync period */
|
|
|
|
dai_dmic_set_sync_period(CONFIG_DAI_DMIC_PLATFORM_SYNC_PERIOD, dmic);
|
|
|
|
|
|
|
|
/* DMIC Owner Select to DSP */
|
|
|
|
dai_dmic_claim_ownership(dmic);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_dmic_remove(struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
uint32_t active_fifos_mask = dai_dmic_global.active_fifos_mask;
|
|
|
|
uint32_t pause_mask = dai_dmic_global.pause_mask;
|
|
|
|
|
|
|
|
LOG_INF("dmic_remove(), dmic_active_fifos_mask = 0x%x, dmic_pause_mask = 0x%x",
|
|
|
|
active_fifos_mask, pause_mask);
|
|
|
|
|
|
|
|
/* The next end tasks must be passed if another DAI FIFO still runs.
|
|
|
|
* Note: dai_put() function that calls remove() applies the spinlock
|
|
|
|
* so it is not needed here to protect access to mask bits.
|
|
|
|
*/
|
2024-08-16 13:26:13 +07:00
|
|
|
if (active_fifos_mask || pause_mask) {
|
2022-05-11 16:42:40 +02:00
|
|
|
return 0;
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
/* Disable DMIC clock and power */
|
|
|
|
dai_dmic_en_clk_gating(dmic);
|
|
|
|
dai_dmic_dis_power(dmic);
|
|
|
|
|
|
|
|
/* DMIC Clean sync period */
|
|
|
|
dai_dmic_clear_sync_period(dmic);
|
|
|
|
|
|
|
|
/* DMIC Owner Select back to Host CPU + DSP */
|
|
|
|
dai_dmic_release_ownership(dmic);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_dmic_timestamp_config(const struct device *dev, struct dai_ts_cfg *cfg)
|
|
|
|
{
|
|
|
|
cfg->walclk_rate = CONFIG_DAI_DMIC_HW_IOCLK;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_timestamp_dmic_start(const struct device *dev, struct dai_ts_cfg *cfg)
|
|
|
|
{
|
|
|
|
uint32_t addr = TS_DMIC_LOCAL_TSCTRL;
|
|
|
|
uint32_t cdmas;
|
|
|
|
|
|
|
|
/* Set DMIC timestamp registers */
|
|
|
|
|
|
|
|
/* First point CDMAS to GPDMA channel that is used by DMIC
|
|
|
|
* also clear NTK to be sure there is no old timestamp.
|
|
|
|
*/
|
2023-04-19 13:45:07 +02:00
|
|
|
cdmas = FIELD_PREP(TS_LOCAL_TSCTRL_CDMAS, cfg->dma_chan_index +
|
2022-05-11 16:42:40 +02:00
|
|
|
cfg->dma_chan_count * cfg->dma_id);
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32(TS_LOCAL_TSCTRL_NTK | cdmas, addr);
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
/* Request on demand timestamp */
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32(TS_LOCAL_TSCTRL_ODTS | cdmas, addr);
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_timestamp_dmic_stop(const struct device *dev, struct dai_ts_cfg *cfg)
|
|
|
|
{
|
|
|
|
/* Clear NTK and write zero to CDMAS */
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32(TS_LOCAL_TSCTRL_NTK, TS_DMIC_LOCAL_TSCTRL);
|
2022-05-11 16:42:40 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_timestamp_dmic_get(const struct device *dev, struct dai_ts_cfg *cfg,
|
2022-08-02 12:39:59 +03:00
|
|
|
struct dai_ts_data *tsd)
|
2022-05-11 16:42:40 +02:00
|
|
|
{
|
|
|
|
/* Read DMIC timestamp registers */
|
|
|
|
uint32_t tsctrl = TS_DMIC_LOCAL_TSCTRL;
|
|
|
|
uint32_t ntk;
|
|
|
|
|
|
|
|
/* Read SSP timestamp registers */
|
2023-04-19 13:45:07 +02:00
|
|
|
ntk = sys_read32(tsctrl) & TS_LOCAL_TSCTRL_NTK;
|
2024-08-16 13:26:13 +07:00
|
|
|
if (!ntk) {
|
2022-05-11 16:42:40 +02:00
|
|
|
goto out;
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
/* NTK was set, get wall clock */
|
|
|
|
tsd->walclk = sys_read64(TS_DMIC_LOCAL_WALCLK);
|
|
|
|
|
|
|
|
/* Sample */
|
|
|
|
tsd->sample = sys_read64(TS_DMIC_LOCAL_SAMPLE);
|
|
|
|
|
|
|
|
/* Clear NTK to enable successive timestamps */
|
2023-04-19 13:45:07 +02:00
|
|
|
sys_write32(TS_LOCAL_TSCTRL_NTK, tsctrl);
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
out:
|
|
|
|
tsd->walclk_rate = cfg->walclk_rate;
|
2024-08-16 13:26:13 +07:00
|
|
|
if (!ntk) {
|
2022-05-11 16:42:40 +02:00
|
|
|
return -ENODATA;
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-06-29 17:36:48 +02:00
|
|
|
/* this ramps volume changes over time */
|
|
|
|
static void dai_dmic_gain_ramp(struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
k_spinlock_key_t key;
|
|
|
|
int32_t gval;
|
|
|
|
uint32_t val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Currently there's no DMIC HW internal mutings and wait times
|
|
|
|
* applied into this start sequence. It can be implemented here if
|
|
|
|
* start of audio capture would contain clicks and/or noise and it
|
|
|
|
* is not suppressed by gain ramp somewhere in the capture pipe.
|
|
|
|
*/
|
|
|
|
LOG_DBG("DMIC gain ramp");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At run-time dmic->gain is only changed in this function, and this
|
|
|
|
* function runs in the pipeline task context, so it cannot run
|
|
|
|
* concurrently on multiple cores, since there's always only one
|
|
|
|
* task associated with each DAI, so we don't need to hold the lock to
|
|
|
|
* read the value here.
|
|
|
|
*/
|
2024-08-16 13:26:13 +07:00
|
|
|
if (dmic->gain == DMIC_HW_FIR_GAIN_MAX << 11) {
|
2022-06-29 17:36:48 +02:00
|
|
|
return;
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-06-29 17:36:48 +02:00
|
|
|
|
|
|
|
key = k_spin_lock(&dmic->lock);
|
|
|
|
|
|
|
|
/* Increment gain with logarithmic step.
|
|
|
|
* Gain is Q2.30 and gain modifier is Q12.20.
|
|
|
|
*/
|
|
|
|
dmic->startcount++;
|
|
|
|
dmic->gain = q_multsr_sat_32x32(dmic->gain, dmic->gain_coef, Q_SHIFT_GAIN_X_GAIN_COEF);
|
|
|
|
|
|
|
|
/* Gain is stored as Q2.30, while HW register is Q1.19 so shift
|
|
|
|
* the value right by 11.
|
|
|
|
*/
|
|
|
|
gval = dmic->gain >> 11;
|
|
|
|
|
|
|
|
/* Note that DMIC gain value zero has a special purpose. Value zero
|
|
|
|
* sets gain bypass mode in HW. Zero value will be applied after ramp
|
|
|
|
* is complete. It is because exact 1.0 gain is not possible with Q1.19.
|
|
|
|
*/
|
|
|
|
if (gval > DMIC_HW_FIR_GAIN_MAX) {
|
|
|
|
gval = 0;
|
|
|
|
dmic->gain = DMIC_HW_FIR_GAIN_MAX << 11;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write gain to registers */
|
|
|
|
for (i = 0; i < CONFIG_DAI_DMIC_HW_CONTROLLERS; i++) {
|
2024-08-21 03:00:57 +07:00
|
|
|
if (!dmic->enable[i]) {
|
2022-06-29 17:36:48 +02:00
|
|
|
continue;
|
2024-08-21 03:00:57 +07:00
|
|
|
}
|
2022-11-28 11:23:55 +02:00
|
|
|
|
2024-08-21 03:00:57 +07:00
|
|
|
if (dmic->startcount == DMIC_UNMUTE_CIC) {
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + CIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
CIC_CONTROL_MIC_MUTE, 0);
|
2024-08-21 03:00:57 +07:00
|
|
|
}
|
2022-11-28 11:23:55 +02:00
|
|
|
|
2022-06-29 17:36:48 +02:00
|
|
|
if (dmic->startcount == DMIC_UNMUTE_FIR) {
|
2023-06-19 15:01:33 +02:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + FIR_CHANNEL_REGS_SIZE *
|
|
|
|
dmic->dai_config_params.dai_index + FIR_CONTROL,
|
|
|
|
FIR_CONTROL_MUTE, 0);
|
2022-06-29 17:36:48 +02:00
|
|
|
}
|
2023-06-19 15:01:33 +02:00
|
|
|
|
2023-09-27 09:33:29 +02:00
|
|
|
if (gval != 0) {
|
|
|
|
val = FIELD_PREP(OUT_GAIN, gval);
|
|
|
|
dai_dmic_write(dmic, dmic_base[i] + FIR_CHANNEL_REGS_SIZE *
|
|
|
|
dmic->dai_config_params.dai_index + OUT_GAIN_LEFT, val);
|
|
|
|
dai_dmic_write(dmic, dmic_base[i] + FIR_CHANNEL_REGS_SIZE *
|
|
|
|
dmic->dai_config_params.dai_index + OUT_GAIN_RIGHT, val);
|
|
|
|
} else {
|
|
|
|
dai_dmic_write(dmic, dmic_base[i] + FIR_CHANNEL_REGS_SIZE *
|
|
|
|
dmic->dai_config_params.dai_index + OUT_GAIN_LEFT,
|
|
|
|
dmic->gain_left);
|
|
|
|
dai_dmic_write(dmic, dmic_base[i] + FIR_CHANNEL_REGS_SIZE *
|
|
|
|
dmic->dai_config_params.dai_index + OUT_GAIN_RIGHT,
|
|
|
|
dmic->gain_right);
|
|
|
|
}
|
2022-06-29 17:36:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
k_spin_unlock(&dmic->lock, key);
|
|
|
|
}
|
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
static void dai_dmic_start(struct dai_intel_dmic *dmic)
|
|
|
|
{
|
|
|
|
k_spinlock_key_t key;
|
|
|
|
int i;
|
|
|
|
int mic_a;
|
|
|
|
int mic_b;
|
2023-06-19 15:01:33 +02:00
|
|
|
int start_fir;
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
/* enable port */
|
|
|
|
key = k_spin_lock(&dmic->lock);
|
2024-06-14 12:40:21 +03:00
|
|
|
|
|
|
|
#ifdef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
|
2024-08-21 03:00:57 +07:00
|
|
|
for (i = 0; i < CONFIG_DAI_DMIC_HW_CONTROLLERS; i++) {
|
2024-06-14 12:40:21 +03:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + CIC_CONTROL, CIC_CONTROL_SOFT_RESET, 0);
|
2024-08-21 03:00:57 +07:00
|
|
|
}
|
2024-06-14 12:40:21 +03:00
|
|
|
#endif
|
|
|
|
|
2022-06-29 17:36:48 +02:00
|
|
|
dmic->startcount = 0;
|
|
|
|
|
|
|
|
/* Compute unmute ramp gain update coefficient. */
|
|
|
|
dmic->gain_coef = db2lin_fixed(LOGRAMP_CONST_TERM / dmic->unmute_time_ms);
|
|
|
|
|
|
|
|
/* Initial gain value, convert Q12.20 to Q2.30 */
|
|
|
|
dmic->gain = Q_SHIFT_LEFT(db2lin_fixed(LOGRAMP_START_DB), 20, 30);
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
dai_dmic_sync_prepare(dmic);
|
|
|
|
|
2023-06-13 16:30:07 +02:00
|
|
|
dai_dmic_start_fifo_packers(dmic, dmic->dai_config_params.dai_index);
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_DAI_DMIC_HW_CONTROLLERS; i++) {
|
|
|
|
mic_a = dmic->enable[i] & 1;
|
|
|
|
mic_b = (dmic->enable[i] & 2) >> 1;
|
2023-06-19 15:01:33 +02:00
|
|
|
start_fir = dmic->enable[i] > 0;
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
/* If both microphones are needed start them simultaneously
|
|
|
|
* to start them in sync. The reset may be cleared for another
|
|
|
|
* FIFO already. If only one mic, start them independently.
|
|
|
|
* This makes sure we do not clear start/en for another DAI.
|
|
|
|
*/
|
|
|
|
if (mic_a && mic_b) {
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + CIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
CIC_CONTROL_CIC_START_A |
|
|
|
|
CIC_CONTROL_CIC_START_B,
|
|
|
|
FIELD_PREP(CIC_CONTROL_CIC_START_A, 1) |
|
|
|
|
FIELD_PREP(CIC_CONTROL_CIC_START_B, 1));
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + MIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
MIC_CONTROL_PDM_EN_A |
|
|
|
|
MIC_CONTROL_PDM_EN_B,
|
|
|
|
FIELD_PREP(MIC_CONTROL_PDM_EN_A, 1) |
|
|
|
|
FIELD_PREP(MIC_CONTROL_PDM_EN_B, 1));
|
2022-05-11 16:42:40 +02:00
|
|
|
} else if (mic_a) {
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + CIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
CIC_CONTROL_CIC_START_A,
|
|
|
|
FIELD_PREP(CIC_CONTROL_CIC_START_A, 1));
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + MIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
MIC_CONTROL_PDM_EN_A,
|
|
|
|
FIELD_PREP(MIC_CONTROL_PDM_EN_A, 1));
|
2022-05-11 16:42:40 +02:00
|
|
|
} else if (mic_b) {
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + CIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
CIC_CONTROL_CIC_START_B,
|
|
|
|
FIELD_PREP(CIC_CONTROL_CIC_START_B, 1));
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + MIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
MIC_CONTROL_PDM_EN_B,
|
|
|
|
FIELD_PREP(MIC_CONTROL_PDM_EN_B, 1));
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
2023-06-19 15:01:33 +02:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + FIR_CHANNEL_REGS_SIZE *
|
|
|
|
dmic->dai_config_params.dai_index + FIR_CONTROL,
|
|
|
|
FIR_CONTROL_START,
|
|
|
|
FIELD_PREP(FIR_CONTROL_START, start_fir));
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
2022-09-14 22:23:15 +02:00
|
|
|
#ifndef CONFIG_SOC_SERIES_INTEL_ADSP_ACE
|
2022-11-28 11:23:55 +02:00
|
|
|
/* Clear soft reset for all/used PDM controllers. This should
|
|
|
|
* start capture in sync.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < CONFIG_DAI_DMIC_HW_CONTROLLERS; i++) {
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + CIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
CIC_CONTROL_SOFT_RESET, 0);
|
2022-11-28 11:23:55 +02:00
|
|
|
|
|
|
|
LOG_INF("dmic_start(), cic 0x%08x",
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_read(dmic, dmic_base[i] + CIC_CONTROL));
|
2022-11-28 11:23:55 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
/* Set bit dai->index */
|
|
|
|
dai_dmic_global.active_fifos_mask |= BIT(dmic->dai_config_params.dai_index);
|
|
|
|
dai_dmic_global.pause_mask &= ~BIT(dmic->dai_config_params.dai_index);
|
|
|
|
|
|
|
|
dmic->state = DAI_STATE_RUNNING;
|
|
|
|
k_spin_unlock(&dmic->lock, key);
|
|
|
|
|
|
|
|
dmic_sync_trigger(dmic);
|
|
|
|
|
|
|
|
LOG_INF("dmic_start(), dmic_active_fifos_mask = 0x%x",
|
2022-08-02 12:39:59 +03:00
|
|
|
dai_dmic_global.active_fifos_mask);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dai_dmic_stop(struct dai_intel_dmic *dmic, bool stop_is_pause)
|
|
|
|
{
|
|
|
|
k_spinlock_key_t key;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
LOG_DBG("dmic_stop()");
|
|
|
|
key = k_spin_lock(&dmic->lock);
|
|
|
|
|
|
|
|
dai_dmic_stop_fifo_packers(dmic, dmic->dai_config_params.dai_index);
|
|
|
|
|
|
|
|
/* Set soft reset and mute on for all PDM controllers. */
|
|
|
|
LOG_INF("dmic_stop(), dmic_active_fifos_mask = 0x%x",
|
|
|
|
dai_dmic_global.active_fifos_mask);
|
|
|
|
|
|
|
|
/* Clear bit dmic->dai_config_params.dai_index for active FIFO.
|
|
|
|
* If stop for pause, set pause mask bit.
|
|
|
|
* If stop is not for pausing, it is safe to clear the pause bit.
|
|
|
|
*/
|
|
|
|
dai_dmic_global.active_fifos_mask &= ~BIT(dmic->dai_config_params.dai_index);
|
2024-08-16 13:26:13 +07:00
|
|
|
if (stop_is_pause) {
|
2022-05-11 16:42:40 +02:00
|
|
|
dai_dmic_global.pause_mask |= BIT(dmic->dai_config_params.dai_index);
|
2024-08-16 13:26:13 +07:00
|
|
|
} else {
|
2022-05-11 16:42:40 +02:00
|
|
|
dai_dmic_global.pause_mask &= ~BIT(dmic->dai_config_params.dai_index);
|
2024-08-16 13:26:13 +07:00
|
|
|
}
|
2022-05-11 16:42:40 +02:00
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_DAI_DMIC_HW_CONTROLLERS; i++) {
|
|
|
|
/* Don't stop CIC yet if one FIFO remains active */
|
|
|
|
if (dai_dmic_global.active_fifos_mask == 0) {
|
2023-08-04 10:57:01 -07:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + CIC_CONTROL,
|
2023-04-19 13:45:07 +02:00
|
|
|
CIC_CONTROL_SOFT_RESET |
|
|
|
|
CIC_CONTROL_MIC_MUTE,
|
|
|
|
CIC_CONTROL_SOFT_RESET |
|
|
|
|
CIC_CONTROL_MIC_MUTE);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
2023-06-19 15:01:33 +02:00
|
|
|
dai_dmic_update_bits(dmic, dmic_base[i] + FIR_CHANNEL_REGS_SIZE *
|
|
|
|
dmic->dai_config_params.dai_index + FIR_CONTROL,
|
|
|
|
FIR_CONTROL_MUTE,
|
|
|
|
FIR_CONTROL_MUTE);
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
k_spin_unlock(&dmic->lock, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct dai_properties *dai_dmic_get_properties(const struct device *dev,
|
|
|
|
enum dai_dir dir,
|
|
|
|
int stream_id)
|
|
|
|
{
|
|
|
|
const struct dai_intel_dmic *dmic = (const struct dai_intel_dmic *)dev->data;
|
|
|
|
struct dai_properties *prop = (struct dai_properties *)dev->config;
|
|
|
|
|
|
|
|
prop->fifo_address = dmic->fifo.offset;
|
2022-09-02 16:20:38 +02:00
|
|
|
prop->fifo_depth = dmic->fifo.depth;
|
2022-05-11 16:42:40 +02:00
|
|
|
prop->dma_hs_id = dmic->fifo.handshake;
|
|
|
|
prop->reg_init_delay = 0;
|
|
|
|
|
|
|
|
return prop;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_dmic_trigger(const struct device *dev, enum dai_dir dir,
|
2022-08-02 12:39:59 +03:00
|
|
|
enum dai_trigger_cmd cmd)
|
2022-05-11 16:42:40 +02:00
|
|
|
{
|
|
|
|
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
|
|
|
|
|
|
|
|
LOG_DBG("dmic_trigger()");
|
|
|
|
|
|
|
|
if (dir != DAI_DIR_RX) {
|
|
|
|
LOG_ERR("dmic_trigger(): direction != DAI_DIR_RX");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case DAI_TRIGGER_START:
|
|
|
|
if (dmic->state == DAI_STATE_PAUSED ||
|
|
|
|
dmic->state == DAI_STATE_PRE_RUNNING) {
|
|
|
|
dai_dmic_start(dmic);
|
|
|
|
dmic->state = DAI_STATE_RUNNING;
|
|
|
|
} else {
|
|
|
|
LOG_ERR("dmic_trigger(): state is not prepare or paused, dmic->state = %u",
|
|
|
|
dmic->state);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DAI_TRIGGER_STOP:
|
|
|
|
dai_dmic_stop(dmic, false);
|
|
|
|
dmic->state = DAI_STATE_PRE_RUNNING;
|
|
|
|
break;
|
|
|
|
case DAI_TRIGGER_PAUSE:
|
|
|
|
dai_dmic_stop(dmic, true);
|
|
|
|
dmic->state = DAI_STATE_PAUSED;
|
|
|
|
break;
|
2022-06-29 17:36:48 +02:00
|
|
|
case DAI_TRIGGER_COPY:
|
|
|
|
dai_dmic_gain_ramp(dmic);
|
|
|
|
break;
|
2022-05-11 16:42:40 +02:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-01-10 11:19:43 +02:00
|
|
|
static int dai_dmic_get_config(const struct device *dev, struct dai_config *cfg, enum dai_dir dir)
|
2022-05-11 16:42:40 +02:00
|
|
|
{
|
|
|
|
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
|
|
|
|
|
2023-01-10 11:19:43 +02:00
|
|
|
if (dir != DAI_DIR_RX) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2022-12-30 14:30:44 +02:00
|
|
|
|
2023-01-10 11:19:43 +02:00
|
|
|
*cfg = dmic->dai_config_params;
|
|
|
|
|
|
|
|
return 0;
|
2022-05-11 16:42:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_dmic_set_config(const struct device *dev,
|
|
|
|
const struct dai_config *cfg, const void *bespoke_cfg)
|
|
|
|
|
|
|
|
{
|
|
|
|
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
|
|
|
|
int ret = 0;
|
|
|
|
int di = dmic->dai_config_params.dai_index;
|
|
|
|
k_spinlock_key_t key;
|
|
|
|
|
|
|
|
LOG_INF("dmic_set_config()");
|
|
|
|
|
|
|
|
if (di >= CONFIG_DAI_DMIC_HW_FIFOS) {
|
|
|
|
LOG_ERR("dmic_set_config(): DAI index exceeds number of FIFOs");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bespoke_cfg) {
|
|
|
|
LOG_ERR("dmic_set_config(): NULL config");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-12-01 18:25:00 +01:00
|
|
|
dai_dmic_program_channel_map(dmic, cfg, di);
|
|
|
|
|
2022-05-11 16:42:40 +02:00
|
|
|
key = k_spin_lock(&dmic->lock);
|
|
|
|
|
|
|
|
#if CONFIG_DAI_INTEL_DMIC_TPLG_PARAMS
|
|
|
|
#error DMIC TPLG is not yet implemented
|
|
|
|
|
|
|
|
#elif CONFIG_DAI_INTEL_DMIC_NHLT
|
|
|
|
ret = dai_dmic_set_config_nhlt(dmic, bespoke_cfg);
|
|
|
|
|
2022-06-29 17:36:48 +02:00
|
|
|
/* There's no unmute ramp duration in blob, so the default rate dependent is used. */
|
|
|
|
dmic->unmute_time_ms = dmic_get_unmute_ramp_from_samplerate(dmic->dai_config_params.rate);
|
2022-05-11 16:42:40 +02:00
|
|
|
#else
|
|
|
|
#error No DMIC config selected
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("dmic_set_config(): Failed to set the requested configuration.");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dmic->state = DAI_STATE_PRE_RUNNING;
|
|
|
|
|
|
|
|
out:
|
|
|
|
k_spin_unlock(&dmic->lock, key);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_dmic_probe_wrapper(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
|
|
|
|
k_spinlock_key_t key;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
key = k_spin_lock(&dmic->lock);
|
|
|
|
|
|
|
|
if (dmic->sref == 0) {
|
|
|
|
ret = dai_dmic_probe(dmic);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
dmic->sref++;
|
|
|
|
}
|
|
|
|
|
|
|
|
k_spin_unlock(&dmic->lock, key);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dai_dmic_remove_wrapper(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct dai_intel_dmic *dmic = (struct dai_intel_dmic *)dev->data;
|
|
|
|
k_spinlock_key_t key;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
key = k_spin_lock(&dmic->lock);
|
|
|
|
|
|
|
|
if (--dmic->sref == 0) {
|
|
|
|
ret = dai_dmic_remove(dmic);
|
|
|
|
}
|
|
|
|
|
|
|
|
k_spin_unlock(&dmic->lock, key);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-11-25 00:12:16 +01:00
|
|
|
static int dmic_pm_action(const struct device *dev, enum pm_device_action action)
|
|
|
|
{
|
|
|
|
switch (action) {
|
|
|
|
case PM_DEVICE_ACTION_SUSPEND:
|
|
|
|
dai_dmic_remove_wrapper(dev);
|
|
|
|
break;
|
|
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
|
|
dai_dmic_probe_wrapper(dev);
|
|
|
|
break;
|
|
|
|
case PM_DEVICE_ACTION_TURN_OFF:
|
|
|
|
case PM_DEVICE_ACTION_TURN_ON:
|
|
|
|
/* All device pm is handled during resume and suspend */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-11-27 21:46:01 +01:00
|
|
|
DEVICE_API(dai, dai_dmic_ops) = {
|
2022-11-25 00:12:16 +01:00
|
|
|
.probe = pm_device_runtime_get,
|
|
|
|
.remove = pm_device_runtime_put,
|
2022-05-11 16:42:40 +02:00
|
|
|
.config_set = dai_dmic_set_config,
|
|
|
|
.config_get = dai_dmic_get_config,
|
|
|
|
.get_properties = dai_dmic_get_properties,
|
|
|
|
.trigger = dai_dmic_trigger,
|
|
|
|
.ts_config = dai_dmic_timestamp_config,
|
|
|
|
.ts_start = dai_timestamp_dmic_start,
|
|
|
|
.ts_stop = dai_timestamp_dmic_stop,
|
|
|
|
.ts_get = dai_timestamp_dmic_get
|
|
|
|
};
|
|
|
|
|
|
|
|
static int dai_dmic_initialize_device(const struct device *dev)
|
|
|
|
{
|
2024-11-19 14:42:00 +01:00
|
|
|
return pm_device_driver_init(dev, dmic_pm_action);
|
2022-05-11 16:42:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#define DAI_INTEL_DMIC_DEVICE_INIT(n) \
|
|
|
|
static struct dai_properties dai_intel_dmic_properties_##n; \
|
|
|
|
\
|
|
|
|
static struct dai_intel_dmic dai_intel_dmic_data_##n = \
|
|
|
|
{ .dai_config_params = \
|
|
|
|
{ \
|
|
|
|
.type = DAI_INTEL_DMIC, \
|
|
|
|
.dai_index = n \
|
|
|
|
}, \
|
|
|
|
.reg_base = DT_INST_REG_ADDR_BY_IDX(n, 0), \
|
2022-08-19 14:16:20 +03:00
|
|
|
.shim_base = DT_INST_PROP(n, shim), \
|
2022-10-21 09:07:41 +02:00
|
|
|
IF_ENABLED(DT_NODE_EXISTS(DT_NODELABEL(hdamlddmic)), \
|
|
|
|
(.hdamldmic_base = DT_REG_ADDR(DT_NODELABEL(hdamlddmic)),)) \
|
|
|
|
IF_ENABLED(DT_NODE_EXISTS(DT_NODELABEL(dmicvss)), \
|
|
|
|
(.vshim_base = DT_REG_ADDR(DT_NODELABEL(dmicvss)),)) \
|
2022-05-11 16:42:40 +02:00
|
|
|
.irq = DT_INST_IRQN(n), \
|
|
|
|
.fifo = \
|
|
|
|
{ \
|
|
|
|
.offset = DT_INST_REG_ADDR_BY_IDX(n, 0) \
|
2022-08-19 14:16:20 +03:00
|
|
|
+ DT_INST_PROP(n, fifo), \
|
2022-05-11 16:42:40 +02:00
|
|
|
.handshake = DMA_HANDSHAKE_DMIC_CH##n \
|
|
|
|
}, \
|
|
|
|
}; \
|
|
|
|
\
|
2022-11-25 00:12:16 +01:00
|
|
|
PM_DEVICE_DT_INST_DEFINE(n, dmic_pm_action); \
|
|
|
|
\
|
2022-05-11 16:42:40 +02:00
|
|
|
DEVICE_DT_INST_DEFINE(n, \
|
|
|
|
dai_dmic_initialize_device, \
|
2022-11-25 00:12:16 +01:00
|
|
|
PM_DEVICE_DT_INST_GET(n), \
|
2022-05-11 16:42:40 +02:00
|
|
|
&dai_intel_dmic_data_##n, \
|
|
|
|
&dai_intel_dmic_properties_##n, \
|
|
|
|
POST_KERNEL, \
|
2022-08-19 14:16:20 +03:00
|
|
|
CONFIG_DAI_INIT_PRIORITY, \
|
2022-05-11 16:42:40 +02:00
|
|
|
&dai_dmic_ops);
|
|
|
|
|
2022-08-02 12:39:59 +03:00
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DAI_INTEL_DMIC_DEVICE_INIT)
|