diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2008-10-10 00:33:02 +0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2008-10-10 00:33:02 +0400 |
commit | 3f30a09a612bac2b531a206c2a58a292dd7ff182 (patch) | |
tree | 62741c2f78aeb3009c66dbcf014ebff2e034e597 /drivers | |
parent | 9e165acf1b9e37af7c0fa39399b43d0bd8600039 (diff) | |
parent | fda50a1c49ad7483eaa29a268d560422c413933f (diff) | |
download | linux-3f30a09a612bac2b531a206c2a58a292dd7ff182.tar.xz |
Merge branch 'pxa-all' into devel
Conflicts:
arch/arm/mach-pxa/Kconfig
arch/arm/mach-pxa/corgi.c
arch/arm/mach-pxa/include/mach/hardware.h
arch/arm/mach-pxa/spitz.c
Diffstat (limited to 'drivers')
47 files changed, 3209 insertions, 1330 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d402e8d813ce..3309e862f317 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -540,6 +540,15 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_MAX1111 + tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip" + depends on SPI_MASTER + help + Say y here to support Maxim's MAX1111 ADC chips. + + This driver can also be built as a module. If so, the module + will be called max1111. + config SENSORS_MAX1619 tristate "Maxim MAX1619 sensor chip" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 950134ab8426..6babc801b348 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o +obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c new file mode 100644 index 000000000000..bfaa665ccf32 --- /dev/null +++ b/drivers/hwmon/max1111.c @@ -0,0 +1,244 @@ +/* + * max1111.c - +2.7V, Low-Power, Multichannel, Serial 8-bit ADCs + * + * Based on arch/arm/mach-pxa/corgi_ssp.c + * + * Copyright (C) 2004-2005 Richard Purdie + * + * Copyright (C) 2008 Marvell International Ltd. + * Eric Miao <eric.miao@marvell.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 + * publishhed by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/spi/spi.h> + +#define MAX1111_TX_BUF_SIZE 1 +#define MAX1111_RX_BUF_SIZE 2 + +/* MAX1111 Commands */ +#define MAX1111_CTRL_PD0 (1u << 0) +#define MAX1111_CTRL_PD1 (1u << 1) +#define MAX1111_CTRL_SGL (1u << 2) +#define MAX1111_CTRL_UNI (1u << 3) +#define MAX1111_CTRL_SEL_SH (5) /* NOTE: bit 4 is ignored */ +#define MAX1111_CTRL_STR (1u << 7) + +struct max1111_data { + struct spi_device *spi; + struct device *hwmon_dev; + struct spi_message msg; + struct spi_transfer xfer[2]; + uint8_t *tx_buf; + uint8_t *rx_buf; +}; + +static int max1111_read(struct device *dev, int channel) +{ + struct max1111_data *data = dev_get_drvdata(dev); + uint8_t v1, v2; + int err; + + data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) | + MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 | + MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR; + + err = spi_sync(data->spi, &data->msg); + if (err < 0) { + dev_err(dev, "spi_sync failed with %d\n", err); + return err; + } + + v1 = data->rx_buf[0]; + v2 = data->rx_buf[1]; + + if ((v1 & 0xc0) || (v2 & 0x3f)) + return -EINVAL; + + return (v1 << 2) | (v2 >> 6); +} + +#ifdef CONFIG_SHARPSL_PM +static struct max1111_data *the_max1111; + +int max1111_read_channel(int channel) +{ + return max1111_read(&the_max1111->spi->dev, channel); +} +EXPORT_SYMBOL(max1111_read_channel); +#endif + +/* + * NOTE: SPI devices do not have a default 'name' attribute, which is + * likely to be used by hwmon applications to distinguish between + * different devices, explicitly add a name attribute here. + */ +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "max1111\n"); +} + +static ssize_t show_adc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int channel = to_sensor_dev_attr(attr)->index; + int ret; + + ret = max1111_read(dev, channel); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + +#define MAX1111_ADC_ATTR(_id) \ + SENSOR_DEVICE_ATTR(adc##_id##_in, S_IRUGO, show_adc, NULL, _id) + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +static MAX1111_ADC_ATTR(0); +static MAX1111_ADC_ATTR(1); +static MAX1111_ADC_ATTR(2); +static MAX1111_ADC_ATTR(3); + +static struct attribute *max1111_attributes[] = { + &dev_attr_name.attr, + &sensor_dev_attr_adc0_in.dev_attr.attr, + &sensor_dev_attr_adc1_in.dev_attr.attr, + &sensor_dev_attr_adc2_in.dev_attr.attr, + &sensor_dev_attr_adc3_in.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max1111_attr_group = { + .attrs = max1111_attributes, +}; + +static int setup_transfer(struct max1111_data *data) +{ + struct spi_message *m; + struct spi_transfer *x; + + data->tx_buf = kmalloc(MAX1111_TX_BUF_SIZE, GFP_KERNEL); + if (!data->tx_buf) + return -ENOMEM; + + data->rx_buf = kmalloc(MAX1111_RX_BUF_SIZE, GFP_KERNEL); + if (!data->rx_buf) { + kfree(data->tx_buf); + return -ENOMEM; + } + + m = &data->msg; + x = &data->xfer[0]; + + spi_message_init(m); + + x->tx_buf = &data->tx_buf[0]; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &data->rx_buf[0]; + x->len = 2; + spi_message_add_tail(x, m); + + return 0; +} + +static int __devinit max1111_probe(struct spi_device *spi) +{ + struct max1111_data *data; + int err; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + err = spi_setup(spi); + if (err < 0) + return err; + + data = kzalloc(sizeof(struct max1111_data), GFP_KERNEL); + if (data == NULL) { + dev_err(&spi->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + err = setup_transfer(data); + if (err) + goto err_free_data; + + data->spi = spi; + spi_set_drvdata(spi, data); + + err = sysfs_create_group(&spi->dev.kobj, &max1111_attr_group); + if (err) { + dev_err(&spi->dev, "failed to create attribute group\n"); + goto err_free_all; + } + + data->hwmon_dev = hwmon_device_register(&spi->dev); + if (IS_ERR(data->hwmon_dev)) { + dev_err(&spi->dev, "failed to create hwmon device\n"); + err = PTR_ERR(data->hwmon_dev); + goto err_remove; + } + +#ifdef CONFIG_SHARPSL_PM + the_max1111 = data; +#endif + return 0; + +err_remove: + sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); +err_free_all: + kfree(data->rx_buf); + kfree(data->tx_buf); +err_free_data: + kfree(data); + return err; +} + +static int __devexit max1111_remove(struct spi_device *spi) +{ + struct max1111_data *data = spi_get_drvdata(spi); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group); + kfree(data->rx_buf); + kfree(data->tx_buf); + kfree(data); + return 0; +} + +static struct spi_driver max1111_driver = { + .driver = { + .name = "max1111", + .owner = THIS_MODULE, + }, + .probe = max1111_probe, + .remove = __devexit_p(max1111_remove), +}; + +static int __init max1111_init(void) +{ + return spi_register_driver(&max1111_driver); +} +module_init(max1111_init); + +static void __exit max1111_exit(void) +{ + spi_unregister_driver(&max1111_driver); +} +module_exit(max1111_exit); + +MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); +MODULE_DESCRIPTION("MAX1111 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 44d838410f15..906f9b9d715d 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -38,7 +38,44 @@ #include <asm/irq.h> #include <asm/io.h> #include <mach/i2c.h> -#include <mach/pxa-regs.h> + +/* + * I2C registers and bit definitions + */ +#define IBMR (0x00) +#define IDBR (0x08) +#define ICR (0x10) +#define ISR (0x18) +#define ISAR (0x20) + +#define ICR_START (1 << 0) /* start bit */ +#define ICR_STOP (1 << 1) /* stop bit */ +#define ICR_ACKNAK (1 << 2) /* send ACK(0) or NAK(1) */ +#define ICR_TB (1 << 3) /* transfer byte bit */ +#define ICR_MA (1 << 4) /* master abort */ +#define ICR_SCLE (1 << 5) /* master clock enable */ +#define ICR_IUE (1 << 6) /* unit enable */ +#define ICR_GCD (1 << 7) /* general call disable */ +#define ICR_ITEIE (1 << 8) /* enable tx interrupts */ +#define ICR_IRFIE (1 << 9) /* enable rx interrupts */ +#define ICR_BEIE (1 << 10) /* enable bus error ints */ +#define ICR_SSDIE (1 << 11) /* slave STOP detected int enable */ +#define ICR_ALDIE (1 << 12) /* enable arbitration interrupt */ +#define ICR_SADIE (1 << 13) /* slave address detected int enable */ +#define ICR_UR (1 << 14) /* unit reset */ +#define ICR_FM (1 << 15) /* fast mode */ + +#define ISR_RWM (1 << 0) /* read/write mode */ +#define ISR_ACKNAK (1 << 1) /* ack/nak status */ +#define ISR_UB (1 << 2) /* unit busy */ +#define ISR_IBB (1 << 3) /* bus busy */ +#define ISR_SSD (1 << 4) /* slave stop detected */ +#define ISR_ALD (1 << 5) /* arbitration loss detected */ +#define ISR_ITE (1 << 6) /* tx buffer empty */ +#define ISR_IRF (1 << 7) /* rx buffer full */ +#define ISR_GCAD (1 << 8) /* general call address detected */ +#define ISR_SAD (1 << 9) /* slave address detected */ +#define ISR_BED (1 << 10) /* bus error no ACK/NAK */ struct pxa_i2c { spinlock_t lock; @@ -60,19 +97,21 @@ struct pxa_i2c { u32 icrlog[32]; void __iomem *reg_base; + unsigned int reg_shift; unsigned long iobase; unsigned long iosize; int irq; - int use_pio; + unsigned int use_pio :1; + unsigned int fast_mode :1; }; -#define _IBMR(i2c) ((i2c)->reg_base + 0) -#define _IDBR(i2c) ((i2c)->reg_base + 8) -#define _ICR(i2c) ((i2c)->reg_base + 0x10) -#define _ISR(i2c) ((i2c)->reg_base + 0x18) -#define _ISAR(i2c) ((i2c)->reg_base + 0x20) +#define _IBMR(i2c) ((i2c)->reg_base + (0x0 << (i2c)->reg_shift)) +#define _IDBR(i2c) ((i2c)->reg_base + (0x4 << (i2c)->reg_shift)) +#define _ICR(i2c) ((i2c)->reg_base + (0x8 << (i2c)->reg_shift)) +#define _ISR(i2c) ((i2c)->reg_base + (0xc << (i2c)->reg_shift)) +#define _ISAR(i2c) ((i2c)->reg_base + (0x10 << (i2c)->reg_shift)) /* * I2C Slave mode address @@ -188,14 +227,14 @@ static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c) static void i2c_pxa_abort(struct pxa_i2c *i2c) { - unsigned long timeout = jiffies + HZ/4; + int i = 250; if (i2c_pxa_is_slavemode(i2c)) { dev_dbg(&i2c->adap.dev, "%s: called in slave mode\n", __func__); return; } - while (time_before(jiffies, timeout) && (readl(_IBMR(i2c)) & 0x1) == 0) { + while ((i > 0) && (readl(_IBMR(i2c)) & 0x1) == 0) { unsigned long icr = readl(_ICR(i2c)); icr &= ~ICR_START; @@ -205,7 +244,8 @@ static void i2c_pxa_abort(struct pxa_i2c *i2c) show_state(i2c); - msleep(1); + mdelay(1); + i --; } writel(readl(_ICR(i2c)) & ~(ICR_MA | ICR_START | ICR_STOP), @@ -364,7 +404,7 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c) writel(i2c->slave_addr, _ISAR(i2c)); /* set control register values */ - writel(I2C_ICR_INIT, _ICR(i2c)); + writel(I2C_ICR_INIT | (i2c->fast_mode ? ICR_FM : 0), _ICR(i2c)); #ifdef CONFIG_I2C_PXA_SLAVE dev_info(&i2c->adap.dev, "Enabling slave mode\n"); @@ -907,12 +947,6 @@ static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num struct pxa_i2c *i2c = adap->algo_data; int ret, i; - /* If the I2C controller is disabled we need to reset it (probably due - to a suspend/resume destroying state). We do this here as we can then - avoid worrying about resuming the controller before its users. */ - if (!(readl(_ICR(i2c)) & ICR_IUE)) - i2c_pxa_reset(i2c); - for (i = adap->retries; i >= 0; i--) { ret = i2c_pxa_do_xfer(i2c, msgs, num); if (ret != I2C_RETRY) @@ -993,6 +1027,7 @@ static int i2c_pxa_probe(struct platform_device *dev) ret = -EIO; goto eremap; } + i2c->reg_shift = (cpu_is_pxa3xx() && (dev->id == 1)) ? 0 : 1; i2c->iobase = res->start; i2c->iosize = res_len(res); @@ -1013,6 +1048,7 @@ static int i2c_pxa_probe(struct platform_device *dev) if (plat) { i2c->adap.class = plat->class; i2c->use_pio = plat->use_pio; + i2c->fast_mode = plat->fast_mode; } if (i2c->use_pio) { @@ -1082,9 +1118,33 @@ static int __exit i2c_pxa_remove(struct platform_device *dev) return 0; } +#ifdef CONFIG_PM +static int i2c_pxa_suspend_late(struct platform_device *dev, pm_message_t state) +{ + struct pxa_i2c *i2c = platform_get_drvdata(dev); + clk_disable(i2c->clk); + return 0; +} + +static int i2c_pxa_resume_early(struct platform_device *dev) +{ + struct pxa_i2c *i2c = platform_get_drvdata(dev); + + clk_enable(i2c->clk); + i2c_pxa_reset(i2c); + + return 0; +} +#else +#define i2c_pxa_suspend_late NULL +#define i2c_pxa_resume_early NULL +#endif + static struct platform_driver i2c_pxa_driver = { .probe = i2c_pxa_probe, .remove = __exit_p(i2c_pxa_remove), + .suspend_late = i2c_pxa_suspend_late, + .resume_early = i2c_pxa_resume_early, .driver = { .name = "pxa2xx-i2c", .owner = THIS_MODULE, diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c index 134e67bf6a90..c8ed065ea0cb 100644 --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -80,9 +80,9 @@ struct corgikbd { #define KB_ACTIVATE_DELAY 10 /* Helper functions for reading the keyboard matrix - * Note: We should really be using pxa_gpio_mode to alter GPDR but it - * requires a function call per GPIO bit which is excessive - * when we need to access 12 bits at once multiple times. + * Note: We should really be using the generic gpio functions to alter + * GPDR but it requires a function call per GPIO bit which is + * excessive when we need to access 12 bits at once, multiple times. * These functions must be called within local_irq_save()/local_irq_restore() * or similar. */ diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c index de67b8e0a799..c48b76a46a58 100644 --- a/drivers/input/keyboard/spitzkbd.c +++ b/drivers/input/keyboard/spitzkbd.c @@ -101,9 +101,9 @@ struct spitzkbd { #define KB_ACTIVATE_DELAY 10 /* Helper functions for reading the keyboard matrix - * Note: We should really be using pxa_gpio_mode to alter GPDR but it - * requires a function call per GPIO bit which is excessive - * when we need to access 11 bits at once, multiple times. + * Note: We should really be using the generic gpio functions to alter + * GPDR but it requires a function call per GPIO bit which is + * excessive when we need to access 11 bits at once, multiple times. * These functions must be called within local_irq_save()/local_irq_restore() * or similar. */ diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c index 44cb50af3ce9..677276b12020 100644 --- a/drivers/input/keyboard/tosakbd.c +++ b/drivers/input/keyboard/tosakbd.c @@ -59,9 +59,9 @@ struct tosakbd { /* Helper functions for reading the keyboard matrix - * Note: We should really be using pxa_gpio_mode to alter GPDR but it - * requires a function call per GPIO bit which is excessive - * when we need to access 12 bits at once, multiple times. + * Note: We should really be using the generic gpio functions to alter + * GPDR but it requires a function call per GPIO bit which is + * excessive when we need to access 12 bits at once, multiple times. * These functions must be called within local_irq_save()/local_irq_restore() * or similar. */ diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 25287e80e236..6e1e8c624f9e 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -220,6 +220,7 @@ config TOUCHSCREEN_ATMEL_TSADCC config TOUCHSCREEN_UCB1400 tristate "Philips UCB1400 touchscreen" select AC97_BUS + depends on UCB1400_CORE help This enables support for the Philips UCB1400 touchscreen interface. The UCB1400 is an AC97 audio codec. The touchscreen interface diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index ce6f48c695f5..8583c766d565 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -24,6 +24,7 @@ #include <linux/input.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/gpio.h> #include <linux/spi/spi.h> #include <linux/spi/ads7846.h> #include <asm/irq.h> @@ -116,6 +117,7 @@ struct ads7846 { void *filter_data; void (*filter_cleanup)(void *data); int (*get_pendown_state)(void); + int gpio_pendown; }; /* leave chip selected when we're done, for quicker re-select? */ @@ -491,6 +493,14 @@ static struct attribute_group ads784x_attr_group = { /*--------------------------------------------------------------------------*/ +static int get_pendown_state(struct ads7846 *ts) +{ + if (ts->get_pendown_state) + return ts->get_pendown_state(); + + return !gpio_get_value(ts->gpio_pendown); +} + /* * PENIRQ only kicks the timer. The timer only reissues the SPI transfer, * to retrieve touchscreen status. @@ -550,7 +560,7 @@ static void ads7846_rx(void *ads) */ if (ts->penirq_recheck_delay_usecs) { udelay(ts->penirq_recheck_delay_usecs); - if (!ts->get_pendown_state()) + if (!get_pendown_state(ts)) Rt = 0; } @@ -677,7 +687,7 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) spin_lock_irq(&ts->lock); - if (unlikely(!ts->get_pendown_state() || + if (unlikely(!get_pendown_state(ts) || device_suspended(&ts->spi->dev))) { if (ts->pendown) { struct input_dev *input = ts->input; @@ -716,7 +726,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle) unsigned long flags; spin_lock_irqsave(&ts->lock, flags); - if (likely(ts->get_pendown_state())) { + if (likely(get_pendown_state(ts))) { if (!ts->irq_disabled) { /* The ARM do_simple_IRQ() dispatcher doesn't act * like the other dispatchers: it will report IRQs @@ -806,6 +816,36 @@ static int ads7846_resume(struct spi_device *spi) return 0; } +static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) +{ + struct ads7846_platform_data *pdata = spi->dev.platform_data; + int err; + + /* REVISIT when the irq can be triggered active-low, or if for some + * reason the touchscreen isn't hooked up, we don't need to access + * the pendown state. + */ + if (!pdata->get_pendown_state && !gpio_is_valid(pdata->gpio_pendown)) { + dev_err(&spi->dev, "no get_pendown_state nor gpio_pendown?\n"); + return -EINVAL; + } + + if (pdata->get_pendown_state) { + ts->get_pendown_state = pdata->get_pendown_state; + return 0; + } + + err = gpio_request(pdata->gpio_pendown, "ads7846_pendown"); + if (err) { + dev_err(&spi->dev, "failed to request pendown GPIO%d\n", + pdata->gpio_pendown); + return err; + } + + ts->gpio_pendown = pdata->gpio_pendown; + return 0; +} + static int __devinit ads7846_probe(struct spi_device *spi) { struct ads7846 *ts; @@ -833,15 +873,6 @@ static int __devinit ads7846_probe(struct spi_device *spi) return -EINVAL; } - /* REVISIT when the irq can be triggered active-low, or if for some - * reason the touchscreen isn't hooked up, we don't need to access - * the pendown state. - */ - if (pdata->get_pendown_state == NULL) { - dev_dbg(&spi->dev, "no get_pendown_state function?\n"); - return -EINVAL; - } - /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except * that even if the hardware can do that, the SPI controller driver * may not. So we stick to very-portable 8 bit words, both RX and TX. @@ -893,7 +924,10 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->filter_data = ts; } else ts->filter = ads7846_no_filter; - ts->get_pendown_state = pdata->get_pendown_state; + + err = setup_pendown(spi, ts); + if (err) + goto err_cleanup_filter; if (pdata->penirq_recheck_delay_usecs) ts->penirq_recheck_delay_usecs = @@ -1085,7 +1119,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi->dev.driver->name, ts)) { dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); err = -EBUSY; - goto err_cleanup_filter; + goto err_free_gpio; } err = ads784x_hwmon_register(spi, ts); @@ -1116,6 +1150,9 @@ static int __devinit ads7846_probe(struct spi_device *spi) ads784x_hwmon_unregister(spi, ts); err_free_irq: free_irq(spi->irq, ts); + err_free_gpio: + if (ts->gpio_pendown != -1) + gpio_free(ts->gpio_pendown); err_cleanup_filter: if (ts->filter_cleanup) ts->filter_cleanup(ts->filter_data); @@ -1140,6 +1177,9 @@ static int __devexit ads7846_remove(struct spi_device *spi) /* suspend left the IRQ disabled */ enable_irq(ts->spi->irq); + if (ts->gpio_pendown != -1) + gpio_free(ts->gpio_pendown); + if (ts->filter_cleanup) ts->filter_cleanup(ts->filter_data); diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index bce018e45bce..54986627def0 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -5,6 +5,10 @@ * 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. @@ -25,124 +29,16 @@ #include <linux/slab.h> #include <linux/kthread.h> #include <linux/freezer.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; -}; +#include <linux/ucb1400.h> static int adcsync; static int ts_delay = 55; /* 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. */ -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_TSMY_GND | UCB_TS_CR_TSPY_GND | 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 * 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_TSMY_GND | UCB_TS_CR_TSPY_GND | UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); 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 * 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_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_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_MODE_POS | UCB_TS_CR_BIAS_ENA); 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 * 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_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_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_MODE_POS | UCB_TS_CR_BIAS_ENA); 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 * 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_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 * 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_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); - return (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)); + unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR); + 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(ucb, UCB_IE_CLEAR, 0); - ucb1400_reg_write(ucb, UCB_IE_FAL, UCB_IE_TSPX); + ucb1400_reg_write(ac97, UCB_IE_CLEAR, UCB_IE_TSPX); + ucb1400_reg_write(ac97, UCB_IE_CLEAR, 0); + 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) @@ -264,25 +160,24 @@ static void ucb1400_ts_event_release(struct input_dev *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; - isr = ucb1400_reg_read(ucb, UCB_IE_STATUS); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, isr); - ucb1400_reg_write(ucb, UCB_IE_CLEAR, 0); + isr = ucb1400_reg_read(ucb->ac97, UCB_IE_STATUS); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, isr); + ucb1400_reg_write(ucb->ac97, UCB_IE_CLEAR, 0); - if (isr & UCB_IE_TSPX) - ucb1400_ts_irq_disable(ucb); - else + if (isr & UCB_IE_TSPX) { + ucb1400_ts_irq_disable(ucb->ac97); + enable_irq(ucb->irq); + } else printk(KERN_ERR "ucb1400: unexpected IE_STATUS = %#x\n", isr); - - enable_irq(ucb->irq); } static int ucb1400_ts_thread(void *_ucb) { - struct ucb1400 *ucb = _ucb; + struct ucb1400_ts *ucb = _ucb; struct task_struct *tsk = current; int valid = 0; struct sched_param param = { .sched_priority = 1 }; @@ -301,19 +196,19 @@ static int ucb1400_ts_thread(void *_ucb) ucb1400_handle_pending_irq(ucb); } - ucb1400_adc_enable(ucb); + ucb1400_adc_enable(ucb->ac97); x = ucb1400_ts_read_xpos(ucb); y = ucb1400_ts_read_ypos(ucb); p = ucb1400_ts_read_pressure(ucb); - ucb1400_adc_disable(ucb); + ucb1400_adc_disable(ucb->ac97); /* Switch back to interrupt mode. */ - ucb1400_ts_mode_int(ucb); + ucb1400_ts_mode_int(ucb->ac97); msleep(10); - if (ucb1400_ts_pen_down(ucb)) { - ucb1400_ts_irq_enable(ucb); + if (ucb1400_ts_pen_down(ucb->ac97)) { + ucb1400_ts_irq_enable(ucb->ac97); /* * 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, - ucb->irq_pending || ucb->ts_restart || kthread_should_stop(), - timeout); + ucb->irq_pending || ucb->ts_restart || + kthread_should_stop(), timeout); } /* 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) { - struct ucb1400 *ucb = devid; + struct ucb1400_ts *ucb = devid; if (irqnr == 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) { - struct ucb1400 *ucb = input_get_drvdata(idev); + struct ucb1400_ts *ucb = input_get_drvdata(idev); int ret = 0; BUG_ON(ucb->ts_task); @@ -385,34 +280,14 @@ static int ucb1400_ts_open(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) kthread_stop(ucb->ts_task); - ucb1400_ts_irq_disable(ucb); - ucb1400_reg_write(ucb, UCB_TS_CR, 0); -} - -#ifdef CONFIG_PM -static int ucb1400_ts_resume(struct device *dev) -{ - struct ucb1400 *ucb = dev_get_drvdata(dev); - - if (ucb->ts_task) { - /* - * Restart the TS thread to ensure the - * TS interrupt mode is set up again - * after sleep. - */ - ucb->ts_restart = 1; - wake_up(&ucb->ts_wait); - } - return 0; + ucb1400_ts_irq_disable(ucb->ac97); + ucb1400_reg_write(ucb->ac97, UCB_TS_CR, 0); } -#else -#define ucb1400_ts_resume NULL -#endif #ifndef NO_IRQ #define NO_IRQ 0 @@ -422,25 +297,26 @@ static int ucb1400_ts_resume(struct device *dev) * Try to probe our interrupt, rather than relying on lots of * hard-coded machine dependencies. */ -static int ucb1400_detect_irq(struct ucb1400 *ucb) +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, 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); + 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, UCB_ADC_CR, UCB_ADC_ENA); - ucb1400_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + 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, UCB_ADC_DATA) & UCB_ADC_DAT_VALID)) { + 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"); @@ -448,13 +324,13 @@ static int ucb1400_detect_irq(struct ucb1400 *ucb) return -ENODEV; } } - ucb1400_reg_write(ucb, UCB_ADC_CR, 0); + ucb1400_reg_write(ucb->ac97, 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); + 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); @@ -464,36 +340,25 @@ static int ucb1400_detect_irq(struct ucb1400 *ucb) return 0; } -static int ucb1400_ts_probe(struct device *dev) +static int ucb1400_ts_probe(struct platform_device *dev) { - struct ucb1400 *ucb; - struct input_dev *idev; - int error, id, x_res, y_res; + int error, x_res, y_res; + struct ucb1400_ts *ucb = dev->dev.platform_data; - ucb = kzalloc(sizeof(struct ucb1400), GFP_KERNEL); - idev = input_allocate_device(); - if (!ucb || !idev) { + ucb->ts_idev = input_allocate_device(); + if (!ucb->ts_idev) { error = -ENOMEM; - goto err_free_devs; + goto err; } - 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); + 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) { @@ -503,80 +368,101 @@ static int ucb1400_ts_probe(struct device *dev) } printk(KERN_DEBUG "UCB1400: found IRQ %d\n", ucb->irq); - input_set_drvdata(idev, ucb); + input_set_drvdata(ucb->ts_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); + 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); + ucb1400_adc_enable(ucb->ac97); x_res = ucb1400_ts_read_xres(ucb); y_res = ucb1400_ts_read_yres(ucb); - ucb1400_adc_disable(ucb); + ucb1400_adc_disable(ucb->ac97); 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); + 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(idev); + error = input_register_device(ucb->ts_idev); if (error) goto err_free_irq; - dev_set_drvdata(dev, ucb); return 0; - err_free_irq: +err_free_irq: free_irq(ucb->irq, ucb); - err_free_devs: - input_free_device(idev); - kfree(ucb); +err_free_devs: + input_free_device(ucb->ts_idev); +err: return error; + } -static int ucb1400_ts_remove(struct device *dev) +static int ucb1400_ts_remove(struct platform_device *dev) { - struct ucb1400 *ucb = dev_get_drvdata(dev); + struct ucb1400_ts *ucb = dev->dev.platform_data; 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, +#ifdef CONFIG_PM +static int ucb1400_ts_resume(struct platform_device *dev) +{ + struct ucb1400_ts *ucb = platform_get_drvdata(dev); + + if (ucb->ts_task) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ucb->ts_restart = 1; + wake_up(&ucb->ts_wait); + } + return 0; +} +#else +#define ucb1400_ts_resume NULL +#endif + +static struct platform_driver ucb1400_ts_driver = { + .probe = ucb1400_ts_probe, + .remove = ucb1400_ts_remove, + .resume = ucb1400_ts_resume, + .driver = { + .name = "ucb1400_ts", + }, }; 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) { - driver_unregister(&ucb1400_ts_driver); + platform_driver_unregister(&ucb1400_ts_driver); } module_param(adcsync, bool, 0444); MODULE_PARM_DESC(adcsync, "Synchronize touch readings with ADCSYNC pin."); 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_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_exit(ucb1400_ts_exit); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 9556262dda5a..5b14262af017 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -24,13 +24,6 @@ config LEDS_ATMEL_PWM This option enables support for LEDs driven using outputs of the dedicated PWM controller found on newer Atmel SOCs. -config LEDS_CORGI - tristate "LED Support for the Sharp SL-C7x0 series" - depends on LEDS_CLASS && PXA_SHARP_C7xx - help - This option enables support for the LEDs on Sharp Zaurus - SL-C7x0 series (C700, C750, C760, C860). - config LEDS_LOCOMO tristate "LED Support for Locomo device" depends on LEDS_CLASS && SHARP_LOCOMO @@ -38,13 +31,6 @@ config LEDS_LOCOMO This option enables support for the LEDs on Sharp Locomo. Zaurus models SL-5500 and SL-5600. -config LEDS_SPITZ - tristate "LED Support for the Sharp SL-Cxx00 series" - depends on LEDS_CLASS && PXA_SHARP_Cxx00 - help - This option enables support for the LEDs on Sharp Zaurus - SL-Cxx00 series (C1000, C3000, C3100). - config LEDS_S3C24XX tristate "LED Support for Samsung S3C24XX GPIO LEDs" depends on LEDS_CLASS && ARCH_S3C2410 diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index ff7982b44565..3a8e6a04363c 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -6,9 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o -obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o -obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o diff --git a/drivers/leds/leds-corgi.c b/drivers/leds/leds-corgi.c deleted file mode 100644 index bc2dcd89f635..000000000000 --- a/drivers/leds/leds-corgi.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * LED Triggers Core - * - * Copyright 2005-2006 Openedhand Ltd. - * - * Author: Richard Purdie <rpurdie@openedhand.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. - * - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/leds.h> -#include <mach/corgi.h> -#include <mach/hardware.h> -#include <mach/pxa-regs.h> -#include <asm/hardware/scoop.h> - -static void corgiled_amber_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - GPSR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE); - else - GPCR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE); -} - -static void corgiled_green_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_LED_GREEN); - else - reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_LED_GREEN); -} - -static struct led_classdev corgi_amber_led = { - .name = "corgi:amber:charge", - .default_trigger = "sharpsl-charge", - .brightness_set = corgiled_amber_set, -}; - -static struct led_classdev corgi_green_led = { - .name = "corgi:green:mail", - .default_trigger = "nand-disk", - .brightness_set = corgiled_green_set, -}; - -#ifdef CONFIG_PM -static int corgiled_suspend(struct platform_device *dev, pm_message_t state) -{ -#ifdef CONFIG_LEDS_TRIGGERS - if (corgi_amber_led.trigger && - strcmp(corgi_amber_led.trigger->name, "sharpsl-charge")) -#endif - led_classdev_suspend(&corgi_amber_led); - led_classdev_suspend(&corgi_green_led); - return 0; -} - -static int corgiled_resume(struct platform_device *dev) -{ - led_classdev_resume(&corgi_amber_led); - led_classdev_resume(&corgi_green_led); - return 0; -} -#endif - -static int corgiled_probe(struct platform_device *pdev) -{ - int ret; - - ret = led_classdev_register(&pdev->dev, &corgi_amber_led); - if (ret < 0) - return ret; - - ret = led_classdev_register(&pdev->dev, &corgi_green_led); - if (ret < 0) - led_classdev_unregister(&corgi_amber_led); - - return ret; -} - -static int corgiled_remove(struct platform_device *pdev) -{ - led_classdev_unregister(&corgi_amber_led); - led_classdev_unregister(&corgi_green_led); - return 0; -} - -static struct platform_driver corgiled_driver = { - .probe = corgiled_probe, - .remove = corgiled_remove, -#ifdef CONFIG_PM - .suspend = corgiled_suspend, - .resume = corgiled_resume, -#endif - .driver = { - .name = "corgi-led", - .owner = THIS_MODULE, - }, -}; - -static int __init corgiled_init(void) -{ - return platform_driver_register(&corgiled_driver); -} - -static void __exit corgiled_exit(void) -{ - platform_driver_unregister(&corgiled_driver); -} - -module_init(corgiled_init); -module_exit(corgiled_exit); - -MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); -MODULE_DESCRIPTION("Corgi LED driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:corgi-led"); diff --git a/drivers/leds/leds-spitz.c b/drivers/leds/leds-spitz.c deleted file mode 100644 index 178831c64bfb..000000000000 --- a/drivers/leds/leds-spitz.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * LED Triggers Core - * - * Copyright 2005-2006 Openedhand Ltd. - * - * Author: Richard Purdie <rpurdie@openedhand.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. - * - */ - -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/leds.h> -#include <asm/hardware/scoop.h> -#include <asm/mach-types.h> -#include <mach/hardware.h> -#include <mach/pxa-regs.h> -#include <mach/spitz.h> - -static void spitzled_amber_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE); - else - reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE); -} - -static void spitzled_green_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - if (value) - set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_GREEN); - else - reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_GREEN); -} - -static struct led_classdev spitz_amber_led = { - .name = "spitz:amber:charge", - .default_trigger = "sharpsl-charge", - .brightness_set = spitzled_amber_set, -}; - -static struct led_classdev spitz_green_led = { - .name = "spitz:green:hddactivity", - .default_trigger = "ide-disk", - .brightness_set = spitzled_green_set, -}; - -#ifdef CONFIG_PM -static int spitzled_suspend(struct platform_device *dev, pm_message_t state) -{ -#ifdef CONFIG_LEDS_TRIGGERS - if (spitz_amber_led.trigger && - strcmp(spitz_amber_led.trigger->name, "sharpsl-charge")) -#endif - led_classdev_suspend(&spitz_amber_led); - led_classdev_suspend(&spitz_green_led); - return 0; -} - -static int spitzled_resume(struct platform_device *dev) -{ - led_classdev_resume(&spitz_amber_led); - led_classdev_resume(&spitz_green_led); - return 0; -} -#endif - -static int spitzled_probe(struct platform_device *pdev) -{ - int ret; - - if (machine_is_akita()) { - spitz_green_led.name = "spitz:green:mail"; - spitz_green_led.default_trigger = "nand-disk"; - } - - ret = led_classdev_register(&pdev->dev, &spitz_amber_led); - if (ret < 0) - return ret; - - ret = led_classdev_register(&pdev->dev, &spitz_green_led); - if (ret < 0) - led_classdev_unregister(&spitz_amber_led); - - return ret; -} - -static int spitzled_remove(struct platform_device *pdev) -{ - led_classdev_unregister(&spitz_amber_led); - led_classdev_unregister(&spitz_green_led); - - return 0; -} - -static struct platform_driver spitzled_driver = { - .probe = spitzled_probe, - .remove = spitzled_remove, -#ifdef CONFIG_PM - .suspend = spitzled_suspend, - .resume = spitzled_resume, -#endif - .driver = { - .name = "spitz-led", - .owner = THIS_MODULE, - }, -}; - -static int __init spitzled_init(void) -{ - return platform_driver_register(&spitzled_driver); -} - -static void __exit spitzled_exit(void) -{ - platform_driver_unregister(&spitzled_driver); -} - -module_init(spitzled_init); -module_exit(spitzled_exit); - -MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); -MODULE_DESCRIPTION("Spitz LED driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:spitz-led"); diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 388cf94055d3..cf96b2cc4f1c 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -1025,9 +1025,9 @@ static int pxa_camera_resume(struct soc_camera_device *icd) struct pxa_camera_dev *pcdev = ici->priv; int i = 0, ret = 0; - DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD; - DRCMR69 = pcdev->dma_chans[1] | DRCMR_MAPVLD; - DRCMR70 = pcdev->dma_chans[2] | DRCMR_MAPVLD; + DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD; + DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD; + DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD; CICR0 = pcdev->save_cicr[i++] & ~CICR0_ENB; CICR1 = pcdev->save_cicr[i++]; @@ -1171,9 +1171,9 @@ static int pxa_camera_probe(struct platform_device *pdev) } dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); - DRCMR68 = pcdev->dma_chans[0] | DRCMR_MAPVLD; - DRCMR69 = pcdev->dma_chans[1] | DRCMR_MAPVLD; - DRCMR70 = pcdev->dma_chans[2] | DRCMR_MAPVLD; + DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD; + DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD; + DRCMR(70) = pcdev->dma_chans[2] | DRCMR_MAPVLD; /* request irq */ err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 10c44d3fe01a..5dba1651f9cc 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -50,6 +50,15 @@ config HTC_PASIC3 HTC Magician devices, respectively. Actual functionality is 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_TMIO bool default n diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 03ad239ecef0..6abebe364419 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -22,3 +22,4 @@ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o ifeq ($(CONFIG_SA1100_ASSABET),y) obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o endif +obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c new file mode 100644 index 000000000000..178159e264ce --- /dev/null +++ b/drivers/mfd/ucb1400_core.c @@ -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"); diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 55093ad132ca..ebfaa9960939 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -520,7 +520,7 @@ static int pxamci_probe(struct platform_device *pdev) /* * Block length register is only 10 bits before PXA27x. */ - mmc->max_blk_size = (cpu_is_pxa21x() || cpu_is_pxa25x()) ? 1023 : 2048; + mmc->max_blk_size = cpu_is_pxa25x() ? 1023 : 2048; /* * Block count register is 16 bits. @@ -554,7 +554,7 @@ static int pxamci_probe(struct platform_device *pdev) MMC_VDD_32_33|MMC_VDD_33_34; mmc->caps = 0; host->cmdat = 0; - if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) { + if (!cpu_is_pxa25x()) { mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; host->cmdat |= CMDAT_SDIO_INT_EN; if (cpu_is_pxa300() || cpu_is_pxa310()) diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index 9eba3f04783a..fa129c09bca8 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c @@ -156,7 +156,7 @@ static int cmx270_init(void) int mtd_parts_nb = 0; int ret; - if (!machine_is_armcore()) + if (!(machine_is_armcore() && cpu_is_pxa27x())) return -ENODEV; ret = gpio_request(GPIO_NAND_CS, "NAND CS"); diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 4aa61a1a3d55..c5b02b66f756 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -572,8 +572,8 @@ static void pxa_irda_startup(struct pxa_irda *si) ICCR2 = ICCR2_TXP | ICCR2_TRIG_32; /* configure DMAC */ - DRCMR17 = si->rxdma | DRCMR_MAPVLD; - DRCMR18 = si->txdma | DRCMR_MAPVLD; + DRCMR(17) = si->rxdma | DRCMR_MAPVLD; + DRCMR(18) = si->txdma | DRCMR_MAPVLD; /* force SIR reinitialization */ si->speed = 4000000; @@ -602,8 +602,8 @@ static void pxa_irda_shutdown(struct pxa_irda *si) /* disable the STUART or FICP clocks */ pxa_irda_disable_clk(si); - DRCMR17 = 0; - DRCMR18 = 0; + DRCMR(17) = 0; + DRCMR(18) = 0; local_irq_restore(flags); diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index 8322e7f37af5..160873f0a817 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -43,7 +43,8 @@ #if defined(CONFIG_ARCH_LUBBOCK) ||\ defined(CONFIG_MACH_MAINSTONE) ||\ defined(CONFIG_MACH_ZYLONITE) ||\ - defined(CONFIG_MACH_LITTLETON) + defined(CONFIG_MACH_LITTLETON) ||\ + defined(CONFIG_ARCH_VIPER) #include <asm/mach-types.h> @@ -778,14 +779,6 @@ smc_pxa_dma_irq(int dma, void *dummy) #define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode #define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb #define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb -#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect -#define RPC_LED_RES (0x01) // LED = Reserved -#define RPC_LED_10 (0x02) // LED = 10Mbps link detect -#define RPC_LED_FD (0x03) // LED = Full Duplex Mode -#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred -#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect -#define RPC_LED_TX (0x06) // LED = TX packet occurred -#define RPC_LED_RX (0x07) // LED = RX packet occurred #ifndef RPC_LSA_DEFAULT #define RPC_LSA_DEFAULT RPC_LED_100 diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index e0f884034c9f..f57eeae3830a 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -220,7 +220,8 @@ config PCMCIA_PXA2XX tristate "PXA2xx support" depends on ARM && ARCH_PXA && PCMCIA depends on (ARCH_LUBBOCK || MACH_MAINSTONE || PXA_SHARPSL \ - || MACH_ARMCORE || ARCH_PXA_PALM) + || MACH_ARMCORE || ARCH_PXA_PALM || TRIZEPS_PCMCIA \ + || ARCH_VIPER) help Say Y here to include support for the PXA2xx PCMCIA controller diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index 269a9e913ba2..a87902de8d3b 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o obj-$(CONFIG_HD64465_PCMCIA) += hd64465_ss.o obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_core.o sa1100_cs.o obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_core.o sa1111_cs.o -obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o pxa2xx_cs.o obj-$(CONFIG_M32R_PCC) += m32r_pcc.o obj-$(CONFIG_M32R_CFC) += m32r_cfc.o obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o @@ -68,9 +67,14 @@ sa1100_cs-$(CONFIG_SA1100_H3600) += sa1100_h3600.o sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o sa1100_cs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o -pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o -pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o -pxa2xx_cs-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o -pxa2xx_cs-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x270.o -pxa2xx_cs-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o +pxa2xx_lubbock_cs-y += pxa2xx_lubbock.o sa1111_generic.o +pxa2xx-obj-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock_cs.o +pxa2xx-obj-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o +pxa2xx-obj-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o +pxa2xx-obj-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x2xx.o pxa2xx_cm_x255.o pxa2xx_cm_x270.o +pxa2xx-obj-$(CONFIG_ARCH_VIPER) += pxa2xx_viper.o +pxa2xx-obj-$(CONFIG_TRIZEPS_PCMCIA) += pxa2xx_trizeps.o +pxa2xx-obj-$(CONFIG_MACH_PALMTX) += pxa2xx_palmtx.o +pxa2xx-obj-$(CONFIG_MACH_PALMLD) += pxa2xx_palmld.o +obj-$(CONFIG_PCMCIA_PXA2XX) += pxa2xx_core.o $(pxa2xx-obj-y) diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 1b07af5a2ed3..13f1e0fd3f31 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -30,6 +30,7 @@ #include <asm/system.h> #include <mach/pxa-regs.h> #include <mach/pxa2xx-regs.h> +#include <asm/mach-types.h> #include <pcmcia/cs_types.h> #include <pcmcia/ss.h> @@ -166,18 +167,32 @@ pxa2xx_pcmcia_frequency_change(struct soc_pcmcia_socket *skt, } #endif +static void pxa2xx_configure_sockets(struct device *dev) +{ + struct pcmcia_low_level *ops = dev->platform_data; + + /* + * We have at least one socket, so set MECR:CIT + * (Card Is There) + */ + MECR |= MECR_CIT; + + /* Set MECR:NOS (Number Of Sockets) */ + if (ops->nr > 1 || machine_is_viper()) + MECR |= MECR_NOS; + else + MECR &= ~MECR_NOS; +} + int __pxa2xx_drv_pcmcia_probe(struct device *dev) { int ret; struct pcmcia_low_level *ops; - int first, nr; if (!dev || !dev->platform_data) return -ENODEV; ops = (struct pcmcia_low_level *)dev->platform_data; - first = ops->first; - nr = ops->nr; /* Provide our PXA2xx specific timing routines. */ ops->set_timing = pxa2xx_pcmcia_set_timing; @@ -185,21 +200,10 @@ int __pxa2xx_drv_pcmcia_probe(struct device *dev) ops->frequency_change = pxa2xx_pcmcia_frequency_change; #endif - ret = soc_common_drv_pcmcia_probe(dev, ops, first, nr); + ret = soc_common_drv_pcmcia_probe(dev, ops, ops->first, ops->nr); - if (ret == 0) { - /* - * We have at least one socket, so set MECR:CIT - * (Card Is There) - */ - MECR |= MECR_CIT; - - /* Set MECR:NOS (Number Of Sockets) */ - if (nr > 1) - MECR |= MECR_NOS; - else - MECR &= ~MECR_NOS; - } + if (!ret) + pxa2xx_configure_sockets(dev); return ret; } @@ -223,11 +227,7 @@ static int pxa2xx_drv_pcmcia_suspend(struct platform_device *dev, pm_message_t s static int pxa2xx_drv_pcmcia_resume(struct platform_device *dev) { - struct pcmcia_low_level *ops = dev->dev.platform_data; - int nr = ops ? ops->nr : 0; - - MECR = nr > 1 ? MECR_CIT | MECR_NOS : (nr > 0 ? MECR_CIT : 0); - + pxa2xx_configure_sockets(&dev->dev); return pcmcia_socket_dev_resume(&dev->dev); } diff --git a/drivers/pcmcia/pxa2xx_cm_x255.c b/drivers/pcmcia/pxa2xx_cm_x255.c new file mode 100644 index 000000000000..7c8bcb476622 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_cm_x255.c @@ -0,0 +1,154 @@ +/* + * linux/drivers/pcmcia/pxa/pxa_cm_x255.c + * + * 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. + * + * Compulab Ltd., 2003, 2007, 2008 + * Mike Rapoport <mike@compulab.co.il> + * + */ + +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <linux/gpio.h> + +#include <asm/mach-types.h> +#include <mach/pxa-regs.h> + +#include "soc_common.h" + +#define GPIO_PCMCIA_SKTSEL (54) +#define GPIO_PCMCIA_S0_CD_VALID (16) +#define GPIO_PCMCIA_S1_CD_VALID (17) +#define GPIO_PCMCIA_S0_RDYINT (6) +#define GPIO_PCMCIA_S1_RDYINT (8) +#define GPIO_PCMCIA_RESET (9) + +#define PCMCIA_S0_CD_VALID IRQ_GPIO(GPIO_PCMCIA_S0_CD_VALID) +#define PCMCIA_S1_CD_VALID IRQ_GPIO(GPIO_PCMCIA_S1_CD_VALID) +#define PCMCIA_S0_RDYINT IRQ_GPIO(GPIO_PCMCIA_S0_RDYINT) +#define PCMCIA_S1_RDYINT IRQ_GPIO(GPIO_PCMCIA_S1_RDYINT) + + +static struct pcmcia_irqs irqs[] = { + { 0, PCMCIA_S0_CD_VALID, "PCMCIA0 CD" }, + { 1, PCMCIA_S1_CD_VALID, "PCMCIA1 CD" }, +}; + +static int cmx255_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset"); + if (ret) + return ret; + gpio_direction_output(GPIO_PCMCIA_RESET, 0); + + skt->irq = skt->nr == 0 ? PCMCIA_S0_RDYINT : PCMCIA_S1_RDYINT; + ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + if (!ret) + gpio_free(GPIO_PCMCIA_RESET); + + return ret; +} + +static void cmx255_pcmcia_shutdown(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + gpio_free(GPIO_PCMCIA_RESET); +} + + +static void cmx255_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + int cd = skt->nr ? GPIO_PCMCIA_S1_CD_VALID : GPIO_PCMCIA_S0_CD_VALID; + int rdy = skt->nr ? GPIO_PCMCIA_S0_RDYINT : GPIO_PCMCIA_S1_RDYINT; + + state->detect = !gpio_get_value(cd); + state->ready = !!gpio_get_value(rdy); + state->bvd1 = 1; + state->bvd2 = 1; + state->vs_3v = 0; + state->vs_Xv = 0; + state->wrprot = 0; /* not available */ +} + + +static int cmx255_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + switch (skt->nr) { + case 0: + if (state->flags & SS_RESET) { + gpio_set_value(GPIO_PCMCIA_SKTSEL, 0); + udelay(1); + gpio_set_value(GPIO_PCMCIA_RESET, 1); + udelay(10); + gpio_set_value(GPIO_PCMCIA_RESET, 0); + } + break; + case 1: + if (state->flags & SS_RESET) { + gpio_set_value(GPIO_PCMCIA_SKTSEL, 1); + udelay(1); + gpio_set_value(GPIO_PCMCIA_RESET, 1); + udelay(10); + gpio_set_value(GPIO_PCMCIA_RESET, 0); + } + break; + } + + return 0; +} + +static void cmx255_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void cmx255_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + + +static struct pcmcia_low_level cmx255_pcmcia_ops __initdata = { + .owner = THIS_MODULE, + .hw_init = cmx255_pcmcia_hw_init, + .hw_shutdown = cmx255_pcmcia_shutdown, + .socket_state = cmx255_pcmcia_socket_state, + .configure_socket = cmx255_pcmcia_configure_socket, + .socket_init = cmx255_pcmcia_socket_init, + .socket_suspend = cmx255_pcmcia_socket_suspend, + .nr = 1, +}; + +static struct platform_device *cmx255_pcmcia_device; + +int __init cmx255_pcmcia_init(void) +{ + int ret; + + cmx255_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + + if (!cmx255_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(cmx255_pcmcia_device, &cmx255_pcmcia_ops, + sizeof(cmx255_pcmcia_ops)); + + if (ret == 0) { + printk(KERN_INFO "Registering cm-x255 PCMCIA interface.\n"); + ret = platform_device_add(cmx255_pcmcia_device); + } + + if (ret) + platform_device_put(cmx255_pcmcia_device); + + return ret; +} + +void __exit cmx255_pcmcia_exit(void) +{ + platform_device_unregister(cmx255_pcmcia_device); +} diff --git a/drivers/pcmcia/pxa2xx_cm_x270.c b/drivers/pcmcia/pxa2xx_cm_x270.c index bcff5cfed051..6c3aac377126 100644 --- a/drivers/pcmcia/pxa2xx_cm_x270.c +++ b/drivers/pcmcia/pxa2xx_cm_x270.c @@ -105,13 +105,10 @@ static struct pcmcia_low_level cmx270_pcmcia_ops __initdata = { static struct platform_device *cmx270_pcmcia_device; -static int __init cmx270_pcmcia_init(void) +int __init cmx270_pcmcia_init(void) { int ret; - if (!machine_is_armcore()) - return -ENODEV; - cmx270_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); if (!cmx270_pcmcia_device) @@ -131,14 +128,7 @@ static int __init cmx270_pcmcia_init(void) return ret; } -static void __exit cmx270_pcmcia_exit(void) +void __exit cmx270_pcmcia_exit(void) { platform_device_unregister(cmx270_pcmcia_device); } - -module_init(cmx270_pcmcia_init); -module_exit(cmx270_pcmcia_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); -MODULE_DESCRIPTION("CM-x270 PCMCIA driver"); diff --git a/drivers/pcmcia/pxa2xx_cm_x2xx.c b/drivers/pcmcia/pxa2xx_cm_x2xx.c new file mode 100644 index 000000000000..4f09506ad8d4 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_cm_x2xx.c @@ -0,0 +1,49 @@ +/* + * linux/drivers/pcmcia/pxa/pxa_cm_x2xx.c + * + * 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. + * + * Compulab Ltd., 2003, 2007, 2008 + * Mike Rapoport <mike@compulab.co.il> + * + */ + +#include <linux/module.h> + +#include <asm/system.h> +#include <asm/mach-types.h> +#include <mach/system.h> + +int cmx255_pcmcia_init(void); +int cmx270_pcmcia_init(void); +void cmx255_pcmcia_exit(void); +void cmx270_pcmcia_exit(void); + +static int __init cmx2xx_pcmcia_init(void) +{ + int ret = -ENODEV; + + if (machine_is_armcore() && cpu_is_pxa25x()) + ret = cmx255_pcmcia_init(); + else if (machine_is_armcore() && cpu_is_pxa27x()) + ret = cmx270_pcmcia_init(); + + return ret; +} + +static void __exit cmx2xx_pcmcia_exit(void) +{ + if (machine_is_armcore() && cpu_is_pxa25x()) + cmx255_pcmcia_exit(); + else if (machine_is_armcore() && cpu_is_pxa27x()) + cmx270_pcmcia_exit(); +} + +module_init(cmx2xx_pcmcia_init); +module_exit(cmx2xx_pcmcia_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); +MODULE_DESCRIPTION("CM-x2xx PCMCIA driver"); diff --git a/drivers/pcmcia/pxa2xx_palmld.c b/drivers/pcmcia/pxa2xx_palmld.c new file mode 100644 index 000000000000..1736c67e547e --- /dev/null +++ b/drivers/pcmcia/pxa2xx_palmld.c @@ -0,0 +1,151 @@ +/* + * linux/drivers/pcmcia/pxa2xx_palmld.c + * + * Driver for Palm LifeDrive PCMCIA + * + * Copyright (C) 2006 Alex Osborne <ato@meshy.org> + * Copyright (C) 2007-2008 Marek Vasut <marek.vasut@gmail.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. + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> + +#include <asm/mach-types.h> +#include <mach/palmld.h> +#include "soc_common.h" + +static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret; + + ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_POWER, "PCMCIA PWR"); + if (ret) + goto err1; + ret = gpio_direction_output(GPIO_NR_PALMLD_PCMCIA_POWER, 0); + if (ret) + goto err2; + + ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_RESET, "PCMCIA RST"); + if (ret) + goto err2; + ret = gpio_direction_output(GPIO_NR_PALMLD_PCMCIA_RESET, 1); + if (ret) + goto err3; + + ret = gpio_request(GPIO_NR_PALMLD_PCMCIA_READY, "PCMCIA RDY"); + if (ret) + goto err3; + ret = gpio_direction_input(GPIO_NR_PALMLD_PCMCIA_READY); + if (ret) + goto err4; + + skt->irq = IRQ_GPIO(GPIO_NR_PALMLD_PCMCIA_READY); + return 0; + +err4: + gpio_free(GPIO_NR_PALMLD_PCMCIA_READY); +err3: + gpio_free(GPIO_NR_PALMLD_PCMCIA_RESET); +err2: + gpio_free(GPIO_NR_PALMLD_PCMCIA_POWER); +err1: + return ret; +} + +static void palmld_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + gpio_free(GPIO_NR_PALMLD_PCMCIA_READY); + gpio_free(GPIO_NR_PALMLD_PCMCIA_RESET); + gpio_free(GPIO_NR_PALMLD_PCMCIA_POWER); +} + +static void palmld_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + state->detect = 1; /* always inserted */ + state->ready = !!gpio_get_value(GPIO_NR_PALMLD_PCMCIA_READY); + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; + state->vs_Xv = 0; +} + +static int palmld_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + gpio_set_value(GPIO_NR_PALMLD_PCMCIA_POWER, 1); + gpio_set_value(GPIO_NR_PALMLD_PCMCIA_RESET, + !!(state->flags & SS_RESET)); + + return 0; +} + +static void palmld_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void palmld_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level palmld_pcmcia_ops = { + .owner = THIS_MODULE, + + .first = 0, + .nr = 2, + + .hw_init = palmld_pcmcia_hw_init, + .hw_shutdown = palmld_pcmcia_hw_shutdown, + + .socket_state = palmld_pcmcia_socket_state, + .configure_socket = palmld_pcmcia_configure_socket, + + .socket_init = palmld_pcmcia_socket_init, + .socket_suspend = palmld_pcmcia_socket_suspend, +}; + +static struct platform_device *palmld_pcmcia_device; + +static int __init palmld_pcmcia_init(void) +{ + int ret; + + if (!machine_is_palmld()) + return -ENODEV; + + palmld_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!palmld_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(palmld_pcmcia_device, &palmld_pcmcia_ops, + sizeof(palmld_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(palmld_pcmcia_device); + + if (ret) + platform_device_put(palmld_pcmcia_device); + + return ret; +} + +static void __exit palmld_pcmcia_exit(void) +{ + platform_device_unregister(palmld_pcmcia_device); +} + +module_init(palmld_pcmcia_init); +module_exit(palmld_pcmcia_exit); + +MODULE_AUTHOR("Alex Osborne <ato@meshy.org>," + " Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("PCMCIA support for Palm LifeDrive"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/pxa2xx_trizeps4.c b/drivers/pcmcia/pxa2xx_trizeps4.c new file mode 100644 index 000000000000..36c7a0b324d2 --- /dev/null +++ b/drivers/pcmcia/pxa2xx_trizeps4.c @@ -0,0 +1,256 @@ +/* + * linux/drivers/pcmcia/pxa2xx_trizeps4.c + * + * TRIZEPS PCMCIA specific routines. + * + * Author: Jürgen Schindele + * Created: 20 02, 2006 + * Copyright: Jürgen Schindele + * + * 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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +#include <asm/mach-types.h> +#include <asm/irq.h> + +#include <mach/hardware.h> +#include <mach/pxa-regs.h> +#include <mach/trizeps4.h> + +#include "soc_common.h" + +extern void board_pcmcia_power(int power); + +static struct pcmcia_irqs irqs[] = { + { 0, IRQ_GPIO(GPIO_PCD), "cs0_cd" } + /* on other baseboards we can have more inputs */ +}; + +static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + int ret, i; + /* we dont have voltage/card/ready detection + * so we dont need interrupts for it + */ + switch (skt->nr) { + case 0: + if (gpio_request(GPIO_PRDY, "cf_irq") < 0) { + pr_err("%s: sock %d unable to request gpio %d\n", __func__, + skt->nr, GPIO_PRDY); + return -EBUSY; + } + if (gpio_direction_input(GPIO_PRDY) < 0) { + pr_err("%s: sock %d unable to set input gpio %d\n", __func__, + skt->nr, GPIO_PRDY); + gpio_free(GPIO_PRDY); + return -EINVAL; + } + skt->irq = IRQ_GPIO(GPIO_PRDY); + break; + +#ifndef CONFIG_MACH_TRIZEPS_CONXS + case 1: +#endif + default: + break; + } + /* release the reset of this card */ + pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->irq); + + /* supplementory irqs for the socket */ + for (i = 0; i < ARRAY_SIZE(irqs); i++) { + if (irqs[i].sock != skt->nr) + continue; + if (gpio_request(IRQ_TO_GPIO(irqs[i].irq), irqs[i].str) < 0) { + pr_err("%s: sock %d unable to request gpio %d\n", + __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq)); + ret = -EBUSY; + goto error; + } + if (gpio_direction_input(IRQ_TO_GPIO(irqs[i].irq)) < 0) { + pr_err("%s: sock %d unable to set input gpio %d\n", + __func__, skt->nr, IRQ_TO_GPIO(irqs[i].irq)); + ret = -EINVAL; + goto error; + } + } + return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + +error: + for (; i >= 0; i--) { + gpio_free(IRQ_TO_GPIO(irqs[i].irq)); + } + return (ret); +} + +static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + int i; + /* free allocated gpio's */ + gpio_free(GPIO_PRDY); + for (i = 0; i < ARRAY_SIZE(irqs); i++) + gpio_free(IRQ_TO_GPIO(irqs[i].irq)); +} + +static unsigned long trizeps_pcmcia_status[2]; + +static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + unsigned short status = 0, change; + status = CFSR_readw(); + change = (status ^ trizeps_pcmcia_status[skt->nr]) & + ConXS_CFSR_BVD_MASK; + if (change) { + trizeps_pcmcia_status[skt->nr] = status; + if (status & ConXS_CFSR_BVD1) { + /* enable_irq empty */ + } else { + /* disable_irq empty */ + } + } + + switch (skt->nr) { + case 0: + /* just fill in fix states */ + state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1; + state->ready = gpio_get_value(GPIO_PRDY) ? 1 : 0; + state->bvd1 = (status & ConXS_CFSR_BVD1) ? 1 : 0; + state->bvd2 = (status & ConXS_CFSR_BVD2) ? 1 : 0; + state->vs_3v = (status & ConXS_CFSR_VS1) ? 0 : 1; + state->vs_Xv = (status & ConXS_CFSR_VS2) ? 0 : 1; + state->wrprot = 0; /* not available */ + break; + +#ifndef CONFIG_MACH_TRIZEPS_CONXS + /* on ConXS we only have one slot. Second is inactive */ + case 1: + state->detect = 0; + state->ready = 0; + state->bvd1 = 0; + state->bvd2 = 0; + state->vs_3v = 0; + state->vs_Xv = 0; + state->wrprot = 0; + break; + +#endif + } +} + +static int trizeps_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + int ret = 0; + unsigned short power = 0; + + /* we do nothing here just check a bit */ + switch (state->Vcc) { + case 0: power &= 0xfc; break; + case 33: power |= ConXS_BCR_S0_VCC_3V3; break; + case 50: + pr_err("%s(): Vcc 5V not supported in socket\n", __func__); + break; + default: + pr_err("%s(): bad Vcc %u\n", __func__, state->Vcc); + ret = -1; + } + + switch (state->Vpp) { + case 0: power &= 0xf3; break; + case 33: power |= ConXS_BCR_S0_VPP_3V3; break; + case 120: + pr_err("%s(): Vpp 12V not supported in socket\n", __func__); + break; + default: + if (state->Vpp != state->Vcc) { + pr_err("%s(): bad Vpp %u\n", __func__, state->Vpp); + ret = -1; + } + } + + switch (skt->nr) { + case 0: /* we only have 3.3V */ + board_pcmcia_power(power); + break; + +#ifndef CONFIG_MACH_TRIZEPS_CONXS + /* on ConXS we only have one slot. Second is inactive */ + case 1: +#endif + default: + break; + } + + return ret; +} + +static void trizeps_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ + /* default is on */ + board_pcmcia_power(0x9); +} + +static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ + board_pcmcia_power(0x0); +} + +static struct pcmcia_low_level trizeps_pcmcia_ops = { + .owner = THIS_MODULE, + .hw_init = trizeps_pcmcia_hw_init, + .hw_shutdown = trizeps_pcmcia_hw_shutdown, + .socket_state = trizeps_pcmcia_socket_state, + .configure_socket = trizeps_pcmcia_configure_socket, + .socket_init = trizeps_pcmcia_socket_init, + .socket_suspend = trizeps_pcmcia_socket_suspend, +#ifdef CONFIG_MACH_TRIZEPS_CONXS + .nr = 1, +#else + .nr = 2, +#endif + .first = 0, +}; + +static struct platform_device *trizeps_pcmcia_device; + +static int __init trizeps_pcmcia_init(void) +{ + int ret; + + trizeps_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!trizeps_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(trizeps_pcmcia_device, + &trizeps_pcmcia_ops, sizeof(trizeps_pcmcia_ops)); + + if (ret == 0) + ret = platform_device_add(trizeps_pcmcia_device); + + if (ret) + platform_device_put(trizeps_pcmcia_device); + + return ret; +} + +static void __exit trizeps_pcmcia_exit(void) +{ + platform_device_unregister(trizeps_pcmcia_device); +} + +fs_initcall(trizeps_pcmcia_init); +module_exit(trizeps_pcmcia_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Juergen Schindele"); +MODULE_ALIAS("platform:pxa2xx-pcmcia"); diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c new file mode 100644 index 000000000000..dd10481be7bf --- /dev/null +++ b/drivers/pcmcia/pxa2xx_viper.c @@ -0,0 +1,179 @@ +/* + * VIPER PCMCIA support + * Copyright 2004 Arcom Control Systems + * + * Maintained by Marc Zyngier <maz@misterjones.org> + * <marc.zyngier@altran.com> + * + * Based on: + * iPAQ h2200 PCMCIA support + * Copyright 2004 Koen Kooi <koen@vestingbar.nl> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> + +#include <pcmcia/ss.h> + +#include <asm/irq.h> + +#include <mach/pxa-regs.h> +#include <mach/viper.h> +#include <asm/mach-types.h> + +#include "soc_common.h" +#include "pxa2xx_base.h" + +static struct pcmcia_irqs irqs[] = { + { 0, gpio_to_irq(VIPER_CF_CD_GPIO), "PCMCIA_CD" } +}; + +static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + unsigned long flags; + + skt->irq = gpio_to_irq(VIPER_CF_RDY_GPIO); + + if (gpio_request(VIPER_CF_CD_GPIO, "CF detect")) + goto err_request_cd; + + if (gpio_request(VIPER_CF_RDY_GPIO, "CF ready")) + goto err_request_rdy; + + if (gpio_request(VIPER_CF_POWER_GPIO, "CF power")) + goto err_request_pwr; + + local_irq_save(flags); + + /* GPIO 82 is the CF power enable line. initially off */ + if (gpio_direction_output(VIPER_CF_POWER_GPIO, 0) || + gpio_direction_input(VIPER_CF_CD_GPIO) || + gpio_direction_input(VIPER_CF_RDY_GPIO)) { + local_irq_restore(flags); + goto err_dir; + } + + local_irq_restore(flags); + + return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); + +err_dir: + gpio_free(VIPER_CF_POWER_GPIO); +err_request_pwr: + gpio_free(VIPER_CF_RDY_GPIO); +err_request_rdy: + gpio_free(VIPER_CF_CD_GPIO); +err_request_cd: + printk(KERN_ERR "viper: Failed to setup PCMCIA GPIOs\n"); + return -1; +} + +/* + * Release all resources. + */ +static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + gpio_free(VIPER_CF_POWER_GPIO); + gpio_free(VIPER_CF_RDY_GPIO); + gpio_free(VIPER_CF_CD_GPIO); +} + +static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + state->detect = gpio_get_value(VIPER_CF_CD_GPIO) ? 0 : 1; + state->ready = gpio_get_value(VIPER_CF_RDY_GPIO) ? 1 : 0; + state->bvd1 = 1; + state->bvd2 = 1; + state->wrprot = 0; + state->vs_3v = 1; /* Can only apply 3.3V */ + state->vs_Xv = 0; +} + +static int viper_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + /* Silently ignore Vpp, output enable, speaker enable. */ + viper_cf_rst(state->flags & SS_RESET); + + /* Apply socket voltage */ + switch (state->Vcc) { + case 0: + gpio_set_value(VIPER_CF_POWER_GPIO, 0); + break; + case 33: + gpio_set_value(VIPER_CF_POWER_GPIO, 1); + break; + default: + printk(KERN_ERR "%s: Unsupported Vcc:%d\n", + __func__, state->Vcc); + return -1; + } + + return 0; +} + +static void viper_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void viper_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +static struct pcmcia_low_level viper_pcmcia_ops __initdata = { + .owner = THIS_MODULE, + .hw_init = viper_pcmcia_hw_init, + .hw_shutdown = viper_pcmcia_hw_shutdown, + .socket_state = viper_pcmcia_socket_state, + .configure_socket = viper_pcmcia_configure_socket, + .socket_init = viper_pcmcia_socket_init, + .socket_suspend = viper_pcmcia_socket_suspend, + .nr = 1, +}; + +static struct platform_device *viper_pcmcia_device; + +static int __init viper_pcmcia_init(void) +{ + int ret; + + if (!machine_is_viper()) + return -ENODEV; + + viper_pcmcia_device = platform_device_alloc("pxa2xx-pcmcia", -1); + if (!viper_pcmcia_device) + return -ENOMEM; + + ret = platform_device_add_data(viper_pcmcia_device, + &viper_pcmcia_ops, + sizeof(viper_pcmcia_ops)); + + if (!ret) + ret = platform_device_add(viper_pcmcia_device); + + if (ret) + platform_device_put(viper_pcmcia_device); + + return ret; +} + +static void __exit viper_pcmcia_exit(void) +{ + platform_device_unregister(viper_pcmcia_device); +} + +module_init(viper_pcmcia_init); +module_exit(viper_pcmcia_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 9ce55850271a..1982f8b42782 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -56,10 +56,10 @@ config BATTERY_TOSA Say Y to enable support for the battery on the Sharp Zaurus SL-6000 (tosa) models. -config BATTERY_PALMTX - tristate "Palm T|X battery" - depends on MACH_PALMTX +config BATTERY_WM97XX + bool "WM97xx generic battery driver" + depends on TOUCHSCREEN_WM97XX help - Say Y to enable support for the battery in Palm T|X. + Say Y to enable support for battery measured by WM97xx aux port. endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 4706bf8ff459..4e20026cc45a 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -21,4 +21,4 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o -obj-$(CONFIG_BATTERY_PALMTX) += palmtx_battery.o +obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
\ No newline at end of file diff --git a/drivers/power/palmtx_battery.c b/drivers/power/palmtx_battery.c deleted file mode 100644 index 7035bfa41c62..000000000000 --- a/drivers/power/palmtx_battery.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * linux/drivers/power/palmtx_battery.c - * - * Battery measurement code for Palm T|X Handheld computer - * - * based on tosa_battery.c - * - * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.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. - * - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/power_supply.h> -#include <linux/wm97xx.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> -#include <linux/gpio.h> - -#include <asm/mach-types.h> -#include <mach/palmtx.h> - -static DEFINE_MUTEX(bat_lock); -static struct work_struct bat_work; -struct mutex work_lock; -int bat_status = POWER_SUPPLY_STATUS_DISCHARGING; - -static unsigned long palmtx_read_bat(struct power_supply *bat_ps) -{ - return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, - WM97XX_AUX_ID3) * 1000 / 414; -} - -static unsigned long palmtx_read_temp(struct power_supply *bat_ps) -{ - return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, - WM97XX_AUX_ID2); -} - -static int palmtx_bat_get_property(struct power_supply *bat_ps, - enum power_supply_property psp, - union power_supply_propval *val) -{ - switch (psp) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = bat_status; - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = palmtx_read_bat(bat_ps); - break; - case POWER_SUPPLY_PROP_VOLTAGE_MAX: - case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: - val->intval = PALMTX_BAT_MAX_VOLTAGE; - break; - case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: - val->intval = PALMTX_BAT_MIN_VOLTAGE; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = palmtx_read_temp(bat_ps); - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = 1; - break; - default: - return -EINVAL; - } - return 0; -} - -static void palmtx_bat_external_power_changed(struct power_supply *bat_ps) -{ - schedule_work(&bat_work); -} - -static char *status_text[] = { - [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", - [POWER_SUPPLY_STATUS_CHARGING] = "Charging", - [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", -}; - -static void palmtx_bat_update(struct power_supply *bat_ps) -{ - int old_status = bat_status; - - mutex_lock(&work_lock); - - bat_status = gpio_get_value(GPIO_NR_PALMTX_POWER_DETECT) ? - POWER_SUPPLY_STATUS_CHARGING : - POWER_SUPPLY_STATUS_DISCHARGING; - - if (old_status != bat_status) { - pr_debug("%s %s -> %s\n", bat_ps->name, - status_text[old_status], - status_text[bat_status]); - power_supply_changed(bat_ps); - } - - mutex_unlock(&work_lock); -} - -static enum power_supply_property palmtx_bat_main_props[] = { - POWER_SUPPLY_PROP_STATUS, - POWER_SUPPLY_PROP_TECHNOLOGY, - POWER_SUPPLY_PROP_VOLTAGE_NOW, - POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, - POWER_SUPPLY_PROP_TEMP, - POWER_SUPPLY_PROP_PRESENT, -}; - -struct power_supply bat_ps = { - .name = "main-battery", - .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = palmtx_bat_main_props, - .num_properties = ARRAY_SIZE(palmtx_bat_main_props), - .get_property = palmtx_bat_get_property, - .external_power_changed = palmtx_bat_external_power_changed, - .use_for_apm = 1, -}; - -static void palmtx_bat_work(struct work_struct *work) -{ - palmtx_bat_update(&bat_ps); -} - -#ifdef CONFIG_PM -static int palmtx_bat_suspend(struct platform_device *dev, pm_message_t state) -{ - flush_scheduled_work(); - return 0; -} - -static int palmtx_bat_resume(struct platform_device *dev) -{ - schedule_work(&bat_work); - return 0; -} -#else -#define palmtx_bat_suspend NULL -#define palmtx_bat_resume NULL -#endif - -static int __devinit palmtx_bat_probe(struct platform_device *dev) -{ - int ret = 0; - - if (!machine_is_palmtx()) - return -ENODEV; - - mutex_init(&work_lock); - - INIT_WORK(&bat_work, palmtx_bat_work); - - ret = power_supply_register(&dev->dev, &bat_ps); - if (!ret) - schedule_work(&bat_work); - - return ret; -} - -static int __devexit palmtx_bat_remove(struct platform_device *dev) -{ - power_supply_unregister(&bat_ps); - return 0; -} - -static struct platform_driver palmtx_bat_driver = { - .driver.name = "wm97xx-battery", - .driver.owner = THIS_MODULE, - .probe = palmtx_bat_probe, - .remove = __devexit_p(palmtx_bat_remove), - .suspend = palmtx_bat_suspend, - .resume = palmtx_bat_resume, -}; - -static int __init palmtx_bat_init(void) -{ - return platform_driver_register(&palmtx_bat_driver); -} - -static void __exit palmtx_bat_exit(void) -{ - platform_driver_unregister(&palmtx_bat_driver); -} - -module_init(palmtx_bat_init); -module_exit(palmtx_bat_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); -MODULE_DESCRIPTION("Palm T|X battery driver"); diff --git a/drivers/power/wm97xx_battery.c b/drivers/power/wm97xx_battery.c new file mode 100644 index 000000000000..8bde92126d34 --- /dev/null +++ b/drivers/power/wm97xx_battery.c @@ -0,0 +1,272 @@ +/* + * linux/drivers/power/wm97xx_battery.c + * + * Battery measurement code for WM97xx + * + * based on tosa_battery.c + * + * Copyright (C) 2008 Marek Vasut <marek.vasut@gmail.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. + * + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/wm97xx.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/wm97xx_batt.h> + +static DEFINE_MUTEX(bat_lock); +static struct work_struct bat_work; +struct mutex work_lock; +static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN; +static struct wm97xx_batt_info *pdata; +static enum power_supply_property *prop; + +static unsigned long wm97xx_read_bat(struct power_supply *bat_ps) +{ + return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + pdata->batt_aux) * pdata->batt_mult / + pdata->batt_div; +} + +static unsigned long wm97xx_read_temp(struct power_supply *bat_ps) +{ + return wm97xx_read_aux_adc(bat_ps->dev->parent->driver_data, + pdata->temp_aux) * pdata->temp_mult / + pdata->temp_div; +} + +static int wm97xx_bat_get_property(struct power_supply *bat_ps, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = bat_status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = pdata->batt_tech; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + if (pdata->batt_aux >= 0) + val->intval = wm97xx_read_bat(bat_ps); + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_TEMP: + if (pdata->temp_aux >= 0) + val->intval = wm97xx_read_temp(bat_ps); + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + if (pdata->max_voltage >= 0) + val->intval = pdata->max_voltage; + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + if (pdata->min_voltage >= 0) + val->intval = pdata->min_voltage; + else + return -EINVAL; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +static void wm97xx_bat_external_power_changed(struct power_supply *bat_ps) +{ + schedule_work(&bat_work); +} + +static void wm97xx_bat_update(struct power_supply *bat_ps) +{ + int old_status = bat_status; + + mutex_lock(&work_lock); + + bat_status = (pdata->charge_gpio >= 0) ? + (gpio_get_value(pdata->charge_gpio) ? + POWER_SUPPLY_STATUS_DISCHARGING : + POWER_SUPPLY_STATUS_CHARGING) : + POWER_SUPPLY_STATUS_UNKNOWN; + + if (old_status != bat_status) { + pr_debug("%s: %i -> %i\n", bat_ps->name, old_status, + bat_status); + power_supply_changed(bat_ps); + } + + mutex_unlock(&work_lock); +} + +static struct power_supply bat_ps = { + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = wm97xx_bat_get_property, + .external_power_changed = wm97xx_bat_external_power_changed, + .use_for_apm = 1, +}; + +static void wm97xx_bat_work(struct work_struct *work) +{ + wm97xx_bat_update(&bat_ps); +} + +#ifdef CONFIG_PM +static int wm97xx_bat_suspend(struct platform_device *dev, pm_message_t state) +{ + flush_scheduled_work(); + return 0; +} + +static int wm97xx_bat_resume(struct platform_device *dev) +{ + schedule_work(&bat_work); + return 0; +} +#else +#define wm97xx_bat_suspend NULL +#define wm97xx_bat_resume NULL +#endif + +static int __devinit wm97xx_bat_probe(struct platform_device *dev) +{ + int ret = 0; + int props = 1; /* POWER_SUPPLY_PROP_PRESENT */ + int i = 0; + + if (dev->id != -1) + return -EINVAL; + + mutex_init(&work_lock); + + if (!pdata) { + dev_err(&dev->dev, "Please use wm97xx_bat_set_pdata\n"); + return -EINVAL; + } + + if (pdata->charge_gpio >= 0 && gpio_is_valid(pdata->charge_gpio)) { + ret = gpio_request(pdata->charge_gpio, "BATT CHRG"); + if (ret) + goto err; + ret = gpio_direction_input(pdata->charge_gpio); + if (ret) + goto err2; + props++; /* POWER_SUPPLY_PROP_STATUS */ + } + + if (pdata->batt_tech >= 0) + props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */ + if (pdata->temp_aux >= 0) + props++; /* POWER_SUPPLY_PROP_TEMP */ + if (pdata->batt_aux >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */ + if (pdata->max_voltage >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */ + if (pdata->min_voltage >= 0) + props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */ + + prop = kzalloc(props * sizeof(*prop), GFP_KERNEL); + if (!prop) + goto err2; + + prop[i++] = POWER_SUPPLY_PROP_PRESENT; + if (pdata->charge_gpio >= 0) + prop[i++] = POWER_SUPPLY_PROP_STATUS; + if (pdata->batt_tech >= 0) + prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY; + if (pdata->temp_aux >= 0) + prop[i++] = POWER_SUPPLY_PROP_TEMP; + if (pdata->batt_aux >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW; + if (pdata->max_voltage >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX; + if (pdata->min_voltage >= 0) + prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN; + + INIT_WORK(&bat_work, wm97xx_bat_work); + + if (!pdata->batt_name) { + dev_info(&dev->dev, "Please consider setting proper battery " + "name in platform definition file, falling " + "back to name \"wm97xx-batt\"\n"); + bat_ps.name = "wm97xx-batt"; + } else + bat_ps.name = pdata->batt_name; + + bat_ps.properties = prop; + bat_ps.num_properties = props; + + ret = power_supply_register(&dev->dev, &bat_ps); + if (!ret) + schedule_work(&bat_work); + else + goto err3; + + return 0; +err3: + kfree(prop); +err2: + gpio_free(pdata->charge_gpio); +err: + return ret; +} + +static int __devexit wm97xx_bat_remove(struct platform_device *dev) +{ + if (pdata && pdata->charge_gpio && pdata->charge_gpio >= 0) + gpio_free(pdata->charge_gpio); + flush_scheduled_work(); + power_supply_unregister(&bat_ps); + kfree(prop); + return 0; +} + +static struct platform_driver wm97xx_bat_driver = { + .driver = { + .name = "wm97xx-battery", + .owner = THIS_MODULE, + }, + .probe = wm97xx_bat_probe, + .remove = __devexit_p(wm97xx_bat_remove), + .suspend = wm97xx_bat_suspend, + .resume = wm97xx_bat_resume, +}; + +static int __init wm97xx_bat_init(void) +{ + return platform_driver_register(&wm97xx_bat_driver); +} + +static void __exit wm97xx_bat_exit(void) +{ + platform_driver_unregister(&wm97xx_bat_driver); +} + +void __init wm97xx_bat_set_pdata(struct wm97xx_batt_info *data) +{ + pdata = data; +} +EXPORT_SYMBOL_GPL(wm97xx_bat_set_pdata); + +module_init(wm97xx_bat_init); +module_exit(wm97xx_bat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_DESCRIPTION("WM97xx battery driver"); diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index f7a0d37c4221..abc00be55433 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -534,6 +534,11 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, serial_out(up, UART_IER, up->ier); + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + else + up->mcr &= ~UART_MCR_AFE; + serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 7f0f35c78185..e294d430733b 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -23,17 +23,90 @@ #include <linux/signal.h> #include <linux/platform_device.h> #include <linux/clk.h> - -#include <mach/hardware.h> -#include <mach/pxa-regs.h> -#include <mach/pxa2xx-regs.h> /* FIXME: for PSSR */ #include <mach/ohci.h> +/* + * UHC: USB Host Controller (OHCI-like) register definitions + */ +#define UHCREV (0x0000) /* UHC HCI Spec Revision */ +#define UHCHCON (0x0004) /* UHC Host Control Register */ +#define UHCCOMS (0x0008) /* UHC Command Status Register */ +#define UHCINTS (0x000C) /* UHC Interrupt Status Register */ +#define UHCINTE (0x0010) /* UHC Interrupt Enable */ +#define UHCINTD (0x0014) /* UHC Interrupt Disable */ +#define UHCHCCA (0x0018) /* UHC Host Controller Comm. Area */ +#define UHCPCED (0x001C) /* UHC Period Current Endpt Descr */ +#define UHCCHED (0x0020) /* UHC Control Head Endpt Descr */ +#define UHCCCED (0x0024) /* UHC Control Current Endpt Descr */ +#define UHCBHED (0x0028) /* UHC Bulk Head Endpt Descr */ +#define UHCBCED (0x002C) /* UHC Bulk Current Endpt Descr */ +#define UHCDHEAD (0x0030) /* UHC Done Head */ +#define UHCFMI (0x0034) /* UHC Frame Interval */ +#define UHCFMR (0x0038) /* UHC Frame Remaining */ +#define UHCFMN (0x003C) /* UHC Frame Number */ +#define UHCPERS (0x0040) /* UHC Periodic Start */ +#define UHCLS (0x0044) /* UHC Low Speed Threshold */ + +#define UHCRHDA (0x0048) /* UHC Root Hub Descriptor A */ +#define UHCRHDA_NOCP (1 << 12) /* No over current protection */ +#define UHCRHDA_OCPM (1 << 11) /* Over Current Protection Mode */ +#define UHCRHDA_POTPGT(x) \ + (((x) & 0xff) << 24) /* Power On To Power Good Time */ + +#define UHCRHDB (0x004C) /* UHC Root Hub Descriptor B */ +#define UHCRHS (0x0050) /* UHC Root Hub Status */ +#define UHCRHPS1 (0x0054) /* UHC Root Hub Port 1 Status */ +#define UHCRHPS2 (0x0058) /* UHC Root Hub Port 2 Status */ +#define UHCRHPS3 (0x005C) /* UHC Root Hub Port 3 Status */ + +#define UHCSTAT (0x0060) /* UHC Status Register */ +#define UHCSTAT_UPS3 (1 << 16) /* USB Power Sense Port3 */ +#define UHCSTAT_SBMAI (1 << 15) /* System Bus Master Abort Interrupt*/ +#define UHCSTAT_SBTAI (1 << 14) /* System Bus Target Abort Interrupt*/ +#define UHCSTAT_UPRI (1 << 13) /* USB Port Resume Interrupt */ +#define UHCSTAT_UPS2 (1 << 12) /* USB Power Sense Port 2 */ +#define UHCSTAT_UPS1 (1 << 11) /* USB Power Sense Port 1 */ +#define UHCSTAT_HTA (1 << 10) /* HCI Target Abort */ +#define UHCSTAT_HBA (1 << 8) /* HCI Buffer Active */ +#define UHCSTAT_RWUE (1 << 7) /* HCI Remote Wake Up Event */ + +#define UHCHR (0x0064) /* UHC Reset Register */ +#define UHCHR_SSEP3 (1 << 11) /* Sleep Standby Enable for Port3 */ +#define UHCHR_SSEP2 (1 << 10) /* Sleep Standby Enable for Port2 */ +#define UHCHR_SSEP1 (1 << 9) /* Sleep Standby Enable for Port1 */ +#define UHCHR_PCPL (1 << 7) /* Power control polarity low */ +#define UHCHR_PSPL (1 << 6) /* Power sense polarity low */ +#define UHCHR_SSE (1 << 5) /* Sleep Standby Enable */ +#define UHCHR_UIT (1 << 4) /* USB Interrupt Test */ +#define UHCHR_SSDC (1 << 3) /* Simulation Scale Down Clock */ +#define UHCHR_CGR (1 << 2) /* Clock Generation Reset */ +#define UHCHR_FHR (1 << 1) /* Force Host Controller Reset */ +#define UHCHR_FSBIR (1 << 0) /* Force System Bus Iface Reset */ + +#define UHCHIE (0x0068) /* UHC Interrupt Enable Register*/ +#define UHCHIE_UPS3IE (1 << 14) /* Power Sense Port3 IntEn */ +#define UHCHIE_UPRIE (1 << 13) /* Port Resume IntEn */ +#define UHCHIE_UPS2IE (1 << 12) /* Power Sense Port2 IntEn */ +#define UHCHIE_UPS1IE (1 << 11) /* Power Sense Port1 IntEn */ +#define UHCHIE_TAIE (1 << 10) /* HCI Interface Transfer Abort + Interrupt Enable*/ +#define UHCHIE_HBAIE (1 << 8) /* HCI Buffer Active IntEn */ +#define UHCHIE_RWIE (1 << 7) /* Remote Wake-up IntEn */ + +#define UHCHIT (0x006C) /* UHC Interrupt Test register */ + #define PXA_UHC_MAX_PORTNUM 3 -#define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 ) +struct pxa27x_ohci { + /* must be 1st member here for hcd_to_ohci() to work */ + struct ohci_hcd ohci; + + struct device *dev; + struct clk *clk; + void __iomem *mmio_base; +}; -static struct clk *usb_clk; +#define to_pxa27x_ohci(hcd) (struct pxa27x_ohci *)hcd_to_ohci(hcd) /* PMM_NPS_MODE -- PMM Non-power switching mode @@ -45,30 +118,35 @@ static struct clk *usb_clk; PMM_PERPORT_MODE -- PMM per port switching mode Ports are powered individually. */ -static int pxa27x_ohci_select_pmm( int mode ) +static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode) { - switch ( mode ) { + uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA); + uint32_t uhcrhdb = __raw_readl(ohci->mmio_base + UHCRHDB); + + switch (mode) { case PMM_NPS_MODE: - UHCRHDA |= RH_A_NPS; + uhcrhda |= RH_A_NPS; break; case PMM_GLOBAL_MODE: - UHCRHDA &= ~(RH_A_NPS & RH_A_PSM); + uhcrhda &= ~(RH_A_NPS & RH_A_PSM); break; case PMM_PERPORT_MODE: - UHCRHDA &= ~(RH_A_NPS); - UHCRHDA |= RH_A_PSM; + uhcrhda &= ~(RH_A_NPS); + uhcrhda |= RH_A_PSM; /* Set port power control mask bits, only 3 ports. */ - UHCRHDB |= (0x7<<17); + uhcrhdb |= (0x7<<17); break; default: printk( KERN_ERR "Invalid mode %d, set to non-power switch mode.\n", mode ); - UHCRHDA |= RH_A_NPS; + uhcrhda |= RH_A_NPS; } + __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA); + __raw_writel(uhcrhdb, ohci->mmio_base + UHCRHDB); return 0; } @@ -76,57 +154,110 @@ extern int usb_disabled(void); /*-------------------------------------------------------------------------*/ -static int pxa27x_start_hc(struct device *dev) +static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci, + struct pxaohci_platform_data *inf) +{ + uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR); + uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA); + + if (inf->flags & ENABLE_PORT1) + uhchr &= ~UHCHR_SSEP1; + + if (inf->flags & ENABLE_PORT2) + uhchr &= ~UHCHR_SSEP2; + + if (inf->flags & ENABLE_PORT3) + uhchr &= ~UHCHR_SSEP3; + + if (inf->flags & POWER_CONTROL_LOW) + uhchr |= UHCHR_PCPL; + + if (inf->flags & POWER_SENSE_LOW) + uhchr |= UHCHR_PSPL; + + if (inf->flags & NO_OC_PROTECTION) + uhcrhda |= UHCRHDA_NOCP; + + if (inf->flags & OC_MODE_PERPORT) + uhcrhda |= UHCRHDA_OCPM; + + if (inf->power_on_delay) { + uhcrhda &= ~UHCRHDA_POTPGT(0xff); + uhcrhda |= UHCRHDA_POTPGT(inf->power_on_delay / 2); + } + + __raw_writel(uhchr, ohci->mmio_base + UHCHR); + __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA); +} + +static inline void pxa27x_reset_hc(struct pxa27x_ohci *ohci) +{ + uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR); + + __raw_writel(uhchr | UHCHR_FHR, ohci->mmio_base + UHCHR); + udelay(11); + __raw_writel(uhchr & ~UHCHR_FHR, ohci->mmio_base + UHCHR); +} + +#ifdef CONFIG_CPU_PXA27x +extern void pxa27x_clear_otgph(void); +#else +#define pxa27x_clear_otgph() do {} while (0) +#endif + +static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev) { int retval = 0; struct pxaohci_platform_data *inf; + uint32_t uhchr; inf = dev->platform_data; - clk_enable(usb_clk); + clk_enable(ohci->clk); - UHCHR |= UHCHR_FHR; - udelay(11); - UHCHR &= ~UHCHR_FHR; + pxa27x_reset_hc(ohci); + + uhchr = __raw_readl(ohci->mmio_base + UHCHR) | UHCHR_FSBIR; + __raw_writel(uhchr, ohci->mmio_base + UHCHR); - UHCHR |= UHCHR_FSBIR; - while (UHCHR & UHCHR_FSBIR) + while (__raw_readl(ohci->mmio_base + UHCHR) & UHCHR_FSBIR) cpu_relax(); + pxa27x_setup_hc(ohci, inf); + if (inf->init) retval = inf->init(dev); if (retval < 0) return retval; - UHCHR &= ~UHCHR_SSE; - - UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE); + uhchr = __raw_readl(ohci->mmio_base + UHCHR) & ~UHCHR_SSE; + __raw_writel(uhchr, ohci->mmio_base + UHCHR); + __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, ohci->mmio_base + UHCHIE); /* Clear any OTG Pin Hold */ - if (cpu_is_pxa27x() && (PSSR & PSSR_OTGPH)) - PSSR |= PSSR_OTGPH; - + pxa27x_clear_otgph(); return 0; } -static void pxa27x_stop_hc(struct device *dev) +static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev) { struct pxaohci_platform_data *inf; + uint32_t uhccoms; inf = dev->platform_data; if (inf->exit) inf->exit(dev); - UHCHR |= UHCHR_FHR; - udelay(11); - UHCHR &= ~UHCHR_FHR; + pxa27x_reset_hc(ohci); - UHCCOMS |= 1; + /* Host Controller Reset */ + uhccoms = __raw_readl(ohci->mmio_base + UHCCOMS) | 0x01; + __raw_writel(uhccoms, ohci->mmio_base + UHCCOMS); udelay(10); - clk_disable(usb_clk); + clk_disable(ohci->clk); } @@ -147,18 +278,22 @@ static void pxa27x_stop_hc(struct device *dev) */ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device *pdev) { - int retval; + int retval, irq; struct usb_hcd *hcd; struct pxaohci_platform_data *inf; + struct pxa27x_ohci *ohci; + struct resource *r; + struct clk *usb_clk; inf = pdev->dev.platform_data; if (!inf) return -ENODEV; - if (pdev->resource[1].flags != IORESOURCE_IRQ) { - pr_debug ("resource[1] is not IORESOURCE_IRQ"); - return -ENOMEM; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + pr_err("no resource of IORESOURCE_IRQ"); + return -ENXIO; } usb_clk = clk_get(&pdev->dev, "USBCLK"); @@ -168,8 +303,16 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device hcd = usb_create_hcd (driver, &pdev->dev, "pxa27x"); if (!hcd) return -ENOMEM; - hcd->rsrc_start = pdev->resource[0].start; - hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + pr_err("no resource of IORESOURCE_MEM"); + retval = -ENXIO; + goto err1; + } + + hcd->rsrc_start = r->start; + hcd->rsrc_len = resource_size(r); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { pr_debug("request_mem_region failed"); @@ -184,24 +327,30 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device goto err2; } - if ((retval = pxa27x_start_hc(&pdev->dev)) < 0) { + /* initialize "struct pxa27x_ohci" */ + ohci = (struct pxa27x_ohci *)hcd_to_ohci(hcd); + ohci->dev = &pdev->dev; + ohci->clk = usb_clk; + ohci->mmio_base = (void __iomem *)hcd->regs; + + if ((retval = pxa27x_start_hc(ohci, &pdev->dev)) < 0) { pr_debug("pxa27x_start_hc failed"); goto err3; } /* Select Power Management Mode */ - pxa27x_ohci_select_pmm(inf->port_mode); + pxa27x_ohci_select_pmm(ohci, inf->port_mode); if (inf->power_budget) hcd->power_budget = inf->power_budget; ohci_hcd_init(hcd_to_ohci(hcd)); - retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED); + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); if (retval == 0) return retval; - pxa27x_stop_hc(&pdev->dev); + pxa27x_stop_hc(ohci, &pdev->dev); err3: iounmap(hcd->regs); err2: @@ -228,12 +377,14 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device */ void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) { + struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); + usb_remove_hcd(hcd); - pxa27x_stop_hc(&pdev->dev); + pxa27x_stop_hc(ohci, &pdev->dev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); - clk_put(usb_clk); + clk_put(ohci->clk); } /*-------------------------------------------------------------------------*/ @@ -266,7 +417,7 @@ ohci_pxa27x_start (struct usb_hcd *hcd) static const struct hc_driver ohci_pxa27x_hc_driver = { .description = hcd_name, .product_desc = "PXA27x OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), + .hcd_priv_size = sizeof(struct pxa27x_ohci), /* * generic hardware linkage @@ -330,13 +481,13 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev) static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *pdev, pm_message_t state) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); - if (time_before(jiffies, ohci->next_statechange)) + if (time_before(jiffies, ohci->ohci.next_statechange)) msleep(5); - ohci->next_statechange = jiffies; + ohci->ohci.next_statechange = jiffies; - pxa27x_stop_hc(&pdev->dev); + pxa27x_stop_hc(ohci, &pdev->dev); hcd->state = HC_STATE_SUSPENDED; return 0; @@ -345,14 +496,14 @@ static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *pdev, pm_message_ static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd); int status; - if (time_before(jiffies, ohci->next_statechange)) + if (time_before(jiffies, ohci->ohci.next_statechange)) msleep(5); - ohci->next_statechange = jiffies; + ohci->ohci.next_statechange = jiffies; - if ((status = pxa27x_start_hc(&pdev->dev)) < 0) + if ((status = pxa27x_start_hc(ohci, &pdev->dev)) < 0) return status; ohci_finish_controller_resume(hcd); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 70d135e0cc47..d85a74c64b54 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -172,11 +172,6 @@ config FB_DEFERRED_IO bool depends on FB -config FB_METRONOME - tristate - depends on FB - depends on FB_DEFERRED_IO - config FB_HECUBA tristate depends on FB @@ -1974,19 +1969,6 @@ config FB_XILINX framebuffer. ML300 carries a 640*480 LCD display on the board, ML403 uses a standard DB15 VGA connector. -config FB_AM200EPD - tristate "AM-200 E-Ink EPD devkit support" - depends on FB && ARCH_PXA && MMU - select FB_SYS_FILLRECT - select FB_SYS_COPYAREA - select FB_SYS_IMAGEBLIT - select FB_SYS_FOPS - select FB_DEFERRED_IO - select FB_METRONOME - help - This enables support for the Metronome display controller used on - the E-Ink AM-200 EPD devkit. - config FB_COBALT tristate "Cobalt server LCD frame buffer support" depends on FB && MIPS_COBALT @@ -2041,6 +2023,19 @@ config XEN_FBDEV_FRONTEND frame buffer driver. It communicates with a back-end in another domain. +config FB_METRONOME + tristate "E-Ink Metronome/8track controller support" + depends on FB + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + select FB_SYS_FOPS + select FB_DEFERRED_IO + help + This driver implements support for the E-Ink Metronome + controller. The pre-release name for this device was 8track + and could also have been called by some vendors as PVI-nnnn. + source "drivers/video/omap/Kconfig" source "drivers/video/backlight/Kconfig" diff --git a/drivers/video/Makefile b/drivers/video/Makefile index a6b55297a7fb..ad0330bf9be3 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o # Hardware specific drivers go first obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o -obj-$(CONFIG_FB_AM200EPD) += am200epd.o obj-$(CONFIG_FB_ARC) += arcfb.o obj-$(CONFIG_FB_CLPS711X) += clps711xfb.o obj-$(CONFIG_FB_CYBER2000) += cyber2000fb.o diff --git a/drivers/video/am200epd.c b/drivers/video/am200epd.c deleted file mode 100644 index 0c35b8b0160e..000000000000 --- a/drivers/video/am200epd.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * linux/drivers/video/am200epd.c -- Platform device for AM200 EPD kit - * - * Copyright (C) 2008, Jaya Kumar - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - * - * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. - * - * This work was made possible by help and equipment support from E-Ink - * Corporation. http://support.eink.com/community - * - * This driver is written to be used with the Metronome display controller. - * on the AM200 EPD prototype kit/development kit with an E-Ink 800x600 - * Vizplex EPD on a Gumstix board using the Lyre interface board. - * - */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/string.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/fb.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/list.h> -#include <linux/uaccess.h> -#include <linux/irq.h> - -#include <video/metronomefb.h> - -#include <mach/pxa-regs.h> - -/* register offsets for gpio control */ -#define LED_GPIO_PIN 51 -#define STDBY_GPIO_PIN 48 -#define RST_GPIO_PIN 49 -#define RDY_GPIO_PIN 32 -#define ERR_GPIO_PIN 17 -#define PCBPWR_GPIO_PIN 16 - -#define AF_SEL_GPIO_N 0x3 -#define GAFR0_U_OFFSET(pin) ((pin - 16) * 2) -#define GAFR1_L_OFFSET(pin) ((pin - 32) * 2) -#define GAFR1_U_OFFSET(pin) ((pin - 48) * 2) -#define GPDR1_OFFSET(pin) (pin - 32) -#define GPCR1_OFFSET(pin) (pin - 32) -#define GPSR1_OFFSET(pin) (pin - 32) -#define GPCR0_OFFSET(pin) (pin) -#define GPSR0_OFFSET(pin) (pin) - -static void am200_set_gpio_output(int pin, int val) -{ - u8 index; - - index = pin >> 4; - - switch (index) { - case 1: - if (val) - GPSR0 |= (1 << GPSR0_OFFSET(pin)); - else - GPCR0 |= (1 << GPCR0_OFFSET(pin)); - break; - case 2: - break; - case 3: - if (val) - GPSR1 |= (1 << GPSR1_OFFSET(pin)); - else - GPCR1 |= (1 << GPCR1_OFFSET(pin)); - break; - default: - printk(KERN_ERR "unimplemented\n"); - } -} - -static void __devinit am200_init_gpio_pin(int pin, int dir) -{ - u8 index; - /* dir 0 is output, 1 is input - - do 2 things here: - - set gpio alternate function to standard gpio - - set gpio direction to input or output */ - - index = pin >> 4; - switch (index) { - case 1: - GAFR0_U &= ~(AF_SEL_GPIO_N << GAFR0_U_OFFSET(pin)); - - if (dir) - GPDR0 &= ~(1 << pin); - else - GPDR0 |= (1 << pin); - break; - case 2: - GAFR1_L &= ~(AF_SEL_GPIO_N << GAFR1_L_OFFSET(pin)); - - if (dir) - GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); - else - GPDR1 |= (1 << GPDR1_OFFSET(pin)); - break; - case 3: - GAFR1_U &= ~(AF_SEL_GPIO_N << GAFR1_U_OFFSET(pin)); - - if (dir) - GPDR1 &= ~(1 << GPDR1_OFFSET(pin)); - else - GPDR1 |= (1 << GPDR1_OFFSET(pin)); - break; - default: - printk(KERN_ERR "unimplemented\n"); - } -} - -static void am200_init_gpio_regs(struct metronomefb_par *par) -{ - am200_init_gpio_pin(LED_GPIO_PIN, 0); - am200_set_gpio_output(LED_GPIO_PIN, 0); - - am200_init_gpio_pin(STDBY_GPIO_PIN, 0); - am200_set_gpio_output(STDBY_GPIO_PIN, 0); - - am200_init_gpio_pin(RST_GPIO_PIN, 0); - am200_set_gpio_output(RST_GPIO_PIN, 0); - - am200_init_gpio_pin(RDY_GPIO_PIN, 1); - - am200_init_gpio_pin(ERR_GPIO_PIN, 1); - - am200_init_gpio_pin(PCBPWR_GPIO_PIN, 0); - am200_set_gpio_output(PCBPWR_GPIO_PIN, 0); -} - -static void am200_disable_lcd_controller(struct metronomefb_par *par) -{ - LCSR = 0xffffffff; /* Clear LCD Status Register */ - LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */ - - /* we reset and just wait for things to settle */ - msleep(200); -} - -static void am200_enable_lcd_controller(struct metronomefb_par *par) -{ - LCSR = 0xffffffff; - FDADR0 = par->metromem_desc_dma; - LCCR0 |= LCCR0_ENB; -} - -static void am200_init_lcdc_regs(struct metronomefb_par *par) -{ - /* here we do: - - disable the lcd controller - - setup lcd control registers - - setup dma descriptor - - reenable lcd controller - */ - - /* disable the lcd controller */ - am200_disable_lcd_controller(par); - - /* setup lcd control registers */ - LCCR0 = LCCR0_LDM | LCCR0_SFM | LCCR0_IUM | LCCR0_EFM | LCCR0_PAS - | LCCR0_QDM | LCCR0_BM | LCCR0_OUM; - - LCCR1 = (par->info->var.xres/2 - 1) /* pixels per line */ - | (27 << 10) /* hsync pulse width - 1 */ - | (33 << 16) /* eol pixel count */ - | (33 << 24); /* bol pixel count */ - - LCCR2 = (par->info->var.yres - 1) /* lines per panel */ - | (24 << 10) /* vsync pulse width - 1 */ - | (2 << 16) /* eof pixel count */ - | (0 << 24); /* bof pixel count */ - - LCCR3 = 2 /* pixel clock divisor */ - | (24 << 8) /* AC Bias pin freq */ - | LCCR3_16BPP /* BPP */ - | LCCR3_PCP; /* PCP falling edge */ - -} - -static void am200_post_dma_setup(struct metronomefb_par *par) -{ - par->metromem_desc->mFDADR0 = par->metromem_desc_dma; - par->metromem_desc->mFSADR0 = par->metromem_dma; - par->metromem_desc->mFIDR0 = 0; - par->metromem_desc->mLDCMD0 = par->info->var.xres - * par->info->var.yres; - am200_enable_lcd_controller(par); -} - -static void am200_free_irq(struct fb_info *info) -{ - free_irq(IRQ_GPIO(RDY_GPIO_PIN), info); -} - -static irqreturn_t am200_handle_irq(int irq, void *dev_id) -{ - struct fb_info *info = dev_id; - struct metronomefb_par *par = info->par; - - wake_up_interruptible(&par->waitq); - return IRQ_HANDLED; -} - -static int am200_setup_irq(struct fb_info *info) -{ - int retval; - - retval = request_irq(IRQ_GPIO(RDY_GPIO_PIN), am200_handle_irq, - IRQF_DISABLED, "AM200", info); - if (retval) { - printk(KERN_ERR "am200epd: request_irq failed: %d\n", retval); - return retval; - } - - return set_irq_type(IRQ_GPIO(RDY_GPIO_PIN), IRQ_TYPE_EDGE_FALLING); -} - -static void am200_set_rst(struct metronomefb_par *par, int state) -{ - am200_set_gpio_output(RST_GPIO_PIN, state); -} - -static void am200_set_stdby(struct metronomefb_par *par, int state) -{ - am200_set_gpio_output(STDBY_GPIO_PIN, state); -} - -static int am200_wait_event(struct metronomefb_par *par) -{ - return wait_event_timeout(par->waitq, (GPLR1 & 0x01), HZ); -} - -static int am200_wait_event_intr(struct metronomefb_par *par) -{ - return wait_event_interruptible_timeout(par->waitq, (GPLR1 & 0x01), HZ); -} - -static struct metronome_board am200_board = { - .owner = THIS_MODULE, - .free_irq = am200_free_irq, - .setup_irq = am200_setup_irq, - .init_gpio_regs = am200_init_gpio_regs, - .init_lcdc_regs = am200_init_lcdc_regs, - .post_dma_setup = am200_post_dma_setup, - .set_rst = am200_set_rst, - .set_stdby = am200_set_stdby, - .met_wait_event = am200_wait_event, - .met_wait_event_intr = am200_wait_event_intr, -}; - -static struct platform_device *am200_device; - -static int __init am200_init(void) -{ - int ret; - - /* request our platform independent driver */ - request_module("metronomefb"); - - am200_device = platform_device_alloc("metronomefb", -1); - if (!am200_device) - return -ENOMEM; - - platform_device_add_data(am200_device, &am200_board, - sizeof(am200_board)); - - /* this _add binds metronomefb to am200. metronomefb refcounts am200 */ - ret = platform_device_add(am200_device); - - if (ret) - platform_device_put(am200_device); - - return ret; -} - -static void __exit am200_exit(void) -{ - platform_device_unregister(am200_device); -} - -module_init(am200_init); -module_exit(am200_exit); - -MODULE_DESCRIPTION("board driver for am200 metronome epd kit"); -MODULE_AUTHOR("Jaya Kumar"); -MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 452b770d8cc9..c72a13562954 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -24,6 +24,13 @@ config LCD_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. +config LCD_CORGI + tristate "LCD Panel support for SHARP corgi/spitz model" + depends on LCD_CLASS_DEVICE && SPI_MASTER && PXA_SHARPSL + help + Say y here to support the LCD panels usually found on SHARP + corgi (C7x0) and spitz (Cxx00) models. + config LCD_LTV350QV tristate "Samsung LTV350QV LCD Panel" depends on LCD_CLASS_DEVICE && SPI_MASTER @@ -44,6 +51,14 @@ config LCD_ILI9320 If you have a panel based on the ILI9320 controller chip then say y to include a power driver for it. +config LCD_TDO24M + tristate "Toppoly TDO24M LCD Panels support" + depends on LCD_CLASS_DEVICE && SPI_MASTER + default n + help + If you have a Toppoly TDO24M series LCD panel, say y here to + include the support for it. + config LCD_VGG2432A4 tristate "VGG2432A4 LCM device support" depends on BACKLIGHT_LCD_SUPPORT && LCD_CLASS_DEVICE && SPI_MASTER diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index b405aace803f..3ec551eb472c 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -1,10 +1,12 @@ # Backlight & LCD drivers obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o +obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o obj-$(CONFIG_LCD_ILI9320) += ili9320.o obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o +obj-$(CONFIG_LCD_TDO24M) += tdo24m.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o diff --git a/drivers/video/backlight/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c new file mode 100644 index 000000000000..2afd47eefe74 --- /dev/null +++ b/drivers/video/backlight/corgi_lcd.c @@ -0,0 +1,641 @@ +/* + * LCD/Backlight Driver for Sharp Zaurus Handhelds (various models) + * + * Copyright (c) 2004-2006 Richard Purdie + * + * Based on Sharp's 2.4 Backlight Driver + * + * Copyright (c) 2008 Marvell International Ltd. + * Converted to SPI device based LCD/Backlight device driver + * by Eric Miao <eric.miao@marvell.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. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/fb.h> +#include <linux/lcd.h> +#include <linux/spi/spi.h> +#include <linux/spi/corgi_lcd.h> +#include <asm/mach/sharpsl_param.h> + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +/* Register Addresses */ +#define RESCTL_ADRS 0x00 +#define PHACTRL_ADRS 0x01 +#define DUTYCTRL_ADRS 0x02 +#define POWERREG0_ADRS 0x03 +#define POWERREG1_ADRS 0x04 +#define GPOR3_ADRS 0x05 +#define PICTRL_ADRS 0x06 +#define POLCTRL_ADRS 0x07 + +/* Register Bit Definitions */ +#define RESCTL_QVGA 0x01 +#define RESCTL_VGA 0x00 + +#define POWER1_VW_ON 0x01 /* VW Supply FET ON */ +#define POWER1_GVSS_ON 0x02 /* GVSS(-8V) Power Supply ON */ +#define POWER1_VDD_ON 0x04 /* VDD(8V),SVSS(-4V) Power Supply ON */ + +#define POWER1_VW_OFF 0x00 /* VW Supply FET OFF */ +#define POWER1_GVSS_OFF 0x00 /* GVSS(-8V) Power Supply OFF */ +#define POWER1_VDD_OFF 0x00 /* VDD(8V),SVSS(-4V) Power Supply OFF */ + +#define POWER0_COM_DCLK 0x01 /* COM Voltage DC Bias DAC Serial Data Clock */ +#define POWER0_COM_DOUT 0x02 /* COM Voltage DC Bias DAC Serial Data Out */ +#define POWER0_DAC_ON 0x04 /* DAC Power Supply ON */ +#define POWER0_COM_ON 0x08 /* COM Power Supply ON */ +#define POWER0_VCC5_ON 0x10 /* VCC5 Power Supply ON */ + +#define POWER0_DAC_OFF 0x00 /* DAC Power Supply OFF */ +#define POWER0_COM_OFF 0x00 /* COM Power Supply OFF */ +#define POWER0_VCC5_OFF 0x00 /* VCC5 Power Supply OFF */ + +#define PICTRL_INIT_STATE 0x01 +#define PICTRL_INIOFF 0x02 +#define PICTRL_POWER_DOWN 0x04 +#define PICTRL_COM_SIGNAL_OFF 0x08 +#define PICTRL_DAC_SIGNAL_OFF 0x10 + +#define POLCTRL_SYNC_POL_FALL 0x01 +#define POLCTRL_EN_POL_FALL 0x02 +#define POLCTRL_DATA_POL_FALL 0x04 +#define POLCTRL_SYNC_ACT_H 0x08 +#define POLCTRL_EN_ACT_L 0x10 + +#define POLCTRL_SYNC_POL_RISE 0x00 +#define POLCTRL_EN_POL_RISE 0x00 +#define POLCTRL_DATA_POL_RISE 0x00 +#define POLCTRL_SYNC_ACT_L 0x00 +#define POLCTRL_EN_ACT_H 0x00 + +#define PHACTRL_PHASE_MANUAL 0x01 +#define DEFAULT_PHAD_QVGA (9) +#define DEFAULT_COMADJ (125) + +struct corgi_lcd { + struct spi_device *spi_dev; + struct lcd_device *lcd_dev; + struct backlight_device *bl_dev; + + int limit_mask; + int intensity; + int power; + int mode; + char buf[2]; + + int gpio_backlight_on; + int gpio_backlight_cont; + int gpio_backlight_cont_inverted; + + void (*kick_battery)(void); +}; + +static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int reg, uint8_t val); + +static struct corgi_lcd *the_corgi_lcd; +static unsigned long corgibl_flags; +#define CORGIBL_SUSPENDED 0x01 +#define CORGIBL_BATTLOW 0x02 + +/* + * This is only a psuedo I2C interface. We can't use the standard kernel + * routines as the interface is write only. We just assume the data is acked... + */ +static void lcdtg_ssp_i2c_send(struct corgi_lcd *lcd, uint8_t data) +{ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, data); + udelay(10); +} + +static void lcdtg_i2c_send_bit(struct corgi_lcd *lcd, uint8_t data) +{ + lcdtg_ssp_i2c_send(lcd, data); + lcdtg_ssp_i2c_send(lcd, data | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, data); +} + +static void lcdtg_i2c_send_start(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, base); +} + +static void lcdtg_i2c_send_stop(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_ssp_i2c_send(lcd, base); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK); + lcdtg_ssp_i2c_send(lcd, base | POWER0_COM_DCLK | POWER0_COM_DOUT); +} + +static void lcdtg_i2c_send_byte(struct corgi_lcd *lcd, + uint8_t base, uint8_t data) +{ + int i; + for (i = 0; i < 8; i++) { + if (data & 0x80) + lcdtg_i2c_send_bit(lcd, base | POWER0_COM_DOUT); + else + lcdtg_i2c_send_bit(lcd, base); + data <<= 1; + } +} + +static void lcdtg_i2c_wait_ack(struct corgi_lcd *lcd, uint8_t base) +{ + lcdtg_i2c_send_bit(lcd, base); +} + +static void lcdtg_set_common_voltage(struct corgi_lcd *lcd, + uint8_t base_data, uint8_t data) +{ + /* Set Common Voltage to M62332FP via I2C */ + lcdtg_i2c_send_start(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, 0x9c); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, 0x00); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_byte(lcd, base_data, data); + lcdtg_i2c_wait_ack(lcd, base_data); + lcdtg_i2c_send_stop(lcd, base_data); +} + +static int corgi_ssp_lcdtg_send(struct corgi_lcd *lcd, int adrs, uint8_t data) +{ + struct spi_message msg; + struct spi_transfer xfer = { + .len = 1, + .cs_change = 1, + .tx_buf = lcd->buf, + }; + + lcd->buf[0] = ((adrs & 0x07) << 5) | (data & 0x1f); + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(lcd->spi_dev, &msg); +} + +/* Set Phase Adjust */ +static void lcdtg_set_phadadj(struct corgi_lcd *lcd, int mode) +{ + int adj; + + switch(mode) { + case CORGI_LCD_MODE_VGA: + /* Setting for VGA */ + adj = sharpsl_param.phadadj; + adj = (adj < 0) ? PHACTRL_PHASE_MANUAL : + PHACTRL_PHASE_MANUAL | ((adj & 0xf) << 1); + break; + case CORGI_LCD_MODE_QVGA: + default: + /* Setting for QVGA */ + adj = (DEFAULT_PHAD_QVGA << 1) | PHACTRL_PHASE_MANUAL; + break; + } + + corgi_ssp_lcdtg_send(lcd, PHACTRL_ADRS, adj); +} + +static void corgi_lcd_power_on(struct corgi_lcd *lcd) +{ + int comadj; + + /* Initialize Internal Logic & Port */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_POWER_DOWN | PICTRL_INIOFF | + PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF | + PICTRL_DAC_SIGNAL_OFF); + + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_OFF | + POWER0_COM_OFF | POWER0_VCC5_OFF); + + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); + + /* VDD(+8V), SVSS(-4V) ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); + mdelay(3); + + /* DAC ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* INIB = H, INI = L */ + /* PICTL[0] = H , PICTL[1] = PICTL[2] = PICTL[4] = L */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_INIT_STATE | PICTRL_COM_SIGNAL_OFF); + + /* Set Common Voltage */ + comadj = sharpsl_param.comadj; + if (comadj < 0) + comadj = DEFAULT_COMADJ; + + lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF | + POWER0_VCC5_OFF, comadj); + + /* VCC5 ON, DAC ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_OFF | POWER0_VCC5_ON); + + /* GVSS(-8V) ON, VDD ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); + mdelay(2); + + /* COM SIGNAL ON (PICTL[3] = L) */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_INIT_STATE); + + /* COM ON, DAC ON, VCC5_ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_COM_DCLK | POWER0_COM_DOUT | POWER0_DAC_ON | + POWER0_COM_ON | POWER0_VCC5_ON); + + /* VW ON, GVSS ON, VDD ON */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_ON | POWER1_GVSS_ON | POWER1_VDD_ON); + + /* Signals output enable */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, 0); + + /* Set Phase Adjust */ + lcdtg_set_phadadj(lcd, lcd->mode); + + /* Initialize for Input Signals from ATI */ + corgi_ssp_lcdtg_send(lcd, POLCTRL_ADRS, + POLCTRL_SYNC_POL_RISE | POLCTRL_EN_POL_RISE | + POLCTRL_DATA_POL_RISE | POLCTRL_SYNC_ACT_L | + POLCTRL_EN_ACT_H); + udelay(1000); + + switch (lcd->mode) { + case CORGI_LCD_MODE_VGA: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA); + break; + case CORGI_LCD_MODE_QVGA: + default: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA); + break; + } +} + +static void corgi_lcd_power_off(struct corgi_lcd *lcd) +{ + /* 60Hz x 2 frame = 16.7msec x 2 = 33.4 msec */ + msleep(34); + + /* (1)VW OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_ON | POWER1_VDD_ON); + + /* (2)COM OFF */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, PICTRL_COM_SIGNAL_OFF); + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_ON); + + /* (3)Set Common Voltage Bias 0V */ + lcdtg_set_common_voltage(lcd, POWER0_DAC_ON | POWER0_COM_OFF | + POWER0_VCC5_ON, 0); + + /* (4)GVSS OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_ON); + + /* (5)VCC5 OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_ON | POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* (6)Set PDWN, INIOFF, DACOFF */ + corgi_ssp_lcdtg_send(lcd, PICTRL_ADRS, + PICTRL_INIOFF | PICTRL_DAC_SIGNAL_OFF | + PICTRL_POWER_DOWN | PICTRL_COM_SIGNAL_OFF); + + /* (7)DAC OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG0_ADRS, + POWER0_DAC_OFF | POWER0_COM_OFF | POWER0_VCC5_OFF); + + /* (8)VDD OFF */ + corgi_ssp_lcdtg_send(lcd, POWERREG1_ADRS, + POWER1_VW_OFF | POWER1_GVSS_OFF | POWER1_VDD_OFF); +} + +static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + int mode = CORGI_LCD_MODE_QVGA; + + if (m->xres == 640 || m->xres == 480) + mode = CORGI_LCD_MODE_VGA; + + if (lcd->mode == mode) + return 0; + + lcdtg_set_phadadj(lcd, mode); + + switch (mode) { + case CORGI_LCD_MODE_VGA: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_VGA); + break; + case CORGI_LCD_MODE_QVGA: + default: + corgi_ssp_lcdtg_send(lcd, RESCTL_ADRS, RESCTL_QVGA); + break; + } + + lcd->mode = mode; + return 0; +} + +static int corgi_lcd_set_power(struct lcd_device *ld, int power) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + corgi_lcd_power_on(lcd); + + if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + corgi_lcd_power_off(lcd); + + lcd->power = power; + return 0; +} + +static int corgi_lcd_get_power(struct lcd_device *ld) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); + + return lcd->power; +} + +static struct lcd_ops corgi_lcd_ops = { + .get_power = corgi_lcd_get_power, + .set_power = corgi_lcd_set_power, + .set_mode = corgi_lcd_set_mode, +}; + +static int corgi_bl_get_intensity(struct backlight_device *bd) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + + return lcd->intensity; +} + +static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity) +{ + int cont; + + if (intensity > 0x10) + intensity += 0x10; + + corgi_ssp_lcdtg_send(lcd, DUTYCTRL_ADRS, intensity); + + /* Bit 5 via GPIO_BACKLIGHT_CONT */ + cont = !!(intensity & 0x20) ^ lcd->gpio_backlight_cont_inverted; + + if (gpio_is_valid(lcd->gpio_backlight_cont)) + gpio_set_value(lcd->gpio_backlight_cont, cont); + + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_set_value(lcd->gpio_backlight_on, intensity); + + if (lcd->kick_battery) + lcd->kick_battery(); + + lcd->intensity = intensity; + return 0; +} + +static int corgi_bl_update_status(struct backlight_device *bd) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); + int intensity = bd->props.brightness; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + if (corgibl_flags & CORGIBL_SUSPENDED) + intensity = 0; + if (corgibl_flags & CORGIBL_BATTLOW) + intensity &= lcd->limit_mask; + + return corgi_bl_set_intensity(lcd, intensity); +} + +void corgibl_limit_intensity(int limit) +{ + if (limit) + corgibl_flags |= CORGIBL_BATTLOW; + else + corgibl_flags &= ~CORGIBL_BATTLOW; + + backlight_update_status(the_corgi_lcd->bl_dev); +} +EXPORT_SYMBOL(corgibl_limit_intensity); + +static struct backlight_ops corgi_bl_ops = { + .get_brightness = corgi_bl_get_intensity, + .update_status = corgi_bl_update_status, +}; + +#ifdef CONFIG_PM +static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + corgibl_flags |= CORGIBL_SUSPENDED; + corgi_bl_set_intensity(lcd, 0); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); + return 0; +} + +static int corgi_lcd_resume(struct spi_device *spi) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + corgibl_flags &= ~CORGIBL_SUSPENDED; + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); + backlight_update_status(lcd->bl_dev); + return 0; +} +#else +#define corgi_lcd_suspend NULL +#define corgi_lcd_resume NULL +#endif + +static int setup_gpio_backlight(struct corgi_lcd *lcd, + struct corgi_lcd_platform_data *pdata) +{ + struct spi_device *spi = lcd->spi_dev; + int err; + + lcd->gpio_backlight_on = -1; + lcd->gpio_backlight_cont = -1; + + if (gpio_is_valid(pdata->gpio_backlight_on)) { + err = gpio_request(pdata->gpio_backlight_on, "BL_ON"); + if (err) { + dev_err(&spi->dev, "failed to request GPIO%d for " + "backlight_on\n", pdata->gpio_backlight_on); + return err; + } + + lcd->gpio_backlight_on = pdata->gpio_backlight_on; + gpio_direction_output(lcd->gpio_backlight_on, 0); + } + + if (gpio_is_valid(pdata->gpio_backlight_cont)) { + err = gpio_request(pdata->gpio_backlight_cont, "BL_CONT"); + if (err) { + dev_err(&spi->dev, "failed to request GPIO%d for " + "backlight_cont\n", pdata->gpio_backlight_cont); + goto err_free_backlight_on; + } + + lcd->gpio_backlight_cont = pdata->gpio_backlight_cont; + + /* spitz and akita use both GPIOs for backlight, and + * have inverted polarity of GPIO_BACKLIGHT_CONT + */ + if (gpio_is_valid(lcd->gpio_backlight_on)) { + lcd->gpio_backlight_cont_inverted = 1; + gpio_direction_output(lcd->gpio_backlight_cont, 1); + } else { + lcd->gpio_backlight_cont_inverted = 0; + gpio_direction_output(lcd->gpio_backlight_cont, 0); + } + } + return 0; + +err_free_backlight_on: + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_free(lcd->gpio_backlight_on); + return err; +} + +static int __devinit corgi_lcd_probe(struct spi_device *spi) +{ + struct corgi_lcd_platform_data *pdata = spi->dev.platform_data; + struct corgi_lcd *lcd; + int ret = 0; + + if (pdata == NULL) { + dev_err(&spi->dev, "platform data not available\n"); + return -EINVAL; + } + + lcd = kzalloc(sizeof(struct corgi_lcd), GFP_KERNEL); + if (!lcd) { + dev_err(&spi->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + lcd->spi_dev = spi; + + lcd->lcd_dev = lcd_device_register("corgi_lcd", &spi->dev, + lcd, &corgi_lcd_ops); + if (IS_ERR(lcd->lcd_dev)) { + ret = PTR_ERR(lcd->lcd_dev); + goto err_free_lcd; + } + lcd->power = FB_BLANK_POWERDOWN; + lcd->mode = (pdata) ? pdata->init_mode : CORGI_LCD_MODE_VGA; + + lcd->bl_dev = backlight_device_register("corgi_bl", &spi->dev, + lcd, &corgi_bl_ops); + if (IS_ERR(lcd->bl_dev)) { + ret = PTR_ERR(lcd->bl_dev); + goto err_unregister_lcd; + } + lcd->bl_dev->props.max_brightness = pdata->max_intensity; + lcd->bl_dev->props.brightness = pdata->default_intensity; + lcd->bl_dev->props.power = FB_BLANK_UNBLANK; + + ret = setup_gpio_backlight(lcd, pdata); + if (ret) + goto err_unregister_bl; + + lcd->kick_battery = pdata->kick_battery; + + dev_set_drvdata(&spi->dev, lcd); + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); + backlight_update_status(lcd->bl_dev); + + lcd->limit_mask = pdata->limit_mask; + the_corgi_lcd = lcd; + return 0; + +err_unregister_bl: + backlight_device_unregister(lcd->bl_dev); +err_unregister_lcd: + lcd_device_unregister(lcd->lcd_dev); +err_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit corgi_lcd_remove(struct spi_device *spi) +{ + struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); + + lcd->bl_dev->props.power = FB_BLANK_UNBLANK; + lcd->bl_dev->props.brightness = 0; + backlight_update_status(lcd->bl_dev); + backlight_device_unregister(lcd->bl_dev); + + if (gpio_is_valid(lcd->gpio_backlight_on)) + gpio_free(lcd->gpio_backlight_on); + + if (gpio_is_valid(lcd->gpio_backlight_cont)) + gpio_free(lcd->gpio_backlight_cont); + + corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->lcd_dev); + kfree(lcd); + + return 0; +} + +static struct spi_driver corgi_lcd_driver = { + .driver = { + .name = "corgi-lcd", + .owner = THIS_MODULE, + }, + .probe = corgi_lcd_probe, + .remove = __devexit_p(corgi_lcd_remove), + .suspend = corgi_lcd_suspend, + .resume = corgi_lcd_resume, +}; + +static int __init corgi_lcd_init(void) +{ + return spi_register_driver(&corgi_lcd_driver); +} +module_init(corgi_lcd_init); + +static void __exit corgi_lcd_exit(void) +{ + spi_unregister_driver(&corgi_lcd_driver); +} +module_exit(corgi_lcd_exit); + +MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00"); +MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index b15b2b84a6f7..8e1731d3b228 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -27,14 +27,26 @@ static int fb_notifier_callback(struct notifier_block *self, struct fb_event *evdata = data; /* If we aren't interested in this event, skip it immediately ... */ - if (event != FB_EVENT_BLANK) + switch (event) { + case FB_EVENT_BLANK: + case FB_EVENT_MODE_CHANGE: + case FB_EVENT_MODE_CHANGE_ALL: + break; + default: return 0; + } ld = container_of(self, struct lcd_device, fb_notif); + if (!ld->ops) + return 0; + mutex_lock(&ld->ops_lock); - if (ld->ops) - if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) + if (!ld->ops->check_fb || ld->ops->check_fb(ld, evdata->info)) { + if (event == FB_EVENT_BLANK) ld->ops->set_power(ld, *(int *)evdata->data); + else + ld->ops->set_mode(ld, evdata->data); + } mutex_unlock(&ld->ops_lock); return 0; } diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c new file mode 100644 index 000000000000..8427669162ea --- /dev/null +++ b/drivers/video/backlight/tdo24m.c @@ -0,0 +1,396 @@ +/* + * tdo24m - SPI-based drivers for Toppoly TDO24M series LCD panels + * + * Copyright (C) 2008 Marvell International Ltd. + * Eric Miao <eric.miao@marvell.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 + * publishhed by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/fb.h> +#include <linux/lcd.h> + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +#define TDO24M_SPI_BUFF_SIZE (4) +#define MODE_QVGA 0 +#define MODE_VGA 1 + +struct tdo24m { + struct spi_device *spi_dev; + struct lcd_device *lcd_dev; + + struct spi_message msg; + struct spi_transfer xfer; + uint8_t *buf; + + int power; + int mode; +}; + +/* use bit 30, 31 as the indicator of command parameter number */ +#define CMD0(x) ((0 << 30) | (x)) +#define CMD1(x, x1) ((1 << 30) | ((x) << 9) | 0x100 | (x1)) +#define CMD2(x, x1, x2) ((2 << 30) | ((x) << 18) | 0x20000 |\ + ((x1) << 9) | 0x100 | (x2)) +#define CMD_NULL (-1) + +static uint32_t lcd_panel_reset[] = { + CMD0(0x1), /* reset */ + CMD0(0x0), /* nop */ + CMD0(0x0), /* nop */ + CMD0(0x0), /* nop */ + CMD_NULL, +}; + +static uint32_t lcd_panel_on[] = { + CMD0(0x29), /* Display ON */ + CMD2(0xB8, 0xFF, 0xF9), /* Output Control */ + CMD0(0x11), /* Sleep out */ + CMD1(0xB0, 0x16), /* Wake */ + CMD_NULL, +}; + +static uint32_t lcd_panel_off[] = { + CMD0(0x28), /* Display OFF */ + CMD2(0xB8, 0x80, 0x02), /* Output Control */ + CMD0(0x10), /* Sleep in */ + CMD1(0xB0, 0x00), /* Deep stand by in */ + CMD_NULL, +}; + +static uint32_t lcd_vga_pass_through[] = { + CMD1(0xB0, 0x16), + CMD1(0xBC, 0x80), + CMD1(0xE1, 0x00), + CMD1(0x36, 0x50), + CMD1(0x3B, 0x00), + CMD_NULL, +}; + +static uint32_t lcd_qvga_pass_through[] = { + CMD1(0xB0, 0x16), + CMD1(0xBC, 0x81), + CMD1(0xE1, 0x00), + CMD1(0x36, 0x50), + CMD1(0x3B, 0x22), + CMD_NULL, +}; + +static uint32_t lcd_vga_transfer[] = { + CMD1(0xcf, 0x02), /* Blanking period control (1) */ + CMD2(0xd0, 0x08, 0x04), /* Blanking period control (2) */ + CMD1(0xd1, 0x01), /* CKV timing control on/off */ + CMD2(0xd2, 0x14, 0x00), /* CKV 1,2 timing control */ + CMD2(0xd3, 0x1a, 0x0f), /* OEV timing control */ + CMD2(0xd4, 0x1f, 0xaf), /* ASW timing control (1) */ + CMD1(0xd5, 0x14), /* ASW timing control (2) */ + CMD0(0x21), /* Invert for normally black display */ + CMD0(0x29), /* Display on */ + CMD_NULL, +}; + +static uint32_t lcd_qvga_transfer[] = { + CMD1(0xd6, 0x02), /* Blanking period control (1) */ + CMD2(0xd7, 0x08, 0x04), /* Blanking period control (2) */ + CMD1(0xd8, 0x01), /* CKV timing control on/off */ + CMD2(0xd9, 0x00, 0x08), /* CKV 1,2 timing control */ + CMD2(0xde, 0x05, 0x0a), /* OEV timing control */ + CMD2(0xdf, 0x0a, 0x19), /* ASW timing control (1) */ + CMD1(0xe0, 0x0a), /* ASW timing control (2) */ + CMD0(0x21), /* Invert for normally black display */ + CMD0(0x29), /* Display on */ + CMD_NULL, +}; + +static uint32_t lcd_panel_config[] = { + CMD2(0xb8, 0xff, 0xf9), /* Output control */ + CMD0(0x11), /* sleep out */ + CMD1(0xba, 0x01), /* Display mode (1) */ + CMD1(0xbb, 0x00), /* Display mode (2) */ + CMD1(0x3a, 0x60), /* Display mode 18-bit RGB */ + CMD1(0xbf, 0x10), /* Drive system change control */ + CMD1(0xb1, 0x56), /* Booster operation setup */ + CMD1(0xb2, 0x33), /* Booster mode setup */ + CMD1(0xb3, 0x11), /* Booster frequency setup */ + CMD1(0xb4, 0x02), /* Op amp/system clock */ + CMD1(0xb5, 0x35), /* VCS voltage */ + CMD1(0xb6, 0x40), /* VCOM voltage */ + CMD1(0xb7, 0x03), /* External display signal */ + CMD1(0xbd, 0x00), /* ASW slew rate */ + CMD1(0xbe, 0x00), /* Dummy data for QuadData operation */ + CMD1(0xc0, 0x11), /* Sleep out FR count (A) */ + CMD1(0xc1, 0x11), /* Sleep out FR count (B) */ + CMD1(0xc2, 0x11), /* Sleep out FR count (C) */ + CMD2(0xc3, 0x20, 0x40), /* Sleep out FR count (D) */ + CMD2(0xc4, 0x60, 0xc0), /* Sleep out FR count (E) */ + CMD2(0xc5, 0x10, 0x20), /* Sleep out FR count (F) */ + CMD1(0xc6, 0xc0), /* Sleep out FR count (G) */ + CMD2(0xc7, 0x33, 0x43), /* Gamma 1 fine tuning (1) */ + CMD1(0xc8, 0x44), /* Gamma 1 fine tuning (2) */ + CMD1(0xc9, 0x33), /* Gamma 1 inclination adjustment */ + CMD1(0xca, 0x00), /* Gamma 1 blue offset adjustment */ + CMD2(0xec, 0x01, 0xf0), /* Horizontal clock cycles */ + CMD_NULL, +}; + +static int tdo24m_writes(struct tdo24m *lcd, uint32_t *array) +{ + struct spi_transfer *x = &lcd->xfer; + uint32_t data, *p = array; + int nparams, err = 0; + + for (; *p != CMD_NULL; p++) { + + nparams = (*p >> 30) & 0x3; + + data = *p << (7 - nparams); + switch (nparams) { + case 0: + lcd->buf[0] = (data >> 8) & 0xff; + lcd->buf[1] = data & 0xff; + break; + case 1: + lcd->buf[0] = (data >> 16) & 0xff; + lcd->buf[1] = (data >> 8) & 0xff; + lcd->buf[2] = data & 0xff; + break; + case 2: + lcd->buf[0] = (data >> 24) & 0xff; + lcd->buf[1] = (data >> 16) & 0xff; + lcd->buf[2] = (data >> 8) & 0xff; + lcd->buf[3] = data & 0xff; + break; + default: + continue; + } + x->len = nparams + 2; + err = spi_sync(lcd->spi_dev, &lcd->msg); + if (err) + break; + } + + return err; +} + +static int tdo24m_adj_mode(struct tdo24m *lcd, int mode) +{ + switch (mode) { + case MODE_VGA: + tdo24m_writes(lcd, lcd_vga_pass_through); + tdo24m_writes(lcd, lcd_panel_config); + tdo24m_writes(lcd, lcd_vga_transfer); + break; + case MODE_QVGA: + tdo24m_writes(lcd, lcd_qvga_pass_through); + tdo24m_writes(lcd, lcd_panel_config); + tdo24m_writes(lcd, lcd_qvga_transfer); + break; + default: + return -EINVAL; + } + + lcd->mode = mode; + return 0; +} + +static int tdo24m_power_on(struct tdo24m *lcd) +{ + int err; + + err = tdo24m_writes(lcd, lcd_panel_on); + if (err) + goto out; + + err = tdo24m_writes(lcd, lcd_panel_reset); + if (err) + goto out; + + err = tdo24m_adj_mode(lcd, lcd->mode); +out: + return err; +} + +static int tdo24m_power_off(struct tdo24m *lcd) +{ + return tdo24m_writes(lcd, lcd_panel_off); +} + +static int tdo24m_power(struct tdo24m *lcd, int power) +{ + int ret = 0; + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = tdo24m_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = tdo24m_power_off(lcd); + + if (!ret) + lcd->power = power; + + return ret; +} + + +static int tdo24m_set_power(struct lcd_device *ld, int power) +{ + struct tdo24m *lcd = lcd_get_data(ld); + return tdo24m_power(lcd, power); +} + +static int tdo24m_get_power(struct lcd_device *ld) +{ + struct tdo24m *lcd = lcd_get_data(ld); + return lcd->power; +} + +static int tdo24m_set_mode(struct lcd_device *ld, struct fb_videomode *m) +{ + struct tdo24m *lcd = lcd_get_data(ld); + int mode = MODE_QVGA; + + if (m->xres == 640 || m->xres == 480) + mode = MODE_VGA; + + if (lcd->mode == mode) + return 0; + + return tdo24m_adj_mode(lcd, mode); +} + +static struct lcd_ops tdo24m_ops = { + .get_power = tdo24m_get_power, + .set_power = tdo24m_set_power, + .set_mode = tdo24m_set_mode, +}; + +static int __devinit tdo24m_probe(struct spi_device *spi) +{ + struct tdo24m *lcd; + struct spi_message *m; + struct spi_transfer *x; + int err; + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + err = spi_setup(spi); + if (err) + return err; + + lcd = kzalloc(sizeof(struct tdo24m), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + lcd->spi_dev = spi; + lcd->power = FB_BLANK_POWERDOWN; + lcd->mode = MODE_VGA; /* default to VGA */ + + lcd->buf = kmalloc(TDO24M_SPI_BUFF_SIZE, sizeof(GFP_KERNEL)); + if (lcd->buf == NULL) { + kfree(lcd); + return -ENOMEM; + } + + m = &lcd->msg; + x = &lcd->xfer; + + spi_message_init(m); + + x->tx_buf = &lcd->buf[0]; + spi_message_add_tail(x, m); + + lcd->lcd_dev = lcd_device_register("tdo24m", &spi->dev, + lcd, &tdo24m_ops); + if (IS_ERR(lcd->lcd_dev)) { + err = PTR_ERR(lcd->lcd_dev); + goto out_free; + } + + dev_set_drvdata(&spi->dev, lcd); + err = tdo24m_power(lcd, FB_BLANK_UNBLANK); + if (err) + goto out_unregister; + + return 0; + +out_unregister: + lcd_device_unregister(lcd->lcd_dev); +out_free: + kfree(lcd->buf); + kfree(lcd); + return err; +} + +static int __devexit tdo24m_remove(struct spi_device *spi) +{ + struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + + tdo24m_power(lcd, FB_BLANK_POWERDOWN); + lcd_device_unregister(lcd->lcd_dev); + kfree(lcd->buf); + kfree(lcd); + + return 0; +} + +#ifdef CONFIG_PM +static int tdo24m_suspend(struct spi_device *spi, pm_message_t state) +{ + struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + + return tdo24m_power(lcd, FB_BLANK_POWERDOWN); +} + +static int tdo24m_resume(struct spi_device *spi) +{ + struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + + return tdo24m_power(lcd, FB_BLANK_UNBLANK); +} +#else +#define tdo24m_suspend NULL +#define tdo24m_resume NULL +#endif + +/* Power down all displays on reboot, poweroff or halt */ +static void tdo24m_shutdown(struct spi_device *spi) +{ + struct tdo24m *lcd = dev_get_drvdata(&spi->dev); + + tdo24m_power(lcd, FB_BLANK_POWERDOWN); +} + +static struct spi_driver tdo24m_driver = { + .driver = { + .name = "tdo24m", + .owner = THIS_MODULE, + }, + .probe = tdo24m_probe, + .remove = __devexit_p(tdo24m_remove), + .shutdown = tdo24m_shutdown, + .suspend = tdo24m_suspend, + .resume = tdo24m_resume, +}; + +static int __init tdo24m_init(void) +{ + return spi_register_driver(&tdo24m_driver); +} +module_init(tdo24m_init); + +static void __exit tdo24m_exit(void) +{ + spi_unregister_driver(&tdo24m_driver); +} +module_exit(tdo24m_exit); + +MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>"); +MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 98843c2ecf73..0737570030f5 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -979,6 +979,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) info->flags &= ~FBINFO_MISC_USEREVENT; event.info = info; + event.data = &mode; fb_notifier_call_chain(evnt, &event); } } diff --git a/drivers/video/metronomefb.c b/drivers/video/metronomefb.c index cc4c038a1b3f..afeed0611e3e 100644 --- a/drivers/video/metronomefb.c +++ b/drivers/video/metronomefb.c @@ -40,29 +40,63 @@ #include <asm/unaligned.h> - -#define DEBUG 1 -#ifdef DEBUG -#define DPRINTK(f, a...) printk(KERN_DEBUG "%s: " f, __func__ , ## a) -#else -#define DPRINTK(f, a...) -#endif - - /* Display specific information */ #define DPY_W 832 #define DPY_H 622 +static int user_wfm_size; + /* frame differs from image. frame includes non-visible pixels */ struct epd_frame { int fw; /* frame width */ int fh; /* frame height */ + u16 config[4]; + int wfm_size; }; static struct epd_frame epd_frame_table[] = { { - .fw = 832, - .fh = 622 + .fw = 832, + .fh = 622, + .config = { + 15 /* sdlew */ + | 2 << 8 /* sdosz */ + | 0 << 11 /* sdor */ + | 0 << 12 /* sdces */ + | 0 << 15, /* sdcer */ + 42 /* gdspl */ + | 1 << 8 /* gdr1 */ + | 1 << 9 /* sdshr */ + | 0 << 15, /* gdspp */ + 18 /* gdspw */ + | 0 << 15, /* dispc */ + 599 /* vdlc */ + | 0 << 11 /* dsi */ + | 0 << 12, /* dsic */ + }, + .wfm_size = 47001, + }, + { + .fw = 1088, + .fh = 791, + .config = { + 0x0104, + 0x031f, + 0x0088, + 0x02ff, + }, + .wfm_size = 46770, + }, + { + .fw = 1200, + .fh = 842, + .config = { + 0x0101, + 0x030e, + 0x0012, + 0x0280, + }, + .wfm_size = 46770, }, }; @@ -134,9 +168,8 @@ static u16 calc_img_cksum(u16 *start, int length) } /* here we decode the incoming waveform file and populate metromem */ -#define EXP_WFORM_SIZE 47001 -static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, - u8 *frame_count) +static int __devinit load_waveform(u8 *mem, size_t size, int m, int t, + struct metronomefb_par *par) { int tta; int wmta; @@ -148,26 +181,31 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, int wfm_idx, owfm_idx; int mem_idx = 0; struct waveform_hdr *wfm_hdr; + u8 *metromem = par->metromem_wfm; + struct device *dev = par->info->dev; - if (size != EXP_WFORM_SIZE) { - printk(KERN_ERR "Error: unexpected size %d != %d\n", size, - EXP_WFORM_SIZE); + if (user_wfm_size) + epd_frame_table[par->dt].wfm_size = user_wfm_size; + + if (size != epd_frame_table[par->dt].wfm_size) { + dev_err(dev, "Error: unexpected size %d != %d\n", size, + epd_frame_table[par->dt].wfm_size); return -EINVAL; } wfm_hdr = (struct waveform_hdr *) mem; if (wfm_hdr->fvsn != 1) { - printk(KERN_ERR "Error: bad fvsn %x\n", wfm_hdr->fvsn); + dev_err(dev, "Error: bad fvsn %x\n", wfm_hdr->fvsn); return -EINVAL; } if (wfm_hdr->luts != 0) { - printk(KERN_ERR "Error: bad luts %x\n", wfm_hdr->luts); + dev_err(dev, "Error: bad luts %x\n", wfm_hdr->luts); return -EINVAL; } cksum = calc_cksum(32, 47, mem); if (cksum != wfm_hdr->wfm_cs) { - printk(KERN_ERR "Error: bad cksum %x != %x\n", cksum, + dev_err(dev, "Error: bad cksum %x != %x\n", cksum, wfm_hdr->wfm_cs); return -EINVAL; } @@ -175,7 +213,7 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, wfm_hdr->trc += 1; for (i = 0; i < 5; i++) { if (*(wfm_hdr->stuff2a + i) != 0) { - printk(KERN_ERR "Error: unexpected value in padding\n"); + dev_err(dev, "Error: unexpected value in padding\n"); return -EINVAL; } } @@ -200,7 +238,7 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, return -EINVAL; cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem); if (cksum != mem[cksum_idx]) { - printk(KERN_ERR "Error: bad temperature range table cksum" + dev_err(dev, "Error: bad temperature range table cksum" " %x != %x\n", cksum, mem[cksum_idx]); return -EINVAL; } @@ -212,7 +250,7 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, return -EINVAL; cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); if (cksum != mem[cksum_idx]) { - printk(KERN_ERR "Error: bad mode table address cksum" + dev_err(dev, "Error: bad mode table address cksum" " %x != %x\n", cksum, mem[cksum_idx]); return -EINVAL; } @@ -224,7 +262,7 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, return -EINVAL; cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); if (cksum != mem[cksum_idx]) { - printk(KERN_ERR "Error: bad temperature table address cksum" + dev_err(dev, "Error: bad temperature table address cksum" " %x != %x\n", cksum, mem[cksum_idx]); return -EINVAL; } @@ -259,11 +297,11 @@ static int load_waveform(u8 *mem, size_t size, u8 *metromem, int m, int t, return -EINVAL; cksum = calc_cksum(owfm_idx, cksum_idx, mem); if (cksum != mem[cksum_idx]) { - printk(KERN_ERR "Error: bad waveform data cksum" + dev_err(dev, "Error: bad waveform data cksum" " %x != %x\n", cksum, mem[cksum_idx]); return -EINVAL; } - *frame_count = (mem_idx/64); + par->frame_count = (mem_idx/64); return 0; } @@ -274,15 +312,12 @@ static int metronome_display_cmd(struct metronomefb_par *par) u16 cs; u16 opcode; static u8 borderval; - u8 *ptr; /* setup display command we can't immediately set the opcode since the controller will try parse the command before we've set it all up so we just set cs here and set the opcode at the end */ - ptr = par->metromem; - if (par->metromem_cmd->opcode == 0xCC40) opcode = cs = 0xCC41; else @@ -335,44 +370,17 @@ static int __devinit metronome_powerup_cmd(struct metronomefb_par *par) static int __devinit metronome_config_cmd(struct metronomefb_par *par) { - int i; - u16 cs; - /* setup config command we can't immediately set the opcode since the controller - will try parse the command before we've set it all up - so we just set cs here and set the opcode at the end */ - - cs = 0xCC10; - - /* set the 12 args ( 8 bytes ) for config. see spec for meanings */ - i = 0; - par->metromem_cmd->args[i] = 15 /* sdlew */ - | 2 << 8 /* sdosz */ - | 0 << 11 /* sdor */ - | 0 << 12 /* sdces */ - | 0 << 15; /* sdcer */ - cs += par->metromem_cmd->args[i++]; - - par->metromem_cmd->args[i] = 42 /* gdspl */ - | 1 << 8 /* gdr1 */ - | 1 << 9 /* sdshr */ - | 0 << 15; /* gdspp */ - cs += par->metromem_cmd->args[i++]; - - par->metromem_cmd->args[i] = 18 /* gdspw */ - | 0 << 15; /* dispc */ - cs += par->metromem_cmd->args[i++]; - - par->metromem_cmd->args[i] = 599 /* vdlc */ - | 0 << 11 /* dsi */ - | 0 << 12; /* dsic */ - cs += par->metromem_cmd->args[i++]; + will try parse the command before we've set it all up */ + memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config, + sizeof(epd_frame_table[par->dt].config)); /* the rest are 0 */ - memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2); + memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2); - par->metromem_cmd->csum = cs; + par->metromem_cmd->csum = 0xCC10; + par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4); par->metromem_cmd->opcode = 0xCC10; /* config cmd */ return par->board->met_wait_event(par); @@ -408,12 +416,9 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par) { int res; - par->board->init_gpio_regs(par); - - par->board->init_lcdc_regs(par); - - /* now that lcd is setup, setup dma descriptor */ - par->board->post_dma_setup(par); + res = par->board->setup_io(par); + if (res) + return res; res = metronome_powerup_cmd(par); if (res) @@ -430,16 +435,16 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par) static void metronomefb_dpy_update(struct metronomefb_par *par) { + int fbsize; u16 cksum; unsigned char *buf = (unsigned char __force *)par->info->screen_base; + fbsize = par->info->fix.smem_len; /* copy from vm to metromem */ - memcpy(par->metromem_img, buf, DPY_W*DPY_H); + memcpy(par->metromem_img, buf, fbsize); - cksum = calc_img_cksum((u16 *) par->metromem_img, - (epd_frame_table[0].fw * DPY_H)/2); - *((u16 *)(par->metromem_img) + - (epd_frame_table[0].fw * DPY_H)/2) = cksum; + cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2); + *((u16 *)(par->metromem_img) + fbsize/2) = cksum; metronome_display_cmd(par); } @@ -574,8 +579,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev) unsigned char *videomemory; struct metronomefb_par *par; const struct firmware *fw_entry; - int cmd_size, wfm_size, img_size, padding_size, totalsize; int i; + int panel_type; + int fw, fh; + int epd_dt_index; /* pick up board specific routines */ board = dev->dev.platform_data; @@ -586,96 +593,108 @@ static int __devinit metronomefb_probe(struct platform_device *dev) if (!try_module_get(board->owner)) return -ENODEV; + info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev); + if (!info) + goto err; + /* we have two blocks of memory. info->screen_base which is vm, and is the fb used by apps. par->metromem which is physically contiguous memory and contains the display controller commands, waveform, processed image data and padding. this is the data pulled - by the device's LCD controller and pushed to Metronome */ + by the device's LCD controller and pushed to Metronome. + the metromem memory is allocated by the board driver and + is provided to us */ + + panel_type = board->get_panel_type(); + switch (panel_type) { + case 6: + epd_dt_index = 0; + break; + case 8: + epd_dt_index = 1; + break; + case 97: + epd_dt_index = 2; + break; + default: + dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n"); + epd_dt_index = 0; + break; + } + + fw = epd_frame_table[epd_dt_index].fw; + fh = epd_frame_table[epd_dt_index].fh; - videomemorysize = (DPY_W*DPY_H); + /* we need to add a spare page because our csum caching scheme walks + * to the end of the page */ + videomemorysize = PAGE_SIZE + (fw * fh); videomemory = vmalloc(videomemorysize); if (!videomemory) - return -ENOMEM; + goto err_fb_rel; memset(videomemory, 0, videomemorysize); - info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev); - if (!info) - goto err_vfree; - info->screen_base = (char __force __iomem *)videomemory; info->fbops = &metronomefb_ops; + metronomefb_fix.line_length = fw; + metronomefb_var.xres = fw; + metronomefb_var.yres = fh; + metronomefb_var.xres_virtual = fw; + metronomefb_var.yres_virtual = fh; info->var = metronomefb_var; info->fix = metronomefb_fix; info->fix.smem_len = videomemorysize; par = info->par; par->info = info; par->board = board; + par->dt = epd_dt_index; init_waitqueue_head(&par->waitq); /* this table caches per page csum values. */ par->csum_table = vmalloc(videomemorysize/PAGE_SIZE); if (!par->csum_table) + goto err_vfree; + + /* the physical framebuffer that we use is setup by + * the platform device driver. It will provide us + * with cmd, wfm and image memory in a contiguous area. */ + retval = board->setup_fb(par); + if (retval) { + dev_err(&dev->dev, "Failed to setup fb\n"); goto err_csum_table; + } - /* the metromem buffer is divided as follows: - command | CRC | padding - 16kb waveform data | CRC | padding - image data | CRC - and an extra 256 bytes for dma descriptors - eg: IW=832 IH=622 WS=128 - */ - - cmd_size = 1 * epd_frame_table[0].fw; - wfm_size = ((16*1024 + 2 + epd_frame_table[0].fw - 1) - / epd_frame_table[0].fw) * epd_frame_table[0].fw; - img_size = epd_frame_table[0].fh * epd_frame_table[0].fw; - padding_size = 4 * epd_frame_table[0].fw; - totalsize = cmd_size + wfm_size + img_size + padding_size; - par->metromemsize = PAGE_ALIGN(totalsize + 256); - DPRINTK("desired memory size = %d\n", par->metromemsize); - dev->dev.coherent_dma_mask = 0xffffffffull; - par->metromem = dma_alloc_writecombine(&dev->dev, par->metromemsize, - &par->metromem_dma, GFP_KERNEL); - if (!par->metromem) { - printk(KERN_ERR - "metronomefb: unable to allocate dma buffer\n"); - goto err_vfree; + /* after this point we should have a framebuffer */ + if ((!par->metromem_wfm) || (!par->metromem_img) || + (!par->metromem_dma)) { + dev_err(&dev->dev, "fb access failure\n"); + retval = -EINVAL; + goto err_csum_table; } info->fix.smem_start = par->metromem_dma; - par->metromem_cmd = (struct metromem_cmd *) par->metromem; - par->metromem_wfm = par->metromem + cmd_size; - par->metromem_img = par->metromem + cmd_size + wfm_size; - par->metromem_img_csum = (u16 *) (par->metromem_img + - (epd_frame_table[0].fw * DPY_H)); - DPRINTK("img offset=0x%x\n", cmd_size + wfm_size); - par->metromem_desc = (struct metromem_desc *) (par->metromem + cmd_size - + wfm_size + img_size + padding_size); - par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size - + img_size + padding_size; /* load the waveform in. assume mode 3, temp 31 for now a) request the waveform file from userspace b) process waveform and decode into metromem */ retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev); if (retval < 0) { - printk(KERN_ERR "metronomefb: couldn't get waveform\n"); - goto err_dma_free; + dev_err(&dev->dev, "Failed to get waveform\n"); + goto err_csum_table; } - retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, - par->metromem_wfm, 3, 31, &par->frame_count); + retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31, + par); release_firmware(fw_entry); if (retval < 0) { - printk(KERN_ERR "metronomefb: couldn't process waveform\n"); - goto err_dma_free; + dev_err(&dev->dev, "Failed processing waveform\n"); + goto err_csum_table; } if (board->setup_irq(info)) - goto err_dma_free; + goto err_csum_table; retval = metronome_init_regs(par); if (retval < 0) @@ -688,8 +707,8 @@ static int __devinit metronomefb_probe(struct platform_device *dev) retval = fb_alloc_cmap(&info->cmap, 8, 0); if (retval < 0) { - printk(KERN_ERR "Failed to allocate colormap\n"); - goto err_fb_rel; + dev_err(&dev->dev, "Failed to allocate colormap\n"); + goto err_free_irq; } /* set cmap */ @@ -704,7 +723,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev) platform_set_drvdata(dev, info); - printk(KERN_INFO + dev_dbg(&dev->dev, "fb%d: Metronome frame buffer device, using %dK of video" " memory\n", info->node, videomemorysize >> 10); @@ -712,17 +731,15 @@ static int __devinit metronomefb_probe(struct platform_device *dev) err_cmap: fb_dealloc_cmap(&info->cmap); -err_fb_rel: - framebuffer_release(info); err_free_irq: - board->free_irq(info); -err_dma_free: - dma_free_writecombine(&dev->dev, par->metromemsize, par->metromem, - par->metromem_dma); + board->cleanup(par); err_csum_table: vfree(par->csum_table); err_vfree: vfree(videomemory); +err_fb_rel: + framebuffer_release(info); +err: module_put(board->owner); return retval; } @@ -733,15 +750,15 @@ static int __devexit metronomefb_remove(struct platform_device *dev) if (info) { struct metronomefb_par *par = info->par; + + unregister_framebuffer(info); fb_deferred_io_cleanup(info); - dma_free_writecombine(&dev->dev, par->metromemsize, - par->metromem, par->metromem_dma); fb_dealloc_cmap(&info->cmap); + par->board->cleanup(par); vfree(par->csum_table); - unregister_framebuffer(info); vfree((void __force *)info->screen_base); - par->board->free_irq(info); module_put(par->board->owner); + dev_dbg(&dev->dev, "calling release\n"); framebuffer_release(info); } return 0; @@ -766,6 +783,9 @@ static void __exit metronomefb_exit(void) platform_driver_unregister(&metronomefb_driver); } +module_param(user_wfm_size, uint, 0); +MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size"); + module_init(metronomefb_init); module_exit(metronomefb_exit); |