kernel_samsung_a34x-permissive/sound/soc/codecs/tfa9878/tfa_vval.c
2024-04-28 15:49:01 +02:00

299 lines
7.2 KiB
C
Executable file

/*
* Copyright (C) 2014-2020 NXP Semiconductors, All Rights Reserved.
* Copyright 2020-2021 GOODIX, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include "inc/tfa_sysfs.h"
#define TFA_VVAL_DEV_NAME "tfa_vval"
#define FILESIZE_VVAL (10)
/* ---------------------------------------------------------------------- */
static ssize_t validation_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t validation_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size);
static DEVICE_ATTR_RW(validation);
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t status_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size);
static DEVICE_ATTR_RW(status);
#if defined(TFA_STEREO_NODE)
static ssize_t status_r_show(struct device *dev,
struct device_attribute *attr, char *buf);
static ssize_t status_r_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size);
static DEVICE_ATTR_RW(status_r);
#endif /* TFA_STEREO_NODE */
static struct attribute *tfa_vval_attr[] = {
&dev_attr_validation.attr,
&dev_attr_status.attr,
#if defined(TFA_STEREO_NODE)
&dev_attr_status_r.attr,
#endif /* TFA_STEREO_NODE */
NULL,
};
static struct attribute_group tfa_vval_attr_grp = {
.attrs = tfa_vval_attr,
};
/* ---------------------------------------------------------------------- */
static struct device *tfa_vval_dev;
static int cur_status;
static int vval_data[MAX_HANDLES] = {
VVAL_UNTESTED, VVAL_UNTESTED, VVAL_UNTESTED, VVAL_UNTESTED
};
/* ---------------------------------------------------------------------- */
static char *get_vval_string(int vval_index)
{
switch (vval_index) {
case VVAL_UNTESTED:
return "0";
case VVAL_PASS:
return "1";
case VVAL_FAIL:
return "-1";
case VVAL_TEST_FAIL:
return "0";
default:
return "0";
}
}
static ssize_t update_vval_status(int idx, char *buf)
{
struct tfa_device *tfa = NULL;
int ret = 0;
uint16_t value;
int size;
char vval_result[FILESIZE_VVAL] = {0};
tfa = tfa98xx_get_tfa_device_from_index(0);
if (tfa == NULL)
return -EINVAL; /* unused device */
if (idx < 0 || idx >= tfa->dev_count)
return -EINVAL;
if (vval_data[idx] != VVAL_PASS
&& vval_data[idx] != VVAL_FAIL) {
ret = tfa_get_vval_data(idx, &value);
if (ret == TFA98XX_ERROR_NOT_OPEN)
value = VVAL_UNTESTED; /* unused device */
if (ret) {
pr_info("%s: tfa_vval failed to read data from amplifier\n",
__func__);
value = VVAL_TEST_FAIL;
}
if (value == 0xffff) {
pr_info("%s: tfa_vval read wrong data from amplifier\n",
__func__);
value = VVAL_UNTESTED;
}
vval_data[idx] = value;
}
snprintf(vval_result, FILESIZE_VVAL,
"%s", get_vval_string(vval_data[idx]));
if (vval_result[0] == 0)
size = snprintf(buf, 1 + 1, "0"); /* not tested */
else
size = snprintf(buf, strlen(vval_result) + 1,
"%s", vval_result);
if (size <= 0) {
pr_err("%s: tfa_vval failed to show in sysfs file\n", __func__);
return -EINVAL;
}
return size;
}
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int idx = tfa_get_dev_idx_from_inchannel(0);
int ret;
ret = update_vval_status(idx, buf);
if (ret > 0)
pr_info("%s: tfa_vval - dev %d - V validation data (%d)\n",
__func__, idx, vval_data[idx]);
else
pr_err("%s: tfa_vval dev %d - error %d\n",
__func__, idx, ret);
return ret;
}
static ssize_t status_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
pr_info("%s: dev %d - not allowed to write V validation result\n",
__func__, tfa_get_dev_idx_from_inchannel(0));
return size;
}
#if defined(TFA_STEREO_NODE)
static ssize_t status_r_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int idx = tfa_get_dev_idx_from_inchannel(1);
int ret;
ret = update_vval_status(idx, buf);
if (ret > 0)
pr_info("%s: tfa_vval - dev %d - V validation data (%d)\n",
__func__, idx, vval_data[idx]);
else
pr_err("%s: tfa_vval dev %d - error %d\n",
__func__, idx, ret);
return ret;
}
static ssize_t status_r_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
pr_info("%s: dev %d - not allowed to write V validation result\n",
__func__, tfa_get_dev_idx_from_inchannel(1));
return size;
}
#endif /* TFA_STEREO_NODE */
static ssize_t validation_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int size;
char vval_state[FILESIZE_VVAL] = {0};
snprintf(vval_state, FILESIZE_VVAL,
"%s", cur_status ?
"enabled" /* V validation is active */
: "disabled"); /* V validation is inactive */
size = snprintf(buf, strlen(vval_state) + 1,
"%s", vval_state);
return size;
}
static ssize_t validation_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct tfa_device *tfa = NULL;
int idx, ndev = MAX_HANDLES;
uint16_t value;
int ret = 0, status;
/* Compare string, excluding the trailing \0 and the potentials eol */
if (!sysfs_streq(buf, "1") && !sysfs_streq(buf, "0")) {
pr_info("%s: tfa_vval invalid value to start V validation\n",
__func__);
return -EINVAL;
}
ret = kstrtou32(buf, 10, &status);
if (!status) {
pr_info("%s: do nothing\n", __func__);
return -EINVAL;
}
if (cur_status)
pr_info("%s: tfa_vval prior V validation still runs\n",
__func__);
pr_info("%s: tfa_vval begin\n", __func__);
cur_status = status; /* run - changed to active */
memset(vval_data, 0, sizeof(int) * MAX_HANDLES);
/* run V valibration */
ret = tfa_run_vval(0, &value);
if (ret == TFA98XX_ERROR_NOT_OPEN)
return -EINVAL; /* unused device */
if (ret) {
pr_info("%s: tfa_vval failed to run V validation\n", __func__);
return -EINVAL;
}
cur_status = 0; /* done - changed to inactive */
tfa = tfa98xx_get_tfa_device_from_index(0);
if (tfa == NULL)
return -EINVAL; /* unused device */
ndev = tfa->dev_count;
if (ndev < 1)
return -EINVAL;
for (idx = 0; idx < ndev; idx++) {
/* read data to store */
ret = tfa_get_vval_data(idx, &value);
if (ret) {
pr_info("%s: tfa_vval failed to read data after V validation\n",
__func__);
value = VVAL_TEST_FAIL;
}
if (value == 0xffff) {
pr_info("%s: tfa_vval invalid data\n", __func__);
value = VVAL_UNTESTED;
}
vval_data[idx] = value;
pr_info("%s: tfa_vval - dev %d - V validation result (%d)\n",
__func__, idx, vval_data[idx]);
}
pr_info("%s: tfa_vval end\n", __func__);
return size;
}
int tfa98xx_vval_init(struct class *tfa_class)
{
int ret = 0;
if (tfa_class) {
tfa_vval_dev = device_create(tfa_class,
NULL, DEV_ID_TFA_VVAL, NULL, TFA_VVAL_DEV_NAME);
if (!IS_ERR(tfa_vval_dev)) {
ret = sysfs_create_group(&tfa_vval_dev->kobj,
&tfa_vval_attr_grp);
if (ret)
pr_err("%s: failed to create sysfs group. ret (%d)\n",
__func__, ret);
}
}
pr_info("%s: initialized (%d)\n", __func__,
(tfa_class != NULL) ? 1 : 0);
return ret;
}
void tfa98xx_vval_exit(struct class *tfa_class)
{
device_destroy(tfa_class, DEV_ID_TFA_VVAL);
pr_info("exited\n");
}