// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 Google, Inc.
*
* ChromeOS Embedded Controller codec driver.
*
* This driver uses the cros-ec interface to communicate with the ChromeOS
* EC for audio function.
*/
#include <crypto/hash.h>
#include <crypto/sha.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
struct cros_ec_codec_priv {
struct device *dev;
struct cros_ec_device *ec_device;
/* common */
uint32_t ec_capabilities;
uint64_t ec_shm_addr;
uint32_t ec_shm_len;
uint64_t ap_shm_phys_addr;
uint32_t ap_shm_len;
uint64_t ap_shm_addr;
uint64_t ap_shm_last_alloc;
/* DMIC */
atomic_t dmic_probed;
/* WoV */
bool wov_enabled;
uint8_t *wov_audio_shm_p;
uint32_t wov_audio_shm_len;
uint8_t wov_audio_shm_type;
uint8_t *wov_lang_shm_p;
uint32_t wov_lang_shm_len;
uint8_t wov_lang_shm_type;
struct mutex wov_dma_lock;
uint8_t wov_buf[64000];
uint32_t wov_rp, wov_wp;
size_t wov_dma_offset;
bool wov_burst_read;
struct snd_pcm_substream *wov_substream;
struct delayed_work wov_copy_work;
struct notifier_block wov_notifier;
};
static int ec_codec_capable(struct cros_ec_codec_priv *priv, uint8_t cap)
{
return priv->ec_capabilities & BIT(cap);
}
static int send_ec_host_command(struct cros_ec_device *ec_dev, uint32_t cmd,
uint8_t *out, size_t outsize,
uint8_t *in, size_t insize)
{
int ret;
struct cros_ec_command *msg;
msg = kmalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL);
if (!msg)
return -ENOMEM;
msg->version = 0;
msg->command = cmd;
msg->outsize = outsize;
msg->insize = insize;
if (outsize)
memcpy(msg->data, out, outsize);
ret = cros_ec_cmd_xfer_status(ec_dev, msg);
if (ret < 0)
goto error;
if (insize)
memcpy(in, msg->data, insize);
ret = 0;
error:
kfree(msg);
return ret;
}
static int calculate_sha256(struct cros_ec_codec_priv *priv,
uint8_t *buf, uint32_t size, uint8_t *digest)
{
struct crypto_shash *tfm;
tfm = crypto_alloc_shash("sha256", CRYPTO_ALG_TYPE_SHASH, 0);
if (IS_ERR(tfm)) {
dev_err(priv->dev, "can't alloc shash\n");
return PTR_ERR(tfm);
}
{
SHASH_DESC_ON_STACK(desc, tfm);
desc->tfm = tfm;
crypto_shash_digest(desc, buf, size, digest);
shash_desc_zero(desc);
}
crypto_free_shash(tfm);
#ifdef DEBUG
{
char digest_str[65];
bin2hex(digest_str, digest, 32);
digest_str[64] = 0;
dev_dbg(priv->dev, "hash=%s\n", digest_str);
}
#endif
return 0;
}
static int dmic_get_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component =
snd_soc_kcontrol_component(kcontrol);
struct cros_ec_codec_priv *priv =
snd_soc_component_get_drvdata(component);
struct ec_param_ec_codec_dmic p;
struct ec_response_ec_codec_dmic_get_gain_idx r;
int ret;
p.cmd = EC_CODEC_DMIC_GET_GAIN_IDX;
p.get_gain_idx_param.channel = EC_CODEC_DMIC_CHANNEL_0;