diff options
author | Andy Green <agreen@pads.home.warmcat.com> | 2008-11-22 09:05:43 +0000 |
---|---|---|
committer | Andy Green <agreen@pads.home.warmcat.com> | 2008-11-22 09:05:43 +0000 |
commit | b66aa08df846bbbbc41d21cd42052d4c96b84668 (patch) | |
tree | 9d796b023ac4c0e6dff0e317d499faf034595a18 /drivers | |
parent | c08433d1b54a2cadc7061a2bd68fbf1a4d42fcca (diff) |
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ar6000/hif/hif2.c | 70 | ||||
-rw-r--r-- | drivers/input/touchscreen/s3c2410_ts.c | 102 | ||||
-rw-r--r-- | drivers/input/touchscreen/ts_filter.c | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/ts_filter_mean.c | 6 | ||||
-rw-r--r-- | drivers/input/touchscreen/ts_filter_median.c | 3 |
5 files changed, 108 insertions, 76 deletions
diff --git a/drivers/ar6000/hif/hif2.c b/drivers/ar6000/hif/hif2.c index 6f2030777c3..8d3095ff1c5 100644 --- a/drivers/ar6000/hif/hif2.c +++ b/drivers/ar6000/hif/hif2.c @@ -36,7 +36,6 @@ * KNOWN BUGS: * * - HIF_DEVICE_IRQ_ASYNC_SYNC doesn't work yet (gets MMC errors) - * - driver doesn't remove cleanly yet * - latency can reach hundreds of ms, probably because of scheduling delays * - packets go through about three queues before finally hitting the network */ @@ -111,6 +110,11 @@ struct hif_request { static HTC_CALLBACKS htcCallbacks; +/* + * shutdown_lock prevents recursion through HIFShutDownDevice + */ +static DEFINE_MUTEX(shutdown_lock); + /* ----- Request processing ------------------------------------------------ */ @@ -521,16 +525,21 @@ static void sdio_ar6000_remove(struct sdio_func *func) HIF_DEVICE *hif = sdio_get_drvdata(func); int ret; -#if 0 - /* - * Funny, Atheros' HIF does this call, but this just puts us in a - * recursion through HTCShutDown/HIFShutDown if unloading the - * module. - */ - ret = htcCallbacks.deviceRemovedHandler(hif->htc_handle, A_OK); - if (ret != A_OK) - dev_err(dev, "deviceRemovedHandler: %d\n", ret); -#endif + dev_dbg(dev, "sdio_ar6000_remove\n"); + if (mutex_trylock(&shutdown_lock)) { + /* + * Funny, Atheros' HIF does this call, but this just puts us in + * a recursion through HTCShutDown/HIFShutDown if unloading the + * module. + * + * However, we need it for suspend/resume. See the comment at + * HIFShutDown, below. + */ + ret = htcCallbacks.deviceRemovedHandler(hif->htc_handle, A_OK); + if (ret != A_OK) + dev_err(dev, "deviceRemovedHandler: %d\n", ret); + mutex_unlock(&shutdown_lock); + } wait_queue_empty(hif); ret = kthread_stop(hif->io_task); if (ret) @@ -591,8 +600,45 @@ int HIFInit(HTC_CALLBACKS *callbacks) } +/* + * We have three possible call chains here: + * + * System shutdown/reboot: + * + * kernel_restart_prepare ...> device_shutdown ... > s3cmci_shutdown -> + * mmc_remove_host ..> sdio_bus_remove -> sdio_ar6000_remove -> + * deviceRemovedHandler (HTCTargetRemovedHandler) -> HIFShutDownDevice + * + * This is roughly the same sequence as suspend, described below. + * + * Module removal: + * + * sys_delete_module -> ar6000_cleanup_module -> HTCShutDown -> + * HIFShutDownDevice -> sdio_unregister_driver ...> sdio_bus_remove -> + * sdio_ar6000_remove + * + * In this case, HIFShutDownDevice must call sdio_unregister_driver to + * notify the driver about its removal. sdio_ar6000_remove must not call + * deviceRemovedHandler, because that would loop back into HIFShutDownDevice. + * + * Suspend: + * + * device_suspend ...> s3cmci_suspend ...> sdio_bus_remove -> + * sdio_ar6000_remove -> deviceRemovedHandler (HTCTargetRemovedHandler) -> + * HIFShutDownDevice + * + * We must call deviceRemovedHandler to inform the ar6k stack that the device + * has been removed. Since HTCTargetRemovedHandler calls back into + * HIFShutDownDevice, we must also prevent the call to + * sdio_unregister_driver, or we'd end up recursing into the SDIO stack, + * eventually deadlocking somewhere. + */ + void HIFShutDownDevice(HIF_DEVICE *hif) { /* Beware, HTCShutDown calls us with hif == NULL ! */ - sdio_unregister_driver(&sdio_ar6000_driver); + if (mutex_trylock(&shutdown_lock)) { + sdio_unregister_driver(&sdio_ar6000_driver); + mutex_unlock(&shutdown_lock); + } } diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 8f0afc3e023..8e580a833e5 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -98,9 +98,10 @@ static char *s3c2410ts_name = "s3c2410 TouchScreen"; struct s3c2410ts { struct input_dev *dev; - int flag_first_touch_sent; struct ts_filter *tsf[MAX_TS_FILTER_CHAIN]; int coords[2]; /* just X and Y for us */ + int is_down; + int need_to_send_first_touch; }; static struct s3c2410ts ts; @@ -116,69 +117,58 @@ static inline void s3c2410_ts_connect(void) s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON); } -static void touch_timer_fire(unsigned long data) -{ - unsigned long data0; - unsigned long data1; - int updown; - - data0 = readl(base_addr + S3C2410_ADCDAT0); - data1 = readl(base_addr + S3C2410_ADCDAT1); +enum ts_input_event {IE_DOWN = 0, IE_UP, IE_UPDATE}; - updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && - (!(data1 & S3C2410_ADCDAT0_UPDOWN)); - - // if we need to send an untouch event, but we haven't yet sent the - // touch event (this happens if the touchscreen was tapped lightly), - // send the touch event first - if (!updown && !ts.flag_first_touch_sent) { - if (ts.tsf[0]) - (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]); +static void ts_input_report(int event) +{ + if (event == IE_DOWN || event == IE_UPDATE) { input_report_abs(ts.dev, ABS_X, ts.coords[0]); input_report_abs(ts.dev, ABS_Y, ts.coords[1]); - input_report_key(ts.dev, BTN_TOUCH, 1); input_report_abs(ts.dev, ABS_PRESSURE, 1); - input_sync(ts.dev); - ts.flag_first_touch_sent = 1; + } else { + input_report_key(ts.dev, BTN_TOUCH, 0); + input_report_abs(ts.dev, ABS_PRESSURE, 0); } - if (updown) { - - if (ts.tsf[0]) - (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]); + input_sync(ts.dev); #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG - { - struct timeval tv; - - do_gettimeofday(&tv); - printk(DEBUG_LVL "T:%06d, X:%03ld, Y:%03ld\n", - (int)tv.tv_usec, ts.coords[0], ts.coords[1]); - } + { + static char *s[] = {"down", "up", "update"}; + struct timeval tv; + do_gettimeofday(&tv); + printk(DEBUG_LVL "T:%06d %6s (X:%03d, Y:%03d)\n", + (int)tv.tv_usec, s[event], ts.coords[0], ts.coords[1]); + } #endif +} +static void touch_timer_fire(unsigned long data) +{ + if (ts.tsf[0]) + (ts.tsf[0]->api->scale)(ts.tsf[0], &ts.coords[0]); + + if (ts.need_to_send_first_touch) { + ts.need_to_send_first_touch = 0; + ts_input_report(IE_DOWN); + if (!ts.is_down) { /* Do we need this? I think so. */ + ts_input_report(IE_UPDATE); + ts_input_report(IE_UP); + } + } else if (ts.is_down) { + ts_input_report(IE_UPDATE); + } else { + ts_input_report(IE_UP); + } - input_report_abs(ts.dev, ABS_X, ts.coords[0]); - input_report_abs(ts.dev, ABS_Y, ts.coords[1]); - - input_report_key(ts.dev, BTN_TOUCH, 1); - input_report_abs(ts.dev, ABS_PRESSURE, 1); - input_sync(ts.dev); - + if (ts.is_down) { 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); } else { - if (ts.tsf[0]) (ts.tsf[0]->api->clear)(ts.tsf[0]); - - input_report_key(ts.dev, BTN_TOUCH, 0); - input_report_abs(ts.dev, ABS_PRESSURE, 0); - input_sync(ts.dev); - ts.flag_first_touch_sent = 0; - writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC); } } @@ -190,20 +180,20 @@ static irqreturn_t stylus_updown(int irq, void *dev_id) { unsigned long data0; unsigned long data1; - int updown; data0 = readl(base_addr+S3C2410_ADCDAT0); data1 = readl(base_addr+S3C2410_ADCDAT1); - updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && + ts.is_down = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN)); - /* TODO we should never get an interrupt with updown set while - * the timer is running, but maybe we ought to verify that the - * timer isn't running anyways. */ - - if (updown) - touch_timer_fire(0); + if (ts.is_down) { + ts.need_to_send_first_touch = 1; + 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); + } return IRQ_HANDLED; } @@ -336,7 +326,9 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) else /* this is OK, just means there won't be any filtering */ dev_info(&pdev->dev, "Unfiltered output selected\n"); - if (!ts.tsf[0]) + if (ts.tsf[0]) + (ts.tsf[0]->api->clear)(ts.tsf[0]); + else dev_info(&pdev->dev, "No filtering\n"); /* Get irqs */ diff --git a/drivers/input/touchscreen/ts_filter.c b/drivers/input/touchscreen/ts_filter.c index f8b2b2f5846..4c650a5c66f 100644 --- a/drivers/input/touchscreen/ts_filter.c +++ b/drivers/input/touchscreen/ts_filter.c @@ -49,9 +49,12 @@ EXPORT_SYMBOL_GPL(ts_filter_create_chain); void ts_filter_destroy_chain(struct ts_filter **list) { + struct ts_filter **first; + first = list; while (*list) { ((*list)->api->destroy)(*list); list++; } + *first = NULL; } EXPORT_SYMBOL_GPL(ts_filter_destroy_chain); diff --git a/drivers/input/touchscreen/ts_filter_mean.c b/drivers/input/touchscreen/ts_filter_mean.c index b589bee826f..2322432fc3f 100644 --- a/drivers/input/touchscreen/ts_filter_mean.c +++ b/drivers/input/touchscreen/ts_filter_mean.c @@ -94,12 +94,6 @@ static void ts_filter_mean_destroy(struct ts_filter *tsf) { struct ts_filter_mean *tsfs = (struct ts_filter_mean *)tsf; - if (!tsf) - return; - - if (tsf->next) /* chain */ - (tsf->next->api->destroy)(tsf->next); - kfree(tsfs->fifo[0]); /* first guy has pointer from kmalloc */ kfree(tsf); } diff --git a/drivers/input/touchscreen/ts_filter_median.c b/drivers/input/touchscreen/ts_filter_median.c index fe1b35f110b..47970da3c6a 100644 --- a/drivers/input/touchscreen/ts_filter_median.c +++ b/drivers/input/touchscreen/ts_filter_median.c @@ -119,9 +119,6 @@ static void ts_filter_median_destroy(struct ts_filter *tsf) { struct ts_filter_median *tsfm = (struct ts_filter_median *)tsf; - if (tsf->next) /* chain */ - (tsf->next->api->destroy)(tsf->next); - kfree(tsfm->sort[0]); /* first guy has pointer from kmalloc */ kfree(tsf); } |