blob: aba1453ce353d66e176c1b22f91ec4bafa94dcaf [file] [log] [blame]
/*
* Copyright 2019 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "audio_codec.h"
#include "console.h"
#include "host_command.h"
#include "util.h"
#define CPRINTS(format, args...) cprints(CC_AUDIO_CODEC, format, ##args)
static const uint32_t capabilities = 0
#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_AUDIO_SHM
| BIT(EC_CODEC_CAP_WOV_AUDIO_SHM)
#endif
#ifdef CONFIG_AUDIO_CODEC_CAP_WOV_LANG_SHM
| BIT(EC_CODEC_CAP_WOV_LANG_SHM)
#endif
;
static struct {
uint8_t cap;
uint8_t type;
uintptr_t *addr;
uint32_t len;
} shms[EC_CODEC_SHM_ID_LAST];
static enum ec_status get_capabilities(struct host_cmd_handler_args *args)
{
struct ec_response_ec_codec_get_capabilities *r = args->response;
r->capabilities = capabilities;
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
static enum ec_status get_shm_addr(struct host_cmd_handler_args *args)
{
const struct ec_param_ec_codec *p = args->params;
struct ec_response_ec_codec_get_shm_addr *r = args->response;
const uint8_t shm_id = p->get_shm_addr_param.shm_id;
if (shm_id >= EC_CODEC_SHM_ID_LAST)
return EC_RES_INVALID_PARAM;
if (!shms[shm_id].addr || !audio_codec_capable(shms[shm_id].cap))
return EC_RES_INVALID_PARAM;
if (!*shms[shm_id].addr &&
shms[shm_id].type == EC_CODEC_SHM_TYPE_EC_RAM)
return EC_RES_ERROR;
r->len = shms[shm_id].len;
r->type = shms[shm_id].type;
r->phys_addr = *shms[shm_id].addr;
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
static enum ec_status set_shm_addr(struct host_cmd_handler_args *args)
{
const struct ec_param_ec_codec *p = args->params;
const uint8_t shm_id = p->set_shm_addr_param.shm_id;
uintptr_t ap_addr, ec_addr;
if (shm_id >= EC_CODEC_SHM_ID_LAST)
return EC_RES_INVALID_PARAM;
if (!shms[shm_id].addr || !audio_codec_capable(shms[shm_id].cap))
return EC_RES_INVALID_PARAM;
if (p->set_shm_addr_param.len < shms[shm_id].len)
return EC_RES_INVALID_PARAM;
if (*shms[shm_id].addr)
return EC_RES_BUSY;
ap_addr = (uintptr_t)p->set_shm_addr_param.phys_addr;
if (audio_codec_memmap_ap_to_ec(ap_addr, &ec_addr) != EC_SUCCESS)
return EC_RES_ERROR;
*shms[shm_id].addr = ec_addr;
args->response_size = 0;
return EC_RES_SUCCESS;
}
static enum ec_status (*sub_cmds[])(struct host_cmd_handler_args *) = {
[EC_CODEC_GET_CAPABILITIES] = get_capabilities,
[EC_CODEC_GET_SHM_ADDR] = get_shm_addr,
[EC_CODEC_SET_SHM_ADDR] = set_shm_addr,
};
#ifdef DEBUG_AUDIO_CODEC
static char *strcmd[] = {
[EC_CODEC_GET_CAPABILITIES] = "EC_CODEC_GET_CAPABILITIES",
[EC_CODEC_GET_SHM_ADDR] = "EC_CODEC_GET_SHM_ADDR",
[EC_CODEC_SET_SHM_ADDR] = "EC_CODEC_SET_SHM_ADDR",
};
BUILD_ASSERT(ARRAY_SIZE(sub_cmds) == ARRAY_SIZE(strcmd));
#endif
static enum ec_status host_command(struct host_cmd_handler_args *args)
{
const struct ec_param_ec_codec *p = args->params;
#ifdef DEBUG_AUDIO_CODEC
CPRINTS("subcommand: %s", strcmd[p->cmd]);
#endif
if (p->cmd < EC_CODEC_SUBCMD_COUNT)
return sub_cmds[p->cmd](args);
return EC_RES_INVALID_PARAM;
}
DECLARE_HOST_COMMAND(EC_CMD_EC_CODEC, host_command, EC_VER_MASK(0));
/*
* Exported interfaces.
*/
int audio_codec_capable(uint8_t cap)
{
return capabilities & BIT(cap);
}
int audio_codec_register_shm(uint8_t shm_id, uint8_t cap, uintptr_t *addr,
uint32_t len, uint8_t type)
{
if (shm_id >= EC_CODEC_SHM_ID_LAST)
return EC_ERROR_INVAL;
if (cap >= EC_CODEC_CAP_LAST)
return EC_ERROR_INVAL;
if (shms[shm_id].addr || shms[shm_id].len)
return EC_ERROR_BUSY;
shms[shm_id].cap = cap;
shms[shm_id].addr = addr;
shms[shm_id].len = len;
shms[shm_id].type = type;
return EC_SUCCESS;
}
__attribute__((weak)) int audio_codec_memmap_ap_to_ec(uintptr_t ap_addr,
uintptr_t *ec_addr)
{
return EC_ERROR_UNIMPLEMENTED;
}
int16_t audio_codec_s16_scale_and_clip(int16_t orig, uint8_t scalar)
{
int32_t val;
val = (int32_t)orig * (int32_t)scalar;
val = MIN(val, (int32_t)INT16_MAX);
val = MAX(val, (int32_t)INT16_MIN);
return val;
}