2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-11-20 16:46:23 +08:00

[ARM] 5184/1: Split ucb1400_ts into core and touchscreen

This patch splits ucb1400_ts into ucb1400_ts and ucb1400_core.
Since this chip supports more features than only touchscreen,
it was necessary to prepare it for feature addition. The
previous functionality is preserved by applying this patch.

[Build fixes for non-ARM by Stephen Rothwell and Takashi Iwai]

Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
Marek Vašut 2008-08-03 21:34:08 +01:00 committed by Russell King
parent 6d341675f8
commit d9105c2b01
6 changed files with 473 additions and 309 deletions

View File

@ -220,6 +220,7 @@ config TOUCHSCREEN_ATMEL_TSADCC
config TOUCHSCREEN_UCB1400 config TOUCHSCREEN_UCB1400
tristate "Philips UCB1400 touchscreen" tristate "Philips UCB1400 touchscreen"
select AC97_BUS select AC97_BUS
depends on UCB1400_CORE
help help
This enables support for the Philips UCB1400 touchscreen interface. This enables support for the Philips UCB1400 touchscreen interface.
The UCB1400 is an AC97 audio codec. The touchscreen interface The UCB1400 is an AC97 audio codec. The touchscreen interface

View File

@ -5,6 +5,10 @@
* Created: September 25, 2006 * Created: September 25, 2006
* Copyright: MontaVista Software, Inc. * Copyright: MontaVista Software, Inc.
* *
* Spliting done by: Marek Vasut <marek.vasut@gmail.com>
* If something doesnt work and it worked before spliting, e-mail me,
* dont bother Nicolas please ;-)
*
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
@ -25,124 +29,16 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/ucb1400.h>
#include <sound/core.h>
#include <sound/ac97_codec.h>
/*
* Interesting UCB1400 AC-link registers
*/
#define UCB_IE_RIS 0x5e
#define UCB_IE_FAL 0x60
#define UCB_IE_STATUS 0x62
#define UCB_IE_CLEAR 0x62
#define UCB_IE_ADC (1 << 11)
#define UCB_IE_TSPX (1 << 12)
#define UCB_TS_CR 0x64
#define UCB_TS_CR_TSMX_POW (1 << 0)
#define UCB_TS_CR_TSPX_POW (1 << 1)
#define UCB_TS_CR_TSMY_POW (1 << 2)
#define UCB_TS_CR_TSPY_POW (1 << 3)
#define UCB_TS_CR_TSMX_GND (1 << 4)
#define UCB_TS_CR_TSPX_GND (1 << 5)
#define UCB_TS_CR_TSMY_GND (1 << 6)
#define UCB_TS_CR_TSPY_GND (1 << 7)
#define UCB_TS_CR_MODE_INT (0 << 8)
#define UCB_TS_CR_MODE_PRES (1 << 8)
#define UCB_TS_CR_MODE_POS (2 << 8)
#define UCB_TS_CR_BIAS_ENA (1 << 11)
#define UCB_TS_CR_TSPX_LOW (1 << 12)
#define UCB_TS_CR_TSMX_LOW (1 << 13)
#define UCB_ADC_CR 0x66
#define UCB_ADC_SYNC_ENA (1 << 0)
#define UCB_ADC_VREFBYP_CON (1 << 1)
#define UCB_ADC_INP_TSPX (0 << 2)
#define UCB_ADC_INP_TSMX (1 << 2)
#define UCB_ADC_INP_TSPY (2 << 2)
#define UCB_ADC_INP_TSMY (3 << 2)
#define UCB_ADC_INP_AD0 (4 << 2)
#define UCB_ADC_INP_AD1 (5 << 2)
#define UCB_ADC_INP_AD2 (6 << 2)
#define UCB_ADC_INP_AD3 (7 << 2)
#define UCB_ADC_EXT_REF (1 << 5)
#define UCB_ADC_START (1 << 7)
#define UCB_ADC_ENA (1 << 15)
#define UCB_ADC_DATA 0x68
#define UCB_ADC_DAT_VALID (1 << 15)
#define UCB_ADC_DAT_VALUE(x) ((x) & 0x3ff)
#define UCB_ID 0x7e
#define UCB_ID_1400 0x4304
struct ucb1400 {
struct snd_ac97 *ac97;
struct input_dev *ts_idev;
int irq;
wait_queue_head_t ts_wait;
struct task_struct *ts_task;
unsigned int irq_pending; /* not bit field shared */
unsigned int ts_restart:1;
unsigned int adcsync:1;
};
static int adcsync; static int adcsync;
static int ts_delay = 55; /* us */ static int ts_delay = 55; /* us */
static int ts_delay_pressure; /* us */ static int ts_delay_pressure; /* us */
static inline u16 ucb1400_reg_read(struct ucb1400 *ucb, u16 reg)
{
return ucb->ac97->bus->ops->read(ucb->ac97, reg);
}
static inline void ucb1400_reg_write(struct ucb1400 *ucb, u16 reg, u16 val)
{
ucb->ac97->bus->ops->write(ucb->ac97, reg, val);
}
static inline void ucb1400_adc_enable(struct ucb1400 *ucb)
{
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
}
static unsigned int ucb1400_adc_read(struct ucb1400 *ucb, u16 adc_channel)
{
unsigned int val;
if (ucb->adcsync)
adc_channel |= UCB_ADC_SYNC_ENA;
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel);
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | adc_channel | UCB_ADC_START);
for (;;) {
val = ucb1400_reg_read(ucb, UCB_ADC_DATA);
if (val & UCB_ADC_DAT_VALID)
break;
/* yield to other processes */
schedule_timeout_uninterruptible(1);
}
return UCB_ADC_DAT_VALUE(val);
}
static inline void ucb1400_adc_disable(struct ucb1400 *ucb)
{
ucb1400_reg_write(ucb, UCB_ADC_CR, 0);
}
/* Switch to interrupt mode. */ /* Switch to interrupt mode. */
static inline void ucb1400_ts_mode_int(struct ucb1400 *ucb) static inline void ucb1400_ts_mode_int(struct snd_ac97 *ac97)
{ {
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ac97, UCB_TS_CR,
UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
UCB_TS_CR_MODE_INT); UCB_TS_CR_MODE_INT);
@ -152,14 +48,14 @@ static inline void ucb1400_ts_mode_int(struct ucb1400 *ucb)
* Switch to pressure mode, and read pressure. We don't need to wait * Switch to pressure mode, and read pressure. We don't need to wait
* here, since both plates are being driven. * here, since both plates are being driven.
*/ */
static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb) static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400_ts *ucb)
{ {
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
udelay(ts_delay_pressure); udelay(ts_delay_pressure);
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY); return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
} }
/* /*
@ -168,21 +64,21 @@ static inline unsigned int ucb1400_ts_read_pressure(struct ucb1400 *ucb)
* gives a faster response time. Even so, we need to wait about 55us * gives a faster response time. Even so, we need to wait about 55us
* for things to stabilise. * for things to stabilise.
*/ */
static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb) static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400_ts *ucb)
{ {
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
udelay(ts_delay); udelay(ts_delay);
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPY); return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPY, adcsync);
} }
/* /*
@ -191,63 +87,63 @@ static inline unsigned int ucb1400_ts_read_xpos(struct ucb1400 *ucb)
* gives a faster response time. Even so, we need to wait about 55us * gives a faster response time. Even so, we need to wait about 55us
* for things to stabilise. * for things to stabilise.
*/ */
static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400 *ucb) static inline unsigned int ucb1400_ts_read_ypos(struct ucb1400_ts *ucb)
{ {
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
udelay(ts_delay); udelay(ts_delay);
return ucb1400_adc_read(ucb, UCB_ADC_INP_TSPX); return ucb1400_adc_read(ucb->ac97, UCB_ADC_INP_TSPX, adcsync);
} }
/* /*
* Switch to X plate resistance mode. Set MX to ground, PX to * Switch to X plate resistance mode. Set MX to ground, PX to
* supply. Measure current. * supply. Measure current.
*/ */
static inline unsigned int ucb1400_ts_read_xres(struct ucb1400 *ucb) static inline unsigned int ucb1400_ts_read_xres(struct ucb1400_ts *ucb)
{ {
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
return ucb1400_adc_read(ucb, 0); return ucb1400_adc_read(ucb->ac97, 0, adcsync);
} }
/* /*
* Switch to Y plate resistance mode. Set MY to ground, PY to * Switch to Y plate resistance mode. Set MY to ground, PY to
* supply. Measure current. * supply. Measure current.
*/ */
static inline unsigned int ucb1400_ts_read_yres(struct ucb1400 *ucb) static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)
{ {
ucb1400_reg_write(ucb, UCB_TS_CR, ucb1400_reg_write(ucb->ac97, UCB_TS_CR,
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
return ucb1400_adc_read(ucb, 0); return ucb1400_adc_read(ucb->ac97, 0, adcsync);
} }
static inline int ucb1400_ts_pen_down(struct ucb1400 *ucb) static inline int ucb1400_ts_pen_down(struct snd_ac97 *ac97)
{ {
unsigned short val = ucb1400_reg_read(ucb, UCB_TS_CR); unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR);
return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)); return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW);
} }
static inline void ucb1400_ts_irq_enable(struct ucb1400 *ucb) static inline void ucb1400_ts_irq_enable(struct snd_ac97 *ac97)
{ {
ucb1400_reg_write(ucb, UCB_IE_CLEAR, UCB_IE_TSPX); ucb1400_reg_write(ac97, UCB_IE_CLEAR, UCB_IE_TSPX);
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); ucb1400_reg_write(ac97, UCB_IE_CLEAR, 0);
ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_TSPX); ucb1400_reg_write(ac97, UCB_IE_FAL, UCB_IE_TSPX);
} }
static inline void ucb1400_ts_irq_disable(struct ucb1400 *ucb) static inline void ucb1400_ts_irq_disable(struct snd_ac97 *ac97)
{ {
ucb1400_reg_write(ucb, UCB_IE_FAL, 0); ucb1400_reg_write(ac97, UCB_IE_FAL, 0);
} }
static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y) static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16 y)
@ -264,25 +160,24 @@ static void ucb1400_ts_event_release(struct input_dev *idev)
input_sync(idev); input_sync(idev);
} }
static void ucb1400_handle_pending_irq(struct ucb1400 *ucb) static void ucb1400_handle_pending_irq(struct ucb1400_ts *ucb)
{ {
unsigned int isr; unsigned int isr;
isr = ucb1400_reg_read(ucb, UCB_IE_STATUS); isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS);
ucb1400_reg_write(ucb, UCB_IE_CLEAR, isr); ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr);
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
if (isr & UCB_IE_TSPX) if (isr & UCB_IE_TSPX) {
ucb1400_ts_irq_disable(ucb); ucb1400_ts_irq_disable(ucb->ac97);
else enable_irq(ucb->irq);
} else
printk(KERN_ERR "ucb1400: unexpected IE_STATUS = %#x\n", isr); printk(KERN_ERR "ucb1400: unexpected IE_STATUS = %#x\n", isr);
enable_irq(ucb->irq);
} }
static int ucb1400_ts_thread(void *_ucb) static int ucb1400_ts_thread(void *_ucb)
{ {
struct ucb1400 *ucb = _ucb; struct ucb1400_ts *ucb = _ucb;
struct task_struct *tsk = current; struct task_struct *tsk = current;
int valid = 0; int valid = 0;
struct sched_param param = { .sched_priority = 1 }; struct sched_param param = { .sched_priority = 1 };
@ -301,19 +196,19 @@ static int ucb1400_ts_thread(void *_ucb)
ucb1400_handle_pending_irq(ucb); ucb1400_handle_pending_irq(ucb);
} }
ucb1400_adc_enable(ucb); ucb1400_adc_enable(ucb->ac97);
x = ucb1400_ts_read_xpos(ucb); x = ucb1400_ts_read_xpos(ucb);
y = ucb1400_ts_read_ypos(ucb); y = ucb1400_ts_read_ypos(ucb);
p = ucb1400_ts_read_pressure(ucb); p = ucb1400_ts_read_pressure(ucb);
ucb1400_adc_disable(ucb); ucb1400_adc_disable(ucb->ac97);
/* Switch back to interrupt mode. */ /* Switch back to interrupt mode. */
ucb1400_ts_mode_int(ucb); ucb1400_ts_mode_int(ucb->ac97);
msleep(10); msleep(10);
if (ucb1400_ts_pen_down(ucb)) { if (ucb1400_ts_pen_down(ucb->ac97)) {
ucb1400_ts_irq_enable(ucb); ucb1400_ts_irq_enable(ucb->ac97);
/* /*
* If we spat out a valid sample set last time, * If we spat out a valid sample set last time,
@ -332,8 +227,8 @@ static int ucb1400_ts_thread(void *_ucb)
} }
wait_event_freezable_timeout(ucb->ts_wait, wait_event_freezable_timeout(ucb->ts_wait,
ucb->irq_pending || ucb->ts_restart || kthread_should_stop(), ucb->irq_pending || ucb->ts_restart ||
timeout); kthread_should_stop(), timeout);
} }
/* Send the "pen off" if we are stopping with the pen still active */ /* Send the "pen off" if we are stopping with the pen still active */
@ -356,7 +251,7 @@ static int ucb1400_ts_thread(void *_ucb)
*/ */
static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid) static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid)
{ {
struct ucb1400 *ucb = devid; struct ucb1400_ts *ucb = devid;
if (irqnr == ucb->irq) { if (irqnr == ucb->irq) {
disable_irq(ucb->irq); disable_irq(ucb->irq);
@ -369,7 +264,7 @@ static irqreturn_t ucb1400_hard_irq(int irqnr, void *devid)
static int ucb1400_ts_open(struct input_dev *idev) static int ucb1400_ts_open(struct input_dev *idev)
{ {
struct ucb1400 *ucb = input_get_drvdata(idev); struct ucb1400_ts *ucb = input_get_drvdata(idev);
int ret = 0; int ret = 0;
BUG_ON(ucb->ts_task); BUG_ON(ucb->ts_task);
@ -385,19 +280,143 @@ static int ucb1400_ts_open(struct input_dev *idev)
static void ucb1400_ts_close(struct input_dev *idev) static void ucb1400_ts_close(struct input_dev *idev)
{ {
struct ucb1400 *ucb = input_get_drvdata(idev); struct ucb1400_ts *ucb = input_get_drvdata(idev);
if (ucb->ts_task) if (ucb->ts_task)
kthread_stop(ucb->ts_task); kthread_stop(ucb->ts_task);
ucb1400_ts_irq_disable(ucb); ucb1400_ts_irq_disable(ucb->ac97);
ucb1400_reg_write(ucb, UCB_TS_CR, 0); ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0);
}
#ifndef NO_IRQ
#define NO_IRQ 0
#endif
/*
* Try to probe our interrupt, rather than relying on lots of
* hard-coded machine dependencies.
*/
static int ucb1400_ts_detect_irq(struct ucb1400_ts *ucb)
{
unsigned long mask, timeout;
mask = probe_irq_on();
/* Enable the ADC interrupt. */
ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, UCB_IE_ADC);
ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, UCB_IE_ADC);
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff);
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
/* Cause an ADC interrupt. */
ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA);
ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
/* Wait for the conversion to complete. */
timeout = jiffies + HZ/2;
while (!(ucb1400_reg_read(ucb->ac97, UCB_ADC_DATA) &
UCB_ADC_DAT_VALID)) {
cpu_relax();
if (time_after(jiffies, timeout)) {
printk(KERN_ERR "ucb1400: timed out in IRQ probe\n");
probe_irq_off(mask);
return -ENODEV;
}
}
ucb1400_reg_write(ucb->ac97, UCB_ADC_CR, 0);
/* Disable and clear interrupt. */
ucb1400_reg_write(ucb->ac97, UCB_IE_RIS, 0);
ucb1400_reg_write(ucb->ac97, UCB_IE_FAL, 0);
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0xffff);
ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0);
/* Read triggered interrupt. */
ucb->irq = probe_irq_off(mask);
if (ucb->irq < 0 || ucb->irq == NO_IRQ)
return -ENODEV;
return 0;
}
static int ucb1400_ts_probe(struct platform_device *dev)
{
int error, x_res, y_res;
struct ucb1400_ts *ucb = dev->dev.platform_data;
ucb->ts_idev = input_allocate_device();
if (!ucb->ts_idev) {
error = -ENOMEM;
goto err;
}
error = ucb1400_ts_detect_irq(ucb);
if (error) {
printk(KERN_ERR "UCB1400: IRQ probe failed\n");
goto err_free_devs;
}
init_waitqueue_head(&ucb->ts_wait);
error = request_irq(ucb->irq, ucb1400_hard_irq, IRQF_TRIGGER_RISING,
"UCB1400", ucb);
if (error) {
printk(KERN_ERR "ucb1400: unable to grab irq%d: %d\n",
ucb->irq, error);
goto err_free_devs;
}
printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq);
input_set_drvdata(ucb->ts_idev, ucb);
ucb->ts_idev->dev.parent = &dev->dev;
ucb->ts_idev->name = "UCB1400 touchscreen interface";
ucb->ts_idev->id.vendor = ucb1400_reg_read(ucb->ac97,
AC97_VENDOR_ID1);
ucb->ts_idev->id.product = ucb->id;
ucb->ts_idev->open = ucb1400_ts_open;
ucb->ts_idev->close = ucb1400_ts_close;
ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS);
ucb1400_adc_enable(ucb->ac97);
x_res = ucb1400_ts_read_xres(ucb);
y_res = ucb1400_ts_read_yres(ucb);
ucb1400_adc_disable(ucb->ac97);
printk(KERN_DEBUG "UCB1400: x/y = %d/%d\n", x_res, y_res);
input_set_abs_params(ucb->ts_idev, ABS_X, 0, x_res, 0, 0);
input_set_abs_params(ucb->ts_idev, ABS_Y, 0, y_res, 0, 0);
input_set_abs_params(ucb->ts_idev, ABS_PRESSURE, 0, 0, 0, 0);
error = input_register_device(ucb->ts_idev);
if (error)
goto err_free_irq;
return 0;
err_free_irq:
free_irq(ucb->irq, ucb);
err_free_devs:
input_free_device(ucb->ts_idev);
err:
return error;
}
static int ucb1400_ts_remove(struct platform_device *dev)
{
struct ucb1400_ts *ucb = dev->dev.platform_data;
free_irq(ucb->irq, ucb);
input_unregister_device(ucb->ts_idev);
return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int ucb1400_ts_resume(struct device *dev) static int ucb1400_ts_resume(struct platform_device *dev)
{ {
struct ucb1400 *ucb = dev_get_drvdata(dev); struct ucb1400_ts *ucb = platform_get_drvdata(dev);
if (ucb->ts_task) { if (ucb->ts_task) {
/* /*
@ -414,169 +433,36 @@ static int ucb1400_ts_resume(struct device *dev)
#define ucb1400_ts_resume NULL #define ucb1400_ts_resume NULL
#endif #endif
#ifndef NO_IRQ static struct platform_driver ucb1400_ts_driver = {
#define NO_IRQ 0 .probe = ucb1400_ts_probe,
#endif .remove = ucb1400_ts_remove,
.resume = ucb1400_ts_resume,
/* .driver = {
* Try to probe our interrupt, rather than relying on lots of .name = "ucb1400_ts",
* hard-coded machine dependencies. },
*/
static int ucb1400_detect_irq(struct ucb1400 *ucb)
{
unsigned long mask, timeout;
mask = probe_irq_on();
/* Enable the ADC interrupt. */
ucb1400_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC);
ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC);
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
/* Cause an ADC interrupt. */
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA);
ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START);
/* Wait for the conversion to complete. */
timeout = jiffies + HZ/2;
while (!(ucb1400_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VALID)) {
cpu_relax();
if (time_after(jiffies, timeout)) {
printk(KERN_ERR "ucb1400: timed out in IRQ probe\n");
probe_irq_off(mask);
return -ENODEV;
}
}
ucb1400_reg_write(ucb, UCB_ADC_CR, 0);
/* Disable and clear interrupt. */
ucb1400_reg_write(ucb, UCB_IE_RIS, 0);
ucb1400_reg_write(ucb, UCB_IE_FAL, 0);
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0xffff);
ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0);
/* Read triggered interrupt. */
ucb->irq = probe_irq_off(mask);
if (ucb->irq < 0 || ucb->irq == NO_IRQ)
return -ENODEV;
return 0;
}
static int ucb1400_ts_probe(struct device *dev)
{
struct ucb1400 *ucb;
struct input_dev *idev;
int error, id, x_res, y_res;
ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL);
idev = input_allocate_device();
if (!ucb || !idev) {
error = -ENOMEM;
goto err_free_devs;
}
ucb->ts_idev = idev;
ucb->adcsync = adcsync;
ucb->ac97 = to_ac97_t(dev);
init_waitqueue_head(&ucb->ts_wait);
id = ucb1400_reg_read(ucb, UCB_ID);
if (id != UCB_ID_1400) {
error = -ENODEV;
goto err_free_devs;
}
error = ucb1400_detect_irq(ucb);
if (error) {
printk(KERN_ERR "UCB1400: IRQ probe failed\n");
goto err_free_devs;
}
error = request_irq(ucb->irq, ucb1400_hard_irq, IRQF_TRIGGER_RISING,
"UCB1400", ucb);
if (error) {
printk(KERN_ERR "ucb1400: unable to grab irq%d: %d\n",
ucb->irq, error);
goto err_free_devs;
}
printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq);
input_set_drvdata(idev, ucb);
idev->dev.parent = dev;
idev->name = "UCB1400 touchscreen interface";
idev->id.vendor = ucb1400_reg_read(ucb, AC97_VENDOR_ID1);
idev->id.product = id;
idev->open = ucb1400_ts_open;
idev->close = ucb1400_ts_close;
idev->evbit[0] = BIT_MASK(EV_ABS);
ucb1400_adc_enable(ucb);
x_res = ucb1400_ts_read_xres(ucb);
y_res = ucb1400_ts_read_yres(ucb);
ucb1400_adc_disable(ucb);
printk(KERN_DEBUG "UCB1400: x/y = %d/%d\n", x_res, y_res);
input_set_abs_params(idev, ABS_X, 0, x_res, 0, 0);
input_set_abs_params(idev, ABS_Y, 0, y_res, 0, 0);
input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0);
error = input_register_device(idev);
if (error)
goto err_free_irq;
dev_set_drvdata(dev, ucb);
return 0;
err_free_irq:
free_irq(ucb->irq, ucb);
err_free_devs:
input_free_device(idev);
kfree(ucb);
return error;
}
static int ucb1400_ts_remove(struct device *dev)
{
struct ucb1400 *ucb = dev_get_drvdata(dev);
free_irq(ucb->irq, ucb);
input_unregister_device(ucb->ts_idev);
dev_set_drvdata(dev, NULL);
kfree(ucb);
return 0;
}
static struct device_driver ucb1400_ts_driver = {
.name = "ucb1400_ts",
.owner = THIS_MODULE,
.bus = &ac97_bus_type,
.probe = ucb1400_ts_probe,
.remove = ucb1400_ts_remove,
.resume = ucb1400_ts_resume,
}; };
static int __init ucb1400_ts_init(void) static int __init ucb1400_ts_init(void)
{ {
return driver_register(&ucb1400_ts_driver); return platform_driver_register(&ucb1400_ts_driver);
} }
static void __exit ucb1400_ts_exit(void) static void __exit ucb1400_ts_exit(void)
{ {
driver_unregister(&ucb1400_ts_driver); platform_driver_unregister(&ucb1400_ts_driver);
} }
module_param(adcsync, bool, 0444); module_param(adcsync, bool, 0444);
MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin."); MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin.");
module_param(ts_delay, int, 0444); module_param(ts_delay, int, 0444);
MODULE_PARM_DESC(ts_delay, "Delay between panel setup and position read. Default = 55us."); MODULE_PARM_DESC(ts_delay, "Delay between panel setup and"
" position read. Default = 55us.");
module_param(ts_delay_pressure, int, 0444); module_param(ts_delay_pressure, int, 0444);
MODULE_PARM_DESC(ts_delay_pressure, MODULE_PARM_DESC(ts_delay_pressure,
"delay between panel setup and pressure read. Default = 0us."); "delay between panel setup and pressure read."
" Default = 0us.");
module_init(ucb1400_ts_init); module_init(ucb1400_ts_init);
module_exit(ucb1400_ts_exit); module_exit(ucb1400_ts_exit);

View File

@ -50,6 +50,15 @@ config HTC_PASIC3
HTC Magician devices, respectively. Actual functionality is HTC Magician devices, respectively. Actual functionality is
handled by the leds-pasic3 and ds1wm drivers. handled by the leds-pasic3 and ds1wm drivers.
config UCB1400_CORE
tristate "Philips UCB1400 Core driver"
help
This enables support for the Philips UCB1400 core functions.
The UCB1400 is an AC97 audio codec.
To compile this driver as a module, choose M here: the
module will be called ucb1400_core.
config MFD_TC6393XB config MFD_TC6393XB
bool "Support Toshiba TC6393XB" bool "Support Toshiba TC6393XB"
depends on GPIOLIB && ARM depends on GPIOLIB && ARM

View File

@ -20,3 +20,4 @@ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
ifeq ($(CONFIG_SA1100_ASSABET),y) ifeq ($(CONFIG_SA1100_ASSABET),y)
obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
endif endif
obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o

106
drivers/mfd/ucb1400_core.c Normal file
View File

@ -0,0 +1,106 @@
/*
* Core functions for:
* Philips UCB1400 multifunction chip
*
* Based on ucb1400_ts.c:
* Author: Nicolas Pitre
* Created: September 25, 2006
* Copyright: MontaVista Software, Inc.
*
* Spliting done by: Marek Vasut <marek.vasut@gmail.com>
* If something doesnt work and it worked before spliting, e-mail me,
* dont bother Nicolas please ;-)
*
* 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 code is heavily based on ucb1x00-*.c copyrighted by Russell King
* covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has
* been made separate from ucb1x00-core/ucb1x00-ts on Russell's request.
*/
#include <linux/module.h>
#include <linux/ucb1400.h>
static int ucb1400_core_probe(struct device *dev)
{
int err;
struct ucb1400 *ucb;
struct ucb1400_ts ucb_ts;
struct snd_ac97 *ac97;
memset(&ucb_ts, 0, sizeof(ucb_ts));
ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL);
if (!ucb) {
err = -ENOMEM;
goto err;
}
dev_set_drvdata(dev, ucb);
ac97 = to_ac97_t(dev);
ucb_ts.id = ucb1400_reg_read(ac97, UCB_ID);
if (ucb_ts.id != UCB_ID_1400) {
err = -ENODEV;
goto err0;
}
/* TOUCHSCREEN */
ucb_ts.ac97 = ac97;
ucb->ucb1400_ts = platform_device_alloc("ucb1400_ts", -1);
if (!ucb->ucb1400_ts) {
err = -ENOMEM;
goto err0;
}
err = platform_device_add_data(ucb->ucb1400_ts, &ucb_ts,
sizeof(ucb_ts));
if (err)
goto err1;
err = platform_device_add(ucb->ucb1400_ts);
if (err)
goto err1;
return 0;
err1:
platform_device_put(ucb->ucb1400_ts);
err0:
kfree(ucb);
err:
return err;
}
static int ucb1400_core_remove(struct device *dev)
{
struct ucb1400 *ucb = dev_get_drvdata(dev);
platform_device_unregister(ucb->ucb1400_ts);
kfree(ucb);
return 0;
}
static struct device_driver ucb1400_core_driver = {
.name = "ucb1400_core",
.bus = &ac97_bus_type,
.probe = ucb1400_core_probe,
.remove = ucb1400_core_remove,
};
static int __init ucb1400_core_init(void)
{
return driver_register(&ucb1400_core_driver);
}
static void __exit ucb1400_core_exit(void)
{
driver_unregister(&ucb1400_core_driver);
}
module_init(ucb1400_core_init);
module_exit(ucb1400_core_exit);
MODULE_DESCRIPTION("Philips UCB1400 driver");
MODULE_LICENSE("GPL");

161
include/linux/ucb1400.h Normal file
View File

@ -0,0 +1,161 @@
/*
* Register definitions and functions for:
* Philips UCB1400 driver
*
* Based on ucb1400_ts:
* Author: Nicolas Pitre
* Created: September 25, 2006
* Copyright: MontaVista Software, Inc.
*
* Spliting done by: Marek Vasut <marek.vasut@gmail.com>
* If something doesnt work and it worked before spliting, e-mail me,
* dont bother Nicolas please ;-)
*
* 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 code is heavily based on ucb1x00-*.c copyrighted by Russell King
* covering the UCB1100, UCB1200 and UCB1300.. Support for the UCB1400 has
* been made separate from ucb1x00-core/ucb1x00-ts on Russell's request.
*/
#ifndef _LINUX__UCB1400_H
#define _LINUX__UCB1400_H
#include <sound/ac97_codec.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
/*
* UCB1400 AC-link registers
*/
#define UCB_IO_DATA 0x5a
#define UCB_IO_DIR 0x5c
#define UCB_IE_RIS 0x5e
#define UCB_IE_FAL 0x60
#define UCB_IE_STATUS 0x62
#define UCB_IE_CLEAR 0x62
#define UCB_IE_ADC (1 << 11)
#define UCB_IE_TSPX (1 << 12)
#define UCB_TS_CR 0x64
#define UCB_TS_CR_TSMX_POW (1 << 0)
#define UCB_TS_CR_TSPX_POW (1 << 1)
#define UCB_TS_CR_TSMY_POW (1 << 2)
#define UCB_TS_CR_TSPY_POW (1 << 3)
#define UCB_TS_CR_TSMX_GND (1 << 4)
#define UCB_TS_CR_TSPX_GND (1 << 5)
#define UCB_TS_CR_TSMY_GND (1 << 6)
#define UCB_TS_CR_TSPY_GND (1 << 7)
#define UCB_TS_CR_MODE_INT (0 << 8)
#define UCB_TS_CR_MODE_PRES (1 << 8)
#define UCB_TS_CR_MODE_POS (2 << 8)
#define UCB_TS_CR_BIAS_ENA (1 << 11)
#define UCB_TS_CR_TSPX_LOW (1 << 12)
#define UCB_TS_CR_TSMX_LOW (1 << 13)
#define UCB_ADC_CR 0x66
#define UCB_ADC_SYNC_ENA (1 << 0)
#define UCB_ADC_VREFBYP_CON (1 << 1)
#define UCB_ADC_INP_TSPX (0 << 2)
#define UCB_ADC_INP_TSMX (1 << 2)
#define UCB_ADC_INP_TSPY (2 << 2)
#define UCB_ADC_INP_TSMY (3 << 2)
#define UCB_ADC_INP_AD0 (4 << 2)
#define UCB_ADC_INP_AD1 (5 << 2)
#define UCB_ADC_INP_AD2 (6 << 2)
#define UCB_ADC_INP_AD3 (7 << 2)
#define UCB_ADC_EXT_REF (1 << 5)
#define UCB_ADC_START (1 << 7)
#define UCB_ADC_ENA (1 << 15)
#define UCB_ADC_DATA 0x68
#define UCB_ADC_DAT_VALID (1 << 15)
#define UCB_ADC_DAT_MASK 0x3ff
#define UCB_ID 0x7e
#define UCB_ID_1400 0x4304
struct ucb1400_ts {
struct input_dev *ts_idev;
struct task_struct *ts_task;
int id;
wait_queue_head_t ts_wait;
unsigned int ts_restart:1;
int irq;
unsigned int irq_pending; /* not bit field shared */
struct snd_ac97 *ac97;
};
struct ucb1400 {
struct platform_device *ucb1400_ts;
};
static inline u16 ucb1400_reg_read(struct snd_ac97 *ac97, u16 reg)
{
return ac97->bus->ops->read(ac97, reg);
}
static inline void ucb1400_reg_write(struct snd_ac97 *ac97, u16 reg, u16 val)
{
ac97->bus->ops->write(ac97, reg, val);
}
static inline u16 ucb1400_gpio_get_value(struct snd_ac97 *ac97, u16 gpio)
{
return ucb1400_reg_read(ac97, UCB_IO_DATA) & (1 << gpio);
}
static inline void ucb1400_gpio_set_value(struct snd_ac97 *ac97, u16 gpio,
u16 val)
{
ucb1400_reg_write(ac97, UCB_IO_DATA, val ?
ucb1400_reg_read(ac97, UCB_IO_DATA) | (1 << gpio) :
ucb1400_reg_read(ac97, UCB_IO_DATA) & ~(1 << gpio));
}
static inline u16 ucb1400_gpio_get_direction(struct snd_ac97 *ac97, u16 gpio)
{
return ucb1400_reg_read(ac97, UCB_IO_DIR) & (1 << gpio);
}
static inline void ucb1400_gpio_set_direction(struct snd_ac97 *ac97, u16 gpio,
u16 dir)
{
ucb1400_reg_write(ac97, UCB_IO_DIR, dir ?
ucb1400_reg_read(ac97, UCB_IO_DIR) | (1 << gpio) :
ucb1400_reg_read(ac97, UCB_IO_DIR) & ~(1 << gpio));
}
static inline void ucb1400_adc_enable(struct snd_ac97 *ac97)
{
ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA);
}
static unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel,
int adcsync)
{
unsigned int val;
if (adcsync)
adc_channel |= UCB_ADC_SYNC_ENA;
ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel);
ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel |
UCB_ADC_START);
while (!((val = ucb1400_reg_read(ac97, UCB_ADC_DATA))
& UCB_ADC_DAT_VALID))
schedule_timeout_uninterruptible(1);
return val & UCB_ADC_DAT_MASK;
}
static inline void ucb1400_adc_disable(struct snd_ac97 *ac97)
{
ucb1400_reg_write(ac97, UCB_ADC_CR, 0);
}
#endif