zephyr/drivers/dai/intel/hda/hda.c
Tomasz Leman fd4a4bf702 dai: intel: hda: Add power management
This patch addresses the following issues with the Intel HDA DAI driver:

1. Adds power management support for the HDA DAI driver by implementing
   the `hda_pm_action` function and integrating it with the Zephyr power
   management framework.
2. Ensures balanced calls to `pm_device_runtime_get` and
   `pm_device_runtime_put` by modifying the `probe` and `remove`
   functions to use these power management calls.
3. Ensures that the io0 power domain is active when the HD Audio is in
   use by assigning the correct power domain to the HDA DAI devices in
   the device tree files for various Intel ADSP platforms (ace15_mtpm,
   ace20_lnl, ace30, ace30_ptl).
4. Enables runtime power management for the HDA DAI devices by adding
   the `zephyr,pm-device-runtime-auto` property in the device tree.

These changes ensure that the HDA DAI driver properly manages power
states, reducing power consumption and improving system stability, while
ensuring the io0 power domain is active when required.

Signed-off-by: Tomasz Leman <tomasz.m.leman@intel.com>
2024-12-11 21:34:35 +01:00

164 lines
3.7 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/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/logging/log.h>
#define DT_DRV_COMPAT intel_hda_dai
#define LOG_DOMAIN dai_intel_hda
LOG_MODULE_REGISTER(LOG_DOMAIN);
#include "hda.h"
static int dai_hda_trigger(const struct device *dev, enum dai_dir dir,
enum dai_trigger_cmd cmd)
{
LOG_DBG("cmd %d", cmd);
return 0;
}
/* Digital Audio interface formatting */
static int dai_hda_set_config_tplg(struct dai_intel_hda *dp, const void *spec_config)
{
struct dai_intel_hda_pdata *hda = dai_get_drvdata(dp);
const struct dai_intel_ipc_hda_params *config = spec_config;
if (config->channels) {
hda->params.channels = config->channels;
}
if (config->rate) {
hda->params.rate = config->rate;
}
return 0;
}
static int dai_hda_config_get(const struct device *dev, struct dai_config *cfg, enum dai_dir dir)
{
struct dai_config *params = (struct dai_config *)dev->config;
struct dai_intel_hda *dp = (struct dai_intel_hda *)dev->data;
struct dai_intel_hda_pdata *hda = dai_get_drvdata(dp);
if (!cfg) {
return -EINVAL;
}
params->rate = hda->params.rate;
params->channels = hda->params.channels;
params->word_size = DAI_INTEL_HDA_DEFAULT_WORD_SIZE;
*cfg = *params;
return 0;
}
static int dai_hda_config_set(const struct device *dev, const struct dai_config *cfg,
const void *bespoke_cfg)
{
struct dai_intel_hda *dp = (struct dai_intel_hda *)dev->data;
if (cfg->type == DAI_INTEL_HDA) {
return dai_hda_set_config_tplg(dp, bespoke_cfg);
}
return 0;
}
static const struct dai_properties *dai_hda_get_properties(const struct device *dev,
enum dai_dir dir, int stream_id)
{
struct dai_intel_hda *dp = (struct dai_intel_hda *)dev->data;
struct dai_intel_hda_pdata *hda = dai_get_drvdata(dp);
struct dai_properties *prop = &hda->props;
prop->fifo_address = 0;
prop->dma_hs_id = 0;
prop->stream_id = 0;
return prop;
}
static int dai_hda_probe(const struct device *dev)
{
LOG_DBG("%s", __func__);
return 0;
}
static int dai_hda_remove(const struct device *dev)
{
LOG_DBG("%s", __func__);
return 0;
}
static int hda_pm_action(const struct device *dev, enum pm_device_action action)
{
switch (action) {
case PM_DEVICE_ACTION_SUSPEND:
dai_hda_remove(dev);
break;
case PM_DEVICE_ACTION_RESUME:
dai_hda_probe(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;
}
static int hda_init(const struct device *dev)
{
LOG_DBG("%s", __func__);
return pm_device_driver_init(dev, hda_pm_action);
}
static DEVICE_API(dai, dai_intel_hda_api_funcs) = {
.probe = pm_device_runtime_get,
.remove = pm_device_runtime_put,
.config_set = dai_hda_config_set,
.config_get = dai_hda_config_get,
.trigger = dai_hda_trigger,
.get_properties = dai_hda_get_properties,
};
#define DAI_INTEL_HDA_DEVICE_INIT(n) \
static struct dai_config dai_intel_hda_config_##n = { \
.type = DAI_INTEL_HDA, \
.dai_index = DT_INST_REG_ADDR(n), \
}; \
static struct dai_intel_hda dai_intel_hda_data_##n = { \
.index = DT_INST_REG_ADDR(n) \
\
}; \
\
PM_DEVICE_DT_INST_DEFINE(n, hda_pm_action); \
\
DEVICE_DT_INST_DEFINE(n, \
hda_init, PM_DEVICE_DT_INST_GET(n), \
&dai_intel_hda_data_##n, \
&dai_intel_hda_config_##n, \
POST_KERNEL, \
CONFIG_DAI_INIT_PRIORITY, \
&dai_intel_hda_api_funcs);
DT_INST_FOREACH_STATUS_OKAY(DAI_INTEL_HDA_DEVICE_INIT)