public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/3] input: Add support for ECI (multimedia) accessories
@ 2010-12-30 13:36 tapio.vihuri
  2010-12-30 13:36 ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver tapio.vihuri
  0 siblings, 1 reply; 7+ messages in thread
From: tapio.vihuri @ 2010-12-30 13:36 UTC (permalink / raw)
  To: dmitry.torokhov, randy.dunlap
  Cc: linux-kernel, alsa-devel, ilkka.koskinen, samu.p.onkalo

From: Tapio Vihuri <tapio.vihuri@nokia.com>

Thank you for comments.

In series two there Kconfig dependency problem fixed, preventing compilation
if X86_MRST (Intel Moorestown) or INPUT_MISC is not selected.

Also created against linux-2.6.37-rc8

--- v1 message ---

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        |   35 ++
 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, 1876 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] 7+ messages in thread

* [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver
  2010-12-30 13:36 [PATCH v2 0/3] input: Add support for ECI (multimedia) accessories tapio.vihuri
@ 2010-12-30 13:36 ` tapio.vihuri
  2010-12-30 13:36   ` [PATCH v2 2/3] ECI: introducing ECI bus driver tapio.vihuri
  2011-01-02  8:19   ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver Dmitry Torokhov
  0 siblings, 2 replies; 7+ messages in thread
From: tapio.vihuri @ 2010-12-30 13:36 UTC (permalink / raw)
  To: dmitry.torokhov, randy.dunlap
  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] 7+ messages in thread

* [PATCH v2 2/3] ECI: introducing ECI bus driver
  2010-12-30 13:36 ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver tapio.vihuri
@ 2010-12-30 13:36   ` tapio.vihuri
  2010-12-30 13:36     ` [PATCH v2 3/3] ECI: adding platform data for ECI driver tapio.vihuri
  2011-01-03  8:42     ` [alsa-devel] [PATCH v2 2/3] ECI: introducing ECI bus driver Peter Ujfalusi
  2011-01-02  8:19   ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver Dmitry Torokhov
  1 sibling, 2 replies; 7+ messages in thread
From: tapio.vihuri @ 2010-12-30 13:36 UTC (permalink / raw)
  To: dmitry.torokhov, randy.dunlap
  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    |   35 +++
 drivers/ecibus/Makefile   |   10 +
 drivers/ecibus/ecibus.c   |  583 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/input/eci.h |    8 +
 6 files changed, 639 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..f2fc8a4
--- /dev/null
+++ b/drivers/ecibus/Kconfig
@@ -0,0 +1,35 @@
+#
+# 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_BUS
+	tristate "ECI bus controller driver"
+	select INPUT_ECI
+	depends on X86_MRST && 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
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] 7+ messages in thread

* [PATCH v2 3/3] ECI: adding platform data for ECI driver
  2010-12-30 13:36   ` [PATCH v2 2/3] ECI: introducing ECI bus driver tapio.vihuri
@ 2010-12-30 13:36     ` tapio.vihuri
  2011-01-03  8:42     ` [alsa-devel] [PATCH v2 2/3] ECI: introducing ECI bus driver Peter Ujfalusi
  1 sibling, 0 replies; 7+ messages in thread
From: tapio.vihuri @ 2010-12-30 13:36 UTC (permalink / raw)
  To: dmitry.torokhov, randy.dunlap
  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] 7+ messages in thread

* Re: [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver
  2010-12-30 13:36 ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver tapio.vihuri
  2010-12-30 13:36   ` [PATCH v2 2/3] ECI: introducing ECI bus driver tapio.vihuri
@ 2011-01-02  8:19   ` Dmitry Torokhov
  2011-01-04 14:01     ` Tapio Vihuri
  1 sibling, 1 reply; 7+ messages in thread
From: Dmitry Torokhov @ 2011-01-02  8:19 UTC (permalink / raw)
  To: tapio.vihuri
  Cc: randy.dunlap, linux-kernel, alsa-devel, ilkka.koskinen,
	samu.p.onkalo

Hi Tapio,

On Thu, Dec 30, 2010 at 03:36:57PM +0200, tapio.vihuri@nokia.com wrote:
> 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

Please keep Makefile and Kconfig sorted alphabetically.

> 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 */
> +};

Consider switching to sparse keymap library. You won't be needing ugly
KEY_MAX + SW_XXX hacks and it will also support remapping keys from
userspace.

> +
> +/* 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");

Do not break strings on 80 column boundaries, wither align the arguments
differently or just go past 80 columns, like this:

			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

Should be 0 I think, and not have a parameter, preferably

static inline int eci_initialize_debugfs(void)
{
	return 0;
}

> +#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);

cpu_to_be16()??? This looks really wierd.

> +
> +	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");

Should it be dsimple 0/1? How is this attribute supposed to be used?

> +}
> +
> +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);
> +	}

Same here. Should it be in debugfs probably?

> +
> +	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
> +	 */

I do not understand this comment.

> +	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);

__set_bit(), no neded to lock bus.

> +
> +	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,
> +};

What does this device do?

> +
> +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
> 

Thank you.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [alsa-devel] [PATCH v2 2/3] ECI: introducing ECI bus driver
  2010-12-30 13:36   ` [PATCH v2 2/3] ECI: introducing ECI bus driver tapio.vihuri
  2010-12-30 13:36     ` [PATCH v2 3/3] ECI: adding platform data for ECI driver tapio.vihuri
@ 2011-01-03  8:42     ` Peter Ujfalusi
  1 sibling, 0 replies; 7+ messages in thread
From: Peter Ujfalusi @ 2011-01-03  8:42 UTC (permalink / raw)
  To: ext tapio.vihuri@nokia.com
  Cc: dmitry.torokhov, randy.dunlap, alsa-devel, ilkka.koskinen,
	linux-kernel, samu.p.onkalo

Hi,

On 12/30/10 15:36, ext 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.

It is kind of misleading to call this driver as a bus driver IMHO.
For what I gathered, this is a driver for a micro-controller, which
implements the ECI communication.
This could be a generic driver for the micro-controller, but two lines
makes it x86 specific...

...

> +#include <asm/intel_scu_ipc.h>

...

> +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);

What happens if the mc is on another bus?
Shall you have platform data for this?
Or even better, use i2c device/driver probing for this driver?

> +       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);

Is this some sort of power enable for the controller itself?
If it is, why not use a callback from the platform to enable/disable the
power, so the driver will be platform independent, and you do this intel
specific thing in arch/ platform code.
If this is not related to the mc itself, then this shall not be part of
this driver.

...

> +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);

Same applies here for the intel dependency.

-- 
Péter

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver
  2011-01-02  8:19   ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver Dmitry Torokhov
@ 2011-01-04 14:01     ` Tapio Vihuri
  0 siblings, 0 replies; 7+ messages in thread
From: Tapio Vihuri @ 2011-01-04 14:01 UTC (permalink / raw)
  To: ext Dmitry Torokhov
  Cc: randy.dunlap, linux-kernel, alsa-devel, ilkka.koskinen,
	samu.p.onkalo

On Sun, 2011-01-02 at 00:19 -0800, ext Dmitry Torokhov wrote:
> Hi Tapio,
> 

Hi Dmitry

Thank you for good comments. I sent v3 patch set right after this email.

--------8<-------

> Please keep Makefile and Kconfig sorted alphabetically.
> 

Corrected in patch set.

--------8<-------

> Consider switching to sparse keymap library. You won't be needing ugly
> KEY_MAX + SW_XXX hacks and it will also support remapping keys from
> userspace.
> 

Much nicer solution, thank you.

--------8<-------

> Do not break strings on 80 column boundaries, wither align the arguments
> differently or just go past 80 columns, like this:
> 
>                         dev_err(eci->dev,
>                                 "Unable to control headset microphone\n");
> 
Corrected in patch set.

--------8<-------


> > +#define eci_initialize_debugfs(eci)  1
> 
> Should be 0 I think, and not have a parameter, preferably
> 
> static inline int eci_initialize_debugfs(void)
> {
>         return 0;
> }
> 

I take this static inline solution, but I needed the parameter.

--------8<-------

> > +     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);
> 
> cpu_to_be16()??? This looks really wierd.
> 

The ECI specification says that data in ECI accessory's memory is in big
endian order. This simply get 16-bit size parameter right in any
endianes machine.

--------8<-------


> > +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");
> 
> Should it be dsimple 0/1? How is this attribute supposed to be used?
> 
> > +static ssize_t store_cable_plugged(struct device *dev,
> > +             struct device_attribute *attr,
> > +             const char *buf, size_t len)
> > +{
--------8<-------

> Same here. Should it be in debugfs probably?
> 

I made it now as 0/1. It's in sysfs as this is user space's interface
for ECI acessories inserting.

The other way is via ALSA sound driver providing jack detection, but
it's not there yet.

--------8<-------

> > +     /*
> > +      * Configure ECI buttons now as we have after parsed
> > +      * enchancement features table
> > +      */
> 
> I do not understand this comment.
> 

I have loose some word somewhere... Now it goes:
 	/*
         * Configure ECI buttons now as we know how after
         * enchancement features table has been parsed
         */


--------8<-------


> > +     set_bit(EV_KEY, eci->acc_input->evbit);
> > +     set_bit(EV_SW, eci->acc_input->evbit);
> > +     set_bit(EV_REP, eci->acc_input->evbit);
> 
> __set_bit(), no neded to lock bus.
> 
Corrected in patch set.

--------8<-------

> > +
> > +static struct miscdevice eci_device = {
> > +     .minor = MISC_DYNAMIC_MINOR,
> > +     .name = ECI_DRIVERNAME,
> > +};
> 
> What does this device do?
> 

Nothing, and I can't even remember why I have put it there first place.
Removed now.

--------8<-------

> Thank you.
> 
> --
> Dmitry

Thank you, now driver is better.

-- 
Tapsa


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2011-01-04 14:02 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-30 13:36 [PATCH v2 0/3] input: Add support for ECI (multimedia) accessories tapio.vihuri
2010-12-30 13:36 ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver tapio.vihuri
2010-12-30 13:36   ` [PATCH v2 2/3] ECI: introducing ECI bus driver tapio.vihuri
2010-12-30 13:36     ` [PATCH v2 3/3] ECI: adding platform data for ECI driver tapio.vihuri
2011-01-03  8:42     ` [alsa-devel] [PATCH v2 2/3] ECI: introducing ECI bus driver Peter Ujfalusi
2011-01-02  8:19   ` [PATCH v2 1/3] ECI: input: introduce ECI accessory input driver Dmitry Torokhov
2011-01-04 14:01     ` Tapio Vihuri

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox