* [PATCH 0/3] input: Add support for ECI (multimedia) accessories
@ 2010-12-22 12:20 tapio.vihuri
2010-12-22 12:20 ` [PATCH 1/3] ECI: input: introduce ECI accessory input driver tapio.vihuri
0 siblings, 1 reply; 6+ messages in thread
From: tapio.vihuri @ 2010-12-22 12:20 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-kernel, alsa-devel, ilkka.koskinen, samu.p.onkalo
From: Tapio Vihuri <tapio.vihuri@nokia.com>
Hi all
This patch set introduce Multimedia Headset Accessory support for
Nokia phones. Technically those are known as ECI (Enhancement Control Interface)
If headset has many buttons, like play, vol+, vol- etc. then it is propably ECI
accessory.
Among several buttons ECI accessories contains memory for storing several
parameters.
This ECI input driver provides the following features:
- reading ECI configuration memory
- ECI buttons as input events
Drive is constructed as follows:
- ECI accessory input driver deals with headset accessory
- ECI bus control driver deals the HW transfering data to/from headset
- platform data match used HW
In the future accessory detection logic will be added using ALSA jack reporting.
Created against linux-2.6.37-rc6
Please review.
Tapio Vihuri (3):
ECI: input: introduce ECI accessory input driver
ECI: introducing ECI bus driver
ECI: adding platform data for ECI driver
arch/x86/platform/mrst/mrst.c | 59 +++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/ecibus/Kconfig | 46 ++
drivers/ecibus/Makefile | 10 +
drivers/ecibus/ecibus.c | 583 ++++++++++++++++++++++++
drivers/input/misc/Kconfig | 18 +
drivers/input/misc/Makefile | 2 +-
drivers/input/misc/eci.c | 1002 +++++++++++++++++++++++++++++++++++++++++
include/linux/input/eci.h | 165 +++++++
10 files changed, 1887 insertions(+), 1 deletions(-)
create mode 100644 drivers/ecibus/Kconfig
create mode 100644 drivers/ecibus/Makefile
create mode 100644 drivers/ecibus/ecibus.c
create mode 100644 drivers/input/misc/eci.c
create mode 100644 include/linux/input/eci.h
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/3] ECI: input: introduce ECI accessory input driver
2010-12-22 12:20 [PATCH 0/3] input: Add support for ECI (multimedia) accessories tapio.vihuri
@ 2010-12-22 12:20 ` tapio.vihuri
2010-12-22 12:20 ` [PATCH 2/3] ECI: introducing ECI bus driver tapio.vihuri
0 siblings, 1 reply; 6+ messages in thread
From: tapio.vihuri @ 2010-12-22 12:20 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-kernel, alsa-devel, ilkka.koskinen, samu.p.onkalo
From: Tapio Vihuri <tapio.vihuri@nokia.com>
ECI stands for (Enhancement Control Interface).
ECI is better known as Multimedia Headset for Nokia phones.
If headset has many buttons, like play, vol+, vol- etc. then it is propably
ECI accessory.
Among several buttons ECI accessory contains memory for storing several
parameters.
ECI input driver provides the following features:
- reading ECI configuration memory
- ECI buttons as input events
Signed-off-by: Tapio Vihuri <tapio.vihuri@nokia.com>
---
drivers/input/misc/Kconfig | 18 +
drivers/input/misc/Makefile | 2 +-
drivers/input/misc/eci.c | 1002 +++++++++++++++++++++++++++++++++++++++++++
include/linux/input/eci.h | 157 +++++++
4 files changed, 1178 insertions(+), 1 deletions(-)
create mode 100644 drivers/input/misc/eci.c
create mode 100644 include/linux/input/eci.h
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index b99b8cb..7a15bc6 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -448,4 +448,22 @@ config INPUT_ADXL34X_SPI
To compile this driver as a module, choose M here: the
module will be called adxl34x-spi.
+config INPUT_ECI
+ tristate "AV ECI (Enhancement Control Interface) input driver"
+ help
+ The Enhancement Control Interface functionality
+ ECI is better known as Multimedia Headset for Nokia phones.
+ If headset has many buttons, like play, vol+, vol- etc. then
+ it is propably ECI accessory.
+ Among several buttons ECI accessory contains memory for storing
+ several parameters.
+
+ ECI input driver provides the following features:
+ - reading ECI configuration memory
+ - ECI buttons as input events
+
+ Say 'y' here to statically link this module into the kernel or 'm'
+ to build it as a dynamically loadable module. The module will be
+ called eci.ko
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 1fe1f6c..99d2289 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -42,4 +42,4 @@ obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
-
+obj-$(CONFIG_INPUT_ECI) += eci.o
diff --git a/drivers/input/misc/eci.c b/drivers/input/misc/eci.c
new file mode 100644
index 0000000..72efccf
--- /dev/null
+++ b/drivers/input/misc/eci.c
@@ -0,0 +1,1002 @@
+/*
+ * This file is part of ECI (Enhancement Control Interface) accessory input
+ * driver
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Tapio Vihuri <tapio.vihuri@nokia.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/*
+ * ECI stands for (Enhancement Control Interface).
+ *
+ * ECI is better known as Multimedia Headset for Nokia phones.
+ * If headset has many buttons, like play, vol+, vol- etc. then it is propably
+ * ECI accessory.
+ * Among several buttons ECI accessory contains memory for storing several
+ * parameters.
+ *
+ * ECI input driver provides the following features:
+ * - reading ECI configuration memory
+ * - ECI buttons as input events
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/debugfs.h>
+
+#include <linux/input.h>
+#include <linux/input/eci.h>
+#include <linux/miscdevice.h>
+
+#define ECI_DRIVERNAME "ECI_accessory"
+
+#define ECI_WAIT_SEND_BUTTON 5 /* ms */
+#define ECI_WAIT_BUS_SETTLE 40 /* ms */
+#define ECI_TRY_GET_MEMORY 2000 /* ms */
+#define ECI_TRY_INIT_IO 200 /* ms */
+#define ECI_TRY_SET_MIC 200 /* ms */
+#define ECI_KEY_REPEAT_INTERVAL 400 /* ms */
+
+#define ECI_EKEY_BLOCK_ID 0xb3
+#define ECI_ENHANCEMENT_FEATURE_BLOCK_ID 0x02
+
+/* ECI Inputs */
+#define ECI_NIL_FEATURE 0x00 /* No feature */
+#define ECI_IGNS 0x01 /* Ignition Sense */
+#define ECI_CK_HANDSET_HOOK 0x02 /* Car-Kit Handset Hook */
+#define ECI_POWER_SUPPLY 0x03 /* Power Supply/Car Battery Det */
+#define ECI_EXT_AUD_IN 0x06 /* External audio In */
+#define ECI_SEND_END_VR 0x07 /* Send, End, and Voice Recogn */
+#define ECI_HD_PLUG 0x08 /* Headphone plug */
+#define ECI_DEV_POWER_REQ 0x0a /* Device Power Request */
+#define ECI_VOL_UP 0x0b /* Volume Up */
+#define ECI_VOL_DOWN 0x0c /* Volume Down */
+#define ECI_PLAY_PAUSE_CTRL 0x0d /* Play / Pause */
+#define ECI_STOP 0x0e /* Stop */
+#define ECI_NEXT_FF_AUTOSRC_UP 0x0f /* Next/Fast Fward/Autosearch up */
+#define ECI_PREV_REW_AUTOSEARCH_DOWN 0x10 /* Prev/Rewind/Autosearch down */
+#define ECI_POC 0x11 /* Push to Talk over Cellular */
+#define ECI_SYNC_BTN 0x14 /* Synchronization Button */
+#define ECI_MUSIC_RADIO_OFF_SELECTOR 0x15 /* Music/Radio/Off Selector */
+#define ECI_REDIAL 0x16 /* Redial */
+#define ECI_LEFT_SOFT_KEY 0x17 /* Left Soft Key */
+#define ECI_RIGHT_SOFT_KEY 0x18 /* Right Soft key */
+#define ECI_SEND_KEY 0x19 /* Send key */
+#define ECI_END_KEY 0x1a /* End key */
+#define ECI_MIDDLE_SOFT_KEY 0x1b /* Middle Soft key */
+#define ECI_UP 0x1c /* UP key/joystick direction */
+#define ECI_DOWN 0x1d /* DOWN key/joystick direction */
+#define ECI_RIGHT 0x1e /* RIGHT key/joystick direction */
+#define ECI_LEFT 0x1f /* LEFT key/joystick direction */
+#define ECI_SYMBIAN_NAVY_KEY 0x20 /* Symbian Application key */
+#define ECI_TERMINAL_APP_CTRL_IN 0x21 /* Terminal Applicat Ctrl Input */
+#define ECI_USB_CLASS_SWITCHING 0x23 /* USB Class Switching */
+#define ECI_MUTE 0x24 /* Mute */
+/* ECI Outputs */
+#define ECI_CRM 0x82 /* Car Radio Mute */
+#define ECI_PWR 0x83 /* Power */
+#define ECI_AUD_AMP 0x85 /* Audio Amplifier */
+#define ECI_EXT_AUD_SWITCH 0x86 /* External Audio Switch */
+#define ECI_HANDSET_AUDIO 0x87 /* Handset Audio */
+#define ECI_RING_INDICATOR 0x88 /* Ringing Indicator */
+#define ECI_CALL_ACTIVE 0x89 /* Call Active */
+#define ECI_ENHANCEMENT_DETECTED 0x8b /* Enhancement Detected */
+#define ECI_AUDIO_BLOCK_IN_USE 0x8e /* Audio Block In Use */
+#define ECI_STEREO_AUDIO_ACTIVE 0x8f /* stereo audio used in terminal */
+#define ECI_MONO_AUDIO_ACTIVE 0x90 /* mono audio used in terminal */
+#define ECI_TERMINAL_APP_CTRL_OUT 0x91 /* Terminal Applicat Ctrl Output */
+
+/*
+ * Most of these are key events.
+ * Switch event codes are put on top of keys (KEY_MAX ->)
+ */
+static int eci_codes[] = {
+ KEY_UNKNOWN, /* 0 ECI_NIL_FEATURE */
+ KEY_UNKNOWN, /* 1 ECI_IGNS */
+ KEY_UNKNOWN, /* 2 ECI_CK_HANDSET_HOOK */
+ KEY_BATTERY, /* 3 ECI_POWER_SUPPLY */
+ KEY_RESERVED, /* 4 ECI feature not defined */
+ KEY_RESERVED, /* 5 ECI feature not defined */
+ KEY_AUDIO, /* 6 ECI_EXT_AUD_IN */
+ KEY_PHONE, /* 7 ECI_SEND_END_VR */
+ KEY_MAX + SW_HEADPHONE_INSERT, /* 8 ECI_HD_PLUG, type switchs */
+ KEY_RESERVED, /* 9 ECI feature not defined */
+ KEY_UNKNOWN, /* 10 ECI_DEV_POWER_REQ */
+ KEY_VOLUMEUP, /* 11 ECI_VOL_UP */
+ KEY_VOLUMEDOWN, /* 12 ECI_VOL_DOWN */
+ KEY_PLAYPAUSE, /* 13 ECI_PLAY_PAUSE_CTRL */
+ KEY_STOP, /* 14 ECI_STOP */
+ KEY_FORWARD, /* 15 ECI_NEXT_FF_AUTOSRC_UP */
+ KEY_REWIND, /* 16 ECI_PREV_REW_AUTOSEARCH_DOWN */
+ KEY_UNKNOWN, /* 17 ECI_POC */
+ KEY_RESERVED, /* 18 ECI feature not defined */
+ KEY_RESERVED, /* 19 ECI feature not defined */
+ KEY_UNKNOWN, /* 20 ECI_SYNC_BTN */
+ KEY_RADIO, /* 21 ECI_MUSIC_RADIO_OFF_SELECTOR */
+ KEY_UNKNOWN, /* 22 ECI_REDIAL */
+ KEY_UNKNOWN, /* 23 ECI_LEFT_SOFT_KEY */
+ KEY_UNKNOWN, /* 24 ECI_RIGHT_SOFT_KEY */
+ KEY_SEND, /* 25 ECI_SEND_KEY */
+ KEY_END, /* 26 ECI_END_KEY */
+ KEY_UNKNOWN, /* 27 ECI_MIDDLE_SOFT_KEY */
+ KEY_UP, /* 28 ECI_UP */
+ KEY_DOWN, /* 29 ECI_DOWN */
+ KEY_RIGHT, /* 30 ECI_RIGHT */
+ KEY_LEFT, /* 31 ECI_LEFT */
+ KEY_UNKNOWN, /* 32 ECI_SYMBIAN_NAVY_KEY */
+ KEY_UNKNOWN, /* 33 ECI_TERMINAL_APP_CTRL_IN */
+ KEY_RESERVED, /* 34 ECI feature not defined */
+ KEY_UNKNOWN, /* 35 ECI_USB_CLASS_SWITCHING */
+ KEY_MUTE, /* 36 ECI_MUTE */
+};
+
+/* ECI accessory register's bits */
+#define ECI_MIC_AUTO 0x00
+#define ECI_MIC_OFF 0x5a
+#define ECI_MIC_ON 0xff
+
+#define ECI_INT_ENABLE 1
+#define ECI_INT_DELAY_ENABLE (1<<1)
+#define ECI_INT_LEN_76MS 0
+#define ECI_INT_LEN_82MS (1<<5)
+#define ECI_INT_LEN_37MS (2<<5)
+#define ECI_INT_LEN_19MS (3<<5)
+#define ECI_INT_LEN_10MS (4<<5)
+#define ECI_INT_LEN_5MS (5<<5)
+#define ECI_INT_LEN_2MS (6<<5)
+#define ECI_INT_LEN_120US (7<<5)
+
+struct eci_mem_block {
+ u8 id;
+ u8 len;
+ u16 size;
+};
+
+static struct eci_cb eci_callback;
+static struct audio_hsmic_event hsmic_event;
+
+static struct eci_data *the_eci;
+
+#ifdef CONFIG_DEBUG_FS
+static void eci_accessory_event(int event, void *priv);
+static void eci_hsmic_event(void *priv, bool on);
+
+static struct dentry *eci_debugfs_dir;
+
+static ssize_t mic_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ /* Assosiated in default_open() */
+ struct eci_data *eci = file->private_data;
+ char buf[80], *state;
+ int len = 0;
+ int ret;
+
+ /* Do not run twice */
+ if (*ppos == 0) {
+ ret = eci->eci_hw_ops->acc_read_reg(ECICMD_MIC_CTRL, buf, 1);
+ if (ret)
+ return ret;
+
+ eci->mic_state = buf[0];
+ switch (eci->mic_state) {
+ case ECI_MIC_AUTO:
+ state = "auto";
+ break;
+ case ECI_MIC_OFF:
+ state = "off";
+ break;
+ case ECI_MIC_ON:
+ state = "on";
+ break;
+ default:
+ state = "unknown";
+ break;
+ }
+
+ len = snprintf(buf, sizeof(buf), "microphone %s\n", state);
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ return ret;
+}
+
+static ssize_t mic_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ /* Assosiated in default_open() */
+ struct eci_data *eci = file->private_data;
+ char buf[80];
+ int buf_size;
+
+ buf_size = min(count, (sizeof(buf) - 1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (!memcmp(buf, "auto", 4))
+ eci_hsmic_event(eci, true);
+ else if (!memcmp(buf, "off", 3))
+ eci_hsmic_event(eci, false);
+ else if (!memcmp(buf, "on", 2)) {
+ eci->mic_state = ECI_MIC_ON;
+ if (eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL,
+ eci->mic_state))
+ dev_err(eci->dev, "Unable to control headset"
+ "microphone\n");
+ }
+
+ return count;
+}
+
+static int default_open(struct inode *inode, struct file *file)
+{
+ /* Assosiated in debugfs_create_file() */
+ if (inode->i_private)
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static const struct file_operations mic_fops = {
+ .open = default_open,
+ .read = mic_read,
+ .write = mic_write,
+};
+
+static void eci_uninitialize_debugfs(void)
+{
+ if (eci_debugfs_dir)
+ debugfs_remove_recursive(eci_debugfs_dir);
+}
+
+static long eci_initialize_debugfs(struct eci_data *eci)
+{
+ void *ok;
+
+ /* /sys/kernel/debug/ECI_accessory.# */
+ eci_debugfs_dir = debugfs_create_dir(dev_name(eci->dev), NULL);
+ if (!eci_debugfs_dir)
+ return -ENOENT;
+
+ /* Struct eci assosiated to inode->i_private */
+ ok = debugfs_create_file("mic", S_IRUGO | S_IWUSR,
+ eci_debugfs_dir, eci, &mic_fops);
+ if (!ok)
+ goto fail;
+
+ return 0;
+fail:
+ eci_uninitialize_debugfs();
+ return -ENOENT;
+}
+#else
+#define eci_initialize_debugfs(eci) 1
+#define eci_uninitialize_debugfs()
+#endif
+
+/* Returns size of accessory memory or error */
+static int eci_get_ekey(struct eci_data *eci, int *key)
+{
+ u8 buf[4];
+ struct eci_mem_block *ekey = (void *)buf;
+ int ret;
+
+ /* Read always four bytes */
+ ret = eci->eci_hw_ops->acc_read_direct(0, buf);
+
+ if (ret)
+ return ret;
+
+ if (ekey->id != ECI_EKEY_BLOCK_ID)
+ return -ENODEV;
+
+ *key = cpu_to_be16(ekey->size);
+
+ return 0;
+}
+
+static ssize_t show_eci_memory(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!the_eci->mem_ok)
+ return -ENXIO;
+
+ memcpy(buf, the_eci->memory, the_eci->mem_size);
+
+ return the_eci->mem_size;
+}
+
+static ssize_t show_cable_plugged(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct eci_data *eci = dev_get_drvdata(dev);
+
+ return snprintf(buf, sizeof(buf), "Cable plugged %s\n",
+ eci->plugged ? "in" : "out");
+}
+
+static ssize_t store_cable_plugged(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct eci_data *eci = dev_get_drvdata(dev);
+
+ if (!memcmp(buf, "in", 2)) {
+ eci->plugged = true;
+ eci_accessory_event(ECI_EVENT_PLUG_IN, eci);
+ } else if (!memcmp(buf, "out", 3)) {
+ eci->plugged = false;
+ eci_accessory_event(ECI_EVENT_PLUG_OUT, eci);
+ }
+
+ return len;
+}
+
+static DEVICE_ATTR(memory, S_IRUGO, show_eci_memory, NULL);
+static DEVICE_ATTR(cable, S_IRUGO | S_IWUSR , show_cable_plugged,
+ store_cable_plugged);
+
+static struct attribute *eci_attributes[] = {
+ &dev_attr_memory.attr,
+ &dev_attr_cable.attr,
+ NULL
+};
+
+static struct attribute_group eci_attr_group = {
+ .attrs = eci_attributes
+};
+
+/* Read ECI device memory into buffer */
+static int eci_get_memory(struct eci_data *eci, int *restart)
+{
+ int i, ret;
+
+ for (i = *restart; i < eci->mem_size; i += 4) {
+ ret = eci->eci_hw_ops->acc_read_direct(i, eci->memory + i);
+ *restart = i;
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+/*
+ * This should be really init_features, but most oftens these are just buttons
+ */
+static int eci_init_buttons(struct eci_data *eci)
+{
+ struct enchancement_features_fixed *eff = eci->e_features_fix;
+ u8 n, mireg;
+ int ret;
+ u8 buf[4];
+
+ n = eff->number_of_features;
+
+ if (n > ECI_MAX_FEATURE_COUNT)
+ return -EINVAL;
+
+ ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, ECI_MIC_OFF);
+ if (ret)
+ return ret;
+
+ ret = eci->eci_hw_ops->acc_read_reg(ECICMD_MASTER_INT_REG, buf, 1);
+ if (ret)
+ return ret;
+
+ mireg = buf[0];
+ mireg &= ~ECI_INT_ENABLE;
+ mireg |= ECI_INT_LEN_120US | ECI_INT_DELAY_ENABLE;
+
+ ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MASTER_INT_REG, mireg);
+ if (ret)
+ return ret;
+
+ msleep(ECI_WAIT_BUS_SETTLE);
+ mireg |= ECI_INT_ENABLE;
+ ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MASTER_INT_REG, mireg);
+ if (ret)
+ return ret;
+
+ msleep(ECI_WAIT_BUS_SETTLE);
+
+ return ret;
+}
+
+/* Find "enchangement features" block from buffer */
+static int eci_get_enchancement_features(struct eci_data *eci)
+{
+ u8 *mem = (void *)eci->memory;
+ struct eci_mem_block *b = (void *)mem;
+ struct eci_mem_block *mem_end = (void *)(eci->memory + eci->mem_size);
+
+ if (b->id != ECI_EKEY_BLOCK_ID)
+ return -ENODEV;
+
+ do {
+ dev_dbg(eci->dev, "skip BLOCK 0x%02x, LEN 0x%02x\n",
+ b->id, b->len);
+ if (!b->len)
+ return -EINVAL;
+
+ mem += b->len;
+ b = (void *)mem;
+ eci->e_features_fix = (void *)b;
+ dev_dbg(eci->dev, "found BLOCK 0x%02x, LEN 0x%02x\n",
+ b->id, b->len);
+ if (b->id == ECI_ENHANCEMENT_FEATURE_BLOCK_ID)
+ return 0;
+ } while (b < mem_end);
+
+ return -ENFILE;
+}
+
+/*
+ * Find out ECI features.
+ * All ECI memory block parsing are done here, be carefull as
+ * pointers to memory tend to go wrong easily.
+ * ECI "Enhancement Features block has variable size, so we try to
+ * catch pointers out of block due memory reading errors etc.
+ *
+ * I/O support field is not implemented.
+ * Data direction field is not implemented, nor writing to the ECI I/O
+ */
+static int eci_parse_enchancement_features(struct eci_data *eci)
+{
+ struct enchancement_features_fixed *eff = eci->e_features_fix;
+ struct enchancement_features_variable *efv = &eci->e_features_var;
+ int i;
+ u8 n, k;
+ void *mem_end = (void *)((u8 *)eff + eff->length);
+
+ dev_dbg(eci->dev, "block id 0x%02x length 0x%02x connector "
+ "configuration 0x%02x\n", eff->block_id, eff->length,
+ eff->connector_conf);
+ n = eff->number_of_features;
+ dev_dbg(eci->dev, "number of features %d\n", n);
+
+ if (n > ECI_MAX_FEATURE_COUNT)
+ return -EINVAL;
+
+ k = DIV_ROUND_UP(n, 8);
+ dev_dbg(eci->dev, "I/O support bytes count %d\n", k);
+
+ efv->io_support = &eff->number_of_features + 1;
+ /* efv->io_functionality[0] is not used! pins are in 1..31 range */
+ efv->io_functionality = efv->io_support + k - 1;
+ efv->active_state = efv->io_functionality + n + 1;
+
+ if ((void *)&efv->active_state[k] > mem_end)
+ return -EINVAL;
+
+ /* Last part of block */
+ for (i = 0; i < k; i++)
+ dev_dbg(eci->dev, "active_state[%d] 0x%02x\n", i,
+ efv->active_state[i]);
+
+ eci->buttons_data.buttons_up_mask =
+ ~(u32)(cpu_to_le32(*(u32 *)efv->active_state));
+
+ /*
+ * ECI accessory responces as many bytes needed for used I/O pins
+ * up to four bytes, when lines 24..31 are used
+ * all tested ECI accessories how ever return two data bytes
+ * event though there are less than eight I/O pins
+ *
+ * so we get alway reading error if there are less than eight I/Os
+ * meanwhile just use this kludge, FIXME
+ */
+ k = DIV_ROUND_UP(n + 1, 8);
+ if (k == 1)
+ k = 2;
+ eci->port_reg_count = k;
+
+ return 0;
+}
+
+static int eci_init_accessory(struct eci_data *eci)
+{
+ int ret, key = 0, restart = 0;
+ unsigned long future;
+
+ eci->mem_ok = false;
+
+ if (!eci->eci_hw_ops)
+ return -ENXIO;
+
+ ret = eci->eci_hw_ops->acc_reset();
+ if (ret)
+ return ret;
+
+ msleep(ECI_WAIT_BUS_SETTLE);
+
+ ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, ECI_MIC_OFF);
+ if (ret)
+ return ret;
+
+ /* Get ECI ekey block to determine memory size */
+ future = jiffies + msecs_to_jiffies(ECI_TRY_GET_MEMORY);
+ do {
+ ret = eci_get_ekey(eci, &key);
+ if (time_is_before_jiffies(future))
+ break;
+ } while (ret);
+
+ if (ret)
+ return ret;
+
+ eci->mem_size = key;
+ if (eci->mem_size > ECI_MAX_MEM_SIZE)
+ return -EINVAL;
+
+ /* Get ECI memory */
+ future = jiffies + msecs_to_jiffies(ECI_TRY_GET_MEMORY);
+ do {
+ ret = eci_get_memory(eci, &restart);
+ if (time_is_before_jiffies(future))
+ break;
+ } while (ret);
+
+ if (ret)
+ return ret;
+
+ if (eci_get_enchancement_features(eci))
+ return -EIO;
+
+ if (eci_parse_enchancement_features(eci))
+ return -EIO;
+
+ /*
+ * Configure ECI buttons now as we have after parsed
+ * enchancement features table
+ */
+ msleep(ECI_WAIT_BUS_SETTLE);
+ future = jiffies + msecs_to_jiffies(ECI_TRY_INIT_IO);
+ do {
+ ret = eci_init_buttons(eci);
+ if (time_is_before_jiffies(future))
+ break;
+ } while (ret);
+
+ if (ret)
+ return ret;
+
+ eci->mem_ok = true;
+ msleep(ECI_WAIT_BUS_SETTLE);
+
+ if (eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL, eci->mic_state))
+ dev_err(eci->dev, "Unable to control headset microphone\n");
+
+ return 0;
+}
+
+static int init_accessory_input(struct eci_data *eci)
+{
+ int err, i, code;
+
+ eci->acc_input = input_allocate_device();
+ if (!eci->acc_input) {
+ dev_err(eci->dev, "Error allocating input device: %d\n",
+ __LINE__);
+ return -ENOMEM;
+ }
+
+ eci->acc_input->name = "ECI Accessory";
+
+ /* Codes on top of KEY_MAX are switch events */
+ for (i = 0; i < ARRAY_SIZE(eci_codes); i++) {
+ code = eci_codes[i];
+ if (code >= KEY_MAX) {
+ code -= KEY_MAX;
+ set_bit(code, eci->acc_input->swbit);
+ } else {
+ set_bit(code, eci->acc_input->keybit);
+ }
+ }
+
+ set_bit(EV_KEY, eci->acc_input->evbit);
+ set_bit(EV_SW, eci->acc_input->evbit);
+ set_bit(EV_REP, eci->acc_input->evbit);
+
+ err = input_register_device(eci->acc_input);
+ if (err) {
+ dev_err(eci->dev, "Error registering input device: %d\n",
+ __LINE__);
+ goto err_free_dev;
+ }
+
+ /* Must set after input_register_device() to take effect */
+ eci->acc_input->rep[REP_PERIOD] = ECI_KEY_REPEAT_INTERVAL;
+
+ return 0;
+
+err_free_dev:
+ input_free_device(eci->acc_input);
+ return err;
+}
+
+static void remove_accessory_input(struct eci_data *eci)
+{
+ input_unregister_device(eci->acc_input);
+}
+
+/* Press/release ccessory button(s) */
+static int eci_get_button(struct eci_data *eci)
+{
+ struct enchancement_features_fixed *eff = eci->e_features_fix;
+ struct eci_buttons_data *b = &eci->buttons_data;
+
+ if (!eci->mem_ok)
+ return -ENXIO;
+
+ if (((b->buttons & 0x0000ffff) == 0) && (eff->number_of_features > 2)) {
+ dev_err(eci->dev, "ECI report all buttons down, rejected %d\n",
+ __LINE__);
+ return -EINVAL;
+ }
+
+ if (b->windex < ECI_BUTTON_BUF_SIZE) {
+ if (b->buttons_buf[b->windex] == 0)
+ b->buttons_buf[b->windex] = b->buttons;
+ else
+ dev_err(eci->dev, "ECI button queue owerflow %d\n",
+ __LINE__);
+ }
+ b->windex++;
+ if (b->windex == ECI_BUTTON_BUF_SIZE)
+ b->windex = 0;
+
+ return 0;
+}
+
+/* Intended to use ONLY inside eci_parse_button() ! */
+#define ACTIVE_STATE(x) (u32)(cpu_to_le32(*(u32 *)efv->active_state) & BIT(x-1))
+#define BUTTON_STATE(x) ((buttons & BIT(x))>>1)
+
+static int eci_parse_button(struct eci_data *eci, u32 buttons)
+{
+ int pin, code, state;
+ u8 n, io_fun;
+ struct enchancement_features_variable *efv = &eci->e_features_var;
+ struct enchancement_features_fixed *eff = eci->e_features_fix;
+
+ if (!eci->mem_ok)
+ return -ENXIO;
+
+ n = eff->number_of_features;
+
+ for (pin = 1; pin <= n; pin++) {
+ io_fun = efv->io_functionality[pin] & ~BIT(7);
+ if (io_fun > ECI_MUTE)
+ break;
+ code = eci_codes[io_fun];
+ state = (BUTTON_STATE(pin) == ACTIVE_STATE(pin));
+ if (state)
+ dev_dbg(eci->dev, "I/O functionality 0x%02x\n", io_fun);
+ if (code >= KEY_MAX)
+ input_report_switch(eci->acc_input, code - KEY_MAX,
+ state);
+ else
+ input_report_key(eci->acc_input, code, state);
+ }
+ input_sync(eci->acc_input);
+
+ return 0;
+}
+
+static int eci_send_button(struct eci_data *eci)
+{
+ int i;
+ struct enchancement_features_fixed *eff = eci->e_features_fix;
+ struct eci_buttons_data *b = &eci->buttons_data;
+ u8 n;
+
+ if (!eci->mem_ok)
+ return -ENXIO;
+
+ n = eff->number_of_features;
+
+ if (n > ECI_MAX_FEATURE_COUNT)
+ return -EINVAL;
+ /*
+ * Codes on top of KEY_MAX are switch events.
+ * Let input system take care multiple key events
+ */
+ for (i = 0; i < ECI_BUTTON_BUF_SIZE; i++) {
+ if (b->buttons_buf[b->rindex] == 0)
+ break;
+
+ if (eci_parse_button(eci, b->buttons_buf[b->rindex]))
+ return -ENXIO;
+
+ b->buttons_buf[b->rindex] = 0;
+ b->rindex++;
+ if (b->rindex == ECI_BUTTON_BUF_SIZE)
+ b->rindex = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Other driver(s) can call this after registering themselves using
+ * eci_register()
+ */
+static void eci_accessory_event(int event, void *priv)
+{
+ struct eci_data *eci = priv;
+ struct eci_buttons_data *b = &eci->buttons_data;
+ int delay = 0;
+ int ret = 0;
+
+ eci->event = event;
+ switch (event) {
+ case ECI_EVENT_IS_ECI:
+ eci->is_eci = true;
+ ret = eci->eci_hw_ops->acc_reset();
+ if (ret)
+ eci->is_eci = false;
+ break;
+ case ECI_EVENT_PLUG_IN:
+ eci->first_event = true;
+ ret = eci_init_accessory(eci);
+ if (ret < 0)
+ ret = eci_init_accessory(eci);
+ if (ret) {
+ dev_err(eci->dev, "Accessory init %s%s%s%sat: %d\n",
+ ret & ACI_COMMERR ? "COMMERR " : "",
+ ret & ACI_FRAERR ? "FRAERR " : "",
+ ret & ACI_RESERR ? "RESERR " : "",
+ ret & ACI_COLL ? "COLLERR " : "",
+ __LINE__);
+ break;
+ }
+ break;
+ case ECI_EVENT_PLUG_OUT:
+ eci->mem_ok = false;
+ break;
+ case ECI_EVENT_BUTTON:
+ /*
+ * First event might not be valid due plug insertion
+ * so we filter it out if it seems garbage
+ */
+ if (eci->first_event) {
+ eci->first_event = false;
+ if ((b->buttons & 0xff) == 0)
+ break;
+ }
+ eci_get_button(eci);
+ delay = msecs_to_jiffies(ECI_WAIT_SEND_BUTTON);
+ schedule_delayed_work(&eci->eci_ws, delay);
+ break;
+ default:
+ dev_err(eci->dev, "unknown event %d: %d\n", event, __LINE__);
+ break;
+ }
+
+ return;
+}
+
+static void eci_hsmic_event(void *priv, bool on)
+{
+ struct eci_data *eci = priv;
+ unsigned long future;
+ int ret;
+
+ if (!eci)
+ return;
+
+ if (on)
+ eci->mic_state = ECI_MIC_AUTO;
+ else
+ eci->mic_state = ECI_MIC_OFF;
+
+ future = jiffies + msecs_to_jiffies(ECI_TRY_SET_MIC);
+ do {
+ ret = eci->eci_hw_ops->acc_write_reg(ECICMD_MIC_CTRL,
+ eci->mic_state);
+ if (time_is_before_jiffies(future))
+ break;
+ } while (ret);
+
+ if (ret)
+ dev_err(eci->dev, "Unable to control headset microphone\n");
+}
+
+/* General work func (eci_ws) for several tasks */
+static void eci_work(struct work_struct *ws)
+{
+ struct eci_data *eci;
+ int ret;
+
+ eci = container_of((struct delayed_work *)ws, struct eci_data,
+ eci_ws);
+
+ ret = eci_send_button(eci);
+ if (ret)
+ dev_err(eci->dev, "Error sending event: %d\n", __LINE__);
+}
+
+static struct miscdevice eci_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = ECI_DRIVERNAME,
+};
+
+struct eci_cb *eci_register(struct eci_hw_ops *eci_ops)
+{
+ if (!the_eci)
+ return ERR_PTR(-EBUSY);
+
+ if (!eci_ops || !eci_ops->acc_read_direct ||
+ !eci_ops->acc_read_reg || !eci_ops->acc_write_reg ||
+ !eci_ops->acc_reset)
+ return ERR_PTR(-EINVAL);
+
+ the_eci->eci_hw_ops = eci_ops;
+
+ return &eci_callback;
+}
+EXPORT_SYMBOL(eci_register);
+
+static int __init eci_probe(struct platform_device *pdev)
+{
+ struct eci_data *eci;
+ struct eci_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ eci = kzalloc(sizeof(*eci), GFP_KERNEL);
+ if (!eci)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, eci);
+ eci->dev = &pdev->dev;
+
+ ret = misc_register(&eci_device);
+ if (ret) {
+ dev_err(eci->dev, "could not register misc_device: %d\n",
+ __LINE__);
+ goto err_misc;
+ }
+
+ the_eci = eci;
+
+ eci_callback.event = eci_accessory_event;
+ eci_callback.priv = eci;
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &eci_attr_group);
+ if (ret) {
+ dev_err(eci->dev, "could not create sysfs entries: %d\n",
+ __LINE__);
+ goto err_sysfs;
+ }
+
+ ret = eci_initialize_debugfs(eci);
+ if (ret)
+ dev_err(eci->dev, "could not create debugfs entries: %d\n",
+ __LINE__);
+
+ ret = init_accessory_input(eci);
+ if (ret) {
+ dev_err(eci->dev, "ERROR initializing accessory input: %d\n",
+ __LINE__);
+ goto err_input;
+ }
+
+ /*
+ * If platform machine has audio driver providing
+ * register_hsmic_event_cb, we should give accessory microphone control,
+ * ie. eci_hsmic_event to it.
+ * This way audio driver get control to ECI accessory microphone and
+ * we can save power
+ */
+ if (pdata) {
+ if (pdata->register_hsmic_event_cb) {
+ hsmic_event.private = eci;
+ hsmic_event.event = eci_hsmic_event;
+ pdata->register_hsmic_event_cb(&hsmic_event);
+ }
+ }
+
+ init_waitqueue_head(&eci->wait);
+ INIT_DELAYED_WORK(&eci->eci_ws, eci_work);
+
+ eci->mem_ok = false;
+ /*
+ * By default ECI driver leaves microphone off, to save power.
+ * Audio driver can set microphone on by using
+ * hsmic_event.event
+ */
+ eci->mic_state = ECI_MIC_OFF;
+
+ /* Init buttons_data indexes and buffer */
+ memset(&eci->buttons_data, 0, sizeof(struct eci_buttons_data));
+ eci->buttons_data.buttons = 0xffffffff;
+
+ return 0;
+
+err_input:
+ eci_uninitialize_debugfs();
+ sysfs_remove_group(&pdev->dev.kobj, &eci_attr_group);
+
+err_sysfs:
+ misc_deregister(&eci_device);
+
+err_misc:
+ kfree(eci);
+
+ return ret;
+}
+
+static int __exit eci_remove(struct platform_device *pdev)
+{
+ struct eci_data *eci = platform_get_drvdata(pdev);
+ struct eci_platform_data *pdata = pdev->dev.platform_data;
+
+ pdata->register_hsmic_event_cb(NULL);
+ cancel_delayed_work_sync(&eci->eci_ws);
+ eci_uninitialize_debugfs();
+ sysfs_remove_group(&pdev->dev.kobj, &eci_attr_group);
+ remove_accessory_input(eci);
+ kfree(eci);
+ misc_deregister(&eci_device);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int eci_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ return -ENOSYS;
+}
+#else
+#define eci_suspend NULL
+#endif
+
+static struct platform_driver eci_driver = {
+ .probe = eci_probe,
+ .remove = __exit_p(eci_remove),
+ .suspend = eci_suspend,
+ .driver = {
+ .name = ECI_DRIVERNAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init eci_init(void)
+{
+ return platform_driver_register(&eci_driver);
+}
+device_initcall(eci_init);
+
+static void __exit eci_exit(void)
+{
+ platform_driver_unregister(&eci_driver);
+}
+module_exit(eci_exit);
+
+MODULE_ALIAS("platform:" ECI_DRIVERNAME);
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("ECI accessory driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/input/eci.h b/include/linux/input/eci.h
new file mode 100644
index 0000000..b8be99c
--- /dev/null
+++ b/include/linux/input/eci.h
@@ -0,0 +1,157 @@
+/*
+ * This file is part of ECI (Enhancement Control Interface) driver
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Tapio Vihuri <tapio.vihuri@nokia.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifndef __ECI_H__
+#define __ECI_H__
+
+#define ECI_MAX_MEM_SIZE 0x7c
+#define ECI_BUTTON_BUF_SIZE 32
+#define ECI_MAX_FEATURE_COUNT 31
+
+#define ACI_COMMERR 0x010
+#define ACI_FRAERR 0x020
+#define ACI_RESERR 0x040
+#define ACI_COLL 0x080
+
+#define ECI_REAL_BUTTONS 0
+#define ECI_FORCE_BUTTONS_UP 1
+
+/* fixed in ECI HW, do not change */
+enum {
+ ECICMD_HWID,
+ ECICMD_SWID,
+ ECICMD_ECI_BUS_SPEED,
+ ECICMD_MIC_CTRL,
+ ECICMD_MASTER_INT_REG,
+ ECICMD_HW_CONF_MEM_ACCESS,
+ ECICMD_EXTENDED_MEM_ACCESS,
+ ECICMD_INDIRECT_MEM_ACCESS,
+ ECICMD_PORT_DATA_0,
+ ECICMD_PORT_DATA_1,
+ ECICMD_PORT_DATA_2,
+ ECICMD_PORT_DATA_3,
+ ECICMD_LATCHED_PORT_DATA_0,
+ ECICMD_LATCHED_PORT_DATA_1,
+ ECICMD_LATCHED_PORT_DATA_2,
+ ECICMD_LATCHED_PORT_DATA_3,
+ ECICMD_DATA_DIR_0,
+ ECICMD_DATA_DIR_1,
+ ECICMD_DATA_DIR_2,
+ ECICMD_DATA_DIR_3,
+ ECICMD_INT_CONFIG_0_LOW,
+ ECICMD_INT_CONFIG_0_HIGH,
+ ECICMD_INT_CONFIG_1_LOW,
+ ECICMD_INT_CONFIG_1_HIGH,
+ ECICMD_INT_CONFIG_2_LOW,
+ ECICMD_INT_CONFIG_2_HIGH,
+ ECICMD_INT_CONFIG_3_LOW,
+ ECICMD_INT_CONFIG_3_HIGH,
+ /*
+ * 0x1c - 0x2f reserved for future
+ * 0x30 - 0x3d reserved
+ */
+ ECICMD_EEPROM_LOCK = 0x3e,
+ ECICMD_RESERVED, /* 0x3f */
+ ECIREG_STATUS, /* 0x40 */
+ ECIREG_READ_COUNT, /* 0x41 */
+ ECIREG_BUF_COUNT, /* 0x42 */
+ ECIREG_RST_LEARN, /* 0x43 */
+ /* 0x44 - 0xdf as data buffer */
+ ECIREG_READ_DIRECT, /* 0x44 */
+ ECIREG_HW_ID = 0xe0, /* 0xe0 */
+ ECIREG_FW_ID, /* 0xe1 */
+ ECIREG_TEST_IN, /* 0xe2 */
+ ECIREG_TEST_OUT, /* 0xe3 */
+};
+
+enum {
+ ECI_EVENT_IS_ECI,
+ ECI_EVENT_PLUG_IN,
+ ECI_EVENT_PLUG_OUT,
+ ECI_EVENT_BUTTON,
+ ECI_EVENT_NO,
+};
+
+struct eci_hw_ops {
+ int (*acc_reset)(void);
+ int (*acc_read_direct)(u8 addr, char *buf);
+ int (*acc_read_reg)(u8 reg, u8 *buf, int count);
+ int (*acc_write_reg)(u8 reg, u8 param);
+};
+
+struct eci_cb {
+ void *priv;
+ void (*event)(int event, void *priv);
+};
+
+struct audio_hsmic_event {
+ void *private;
+ void (*event)(void *priv, bool on);
+};
+
+struct eci_platform_data {
+ void (*register_hsmic_event_cb)(struct audio_hsmic_event *);
+};
+
+struct enchancement_features_fixed {
+ u8 block_id;
+ u8 length;
+ u8 connector_conf;
+ u8 number_of_features;
+};
+
+struct enchancement_features_variable {
+ u8 *io_support;
+ u8 *io_functionality;
+ u8 *active_state;
+};
+
+struct eci_buttons_data {
+ u32 buttons;
+ int windex;
+ int rindex;
+ u32 buttons_up_mask;
+ u32 buttons_buf[ECI_BUTTON_BUF_SIZE];
+};
+
+struct eci_data {
+ struct device *dev;
+ struct delayed_work eci_ws;
+ wait_queue_head_t wait;
+ struct input_dev *acc_input;
+ int event;
+ bool first_event;
+ bool mem_ok;
+ u16 mem_size;
+ u8 memory[ECI_MAX_MEM_SIZE];
+ struct enchancement_features_fixed *e_features_fix;
+ struct enchancement_features_variable e_features_var;
+ u8 port_reg_count;
+ struct eci_buttons_data buttons_data;
+ struct eci_hw_ops *eci_hw_ops;
+ u8 mic_state;
+ bool plugged;
+ bool is_eci;
+};
+
+struct eci_cb *eci_register(struct eci_hw_ops *eci_ops);
+#endif
--
1.6.5
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 2/3] ECI: introducing ECI bus driver
2010-12-22 12:20 ` [PATCH 1/3] ECI: input: introduce ECI accessory input driver tapio.vihuri
@ 2010-12-22 12:20 ` tapio.vihuri
2010-12-22 12:20 ` [PATCH 3/3] ECI: adding platform data for ECI driver tapio.vihuri
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: tapio.vihuri @ 2010-12-22 12:20 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-kernel, alsa-devel, ilkka.koskinen, samu.p.onkalo
From: Tapio Vihuri <tapio.vihuri@nokia.com>
ECI bus controller is kind of bridge between host CPU I2C and ECI accessory
ECI communication.
Signed-off-by: Tapio Vihuri <tapio.vihuri@nokia.com>
---
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/ecibus/Kconfig | 46 ++++
drivers/ecibus/Makefile | 10 +
drivers/ecibus/ecibus.c | 583 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/input/eci.h | 8 +
6 files changed, 650 insertions(+), 0 deletions(-)
create mode 100644 drivers/ecibus/Kconfig
create mode 100644 drivers/ecibus/Makefile
create mode 100644 drivers/ecibus/ecibus.c
diff --git a/drivers/Kconfig b/drivers/Kconfig
index a2b902f..f450f98 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -111,4 +111,6 @@ source "drivers/xen/Kconfig"
source "drivers/staging/Kconfig"
source "drivers/platform/Kconfig"
+
+source "drivers/ecibus/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index f3ebb30..11f5d57 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -113,5 +113,6 @@ obj-$(CONFIG_SSB) += ssb/
obj-$(CONFIG_VHOST_NET) += vhost/
obj-$(CONFIG_VLYNQ) += vlynq/
obj-$(CONFIG_STAGING) += staging/
+obj-$(CONFIG_ECI) += ecibus/
obj-y += platform/
obj-y += ieee802154/
diff --git a/drivers/ecibus/Kconfig b/drivers/ecibus/Kconfig
new file mode 100644
index 0000000..27e5e36
--- /dev/null
+++ b/drivers/ecibus/Kconfig
@@ -0,0 +1,46 @@
+#
+# ECI driver configuration
+#
+menuconfig ECI
+ bool "ECI support"
+ help
+ ECI (Enhancement Control Interface) accessory support
+
+ The Enhancement Control Interface functionality
+ ECI is better known as Multimedia Headset for Nokia phones.
+ If headset has many buttons, like play, vol+, vol- etc. then
+ it is propably ECI accessory.
+ Among several buttons ECI accessory contains memory for storing
+ several parameters.
+
+ Enable ECI support in terminal so that ECI input driver is able
+ to communicate with ECI accessory
+
+if ECI
+
+config ECI_DEBUG
+ boolean "Debug support for ECI drivers"
+ depends on DEBUG_KERNEL
+ help
+ Selects ECI driver debug messaging.
+
+ Say "yes" to enable debug messaging (like dev_dbg and pr_debug),
+ sysfs, and debugfs support in ECI controller.
+
+comment "ECI Master Controller Drivers"
+
+config ECI_BUS
+ tristate "ECI bus controller driver"
+ select INPUT_ECI
+ help
+ This selects a driver for the ECI bus controller
+
+ ECI bus controller is kind of bridge between host CPU I2C and
+ ECI accessory ECI communication.
+
+ Say 'y' here to statically link this module into the kernel or 'm'
+ to build it as a dynamically loadable module. The module will be
+ called ecibus.ko
+
+
+endif # ECI
diff --git a/drivers/ecibus/Makefile b/drivers/ecibus/Makefile
new file mode 100644
index 0000000..ce4206b
--- /dev/null
+++ b/drivers/ecibus/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for kernel ECI drivers.
+#
+
+ifeq ($(CONFIG_ECI_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
+# ECI master controller drivers (bus)
+obj-$(CONFIG_ECI_BUS) += ecibus.o
diff --git a/drivers/ecibus/ecibus.c b/drivers/ecibus/ecibus.c
new file mode 100644
index 0000000..93266b3
--- /dev/null
+++ b/drivers/ecibus/ecibus.c
@@ -0,0 +1,583 @@
+/*
+ * This file is part of ECI (Enhancement Control Interface) bus controller
+ * driver
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Tapio Vihuri <tapio.vihuri@nokia.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/*
+ * ECI stands for (Enhancement Control Interface).
+ *
+ * ECI is better known as Multimedia Headset for Nokia phones.
+ * If headset has many buttons, like play, vol+, vol- etc. then it is propably
+ * ECI accessory.
+ *
+ * ECI bus controller is kind of bridge between host CPU I2C and ECI accessory
+ * ECI communication.
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/input/eci.h>
+
+#include <asm/intel_scu_ipc.h>
+
+/*
+ * VPROG2CNT - VPROG2 Control Register
+ * 2.5V | normal | normal
+ * 10 | 111 | 111
+ * 2.5V | off | off
+ * 10 | 100 | 100
+ */
+#define AvP_MSIC_VPROG2 0xd7
+#define AvP_MSIC_VPROG2_2V5_ON 0xbf
+#define AvP_MSIC_VPROG2_2V5_OFF 0xa4
+
+#define ECIBUS_I2C_ADDRS 0x6d
+#define ECIBUS_STATUS_DATA_READY 0x01
+#define ECIBUS_STATUS_ACCESSORY_INT 0x02
+
+#define ECIBUS_WAIT_IRQ 100 /* msec */
+#define ECI_RST_MIN 62
+#define ECI_RST_WAIT 10 /* msec */
+
+struct ecibus_data {
+ struct i2c_client *client;
+ struct device *dev;
+ struct eci_cb *eci_callback;
+ int ecibus_rst_gpio;
+ int ecibus_sw_ctrl_gpio;
+ int ecibus_int_gpio;
+ wait_queue_head_t wait;
+ bool wait_eci_buttons;
+ bool wait_data;
+};
+
+static struct ecibus_data *the_ed;
+
+#ifdef CONFIG_DEBUG_FS
+static int ecibus_ctrl_read_reg(u8 reg);
+static int ecibus_ctrl_write_reg(u8 reg, u8 param);
+
+static struct dentry *ecibus_debugfs_dir;
+
+static ssize_t debug_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[80];
+ int len = 0;
+ int ret;
+
+ if (*ppos == 0) {
+ ret = ecibus_ctrl_read_reg(ECIREG_TEST_IN);
+ if (ret < 0)
+ return ret;
+ len += snprintf(buf + len, sizeof(buf) - len, "%02x\n", ret);
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ return ret;
+}
+
+static ssize_t debug_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[80];
+ int buf_size;
+ unsigned long val;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ if (!strict_strtoul(buf, 0, &val))
+ ecibus_ctrl_write_reg(ECIREG_TEST_OUT, val);
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t reset_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct ecibus_data *ed = file->private_data;
+ char buf[80];
+ int len = 0;
+ int ret;
+
+ if (*ppos == 0) {
+ ret = !!gpio_get_value(ed->ecibus_rst_gpio);
+ len = snprintf(buf, sizeof(buf), "%d\n", ret);
+ }
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+ return ret;
+}
+
+static ssize_t reset_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ /* Assosiated in default_open() */
+ struct ecibus_data *ed = file->private_data;
+ char buf[32];
+ int buf_size;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (!memcmp(buf, "0", 1))
+ gpio_set_value(ed->ecibus_rst_gpio, 0);
+ else if (!memcmp(buf, "1", 1))
+ gpio_set_value(ed->ecibus_rst_gpio, 1);
+
+ return count;
+}
+
+static int default_open(struct inode *inode, struct file *file)
+{
+ /* Assosiated in debugfs_create_file() */
+ if (inode->i_private)
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static const struct file_operations debug_fops = {
+ .open = default_open,
+ .read = debug_read,
+ .write = debug_write,
+};
+
+static const struct file_operations reset_fops = {
+ .open = default_open,
+ .read = reset_read,
+ .write = reset_write,
+};
+
+static void ecibus_uninitialize_debugfs(void)
+{
+ if (ecibus_debugfs_dir)
+ debugfs_remove_recursive(ecibus_debugfs_dir);
+}
+
+static long ecibus_initialize_debugfs(struct ecibus_data *ed)
+{
+ void *ok;
+
+ /* /sys/kernel/debug/ecibus.# */
+ ecibus_debugfs_dir = debugfs_create_dir(dev_name(ed->dev), NULL);
+ if (!ecibus_debugfs_dir)
+ return -ENOENT;
+
+ /* Struct ed assosiated to inode->i_private */
+ ok = debugfs_create_file("debug", S_IRUGO | S_IWUSR,
+ ecibus_debugfs_dir, ed, &debug_fops);
+ if (!ok)
+ goto fail;
+
+ ok = debugfs_create_file("reset", S_IRUGO | S_IWUSR,
+ ecibus_debugfs_dir, ed, &reset_fops);
+ if (!ok)
+ goto fail;
+
+ return 0;
+fail:
+ ecibus_uninitialize_debugfs();
+ return -ENOENT;
+}
+#else
+#define ecibus_initialize_debugfs(ed) 1
+#define ecibus_uninitialize_debugfs()
+#endif
+
+static struct i2c_board_info ecibus_i2c = {
+ I2C_BOARD_INFO("ecibus", ECIBUS_I2C_ADDRS) };
+
+/* For ecibus controller internal registers */
+static int ecibus_ctrl_read_reg(u8 reg)
+{
+
+ if (reg <= ECICMD_RESERVED)
+ return -EINVAL;
+
+ return i2c_smbus_read_byte_data(the_ed->client, reg);
+}
+
+static int ecibus_ctrl_write_reg(u8 reg, u8 param)
+{
+
+ if (reg <= ECICMD_RESERVED)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(the_ed->client, reg, param);
+}
+
+/* Reset and learn ECI accessory, ie. get speed */
+static int ecibus_acc_reset(void)
+{
+ s32 ret;
+
+ ecibus_ctrl_write_reg(ECIREG_RST_LEARN, 0);
+
+ msleep(ECI_RST_WAIT);
+
+ ret = ecibus_ctrl_read_reg(ECIREG_RST_LEARN);
+ if (ret < ECI_RST_MIN)
+ return -EIO;
+
+ return 0;
+}
+/* Read always four bytes, as stated in ECI specification */
+static int ecibus_acc_read_direct(u8 addr, char *buf)
+{
+ s32 ret;
+ int i;
+
+ /* Initiate ECI accessory memory read */
+ the_ed->wait_data = false;
+ if (!ecibus_ctrl_write_reg(ECIREG_READ_COUNT, 4))
+ if (ecibus_ctrl_write_reg(ECIREG_READ_DIRECT, addr))
+ return -EIO;
+
+ if (!wait_event_timeout(the_ed->wait, the_ed->wait_data == true,
+ msecs_to_jiffies(ECIBUS_WAIT_IRQ)))
+ return -EIO;
+
+ for (i = 0; i < 4; i++) {
+ ret = ecibus_ctrl_read_reg(ECIREG_READ_DIRECT + i);
+ if (ret < 0)
+ return ret;
+ buf[i] = ret;
+ usleep_range(2000, 10000);
+ }
+ return 0;
+}
+
+/* Trigger ECI accessory register data write (from accessory) */
+static int ecibus_fire_acc_read_reg(u8 reg, int count)
+{
+ if (!ecibus_ctrl_write_reg(ECIREG_READ_COUNT, count))
+ return i2c_smbus_read_byte_data(the_ed->client, reg);
+ else
+ return -EIO;
+}
+
+/* For ECI accessory internal registers */
+static int ecibus_acc_read_reg(u8 reg, u8 *buf, int count)
+{
+ s32 ret;
+ int i;
+
+ if (reg > ECICMD_RESERVED)
+ return -EINVAL;
+
+ the_ed->wait_data = false;
+ if (ecibus_fire_acc_read_reg(reg, count))
+ return -EIO;
+
+ if (!wait_event_timeout(the_ed->wait, the_ed->wait_data == true,
+ msecs_to_jiffies(ECIBUS_WAIT_IRQ)))
+ return -EIO;
+
+ for (i = 0; i < count; i++) {
+ ret = ecibus_ctrl_read_reg(ECIREG_READ_DIRECT + i);
+ if (ret < 0)
+ return ret;
+
+ buf[i] = ret;
+ }
+
+ return 0;
+}
+
+/* ECI accessory register write */
+static int ecibus_acc_write_reg(u8 reg, u8 param)
+{
+ if (reg > ECICMD_RESERVED)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(the_ed->client, reg, param);
+}
+
+
+/*
+ * Struct eci_data is ECI input driver (dealing ECI accessories) data.
+ * Struct ecibus_data is this driver data, dealing just ECI communication.
+ * Global eci_register() pairs structs so that we can call ECI input driver
+ * event function with eci_data
+ */
+static void ecibus_emit_buttons(struct ecibus_data *ed, bool force_up)
+{
+ struct eci_data *eci = ed->eci_callback->priv;
+ struct eci_buttons_data *b = &eci->buttons_data;
+
+ if (force_up)
+ b->buttons = b->buttons_up_mask;
+
+ ed->eci_callback->event(ECI_EVENT_BUTTON, eci);
+}
+
+static void ecibus_get_buttons(u8 *buf)
+{
+ int i, ret;
+
+ for (i = 0; i < 2; i++) {
+ ret = ecibus_ctrl_read_reg(ECIREG_READ_DIRECT + i);
+ buf[i] = ret;
+ }
+}
+
+static irqreturn_t ecibus_irq_handler(int irq, void *_ed)
+{
+ struct ecibus_data *ed = _ed;
+ struct eci_data *eci = ed->eci_callback->priv;
+ struct eci_buttons_data *b = &eci->buttons_data;
+ int status;
+ char buf[4];
+
+ /* Clears ecibus DATA interrupt */
+ status = ecibus_ctrl_read_reg(ECIREG_STATUS);
+
+ if (status & ECIBUS_STATUS_DATA_READY) {
+ /*
+ * Buttons are special case as we want be fast with them
+ * and this way we cope with nested button and data interrupts
+ */
+ if (ed->wait_eci_buttons) {
+ ecibus_get_buttons(buf);
+ b->buttons = cpu_to_le32(*(u32 *)buf);
+ ecibus_emit_buttons(ed, ECI_REAL_BUTTONS);
+ ed->wait_eci_buttons = false;
+ }
+ /* Complete ECI data reading */
+ ed->wait_data = true;
+ wake_up(&ed->wait);
+ }
+
+ /* Accessory interrupt, ie. button pressed */
+ if (status & ECIBUS_STATUS_ACCESSORY_INT) {
+ if (eci->mem_ok) {
+ ecibus_fire_acc_read_reg(ECICMD_PORT_DATA_0, 2);
+ ed->wait_eci_buttons = true;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct eci_hw_ops ecibus_hw_ops = {
+ .acc_reset = ecibus_acc_reset,
+ .acc_read_direct = ecibus_acc_read_direct,
+ .acc_read_reg = ecibus_acc_read_reg,
+ .acc_write_reg = ecibus_acc_write_reg,
+};
+
+static int __init ecibus_probe(struct platform_device *pdev)
+{
+ struct ecibus_data *ed;
+ struct ecibus_platform_data *pdata = pdev->dev.platform_data;
+ struct i2c_adapter *adapter;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "platform_data not available: %d\n",
+ __LINE__);
+ return -EINVAL;
+ }
+
+ ed = kzalloc(sizeof(*ed), GFP_KERNEL);
+ if (!ed)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ed);
+ ed->dev = &pdev->dev;
+ ed->ecibus_rst_gpio = pdata->ecibus_rst_gpio;
+ ed->ecibus_sw_ctrl_gpio = pdata->ecibus_sw_ctrl_gpio;
+ ed->ecibus_int_gpio = pdata->ecibus_int_gpio;
+
+ if (ed->ecibus_rst_gpio == 0 || ed->ecibus_sw_ctrl_gpio == 0 ||
+ ed->ecibus_int_gpio == 0) {
+ ret = -ENXIO;
+ goto gpio_err;
+ }
+
+ the_ed = ed;
+
+ adapter = i2c_get_adapter(1);
+ if (adapter)
+ ed->client = i2c_new_device(adapter, &ecibus_i2c);
+ if (!ed->client) {
+ dev_err(ed->dev, "could not get i2c device %s at 0x%02x: %d\n",
+ ecibus_i2c.type, ecibus_i2c.addr, __LINE__);
+ kfree(ed);
+
+ return -ENODEV;
+ }
+
+ ret = ecibus_initialize_debugfs(ed);
+ if (ret)
+ dev_err(ed->dev, "could not create debugfs entries: %d\n",
+ __LINE__);
+
+ ret = gpio_request(ed->ecibus_rst_gpio, "ECI_RSTn");
+ if (ret) {
+ dev_err(ed->dev, "could not request ECI_RSTn gpio %d: %d\n",
+ ed->ecibus_rst_gpio, __LINE__);
+ goto rst_gpio_err;
+ }
+
+ gpio_direction_output(ed->ecibus_rst_gpio, 0);
+ gpio_set_value(ed->ecibus_rst_gpio, 1);
+
+ ret = gpio_request(ed->ecibus_sw_ctrl_gpio, "ECI_SW_CTRL");
+ if (ret) {
+ dev_err(ed->dev, "could not request ECI_SW_CTRL gpio %d: %d\n",
+ ed->ecibus_sw_ctrl_gpio, __LINE__);
+ goto sw_ctrl_gpio_err;
+ }
+
+ gpio_direction_input(ed->ecibus_sw_ctrl_gpio);
+
+ ret = gpio_request(ed->ecibus_int_gpio, "ECI_INT");
+ if (ret) {
+ dev_err(ed->dev, "could not request ECI_INT gpio %d: %d\n",
+ ed->ecibus_int_gpio, __LINE__);
+ goto int_gpio_err;
+ }
+
+ gpio_direction_input(ed->ecibus_int_gpio);
+
+ ret = request_threaded_irq(gpio_to_irq(ed->ecibus_int_gpio), NULL,
+ ecibus_irq_handler, IRQF_TRIGGER_RISING, "ECI_INT", ed);
+ if (ret) {
+ dev_err(ed->dev, "could not request irq %d: %d\n",
+ gpio_to_irq(ed->ecibus_int_gpio), __LINE__);
+ goto int_irq_err;
+ }
+
+ /* Register itself to the ECI accessory driver */
+ ed->eci_callback = eci_register(&ecibus_hw_ops);
+ if (IS_ERR(ed->eci_callback)) {
+ ret = PTR_ERR(ed->eci_callback);
+ goto eci_register_err;
+
+ }
+
+ /*
+ * Turn on vprog2
+ * Some decent power ctrl interface, please
+ */
+ intel_scu_ipc_iowrite8(AvP_MSIC_VPROG2, AvP_MSIC_VPROG2_2V5_ON);
+
+ init_waitqueue_head(&ed->wait);
+
+ return 0;
+
+eci_register_err:
+ free_irq(gpio_to_irq(ed->ecibus_int_gpio), ed);
+int_irq_err:
+ gpio_free(ed->ecibus_int_gpio);
+int_gpio_err:
+ gpio_free(ed->ecibus_sw_ctrl_gpio);
+sw_ctrl_gpio_err:
+ gpio_set_value(ed->ecibus_rst_gpio, 0);
+ gpio_free(ed->ecibus_rst_gpio);
+rst_gpio_err:
+ ecibus_uninitialize_debugfs();
+ i2c_unregister_device(ed->client);
+gpio_err:
+ kfree(ed);
+
+ return ret;
+}
+
+static int __exit ecibus_remove(struct platform_device *pdev)
+{
+ struct ecibus_data *ed = platform_get_drvdata(pdev);
+
+ gpio_set_value(ed->ecibus_rst_gpio, 0);
+ gpio_free(ed->ecibus_rst_gpio);
+
+ gpio_free(ed->ecibus_sw_ctrl_gpio);
+
+ free_irq(gpio_to_irq(ed->ecibus_int_gpio), ed);
+ gpio_free(ed->ecibus_int_gpio);
+
+ ecibus_uninitialize_debugfs();
+ i2c_unregister_device(ed->client);
+
+ /*
+ * Turn off vprog2
+ * Some decent power ctrl interface, please
+ */
+ intel_scu_ipc_iowrite8(AvP_MSIC_VPROG2, AvP_MSIC_VPROG2_2V5_OFF);
+
+ kfree(ed);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int ecibus_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ return -ENOSYS;
+}
+#else
+#define ecibus_suspend NULL
+#endif
+
+static struct platform_driver ecibus_driver = {
+ .driver = {
+ .name = "ecibus",
+ .owner = THIS_MODULE,
+ },
+ .suspend = ecibus_suspend,
+ .remove = __exit_p(ecibus_remove),
+};
+
+static int __init ecibus_init(void)
+{
+ return platform_driver_probe(&ecibus_driver, ecibus_probe);
+}
+module_init(ecibus_init);
+
+static void __exit ecibus_exit(void)
+{
+
+ platform_driver_unregister(&ecibus_driver);
+}
+module_exit(ecibus_exit);
+
+MODULE_DESCRIPTION("ECI accessory controller driver");
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_ALIAS("platform:ecibus");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/input/eci.h b/include/linux/input/eci.h
index b8be99c..a9d1008 100644
--- a/include/linux/input/eci.h
+++ b/include/linux/input/eci.h
@@ -154,4 +154,12 @@ struct eci_data {
};
struct eci_cb *eci_register(struct eci_hw_ops *eci_ops);
+
+/* ecibus controller data */
+struct ecibus_platform_data {
+ int ecibus_rst_gpio;
+ int ecibus_sw_ctrl_gpio;
+ int ecibus_int_gpio;
+};
+
#endif
--
1.6.5
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/3] ECI: adding platform data for ECI driver
2010-12-22 12:20 ` [PATCH 2/3] ECI: introducing ECI bus driver tapio.vihuri
@ 2010-12-22 12:20 ` tapio.vihuri
2010-12-22 17:11 ` [PATCH 2/3] ECI: introducing ECI bus driver Randy Dunlap
2010-12-22 17:17 ` Randy Dunlap
2 siblings, 0 replies; 6+ messages in thread
From: tapio.vihuri @ 2010-12-22 12:20 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-kernel, alsa-devel, ilkka.koskinen, samu.p.onkalo
From: Tapio Vihuri <tapio.vihuri@nokia.com>
Gives platform data for ECI accessory input driver and
ECI bus controller driver.
Signed-off-by: Tapio Vihuri <tapio.vihuri@nokia.com>
---
arch/x86/platform/mrst/mrst.c | 59 +++++++++++++++++++++++++++++++++++++++++
1 files changed, 59 insertions(+), 0 deletions(-)
diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c
index 79ae681..60dca78 100644
--- a/arch/x86/platform/mrst/mrst.c
+++ b/arch/x86/platform/mrst/mrst.c
@@ -14,6 +14,7 @@
#include <linux/sfi.h>
#include <linux/irq.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <asm/setup.h>
#include <asm/mpspec_def.h>
@@ -309,3 +310,61 @@ static inline int __init setup_x86_mrst_timer(char *arg)
return 0;
}
__setup("x86_mrst_timer=", setup_x86_mrst_timer);
+
+#if defined(CONFIG_ECI) || defined(CONFIG_ECI_MODULE)
+#include <linux/input/eci.h>
+
+#define GPIO_ECI_RSTn 126 /* GP_CORE_030 + 96 */
+#define GPIO_ECI_SW_CTRL 178 /* GP_CORE_082 + 96 */
+#define GPIO_ECI_INT 16 /* GP_AON_016 */
+
+static struct ecibus_platform_data medfield_ecibus_control = {
+ .ecibus_rst_gpio = GPIO_ECI_RSTn,
+ .ecibus_sw_ctrl_gpio = GPIO_ECI_SW_CTRL,
+ .ecibus_int_gpio = GPIO_ECI_INT,
+};
+
+/*
+ * This is just example, should be used in platform audio driver
+ * hsmic_event->event(hsmic_event->private, true)
+ */
+static void medfield_register_hsmic_event_cb(struct audio_hsmic_event *event)
+{
+ struct audio_hsmic_event *hsmic_event;
+
+ hsmic_event = event;
+}
+
+static struct eci_platform_data medfield_eci_platform_data = {
+ .register_hsmic_event_cb = medfield_register_hsmic_event_cb,
+};
+
+static struct platform_device medfield_ecibus_device = {
+ .name = "ecibus",
+ .id = 1,
+ .dev = {
+ .platform_data = &medfield_ecibus_control,
+ },
+};
+
+static struct platform_device medfield_eci_device = {
+ .name = "ECI_accessory",
+ .dev = {
+ .platform_data = &medfield_eci_platform_data,
+ },
+};
+
+static int __init medfield_ecibus_init(void)
+{
+ int retval;
+
+ retval = platform_device_register(&medfield_ecibus_device);
+ if (retval < 0)
+ return retval;
+
+ retval = platform_device_register(&medfield_eci_device);
+
+ return retval;
+}
+device_initcall(medfield_ecibus_init);
+#endif
--
1.6.5
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] ECI: introducing ECI bus driver
2010-12-22 12:20 ` [PATCH 2/3] ECI: introducing ECI bus driver tapio.vihuri
2010-12-22 12:20 ` [PATCH 3/3] ECI: adding platform data for ECI driver tapio.vihuri
@ 2010-12-22 17:11 ` Randy Dunlap
2010-12-22 17:17 ` Randy Dunlap
2 siblings, 0 replies; 6+ messages in thread
From: Randy Dunlap @ 2010-12-22 17:11 UTC (permalink / raw)
To: tapio.vihuri
Cc: dmitry.torokhov, linux-kernel, alsa-devel, ilkka.koskinen, samu.p.onkalo
On Wed, 22 Dec 2010 14:20:34 +0200 tapio.vihuri@nokia.com wrote:
> From: Tapio Vihuri <tapio.vihuri@nokia.com>
>
> ECI bus controller is kind of bridge between host CPU I2C and ECI accessory
> ECI communication.
>
> Signed-off-by: Tapio Vihuri <tapio.vihuri@nokia.com>
> ---
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/ecibus/Kconfig | 46 ++++
> drivers/ecibus/Makefile | 10 +
> drivers/ecibus/ecibus.c | 583 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/input/eci.h | 8 +
> 6 files changed, 650 insertions(+), 0 deletions(-)
> create mode 100644 drivers/ecibus/Kconfig
> create mode 100644 drivers/ecibus/Makefile
> create mode 100644 drivers/ecibus/ecibus.c
> diff --git a/drivers/ecibus/Kconfig b/drivers/ecibus/Kconfig
> new file mode 100644
> index 0000000..27e5e36
> --- /dev/null
> +++ b/drivers/ecibus/Kconfig
> @@ -0,0 +1,46 @@
> +#
> +# ECI driver configuration
> +#
> +menuconfig ECI
> + bool "ECI support"
> + help
> + ECI (Enhancement Control Interface) accessory support
> +
> + The Enhancement Control Interface functionality
> + ECI is better known as Multimedia Headset for Nokia phones.
> + If headset has many buttons, like play, vol+, vol- etc. then
> + it is propably ECI accessory.
> + Among several buttons ECI accessory contains memory for storing
> + several parameters.
> +
> + Enable ECI support in terminal so that ECI input driver is able
> + to communicate with ECI accessory
> +
> +if ECI
> +
> +config ECI_DEBUG
> + boolean "Debug support for ECI drivers"
> + depends on DEBUG_KERNEL
> + help
> + Selects ECI driver debug messaging.
> +
> + Say "yes" to enable debug messaging (like dev_dbg and pr_debug),
> + sysfs, and debugfs support in ECI controller.
> +
> +comment "ECI Master Controller Drivers"
> +
> +config ECI_BUS
> + tristate "ECI bus controller driver"
> + select INPUT_ECI
'select' INPUT_ECI when INPUT and/or INPUT_MISC are not enabled causes this
kconfig warning:
warning: (ECI_BUS && ECI) selects INPUT_ECI which has unmet direct dependencies (!S390 && INPUT && INPUT_MISC)
> + help
> + This selects a driver for the ECI bus controller
> +
> + ECI bus controller is kind of bridge between host CPU I2C and
> + ECI accessory ECI communication.
> +
> + Say 'y' here to statically link this module into the kernel or 'm'
> + to build it as a dynamically loadable module. The module will be
> + called ecibus.ko
> +
> +
> +endif # ECI
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
desserts: http://www.xenotime.net/linux/recipes/
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 2/3] ECI: introducing ECI bus driver
2010-12-22 12:20 ` [PATCH 2/3] ECI: introducing ECI bus driver tapio.vihuri
2010-12-22 12:20 ` [PATCH 3/3] ECI: adding platform data for ECI driver tapio.vihuri
2010-12-22 17:11 ` [PATCH 2/3] ECI: introducing ECI bus driver Randy Dunlap
@ 2010-12-22 17:17 ` Randy Dunlap
2 siblings, 0 replies; 6+ messages in thread
From: Randy Dunlap @ 2010-12-22 17:17 UTC (permalink / raw)
To: tapio.vihuri
Cc: dmitry.torokhov, linux-kernel, alsa-devel, ilkka.koskinen, samu.p.onkalo
On Wed, 22 Dec 2010 14:20:34 +0200 tapio.vihuri@nokia.com wrote:
> From: Tapio Vihuri <tapio.vihuri@nokia.com>
>
> ECI bus controller is kind of bridge between host CPU I2C and ECI accessory
> ECI communication.
>
> Signed-off-by: Tapio Vihuri <tapio.vihuri@nokia.com>
> ---
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/ecibus/Kconfig | 46 ++++
> drivers/ecibus/Makefile | 10 +
> drivers/ecibus/ecibus.c | 583 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/input/eci.h | 8 +
> 6 files changed, 650 insertions(+), 0 deletions(-)
> create mode 100644 drivers/ecibus/Kconfig
> create mode 100644 drivers/ecibus/Makefile
> create mode 100644 drivers/ecibus/ecibus.c
ERROR: "intel_scu_ipc_iowrite8" [drivers/ecibus/ecibus.ko] undefined!
You must have had some kernel config enabled that I don't have enabled
when you built this. Please fix Kconfig files so that this won't happen.
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
desserts: http://www.xenotime.net/linux/recipes/
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2010-12-22 17:18 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-12-22 12:20 [PATCH 0/3] input: Add support for ECI (multimedia) accessories tapio.vihuri
2010-12-22 12:20 ` [PATCH 1/3] ECI: input: introduce ECI accessory input driver tapio.vihuri
2010-12-22 12:20 ` [PATCH 2/3] ECI: introducing ECI bus driver tapio.vihuri
2010-12-22 12:20 ` [PATCH 3/3] ECI: adding platform data for ECI driver tapio.vihuri
2010-12-22 17:11 ` [PATCH 2/3] ECI: introducing ECI bus driver Randy Dunlap
2010-12-22 17:17 ` Randy Dunlap
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).