bos0614_mmi: waveform shape feature

Haptics waveform has shape up and shape down settings that were not
availbale through sysfs set_waveform interface. This change adds an
extra parameter allowing for waveform shape to be specified.

Includes some code cleanup as well

Change-Id: I1901b69520515d79f1f6ccf4d701fc373aab74e9
Signed-off-by: Konstantin Makariev <hcv867@motorola.com>
Reviewed-on: https://gerrit.mot.com/1952944
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Ling Jin <lingjin@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
Konstantin Makariev 2021-05-18 15:14:49 -05:00 • committed by Konstantin Makariev
commit 2873bd469d
4 changed files with 124 additions and 142 deletions

View file

@ -82,6 +82,7 @@ typedef struct
#define SET_WAVEFORM_PARAM_LENGTH (5)
#define SET_SLICE_PARAM_LENGTH (5)
#define SET_SHAPED_SLICE_PARAM_LENGTH (6)
#define SYNTH_PLAY_PARAM_LENGTH (2)
#define CTRL_OUTPUT_PARAM_LENGTH (1)
#define SENSING_CONFIG_PARAM_LENGTH (6)
@ -209,6 +210,29 @@ static ssize_t getIcErrors(struct device *dev,
static DEVICE_ATTR(ic_errors, 0440, getIcErrors, NULL);
static ssize_t getIcSensingState(struct device *dev,
struct device_attribute *attr, char *buf)
{
Context *ctx = dev_get_drvdata(dev);
ssize_t res = -EIO;
if (ctx != NULL && ctx->hapticDriver != NULL)
{
HapticDriver *driver = ctx->hapticDriver;
uint16_t regVal = 0;
mutex_lock(&ctx->lock);
driver->getRegister(driver, ADDRESS_BOS0614_SENSECONFIG_REG, &regVal);
mutex_unlock(&ctx->lock);
res = snprintf(buf, PAGE_SIZE, "%d\n", (regVal & 0x000F) ? 1 : 0);
}
return res;
}
static DEVICE_ATTR(is_sensing, 0444, getIcSensingState, NULL);
static ssize_t setSynthWaveform(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@ -273,27 +297,30 @@ static ssize_t setSynthSlice(struct device *dev,
SynthSlice slice;
uint8_t outputChannel = 0;
size_t paramLength = 0;
int numP = 0;
ParamsLst params[SET_SLICE_PARAM_LENGTH];
uint8_t shape = 0;
int numP = 0;
ParamsLst params[SET_SHAPED_SLICE_PARAM_LENGTH];
PARAM_ADD(PARAM_UINT32, &slice.sliceId);
PARAM_ADD(PARAM_INT32, &slice.mVAmp);
PARAM_ADD(PARAM_UINT32, &slice.mHzFreq);
PARAM_ADD(PARAM_UINT32, &slice.cycle);
PARAM_ADD(PARAM_UCHAR8, &outputChannel);
PARAM_ADD(PARAM_UINT32, &slice.sliceId);
PARAM_ADD(PARAM_INT32, &slice.mVAmp);
PARAM_ADD(PARAM_UINT32, &slice.mHzFreq);
PARAM_ADD(PARAM_UINT32, &slice.cycle);
PARAM_ADD(PARAM_UCHAR8, &outputChannel);
PARAM_ADD(PARAM_UCHAR8, &shape);
paramLength = process_params(params, numP, buf);
if (paramLength != SET_SLICE_PARAM_LENGTH) {
return (ssize_t)paramLength;
}
paramLength = process_params(params, numP, buf);
if (paramLength != SET_SLICE_PARAM_LENGTH &&
paramLength != SET_SHAPED_SLICE_PARAM_LENGTH) {
return (ssize_t)paramLength;
}
dev_dbg(ctx->dev,
"[Set Slice] Slice Id: %d Amplitude: %d mV Frequency: %d milliHertz Cycle: %d Output Channel: %d\n",
slice.sliceId, slice.mVAmp, slice.mHzFreq, slice.cycle, outputChannel);
"[Set Slice] Slice Id: %d Amplitude: %d mV Frequency: %d milliHertz Cycle: %d Shape: %d Output Channel: %d\n",
slice.sliceId, slice.mVAmp, slice.mHzFreq, slice.cycle, shape, outputChannel);
mutex_lock(&ctx->lock);
if (driver->synthSetSlice(driver, (const SynthSlice *) &slice, outputChannel))
if (driver->synthSetSlice(driver, (const SynthSlice *) &slice, shape, outputChannel))
{
res = count;
}
@ -753,7 +780,8 @@ static bool applyingFeedbackButtonConf(Context* ctx, HwButtonFeedback* feedbacks
//Slice configuration
for (sliceId = 0; sliceId < feedbacks->nbrSlice; sliceId++)
{
res = res && driver->synthSetSlice(driver, &feedbacks->slice[sliceId], IGNORE_OUPUT_CHANNEL);
res = res && driver->synthSetSlice(driver, &feedbacks->slice[sliceId], SHAPE_NO_SHAPE, IGNORE_OUPUT_CHANNEL);
pr_debug("set_slice: %d\n", sliceId);
}
res = res && driver->synthSetWaveform(driver, waveformId, feedbacks->slice->sliceId, feedbacks->nbrSlice, 1,
@ -774,6 +802,7 @@ static bool applyButtonSensingConfiguration(Context* ctx, uint buttonId, Sensing
HapticDriver* driver = ctx->hapticDriver;
pr_debug("enter\n");
res = res && driver->buttonSensing(driver, buttonId, *conf);
res = res && driver->sensingAutoPlayWave(driver, buttonId, waveformId, conf->direction);
@ -790,9 +819,9 @@ static bool applyDefaultConfiguration(Context* ctx)
uint buttonId;
bool res = applyingFeedbackButtonConf(ctx, &ctx->defaultButtonConfig.feedback[SENSING_DIRECTION_PRESS],
DEFAULT_PRESS_WAVEFORM_ID);
pr_debug("enter\n");
res = res && applyingFeedbackButtonConf(ctx, &ctx->defaultButtonConfig.feedback[SENSING_DIRECTION_RELEASE],
DEFAULT_RELEASE_WAVEFORM_ID);
for (buttonId = 0; buttonId < ARRAY_SIZE(ctx->defaultButtonConfig.channel) && res; buttonId++)
{
if (ctx->defaultButtonConfig.channel[buttonId].present)
@ -826,6 +855,7 @@ static struct attribute *bosDriverAttrs[] = {
&dev_attr_sensing_config.attr,
&dev_attr_sensing_autoplay.attr,
&dev_attr_sensing_stop.attr,
&dev_attr_is_sensing.attr,
NULL,
};
@ -916,7 +946,8 @@ static int bosDriverI2cProbe(struct i2c_client *client,
dev_err(&client->dev, "Failed to decode DT configuration\n");
goto bosDriverProbeError;
}
}
} else
pr_info("cannot find matching device tree\n");
mutex_init(&ctx->lock);

View file

@ -55,11 +55,13 @@ int process_params(ParamsLst *params, int numP, const char *buffer)
{
char *arg, *buf, *p;
int n, err;
unsigned int valueUI = 0;
int valueI = 0;
unsigned int valueUI;
int valueI;
p = buf = kstrdup(buffer, GFP_KERNEL);
for (n = 0; n < numP && p && *p; n++, params++) {
valueI = INT16_MIN;
valueUI = UINT16_MAX;
arg = strsep(&p, " ");
if (!arg || !*arg)
break;
@ -92,89 +94,18 @@ int process_params(ParamsLst *params, int numP, const char *buffer)
if (err) {
n = err;
break;
} else if (valueUI != 0) {
pr_debug("[%d]=%u\n", n, valueUI);
} else if (valueI != 0)
pr_debug("[%d]=%d\n", n, valueI);
}
valueI = 0;
valueUI = 0;
if (valueUI != UINT16_MAX)
pr_debug("[%d]=%u\n", n, valueUI);
else if (valueI != INT16_MIN)
pr_debug("[%d]=%d\n", n, valueI);
}
kfree(buf);
pr_debug("processed %d input parameters\n", n);
return n;
}
#if 0
static int readRegsConfig(Context *ctx, struct device_node *parent, const char *suffix)
{
Bos0614RegisterStruct *config = getAllRegsPtr();
struct device_node *node;
char node_name[64];
u32 *temp, length = 0;
int npairs, i, index, ret = -EIO;
scnprintf(node_name, 63, "config-%s", suffix);
node = of_find_node_by_name(parent, node_name);
if (!node)
return -ENODEV;
if (!of_find_property(node, "config-data", &length)) {
dev_err(&ctx->client->dev, "(config-%s) prop config-data not found\n", suffix);
goto out;
}
npairs = length / 2;
dev_info(&ctx->client->dev, "(config-%s) array size %d\n", suffix, npairs);
temp = kzalloc(length, GFP_KERNEL);
if (!temp)
goto out;
ret = of_property_read_u32_array(node, "config-data", temp, sizeof(u32) * npairs * 2);
if (ret) {
dev_err(&ctx->client->dev, "error reading config-data (config-%s)\n", suffix);
goto release_mem;
}
for (i = 0; i < npairs; i++) {
index =(uint16_t)*temp++;
config[index].value = (uint16_t)*temp++;
dev_info(&ctx->client->dev, "[%d] addr=0x%02x, val=0x%04x\n",
i, config[index].addr, config[index].value);
}
release_mem:
kfree(temp);
out:
of_node_put(node);
return ret;
}
static int readDevTree(Context *ctx)
{
int ret, verno;
struct device_node *np = ctx->client->dev.of_node;
struct device_node *config_np;
config_np = of_find_node_by_name(np, "configs");
if (!config_np) {
dev_info(&ctx->client->dev, "does not support configs\n");
return 0;
}
if (!of_property_read_u32(config_np, "config-ver", &verno))
dev_info(&ctx->client->dev, "dt config Rev.%u\n", verno);
ret = readRegsConfig(ctx, config_np, "default");
if (ret > 0)
dev_info(&ctx->client->dev, "has default config\n");
of_node_put(config_np);
return 0;
}
#endif
static void logBuffer(struct i2c_client* client, const char* message,
uint8_t addr, const void* data, size_t length)
{

View file

@ -545,12 +545,41 @@ static bool burstWriteRam(Context *driver, WSFBank bank, uint16_t address, uint1
return res;
}
#if 0
static void hexDump(char* message, uint8_t *data, size_t length)
{
size_t bufferLength = length * 5 + 5;
char *buf = kzalloc(bufferLength, GFP_KERNEL);
if (buf != NULL)
{
size_t index;
char *ptr = buf;
char *end = buf + bufferLength;
uint8_t *_data = (uint8_t *) data;
for (index = 0; index < length && ptr < end; index++)
{
ptr += sprintf(ptr, index < (length - 1) ? "0x%02x " : "0x%02x", _data[index]);
}
pr_debug("%s [%s]\n", message, buf);
kfree(buf);
}
}
#endif
static bool writeRam(Context *driver, WSFBank bank, uint16_t *data, size_t length)
{
int i;
bool res = false;
#if 0
char buffer[64];
snprintf(buffer, sizeof(buffer) - 1, "Bank: %d, length=%zu", bank, length);
hexDump(buffer, (uint8_t *)data, length * 2);
#endif
memset(driver->txBuffer, 0, sizeof(driver->txBuffer));
driver->txBuffer[I2C_ADDRESS_INDEX] = ADDRESS_BOS0614_REFERENCE_REG; // [0] Main register map address
htoBe16(bank, &driver->txBuffer[WRITE_RAM_BANK_INDEX]); // [1-2] WFS bank address
@ -586,12 +615,13 @@ static bool writeRam(Context *driver, WSFBank bank, uint16_t *data, size_t lengt
#define VOLT_TO_MILLI_VOLT (1000)
static bool setSlice(Context *driver, SynthSlice const *slice,
uint8_t outputChannel)
SliceShape shape, uint8_t outputChannel)
{
uint16_t buffer[SLICE_LENGTH + RAM_ADDRESS_LENGTH];
uint32_t amplitude_computed =
(slice->mVAmp * AMPLITUDE_MAX_VALUE) / (AMPLITUDE_MAX_VALUE_VOLT * VOLT_TO_MILLI_VOLT);
uint8_t channel_computed = outputChannel;
uint16_t setShape;
uint32_t freq_computed_den = FREQUENCY_RESOLUTION_HZ * Hz_In_MilliHz;
uint16_t freq = (uint16_t) (slice->mHzFreq / freq_computed_den);
@ -601,7 +631,26 @@ static bool setSlice(Context *driver, SynthSlice const *slice,
buffer[SLICE_AMPLITUDE_INDEX] = (uint16_t) ((channel_computed << 12) | amplitude_computed);
buffer[SLICE_FREQ_CYCLE_INDEX] = (uint16_t) freq;
buffer[SLICE_FREQ_CYCLE_INDEX] |= (0xFF & slice->cycle) << SLICE_CYCLE_SHIFT;
buffer[SLICE_SHAPE_INDEX] = SLICE_NO_SHAPE;
switch(shape) {
case SHAPE_SAW:
setShape = 0x0020; /* 64ms shapeUp only */
break;
case SHAPE_TRIANGLE:
setShape = 0x0022; /* 64ms both ways */
break;
case SHAPE_SLOW_RISE:
setShape = 0x0042; /* 128ms shapeUp and 64ms shapeDn */
break;
case SHAPE_SLOW_DROP:
setShape = 0x0024; /* 64ms shapeUp and 128ms shapeDn */
break;
default:
setShape = SLICE_NO_SHAPE;
break;
}
buffer[SLICE_SHAPE_INDEX] = setShape;
pr_debug("Shape = 0x%04x\n", setShape);
return writeRam(driver, WSFBank_Ram, buffer, sizeof(buffer));
}
@ -1311,7 +1360,7 @@ bool bos0614CtrlOutput(HapticDriver *driver, bool activate)
}
bool
bos0614SetSlice(HapticDriver *driver, const SynthSlice *slice, const uint8_t outputChannel)
bos0614SetSlice(HapticDriver *driver, const SynthSlice *slice, SliceShape shape, const uint8_t outputChannel)
{
bool res = false;
@ -1321,7 +1370,7 @@ bos0614SetSlice(HapticDriver *driver, const SynthSlice *slice, const uint8_t out
Context *ctx = container_of(driver, Context, hDriver);
res = setMode(ctx, Bos0614Mode_RAM_Synthesis);
res = res && setSlice(ctx, slice, outputChannel);
res = res && setSlice(ctx, slice, shape, outputChannel);
}
return res;
@ -2163,7 +2212,6 @@ static uint8_t regAddrToReadRevC[] = {
ADDRESS_BOS0614_REG46_REG,
ADDRESS_BOS0614_REG47_REG
};
#if 0
static BOS0614_REGS bOS0614Regs =
{
@ -2337,54 +2385,16 @@ static bool setDefaultConfig(Context *ctx)
{
memcpy(&ctx->reg, &bos0614RegsRevC, sizeof(bos0614RegsRevC));
}
#if 0
//No need to copy registers again here!!!
memcpy(&ctx->reg, &bOS0614Regs, sizeof(bOS0614Regs));
//Why do we need to do a dummy read???
res = readAllRegister(ctx);
//bosDriverI2cProbe calls i2cBoreasLinuxInit
// i2cBoreasLinuxInit calls readDeviceTree
// readDeviceTree populates regToRead with default configuration
//
//Read SENSECONFIG to determine whether power cut occurred
//and necessary to apply default config provided in device tree
res = readRegister(ctx, &ctx->reg.common.senseConfig0614.reg);
res = res && ctx->reg.common.senseConfig0614.reg.generic.value == regToRead[ADDRESS_BOS0614_SENSECONFIG_REG].value;
res = readAllRegister(ctx);
#endif
ctx->reg.common.senseConfig0614.bit.ch0 = BOS0614_DISABLE;
ctx->reg.common.senseConfig0614.bit.ch1 = BOS0614_DISABLE;
ctx->reg.common.senseConfig0614.bit.ch2 = BOS0614_DISABLE;
ctx->reg.common.senseConfig0614.bit.ch3 = BOS0614_DISABLE;
ctx->reg.common.senseConfig0614.bit.same = BOS0614_DISABLE;
#if 0
res = ctx->hDriver.setRegister(&ctx->hDriver,
ctx->reg.common.senseConfig0614.generic.addr,
ctx->reg.common.senseConfig0614.generic.value);
res = ctx->hDriver.setRegister(&ctx->hDriver,
ctx->reg.common.senseConfig0614.reg.generic.addr,
ctx->reg.common.senseConfig0614.reg.generic.value);
//This happens when SENSECONFIG register value from IC
//won't match config from device tree
if (res == false)
{
uint32_t index;
res = true;
for (index = 0; index < DATA_ARRAY_LENGTH(regToRead); index++)
{
if (regToRead[index].value != 0)
res = res && ctx->hDriver.setRegister(&ctx->hDriver,
regToRead[index].addr,
regToRead[index].value);
}
}
#endif
return res;
}

View file

@ -143,6 +143,15 @@ typedef enum
BOS_EVENT_NO_EVENT
} BOSEvent;
typedef enum
{
SHAPE_NO_SHAPE,
SHAPE_SAW,
SHAPE_TRIANGLE,
SHAPE_SLOW_RISE,
SHAPE_SLOW_DROP
} SliceShape;
typedef uint8_t ChannelId;
typedef struct _HapticDriver HapticDriver;
@ -268,7 +277,7 @@ typedef size_t (* BosGetNumberOfRegister)(void);
*
* @return True if the operation succeed, otherwise false
*/
typedef bool (* BosSynthesizerSetSlice)(HapticDriver* driver, const SynthSlice* slice, const uint8_t outputChannel);
typedef bool (* BosSynthesizerSetSlice)(HapticDriver* driver, const SynthSlice* slice, SliceShape shape, const uint8_t outputChannel);
/**
* @brief Set the synthesizer mode
@ -278,6 +287,7 @@ typedef bool (* BosSynthesizerSetSlice)(HapticDriver* driver, const SynthSlice*
* @param[in] frequency The frequency of the specified waveform
* @param[in] amplitude The amplitude of the specified waveform
* @param[in] cycle The amount of waveform period to play
* @param[in] shape Shape In/Out arrangement
* @param[in] outputChannel The output channel to play the waveform
*
* @return True if the operation succeed, otherwise false