summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2009-08-07 11:20:34 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2009-08-07 11:20:34 +0200
commit60785ba6ff60262adfa40e62cd5ff5a372db3feb (patch)
tree702ae75275e4d9c74a2c2c064d12b905fa1abe97
parent558096b5369b0b555cc31f1eba8ac140575676ce (diff)
parentd268ffd8f3e1deceaa2f23d863aea5c2b94ad488 (diff)
downloadlinux-2.6-master.tar.gz
linux-2.6-master.tar.xz
Merge branch 'phytec-master-sha-lfu' of /ptx/work/octopus/lfu/kernel/linux-2.6-git-1 into phytec-masterHEADphytec-mastermaster
Conflicts: drivers/mfd/Makefile Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--drivers/hwmon/Kconfig6
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/mc13783-adc.c314
-rw-r--r--drivers/input/touchscreen/Kconfig1
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c113
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/mc13783-core.c16
-rw-r--r--include/linux/mfd/mc13783-private.h167
-rw-r--r--include/linux/mfd/mc13783.h5
-rw-r--r--sound/arm/mc13783.c2
10 files changed, 580 insertions, 46 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2d5016691d4..a7e34fd31cf 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1017,6 +1017,12 @@ config SENSORS_APPLESMC
Say Y here if you have an applicable laptop and want to experience
the awesome power of applesmc.
+config SENSORS_MC13783_ADC
+ tristate "Freescale MC13783 ADC"
+ depends on MFD_MC13783
+ help
+ Support for the ad converter on mc13783 pmic.
+
config HWMON_DEBUG_CHIP
bool "Hardware Monitoring Chip debugging messages"
default n
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b793dce6bed..9b4e1316964 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -89,6 +89,7 @@ obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
+obj-$(CONFIG_SENSORS_MC13783_ADC) += mc13783-adc.o
ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
new file mode 100644
index 00000000000..2f4d1890772
--- /dev/null
+++ b/drivers/hwmon/mc13783-adc.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/mc13783-private.h>
+
+#define MC13783_ADC_NAME "mc13783-adc"
+
+struct mc13783_adc_priv {
+ struct mc13783 *mc13783;
+ struct mutex adc_conv_lock;
+ unsigned int sample[4];
+ struct device *hwmon_dev;
+ unsigned int conv_mode;
+ unsigned int ts_active;
+};
+
+/* set adc to ts interrupt mode, which generates touchscreen wakeup interrupt */
+static void inline mc13783_adc_set_ts_irq_mode(struct mc13783 *mc13783)
+{
+ unsigned int reg_adc0, reg_adc1;
+
+ reg_adc0 = MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+ | MC13783_ADC0_TSMOD0;
+ reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN;
+
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_0, reg_adc0);
+ mc13783_reg_write(mc13783, MC13783_REG_ADC_1, reg_adc1);
+}
+
+int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+ unsigned int channel, unsigned int *sample)
+{
+ unsigned int reg_adc0, reg_adc1;
+ int i;
+ struct mc13783_adc_priv *priv = platform_get_drvdata(mc13783->adc);
+
+ mutex_lock(&priv->adc_conv_lock);
+
+ /* set up auto incrementing anyway to make quick read */
+ reg_adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2;
+ /* enable the adc, ignore external triggering and set ASC to trigger
+ * conversion */
+ reg_adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN
+ | MC13783_ADC1_ASC;
+
+ /* setup channel number */
+ if (channel > 7)
+ reg_adc1 |= MC13783_ADC1_ADSEL;
+
+ switch (mode) {
+ case MC13783_ADC_MODE_TS:
+ /* enables touch screen reference mode and set touchscreen mode
+ * to position mode */
+ reg_adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_ADREFMODE
+ | MC13783_ADC0_TSMOD0 | MC13783_ADC0_TSMOD1;
+ reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ break;
+ case MC13783_ADC_MODE_SINGLE_CHAN:
+ reg_adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT;
+ reg_adc1 |= MC13783_ADC1_RAND;
+ break;
+ case MC13783_ADC_MODE_MULT_CHAN:
+ reg_adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT;
+ break;
+ default:
+ pr_err("Unknow ADC mode\n");
+ return -EINVAL;
+ }
+
+ pr_debug("writing reg0: 0x%x reg1: 0x%x\n", reg_adc0, reg_adc1);
+
+ mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_0, reg_adc0);
+ mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_1, reg_adc1);
+
+ wait_for_completion_interruptible(&mc13783->adc_done);
+
+ for (i = 0; i < 4; i++)
+ mc13783_reg_read(mc13783, MC13783_REG_ADC_2,
+ &sample[i]);
+
+ if (priv->ts_active)
+ mc13783_adc_set_ts_irq_mode(mc13783);
+
+ mutex_unlock(&priv->adc_conv_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(mc13783_adc_do_conversion);
+
+void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status)
+{
+ struct mc13783_adc_priv *priv = platform_get_drvdata(mc13783->adc);
+
+ priv->ts_active = status;
+}
+EXPORT_SYMBOL(mc13783_adc_set_ts_status);
+
+static ssize_t mc13783_adc_show_read_mode(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ unsigned int mode;
+ const char *mode_name;
+
+ mode = priv->conv_mode;
+
+ if (mode == MC13783_ADC_MODE_SINGLE_CHAN)
+ mode_name = "Single Channel Conversion, 8 X sample / channel";
+ if (mode == MC13783_ADC_MODE_MULT_CHAN)
+ mode_name = "Multi Channel Conversion 1 x sample / channel ";
+
+ return sprintf(buf, "Mode: %s\n", mode_name);
+}
+
+static ssize_t mc13783_adc_set_read_mode(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+
+ unsigned long mode = 0;
+
+ if (!strncmp(buf, "Single", 6) || !strncmp(buf, "single", 6))
+ mode = MC13783_ADC_MODE_SINGLE_CHAN;
+
+ if (!strncmp(buf, "Multi", 5) || !strncmp(buf, "multi", 5))
+ mode = MC13783_ADC_MODE_MULT_CHAN;
+
+ if (!mode) {
+ pr_err("unknown or reserved mode\n");
+ return -EINVAL;
+ }
+
+ priv->conv_mode = mode;
+
+ return count;
+}
+
+static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ return sprintf(buf, "mc13783_adc\n");
+}
+
+static ssize_t mc13783_adc_read(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ unsigned int channel = attr -> index;
+ unsigned int mode, res[8];
+ int i, c, res_index;
+
+ mode = priv->conv_mode;
+
+ mc13783_adc_do_conversion(priv->mc13783, mode, channel, priv->sample);
+
+ for (i = 0; i < 4; i++) {
+ res[i] = (priv->sample[i] >> 2) & 0x3ff;
+ res[i + 4] = (priv->sample[i] >> 14) & 0x3ff;
+ }
+
+ if (mode == MC13783_ADC_MODE_SINGLE_CHAN) {
+ for (i = 0; i < 8; i++) {
+ c += sprintf(buf, "%u%s", res[i], i < 7 ? ", " : "\n");
+ buf += c;
+ }
+ }
+
+ if (mode == MC13783_ADC_MODE_MULT_CHAN)
+ c = sprintf(res_print, "%u\n", res[channel & 0x7]);
+
+ return c;
+}
+
+static struct sensor_device_attribute mc13783_adc_ctl[] = {
+ SENSOR_ATTR(name, S_IRUGO, mc13783_adc_show_name, NULL, 0),
+ SENSOR_ATTR(read_mode, S_IWUSR | S_IRUGO,
+ mc13783_adc_show_read_mode, mc13783_adc_set_read_mode, 0),
+
+ SENSOR_ATTR(in0_input, S_IRUGO, mc13783_adc_read, NULL, 0),
+ SENSOR_ATTR(in1_input, S_IRUGO, mc13783_adc_read, NULL, 1),
+ SENSOR_ATTR(in2_input, S_IRUGO, mc13783_adc_read, NULL, 2),
+ SENSOR_ATTR(in3_input, S_IRUGO, mc13783_adc_read, NULL, 3),
+ SENSOR_ATTR(in4_input, S_IRUGO, mc13783_adc_read, NULL, 4),
+ SENSOR_ATTR(in5_input, S_IRUGO, mc13783_adc_read, NULL, 5),
+ SENSOR_ATTR(in6_input, S_IRUGO, mc13783_adc_read, NULL, 6),
+ SENSOR_ATTR(in7_input, S_IRUGO, mc13783_adc_read, NULL, 7),
+ SENSOR_ATTR(in8_input, S_IRUGO, mc13783_adc_read, NULL, 8),
+ SENSOR_ATTR(in9_input, S_IRUGO, mc13783_adc_read, NULL, 9),
+ SENSOR_ATTR(in10_input, S_IRUGO, mc13783_adc_read, NULL, 10),
+ SENSOR_ATTR(in11_input, S_IRUGO, mc13783_adc_read, NULL, 11),
+ SENSOR_ATTR(in12_input, S_IRUGO, mc13783_adc_read, NULL, 12),
+ SENSOR_ATTR(in13_input, S_IRUGO, mc13783_adc_read, NULL, 13),
+ SENSOR_ATTR(in14_input, S_IRUGO, mc13783_adc_read, NULL, 14),
+ SENSOR_ATTR(in15_input, S_IRUGO, mc13783_adc_read, NULL, 15),
+};
+
+static int __init mc13783_adc_probe(struct platform_device *pdev)
+{
+ struct mc13783_adc_priv *priv;
+ int ret, i;
+ int entries = sizeof(mc13783_adc_ctl)
+ / sizeof(struct sensor_device_attribute);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mc13783 = pdev->dev.platform_data;
+ priv->conv_mode = MC13783_ADC_MODE_MULT_CHAN;
+
+ for (i = 0; i < entries; i++) {
+ ret = device_create_file(&pdev->dev,
+ &mc13783_adc_ctl[i].dev_attr);
+ if (ret) {
+ dev_err(&pdev->dev, "device_create_file failed.\n");
+ goto out_err;
+ }
+ }
+
+ priv->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(priv->hwmon_dev)) {
+ dev_err(&pdev->dev, "hwmon_device_register failed.\n");
+ ret = PTR_ERR(priv->hwmon_dev);
+ goto out_err;
+ }
+
+ mutex_init(&priv->adc_conv_lock);
+
+ /* unmask adcdone interrupts */
+ mc13783_set_bits(priv->mc13783, MC13783_REG_INTERRUPT_MASK_0,
+ MC13783_INT_MASK_ADCDONEM, 0);
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+
+out_err:
+ for (i--; i >= 0; i--)
+ device_remove_file(&pdev->dev, &mc13783_adc_ctl[i].dev_attr);
+
+ kfree(priv);
+
+ return ret;
+}
+
+static int __devexit mc13783_adc_remove(struct platform_device *pdev)
+{
+ struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
+ int entries = sizeof(mc13783_adc_ctl)
+ / sizeof(struct sensor_device_attribute);
+ int i;
+
+ hwmon_device_unregister(&pdev->dev);
+
+ for (i = 0; i < entries; i++)
+ device_remove_file(&pdev->dev, &mc13783_adc_ctl[i].dev_attr);
+
+ mutex_destroy(&priv->adc_conv_lock);
+
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_adc_driver = {
+ .probe = mc13783_adc_probe,
+ .remove = __devexit_p(mc13783_adc_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MC13783_ADC_NAME,
+ },
+};
+
+static int __init mc13783_adc_init(void)
+{
+ return platform_driver_register(&mc13783_adc_driver);
+}
+
+static void __exit mc13783_adc_exit(void)
+{
+ platform_driver_unregister(&mc13783_adc_driver);
+}
+
+module_init(mc13783_adc_init);
+module_exit(mc13783_adc_exit);
+
+MODULE_DESCRIPTION("MC13783 input touchscreen driver");
+MODULE_AUTHOR("Luotao Fu, <l.fu@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 7451cf07ab6..dbe7be3b140 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -416,6 +416,7 @@ config TOUCHSCREEN_USB_COMPOSITE
config TOUCHSCREEN_MC13783
tristate "Freescale MC13783 touchscreen input driver"
depends on MFD_MC13783
+ depends on SENSORS_MC13783_ADC
help
Say Y here if you have an Freescale MC13783 PMIC on your
board and want to use its touchscreen
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
index f9653dab4e8..efce05a17e5 100644
--- a/drivers/input/touchscreen/mc13783_ts.c
+++ b/drivers/input/touchscreen/mc13783_ts.c
@@ -22,77 +22,92 @@
#include <linux/input.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/mfd/mc13783-private.h>
#include <linux/delay.h>
+#include <linux/mfd/mc13783-private.h>
+#include <linux/mfd/mc13783.h>
+
+#define MC13783_TS_NAME "mc13783-ts"
-#define MC13783_NAME "mc13783-ts"
+static unsigned int sample_tolerance = 75;
+module_param(sample_tolerance, uint, S_IRUGO);
+MODULE_PARM_DESC(sample_tolerance,
+ "sample_tolerance: The device reads sequetial 3 values for"
+ "both x and y coordinates. In normal case the three values"
+ " should be similar to another. You can play with this value"
+ "if you to throw out errneous readings. default will be 75");
struct mc13783_ts_priv {
struct input_dev *idev;
struct mc13783 *mc13783;
struct delayed_work work;
+ struct workqueue_struct *workq;
+ unsigned int sample[4];
};
-static inline void mc13783_ts_start_conversion(struct mc13783_ts_priv *priv)
-{
- mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_0, 0x00033c00);
- mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_1, 0x00300409);
-}
-static inline void mc13783_ts_set_irq_mode(struct mc13783_ts_priv *priv)
-{
- mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_0, 0x00001c00);
- mc13783_reg_write(priv->mc13783, MC13783_REG_ADC_1, 0x00200001);
-}
-
-#define TS_MIN 80
+#define TS_MIN 1
#define TS_MAX 1000
static void mc13783_ts_handler(int irq, void *data)
{
struct mc13783_ts_priv *priv = data;
unsigned int sts;
- unsigned int sample[4];
- int i;
mc13783_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_STATUS_0, &sts);
mc13783_reg_write(priv->mc13783, MC13783_REG_INTERRUPT_STATUS_0,
- sts & 0xf);
+ sts & MC13783_INT_STAT_TSI);
- if (sts & 4)
- mc13783_ts_start_conversion(priv);
+ if (sts & MC13783_INT_STAT_TSI)
+ queue_work(priv->workq, &priv->work.work);
+}
- if (sts & 1) {
- int x, y, press = 0;
- for (i = 0; i < 4; i++)
- mc13783_reg_read(priv->mc13783, MC13783_REG_ADC_2,
- &sample[i]);
+static void mc13783_ts_report_sample(struct mc13783_ts_priv *priv)
+{
+ int x, x1, x2, y, y1, y2;
+ int press = 0;
+ unsigned int tol = sample_tolerance;
+
+ x = (priv->sample[2] >> 2) & 0x3ff;
+ x1 = (priv->sample[0] >> 2) & 0x3ff;
+ x2 = (priv->sample[1] >> 2) & 0x3ff;
+ y = (priv->sample[3] >> 2) & 0x3ff;
+ y1 = (priv->sample[0] >> 14) & 0x3ff;
+ y2 = (priv->sample[1] >> 14) & 0x3ff;
- x = (sample[2] >> 2) & 0x3ff;
- y = (sample[1] >> 14) & 0x3ff;
+ pr_debug("mc13783_ts: x %d, x1 %d, x2 %d, y %d, y1 %d, y2 %d"
+ "tolerance: %d\n",
+ x, x1, x2, y, y1, y2, sample_tolerance);
- pr_debug("mc13783_ts: x: %d y: %d\n", x, y);
+ if (x > TS_MIN && x < TS_MAX && y > TS_MIN && y < TS_MAX) {
- if (x > TS_MIN && x < TS_MAX && y > TS_MIN && y < TS_MAX) {
+ if ( abs(x-x1) < tol && abs(x-x2) < tol && abs(x1-x2) < tol
+ && abs(y-y1) < tol && abs(y-y2) < tol && abs(y1-y2) < tol ) {
press = 1;
input_report_abs(priv->idev, ABS_X, x);
input_report_abs(priv->idev, ABS_Y, y);
- schedule_delayed_work(&priv->work, HZ / 50);
} else
- mc13783_ts_set_irq_mode(priv);
+ pr_debug("XY Reading error!\n");
- input_report_abs(priv->idev, ABS_PRESSURE, press);
- input_sync(priv->idev);
+ queue_delayed_work(priv->workq, &priv->work, HZ / 50);
}
+ input_report_abs(priv->idev, ABS_PRESSURE, press);
+ input_sync(priv->idev);
}
-static void mc13783_work(struct work_struct *work)
+static void mc13783_ts_work(struct work_struct *work)
{
struct mc13783_ts_priv *priv =
container_of(work, struct mc13783_ts_priv, work.work);
+ unsigned int mode = MC13783_ADC_MODE_TS;
+ unsigned int channel = 12;
+ int ret;
- mc13783_ts_start_conversion(priv);
+ ret = mc13783_adc_do_conversion
+ (priv->mc13783, mode, channel, priv->sample);
+
+ if (! ret)
+ mc13783_ts_report_sample(priv);
}
static int __init mc13783_ts_probe(struct platform_device *pdev)
@@ -105,7 +120,7 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- priv->mc13783 = platform_get_drvdata(pdev);
+ priv->mc13783 = pdev->dev.platform_data;
idev = input_allocate_device();
if (!idev) {
@@ -114,7 +129,7 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
}
priv->idev = idev;
- idev->name = MC13783_NAME;
+ idev->name = MC13783_TS_NAME;
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
idev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) |
@@ -122,7 +137,7 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, priv);
- ret = mc13783_register_irq(priv->mc13783, MC13783_IRQ_ADC,
+ ret = mc13783_register_irq(priv->mc13783, MC13783_IRQ_TS,
mc13783_ts_handler, priv);
if (ret)
goto err_failed_irq;
@@ -134,15 +149,21 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)
goto err_failed_register;
}
- mc13783_set_bits(priv->mc13783, MC13783_REG_INTERRUPT_MASK_0, 0xf, 0);
+ /* unmask the ts wakeup interrupt */
+ mc13783_set_bits(priv->mc13783, MC13783_REG_INTERRUPT_MASK_0,
+ MC13783_INT_MASK_TSM, 0);
+
+ mc13783_adc_set_ts_status(priv->mc13783, 1);
- INIT_DELAYED_WORK(&priv->work, mc13783_work);
- schedule_delayed_work(&priv->work, HZ / 20);
+ INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);
+
+ priv->workq = create_singlethread_workqueue("mc13783_ts");
+ queue_delayed_work(priv->workq, &priv->work, HZ / 20);
return 0;
err_failed_register:
- mc13783_free_irq(priv->mc13783, MC13783_IRQ_ADC);
+ mc13783_free_irq(priv->mc13783, MC13783_IRQ_TS);
err_failed_irq:
input_free_device(priv->idev);
err_input_alloc:
@@ -155,10 +176,16 @@ static int __devexit mc13783_ts_remove(struct platform_device *pdev)
{
struct mc13783_ts_priv *priv = platform_get_drvdata(pdev);
- mc13783_free_irq(priv->mc13783, MC13783_IRQ_ADC);
+ mc13783_adc_set_ts_status(priv->mc13783, 0);
+
+ mc13783_free_irq(priv->mc13783, MC13783_IRQ_TS);
+
cancel_delayed_work(&priv->work);
+ destroy_workqueue(priv->workq);
+
input_unregister_device(priv->idev);
input_free_device(priv->idev);
+
kfree(priv);
return 0;
@@ -169,7 +196,7 @@ static struct platform_driver mc13783_ts_driver = {
.remove = __devexit_p(mc13783_ts_remove),
.driver = {
.owner = THIS_MODULE,
- .name = MC13783_NAME,
+ .name = MC13783_TS_NAME,
},
};
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 7262822855c..9633d149acd 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -46,3 +46,4 @@ obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o
obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
+
diff --git a/drivers/mfd/mc13783-core.c b/drivers/mfd/mc13783-core.c
index 00b1e20aba8..f8ccafeaf82 100644
--- a/drivers/mfd/mc13783-core.c
+++ b/drivers/mfd/mc13783-core.c
@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/completion.h>
#include <linux/spi/spi.h>
#include <linux/mfd/mc13783.h>
#include <linux/mfd/mc13783-private.h>
@@ -168,6 +169,15 @@ static void mc13783_irq_work(struct work_struct *work)
{
struct mc13783 *mc13783 = container_of(work, struct mc13783, work);
int i;
+ unsigned int adc_sts;
+
+ /* check if the adc has finished any completion */
+ mc13783_reg_read(mc13783, MC13783_REG_INTERRUPT_STATUS_0, &adc_sts);
+ mc13783_reg_write(mc13783, MC13783_REG_INTERRUPT_STATUS_0,
+ adc_sts & MC13783_INT_STAT_ADCDONEI);
+
+ if (adc_sts & MC13783_INT_STAT_ADCDONEI)
+ complete_all(&mc13783->adc_done);
for (i = 0; i < MC13783_NUM_IRQ; i++)
if (mc13783->irq_handler[i].handler)
@@ -230,7 +240,7 @@ static void mc13783_client_dev_register(struct mc13783 *mc13783,
}
(*pdev)->dev.parent = mc13783->dev;
- platform_set_drvdata(*pdev, mc13783);
+ (*pdev)->dev.platform_data = mc13783;
ret = platform_device_add(*pdev);
if (ret != 0) {
dev_err(mc13783->dev, "Failed to register %s: %d\n", name, ret);
@@ -261,6 +271,7 @@ static int __devinit mc13783_probe(struct spi_device *spi)
INIT_WORK(&mc13783->work, mc13783_irq_work);
mutex_init(&mc13783->io_lock);
+ init_completion(&mc13783->adc_done);
if (pdata && pdata->init)
pdata->init(mc13783);
@@ -286,6 +297,8 @@ static int __devinit mc13783_probe(struct spi_device *spi)
&mc13783->codec);
mc13783_client_dev_register(mc13783, "mc13783-ts",
&mc13783->ts);
+ mc13783_client_dev_register(mc13783, "mc13783-adc",
+ &mc13783->adc);
return 0;
@@ -305,6 +318,7 @@ static int __devexit mc13783_remove(struct spi_device *spi)
platform_device_unregister(mc13783->ts);
platform_device_unregister(mc13783->codec);
+ platform_device_unregister(mc13783->adc);
if (pdata && pdata->exit)
pdata->exit();
diff --git a/include/linux/mfd/mc13783-private.h b/include/linux/mfd/mc13783-private.h
index 5db77c863fe..b307ee82e81 100644
--- a/include/linux/mfd/mc13783-private.h
+++ b/include/linux/mfd/mc13783-private.h
@@ -33,9 +33,13 @@ struct mc13783_irq {
};
#define MC13783_NUM_IRQ 2
-#define MC13783_IRQ_ADC 0
+#define MC13783_IRQ_TS 0
#define MC13783_IRQ_REGULATOR 1
+#define MC13783_ADC_MODE_TS 1
+#define MC13783_ADC_MODE_SINGLE_CHAN 2
+#define MC13783_ADC_MODE_MULT_CHAN 3
+
struct mc13783 {
int revision;
struct device *dev;
@@ -51,9 +55,12 @@ struct mc13783 {
struct platform_device regulators[30];
struct platform_device *codec;
struct platform_device *ts;
+ struct platform_device *rtc;
+ struct platform_device *adc;
struct mc13783_irq irq_handler[MC13783_NUM_IRQ];
struct work_struct work;
+ struct completion adc_done;
};
int mc13783_reg_read(struct mc13783 *, int reg_num, u32 *);
@@ -131,6 +138,56 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
/*
+ * Interrupt Status
+ */
+
+#define MC13783_INT_STAT_ADCDONEI (1 << 0)
+#define MC13783_INT_STAT_ADCBISDONEI (1 << 1)
+#define MC13783_INT_STAT_TSI (1 << 2)
+#define MC13783_INT_STAT_WHIGHI (1 << 3)
+#define MC13783_INT_STAT_WLOWI (1 << 4)
+#define MC13783_INT_STAT_CHGDETI (1 << 6)
+#define MC13783_INT_STAT_CHGOVI (1 << 7)
+#define MC13783_INT_STAT_CHGREVI (1 << 8)
+#define MC13783_INT_STAT_CHGSHORTI (1 << 9)
+#define MC13783_INT_STAT_CCCVI (1 << 10)
+#define MC13783_INT_STAT_CHGCURRI (1 << 11)
+#define MC13783_INT_STAT_BPONI (1 << 12)
+#define MC13783_INT_STAT_LOBATLI (1 << 13)
+#define MC13783_INT_STAT_LOBATHI (1 << 14)
+#define MC13783_INT_STAT_UDPI (1 << 15)
+#define MC13783_INT_STAT_USBI (1 << 16)
+#define MC13783_INT_STAT_IDI (1 << 19)
+#define MC13783_INT_STAT_Unused (1 << 20)
+#define MC13783_INT_STAT_SE1I (1 << 21)
+#define MC13783_INT_STAT_CKDETI (1 << 22)
+#define MC13783_INT_STAT_UDMI (1 << 23)
+
+/*
+ * Interrup Mask
+ */
+
+#define MC13783_INT_MASK_ADCDONEM (1 << 0)
+#define MC13783_INT_MASK_ADCBISDONEM (1 << 1)
+#define MC13783_INT_MASK_TSM (1 << 2)
+#define MC13783_INT_MASK_WHIGHM (1 << 3)
+#define MC13783_INT_MASK_WLOWM (1 << 4)
+#define MC13783_INT_MASK_CHGDETM (1 << 6)
+#define MC13783_INT_MASK_CHGOVM (1 << 7)
+#define MC13783_INT_MASK_CHGREVM (1 << 8)
+#define MC13783_INT_MASK_CHGSHORTM (1 << 9)
+#define MC13783_INT_MASK_CCCVM (1 << 10)
+#define MC13783_INT_MASK_CHGCURRM (1 << 11)
+#define MC13783_INT_MASK_BPONM (1 << 12)
+#define MC13783_INT_MASK_LOBATLM (1 << 13)
+#define MC13783_INT_MASK_LOBATHM (1 << 14)
+#define MC13783_INT_MASK_UDPM (1 << 15)
+#define MC13783_INT_MASK_USBM (1 << 16)
+#define MC13783_INT_MASK_IDM (1 << 19)
+#define MC13783_INT_MASK_SE1M (1 << 21)
+#define MC13783_INT_MASK_CKDETM (1 << 22)
+
+/*
* Reg Regulator Mode 0
*/
#define MC13783_REGCTRL_VAUDIO_EN (1 << 0)
@@ -229,4 +286,112 @@ int mc13783_register_irq(struct mc13783 *mc13783, int irq,
#define MC13783_SWCTRL_SW3_STBY (1 << 21)
#define MC13783_SWCTRL_SW3_MODE (1 << 22)
+/*
+ * ADC/Touch
+ */
+#define MC13783_ADC0_LICELLCON (1 << 0)
+#define MC13783_ADC0_CHRGICON (1 << 1)
+#define MC13783_ADC0_BATICON (1 << 2)
+#define MC13783_ADC0_RTHEN (1 << 3)
+#define MC13783_ADC0_DTHEN (1 << 4)
+#define MC13783_ADC0_UIDEN (1 << 5)
+#define MC13783_ADC0_ADOUTEN (1 << 6)
+#define MC13783_ADC0_ADOUTPER (1 << 7)
+#define MC13783_ADC0_ADREFEN (1 << 10)
+#define MC13783_ADC0_ADREFMODE (1 << 11)
+#define MC13783_ADC0_TSMOD0 (1 << 12)
+#define MC13783_ADC0_TSMOD1 (1 << 13)
+#define MC13783_ADC0_TSMOD2 (1 << 14)
+#define MC13783_ADC0_CHRGRAWDIV (1 << 15)
+#define MC13783_ADC0_ADINC1 (1 << 16)
+#define MC13783_ADC0_ADINC2 (1 << 17)
+#define MC13783_ADC0_WCOMP (1 << 18)
+#define MC13783_ADC0_ADCBIS0 (1 << 23)
+
+#define MC13783_ADC1_ADEN (1 << 0)
+#define MC13783_ADC1_RAND (1 << 1)
+#define MC13783_ADC1_ADSEL (1 << 3)
+#define MC13783_ADC1_TRIGMASK (1 << 4)
+#define MC13783_ADC1_ADA10 (1 << 5)
+#define MC13783_ADC1_ADA11 (1 << 6)
+#define MC13783_ADC1_ADA12 (1 << 7)
+#define MC13783_ADC1_ADA20 (1 << 8)
+#define MC13783_ADC1_ADA21 (1 << 9)
+#define MC13783_ADC1_ADA22 (1 << 10)
+#define MC13783_ADC1_ATO0 (1 << 11)
+#define MC13783_ADC1_ATO1 (1 << 12)
+#define MC13783_ADC1_ATO2 (1 << 13)
+#define MC13783_ADC1_ATO3 (1 << 14)
+#define MC13783_ADC1_ATO4 (1 << 15)
+#define MC13783_ADC1_ATO5 (1 << 16)
+#define MC13783_ADC1_ATO6 (1 << 17)
+#define MC13783_ADC1_ATO7 (1 << 18)
+#define MC13783_ADC1_ATOX (1 << 19)
+#define MC13783_ADC1_ASC (1 << 20)
+#define MC13783_ADC1_ADTRIGIGN (1 << 21)
+#define MC13783_ADC1_ADONESHOT (1 << 22)
+#define MC13783_ADC1_ADCBIS1 (1 << 23)
+
+#define MC13783_ADC1_CHAN0_SHIFT 5
+#define MC13783_ADC1_CHAN1_SHIFT 8
+
+#define MC13783_ADC2_ADD10 (1 << 2)
+#define MC13783_ADC2_ADD11 (1 << 3)
+#define MC13783_ADC2_ADD12 (1 << 4)
+#define MC13783_ADC2_ADD13 (1 << 5)
+#define MC13783_ADC2_ADD14 (1 << 6)
+#define MC13783_ADC2_ADD15 (1 << 7)
+#define MC13783_ADC2_ADD16 (1 << 8)
+#define MC13783_ADC2_ADD17 (1 << 9)
+#define MC13783_ADC2_ADD18 (1 << 10)
+#define MC13783_ADC2_ADD19 (1 << 11)
+#define MC13783_ADC2_ADD20 (1 << 14)
+#define MC13783_ADC2_ADD21 (1 << 15)
+#define MC13783_ADC2_ADD22 (1 << 16)
+#define MC13783_ADC2_ADD23 (1 << 17)
+#define MC13783_ADC2_ADD24 (1 << 18)
+#define MC13783_ADC2_ADD25 (1 << 19)
+#define MC13783_ADC2_ADD26 (1 << 20)
+#define MC13783_ADC2_ADD27 (1 << 21)
+#define MC13783_ADC2_ADD28 (1 << 22)
+#define MC13783_ADC2_ADD29 (1 << 23)
+
+#define MC13783_ADC3_WHIGH0 (1 << 0)
+#define MC13783_ADC3_WHIGH1 (1 << 1)
+#define MC13783_ADC3_WHIGH2 (1 << 2)
+#define MC13783_ADC3_WHIGH3 (1 << 3)
+#define MC13783_ADC3_WHIGH4 (1 << 4)
+#define MC13783_ADC3_WHIGH5 (1 << 5)
+#define MC13783_ADC3_ICID0 (1 << 6)
+#define MC13783_ADC3_ICID1 (1 << 7)
+#define MC13783_ADC3_ICID2 (1 << 8)
+#define MC13783_ADC3_WLOW0 (1 << 9)
+#define MC13783_ADC3_WLOW1 (1 << 10)
+#define MC13783_ADC3_WLOW2 (1 << 11)
+#define MC13783_ADC3_WLOW3 (1 << 12)
+#define MC13783_ADC3_WLOW4 (1 << 13)
+#define MC13783_ADC3_WLOW5 (1 << 14)
+#define MC13783_ADC3_ADCBIS2 (1 << 23)
+
+#define MC13783_ADC4_ADDBIS10 (1 << 2)
+#define MC13783_ADC4_ADDBIS11 (1 << 3)
+#define MC13783_ADC4_ADDBIS12 (1 << 4)
+#define MC13783_ADC4_ADDBIS13 (1 << 5)
+#define MC13783_ADC4_ADDBIS14 (1 << 6)
+#define MC13783_ADC4_ADDBIS15 (1 << 7)
+#define MC13783_ADC4_ADDBIS16 (1 << 8)
+#define MC13783_ADC4_ADDBIS17 (1 << 9)
+#define MC13783_ADC4_ADDBIS18 (1 << 10)
+#define MC13783_ADC4_ADDBIS19 (1 << 11)
+#define MC13783_ADC4_ADDBIS20 (1 << 14)
+#define MC13783_ADC4_ADDBIS21 (1 << 15)
+#define MC13783_ADC4_ADDBIS22 (1 << 16)
+#define MC13783_ADC4_ADDBIS23 (1 << 17)
+#define MC13783_ADC4_ADDBIS24 (1 << 18)
+#define MC13783_ADC4_ADDBIS25 (1 << 19)
+#define MC13783_ADC4_ADDBIS26 (1 << 20)
+#define MC13783_ADC4_ADDBIS27 (1 << 21)
+#define MC13783_ADC4_ADDBIS28 (1 << 22)
+#define MC13783_ADC4_ADDBIS29 (1 << 23)
+
#endif
diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h
index db4c0e8304c..968733383f1 100644
--- a/include/linux/mfd/mc13783.h
+++ b/include/linux/mfd/mc13783.h
@@ -11,6 +11,11 @@ struct mc13783_platform_data {
int mc13783_register_regulator(struct device *, int, struct regulator_init_data *);
+int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode,
+ unsigned int channel, unsigned int *sample);
+
+void mc13783_adc_set_ts_status(struct mc13783 *mc13783, unsigned int status);
+
#define SW_SW1A 0
#define SW_SW1B 1
#define SW_SW2A 2
diff --git a/sound/arm/mc13783.c b/sound/arm/mc13783.c
index a0adce8168c..be7b065d865 100644
--- a/sound/arm/mc13783.c
+++ b/sound/arm/mc13783.c
@@ -537,7 +537,7 @@ static int mc13783_codec_probe(struct platform_device *pdev)
if (mc13783)
return -EBUSY;
- mc13783 = platform_get_drvdata(pdev);
+ mc13783 = pdev->dev.platform_data;
return 0;
}