mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-09 21:59:12 -04:00
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:
parent
cb0f2ebc47
commit
2873bd469d
4 changed files with 124 additions and 142 deletions
|
|
@ -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, ®Val);
|
||||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue