[PATCH] [RFC] Use ADC API

Nelson Castillo arhuaco at freaks-unidos.net
Wed Mar 11 23:12:02 CET 2009


From: Nelson Castillo <arhuaco at freaks-unidos.net>

This is a small fun patch that makes the TS driver use the s3c-adc
driver. This is something we will have to do in order to get the
driver upstream.

This patch is a proof of concept only and should not be applied.

It works but it is slow because we schedule conversions one by one. It
could be improved in different ways. The following two come to my mind
now:

1) As for conversion in quantums (say of 10) and discard a few if we
don't get to need them.

2) Add a method to the TS filters called "minimum_needed" that helps
the driver estimate how many points to ask for.

If we use the ADC API we should play nice with other devices
that might want to use the ADC thus the quantum approach should be
good.

(1) and (2) could be used together.

Signed-off-by: Nelson Castillo <arhuaco at freaks-unidos.net>
---

 0 files changed, 0 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2442/mach-gta02.c b/arch/arm/mach-s3c2442/mach-gta02.c
index 229271b..430d04d 100644
--- a/arch/arm/mach-s3c2442/mach-gta02.c
+++ b/arch/arm/mach-s3c2442/mach-gta02.c
@@ -1666,6 +1666,8 @@ static void __init gta02_machine_init(void)
 	gta02_vibrator_dev.dev.parent = &s3c24xx_pwm_device.dev; 
 	platform_device_register(&gta02_vibrator_dev);
 #endif
+
+	platform_device_register(&s3c_device_adc);
 }
 
 void DEBUG_LED(int n)
diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c
index 23d7e28..850bcbb 100644
--- a/drivers/input/touchscreen/s3c2410_ts.c
+++ b/drivers/input/touchscreen/s3c2410_ts.c
@@ -64,6 +64,7 @@
 #include <mach/ts.h>
 #include <mach/hardware.h>
 #include <plat/regs-adc.h>
+#include <plat/adc.h>
 
 #include "ts_filter_chain.h"
 
@@ -110,6 +111,7 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen";
 
 struct s3c2410ts {
 	struct input_dev *dev;
+	struct s3c_adc_client *adc_client;
 	struct ts_filter_chain *chain;
 	int is_down;
 	int state;
@@ -132,12 +134,46 @@ static inline void s3c2410_ts_connect(void)
 	s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);
 }
 
-static void s3c2410_ts_start_adc_conversion(void)
+static void ts_adc_timer_f(unsigned long data);
+static struct timer_list ts_adc_timer = TIMER_INITIALIZER(ts_adc_timer_f, 0, 0);
+
+static void ts_adc_timer_f(unsigned long data)
 {
 	writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,
 	       base_addr + S3C2410_ADCTSC);
-	writel(readl(base_addr + S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,
-	       base_addr + S3C2410_ADCCON);
+
+	/*
+	 * TODO:
+	 * We can make things much faster asking for more points.
+	 * Let's think of a good way for doing it.
+	 */
+	if (s3c_adc_start(ts.adc_client, 0, 1))
+		mod_timer(&ts_adc_timer, jiffies + 1);
+}
+
+static unsigned adc_selected; /* FIXME: Move to ts data. */
+void adc_selected_f(unsigned selected)
+{
+	adc_selected = selected;
+}
+
+static void s3c2410_ts_start_adc_conversion(void)
+{
+	if (adc_selected) {
+		mod_timer(&ts_adc_timer, jiffies + 1);
+		return;
+	}
+	do {
+		/*
+		 * FIXME:
+		 * This busy waiting doesn't make sense.
+		 * If another driver schedules a lot of
+		 * conversions we are going to stall.
+		 */
+		if (!s3c_adc_start(ts.adc_client, 0, 1))
+			return;
+		msleep(1);
+	} while (1);
 }
 
 /*
@@ -208,8 +244,12 @@ static void event_send_timer_f(unsigned long data)
 			break;
 
 		case 'P':
-			if (ts.is_down) /* stylus_action needs a conversion */
-				s3c2410_ts_start_adc_conversion();
+			/*
+			 * TODO: We cannot call ts3c2410_ts_start_adc_conversion
+			 * because it could msleep. Right?
+			 */
+			if (ts.is_down) /* Stylus_action needs a conversion. */
+				mod_timer(&ts_adc_timer, jiffies);
 
 			if (unlikely(__kfifo_get(ts.event_fifo,
 						 (unsigned char *)buf,
@@ -264,7 +304,7 @@ static irqreturn_t stylus_updown(int irq, void *dev_id)
 	data1 = readl(base_addr+S3C2410_ADCDAT1);
 
 	ts.is_down = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) &&
-					    (!(data1 & S3C2410_ADCDAT0_UPDOWN));
+		     (!(data1 & S3C2410_ADCDAT0_UPDOWN));
 
 	event_type = ts.is_down ? 'D' : 'U';
 
@@ -282,21 +322,24 @@ static irqreturn_t stylus_updown(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t stylus_action(int irq, void *dev_id)
+static void stylus_adc_action(unsigned data0, unsigned data1)
 {
 	int buf[3];
 
-	/* Grab the ADC results. */
-	buf[1] = readl(base_addr + S3C2410_ADCDAT0) &
-		       S3C2410_ADCDAT0_XPDATA_MASK;
-	buf[2] = readl(base_addr + S3C2410_ADCDAT1) &
-		       S3C2410_ADCDAT1_YPDATA_MASK;
+	if (data0 & S3C2410_ADCDAT0_AUTO_PST ||
+	    data1 & S3C2410_ADCDAT1_AUTO_PST) {
+		/* FIXME: Do we need this? */
+		return;
+	}
+
+	buf[1] = data0; /* TODO: & S3C2410_ADCDAT0_XPDATA_MASK */
+	buf[2] = data1; /* TODO: & S3C2410_ADCDAT1_YPDATA_MASK */
 
 	switch (ts_filter_chain_feed(ts.chain, &buf[1])) {
 	case 0:
 		/* The filter wants more points. */
 		s3c2410_ts_start_adc_conversion();
-		return IRQ_HANDLED;
+		return;
 	case 1:
 		/* We have a point from the filters or no filtering enabled. */
 		buf[0] = 'P';
@@ -309,7 +352,7 @@ static irqreturn_t stylus_action(int irq, void *dev_id)
 		/* Error. Ignore the event. */
 		ts_filter_chain_clear(ts.chain);
 		writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
-		return IRQ_HANDLED;
+		return;
 	};
 
 	if (unlikely(__kfifo_put(ts.event_fifo, (unsigned char *)buf,
@@ -319,11 +362,9 @@ static irqreturn_t stylus_action(int irq, void *dev_id)
 	writel(WAIT4INT(1), base_addr + S3C2410_ADCTSC);
 	mod_timer(&event_send_timer, jiffies + 1);
 
-	return IRQ_HANDLED;
+	return;
 }
 
-static struct clk	*adc_clock;
-
 /*
  * The functions for inserting/removing us as a module.
  */
@@ -349,17 +390,6 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
 	printk(DEBUG_LVL "Entering s3c2410ts_init\n");
 #endif
 
-	adc_clock = clk_get(NULL, "adc");
-	if (!adc_clock) {
-		dev_err(&pdev->dev, "failed to get adc clock source\n");
-		return -ENOENT;
-	}
-	clk_enable(adc_clock);
-
-#ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
-	printk(DEBUG_LVL "got and enabled clock\n");
-#endif
-
 	base_addr = ioremap(S3C2410_PA_ADC,0x20);
 	if (base_addr == NULL) {
 		dev_err(&pdev->dev, "Failed to remap register block\n");
@@ -367,28 +397,26 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
 		goto bail0;
 	}
 
-
 	/* If we acutally are a S3C2410: Configure GPIOs */
 	if (!strcmp(pdev->name, "s3c2410-ts"))
 		s3c2410_ts_connect();
 
-	if ((info->presc & 0xff) > 0)
-		writel(S3C2410_ADCCON_PRSCEN |
-		       S3C2410_ADCCON_PRSCVL(info->presc&0xFF),
-						    base_addr + S3C2410_ADCCON);
-	else
-		writel(0, base_addr+S3C2410_ADCCON);
-
-	/* Initialise registers */
-	if ((info->delay & 0xffff) > 0)
-		writel(info->delay & 0xffff,  base_addr + S3C2410_ADCDLY);
-
 	writel(WAIT4INT(0), base_addr + S3C2410_ADCTSC);
 
 	/* Initialise input stuff */
 	memset(&ts, 0, sizeof(struct s3c2410ts));
-	input_dev = input_allocate_device();
 
+	ts.adc_client =
+		s3c_adc_register(pdev, adc_selected_f, stylus_adc_action, 1);
+	if (!ts.adc_client) {
+		dev_err(&pdev->dev,
+			"Unable to register s3c2410_ts as s3_adc client\n");
+		iounmap(base_addr);
+		ret = -EIO;
+		goto bail0;
+	}
+
+	input_dev = input_allocate_device();
 	if (!input_dev) {
 		dev_err(&pdev->dev, "Unable to allocate the input device\n");
 		ret = -ENOMEM;
@@ -423,18 +451,10 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
 
 	ts_filter_chain_clear(ts.chain);
 
-	/* Get irqs */
-	if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM,
-						    "s3c2410_action", ts.dev)) {
-		dev_err(&pdev->dev, "Could not allocate ts IRQ_ADC !\n");
-		iounmap(base_addr);
-		ret = -EIO;
-		goto bail3;
-	}
+	/* Get IRQ. */
 	if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,
 			"s3c2410_action", ts.dev)) {
 		dev_err(&pdev->dev, "Could not allocate ts IRQ_TC !\n");
-		free_irq(IRQ_ADC, ts.dev);
 		iounmap(base_addr);
 		ret = -EIO;
 		goto bail4;
@@ -445,6 +465,7 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
 	/* All went ok, so register to the input system */
 	rc = input_register_device(ts.dev);
 	if (rc) {
+		dev_info(&pdev->dev, "Could not register input device\n");
 		ret = -EIO;
 		goto bail5;
 	}
@@ -453,13 +474,9 @@ static int __init s3c2410ts_probe(struct platform_device *pdev)
 
 bail5:
 	free_irq(IRQ_TC, ts.dev);
-	free_irq(IRQ_ADC, ts.dev);
-	clk_disable(adc_clock);
 	iounmap(base_addr);
 	disable_irq(IRQ_TC);
 bail4:
-	disable_irq(IRQ_ADC);
-bail3:
 	ts_filter_chain_destroy(ts.chain);
 	kfifo_free(ts.event_fifo);
 bail2:
@@ -473,16 +490,8 @@ bail0:
 
 static int s3c2410ts_remove(struct platform_device *pdev)
 {
-	disable_irq(IRQ_ADC);
 	disable_irq(IRQ_TC);
 	free_irq(IRQ_TC,ts.dev);
-	free_irq(IRQ_ADC,ts.dev);
-
-	if (adc_clock) {
-		clk_disable(adc_clock);
-		clk_put(adc_clock);
-		adc_clock = NULL;
-	}
 
 	input_unregister_device(ts.dev);
 	iounmap(base_addr);
@@ -501,11 +510,8 @@ static int s3c2410ts_suspend(struct platform_device *pdev, pm_message_t state)
 	writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_STDBM,
 	       base_addr+S3C2410_ADCCON);
 
-	disable_irq(IRQ_ADC);
 	disable_irq(IRQ_TC);
 
-	clk_disable(adc_clock);
-
 	return 0;
 }
 
@@ -514,12 +520,10 @@ static int s3c2410ts_resume(struct platform_device *pdev)
 	struct s3c2410_ts_mach_info *info =
 		( struct s3c2410_ts_mach_info *)pdev->dev.platform_data;
 
-	clk_enable(adc_clock);
 	mdelay(1);
 
 	ts_filter_chain_clear(ts.chain);
 
-	enable_irq(IRQ_ADC);
 	enable_irq(IRQ_TC);
 
 	if ((info->presc&0xff) > 0)




More information about the openmoko-kernel mailing list