summaryrefslogtreecommitdiffstats
path: root/firmware/target/arm/imx233/creative-zenxfi3
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2016-05-30 16:24:38 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2016-06-01 23:09:27 +0200
commitd42a4a4eb4bc20fda41543b8b831c1970723346f (patch)
treef6ba16e95c61e441c1f110131b17a710c785285c /firmware/target/arm/imx233/creative-zenxfi3
parentb2afd931e2d83ce346811a68a34ee56c48be6d35 (diff)
downloadrockbox-d42a4a4eb4bc20fda41543b8b831c1970723346f.tar.gz
rockbox-d42a4a4eb4bc20fda41543b8b831c1970723346f.tar.bz2
rockbox-d42a4a4eb4bc20fda41543b8b831c1970723346f.zip
zenxfi3: rewrite mpr121 driver
The new driver uses an asynchronous architecture for touch status reading. Change-Id: Ic75a8b91bc47ee16c3af873afde178cd70186376
Diffstat (limited to 'firmware/target/arm/imx233/creative-zenxfi3')
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/backlight-zenxfi3.c2
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/button-zenxfi3.c95
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.c236
-rw-r--r--firmware/target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.h176
4 files changed, 433 insertions, 76 deletions
diff --git a/firmware/target/arm/imx233/creative-zenxfi3/backlight-zenxfi3.c b/firmware/target/arm/imx233/creative-zenxfi3/backlight-zenxfi3.c
index 2b77a4d7ac..e3baddea1e 100644
--- a/firmware/target/arm/imx233/creative-zenxfi3/backlight-zenxfi3.c
+++ b/firmware/target/arm/imx233/creative-zenxfi3/backlight-zenxfi3.c
@@ -25,7 +25,7 @@
#include "backlight.h"
#include "backlight-target.h"
#include "pwm-imx233.h"
-#include "mpr121.h"
+#include "mpr121-zenxfi3.h"
void backlight_hw_brightness(int brightness)
{
diff --git a/firmware/target/arm/imx233/creative-zenxfi3/button-zenxfi3.c b/firmware/target/arm/imx233/creative-zenxfi3/button-zenxfi3.c
index 756231a042..ed8e769e2d 100644
--- a/firmware/target/arm/imx233/creative-zenxfi3/button-zenxfi3.c
+++ b/firmware/target/arm/imx233/creative-zenxfi3/button-zenxfi3.c
@@ -18,16 +18,10 @@
* KIND, either express or implied.
*
****************************************************************************/
-#include "button-target.h"
#include "system.h"
-#include "system-target.h"
-#include "pinctrl-imx233.h"
-#include "power-imx233.h"
+#include "tick.h"
#include "button-imx233.h"
-#include "string.h"
-#include "usb.h"
-#include "backlight.h"
-#include "mpr121.h"
+#include "mpr121-zenxfi3.h"
#define I_VDDIO 0 /* index in the table */
@@ -42,7 +36,8 @@ struct imx233_button_map_t imx233_button_map[] =
IMX233_BUTTON_(END, END(), "")
};
-static struct mpr121_config_t config =
+/* MPR121 configuration, mostly extracted from OF */
+static struct mpr121_config_t mpr121_config =
{
.ele =
{
@@ -73,74 +68,11 @@ static struct mpr121_config_t config =
.cal_lock = CL_TRACK
};
-#define MPR121_INTERRUPT 1
-
-static int touchpad_btns = 0;
-static long mpr121_stack[DEFAULT_STACK_SIZE/sizeof(long)];
-static const char mpr121_thread_name[] = "mpr121";
-static struct event_queue mpr121_queue;
-
-static void mpr121_irq_cb(int bank, int pin, intptr_t user)
-{
- (void) bank;
- (void) pin;
- (void) user;
- /* the callback will not be fired until interrupt is enabled back so
- * the queue will not overflow or contain multiple MPR121_INTERRUPT events */
- queue_post(&mpr121_queue, MPR121_INTERRUPT, 0);
-}
-
-static void mpr121_thread(void)
-{
- struct queue_event ev;
-
- while(1)
- {
- queue_wait(&mpr121_queue, &ev);
- /* handle usb connect and ignore all messages except rmi interrupts */
- if(ev.id == SYS_USB_CONNECTED)
- {
- usb_acknowledge(SYS_USB_CONNECTED_ACK);
- continue;
- }
- else if(ev.id != MPR121_INTERRUPT)
- continue;
- /* clear interrupt and get status */
- unsigned status;
- touchpad_btns = 0;
- if(!mpr121_get_touch_status(&status))
- {
- /* ELE3: up
- * ELE4: back
- * ELE5: menu
- * ELE6: down
- * ELE7: play */
- if(status & 0x8) touchpad_btns |= BUTTON_UP;
- if(status & 0x10) touchpad_btns |= BUTTON_BACK;
- if(status & 0x20) touchpad_btns |= BUTTON_MENU;
- if(status & 0x40) touchpad_btns |= BUTTON_DOWN;
- if(status & 0x80) touchpad_btns |= BUTTON_PLAY;
- }
- /* enable interrupt */
- imx233_pinctrl_setup_irq(0, 18, true, true, false, &mpr121_irq_cb, 0);
- }
-}
-
/* B0P18 is #IRQ line of the touchpad */
void button_init_device(void)
{
- mpr121_init(0xb4);
- mpr121_soft_reset();
- mpr121_set_config(&config);
-
- queue_init(&mpr121_queue, true);
- create_thread(mpr121_thread, mpr121_stack, sizeof(mpr121_stack), 0,
- mpr121_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU));
- /* enable interrupt */
- imx233_pinctrl_acquire(0, 18, "mpr121_int");
- imx233_pinctrl_set_function(0, 18, PINCTRL_FUNCTION_GPIO);
- imx233_pinctrl_enable_gpio(0, 18, false);
- imx233_pinctrl_setup_irq(0, 18, true, true, false, &mpr121_irq_cb, 0);
+ mpr121_init();
+ mpr121_set_config(&mpr121_config);
/* generic part */
imx233_button_init();
}
@@ -151,7 +83,6 @@ int button_read_device(void)
* for one second after hold is released */
static int power_ignore_counter = 0;
static bool old_hold;
- /* light handling */
bool hold = button_hold();
if(hold != old_hold)
{
@@ -159,6 +90,20 @@ int button_read_device(void)
if(!hold)
power_ignore_counter = HZ;
}
+ /* interpret touchpad status */
+ unsigned status = mpr121_get_touch_status();
+ unsigned touchpad_btns = 0;
+ /* ELE3: up
+ * ELE4: back
+ * ELE5: menu
+ * ELE6: down
+ * ELE7: play */
+ if(status & 0x8) touchpad_btns |= BUTTON_UP;
+ if(status & 0x10) touchpad_btns |= BUTTON_BACK;
+ if(status & 0x20) touchpad_btns |= BUTTON_MENU;
+ if(status & 0x40) touchpad_btns |= BUTTON_DOWN;
+ if(status & 0x80) touchpad_btns |= BUTTON_PLAY;
+ /* feed it to generic code */
int res = imx233_button_read(touchpad_btns);
if(power_ignore_counter > 0)
{
diff --git a/firmware/target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.c b/firmware/target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.c
new file mode 100644
index 0000000000..23fcc7f0e4
--- /dev/null
+++ b/firmware/target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.c
@@ -0,0 +1,236 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2012 Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/** Driver for the Freescale MPR121 Capacitive Proximity Sensor */
+#include "system.h"
+#include "kernel.h"
+#include "usb.h"
+#include "mpr121.h"
+#include "mpr121-zenxfi3.h"
+#include "i2c-imx233.h"
+#include "pinctrl-imx233.h"
+
+#define MPR121_I2C_ADDR 0xb4
+
+/* NOTE on the architecture of the driver
+ *
+ * All non-time-critical operations (setup, gpio/pwm changes) are done with
+ * blocking i2c transfers to make the code simpler. Since reading the touch
+ * status is time critical, it is done asynchronously: when the IRQ pin is
+ * asserted, it will disable IRQ pin sensing and trigger an asynchronous i2c
+ * transfer to read touch status. When the transfer finishes, the driver will
+ * renable IRQ pin sensing. */
+
+static unsigned touch_status = 0; /* touch bitmask as reported by mpr121 */
+static struct imx233_i2c_xfer_t read_status_xfer; /* async transfer to read touch status */
+static uint8_t read_status_sel_reg; /* buffer for async transfer operation */
+static uint8_t read_status_buf[2]; /* buffer for async transfer operation */
+
+static void mpr121_irq_cb(int bank, int pin, intptr_t user);
+
+static void touch_status_i2c_cb(struct imx233_i2c_xfer_t *xfer, enum imx233_i2c_error_t status)
+{
+ (void) xfer;
+ (void) status;
+ /* put status in the global variable */
+ touch_status = read_status_buf[0] | read_status_buf[1] << 8;
+ /* start sensing IRQ pin again */
+ imx233_pinctrl_setup_irq(0, 18, true, true, false, &mpr121_irq_cb, 0);
+}
+
+void mpr121_irq_cb(int bank, int pin, intptr_t user)
+{
+ (void) bank;
+ (void) pin;
+ (void) user;
+ /* NOTE the callback will not be fired until interrupt is enabled back.
+ *
+ * now setup an asynchronous i2c transfer to read touch status register,
+ * this is a readmem operation with a first stage to select register
+ * and a second stage to read status (2 bytes) */
+ read_status_sel_reg = REG_TOUCH_STATUS;
+
+ read_status_xfer.next = NULL;
+ read_status_xfer.fast_mode = true;
+ read_status_xfer.dev_addr = MPR121_I2C_ADDR;
+ read_status_xfer.mode = I2C_READ;
+ read_status_xfer.count[0] = 1; /* set touch status register address */
+ read_status_xfer.data[0] = &read_status_sel_reg;
+ read_status_xfer.count[1] = 2;
+ read_status_xfer.data[1] = &read_status_buf;
+ read_status_xfer.tmo_ms = 1000;
+ read_status_xfer.callback = &touch_status_i2c_cb;
+
+ imx233_i2c_transfer(&read_status_xfer);
+}
+
+static inline int mpr121_write_reg(uint8_t reg, uint8_t data)
+{
+ return i2c_writemem(MPR121_I2C_ADDR, reg, &data, 1);
+}
+
+static inline int mpr121_read_reg(uint8_t reg, uint8_t *data)
+{
+ return i2c_readmem(MPR121_I2C_ADDR, reg, data, 1);
+}
+
+void mpr121_init(void)
+{
+ /* soft reset */
+ mpr121_write_reg(REG_SOFTRESET, REG_SOFTRESET__MAGIC);
+ /* enable interrupt */
+ imx233_pinctrl_acquire(0, 18, "mpr121_int");
+ imx233_pinctrl_set_function(0, 18, PINCTRL_FUNCTION_GPIO);
+ imx233_pinctrl_enable_gpio(0, 18, false);
+ imx233_pinctrl_setup_irq(0, 18, true, true, false, &mpr121_irq_cb, 0);
+}
+
+void mpr121_set_config(struct mpr121_config_t *conf)
+{
+ /* stop mode */
+ mpr121_write_reg(REG_ELECTRODE, 0);
+ /* write baseline values */
+ for(int i = 0; i < ELECTRODE_COUNT; i++)
+ mpr121_write_reg(REG_ExBV(i), conf->ele[i].bv);
+ /* write eleprox bv */
+ mpr121_write_reg(REG_EPROXBV, conf->eleprox.bv);
+ /* write global fields */
+ mpr121_write_reg(REG_MHDR, conf->filters.ele.rising.mhd);
+ mpr121_write_reg(REG_NHDR, conf->filters.ele.rising.nhd);
+ mpr121_write_reg(REG_NCLR, conf->filters.ele.rising.ncl);
+ mpr121_write_reg(REG_FDLR, conf->filters.ele.rising.fdl);
+ mpr121_write_reg(REG_MHDF, conf->filters.ele.falling.mhd);
+ mpr121_write_reg(REG_NHDF, conf->filters.ele.falling.nhd);
+ mpr121_write_reg(REG_NCLF, conf->filters.ele.falling.ncl);
+ mpr121_write_reg(REG_FDLF, conf->filters.ele.falling.fdl);
+ mpr121_write_reg(REG_NHDT, conf->filters.ele.touched.nhd);
+ mpr121_write_reg(REG_NCLT, conf->filters.ele.touched.ncl);
+ mpr121_write_reg(REG_FDLT, conf->filters.ele.touched.fdl);
+ mpr121_write_reg(REG_MHDPROXR, conf->filters.eleprox.rising.mhd);
+ mpr121_write_reg(REG_NHDPROXR, conf->filters.eleprox.rising.nhd);
+ mpr121_write_reg(REG_NCLPROXR, conf->filters.eleprox.rising.ncl);
+ mpr121_write_reg(REG_FDLPROXR, conf->filters.eleprox.rising.fdl);
+ mpr121_write_reg(REG_MHDPROXF, conf->filters.eleprox.falling.mhd);
+ mpr121_write_reg(REG_NHDPROXF, conf->filters.eleprox.falling.nhd);
+ mpr121_write_reg(REG_NCLPROXF, conf->filters.eleprox.falling.ncl);
+ mpr121_write_reg(REG_FDLPROXF, conf->filters.eleprox.falling.fdl);
+ mpr121_write_reg(REG_NHDPROXT, conf->filters.eleprox.touched.nhd);
+ mpr121_write_reg(REG_NCLPROXT, conf->filters.eleprox.touched.ncl);
+ mpr121_write_reg(REG_FDLPROXT, conf->filters.eleprox.touched.fdl);
+ /* touch & release thresholds */
+ for(int i = 0; i < ELECTRODE_COUNT; i++)
+ {
+ mpr121_write_reg(REG_ExTTH(i), conf->ele[i].tth);
+ mpr121_write_reg(REG_ExRTH(i), conf->ele[i].rth);
+ }
+ mpr121_write_reg(REG_EPROXTTH, conf->eleprox.tth);
+ mpr121_write_reg(REG_EPROXRTH, conf->eleprox.rth);
+ /* debounce */
+ mpr121_write_reg(REG_DEBOUNCE, REG_DEBOUNCE__DR(conf->debounce.dr) |
+ REG_DEBOUNCE__DT(conf->debounce.dt));
+ /* analog-front end and filters */
+ mpr121_write_reg(REG_AFE, REG_AFE__CDC(conf->global.cdc) |
+ REG_AFE__FFI(conf->global.ffi));
+ mpr121_write_reg(REG_FILTER, REG_FILTER__CDT(conf->global.cdt) |
+ REG_FILTER__ESI(conf->global.esi) | REG_FILTER__SFI(conf->global.sfi));
+ /* electrode charge */
+ for(int i = 0; i < ELECTRODE_COUNT; i++)
+ mpr121_write_reg(REG_CDCx(i), conf->ele[i].cdc);
+ mpr121_write_reg(REG_CDCPROX, conf->eleprox.cdc);
+ for(int i = 0; i < ELECTRODE_COUNT; i += 2)
+ {
+ mpr121_write_reg(REG_CDTx(i), REG_CDTx__CDT0(conf->ele[i].cdt) |
+ REG_CDTx__CDT1(conf->ele[i+1].cdt));
+ }
+ mpr121_write_reg(REG_CDTPROX, conf->eleprox.cdt);
+ /* Auto-Configuration */
+ mpr121_write_reg(REG_AUTO_CONF, REG_AUTO_CONF__ACE(conf->autoconf.en) |
+ REG_AUTO_CONF__ARE(conf->autoconf.ren) |
+ REG_AUTO_CONF__BVA(conf->cal_lock) |
+ REG_AUTO_CONF__RETRY(conf->autoconf.retry) |
+ REG_AUTO_CONF__FFI(conf->global.ffi));
+ mpr121_write_reg(REG_AUTO_CONF2, REG_AUTO_CONF2__ACFIE(conf->autoconf.acfie) |
+ REG_AUTO_CONF2__ARFIE(conf->autoconf.arfie) |
+ REG_AUTO_CONF2__OORIE(conf->autoconf.oorie) |
+ REG_AUTO_CONF2__SCTS(conf->autoconf.scts));
+ mpr121_write_reg(REG_USL, conf->autoconf.usl);
+ mpr121_write_reg(REG_LSL, conf->autoconf.lsl);
+ mpr121_write_reg(REG_TL, conf->autoconf.tl);
+ /* electrode configuration */
+ mpr121_write_reg(REG_ELECTRODE, REG_ELECTRODE__ELE_EN(conf->ele_en) |
+ REG_ELECTRODE__ELEPROX_EN(conf->eleprox_en) |
+ REG_ELECTRODE__CL(conf->cal_lock));
+ /* gpio config */
+ uint8_t ctl = 0;
+ for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
+ if(ELE_GPIO_CTL0(conf->ele[i].gpio))
+ ctl |= REG_GPIO_CTL0__CTL0x(i);
+ mpr121_write_reg(REG_GPIO_CTL0, ctl);
+ ctl = 0;
+ for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
+ if(ELE_GPIO_CTL1(conf->ele[i].gpio))
+ ctl |= REG_GPIO_CTL1__CTL1x(i);
+ mpr121_write_reg(REG_GPIO_CTL1, ctl);
+ ctl = 0;
+ for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
+ if(ELE_GPIO_DIR(conf->ele[i].gpio))
+ ctl |= REG_GPIO_DIR__DIRx(i);
+ mpr121_write_reg(REG_GPIO_DIR, ctl);
+ ctl = 0;
+ for(int i = ELE_GPIO_FIRST; i <= ELE_GPIO_LAST; i++)
+ if(ELE_GPIO_EN(conf->ele[i].gpio))
+ ctl |= REG_GPIO_EN__ENx(i);
+ mpr121_write_reg(REG_GPIO_EN, ctl);
+}
+
+void mpr121_set_gpio_output(int ele, int gpio_val)
+{
+ switch(gpio_val)
+ {
+ case ELE_GPIO_SET:
+ mpr121_write_reg(REG_GPIO_SET, REG_GPIO_SET__SETx(ele));
+ break;
+ case ELE_GPIO_CLR:
+ mpr121_write_reg(REG_GPIO_CLR, REG_GPIO_CLR__CLRx(ele));
+ break;
+ case ELE_GPIO_TOG:
+ mpr121_write_reg(REG_GPIO_TOG, REG_GPIO_TOG__TOGx(ele));
+ break;
+ default:
+ break;
+ }
+}
+
+void mpr121_set_gpio_pwm(int ele, int pwm)
+{
+ uint8_t reg_val;
+ mpr121_read_reg(REG_PWMx(ele), &reg_val);
+ if(REG_PWMx_IS_PWM0(ele))
+ reg_val = (reg_val & ~REG_PWMx__PWM0_BM) | REG_PWMx__PWM0(pwm);
+ else
+ reg_val = (reg_val & ~REG_PWMx__PWM1_BM) | REG_PWMx__PWM1(pwm);
+ mpr121_write_reg(REG_PWMx(ele), reg_val);
+}
+
+unsigned mpr121_get_touch_status(void)
+{
+ return touch_status;
+}
diff --git a/firmware/target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.h b/firmware/target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.h
new file mode 100644
index 0000000000..eb8b00eeee
--- /dev/null
+++ b/firmware/target/arm/imx233/creative-zenxfi3/mpr121-zenxfi3.h
@@ -0,0 +1,176 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2016 Amaury Pouly
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __MPR121_ZENXFI3_H__
+#define __MPR121_ZENXFI3_H__
+
+/** Driver for the Freescale MPR121 Capacitive Proximity Sensor */
+#include "system.h"
+
+#define ELECTRODE_COUNT 12
+#define ELE_GPIO_FIRST 4
+#define ELE_GPIO_LAST 11
+
+/* gpio config (encoding: [0]=en,[1]=dir,[2]=ctl[1],[3]=ctl[0]) */
+#define ELE_GPIO_DISABLE 0
+#define ELE_GPIO_INPUT 1
+#define ELE_GPIO_INPUT_PULLDOWN 9 /* input with pull-down */
+#define ELE_GPIO_INPUT_PULLUP 13 /* input with pull-up */
+#define ELE_GPIO_OUTPUT 3
+#define ELE_GPIO_OUTPUT_OPEN 11 /* open drain low-side */
+#define ELE_GPIO_OUTPUT_OPEN_LED 15 /* open drain high-side (led driver) */
+
+/* internal use */
+#define ELE_GPIO_EN(val) ((val) & 1)
+#define ELE_GPIO_DIR(val) (((val) >> 1) & 1)
+#define ELE_GPIO_CTL0(val) (((val) >> 3) & 1)
+#define ELE_GPIO_CTL1(val) (((val) >> 1) & 1)
+
+struct mpr121_electrode_config_t
+{
+ uint8_t bv; /* baseline value */
+ uint8_t tth; /* touch threshold */
+ uint8_t rth; /* release threshold */
+ uint8_t cdc; /* charge current (optional if auto-conf) */
+ uint8_t cdt; /* charge time (optional if auto-conf) */
+ int gpio; /* gpio config */
+};
+
+struct mpr121_baseline_filter_config_t
+{
+ uint8_t mhd; /* max half delta (except for touched) */
+ uint8_t nhd; /* noise half delta */
+ uint8_t ncl; /* noise count limit */
+ uint8_t fdl; /* filter delay count limit */
+};
+
+struct mpr121_baseline_filters_config_t
+{
+ struct mpr121_baseline_filter_config_t rising;
+ struct mpr121_baseline_filter_config_t falling;
+ struct mpr121_baseline_filter_config_t touched;
+};
+
+struct mpr121_debounce_config_t
+{
+ uint8_t dt; /* debounce count for touch */
+ uint8_t dr; /* debounce count for release */
+};
+
+/* first filter iterations */
+#define FFI_6_SAMPLES 0
+#define FFI_10_SAMPLES 1
+#define FFI_18_SAMPLES 2
+#define FFI_34_SAMPLES 3
+/* charge discharge current */
+#define CDC_DISABLE 0
+#define CDC_uA(ua) (ua)
+/* charge discharge time */
+#define CDT_DISABLE 0
+#define CDT_log_us(lus) (lus) /* actual value = 2^{us-2} ┬Ás */
+/* second filter iterations */
+#define SFI_4_SAMPLES 0
+#define SFI_6_SAMPLES 1
+#define SFI_10_SAMPLES 2
+#define SFI_18_SAMPLES 3
+/* Eletrode sample interval */
+#define ESI_log_ms(lms) (lms) /* actual value = 2^{lms} ms */
+
+struct mpr121_global_config_t
+{
+ uint8_t ffi; /* first filter iterations */
+ uint8_t cdc; /* global charge discharge current */
+ uint8_t cdt; /* global charge discharge time */
+ uint8_t sfi; /* second first iterations */
+ uint8_t esi; /* electrode sample interval */
+};
+
+#define RETRY_NEVER 0
+#define RETRY_2_TIMES 1
+#define RETRY_4_TIMES 2
+#define RETRY_8_TIMES 3
+
+struct mpr121_auto_config_t
+{
+ bool en; /* auto-conf enable */
+ bool ren; /* auto-reconf enable */
+ uint8_t retry; /* retry count */
+ bool scts; /* skip charge time search */
+ uint8_t usl; /* upper-side limit */
+ uint8_t lsl; /* lower-side limit */
+ uint8_t tl; /* target level */
+ bool acfie; /* auto-conf fail interrupt en */
+ bool arfie; /* auto-reconf fail interrupt en */
+ bool oorie; /* out of range interrupt en */
+};
+
+/* electrode mode */
+#define ELE_DISABLE 0
+#define ELE_EN0_x(x) ((x) + 1)
+/* eleprox mode */
+#define ELEPROX_DISABLE 0
+#define ELEPROX_EN0_1 1
+#define ELEPROX_EN0_3 2
+#define ELEPROX_EN0_11 3
+/* calibration lock */
+#define CL_SLOW_TRACK 0
+#define CL_DISABLE 1
+#define CL_TRACK 2
+#define CL_FAST_TRACK 3
+
+struct mpr121_config_t
+{
+ struct mpr121_electrode_config_t ele[ELECTRODE_COUNT];
+ struct mpr121_electrode_config_t eleprox;
+ struct
+ {
+ struct mpr121_baseline_filters_config_t ele;
+ struct mpr121_baseline_filters_config_t eleprox;
+ }filters;
+ struct mpr121_debounce_config_t debounce;
+ struct mpr121_global_config_t global;
+ struct mpr121_auto_config_t autoconf;
+ uint8_t ele_en; /* eletroce mode */
+ uint8_t eleprox_en; /* proximity mode */
+ uint8_t cal_lock; /* calibration lock */
+};
+
+/* gpio value */
+#define ELE_GPIO_CLR 0
+#define ELE_GPIO_SET 1
+#define ELE_GPIO_TOG 2
+/* pwm value */
+#define ELE_PWM_DISABLE 0
+#define ELE_PWM_DUTY(x) (x)
+#define ELE_PWM_MIN_DUTY 1
+#define ELE_PWM_MAX_DUTY 15
+
+void mpr121_init(void);
+void mpr121_set_config(struct mpr121_config_t *conf);
+/* gpios are only implemented for electrode>=4, use ELE_GPIO_* for value */
+void mpr121_set_gpio_output(int ele, int gpio_val);
+/* pwm value is between 0 and 15, use ELE_PWM_DISABLE or ELE_PWM_DUTY */
+void mpr121_set_gpio_pwm(int ele, int pwm);
+/* get electrode status (bitmap)
+ * NOTE this function merely returns the last electrode status read from the
+ * device and does not actively ask the device for touch status. */
+unsigned mpr121_get_touch_status(void);
+
+#endif /* __MPR121_ZENXFI3_H__ */