Patch provided by Mocean in order to enable the timberdale subsystem of the Russelville board. Signed-off-by: Joel Clark Acked-by: Arjan van de Ven Signed-off-by: Todd Brandt todd.e.brandt@intel.com diff -uNr linux-2.6.29-clean/drivers/gpio/Kconfig linux-2.6.29/drivers/gpio/Kconfig --- linux-2.6.29-clean/drivers/gpio/Kconfig 2009-04-01 09:20:23.000000000 -0700 +++ linux-2.6.29/drivers/gpio/Kconfig 2009-04-06 13:51:47.000000000 -0700 @@ -161,6 +161,12 @@ If unsure, say N. +config GPIO_TIMBERDALE + tristate "Support for timberdale GPIO" + depends on MFD_TIMBERDALE && GPIOLIB + ---help--- + Add support for GPIO usage of some pins of the timberdale FPGA. + comment "SPI GPIO expanders:" config GPIO_MAX7301 diff -uNr linux-2.6.29-clean/drivers/gpio/Makefile linux-2.6.29/drivers/gpio/Makefile --- linux-2.6.29-clean/drivers/gpio/Makefile 2009-04-01 09:20:23.000000000 -0700 +++ linux-2.6.29/drivers/gpio/Makefile 2009-04-06 13:51:47.000000000 -0700 @@ -12,3 +12,4 @@ obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o +obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o diff -uNr linux-2.6.29-clean/drivers/gpio/timbgpio.c linux-2.6.29/drivers/gpio/timbgpio.c --- linux-2.6.29-clean/drivers/gpio/timbgpio.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/gpio/timbgpio.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,275 @@ +/* + * timbgpio.c timberdale FPGA GPIO driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA GPIO + */ + +#include +#include +#include +#include +#include + +#include "timbgpio.h" + +static u32 timbgpio_configure(struct gpio_chip *gpio, unsigned nr, + unsigned off, unsigned val) +{ + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); + + u32 config, oldconfig, wconfig; + + mutex_lock(&tgpio->lock); + config = ioread32(tgpio->membase + off); + oldconfig = config; + + if (val) + config |= (1 << nr); + else + config &= ~(1 << nr); + + iowrite32(config, tgpio->membase + off); + wconfig = ioread32(tgpio->membase + off); + mutex_unlock(&tgpio->lock); + + return oldconfig; +} + +static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + timbgpio_configure(gpio, nr, TGPIODIR, 1); + return 0; +} + +static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct timbgpio *tgpio = container_of(gpio, struct timbgpio, gpio); + u32 value; + + value = ioread32(tgpio->membase + TGPIOVAL); + return (value & (1 << nr)) ? 1 : 0; +} + +static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, + unsigned nr, int val) +{ + timbgpio_configure(gpio, nr, TGPIODIR, 0); + return 0; +} + + + +static void timbgpio_gpio_set(struct gpio_chip *gpio, + unsigned nr, int val) +{ + timbgpio_configure(gpio, nr, TGPIOVAL, val); +} + +/* + * Function to control flank or level triggered GPIO pin + * @nr - pin + * @ val - 1: flank, 0: level + * + */ +static void timbgpio_gpio_flnk_lvl_ctrl(struct gpio_chip *gpio, + unsigned nr, int val) +{ + timbgpio_configure(gpio, nr, TGPIOFLK, val); +} +EXPORT_SYMBOL(timbgpio_gpio_flnk_lvl_ctrl); + +/* + * Enable or disable interrupt + * + */ +static void timbgpio_gpio_int_ctrl(struct gpio_chip *gpio, + unsigned nr, int val) +{ + timbgpio_configure(gpio, nr, TGPIOINT, val); +} +EXPORT_SYMBOL(timbgpio_gpio_int_ctrl); + +/* + * @val - 1: Asserted high or on positive flank, 0: Asserted low or on negative flank + * + */ +static void timbgpio_gpio_lvl_ctrl(struct gpio_chip *gpio, + unsigned nr, int val) +{ + timbgpio_configure(gpio, nr, TGPIOLVL, val); +} +EXPORT_SYMBOL(timbgpio_gpio_lvl_ctrl); + +static void timbgpio_gpio_int_clr(struct gpio_chip *gpio, + unsigned nr, int val) +{ + timbgpio_configure(gpio, nr, TGPIOINT_CLR, val); +} +EXPORT_SYMBOL(timbgpio_gpio_int_clr); + + +static irqreturn_t timbgpio_handleinterrupt(int irq, void *devid) +{ + struct timbgpio *tgpio = (struct timbgpio *)devid; + + iowrite32(0xffffffff, tgpio->membase + TGPIOINT_CLR); + + return IRQ_HANDLED; +} + +static int timbgpio_probe(struct platform_device *dev) +{ + int err, irq; + struct gpio_chip *gc; + struct timbgpio *tgpio; + struct resource *iomem, *rscr; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) { + err = -EINVAL; + goto err_mem; + } + + tgpio = kzalloc(sizeof(*tgpio), GFP_KERNEL); + if (!tgpio) { + err = -EINVAL; + goto err_mem; + } + + mutex_init(&tgpio->lock); + + rscr = &tgpio->rscr; + rscr->name = "timb-gpio"; + rscr->start = iomem->start; + rscr->end = iomem->end; + rscr->flags = IORESOURCE_MEM; + + err = request_resource(iomem, rscr); + if (err) + goto err_request; + + tgpio->membase = ioremap(rscr->start, resource_size(rscr)); + if (!tgpio->membase) { + err = -ENOMEM; + goto err_ioremap; + } + + gc = &tgpio->gpio; + + gc->label = "timbgpio"; + gc->owner = THIS_MODULE; + gc->direction_input = timbgpio_gpio_direction_input; + gc->get = timbgpio_gpio_get; + gc->direction_output = timbgpio_gpio_direction_output; + gc->set = timbgpio_gpio_set; + gc->dbg_show = NULL; + gc->base = 0; + gc->ngpio = TIMB_NR_GPIOS; + gc->can_sleep = 0; + + err = gpiochip_add(gc); + if (err) + goto err_chipadd; + + platform_set_drvdata(dev, tgpio); + + /* register interrupt */ + irq = platform_get_irq(dev, 0); + if (irq < 0) + goto err_get_irq; + + /* clear pending interrupts */ + iowrite32(0xffffffff, tgpio->membase + TGPIOINT_CLR); + iowrite32(0x0, tgpio->membase + TGPIOINT); + + /* request IRQ */ + err = request_irq(irq, timbgpio_handleinterrupt, IRQF_SHARED, + "timb-gpio", tgpio); + if (err) { + printk(KERN_ERR "timbgpio: Failed to request IRQ\n"); + goto err_get_irq; + } + + return err; + +err_get_irq: + err = gpiochip_remove(&tgpio->gpio); + if (err) + printk(KERN_ERR "timbgpio: failed to remove gpio_chip\n"); +err_chipadd: + iounmap(tgpio->membase); +err_ioremap: + release_resource(&tgpio->rscr); +err_request: + kfree(tgpio); +err_mem: + printk(KERN_ERR "timberdale: Failed to register GPIOs: %d\n", err); + + return err; +} + +static int timbgpio_remove(struct platform_device *dev) +{ + int err; + struct timbgpio *tgpio = platform_get_drvdata(dev); + + /* disable interrupts */ + iowrite32(0x0, tgpio->membase + TGPIOINT); + + free_irq(platform_get_irq(dev, 0), tgpio); + err = gpiochip_remove(&tgpio->gpio); + if (err) + printk(KERN_ERR "timbgpio: failed to remove gpio_chip\n"); + + iounmap(tgpio->membase); + release_resource(&tgpio->rscr); + kfree(tgpio); + + return 0; +} + +static struct platform_driver timbgpio_platform_driver = { + .driver = { + .name = "timb-gpio", + .owner = THIS_MODULE, + }, + .probe = timbgpio_probe, + .remove = timbgpio_remove, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timbgpio_init(void) +{ + return platform_driver_register(&timbgpio_platform_driver); +} + +static void __exit timbgpio_exit(void) +{ + platform_driver_unregister(&timbgpio_platform_driver); +} + +module_init(timbgpio_init); +module_exit(timbgpio_exit); + +MODULE_DESCRIPTION("Timberdale GPIO driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_ALIAS("platform:timb-gpio"); + diff -uNr linux-2.6.29-clean/drivers/gpio/timbgpio.h linux-2.6.29/drivers/gpio/timbgpio.h --- linux-2.6.29-clean/drivers/gpio/timbgpio.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/gpio/timbgpio.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,48 @@ +/* + * timbgpio.h timberdale FPGA GPIO driver defines + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA GPIO + */ + +#ifndef _TIMBGPIO_H_ +#define _TIMBGPIO_H_ + +#include +#include + +#define TIMB_NR_GPIOS 16 + +#define TGPIOVAL 0 +#define TGPIODIR 0x04 +#define TGPIOINT 0x08 +#define TGPIOINT_STATUS 0x0c +#define TGPIOINT_PENDING 0x10 +#define TGPIOINT_CLR 0x14 +#define TGPIOFLK 0x18 +#define TGPIOLVL 0x1c + +struct timbgpio { + void __iomem *membase; + struct resource rscr; + struct mutex lock; /* mutual exclusion */ + struct pci_dev *pdev; + struct gpio_chip gpio; +}; + +#endif diff -uNr linux-2.6.29-clean/drivers/i2c/busses/i2c-ocores.c linux-2.6.29/drivers/i2c/busses/i2c-ocores.c --- linux-2.6.29-clean/drivers/i2c/busses/i2c-ocores.c 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/i2c/busses/i2c-ocores.c 2009-04-06 13:51:47.000000000 -0700 @@ -216,6 +216,7 @@ struct ocores_i2c_platform_data *pdata; struct resource *res, *res2; int ret; + u8 i; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -271,6 +272,10 @@ goto add_adapter_failed; } + /* add in known devices to the bus */ + for (i = 0; i < pdata->num_devices; i++) + i2c_new_device(&i2c->adap, pdata->devices + i); + return 0; add_adapter_failed: diff -uNr linux-2.6.29-clean/drivers/input/touchscreen/Kconfig linux-2.6.29/drivers/input/touchscreen/Kconfig --- linux-2.6.29-clean/drivers/input/touchscreen/Kconfig 2009-04-01 09:20:23.000000000 -0700 +++ linux-2.6.29/drivers/input/touchscreen/Kconfig 2009-04-06 13:51:47.000000000 -0700 @@ -397,6 +397,17 @@ To compile this driver as a module, choose M here: the module will be called touchit213. +config TOUCHSCREEN_TSC2003 + tristate "TSC2003 based touchscreens" + depends on I2C + help + Say Y here if you have a TSC2003 based touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called tsc2003. + config TOUCHSCREEN_TSC2007 tristate "TSC2007 based touchscreens" depends on I2C diff -uNr linux-2.6.29-clean/drivers/input/touchscreen/Makefile linux-2.6.29/drivers/input/touchscreen/Makefile --- linux-2.6.29-clean/drivers/input/touchscreen/Makefile 2009-04-01 09:20:23.000000000 -0700 +++ linux-2.6.29/drivers/input/touchscreen/Makefile 2009-04-06 13:51:47.000000000 -0700 @@ -25,6 +25,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o +obj-$(CONFIG_TOUCHSCREEN_TSC2003) += tsc2003.o obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o diff -uNr linux-2.6.29-clean/drivers/input/touchscreen/tsc2003.c linux-2.6.29/drivers/input/touchscreen/tsc2003.c --- linux-2.6.29-clean/drivers/input/touchscreen/tsc2003.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/input/touchscreen/tsc2003.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,387 @@ +/* + * tsc2003.c Driver for TI TSC2003 touch screen controller + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * TI TSC2003 + * + * Inspired by tsc2007, Copyright (c) 2008 MtekVision Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include + +#define TSC2003_DRIVER_NAME "tsc2003" + +#define TS_POLL_PERIOD 20 /* ms delay between samples */ + +#define TSC2003_MEASURE_TEMP0 (0x0 << 4) +#define TSC2003_MEASURE_AUX (0x2 << 4) +#define TSC2003_MEASURE_TEMP1 (0x4 << 4) +#define TSC2003_ACTIVATE_XN (0x8 << 4) +#define TSC2003_ACTIVATE_YN (0x9 << 4) +#define TSC2003_ACTIVATE_YP_XN (0xa << 4) +#define TSC2003_SETUP (0xb << 4) +#define TSC2003_MEASURE_X (0xc << 4) +#define TSC2003_MEASURE_Y (0xd << 4) +#define TSC2003_MEASURE_Z1 (0xe << 4) +#define TSC2003_MEASURE_Z2 (0xf << 4) + +#define TSC2003_POWER_OFF_IRQ_EN (0x0 << 2) +#define TSC2003_ADC_ON_IRQ_DIS0 (0x1 << 2) +#define TSC2003_ADC_OFF_IRQ_EN (0x2 << 2) +#define TSC2003_ADC_ON_IRQ_DIS1 (0x3 << 2) + +#define TSC2003_12BIT (0x0 << 1) +#define TSC2003_8BIT (0x1 << 1) + +#define MAX_12BIT ((1 << 12) - 1) + +#define ADC_ON_12BIT (TSC2003_12BIT | TSC2003_ADC_ON_IRQ_DIS0) + +#define READ_Y (ADC_ON_12BIT | TSC2003_MEASURE_Y) +#define READ_Z1 (ADC_ON_12BIT | TSC2003_MEASURE_Z1) +#define READ_Z2 (ADC_ON_12BIT | TSC2003_MEASURE_Z2) +#define READ_X (ADC_ON_12BIT | TSC2003_MEASURE_X) +#define PWRDOWN (TSC2003_12BIT | TSC2003_POWER_OFF_IRQ_EN) + +struct ts_event { + int x; + int y; + int z1, z2; +}; + +struct tsc2003 { + struct input_dev *input; + char phys[32]; + struct task_struct *task; + struct ts_event tc; + struct completion penirq_completion; + + struct i2c_client *client; + + u16 model; + u16 x_plate_ohms; + + unsigned pendown; +}; + +static inline int tsc2003_xfer(struct tsc2003 *tsc, u8 cmd) +{ + s32 data; + u16 val; + + data = i2c_smbus_read_word_data(tsc->client, cmd); + if (data < 0) { + dev_err(&tsc->client->dev, "i2c io error: %d\n", data); + return data; + } + + /* The protocol and raw data format from i2c interface: + * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. + */ + val = swab16(data) >> 4; + + dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); + + return val; +} + +static void tsc2003_send_event(void *tsc) +{ + struct tsc2003 *ts = tsc; + struct input_dev *input = ts->input; + u32 rt = 0; + u16 x, y, z1, z2; + + x = ts->tc.x; + y = ts->tc.y; + z1 = ts->tc.z1; + z2 = ts->tc.z2; + + /* range filtering */ + if (x == MAX_12BIT) + x = 0; + + if (likely(x && z1)) { + /* compute touch pressure resistance using equation #1 */ + rt = z2; + rt -= z1; + rt *= x; + rt *= ts->x_plate_ohms; + rt /= z1; + rt = (rt + 2047) >> 12; + } + + /* Sample found inconsistent by debouncing or pressure is beyond + * the maximum. Don't report it to user space, repeat at least + * once more the measurement + */ + if (rt > MAX_12BIT) + return; + + /* NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure + * value can fluctuate for quite a while after lifting the pen and + * in some cases may not even settle at the expected value. + * + * The only safe way to check for the pen up condition is in the + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). + */ + if (rt) { + if (!ts->pendown) { + dev_dbg(&ts->client->dev, "DOWN\n"); + + input_report_key(input, BTN_TOUCH, 1); + ts->pendown = 1; + } + + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, rt); + + input_sync(input); + + dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", + x, y, rt); + } else if (ts->pendown) { + /* pen up */ + dev_dbg(&ts->client->dev, "UP\n"); + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + ts->pendown = 0; + } +} + +static int tsc2003_power_off_irq_en(struct tsc2003 *tsc) +{ + /* power down */ + return tsc2003_xfer(tsc, PWRDOWN); +} + +static int tsc2003_read_values(struct tsc2003 *tsc) +{ + /* y- still on; turn on only y+ (and ADC) */ + tsc->tc.y = tsc2003_xfer(tsc, READ_Y); + if (tsc->tc.y < 0) + return tsc->tc.y; + + /* turn y- off, x+ on, then leave in lowpower */ + tsc->tc.x = tsc2003_xfer(tsc, READ_X); + if (tsc->tc.x < 0) + return tsc->tc.x; + + /* turn y+ off, x- on; we'll use formula #1 */ + tsc->tc.z1 = tsc2003_xfer(tsc, READ_Z1); + if (tsc->tc.z1 < 0) + return tsc->tc.z1; + + tsc->tc.z2 = tsc2003_xfer(tsc, READ_Z2); + if (tsc->tc.z2 < 0) + return tsc->tc.z2; + + return 0; +} + + +static irqreturn_t tsc2003_irq(int irq, void *handle) +{ + struct tsc2003 *ts = handle; + + /* do not call the synced version -> deadlock */ + disable_irq_nosync(irq); + /* signal the thread to continue */ + complete(&ts->penirq_completion); + + return IRQ_HANDLED; +} + +static int tsc2003_thread(void *d) +{ + struct tsc2003 *ts = (struct tsc2003 *)d; + int ret; + + allow_signal(SIGKILL); + + while (!signal_pending(current)) { + /* power down and wait for interrupt */ + do { + /* loop because the I2C bus might be busy */ + ret = msleep_interruptible(TS_POLL_PERIOD); + if (!ret) + ret = tsc2003_power_off_irq_en(ts); + } while (ret == -EAGAIN && !signal_pending(current)); + + if (signal_pending(current)) + break; + + ret = wait_for_completion_interruptible(&ts->penirq_completion); + if (!ret) { + int first = 1; + /* got IRQ, start poll, until pen is up */ + while (!ret && !signal_pending(current) + && (first || ts->pendown)) { + ret = tsc2003_read_values(ts); + if (!ret) + tsc2003_send_event(ts); + ret = msleep_interruptible(TS_POLL_PERIOD); + first = 0; + } + + /* we re enable the interrupt */ + if (!signal_pending(current)) + enable_irq(ts->client->irq); + } + } + + return 0; +} + +static int tsc2003_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tsc2003 *ts; + struct tsc2007_platform_data *pdata = client->dev.platform_data; + struct input_dev *input_dev; + int err; + + if (!pdata) { + dev_err(&client->dev, "platform data is required!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct tsc2003), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + i2c_set_clientdata(client, ts); + + ts->input = input_dev; + + ts->model = pdata->model; + ts->x_plate_ohms = pdata->x_plate_ohms; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = TSC2003_DRIVER_NAME" Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + init_completion(&ts->penirq_completion); + + ts->task = kthread_run(tsc2003_thread, ts, TSC2003_DRIVER_NAME); + if (IS_ERR(ts->task)) { + err = PTR_ERR(ts->task); + goto err_free_mem; + } + + err = request_irq(client->irq, tsc2003_irq, 0, + client->dev.driver->name, ts); + if (err < 0) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_free_thread; + } + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + dev_info(&client->dev, "registered with irq (%d)\n", client->irq); + + return 0; + + err_free_irq: + free_irq(client->irq, ts); + err_free_thread: + kthread_stop(ts->task); + err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +static int tsc2003_remove(struct i2c_client *client) +{ + struct tsc2003 *ts = i2c_get_clientdata(client); + + free_irq(client->irq, ts); + send_sig(SIGKILL, ts->task, 1); + kthread_stop(ts->task); + input_unregister_device(ts->input); + kfree(ts); + + return 0; +} + +static struct i2c_device_id tsc2003_idtable[] = { + { TSC2003_DRIVER_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, tsc2003_idtable); + +static struct i2c_driver tsc2003_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TSC2003_DRIVER_NAME, + .bus = &i2c_bus_type, + }, + .id_table = tsc2003_idtable, + .probe = tsc2003_probe, + .remove = tsc2003_remove, +}; + +static int __init tsc2003_init(void) +{ + return i2c_add_driver(&tsc2003_driver); +} + +static void __exit tsc2003_exit(void) +{ + i2c_del_driver(&tsc2003_driver); +} + +module_init(tsc2003_init); +module_exit(tsc2003_exit); + +MODULE_AUTHOR("Mocean Laboratories "); +MODULE_DESCRIPTION("TSC2003 TouchScreen Driver"); +MODULE_LICENSE("GPL v2"); + diff -uNr linux-2.6.29-clean/drivers/media/video/adv7180.c linux-2.6.29/drivers/media/video/adv7180.c --- linux-2.6.29-clean/drivers/media/video/adv7180.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/media/video/adv7180.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,361 @@ +/* + * adv7180.c Analog Devices ADV7180 video decoder driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_LICENSE("GPL v2"); + +static inline int adv7180_write(struct i2c_client *client, + u8 reg, u8 value) +{ + struct adv7180 *decoder = i2c_get_clientdata(client); + + decoder->reg[reg] = value; + return i2c_smbus_write_byte_data(client, reg, value); +} + +static inline int adv7180_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int adv7180_write_block(struct i2c_client *client, + const u8 *data, unsigned int len) +{ + int ret = -1; + u8 reg; + + /* the adv7180 has an autoincrement function, use it if + * the adapter understands raw I2C */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + /* do raw I2C, not smbus compatible */ + struct adv7180 *decoder = i2c_get_clientdata(client); + u8 block_data[32]; + int block_len; + + while (len >= 2) { + block_len = 0; + reg = data[0]; + block_data[block_len++] = reg; + do { + block_data[block_len++] = + decoder->reg[reg++] = data[1]; + len -= 2; + data += 2; + } while (len >= 2 && data[0] == reg && + block_len < 32); + + ret = i2c_master_send(client, block_data, block_len); + if (ret < 0) + break; + } + } else { + /* do some slow I2C emulation kind of thing */ + while (len >= 2) { + reg = *data++; + ret = adv7180_write(client, reg, *data++); + if (ret < 0) + break; + + len -= 2; + } + } + + return ret; +} +#ifdef CONFIG_MFD_TIMBERDALE +static irqreturn_t adv7180_irq(int irq, void *dev_id) +{ + struct adv7180 *decoder = (struct adv7180 *) dev_id; + + /* Activate access to sub-regs */ + adv7180_write(decoder->client, ADV7180_ADI_CTRL, ADI_ENABLE); + + /* TODO: implement a real interrupt handler + * for now just + * clear all four regs + */ + adv7180_write_block(decoder->client, reset_icr, sizeof(reset_icr)); + + return IRQ_HANDLED; +} +#endif +static int adv7180_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct adv7180 *decoder = i2c_get_clientdata(client); + int *iarg = (int *)arg; + int status; + + switch (cmd) { + + case DECODER_INIT: + adv7180_write(client, 0x0f, 0x80); /* Reset */ + break; + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *cap = arg; + cap->flags = VIDEO_DECODER_PAL | + VIDEO_DECODER_NTSC | + VIDEO_DECODER_SECAM | + VIDEO_DECODER_AUTO; + cap->inputs = 3; + cap->outputs = 1; + } + break; + + case DECODER_GET_STATUS: + { + *iarg = 0; + status = adv7180_read(client, ADV7180_SR); + if ((status & ADV7180_STATUS_PAL)) + *iarg = (*iarg | DECODER_STATUS_PAL); + + if ((status & ADV7180_STATUS_NTSC)) + *iarg = (*iarg | DECODER_STATUS_NTSC); + + if ((status & ADV7180_STATUS_SECAM)) + *iarg = (*iarg | DECODER_STATUS_SECAM); + } + break; + + case DECODER_SET_NORM: + { + int v = *(int *) arg; + if (decoder->norm != v) { + decoder->norm = v; + switch (v) { + case VIDEO_MODE_NTSC: + adv7180_write(client, ADV7180_IN_CTRL, 0x40); + break; + case VIDEO_MODE_PAL: + adv7180_write(client, ADV7180_IN_CTRL, 0x70); + break; + case VIDEO_MODE_SECAM: + adv7180_write(client, ADV7180_IN_CTRL, 0x90); + break; + case VIDEO_MODE_AUTO: + adv7180_write(client, ADV7180_IN_CTRL, 0x00); + break; + default: + return -EPERM; + } + } + } + break; + + case DECODER_SET_INPUT: + { + int v = *(int *) arg; + if (decoder->input != v) { + decoder->input = v; + + switch (v) { + case CVBS: + adv7180_write_block(client, init_cvbs_64, + sizeof(init_cvbs_64)); + break; + case SVIDEO: + adv7180_write_block(client, init_svideo_64, + sizeof(init_svideo_64)); + break; + case YPbPr: + adv7180_write_block(client, init_ypbpr_64, + sizeof(init_ypbpr_64)); + break; + default: + return -EINVAL; + } + } + } + break; + + case DECODER_SET_OUTPUT: + { + } + break; + + case DECODER_ENABLE_OUTPUT: + { + } + break; + + case DECODER_SET_PICTURE: + { + } + break; + + case DECODER_DUMP: + { + adv7180_write(client, 1, 0x88); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = { + 0x40 >> 1, 0x41 >> 1, + I2C_ADV7180 >> 1, 0x43 >> 1, + I2C_CLIENT_END +}; + +I2C_CLIENT_INSMOD; + +static int adv7180_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE + | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + /* Is chip alive ? */ + if (adv7180_read(client, 0x11) != 0x1b) + return -ENODEV; + + strlcpy(info->type, DRIVER_NAME, I2C_NAME_SIZE); + + return 0; +} + +static int adv7180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err = 0; + struct adv7180 *decoder; + + printk(KERN_INFO DRIVER_NAME" chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + decoder = kzalloc(sizeof(struct adv7180), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + decoder->norm = VIDEO_MODE_PAL | VIDEO_MODE_NTSC | + VIDEO_MODE_SECAM | + VIDEO_MODE_AUTO; + decoder->input = CVBS; + decoder->enable = 1; + decoder->client = client; + i2c_set_clientdata(client, decoder); +#ifdef CONFIG_MFD_TIMBERDALE + err = request_irq(client->irq, adv7180_irq, 0, + client->dev.driver->name, decoder); + if (err < 0) { + dev_err(&client->dev, "irq %d busy?\n", client->irq); + goto err_free_dec; + } + dev_info(&client->dev, "registered with irq (%d)\n", client->irq); +#endif + adv7180_command(client, DECODER_INIT, NULL); /* Reset */ + + return 0; +#ifdef CONFIG_MFD_TIMBERDALE +err_free_dec: + kfree(decoder); + + return err; +#endif +} + +static int adv7180_remove(struct i2c_client *client) +{ + struct adv7180 *decoder = i2c_get_clientdata(client); +#ifdef CONFIG_MFD_TIMBERDALE + free_irq(client->irq, decoder); +#endif + kfree(decoder); + return 0; +} + +/* ----------------------------------------------------------------------- */ +static const struct i2c_device_id adv7180_id[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adv7180_id); + +static struct i2c_driver i2c_driver_adv7180 = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .bus = &i2c_bus_type, + }, + + .id_table = adv7180_id, + .probe = adv7180_probe, + .remove = adv7180_remove, + + .class = 0xffffffff, + .detect = adv7180_detect, + .address_data = &addr_data, + + .command = adv7180_command, +}; + +static int __init adv7180_init(void) +{ + return i2c_add_driver(&i2c_driver_adv7180); +} + +static void __exit adv7180_exit(void) +{ + i2c_del_driver(&i2c_driver_adv7180); +} + +module_init(adv7180_init); +module_exit(adv7180_exit); diff -uNr linux-2.6.29-clean/drivers/media/video/Kconfig linux-2.6.29/drivers/media/video/Kconfig --- linux-2.6.29-clean/drivers/media/video/Kconfig 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/media/video/Kconfig 2009-04-06 13:51:47.000000000 -0700 @@ -251,6 +251,15 @@ comment "Video decoders" +config VIDEO_ADV7180 + tristate "Analog Devices ADV7180 decoder" + depends on VIDEO_V4L1 && I2C + ---help--- + Support for the Analog Devices ADV7180 video decoder. + + To compile this driver as a module, choose M here: the + module will be called adv7180. + config VIDEO_BT819 tristate "BT819A VideoStream decoder" depends on VIDEO_V4L1 && I2C @@ -800,6 +809,12 @@ ---help--- This is a v4l2 driver for the TI OMAP2 camera capture interface +config VIDEO_TIMBERDALE + tristate "Support for timberdale Video In/LogiWIN" + depends on VIDEO_V4L2 && MFD_TIMBERDALE_DMA + ---help--- + Add support for the Video In peripherial of the timberdale FPGA. + # # USB Multimedia device configuration # diff -uNr linux-2.6.29-clean/drivers/media/video/Makefile linux-2.6.29/drivers/media/video/Makefile --- linux-2.6.29-clean/drivers/media/video/Makefile 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/media/video/Makefile 2009-04-06 13:51:47.000000000 -0700 @@ -52,6 +52,7 @@ obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o +obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_BT819) += bt819.o obj-$(CONFIG_VIDEO_BT856) += bt856.o @@ -148,6 +149,8 @@ obj-$(CONFIG_VIDEO_AU0828) += au0828/ +obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o + obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff -uNr linux-2.6.29-clean/drivers/media/video/timblogiw.c linux-2.6.29/drivers/media/video/timblogiw.c --- linux-2.6.29-clean/drivers/media/video/timblogiw.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/media/video/timblogiw.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,930 @@ +/* + * timblogiw.c timberdale FPGA LogiWin Video In driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA LogiWin Video In + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "timblogiw.h" +#include + + +#define TIMBLOGIW_CTRL 0x40 + +#define TIMBLOGIW_H_SCALE 0x20 +#define TIMBLOGIW_V_SCALE 0x28 + +#define TIMBLOGIW_X_CROP 0x58 +#define TIMBLOGIW_Y_CROP 0x60 + +#define TIMBLOGIW_W_CROP 0x00 +#define TIMBLOGIW_H_CROP 0x08 + +#define TIMBLOGIW_VERSION_CODE 0x02 + +#define TIMBLOGIW_FRAME 0x10 +#define TIMBLOGIW_DROP 0x20 + +#define TIMBLOGIW_BUF 0x04 +#define TIMBLOGIW_TBI 0x2c +#define TIMBLOGIW_BPL 0x30 + +#define dbg(...) + +const struct timblogiw_tvnorm timblogiw_tvnorms[] = { + { + .v4l2_id = V4L2_STD_PAL, + .name = "PAL", + .swidth = 720, + .sheight = 576 + }, + { + .v4l2_id = V4L2_STD_NTSC_M, + .name = "NTSC", + .swidth = 720, + .sheight = 480 + } +}; + +static void timblogiw_handleframe(unsigned long arg) +{ + struct timblogiw_frame *f; + struct timblogiw *lw = (struct timblogiw *)arg; + + spin_lock_bh(&lw->queue_lock); + if (!list_empty(&lw->inqueue)) { + /* put the entry in the outqueue */ + f = list_entry(lw->inqueue.next, struct timblogiw_frame, frame); + + /* copy data from the DMA buffer */ + memcpy(f->bufmem, lw->dma.filled->buf, f->buf.length); + /* buffer consumed */ + lw->dma.filled = NULL; + + do_gettimeofday(&f->buf.timestamp); + f->buf.sequence = ++lw->frame_count; + f->buf.field = V4L2_FIELD_NONE; + f->state = F_DONE; + f->buf.bytesused = lw->frame_size; + list_move_tail(&f->frame, &lw->outqueue); + /* wake up any waiter */ + wake_up(&lw->wait_frame); + } + spin_unlock_bh(&lw->queue_lock); +} + +static int timblogiw_isr(u32 flag, void *pdev) +{ + struct timblogiw *lw = (struct timblogiw *)pdev; + + if (!lw->dma.filled) { + /* no stored transfer so far, store this, and flip to next */ + lw->dma.filled = lw->dma.transfer + lw->dma.curr; + lw->dma.curr = !lw->dma.curr; + } + + if (lw->stream == STREAM_ON) + timb_start_dma(DMA_IRQ_VIDEO_RX, + lw->dma.transfer[lw->dma.curr].handle, lw->frame_size, + lw->bytesperline); + + if (flag & DMA_IRQ_VIDEO_DROP) + dbg("%s: frame dropped\n", __func__); + if (flag & DMA_IRQ_VIDEO_RX) { + dbg("%s: frame RX\n", __func__); + tasklet_schedule(&lw->tasklet); + } + return 0; +} + +static void timblogiw_empty_framequeues(struct timblogiw *lw) +{ + u32 i; + + dbg("%s\n", __func__); + + INIT_LIST_HEAD(&lw->inqueue); + INIT_LIST_HEAD(&lw->outqueue); + + for (i = 0; i < lw->num_frames; i++) { + lw->frame[i].state = F_UNUSED; + lw->frame[i].buf.bytesused = 0; + } +} + +u32 timblogiw_request_buffers(struct timblogiw *lw, u32 count) +{ + /* needs to be page aligned cause the */ + /* buffers can be mapped individually! */ + const size_t imagesize = PAGE_ALIGN(lw->frame_size); + void *buff = NULL; + u32 i; + + dbg("%s - request of %i buffers of size %zi\n", + __func__, count, lw->frame_size); + + lw->dma.transfer[0].buf = pci_alloc_consistent(lw->dev, imagesize, + &lw->dma.transfer[0].handle); + lw->dma.transfer[1].buf = pci_alloc_consistent(lw->dev, imagesize, + &lw->dma.transfer[1].handle); + if ((lw->dma.transfer[0].buf == NULL) || + (lw->dma.transfer[1].buf == NULL)) { + printk(KERN_ALERT "alloc failed\n"); + if (lw->dma.transfer[0].buf != NULL) + pci_free_consistent(lw->dev, imagesize, + lw->dma.transfer[0].buf, + lw->dma.transfer[0].handle); + if (lw->dma.transfer[1].buf != NULL) + pci_free_consistent(lw->dev, imagesize, + lw->dma.transfer[1].buf, + lw->dma.transfer[1].handle); + return 0; + } + + if (count > TIMBLOGIW_NUM_FRAMES) + count = TIMBLOGIW_NUM_FRAMES; + + lw->num_frames = count; + while (lw->num_frames > 0) { + buff = vmalloc_32(lw->num_frames * imagesize); + if (buff) { + memset(buff, 0, lw->num_frames * imagesize); + break; + } + lw->num_frames--; + } + + for (i = 0; i < lw->num_frames; i++) { + lw->frame[i].bufmem = buff + i * imagesize; + lw->frame[i].buf.index = i; + lw->frame[i].buf.m.offset = i * imagesize; + lw->frame[i].buf.length = lw->frame_size; + lw->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + lw->frame[i].buf.sequence = 0; + lw->frame[i].buf.field = V4L2_FIELD_NONE; + lw->frame[i].buf.memory = V4L2_MEMORY_MMAP; + lw->frame[i].buf.flags = 0; + } + + lw->dma.curr = 0; + lw->dma.filled = NULL; + return lw->num_frames; +} + +void timblogiw_release_buffers(struct timblogiw *lw) +{ + dbg("%s\n", __func__); + + if (lw->frame[0].bufmem != NULL) { + vfree(lw->frame[0].bufmem); + lw->frame[0].bufmem = NULL; + lw->num_frames = TIMBLOGIW_NUM_FRAMES; + pci_free_consistent(lw->dev, lw->frame_size, + lw->dma.transfer[0].buf, lw->dma.transfer[0].handle); + pci_free_consistent(lw->dev, lw->frame_size, + lw->dma.transfer[1].buf, lw->dma.transfer[1].handle); + } +} + +/* IOCTL functions */ + +static int timblogiw_g_fmt(struct timblogiw *lw, struct v4l2_format *format) +{ + dbg("%s -\n", __func__); + + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + format->fmt.pix.width = lw->width; + format->fmt.pix.height = lw->height; + format->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + format->fmt.pix.bytesperline = lw->bytesperline; + format->fmt.pix.sizeimage = lw->frame_size; + format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + format->fmt.pix.field = V4L2_FIELD_NONE; + return 0; +} + +static int timblogiw_s_fmt(struct timblogiw *lw, struct v4l2_format *format) +{ + struct v4l2_pix_format *pix = &format->fmt.pix; + dbg("%s - type: %d\n", __func__, format->type); + + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if ((lw->height != pix->height) || (lw->width != lw->width)) + return -EINVAL; + + if (format->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + + dbg("%s - width=%d, height=%d, pixelformat=%d, field=%d\n" + "bytes per line %d, size image: %d, colorspace: %d\n", + __func__, + pix->width, pix->height, pix->pixelformat, pix->field, + pix->bytesperline, pix->sizeimage, pix->colorspace); + + return 0; +} + +static int timblogiw_querycap(struct timblogiw *lw, + struct v4l2_capability *cap) +{ + memset(cap, 0, sizeof(*cap)); + strncpy(cap->card, "Timberdale Video", sizeof(cap->card)-1); + strncpy(cap->driver, "Timblogiw", sizeof(cap->card)-1); + cap->version = TIMBLOGIW_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; + + return 0; +} + +static int timblogiw_enum_fmt(struct timblogiw *lw, struct v4l2_fmtdesc *fmt) +{ + dbg("%s - VIDIOC_ENUM_FMT\n", __func__); + + if (fmt->index != 0) + return -EINVAL; + memset(fmt, 0, sizeof(*fmt)); + fmt->index = 0; + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strncpy(fmt->description, "4:2:2, packed, YUYV", + sizeof(fmt->description)-1); + fmt->pixelformat = V4L2_PIX_FMT_YUYV; + memset(fmt->reserved, 0, sizeof(fmt->reserved)); + + return 0; +} + +static int timblogiw_reqbufs(struct timblogiw *lw, + struct v4l2_requestbuffers *rb) +{ + if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + timblogiw_empty_framequeues(lw); + + timblogiw_release_buffers(lw); + if (rb->count) + rb->count = timblogiw_request_buffers(lw, rb->count); + + dbg("%s - VIDIOC_REQBUFS: io method is mmap. num bufs %i\n", + __func__, rb->count); + + return 0; +} + +static int timblogiw_querybuf(struct timblogiw *lw, struct v4l2_buffer *b) +{ + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b->index >= lw->num_frames) + return -EINVAL; + + memcpy(b, &lw->frame[b->index].buf, sizeof(*b)); + + if (lw->frame[b->index].vma_use_count) + b->flags |= V4L2_BUF_FLAG_MAPPED; + + if (lw->frame[b->index].state == F_DONE) + b->flags |= V4L2_BUF_FLAG_DONE; + else if (lw->frame[b->index].state != F_UNUSED) + b->flags |= V4L2_BUF_FLAG_QUEUED; + + return 0; +} + +static int timblogiw_qbuf(struct timblogiw *lw, struct v4l2_buffer *b) +{ + unsigned long lock_flags; + + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + b->index >= lw->num_frames) + return -EINVAL; + + if (lw->frame[b->index].state != F_UNUSED) + return -EAGAIN; + + if (b->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + lw->frame[b->index].state = F_QUEUED; + + spin_lock_irqsave(&lw->queue_lock, lock_flags); + list_add_tail(&lw->frame[b->index].frame, &lw->inqueue); + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); + + return 0; +} + +static int timblogiw_dqbuf(struct timblogiw *lw, struct file *file, + struct v4l2_buffer *b) +{ + struct timblogiw_frame *f; + unsigned long lock_flags; + int ret = 0; + + if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dbg("%s - VIDIOC_DQBUF, illegal buf type!\n", + __func__); + return -EINVAL; + } + + if (list_empty(&lw->outqueue)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(lw->wait_frame, + !list_empty(&lw->outqueue)); + if (ret) + return ret; + } + + spin_lock_irqsave(&lw->queue_lock, lock_flags); + f = list_entry(lw->outqueue.next, + struct timblogiw_frame, frame); + list_del(lw->outqueue.next); + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); + + f->state = F_UNUSED; + memcpy(b, &f->buf, sizeof(*b)); + + if (f->vma_use_count) + b->flags |= V4L2_BUF_FLAG_MAPPED; + + return 0; +} + +static int timblogiw_enumstd(struct timblogiw *lw, struct v4l2_standard *std) +{ + if (std->index != 0) + return -EINVAL; + + memset(std, 0, sizeof(*std)); + std->index = 0; + + std->id = V4L2_STD_PAL; + strncpy(std->name, "PAL", sizeof(std->name)-1); + + return 0; +} + +static int timblogiw_g_std(struct timblogiw *lw, v4l2_std_id *std) +{ + *std = V4L2_STD_PAL; + return 0; +} + +static int timblogiw_s_std(struct timblogiw *lw, v4l2_std_id *std) +{ + if (!(*std & V4L2_STD_PAL)) + return -EINVAL; + return 0; +} + +static int timblogiw_enuminput(struct timblogiw *lw, struct v4l2_input *inp) +{ + if (inp->index != 0) + return -EINVAL; + + memset(inp, 0, sizeof(*inp)); + inp->index = 0; + + strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1); + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_ALL; + + return 0; +} + +static int timblogiw_g_input(struct timblogiw *lw, int *input) +{ + *input = 0; + + return 0; +} + +static int timblogiw_s_input(struct timblogiw *lw, int *input) +{ + if (*input != 0) + return -EINVAL; + return 0; +} + +static int timblogiw_streamon(struct timblogiw *lw, int *type) +{ + struct timblogiw_frame *f; + + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dbg("%s - No capture device\n", __func__); + return -EINVAL; + } + + if (list_empty(&lw->inqueue)) { + dbg("%s - inqueue is empty\n", __func__); + return -EINVAL; + } + + if (lw->stream == STREAM_ON) + return 0; + + lw->stream = STREAM_ON; + + f = list_entry(lw->inqueue.next, + struct timblogiw_frame, frame); + + dbg("%s - f size: %d, bpr: %d, dma addr: %x\n", __func__, + lw->frame_size, lw->bytesperline, + (unsigned int)lw->dma.transfer[lw->dma.curr].handle); + timb_start_dma(DMA_IRQ_VIDEO_RX, + lw->dma.transfer[lw->dma.curr].handle, + lw->frame_size, lw->bytesperline); + + return 0; +} + +static int timblogiw_streamoff(struct timblogiw *lw, int *type) +{ + if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (lw->stream == STREAM_ON) { + unsigned long lock_flags; + spin_lock_irqsave(&lw->queue_lock, lock_flags); + timb_stop_dma(DMA_IRQ_VIDEO_RX); + lw->stream = STREAM_OFF; + spin_unlock_irqrestore(&lw->queue_lock, lock_flags); + } + timblogiw_empty_framequeues(lw); + + return 0; +} + +static int timblogiw_querystd(struct timblogiw *lw, v4l2_std_id *std) +{ + /* TODO: Ask encoder */ + *std = V4L2_STD_PAL; + return 0; +} + +static int timblogiw_enum_framsizes(struct timblogiw *lw, + struct v4l2_frmsizeenum *fsize) +{ + if ((fsize->index != 0) || + (fsize->pixel_format != V4L2_PIX_FMT_YUYV)) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = lw->width; + fsize->discrete.height = lw->height; + + return 0; +} + +static int timblogiw_g_parm(struct timblogiw *lw, struct v4l2_streamparm *sp) +{ + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + sp->parm.capture.extendedmode = 0; + sp->parm.capture.readbuffers = lw->num_frames; + return 0; +} + +/******************************* + * Device Operations functions * + *******************************/ + +static int timblogiw_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw *lw = video_get_drvdata(vdev); + + dbg("%s -\n", __func__); + + mutex_init(&lw->fileop_lock); + spin_lock_init(&lw->queue_lock); + init_waitqueue_head(&lw->wait_frame); + + mutex_lock(&lw->lock); + + lw->width = 720; /* TODO: Should depend on tv norm */ + lw->height = 576; + lw->frame_size = lw->width * lw->height * 2; + lw->bytesperline = lw->width * 2; + + file->private_data = lw; + lw->stream = STREAM_OFF; + lw->num_frames = TIMBLOGIW_NUM_FRAMES; + + timblogiw_empty_framequeues(lw); + + timb_set_dma_interruptcb(DMA_IRQ_VIDEO_RX | DMA_IRQ_VIDEO_DROP, + timblogiw_isr, (void *)lw); + + mutex_unlock(&lw->lock); + + return 0; +} + +static int timblogiw_close(struct file *file) +{ + struct timblogiw *lw = file->private_data; + + dbg("%s - entry\n", __func__); + + mutex_lock(&lw->lock); + + timb_stop_dma(DMA_IRQ_VIDEO_RX); + timb_set_dma_interruptcb(DMA_IRQ_VIDEO_RX | DMA_IRQ_VIDEO_DROP, NULL, + NULL); + timblogiw_release_buffers(lw); + + mutex_unlock(&lw->lock); + return 0; +} + +static ssize_t timblogiw_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + dbg("%s - read request\n", __func__); + return -EINVAL; +} + +static void timblogiw_vm_open(struct vm_area_struct *vma) +{ + struct timblogiw_frame *f = vma->vm_private_data; + f->vma_use_count++; +} + +static void timblogiw_vm_close(struct vm_area_struct *vma) +{ + struct timblogiw_frame *f = vma->vm_private_data; + f->vma_use_count--; +} + +static struct vm_operations_struct timblogiw_vm_ops = { + .open = timblogiw_vm_open, + .close = timblogiw_vm_close, +}; + +static int timblogiw_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned long size = vma->vm_end - vma->vm_start, start = vma->vm_start; + void *pos; + u32 i; + int ret = -EINVAL; + + struct timblogiw *lw = filp->private_data; + dbg("%s\n", __func__); + + if (mutex_lock_interruptible(&lw->fileop_lock)) + return -ERESTARTSYS; + + if (!(vma->vm_flags & VM_WRITE) || + size != PAGE_ALIGN(lw->frame[0].buf.length)) + goto error_unlock; + + for (i = 0; i < lw->num_frames; i++) + if ((lw->frame[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) + break; + + if (i == lw->num_frames) { + dbg("%s - user supplied mapping address is out of range\n", + __func__); + goto error_unlock; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= VM_RESERVED; /* Do not swap out this VMA */ + + pos = lw->frame[i].bufmem; + while (size > 0) { /* size is page-aligned */ + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { + dbg("%s - vm_insert_page failed\n", __func__); + ret = -EAGAIN; + goto error_unlock; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + size -= PAGE_SIZE; + } + + vma->vm_ops = &timblogiw_vm_ops; + vma->vm_private_data = &lw->frame[i]; + timblogiw_vm_open(vma); + ret = 0; + +error_unlock: + mutex_unlock(&lw->fileop_lock); + return ret; +} + +static long +timblogiw_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct timblogiw *lw = file->private_data; + + switch (cmd) { + + case VIDIOC_QUERYCAP: + { + dbg("%s - VIDIOC_QUERYCAP\n", __func__); + return timblogiw_querycap(lw, (struct v4l2_capability *)arg); + } + + case VIDIOC_ENUM_FMT: + { + dbg("%s - VIDIOC_ENUM_FMT\n", __func__); + return timblogiw_enum_fmt(lw, (struct v4l2_fmtdesc *)arg); + } + + case VIDIOC_G_FMT: + { + dbg("%s - VIDIOC_G_FMT\n", __func__); + return timblogiw_g_fmt(lw, (struct v4l2_format *) arg); + } + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: + { + dbg("%s - VIDIOC_S_FMT\n", __func__); + return timblogiw_s_fmt(lw, (struct v4l2_format *)arg); + } + + case VIDIOC_REQBUFS: + { + dbg("%s - VIDIOC_REQBUFS\n", __func__); + return timblogiw_reqbufs(lw, (struct v4l2_requestbuffers *)arg); + } + + case VIDIOC_QUERYBUF: + { + dbg("%s - VIDIOC_QUERYBUF\n", __func__); + return timblogiw_querybuf(lw, (struct v4l2_buffer *)arg); + } + + case VIDIOC_QBUF: + { + return timblogiw_qbuf(lw, (struct v4l2_buffer *)arg); + } + + case VIDIOC_DQBUF: + { + return timblogiw_dqbuf(lw, file, (struct v4l2_buffer *)arg); + } + + case VIDIOC_ENUMSTD: + { + dbg("%s - VIDIOC_ENUMSTD\n", __func__); + return timblogiw_enumstd(lw, (struct v4l2_standard *)arg); + } + + case VIDIOC_G_STD: + { + dbg("%s - VIDIOC_G_STD\n", __func__); + return timblogiw_g_std(lw, (v4l2_std_id *)arg); + } + + case VIDIOC_S_STD: + { + dbg("%s - VIDIOC_S_STD\n", __func__); + return timblogiw_s_std(lw, (v4l2_std_id *)arg); + } + + case VIDIOC_ENUMINPUT: + { + dbg("%s - VIDIOC_ENUMINPUT\n", __func__); + return timblogiw_enuminput(lw, (struct v4l2_input *)arg); + } + + case VIDIOC_G_INPUT: + { + dbg("%s - VIDIOC_G_INPUT\n", __func__); + return timblogiw_g_input(lw, (int *)arg); + } + + case VIDIOC_S_INPUT: + { + dbg("%s - VIDIOC_S_INPUT\n", __func__); + return timblogiw_s_input(lw, (int *)arg); + } + + case VIDIOC_STREAMON: + { + dbg("%s - VIDIOC_STREAMON\n", __func__); + return timblogiw_streamon(lw, (int *)arg); + } + + case VIDIOC_STREAMOFF: + { + dbg("%s - VIDIOC_STREAMOFF\n", __func__); + return timblogiw_streamoff(lw, (int *)arg); + } + + case VIDIOC_QUERYSTD: + { + dbg("%s - VIDIOC_QUERYSTD\n", __func__); + return timblogiw_querystd(lw, (v4l2_std_id *)arg); + } + + case VIDIOC_ENUM_FRAMESIZES: + { + dbg("%s - VIDIOC_ENUM_FRAMESIZES\n", __func__); + return timblogiw_enum_framsizes(lw, + (struct v4l2_frmsizeenum *)arg); + } + + case VIDIOC_G_PARM: + { + dbg("%s - VIDIOC_G_PARM\n", __func__); + return timblogiw_g_parm(lw, (struct v4l2_streamparm *)arg); + } + + default: + { + dbg("%s Unknown command, dir: %x, type: %x, nr: %x, size: %x\n", + __func__, + _IOC_DIR(cmd), + _IOC_TYPE(cmd), + _IOC_NR(cmd), + _IOC_SIZE(cmd)); + break; + } + } + + return -EINVAL; +} + +void timblogiw_vdev_release(struct video_device *vdev) +{ + kfree(vdev); +} + +static const struct v4l2_file_operations timblogiw_fops = { + .owner = THIS_MODULE, + .open = timblogiw_open, + .release = timblogiw_close, + .ioctl = timblogiw_ioctl, + .mmap = timblogiw_mmap, + .read = timblogiw_read, +}; + +static const struct video_device timblogiw_template = { + .name = TIMBLOGIWIN_NAME, + .fops = &timblogiw_fops, + .release = &timblogiw_vdev_release, + .minor = -1 +}; + +static int timblogiw_probe(struct platform_device *dev) +{ + int err; + struct timblogiw *lw; + struct resource *iomem; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) { + err = -EINVAL; + goto err_mem; + } + + lw = kzalloc(sizeof(*lw), GFP_KERNEL); + if (!lw) { + err = -EINVAL; + goto err_mem; + } + + /* find the PCI device from the parent... */ + if (!dev->dev.parent) { + printk(KERN_ERR "timblogwi: No parent device found??\n"); + err = -ENODEV; + goto err_mem; + } + + lw->dev = container_of(dev->dev.parent, struct pci_dev, dev); + + mutex_init(&lw->lock); + + lw->video_dev = video_device_alloc(); + if (!lw->video_dev) { + err = -ENOMEM; + goto err_video_req; + } + *lw->video_dev = timblogiw_template; + + err = video_register_device(lw->video_dev, VFL_TYPE_GRABBER, 0); + if (err) { + video_device_release(lw->video_dev); + printk(KERN_ALERT "Error reg video\n"); + goto err_video_req; + } + + tasklet_init(&lw->tasklet, timblogiw_handleframe, (unsigned long)lw); + + if (!request_mem_region(iomem->start, resource_size(iomem), + "timb-video")) { + err = -EBUSY; + goto err_request; + } + + lw->membase = ioremap(iomem->start, resource_size(iomem)); + if (!lw->membase) { + err = -ENOMEM; + goto err_ioremap; + } + + platform_set_drvdata(dev, lw); + video_set_drvdata(lw->video_dev, lw); + + return 0; + +err_ioremap: + release_mem_region(iomem->start, resource_size(iomem)); +err_request: + if (-1 != lw->video_dev->minor) + video_unregister_device(lw->video_dev); + else + video_device_release(lw->video_dev); +err_video_req: + kfree(lw); +err_mem: + printk(KERN_ERR + "timberdale: Failed to register Timberdale Video In: %d\n", + err); + + return err; +} + +static int timblogiw_remove(struct platform_device *dev) +{ + struct timblogiw *lw = platform_get_drvdata(dev); + struct resource *iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + + if (-1 != lw->video_dev->minor) + video_unregister_device(lw->video_dev); + else + video_device_release(lw->video_dev); + + tasklet_kill(&lw->tasklet); + iounmap(lw->membase); + release_mem_region(iomem->start, resource_size(iomem)); + kfree(lw); + + return 0; +} + +static struct platform_driver timblogiw_platform_driver = { + .driver = { + .name = "timb-video", + .owner = THIS_MODULE, + }, + .probe = timblogiw_probe, + .remove = timblogiw_remove, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timblogiw_init(void) +{ + return platform_driver_register(&timblogiw_platform_driver); +} + +static void __exit timblogiw_exit(void) +{ + platform_driver_unregister(&timblogiw_platform_driver); +} + +module_init(timblogiw_init); +module_exit(timblogiw_exit); + +MODULE_DESCRIPTION("Timberdale Video In driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:timb-video"); + diff -uNr linux-2.6.29-clean/drivers/media/video/timblogiw.h linux-2.6.29/drivers/media/video/timblogiw.h --- linux-2.6.29-clean/drivers/media/video/timblogiw.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/media/video/timblogiw.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,95 @@ +/* + * timblogiw.h timberdale FPGA LogiWin Video In driver defines + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA LogiWin Video In + */ + +#ifndef _TIMBLOGIW_H +#define _TIMBLOGIW_H + +#include + +#define TIMBLOGIWIN_NAME "Timberdale Video-In" + +#define TIMBLOGIW_NUM_FRAMES 10 + + +enum timblogiw_stream_state { + STREAM_OFF, + STREAM_ON, +}; + +enum timblogiw_frame_state { + F_UNUSED = 0, + F_QUEUED, + F_GRABBING, + F_DONE, + F_ERROR, +}; + +struct timblogiw_frame { + void *bufmem; + struct v4l2_buffer buf; + enum timblogiw_frame_state state; + struct list_head frame; + unsigned long vma_use_count; +}; + +struct timblogiw_tvnorm { + int v4l2_id; + char *name; + u16 swidth; + u16 sheight; +}; + + +struct timbdma_transfer { + dma_addr_t handle; + void *buf; +}; + +struct timbdma_control { + struct timbdma_transfer transfer[2]; + struct timbdma_transfer *filled; + int curr; +}; + +struct timblogiw { + struct i2c_client *decoder; + struct timblogiw_frame frame[TIMBLOGIW_NUM_FRAMES]; + int num_frames; + unsigned int frame_count; + struct list_head inqueue, outqueue; + spinlock_t queue_lock; /* mutual exclusion */ + enum timblogiw_stream_state stream; + struct video_device *video_dev; + struct mutex lock, fileop_lock; + wait_queue_head_t wait_frame; + int width; + int height; + u32 frame_size; + int bytesperline; + struct pci_dev *dev; + struct timbdma_control dma; + void __iomem *membase; + struct tasklet_struct tasklet; +}; + +#endif /* _TIMBLOGIW_H */ + diff -uNr linux-2.6.29-clean/drivers/mfd/Kconfig linux-2.6.29/drivers/mfd/Kconfig --- linux-2.6.29-clean/drivers/mfd/Kconfig 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/mfd/Kconfig 2009-04-06 13:51:47.000000000 -0700 @@ -240,6 +240,27 @@ Say yes here if you want to include support GPIO for pins on the PCF50633 chip. +config MFD_TIMBERDALE + bool "Support for Timberdale" + select MFD_CORE + ---help--- + This is the core driver for the timberdale FPGA. This device is a + multifunctioanl device which may provide numerous interfaces. + +config MFD_TIMBERDALE_DMA + tristate "Support for timberdale DMA" + depends on MFD_TIMBERDALE + ---help--- + Add support the DMA block inside the timberdale FPGA. This to be able + to do DMA transfers directly to some of the blocks inside the FPGA + +config MFD_TIMBERDALE_I2S + tristate "Support for timberdale I2S bus" + depends on MFD_TIMBERDALE + ---help--- + Add support for the I2S bus handled by timberdale FPGA. + I2S RX and TX instances are then available for other devices to make use of. + endmenu menu "Multimedia Capabilities Port drivers" diff -uNr linux-2.6.29-clean/drivers/mfd/Makefile linux-2.6.29/drivers/mfd/Makefile --- linux-2.6.29-clean/drivers/mfd/Makefile 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/mfd/Makefile 2009-04-06 13:51:47.000000000 -0700 @@ -40,4 +40,8 @@ obj-$(CONFIG_MFD_PCF50633) += pcf50633-core.o obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o -obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o \ No newline at end of file +obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o + +obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o +obj-$(CONFIG_MFD_TIMBERDALE_DMA) += timbdma.o +obj-$(CONFIG_MFD_TIMBERDALE_I2S) += timbi2s.o diff -uNr linux-2.6.29-clean/drivers/mfd/timbdma.c linux-2.6.29/drivers/mfd/timbdma.c --- linux-2.6.29-clean/drivers/mfd/timbdma.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/mfd/timbdma.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,301 @@ +/* + * timbdma.c timberdale FPGA DMA driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA DMA engine + */ + +#include +#include +#include +#include +#include + +#include + +static struct timbdma_dev *self_g; + +static irqreturn_t timbdma_handleinterrupt(int irq, void *devid) +{ + struct timbdma_dev *dev = (struct timbdma_dev *)devid; + int ipr; + int i; + + ipr = ioread32(dev->membase + timbdma_ctrlmap_TIMBPEND); + + /* ack */ + iowrite32(ipr, dev->membase + timbdma_ctrlmap_TIMBSTATUS); + + /* call the callbacks */ + for (i = 0; i < DMA_IRQS; i++) { + int mask = 1 << i; + if ((ipr & mask) && dev->callbacks[i]) + dev->callbacks[i](mask, dev->callback_data[i]); + } + + if (ipr) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + + +void timb_start_dma(u32 flag, unsigned long buf, int len, int bytes_per_row) +{ + int i; + unsigned long irqflags; + struct timbdma_dev *dev = self_g; + + spin_lock_irqsave(&dev->lock, irqflags); + + /* now enable the DMA transfer */ + for (i = 0; i < DMA_IRQS; i++) + if (flag & (1 << i)) { + u32 offset = i / 2 * 0x40; + + if (!(i % 2)) { + /* RX */ + /* bytes per row */ + iowrite32(bytes_per_row, dev->membase + offset + + timbdma_dmacfg_BPERROW); + /* address high */ + iowrite32(0, dev->membase + offset + + timbdma_dmacfg_RXSTARTH); + /* address low */ + iowrite32(buf, dev->membase + offset + + timbdma_dmacfg_RXSTARTL); + /* Length */ + iowrite32(len, dev->membase + offset + + timbdma_dmacfg_RXLENGTH); + /* Clear rx sw read pointer */ + iowrite32(0, dev->membase + offset + + timbdma_dmacfg_RXSWRP); + /* enable the transfer */ + iowrite32(1, dev->membase + offset + + timbdma_dmacfg_RXENABLE); + } else { + /* TX */ + /* address high */ + iowrite32(0, dev->membase + offset + + timbdma_dmacfg_TXSTARTH); + /* address low */ + iowrite32(buf, dev->membase + offset + + timbdma_dmacfg_TXSTARTL); + /* Length */ + iowrite32(len, dev->membase + offset + + timbdma_dmacfg_TXLENGTH); + /* Set tx sw write pointer */ + iowrite32(len, dev->membase + offset + + timbdma_dmacfg_TXSWWP); + } + + /* only allow one bit in the flag field */ + break; + } + spin_unlock_irqrestore(&dev->lock, irqflags); +} +EXPORT_SYMBOL(timb_start_dma); + +void *timb_stop_dma(u32 flags) +{ + int i; + unsigned long irqflags; + struct timbdma_dev *dev = self_g; + void *result = 0; + + spin_lock_irqsave(&dev->lock, irqflags); + + /* now disable the DMA transfers */ + for (i = 0; i < DMA_IRQS; i++) + if (flags & (1 << i)) { + /* + RX enable registers are located at: + 0x14 + 0x54 + 0x94 + + TX SW pointer registers are located at: + 0x24 + 0x64 + */ + u32 offset = i / 2 * 0x40; + u32 result_offset = offset; + if (!(i % 2)) { + /* even -> RX enable */ + offset += timbdma_dmacfg_RXENABLE; + result_offset += timbdma_dmacfg_RXFPGAWP; + } else { + /* odd -> TX SW pointer reg */ + offset += timbdma_dmacfg_TXSWWP; + result_offset = timbdma_dmacfg_TXFPGARP; + } + + iowrite32(0, dev->membase + offset); + /* check how far the FPGA has written/read */ + result = (void *)ioread32(dev->membase + result_offset); + } + + /* ack any pending IRQs */ + iowrite32(flags, dev->membase + timbdma_ctrlmap_TIMBSTATUS); + + spin_unlock_irqrestore(&dev->lock, irqflags); + + return result; +} +EXPORT_SYMBOL(timb_stop_dma); + +void timb_set_dma_interruptcb(u32 flags, timbdma_interruptcb icb, void *data) +{ + int i; + unsigned long irqflags; + struct timbdma_dev *dev = self_g; + u32 ier; + + spin_lock_irqsave(&dev->lock, irqflags); + + for (i = 0; i < DMA_IRQS; i++) + if (flags & (1 << i)) { + dev->callbacks[i] = icb; + dev->callback_data[i] = data; + } + + /* Ack any pending IRQ */ + iowrite32(flags, dev->membase + timbdma_ctrlmap_TIMBSTATUS); + + /* if a null callback is given -> clear interrupt, else -> enable */ + ier = ioread32(dev->membase + timbdma_ctrlmap_TIMBENABLE); + if (icb != NULL) + ier |= flags; + else + ier &= ~flags; + iowrite32(ier, dev->membase + timbdma_ctrlmap_TIMBENABLE); + + spin_unlock_irqrestore(&dev->lock, irqflags); +} +EXPORT_SYMBOL(timb_set_dma_interruptcb); + +static int timbdma_probe(struct platform_device *dev) +{ + int err, irq; + struct timbdma_dev *self; + struct resource *iomem; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) { + err = -EINVAL; + goto err_mem; + } + + self = kzalloc(sizeof(*self), GFP_KERNEL); + if (!self) { + err = -EINVAL; + goto err_mem; + } + + spin_lock_init(&self->lock); + + if (!request_mem_region(iomem->start, + resource_size(iomem), "timb-dma")) { + err = -EBUSY; + goto err_request; + } + + self->membase = ioremap(iomem->start, resource_size(iomem)); + if (!self->membase) { + printk(KERN_ERR "timbdma: Failed to remap I/O memory\n"); + err = -ENOMEM; + goto err_ioremap; + } + + /* register interrupt */ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + err = irq; + goto err_get_irq; + } + + /* request IRQ */ + err = request_irq(irq, timbdma_handleinterrupt, IRQF_SHARED, + "timb-dma", self); + if (err) { + printk(KERN_ERR "timbdma: Failed to request IRQ\n"); + goto err_get_irq; + } + + platform_set_drvdata(dev, self); + + /* assign the global pointer */ + self_g = self; + + return 0; + +err_get_irq: + iounmap(self->membase); +err_ioremap: + release_mem_region(iomem->start, resource_size(iomem)); +err_request: + kfree(self); +err_mem: + printk(KERN_ERR "timberdale: Failed to register Timberdale DMA: %d\n", + err); + + return err; +} + +static int timbdma_remove(struct platform_device *dev) +{ + struct timbdma_dev *self = platform_get_drvdata(dev); + struct resource *iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + + free_irq(platform_get_irq(dev, 0), self); + iounmap(self->membase); + release_mem_region(iomem->start, resource_size(iomem)); + kfree(self); + self_g = NULL; + return 0; +} + +static struct platform_driver timbdma_platform_driver = { + .driver = { + .name = "timb-dma", + .owner = THIS_MODULE, + }, + .probe = timbdma_probe, + .remove = timbdma_remove, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timbdma_init(void) +{ + self_g = NULL; + return platform_driver_register(&timbdma_platform_driver); +} + +static void __exit timbdma_exit(void) +{ + platform_driver_unregister(&timbdma_platform_driver); +} + +module_init(timbdma_init); +module_exit(timbdma_exit); + +MODULE_DESCRIPTION("Timberdale DMA driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:timb-dma"); + diff -uNr linux-2.6.29-clean/drivers/mfd/timberdale.c linux-2.6.29/drivers/mfd/timberdale.c --- linux-2.6.29-clean/drivers/mfd/timberdale.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/mfd/timberdale.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,599 @@ +/* + * timberdale.c timberdale FPGA mfd shim driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "timberdale.h" + +struct timberdale_device { + resource_size_t intc_mapbase; + resource_size_t ctl_mapbase; + unsigned char __iomem *intc_membase; + unsigned char __iomem *ctl_membase; + int irq_base; + u32 irq_ack_mask; + /* locking from interrupts while modifiying registers */ + spinlock_t lock; +}; + +/*--------------------------------------------------------------------------*/ + +struct tsc2007_platform_data timberdale_tsc2007_platform_data = { + .model = 2003, + .x_plate_ohms = 100 +}; + +struct i2c_board_info timberdale_i2c_board_info[] = { + { + I2C_BOARD_INFO("tsc2003", 0x48), + .platform_data = &timberdale_tsc2007_platform_data, + .irq = IRQ_TIMBERDALE_TSC_INT + }, + { + I2C_BOARD_INFO("adv7180", 0x42 >> 1), + .irq = IRQ_TIMBERDALE_ADV7180 + } +}; + +static __devinitdata struct ocores_i2c_platform_data +timberdale_i2c_platform_data = { + .regstep = 4, + .clock_khz = 62500, + .devices = timberdale_i2c_board_info, + .num_devices = ARRAY_SIZE(timberdale_i2c_board_info) +}; + +const static __devinitconst struct resource timberdale_i2c_resources[] = { + { + .start = I2COFFSET, + .end = I2CEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_I2C, + .end = IRQ_TIMBERDALE_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +static __devinitdata struct xspi_platform_data timberdale_xspi_platorm_data = { + .bus_num = -1, + /* according to spec. we can have up to 32 slaves however, + * as of current(2009-03-06) revision of + * Timberdale we can only handle 3 right now + */ + .num_chipselect = 3, + .speed_hz = 1953125, /* hardcoded value in IP, for now */ + .cr_offset = 0x60, + .sr_offset = 0x64, + .txd_offset = 0x68, + .rxd_offset = 0x6c, + .ssr_offset = 0x70 +}; + +const static __devinitconst struct resource timberdale_spi_resources[] = { + { + .start = SPIOFFSET, + .end = SPIEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_SPI, + .end = IRQ_TIMBERDALE_SPI, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_eth_resources[] = { + { + .start = ETHOFFSET, + .end = ETHEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_ETHSW_IF, + .end = IRQ_TIMBERDALE_ETHSW_IF, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_gpio_resources[] = { + { + .start = GPIOOFFSET, + .end = GPIOEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_GPIO, + .end = IRQ_TIMBERDALE_GPIO, + .flags = IORESOURCE_IRQ, + }, +}; + + +const static __devinitconst struct resource timberdale_most_resources[] = { + { + .start = MOSTOFFSET, + .end = MOSTEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_MLB, + .end = IRQ_TIMBERDALE_MLB, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_uart_resources[] = { + { + .start = UARTOFFSET, + .end = UARTEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_UART, + .end = IRQ_TIMBERDALE_UART, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_i2s_resources[] = { + { + .start = I2SOFFSET, + .end = I2SEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_I2S, + .end = IRQ_TIMBERDALE_I2S, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_video_resources[] = { + { + .start = LOGIWOFFSET, + .end = LOGIWEND, + .flags = IORESOURCE_MEM, + }, + /* + note that the "frame buffer" is located in DMA area + starting at 0x1200000 + */ +}; + +const static __devinitconst struct resource timberdale_dma_resources[] = { + { + .start = DMAOFFSET, + .end = DMAEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_DMA, + .end = IRQ_TIMBERDALE_DMA, + .flags = IORESOURCE_IRQ, + }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar0[] = { + { + .name = "timb-uart", + .num_resources = ARRAY_SIZE(timberdale_uart_resources), + .resources = timberdale_uart_resources, + }, + { + .name = "ocores-i2c", + .num_resources = ARRAY_SIZE(timberdale_i2c_resources), + .resources = timberdale_i2c_resources, + .platform_data = &timberdale_i2c_platform_data, + .data_size = sizeof(timberdale_i2c_platform_data), + }, + { + .name = "timb-gpio", + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), + .resources = timberdale_gpio_resources, + }, + { + .name = "timb-i2s", + .num_resources = ARRAY_SIZE(timberdale_i2s_resources), + .resources = timberdale_i2s_resources, + }, + { + .name = "timb-most", + .num_resources = ARRAY_SIZE(timberdale_most_resources), + .resources = timberdale_most_resources, + }, + { + .name = "timb-video", + .num_resources = ARRAY_SIZE(timberdale_video_resources), + .resources = timberdale_video_resources, + }, + { + .name = "xilinx_spi", + .num_resources = ARRAY_SIZE(timberdale_spi_resources), + .resources = timberdale_spi_resources, + .platform_data = &timberdale_xspi_platorm_data, + .data_size = sizeof(timberdale_xspi_platorm_data), + }, + { + .name = "ks884x", + .num_resources = ARRAY_SIZE(timberdale_eth_resources), + .resources = timberdale_eth_resources, + }, + { + .name = "timb-dma", + .num_resources = ARRAY_SIZE(timberdale_dma_resources), + .resources = timberdale_dma_resources, + }, +}; + +static const __devinitconst struct resource timberdale_sdhc_resources_bar1[] = { + { + .start = SDHC0OFFSET, + .end = SDHC0END, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_SDHC, + .end = IRQ_TIMBERDALE_SDHC, + .flags = IORESOURCE_IRQ, + }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar1[] = { + { + .name = "sdhci", + .num_resources = ARRAY_SIZE(timberdale_sdhc_resources_bar1), + .resources = timberdale_sdhc_resources_bar1, + }, +}; + +/*--------------------------------------------------------------------------*/ + + +/* Handle the timberdale interrupt mux */ +static void timberdale_irq(unsigned int irq, struct irq_desc *desc) +{ + struct timberdale_device *priv = get_irq_data(irq); + unsigned int i, ipr; + + desc->chip->ack(irq); + + while ((ipr = ioread32(priv->intc_membase + IPR))) { + priv->irq_ack_mask = 0; + for (i = 0; i < TIMBERDALE_NR_IRQS; i++) + if (ipr & (1 << i)) + generic_handle_irq(priv->irq_base + i); + if (priv->irq_ack_mask) + iowrite32(priv->irq_ack_mask, priv->intc_membase + IAR); + } +} + +static void timberdale_irq_mask(unsigned int irq) +{ + struct timberdale_device *priv = get_irq_chip_data(irq); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + iowrite32(1 << (irq - priv->irq_base), priv->intc_membase + CIE); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void timberdale_irq_unmask(unsigned int irq) +{ + struct timberdale_device *priv = get_irq_chip_data(irq); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + iowrite32(1 << (irq - priv->irq_base), priv->intc_membase + SIE); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void timberdale_irq_ack(unsigned int irq) +{ + struct timberdale_device *priv = get_irq_chip_data(irq); + unsigned long flags; + u32 ack_mask = 1 << (irq - priv->irq_base); + + spin_lock_irqsave(&priv->lock, flags); + /* if edge triggered, ack directly. Otherwhise ack in the end of + * irq handler + */ + if (ack_mask & IRQ_TIMBERDALE_EDGE_MASK) + iowrite32(ack_mask, priv->intc_membase + IAR); + else + priv->irq_ack_mask |= ack_mask; + spin_unlock_irqrestore(&priv->lock, flags); +} + +static struct irq_chip timberdale_chip = { + .name = "timberdale", + .ack = timberdale_irq_ack, + .mask = timberdale_irq_mask, + .unmask = timberdale_irq_unmask, + .disable = timberdale_irq_mask, + .enable = timberdale_irq_unmask, +}; + +/*--------------------------------------------------------------------------*/ + +/* Install the IRQ handler */ +static void timberdale_attach_irq(struct pci_dev *dev) +{ + struct timberdale_device *priv = pci_get_drvdata(dev); + unsigned int irq, irq_base; + + irq_base = priv->irq_base; + for (irq = irq_base; irq < irq_base + TIMBERDALE_NR_IRQS; irq++) { + set_irq_chip_and_handler_name(irq, &timberdale_chip, + handle_edge_irq, "mux"); + + set_irq_chip_data(irq, priv); + +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); +#endif + } + + set_irq_data(dev->irq, priv); + set_irq_chained_handler(dev->irq, timberdale_irq); +} + +static void timberdale_detach_irq(struct pci_dev *dev) +{ + struct timberdale_device *priv = pci_get_drvdata(dev); + unsigned int irq, irq_base; + + irq_base = priv->irq_base; + + set_irq_chained_handler(dev->irq, NULL); + set_irq_data(dev->irq, NULL); + + for (irq = irq_base; irq < irq_base + TIMBERDALE_NR_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + set_irq_chip(irq, NULL); + set_irq_chip_data(irq, NULL); + } +} + +static int __devinit timb_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct timberdale_device *priv; + int err, i; + u16 ver; + resource_size_t mapbase; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->lock); + pci_set_drvdata(dev, priv); + + err = pci_enable_device(dev); + if (err) + goto err_enable; + + mapbase = pci_resource_start(dev, 0); + if (!mapbase) { + printk(KERN_ERR "timberdale: No resource\n"); + goto err_start; + } + + /* create a resource for the Interrupt controller registers */ + priv->intc_mapbase = mapbase + INTCOFFSET; + if (!request_mem_region(priv->intc_mapbase, INTCSIZE, "timb-intc")) { + printk(KERN_ERR "timberdale: Failed to request intc mem\n"); + goto err_request; + } + + /* create a resource for the PCI master register */ + priv->ctl_mapbase = mapbase + CHIPCTLOFFSET; + if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-intc")) { + printk(KERN_ERR "timberdale: Failed to request ctl mem\n"); + goto err_request_ctl; + } + + priv->intc_membase = ioremap(priv->intc_mapbase, INTCSIZE); + if (!priv->intc_membase) { + printk(KERN_ALERT "timberdale: Map error, intc\n"); + goto err_ioremap; + } + + priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE); + if (!priv->ctl_membase) { + printk(KERN_ALERT "timberdale: Map error, ctl\n"); + goto err_ioremap_ctl; + } + + err = pci_enable_msi(dev); + if (err) { + printk(KERN_WARNING "timberdale: MSI init failed: %d\n", err); + goto err_msi; + } + + /* Reset all FPGA PLB peripherals */ + iowrite32(0x1, priv->ctl_membase + MAYSVILLERST); + + /* at this stage the FPGA does not generate a + * unique interrupt per function, to emulate real interrupts + * we assign them a faked interrupt which we issue in the + * interrupt handler. For now just hard code a base number + */ + priv->irq_base = NR_IRQS - TIMBERDALE_NR_IRQS - 1; + if (priv->irq_base < dev->irq) + /* ops the device itself got the IRQ in the end... */ + priv->irq_base = 400; + + timberdale_attach_irq(dev); + + /* update IRQ offsets in I2C board info */ + for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++) + timberdale_i2c_board_info[i].irq += priv->irq_base; + + /* don't leave platform_data empty on any device */ + for (i = 0; i < ARRAY_SIZE(timberdale_cells_bar0); i++) + if (timberdale_cells_bar0[i].platform_data == NULL) { + timberdale_cells_bar0[i].platform_data = + timberdale_cells_bar0 + i; + timberdale_cells_bar0[i].data_size = + sizeof(timberdale_cells_bar0[i]); + } + + err = mfd_add_devices(&dev->dev, -1, + timberdale_cells_bar0, ARRAY_SIZE(timberdale_cells_bar0), + &dev->resource[0], priv->irq_base); + if (err) + printk(KERN_WARNING + "timberdale: mfd_add_devices failed: %d\n", err); + else { + err = mfd_add_devices(&dev->dev, -1, + timberdale_cells_bar1, + ARRAY_SIZE(timberdale_cells_bar1), + &dev->resource[1], priv->irq_base); + + if (err) + printk(KERN_WARNING + "timberdale: timb_add_sdhci failed: %d\n", err); + } + + if (err) + goto err_mfd; + + ver = ioread16(priv->ctl_membase + TIMB_REV); + + printk(KERN_INFO "Found Maysville Card. Rev: %d\n", ver); + + /* Enable interrupts and wire the hardware interrupts */ + iowrite32(0x3, priv->intc_membase + MER); + + return 0; +err_mfd: + timberdale_detach_irq(dev); + pci_disable_msi(dev); +err_msi: + iounmap(priv->ctl_membase); +err_ioremap_ctl: + iounmap(priv->intc_membase); +err_ioremap: + release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); +err_request_ctl: + release_mem_region(priv->intc_mapbase, INTCSIZE); +err_request: + pci_set_drvdata(dev, NULL); +err_start: + pci_disable_device(dev); +err_enable: + kfree(priv); + pci_set_drvdata(dev, NULL); + return -ENODEV; +} + +static void __devexit timb_remove(struct pci_dev *dev) +{ + /* clean up any allocated resources and stuff here. + * like call release_region(); + */ + struct timberdale_device *priv; + + priv = pci_get_drvdata(dev); + + mfd_remove_devices(&dev->dev); + + timberdale_detach_irq(dev); + + iowrite32(0xffffffff, priv->intc_membase + IAR); + iowrite32(0, priv->intc_membase + MER); + iowrite32(0, priv->intc_membase + IER); + + iounmap(priv->ctl_membase); + iounmap(priv->intc_membase); + release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); + release_mem_region(priv->intc_mapbase, INTCSIZE); + + pci_disable_msi(dev); + pci_disable_device(dev); + pci_set_drvdata(dev, NULL); + kfree(priv); +} + +static struct pci_device_id timberdale_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl); + +static struct pci_driver timberdale_pci_driver = { + .name = "timberdale", + .id_table = timberdale_pci_tbl, + .probe = timb_probe, + .remove = timb_remove, +}; + +static int __init timberdale_init(void) +{ + int err; + + err = pci_register_driver(&timberdale_pci_driver); + if (err < 0) { + printk(KERN_ERR + "Failed to register PCI driver for %s device.\n", + timberdale_pci_driver.name); + return -ENODEV; + } + + printk(KERN_INFO "Driver for %s has been successfully registered.\n", + timberdale_pci_driver.name); + + return 0; +} + +static void __exit timberdale_exit(void) +{ + pci_unregister_driver(&timberdale_pci_driver); + + printk(KERN_INFO "Driver for %s has been successfully unregistered.\n", + timberdale_pci_driver.name); +} + +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("Richard Rojfors"); + +module_init(timberdale_init); +module_exit(timberdale_exit); + diff -uNr linux-2.6.29-clean/drivers/mfd/timberdale.h linux-2.6.29/drivers/mfd/timberdale.h --- linux-2.6.29-clean/drivers/mfd/timberdale.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/mfd/timberdale.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,114 @@ +/* + * timberdale.h timberdale FPGA mfd shim driver defines + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA + */ + +#ifndef MFD_TIMBERDALE_H +#define MFD_TIMBERDALE_H + +/* Registers of the interrupt controller */ +#define ISR 0x00 +#define IPR 0x04 +#define IER 0x08 +#define IAR 0x0c +#define SIE 0x10 +#define CIE 0x14 +#define MER 0x1c + +/* Registers of the control area */ +#define TIMB_REV 0x00 +#define MAYSVILLERST 0x40 + + +#define I2COFFSET 0x0 +#define I2CEND 0x1f + +#define SPIOFFSET 0x80 +#define SPIEND 0xff + +#define ETHOFFSET 0x300 +#define ETHEND 0x30f + +#define GPIOOFFSET 0x400 +#define GPIOEND 0x7ff + +#define CHIPCTLOFFSET 0x800 +#define CHIPCTLEND 0x8ff +#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET) + +#define INTCOFFSET 0xc00 +#define INTCEND 0xfff +#define INTCSIZE (INTCEND - INTCOFFSET) + +#define MOSTOFFSET 0x1000 +#define MOSTEND 0x13ff + +#define UARTOFFSET 0x1400 +#define UARTEND 0x17ff + +#define I2SOFFSET 0x1C00 +#define I2SEND 0x1fff + +#define LOGIWOFFSET 0x30000 +#define LOGIWEND 0x37fff + +#define DMAOFFSET 0x01000000 +#define DMAEND 0x013fffff + +/* SDHC0 is placed in PCI bar 1 */ +#define SDHC0OFFSET 0x00 +#define SDHC0END 0xff + +/* SDHC1 is placed in PCI bar 2 */ +#define SDHC1OFFSET 0x00 +#define SDHC1END 0xff + +#define PCI_VENDOR_ID_TIMB 0x10ee +#define PCI_DEVICE_ID_TIMB 0xa123 +#define DRV_VERSION "0.1" + + +#define IRQ_TIMBERDALE_INIC 0 +#define IRQ_TIMBERDALE_MLB 1 +#define IRQ_TIMBERDALE_GPIO 2 +#define IRQ_TIMBERDALE_I2C 3 +#define IRQ_TIMBERDALE_UART 4 +#define IRQ_TIMBERDALE_DMA 5 +#define IRQ_TIMBERDALE_I2S 6 +#define IRQ_TIMBERDALE_TSC_INT 7 +#define IRQ_TIMBERDALE_SDHC 8 +#define IRQ_TIMBERDALE_ADV7180 9 +#define IRQ_TIMBERDALE_ETHSW_IF 10 +#define IRQ_TIMBERDALE_SPI 11 + +#define TIMBERDALE_NR_IRQS 12 + +/* Some of the interrupts are level triggered, some are edge triggered */ +#define IRQ_TIMBERDALE_EDGE_MASK ((1 << IRQ_TIMBERDALE_ADV7180) | \ + (1 << IRQ_TIMBERDALE_TSC_INT) | (1 << IRQ_TIMBERDALE_DMA) | \ + (1 << IRQ_TIMBERDALE_MLB) | (1 << IRQ_TIMBERDALE_INIC)) + +#define IRQ_TIMBERDALE_LEVEL_MASK ((1 << IRQ_TIMBERDALE_SPI) | \ + (1 << IRQ_TIMBERDALE_ETHSW_IF) | (1 << IRQ_TIMBERDALE_SDHC) | \ + (1 << IRQ_TIMBERDALE_I2S) | (1 << IRQ_TIMBERDALE_UART) | \ + (1 << IRQ_TIMBERDALE_I2C) | (1 << IRQ_TIMBERDALE_GPIO)) + +#endif + diff -uNr linux-2.6.29-clean/drivers/mfd/timbi2s.c linux-2.6.29/drivers/mfd/timbi2s.c --- linux-2.6.29-clean/drivers/mfd/timbi2s.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/mfd/timbi2s.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,597 @@ +/* + * timbi2s.c timberdale FPGA I2S driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA I2S + * + * As of 2009-03-23 I2S instances + * are not configured as masters + * + * TODO: implement switching between master and slave + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "timb-i2s" + +#define I2S_CLOCK_SPEED 62500000 /* 62,5MHz */ + +#define FIFO_FILL_SIZE 127 +#define I2S_BUFFER_SIZE PAGE_SIZE + +#define ALMOST_FULL 170 +#define ALMOST_EMPTY 85 + +/* As of 2009-03-16, IP can instanciate max. 4 RX and 4 TX */ +#define MAX_TX_NR 4 +#define MAX_RX_NR 4 +/* and actually up and running only 4. + * 1 TX and 3 RX + */ +#define IP_I2S_NR 4 +#define REGSTEP 0x04 + +#define VERSION 0x00 +#define I2S_UIR 0x04 /* Unit Interrupt Register */ + +/* Registers for all possible I2S IP instances + * are the same as for first one (from 0x08 to 0x20) + */ +#define I2S_PRESCALE 0x08 /* Holds prescale value, if clock master */ +#define I2S_ICR 0x0c /* Interrupt Clear Register */ +# define ICR_F 0x01 /* Full */ +# define ICR_AF 0x02 /* Almost full */ +# define ICR_AE 0x04 /* Almost empty */ +# define ICR_RX_D 0x08 /* Data present, RX only */ +# define ICR_TX_E 0x08 /* Epmty, TX only */ + +#define I2S_IPR 0x10 /* Interrupt Pending Register */ +#define I2S_ISR 0x14 /* Interrupt Status Register */ + +#define I2S_IER 0x18 /* Interrupt Enable Register */ +# define IER_FF 0x01 /* RX/TX FIFO Full */ +# define IER_FAF 0x02 /* RX/TX FIFO Almost Full */ +# define IER_FAE 0x04 /* RX/TX FIFO Almost Empty */ +# define IER_RX_DATA 0x08 /* RX. Data Present */ +# define IER_TX_FE 0x08 /* TX. FIFO Empty */ + +#define I2S_CTRL 0x1c /* Control Register */ +# define CTRL_TX_ENABLE 0x01 /* Enable TX */ +# define CTRL_RX_ENABLE 0x02 /* Enable RX */ +# define CTRL_NONE 0x04 /* Not used */ +# define CTRL_FIFO_CLR 0x08 /* FIFO Clear */ +# define CTRL_SWR 0x10 /* Soft reset */ +# define CTRL_CLKMASTER 0x1000 /* IP I2S instance is master */ +# define CTRL_IS_TX 0x40000000 /* IP I2S is an TX-instance */ +# define CTRL_IS_RX 0x20000000 /* IP I2S is an RX-instance */ + +#define I2S_FIFO 0x20 /* read/write FIFO */ + +#define INC_HEAD(buf, size) \ + (buf->head = (buf->head + 1) & (size-1)) + +#define INC_TAIL(buf, size) \ + (buf->tail = (buf->tail + 1) & (size-1)) + + +/* circular buffer */ +static struct circ_buf *timbi2s_buf_alloc(void); +static void timbi2s_buf_free(struct circ_buf *cb); +static void timbi2s_buf_clear(struct circ_buf *cb); + +static int timbi2s_fifo_read(struct circ_buf *cb, ssize_t count, long add); +static int timbi2s_fifo_write(struct circ_buf *cb, ssize_t count, long add); + +static int timbi2s_ioctrl(struct timbi2s_dev *); + +static struct timbi2s_bus *bus_p; + +static int timbi2s_is_tx(struct timbi2s_dev *i2sdev) +{ + return (ioread32(i2sdev->membase + i2sdev->ctrl_offset) + & CTRL_IS_TX) ? 1 : 0; +} + +static int timbi2s_is_rx(struct timbi2s_dev *i2sdev) +{ + return (ioread32(i2sdev->membase + i2sdev->ctrl_offset) + & CTRL_IS_RX) ? 1 : 0; +} + +/* Return unused TX-instance */ +static struct timbi2s_dev *timbi2s_get_tx(void) +{ + struct timbi2s_dev *tdev, *tmp; + + if (bus_p == NULL) + return NULL; + + list_for_each_entry_safe(tdev, tmp, &bus_p->control->list, item) { + if (!tdev->in_use && timbi2s_is_tx(tdev)) { + tdev->in_use = 1; + return tdev; + } + + } + return NULL; +} +EXPORT_SYMBOL_GPL(timbi2s_get_tx); + +/* Return unused RX-instance */ +static struct timbi2s_dev *timbi2s_get_rx(void) +{ + struct timbi2s_dev *tdev, *tmp; + + if (bus_p == NULL) + return NULL; + + list_for_each_entry_safe(tdev, tmp, &bus_p->control->list, item) { + if (!tdev->in_use && timbi2s_is_rx(tdev)) { + tdev->in_use = 1; + return tdev; + } + + } + return NULL; +} +EXPORT_SYMBOL_GPL(timbi2s_get_rx); + +/* Flag TX/RX as unused and reset it */ +static void timbi2s_put(struct timbi2s_dev *tdev) +{ + if (tdev->in_use) { + tdev->in_use = 0; + timbi2s_ioctrl(tdev); + } +} +EXPORT_SYMBOL_GPL(timbi2s_put); + +/* + * Write data to the FIFO + */ +static void timbi2s_tx_handler(struct timbi2s_dev *i2sdev) +{ + u32 pend; + + pend = ioread32(i2sdev->membase + i2sdev->ipr_offset); + + if (pend & IER_FAE) { + timbi2s_fifo_write(i2sdev->buffer, + ALMOST_FULL - ALMOST_EMPTY, + (unsigned long)i2sdev->membase + + i2sdev->fifo); + /* clear interrupt */ + iowrite32(ICR_AE, i2sdev->membase + i2sdev->icr_offset); + } +} + +/* + * Read data from the FIFO + */ +static void timbi2s_rx_handler(struct timbi2s_dev *i2sdev) +{ + u32 pend; + pend = ioread32(i2sdev->membase + i2sdev->ipr_offset); + + if (pend & IER_FAE) { + timbi2s_fifo_read(i2sdev->buffer, + ALMOST_EMPTY, + (unsigned long)i2sdev->membase + + i2sdev->fifo); + + /* clear interrupt */ + iowrite32(ICR_AE | ICR_AF, + i2sdev->membase + i2sdev->icr_offset); + } +} + +void timbi2s_int_handler(struct work_struct *workp) +{ + u32 pend, stat, i2stype; + unsigned long flags; + struct timbi2s_dev *i2sdev = container_of(workp, + struct timbi2s_dev, + work); + + pend = ioread32(i2sdev->membase + i2sdev->ipr_offset); + stat = ioread32(i2sdev->membase + i2sdev->isr_offset); + i2stype = ioread32(i2sdev->membase + i2sdev->ctrl_offset); + + spin_lock_irqsave(&i2sdev->lock, flags); + + if (i2stype & CTRL_IS_RX) { + /* Enable Almost Empty Almost Full interrupt */ + iowrite32(IER_FAE | IER_FAF, + i2sdev->membase + i2sdev->ier_offset); + /* Enable RX */ + iowrite32(CTRL_RX_ENABLE, + i2sdev->membase + i2sdev->ctrl_offset); + timbi2s_rx_handler(i2sdev); + } else if (i2stype & CTRL_IS_TX) { + /* Enable Almost Empty interrupt */ + iowrite32(IER_FAE, i2sdev->membase + i2sdev->ier_offset); + /* Enable TX */ + iowrite32(CTRL_TX_ENABLE, + i2sdev->membase + i2sdev->ctrl_offset); + timbi2s_tx_handler(i2sdev); + } + + spin_unlock_irqrestore(&i2sdev->lock, flags); +} + +static int timbi2s_ioctrl(struct timbi2s_dev *i2sdev) +{ + u32 i2stype; + + /* Reset */ + iowrite8(CTRL_SWR, i2sdev->membase + i2sdev->ctrl_offset); + /* Clear IER */ + iowrite32(0x00000000, i2sdev->membase + i2sdev->ier_offset); + /* Clear ICR */ + iowrite32(0xffffffff, i2sdev->membase + i2sdev->icr_offset); + + i2stype = ioread32(i2sdev->membase + i2sdev->ctrl_offset); + + if (i2stype & CTRL_IS_TX) + printk(KERN_INFO DRIVER_NAME": found active I2S Transmitter\n"); + else if (i2stype & CTRL_IS_RX) + printk(KERN_INFO DRIVER_NAME": found active I2S Receiver\n"); + + return 1; +} +EXPORT_SYMBOL_GPL(timbi2s_ioctrl); + +static struct circ_buf *timbi2s_buf_alloc(void) +{ + struct circ_buf *cb; + + cb = kzalloc(sizeof(*cb), GFP_KERNEL); + if (cb == NULL) + return NULL; + + cb->buf = kzalloc(I2S_BUFFER_SIZE, GFP_KERNEL); + if (cb->buf == NULL) { + kfree(cb); + return NULL; + } + + timbi2s_buf_clear(cb); + + return cb; +} + +static void timbi2s_buf_free(struct circ_buf *cb) +{ + kfree(cb->buf); + kfree(cb); +} + +static void timbi2s_buf_clear(struct circ_buf *cb) +{ + cb->head = 0; + cb->tail = cb->head; +} + +/* + * Read data from the FIFO and write it to the given circular buffer + */ +static int timbi2s_fifo_read(struct circ_buf *cb, ssize_t count, long add) +{ + int c, ret = 0; + + unsigned char *hi = (unsigned char *)ioread32((void *)(add >> 16)); + unsigned char *lo = (unsigned char *)ioread32((void *)(add & 0xFFFF)); + + c = CIRC_SPACE_TO_END(cb->head, cb->tail, I2S_BUFFER_SIZE); + if (count < c) + c = count; + + if (c <= 0) + return 1; + + while (c >= 0) { + memcpy(cb->buf + cb->head, hi, 2); + INC_HEAD(cb, I2S_BUFFER_SIZE); + + memcpy(cb->buf + cb->head, lo, 2); + INC_HEAD(cb, I2S_BUFFER_SIZE); + count -= 4; + } + return ret; +} + +/* + * Get data from the circular buffer and write it to the given FIFO address + */ +static int timbi2s_fifo_write(struct circ_buf *cb, ssize_t count, long add) +{ + int c, ret = 0; + + c = CIRC_CNT_TO_END(cb->head, cb->tail, I2S_BUFFER_SIZE); + if (count < c) + c = count; + + if (c <= 0) + return 1; + + while (c >= 0) { + iowrite32(*(s16 *)(cb->buf + cb->tail), (void *)(add >> 16)); + INC_TAIL(cb, I2S_BUFFER_SIZE); + + iowrite32(*(s16 *)(cb->buf + cb->tail), (void *)(add & 0xFFFF)); + INC_TAIL(cb, I2S_BUFFER_SIZE); + count -= 4; + } + + return ret; +} + +static void timbi2s_control_destroy(struct timbi2s_bus_control *control) +{ + kfree(control); + control = NULL; +} + +static void timbi2s_control_add_dev(struct timbi2s_dev *i2sdev) +{ + list_add(&i2sdev->item, &i2sdev->bus->control->list); +} + +static void timbi2s_control_del_dev(struct timbi2s_dev *i2sdev) +{ + list_del(&i2sdev->item); + if (list_empty(&i2sdev->bus->control->list)) + timbi2s_control_destroy(i2sdev->bus->control); +} + +static irqreturn_t timbi2s_irq(int irq, void *dev_id) +{ + u8 pend; + u32 iunit; + int i; + + struct timbi2s_bus *tbus = dev_id; + queue_work(tbus->workqueue, &tbus->work); + + iunit = ioread32(tbus->membase + I2S_UIR); + /* Find out which I2S instance is interrupting */ + for (i = 0; i < 32; i++) { + if ((1 << i) & iunit) { + pend = ioread8(tbus->membase + + (I2S_IPR + (i * REGSTEP * 7))); + iowrite8(pend, tbus->membase + + (I2S_ICR + (i * REGSTEP * 7))); + } + } + + return IRQ_HANDLED; +} + +static int __init timbi2s_probe(struct platform_device *dev) +{ + int err = 0; + struct timbi2s_dev *tdev, *tmp; + struct timbi2s_bus *tbus; + struct resource *iomem; + int i; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) { + err = -EINVAL; + goto err_mem; + } + + tbus = kzalloc(sizeof(*tbus), GFP_KERNEL); + if (!tbus) { + err = -EINVAL; + goto err_mem; + } + + /* Init bus_control */ + tbus->control = kzalloc(sizeof(struct timbi2s_bus_control), GFP_KERNEL); + if (!tbus->control) { + printk(KERN_ERR DRIVER_NAME + ": Failed to allocate timbi2s_bus_control.\n"); + err = -ENOMEM; + goto err_free; + } + INIT_LIST_HEAD(&tbus->control->list); + + /* Init workqueue */ + tbus->workqueue = create_singlethread_workqueue("timbi2s"); + if (tbus->workqueue == NULL) { + printk(KERN_ERR DRIVER_NAME + ": unable to create workqueue\n"); + err = -ENOMEM; + goto err_control; + } + INIT_WORK(&tbus->work, timbi2s_int_handler); + + if (!request_mem_region(iomem->start, + resource_size(iomem), DRIVER_NAME)) { + printk(KERN_EMERG DRIVER_NAME + ": Mem region is already in use\n"); + err = -ENXIO; + goto err_control; + } + + tbus->membase = ioremap(iomem->start, resource_size(iomem)); + if (tbus->membase == NULL) { + err = -ENOMEM; + goto err_request; + } + + bus_p = tbus; + + + + /* For now we have only 4 I2S instances in IP : 3 RX and 1 TX */ + /* Note: TX'es are always on top */ + /* TODO: auto-check how many are alive and bring them into control */ + for (i = 0; i < IP_I2S_NR; i++) { + tdev = kzalloc(sizeof(*tdev), GFP_KERNEL); + if (!tdev) { + err = -EINVAL; + goto clean_list; + } + + /* Allocate circ_buf */ + tdev->buffer = timbi2s_buf_alloc(); + if (tdev->buffer == NULL) { + printk(KERN_ERR "timbi2s: unable to allocate buffer\n"); + goto clean_list; + } + + INIT_LIST_HEAD(&tdev->item); + spin_lock_init(&tdev->lock); + + /* set up offsets for each instance of I2S */ + tdev->bus = tbus; /* ptr to our bus */ + tdev->membase = tbus->membase; + tdev->in_use = 0; + tdev->pscale_offset = I2S_PRESCALE + (i * REGSTEP * 7); + tdev->icr_offset = I2S_ICR + (i * REGSTEP * 7); + tdev->isr_offset = I2S_ISR + (i * REGSTEP * 7); + tdev->ipr_offset = I2S_IPR + (i * REGSTEP * 7); + tdev->ier_offset = I2S_IER + (i * REGSTEP * 7); + tdev->ctrl_offset = I2S_CTRL + (i * REGSTEP * 7); + tdev->fifo = I2S_FIFO + (i * REGSTEP * 7); + + /* Try to check and reset hardware */ + if (timbi2s_ioctrl(tdev)) + timbi2s_control_add_dev(tdev); + + tdev = NULL; + } + + tbus->irq = platform_get_irq(dev, 0); + if (tbus->irq < 0) { + err = -EINVAL; + goto clean_list; + } + + err = request_irq(tbus->irq, timbi2s_irq, 0, DRIVER_NAME, tbus); + if (err != 0) + goto clean_list; + + platform_set_drvdata(dev, tbus); + + dev_info(&dev->dev, "Driver for Timberdale I2S (ver: %d)" + " has been successfully registered.\n", + ioread32(tbus->membase + 0x00)); + return 0; + +clean_list: + list_for_each_entry_safe(tdev, tmp, &tbus->control->list, item) { + if (tdev->workqueue != NULL) { + flush_workqueue(tdev->workqueue); + destroy_workqueue(tdev->workqueue); + } + + if (tdev->buffer != NULL) + timbi2s_buf_free(tdev->buffer); + + timbi2s_control_del_dev(tdev); + kfree(tdev); + } + free_irq(tbus->irq, tbus); + iounmap(tbus->membase); +err_request: + release_mem_region(iomem->start, resource_size(iomem)); +err_control: + if (tbus->control != NULL) + timbi2s_control_destroy(tbus->control); +err_free: + kfree(tbus); +err_mem: + printk(KERN_ERR + DRIVER_NAME": Failed to register Timberdale I2S: %d\n", err); + + return err; +} + +static int __devexit timbi2s_remove(struct platform_device *dev) +{ + struct timbi2s_bus *tbus; + struct timbi2s_dev *tdev, *tmp; + struct resource *r; + + tbus = platform_get_drvdata(dev); + free_irq(tbus->irq, tbus); + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + + list_for_each_entry_safe(tdev, tmp, &tbus->control->list, item) { + if (tdev->workqueue != NULL) { + flush_workqueue(tdev->workqueue); + destroy_workqueue(tdev->workqueue); + } + + if (tdev->buffer != NULL) + timbi2s_buf_free(tdev->buffer); + + kfree(tdev); + } + + iounmap(tdev->membase); + if (r) + release_mem_region(r->start, resource_size(r)); + + dev_info(&dev->dev, "Driver for Timberdale I2S has been" + " successfully unregistered.\n"); + + platform_set_drvdata(dev, 0); + return 0; +} + +static struct platform_driver timbi2s_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = timbi2s_probe, + .remove = __devexit_p(timbi2s_remove), +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timbi2s_init(void) +{ + return platform_driver_register(&timbi2s_platform_driver); +} + +static void __exit timbi2s_exit(void) +{ + platform_driver_unregister(&timbi2s_platform_driver); +} + +module_init(timbi2s_init); +module_exit(timbi2s_exit); + +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_DESCRIPTION("Timberdale I2S bus driver"); +MODULE_LICENSE("GPL v2"); diff -uNr linux-2.6.29-clean/drivers/mmc/host/Kconfig linux-2.6.29/drivers/mmc/host/Kconfig --- linux-2.6.29-clean/drivers/mmc/host/Kconfig 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/mmc/host/Kconfig 2009-04-06 13:51:47.000000000 -0700 @@ -65,6 +65,16 @@ If unsure, say Y. +config MMC_SDHCI_PLTFM + tristate "SDHCI support on platform devices" + depends on MMC_SDHCI + help + This selects the Secure Digital Host Controller Interface. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_OMAP tristate "TI OMAP Multimedia Card Interface support" depends on ARCH_OMAP diff -uNr linux-2.6.29-clean/drivers/mmc/host/Makefile linux-2.6.29/drivers/mmc/host/Makefile --- linux-2.6.29-clean/drivers/mmc/host/Makefile 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/mmc/host/Makefile 2009-04-06 13:51:47.000000000 -0700 @@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o +obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o diff -uNr linux-2.6.29-clean/drivers/mmc/host/sdhci-pltfm.c linux-2.6.29/drivers/mmc/host/sdhci-pltfm.c --- linux-2.6.29-clean/drivers/mmc/host/sdhci-pltfm.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/mmc/host/sdhci-pltfm.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,262 @@ +/* + * sdhci-pltfm.c Support for SDHCI platform devices + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * SDHCI platform devices + * + * Inspired by sdhci-pci.c, by Pierre Ossman + */ + +#include +#include +#include + +#include + +#include + +#include "sdhci.h" + + +#define MAX_SLOTS 8 + +struct sdhci_pltfm_chip; + +struct sdhci_pltfm_slot { + struct sdhci_pltfm_chip *chip; + struct sdhci_host *host; + + int pltfm_resource; +}; + +struct sdhci_pltfm_chip { + struct platform_device *pdev; + + unsigned int quirks; + + int num_slots; /* Slots on controller */ + struct sdhci_pltfm_slot *slots[MAX_SLOTS]; /* Pointers to host slots */ +}; + + +/*****************************************************************************\ + * * + * SDHCI core callbacks * + * * +\*****************************************************************************/ + +static struct sdhci_ops sdhci_pltfm_ops = { +}; + +/*****************************************************************************\ + * * + * Device probing/removal * + * * +\*****************************************************************************/ + + +static struct sdhci_pltfm_slot * __devinit sdhci_pltfm_probe_slot( + struct platform_device *pdev, struct sdhci_pltfm_chip *chip, + int resource) +{ + struct sdhci_pltfm_slot *slot; + struct sdhci_host *host; + struct resource *iomem; + int ret; + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, resource); + if (!iomem) + return ERR_PTR(-ENODEV); + + if (resource_size(iomem) != 0x100) { + dev_err(&pdev->dev, "Invalid iomem size. You may " + "experience problems.\n"); + } + + if (!pdev->dev.parent) { + dev_err(&pdev->dev, "The parent device be a PCI device\n"); + return ERR_PTR(-ENODEV); + } + + host = sdhci_alloc_host(pdev->dev.parent, + sizeof(struct sdhci_pltfm_slot)); + if (IS_ERR(host)) + return ERR_PTR(PTR_ERR(host)); + + slot = sdhci_priv(host); + + slot->chip = chip; + slot->host = host; + slot->pltfm_resource = resource; + + host->hw_name = "PLTFM"; + host->ops = &sdhci_pltfm_ops; + host->quirks = chip->quirks; + + host->irq = platform_get_irq(pdev, 0); + + if (!request_mem_region(iomem->start, resource_size(iomem), + mmc_hostname(host->mmc))) { + dev_err(&pdev->dev, "cannot request region\n"); + ret = -EBUSY; + goto free; + } + + host->ioaddr = ioremap(iomem->start, resource_size(iomem)); + if (!host->ioaddr) { + dev_err(&pdev->dev, "failed to remap registers\n"); + goto release; + } + + ret = sdhci_add_host(host); + if (ret) + goto unmap; + + return slot; + +unmap: + iounmap(host->ioaddr); +release: + release_mem_region(iomem->start, resource_size(iomem)); +free: + sdhci_free_host(host); + + return ERR_PTR(ret); +} + +static void sdhci_pltfm_remove_slot(struct sdhci_pltfm_slot *slot) +{ + int dead; + u32 scratch; + struct resource *iomem; + + dead = 0; + scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); + if (scratch == (u32)-1) + dead = 1; + + sdhci_remove_host(slot->host, dead); + + iounmap(slot->host->ioaddr); + + iomem = platform_get_resource(slot->chip->pdev, IORESOURCE_MEM, + slot->pltfm_resource); + release_mem_region(iomem->start, resource_size(iomem)); + + sdhci_free_host(slot->host); +} + +static int __devinit sdhci_pltfm_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_chip *chip; + struct sdhci_pltfm_slot *slot; + u8 slots; + int ret, i; + + BUG_ON(pdev == NULL); + + for (slots = 0; slots <= MAX_SLOTS; slots++) + if (!platform_get_resource(pdev, IORESOURCE_MEM, slots)) + break; + + BUG_ON(slots > MAX_SLOTS || slots == 0); + + chip = kzalloc(sizeof(struct sdhci_pltfm_chip), GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto err; + } + + chip->pdev = pdev; + chip->num_slots = slots; + platform_set_drvdata(pdev, chip); + + for (i = 0; i < slots; i++) { + slot = sdhci_pltfm_probe_slot(pdev, chip, i); + if (IS_ERR(slot)) { + for (i--; i >= 0; i--) + sdhci_pltfm_remove_slot(chip->slots[i]); + ret = PTR_ERR(slot); + goto free; + } + + chip->slots[i] = slot; + } + + return 0; + +free: + platform_set_drvdata(pdev, NULL); + kfree(chip); + +err: + printk(KERN_ERR"Probing of sdhci-pltfm failed: %d\n", ret); + return ret; +} + +static int __devexit sdhci_pltfm_remove(struct platform_device *pdev) +{ + int i; + struct sdhci_pltfm_chip *chip; + + chip = platform_get_drvdata(pdev); + + if (chip) { + for (i = 0; i < chip->num_slots; i++) + sdhci_pltfm_remove_slot(chip->slots[i]); + + platform_set_drvdata(pdev, NULL); + kfree(chip); + } + + return 0; +} + +static struct platform_driver sdhci_pltfm_driver = { + .driver = { + .name = "sdhci", + .owner = THIS_MODULE, + }, + .probe = sdhci_pltfm_probe, + .remove = __devexit_p(sdhci_pltfm_remove), +}; + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +static int __init sdhci_drv_init(void) +{ + return platform_driver_register(&sdhci_pltfm_driver); +} + +static void __exit sdhci_drv_exit(void) +{ + platform_driver_unregister(&sdhci_pltfm_driver); +} + +module_init(sdhci_drv_init); +module_exit(sdhci_drv_exit); + +MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); +MODULE_AUTHOR("Mocean Laboratories "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:sdhci"); + diff -uNr linux-2.6.29-clean/drivers/serial/Kconfig linux-2.6.29/drivers/serial/Kconfig --- linux-2.6.29-clean/drivers/serial/Kconfig 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/serial/Kconfig 2009-04-06 13:51:47.000000000 -0700 @@ -1412,4 +1412,11 @@ default 19200 if (SERIAL_SPORT_BAUD_RATE_19200) default 9600 if (SERIAL_SPORT_BAUD_RATE_9600) +config SERIAL_TIMBERDALE + tristate "Support for timberdale UART" + depends on MFD_TIMBERDALE + select SERIAL_CORE + ---help--- + Add support for UART controller on timberdale. + endmenu diff -uNr linux-2.6.29-clean/drivers/serial/Makefile linux-2.6.29/drivers/serial/Makefile --- linux-2.6.29-clean/drivers/serial/Makefile 2009-04-01 09:20:24.000000000 -0700 +++ linux-2.6.29/drivers/serial/Makefile 2009-04-06 13:51:47.000000000 -0700 @@ -76,3 +76,4 @@ obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o +obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o diff -uNr linux-2.6.29-clean/drivers/serial/timbuart.c linux-2.6.29/drivers/serial/timbuart.c --- linux-2.6.29-clean/drivers/serial/timbuart.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/serial/timbuart.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,519 @@ +/* + * timbuart.c timberdale FPGA UART driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA UART + */ + +#include +#include +#include +#include +#include +#include + +#include "timbuart.h" + +struct timbuart_port { + struct uart_port port; + struct tasklet_struct tasklet; + int usedma; + u8 last_ier; + struct platform_device *dev; +}; + +static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, + 921600, 1843200, 3250000}; + +static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier); + +static irqreturn_t timbuart_handleinterrupt(int irq, void *devid); + +static void timbuart_stop_rx(struct uart_port *port) +{ + /* spin lock held by upper layer, disable all RX interrupts */ + u8 ier = ioread8(port->membase + TIMBUART_IER) & ~RXFLAGS; + iowrite8(ier, port->membase + TIMBUART_IER); +} + +static void timbuart_stop_tx(struct uart_port *port) +{ + /* spinlock held by upper layer, disable TX interrupt */ + u8 ier = ioread8(port->membase + TIMBUART_IER) & ~TXBAE; + iowrite8(ier, port->membase + TIMBUART_IER); +} + +static void timbuart_start_tx(struct uart_port *port) +{ + struct timbuart_port *uart = + container_of(port, struct timbuart_port, port); + + /* do not transfer anything here -> fire off the tasklet */ + tasklet_schedule(&uart->tasklet); +} + +static void timbuart_flush_buffer(struct uart_port *port) +{ + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX; + + iowrite8(ctl, port->membase + TIMBUART_CTRL); + iowrite8(TXBF, port->membase + TIMBUART_ISR); +} + +static void timbuart_rx_chars(struct uart_port *port) +{ + struct tty_struct *tty = port->info->port.tty; + + while (ioread8(port->membase + TIMBUART_ISR) & RXDP) { + u8 ch = ioread8(port->membase + TIMBUART_RXFIFO); + /* ack */ + iowrite8(RXDP, port->membase + TIMBUART_ISR); + port->icount.rx++; + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } + + spin_unlock(&port->lock); + tty_flip_buffer_push(port->info->port.tty); + spin_lock(&port->lock); + + dev_dbg(port->dev, "%s - total read %d bytes\n", + __func__, port->icount.rx); +} + +static void timbuart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + + while (!(ioread8(port->membase + TIMBUART_ISR) & TXBF) && + !uart_circ_empty(xmit)) { + iowrite8(xmit->buf[xmit->tail], + port->membase + TIMBUART_TXFIFO); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + + dev_dbg(port->dev, + "%s - total written %d bytes, CTL: %x, RTS: %x, baud: %x\n", + __func__, + port->icount.tx, + ioread8(port->membase + TIMBUART_CTRL), + port->mctrl & TIOCM_RTS, + ioread8(port->membase + TIMBUART_BAUDRATE)); +} + +static void timbuart_handle_tx_port(struct uart_port *port, u8 isr, u8 *ier) +{ + struct timbuart_port *uart = + container_of(port, struct timbuart_port, port); + struct circ_buf *xmit = &port->info->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + if (port->x_char) + return; + + if (isr & TXFLAGS) { + timbuart_tx_chars(port); + /* clear all TX interrupts */ + iowrite8(TXFLAGS, port->membase + TIMBUART_ISR); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + } else + /* Re-enable any tx interrupt */ + *ier |= uart->last_ier & TXFLAGS; + + /* enable interrupts if there are chars in the transmit buffer, + * Or if we delivered some bytes and want the almost empty interrupt + * we wake up the upper layer later when we got the interrupt + * to give it some time to go out... + */ + if (!uart_circ_empty(xmit)) + *ier |= TXBAE; + + dev_dbg(port->dev, "%s - leaving\n", __func__); +} + +void timbuart_handle_rx_port(struct uart_port *port, u8 isr, u8 *ier) +{ + if (isr & RXFLAGS) { + /* Some RX status is set */ + if (isr & RXBF) { + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | + TIMBUART_CTRL_FLSHRX; + iowrite8(ctl, port->membase + TIMBUART_CTRL); + port->icount.overrun++; + } else if (isr & (RXDP)) + timbuart_rx_chars(port); + + /* ack all RX interrupts */ + iowrite8(RXFLAGS, port->membase + TIMBUART_ISR); + } + + /* always have the RX interrupts enabled */ + *ier |= RXBAF | RXBF | RXTT; + + dev_dbg(port->dev, "%s - leaving\n", __func__); +} + +void timbuart_tasklet(unsigned long arg) +{ + struct timbuart_port *uart = (struct timbuart_port *)arg; + u8 isr, ier = 0; + + spin_lock(&uart->port.lock); + + isr = ioread8(uart->port.membase + TIMBUART_ISR); + dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr); + + if (!uart->usedma) + timbuart_handle_tx_port(&uart->port, isr, &ier); + + timbuart_mctrl_check(&uart->port, isr, &ier); + + if (!uart->usedma) + timbuart_handle_rx_port(&uart->port, isr, &ier); + + iowrite8(ier, uart->port.membase + TIMBUART_IER); + + spin_unlock(&uart->port.lock); + dev_dbg(uart->port.dev, "%s leaving\n", __func__); +} + +static unsigned int timbuart_tx_empty(struct uart_port *port) +{ + u8 isr = ioread8(port->membase + TIMBUART_ISR); + + return (isr & TXBAE) ? TIOCSER_TEMT : 0; +} + +static unsigned int timbuart_get_mctrl(struct uart_port *port) +{ + u8 cts = ioread8(port->membase + TIMBUART_CTRL); + dev_dbg(port->dev, "%s - cts %x\n", __func__, cts); + + if (cts & TIMBUART_CTRL_CTS) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + dev_dbg(port->dev, "%s - %x\n", __func__, mctrl); + + if (mctrl & TIOCM_RTS) + iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); + else + iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); +} + +static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier) +{ + unsigned int cts; + + if (isr & CTS_DELTA) { + /* ack */ + iowrite8(CTS_DELTA, port->membase + TIMBUART_ISR); + cts = timbuart_get_mctrl(port); + uart_handle_cts_change(port, cts & TIOCM_CTS); + wake_up_interruptible(&port->info->delta_msr_wait); + } + + *ier |= CTS_DELTA; +} + +static void timbuart_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +static void timbuart_break_ctl(struct uart_port *port, int ctl) +{ + /* N/A */ +} + +static int timbuart_startup(struct uart_port *port) +{ + struct timbuart_port *uart = + container_of(port, struct timbuart_port, port); + + dev_dbg(port->dev, "%s\n", __func__); + + iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL); + iowrite8(0xff, port->membase + TIMBUART_ISR); + /* Enable all but TX interrupts */ + iowrite8(RXBAF | RXBF | RXTT | CTS_DELTA, + port->membase + TIMBUART_IER); + + return request_irq(port->irq, timbuart_handleinterrupt, IRQF_SHARED, + "timb-uart", uart); +} + +static void timbuart_shutdown(struct uart_port *port) +{ + struct timbuart_port *uart = + container_of(port, struct timbuart_port, port); + dev_dbg(port->dev, "%s\n", __func__); + free_irq(port->irq, uart); + iowrite8(0, port->membase + TIMBUART_IER); +} + +static int get_bindex(int baud) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(baudrates); i++) + if (baud == baudrates[i]) + return i; + + return -1; +} + +static void timbuart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned int baud; + short bindex; + unsigned long flags; + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + bindex = get_bindex(baud); + dev_dbg(port->dev, "%s - bindex %d\n", __func__, bindex); + + if (bindex < 0) { + printk(KERN_ALERT "timbuart: Unsupported baud rate\n"); + } else { + spin_lock_irqsave(&port->lock, flags); + iowrite8((u8)bindex, port->membase + TIMBUART_BAUDRATE); + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock_irqrestore(&port->lock, flags); + } +} + +static const char *timbuart_type(struct uart_port *port) +{ + return port->type == PORT_UNKNOWN ? "timbuart" : NULL; +} + +/* We do not request/release mappings of the registers here, + * currently it's done in the proble function. + */ +static void timbuart_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + int size = + resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0)); + + if (port->flags & UPF_IOREMAP) { + iounmap(port->membase); + port->membase = NULL; + } + + release_mem_region(port->mapbase, size); +} + +static int timbuart_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + int size = + resource_size(platform_get_resource(pdev, IORESOURCE_MEM, 0)); + + if (!request_mem_region(port->mapbase, size, "timb-uart")) + return -EBUSY; + + if (port->flags & UPF_IOREMAP) { + port->membase = ioremap(port->mapbase, size); + if (port->membase == NULL) { + release_mem_region(port->mapbase, size); + return -ENOMEM; + } + } + + return 0; +} + +static irqreturn_t timbuart_handleinterrupt(int irq, void *devid) +{ + struct timbuart_port *uart = (struct timbuart_port *)devid; + + uart->last_ier = ioread8(uart->port.membase + TIMBUART_IER); + + /* disable interrupts, let the tasklet enable them again if needed */ + iowrite8(0, uart->port.membase + TIMBUART_IER); + + /* fire off bottom half */ + tasklet_schedule(&uart->tasklet); + + return IRQ_HANDLED; +} + +/* + * Configure/autoconfigure the port. + */ +static void timbuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_TIMBUART; + timbuart_request_port(port); + } +} + +static int timbuart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + return -EINVAL; +} + +static struct uart_ops timbuart_ops = { + .tx_empty = timbuart_tx_empty, + .set_mctrl = timbuart_set_mctrl, + .get_mctrl = timbuart_get_mctrl, + .stop_tx = timbuart_stop_tx, + .start_tx = timbuart_start_tx, + .flush_buffer = timbuart_flush_buffer, + .stop_rx = timbuart_stop_rx, + .enable_ms = timbuart_enable_ms, + .break_ctl = timbuart_break_ctl, + .startup = timbuart_startup, + .shutdown = timbuart_shutdown, + .set_termios = timbuart_set_termios, + .type = timbuart_type, + .release_port = timbuart_release_port, + .request_port = timbuart_request_port, + .config_port = timbuart_config_port, + .verify_port = timbuart_verify_port +}; + +static struct uart_driver timbuart_driver = { + .owner = THIS_MODULE, + .driver_name = "timberdale_uart", + .dev_name = "ttyTU", + .major = TIMBUART_MAJOR, + .minor = TIMBUART_MINOR, + .nr = 1 +}; + +static int timbuart_probe(struct platform_device *dev) +{ + int err; + struct timbuart_port *uart; + struct resource *iomem; + + dev_dbg(&dev->dev, "%s\n", __func__); + + uart = kzalloc(sizeof(*uart), GFP_KERNEL); + if (!uart) { + err = -EINVAL; + goto err_mem; + } + + uart->usedma = 0; + + uart->port.uartclk = 3250000 * 16; + uart->port.fifosize = TIMBUART_FIFO_SIZE; + uart->port.regshift = 2; + uart->port.iotype = UPIO_MEM; + uart->port.ops = &timbuart_ops; + uart->port.irq = 0; + uart->port.flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + uart->port.line = 0; + uart->port.dev = &dev->dev; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) { + err = -ENOMEM; + goto err_register; + } + uart->port.mapbase = iomem->start; + uart->port.membase = NULL; + + uart->port.irq = platform_get_irq(dev, 0); + if (uart->port.irq < 0) { + err = -EINVAL; + goto err_register; + } + + tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart); + + err = uart_register_driver(&timbuart_driver); + if (err) + goto err_register; + + err = uart_add_one_port(&timbuart_driver, &uart->port); + if (err) + goto err_add_port; + + platform_set_drvdata(dev, uart); + + return 0; + +err_add_port: + uart_unregister_driver(&timbuart_driver); +err_register: + kfree(uart); +err_mem: + printk(KERN_ERR "timberdale: Failed to register Timberdale UART: %d\n", + err); + + return err; +} + +static int timbuart_remove(struct platform_device *dev) +{ + struct timbuart_port *uart = platform_get_drvdata(dev); + + tasklet_kill(&uart->tasklet); + uart_remove_one_port(&timbuart_driver, &uart->port); + uart_unregister_driver(&timbuart_driver); + kfree(uart); + + return 0; +} + +static struct platform_driver timbuart_platform_driver = { + .driver = { + .name = "timb-uart", + .owner = THIS_MODULE, + }, + .probe = timbuart_probe, + .remove = timbuart_remove, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timbuart_init(void) +{ + return platform_driver_register(&timbuart_platform_driver); +} + +static void __exit timbuart_exit(void) +{ + platform_driver_unregister(&timbuart_platform_driver); +} + +module_init(timbuart_init); +module_exit(timbuart_exit); + +MODULE_DESCRIPTION("Timberdale UART driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:timb-uart"); + diff -uNr linux-2.6.29-clean/drivers/serial/timbuart.h linux-2.6.29/drivers/serial/timbuart.h --- linux-2.6.29-clean/drivers/serial/timbuart.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/serial/timbuart.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,57 @@ +/* + * timbuart.c timberdale FPGA GPIO driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA UART + */ + +#ifndef _TIMBUART_H +#define _TIMBUART_H + +#define TIMBUART_FIFO_SIZE 2048 + +#define TIMBUART_RXFIFO 0x08 +#define TIMBUART_TXFIFO 0x0c +#define TIMBUART_IER 0x10 +#define TIMBUART_IPR 0x14 +#define TIMBUART_ISR 0x18 +#define TIMBUART_CTRL 0x1c +#define TIMBUART_BAUDRATE 0x20 + +#define TIMBUART_CTRL_RTS 0x01 +#define TIMBUART_CTRL_CTS 0x02 +#define TIMBUART_CTRL_FLSHTX 0x40 +#define TIMBUART_CTRL_FLSHRX 0x80 + +#define TXBF 0x01 +#define TXBAE 0x02 +#define CTS_DELTA 0x04 +#define RXDP 0x08 +#define RXBAF 0x10 +#define RXBF 0x20 +#define RXTT 0x40 +#define RXBNAE 0x80 + +#define RXFLAGS (RXDP | RXBAF | RXBF | RXTT | RXBNAE) +#define TXFLAGS (TXBF | TXBAE) + +#define TIMBUART_MAJOR 204 +#define TIMBUART_MINOR 192 + +#endif /* _TIMBUART_H */ + diff -uNr linux-2.6.29-clean/drivers/spi/Kconfig linux-2.6.29/drivers/spi/Kconfig --- linux-2.6.29-clean/drivers/spi/Kconfig 2009-04-01 09:20:25.000000000 -0700 +++ linux-2.6.29/drivers/spi/Kconfig 2009-04-06 13:51:47.000000000 -0700 @@ -211,8 +211,8 @@ SPI driver for Toshiba TXx9 MIPS SoCs config SPI_XILINX - tristate "Xilinx SPI controller" - depends on XILINX_VIRTEX && EXPERIMENTAL + tristate "Xilinx SPI controller common module" + depends on EXPERIMENTAL select SPI_BITBANG help This exposes the SPI controller IP from the Xilinx EDK. @@ -220,6 +220,25 @@ See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" Product Specification document (DS464) for hardware details. +config SPI_XILINX_OF + tristate "Xilinx SPI controller OF device" + depends on SPI_XILINX && XILINX_VIRTEX + help + This exposes the SPI controller IP from the Xilinx EDK. + + See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" + Product Specification document (DS464) for hardware details. + +config SPI_XILINX_PLTFM + tristate "Xilinx SPI controller platform device" + depends on SPI_XILINX + help + This exposes the SPI controller IP from the Xilinx EDK. + + See the "OPB Serial Peripheral Interface (SPI) (v1.00e)" + Product Specification document (DS464) for hardware details. + + # # Add new SPI master controllers in alphabetical order above this line # diff -uNr linux-2.6.29-clean/drivers/spi/Makefile linux-2.6.29/drivers/spi/Makefile --- linux-2.6.29-clean/drivers/spi/Makefile 2009-04-01 09:20:25.000000000 -0700 +++ linux-2.6.29/drivers/spi/Makefile 2009-04-06 13:51:47.000000000 -0700 @@ -29,6 +29,8 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o +obj-$(CONFIG_SPI_XILINX_OF) += xilinx_spi_of.o +obj-$(CONFIG_SPI_XILINX_PLTFM) += xilinx_spi_pltfm.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o # ... add above this line ... diff -uNr linux-2.6.29-clean/drivers/spi/xilinx_spi.c linux-2.6.29/drivers/spi/xilinx_spi.c --- linux-2.6.29-clean/drivers/spi/xilinx_spi.c 2009-04-01 09:20:25.000000000 -0700 +++ linux-2.6.29/drivers/spi/xilinx_spi.c 2009-04-06 13:51:47.000000000 -0700 @@ -14,22 +14,28 @@ #include #include #include -#include - -#include -#include -#include #include #include #include -#define XILINX_SPI_NAME "xilinx_spi" +#include "xilinx_spi.h" + +#ifndef CONFIG_PPC +#define in_8(addr) ioread8(addr) +#define in_be16(addr) ioread16(addr) +#define in_be32(addr) ioread32(addr) + +#define out_8(addr, b) iowrite8(b, addr) +#define out_be16(addr, w) iowrite16(w, addr) +#define out_be32(addr, l) iowrite32(l, addr) +#endif + /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) * Product Specification", DS464 */ -#define XSPI_CR_OFFSET 0x62 /* 16-bit Control Register */ +#define XSPI_CR_OFFSET_DEF 0x62 /* 16-bit Control Register */ #define XSPI_CR_ENABLE 0x02 #define XSPI_CR_MASTER_MODE 0x04 @@ -41,7 +47,7 @@ #define XSPI_CR_MANUAL_SSELECT 0x80 #define XSPI_CR_TRANS_INHIBIT 0x100 -#define XSPI_SR_OFFSET 0x67 /* 8-bit Status Register */ +#define XSPI_SR_OFFSET_DEF 0x67 /* 8-bit Status Register */ #define XSPI_SR_RX_EMPTY_MASK 0x01 /* Receive FIFO is empty */ #define XSPI_SR_RX_FULL_MASK 0x02 /* Receive FIFO is full */ @@ -49,10 +55,10 @@ #define XSPI_SR_TX_FULL_MASK 0x08 /* Transmit FIFO is full */ #define XSPI_SR_MODE_FAULT_MASK 0x10 /* Mode fault error */ -#define XSPI_TXD_OFFSET 0x6b /* 8-bit Data Transmit Register */ -#define XSPI_RXD_OFFSET 0x6f /* 8-bit Data Receive Register */ +#define XSPI_TXD_OFFSET_DEF 0x6b /* 8-bit Data Transmit Register */ +#define XSPI_RXD_OFFSET_DEF 0x6f /* 8-bit Data Receive Register */ -#define XSPI_SSR_OFFSET 0x70 /* 32-bit Slave Select Register */ +#define XSPI_SSR_OFFSET_DEF 0x70 /* 32-bit Slave Select Register */ /* Register definitions as per "OPB IPIF (v3.01c) Product Specification", DS414 * IPIF registers are 32 bit @@ -74,24 +80,10 @@ #define XIPIF_V123B_RESETR_OFFSET 0x40 /* IPIF reset register */ #define XIPIF_V123B_RESET_MASK 0x0a /* the value to write */ -struct xilinx_spi { - /* bitbang has to be first */ - struct spi_bitbang bitbang; - struct completion done; - - void __iomem *regs; /* virt. address of the control registers */ - - u32 irq; - - u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */ - - u8 *rx_ptr; /* pointer in the Tx buffer */ - const u8 *tx_ptr; /* pointer in the Rx buffer */ - int remaining_bytes; /* the number of bytes left to transfer */ -}; -static void xspi_init_hw(void __iomem *regs_base) +void xspi_init_hw(struct xilinx_spi *xspi) { + void __iomem *regs_base = xspi->regs; /* Reset the SPI device */ out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET, XIPIF_V123B_RESET_MASK); @@ -101,30 +93,31 @@ out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET, XIPIF_V123B_GINTR_ENABLE); /* Deselect the slave on the SPI bus */ - out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff); + out_be32(regs_base + xspi->ssr_offset, 0xffff); /* Disable the transmitter, enable Manual Slave Select Assertion, * put SPI controller into master mode, and enable it */ - out_be16(regs_base + XSPI_CR_OFFSET, + out_be16(regs_base + xspi->cr_offset, XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE); } +EXPORT_SYMBOL(xspi_init_hw); -static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) +void xilinx_spi_chipselect(struct spi_device *spi, int is_on) { struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); if (is_on == BITBANG_CS_INACTIVE) { /* Deselect the slave on the SPI bus */ - out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff); + out_be32(xspi->regs + xspi->ssr_offset, 0xffff); } else if (is_on == BITBANG_CS_ACTIVE) { /* Set the SPI clock phase and polarity */ - u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET) + u16 cr = in_be16(xspi->regs + xspi->cr_offset) & ~XSPI_CR_MODE_MASK; if (spi->mode & SPI_CPHA) cr |= XSPI_CR_CPHA; if (spi->mode & SPI_CPOL) cr |= XSPI_CR_CPOL; - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + out_be16(xspi->regs + xspi->cr_offset, cr); /* We do not check spi->max_speed_hz here as the SPI clock * frequency is not software programmable (the IP block design @@ -132,10 +125,11 @@ */ /* Activate the chip select */ - out_be32(xspi->regs + XSPI_SSR_OFFSET, + out_be32(xspi->regs + xspi->ssr_offset, ~(0x0001 << spi->chip_select)); } } +EXPORT_SYMBOL(xilinx_spi_chipselect); /* spi_bitbang requires custom setup_transfer() to be defined if there is a * custom txrx_bufs(). We have nothing to setup here as the SPI IP block @@ -143,8 +137,7 @@ * Check for 8 bits per word. Chip select delay calculations could be * added here as soon as bitbang_work() can be made aware of the delay value. */ -static int xilinx_spi_setup_transfer(struct spi_device *spi, - struct spi_transfer *t) +int xilinx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { u8 bits_per_word; @@ -157,11 +150,12 @@ return 0; } +EXPORT_SYMBOL(xilinx_spi_setup_transfer); /* the spi->mode bits understood by this driver: */ #define MODEBITS (SPI_CPOL | SPI_CPHA) -static int xilinx_spi_setup(struct spi_device *spi) +int xilinx_spi_setup(struct spi_device *spi) { struct spi_bitbang *bitbang; struct xilinx_spi *xspi; @@ -188,25 +182,25 @@ return 0; } +EXPORT_SYMBOL(xilinx_spi_setup); static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) { u8 sr; /* Fill the Tx FIFO with as many bytes as possible */ - sr = in_8(xspi->regs + XSPI_SR_OFFSET); + sr = in_8(xspi->regs + xspi->sr_offset); while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) { - if (xspi->tx_ptr) { - out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++); - } else { - out_8(xspi->regs + XSPI_TXD_OFFSET, 0); - } + if (xspi->tx_ptr) + out_8(xspi->regs + xspi->txd_offset, *xspi->tx_ptr++); + else + out_8(xspi->regs + xspi->txd_offset, 0); xspi->remaining_bytes--; - sr = in_8(xspi->regs + XSPI_SR_OFFSET); + sr = in_8(xspi->regs + xspi->sr_offset); } } -static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) +int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) { struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); u32 ipif_ier; @@ -229,8 +223,8 @@ ipif_ier | XSPI_INTR_TX_EMPTY); /* Start the transfer by not inhibiting the transmitter any longer */ - cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT; - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + cr = in_be16(xspi->regs + xspi->cr_offset) & ~XSPI_CR_TRANS_INHIBIT; + out_be16(xspi->regs + xspi->cr_offset, cr); wait_for_completion(&xspi->done); @@ -239,14 +233,14 @@ return t->len - xspi->remaining_bytes; } - +EXPORT_SYMBOL(xilinx_spi_txrx_bufs); /* This driver supports single master mode only. Hence Tx FIFO Empty * is the only interrupt we care about. * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, and Slave Mode * Fault are not to happen. */ -static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) +irqreturn_t xilinx_spi_irq(int irq, void *dev_id) { struct xilinx_spi *xspi = dev_id; u32 ipif_isr; @@ -264,20 +258,19 @@ * transmitter while the Isr refills the transmit register/FIFO, * or make sure it is stopped if we're done. */ - cr = in_be16(xspi->regs + XSPI_CR_OFFSET); - out_be16(xspi->regs + XSPI_CR_OFFSET, + cr = in_be16(xspi->regs + xspi->cr_offset); + out_be16(xspi->regs + xspi->cr_offset, cr | XSPI_CR_TRANS_INHIBIT); /* Read out all the data from the Rx FIFO */ - sr = in_8(xspi->regs + XSPI_SR_OFFSET); + sr = in_8(xspi->regs + xspi->sr_offset); while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) { u8 data; - data = in_8(xspi->regs + XSPI_RXD_OFFSET); - if (xspi->rx_ptr) { + data = in_8(xspi->regs + xspi->rxd_offset); + if (xspi->rx_ptr) *xspi->rx_ptr++ = data; - } - sr = in_8(xspi->regs + XSPI_SR_OFFSET); + sr = in_8(xspi->regs + xspi->sr_offset); } /* See if there is more data to send */ @@ -286,7 +279,7 @@ /* Start the transfer by not inhibiting the * transmitter any longer */ - out_be16(xspi->regs + XSPI_CR_OFFSET, cr); + out_be16(xspi->regs + xspi->cr_offset, cr); } else { /* No more data to send. * Indicate the transfer is completed. @@ -297,167 +290,18 @@ return IRQ_HANDLED; } +EXPORT_SYMBOL(xilinx_spi_irq); -static int __init xilinx_spi_of_probe(struct of_device *ofdev, - const struct of_device_id *match) -{ - struct spi_master *master; - struct xilinx_spi *xspi; - struct resource r_irq_struct; - struct resource r_mem_struct; - - struct resource *r_irq = &r_irq_struct; - struct resource *r_mem = &r_mem_struct; - int rc = 0; - const u32 *prop; - int len; - - /* Get resources(memory, IRQ) associated with the device */ - master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi)); - - if (master == NULL) { - return -ENOMEM; - } - - dev_set_drvdata(&ofdev->dev, master); - - rc = of_address_to_resource(ofdev->node, 0, r_mem); - if (rc) { - dev_warn(&ofdev->dev, "invalid address\n"); - goto put_master; - } - - rc = of_irq_to_resource(ofdev->node, 0, r_irq); - if (rc == NO_IRQ) { - dev_warn(&ofdev->dev, "no IRQ found\n"); - goto put_master; - } - - xspi = spi_master_get_devdata(master); - xspi->bitbang.master = spi_master_get(master); - xspi->bitbang.chipselect = xilinx_spi_chipselect; - xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; - xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; - xspi->bitbang.master->setup = xilinx_spi_setup; - init_completion(&xspi->done); - - xspi->irq = r_irq->start; - - if (!request_mem_region(r_mem->start, - r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) { - rc = -ENXIO; - dev_warn(&ofdev->dev, "memory request failure\n"); - goto put_master; - } - - xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1); - if (xspi->regs == NULL) { - rc = -ENOMEM; - dev_warn(&ofdev->dev, "ioremap failure\n"); - goto put_master; - } - xspi->irq = r_irq->start; - - /* dynamic bus assignment */ - master->bus_num = -1; - - /* number of slave select bits is required */ - prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); - if (!prop || len < sizeof(*prop)) { - dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); - goto put_master; - } - master->num_chipselect = *prop; - - /* SPI controller initializations */ - xspi_init_hw(xspi->regs); - - /* Register for SPI Interrupt */ - rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); - if (rc != 0) { - dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq); - goto unmap_io; - } - - rc = spi_bitbang_start(&xspi->bitbang); - if (rc != 0) { - dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n"); - goto free_irq; - } - - dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", - (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq); - - /* Add any subnodes on the SPI bus */ - of_register_spi_devices(master, ofdev->node); - - return rc; - -free_irq: - free_irq(xspi->irq, xspi); -unmap_io: - iounmap(xspi->regs); -put_master: - spi_master_put(master); - return rc; -} - -static int __devexit xilinx_spi_remove(struct of_device *ofdev) +void xilinx_spi_set_default_reg_offsets(struct xilinx_spi *xspi) { - struct xilinx_spi *xspi; - struct spi_master *master; - - master = platform_get_drvdata(ofdev); - xspi = spi_master_get_devdata(master); - - spi_bitbang_stop(&xspi->bitbang); - free_irq(xspi->irq, xspi); - iounmap(xspi->regs); - dev_set_drvdata(&ofdev->dev, 0); - spi_master_put(xspi->bitbang.master); - - return 0; -} - -/* work with hotplug and coldplug */ -MODULE_ALIAS("platform:" XILINX_SPI_NAME); - -static int __exit xilinx_spi_of_remove(struct of_device *op) -{ - return xilinx_spi_remove(op); + xspi->cr_offset = XSPI_CR_OFFSET_DEF; + xspi->sr_offset = XSPI_SR_OFFSET_DEF; + xspi->txd_offset = XSPI_TXD_OFFSET_DEF; + xspi->rxd_offset = XSPI_RXD_OFFSET_DEF; + xspi->ssr_offset = XSPI_SSR_OFFSET_DEF; } +EXPORT_SYMBOL(xilinx_spi_set_default_reg_offsets); -static struct of_device_id xilinx_spi_of_match[] = { - { .compatible = "xlnx,xps-spi-2.00.a", }, - { .compatible = "xlnx,xps-spi-2.00.b", }, - {} -}; - -MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); - -static struct of_platform_driver xilinx_spi_of_driver = { - .owner = THIS_MODULE, - .name = "xilinx-xps-spi", - .match_table = xilinx_spi_of_match, - .probe = xilinx_spi_of_probe, - .remove = __exit_p(xilinx_spi_of_remove), - .driver = { - .name = "xilinx-xps-spi", - .owner = THIS_MODULE, - }, -}; - -static int __init xilinx_spi_init(void) -{ - return of_register_platform_driver(&xilinx_spi_of_driver); -} -module_init(xilinx_spi_init); - -static void __exit xilinx_spi_exit(void) -{ - of_unregister_platform_driver(&xilinx_spi_of_driver); -} -module_exit(xilinx_spi_exit); MODULE_AUTHOR("MontaVista Software, Inc. "); MODULE_DESCRIPTION("Xilinx SPI driver"); MODULE_LICENSE("GPL"); diff -uNr linux-2.6.29-clean/drivers/spi/xilinx_spi.h linux-2.6.29/drivers/spi/xilinx_spi.h --- linux-2.6.29-clean/drivers/spi/xilinx_spi.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/spi/xilinx_spi.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,52 @@ +/* + * xilinx_spi.c + * + * Xilinx SPI controller driver (master mode only) + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#ifndef _XILINX_SPI_H_ +#define _XILINX_SPI_H_ 1 + +#include +#include + +#define XILINX_SPI_NAME "xilinx_spi" + + +struct xilinx_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct completion done; + + void __iomem *regs; /* virt. address of the control registers */ + + u32 irq; + + u32 speed_hz; /* SCK has a fixed frequency of speed_hz Hz */ + + u8 *rx_ptr; /* pointer in the Tx buffer */ + const u8 *tx_ptr; /* pointer in the Rx buffer */ + int remaining_bytes; /* the number of bytes left to transfer */ + /* offset to the XSPI regs, these might vary... */ + u8 cr_offset; + u8 sr_offset; + u8 txd_offset; + u8 rxd_offset; + u8 ssr_offset; +}; + +void xspi_init_hw(struct xilinx_spi *xspi); +void xilinx_spi_set_default_reg_offsets(struct xilinx_spi *xspi); +void xilinx_spi_chipselect(struct spi_device *spi, int is_on); +int xilinx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t); +int xilinx_spi_setup(struct spi_device *spi); +int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t); +irqreturn_t xilinx_spi_irq(int irq, void *dev_id); +#endif diff -uNr linux-2.6.29-clean/drivers/spi/xilinx_spi_of.c linux-2.6.29/drivers/spi/xilinx_spi_of.c --- linux-2.6.29-clean/drivers/spi/xilinx_spi_of.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/spi/xilinx_spi_of.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,193 @@ +/* + * xilinx_spi.c + * + * Xilinx SPI controller driver (master mode only) + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "xilinx_spi.h" + + +static int __init xilinx_spi_of_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct spi_master *master; + struct xilinx_spi *xspi; + struct resource r_irq_struct; + struct resource r_mem_struct; + + struct resource *r_irq = &r_irq_struct; + struct resource *r_mem = &r_mem_struct; + int rc = 0; + const u32 *prop; + int len; + + /* Get resources(memory, IRQ) associated with the device */ + master = spi_alloc_master(&ofdev->dev, sizeof(struct xilinx_spi)); + + if (master == NULL) + return -ENOMEM; + + dev_set_drvdata(&ofdev->dev, master); + + rc = of_address_to_resource(ofdev->node, 0, r_mem); + if (rc) { + dev_warn(&ofdev->dev, "invalid address\n"); + goto put_master; + } + + rc = of_irq_to_resource(ofdev->node, 0, r_irq); + if (rc == NO_IRQ) { + dev_warn(&ofdev->dev, "no IRQ found\n"); + goto put_master; + } + + xspi = spi_master_get_devdata(master); + xspi->bitbang.master = spi_master_get(master); + xspi->bitbang.chipselect = xilinx_spi_chipselect; + xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; + xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; + xspi->bitbang.master->setup = xilinx_spi_setup; + init_completion(&xspi->done); + + xspi->irq = r_irq->start; + + if (!request_mem_region(r_mem->start, + r_mem->end - r_mem->start + 1, XILINX_SPI_NAME)) { + rc = -ENXIO; + dev_warn(&ofdev->dev, "memory request failure\n"); + goto put_master; + } + + xspi->regs = ioremap(r_mem->start, r_mem->end - r_mem->start + 1); + if (xspi->regs == NULL) { + rc = -ENOMEM; + dev_warn(&ofdev->dev, "ioremap failure\n"); + goto put_master; + } + xspi->irq = r_irq->start; + + /* dynamic bus assignment */ + master->bus_num = -1; + + /* number of slave select bits is required */ + prop = of_get_property(ofdev->node, "xlnx,num-ss-bits", &len); + if (!prop || len < sizeof(*prop)) { + dev_warn(&ofdev->dev, "no 'xlnx,num-ss-bits' property\n"); + goto put_master; + } + master->num_chipselect = *prop; + + xilinx_spi_set_default_reg_offsets(xspi); + + /* SPI controller initializations */ + xspi_init_hw(xspi->regs); + + /* Register for SPI Interrupt */ + rc = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); + if (rc != 0) { + dev_warn(&ofdev->dev, "irq request failure: %d\n", xspi->irq); + goto unmap_io; + } + + rc = spi_bitbang_start(&xspi->bitbang); + if (rc != 0) { + dev_err(&ofdev->dev, "spi_bitbang_start FAILED\n"); + goto free_irq; + } + + dev_info(&ofdev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", + (unsigned int)r_mem->start, (u32)xspi->regs, xspi->irq); + + /* Add any subnodes on the SPI bus */ + of_register_spi_devices(master, ofdev->node); + + return rc; + +free_irq: + free_irq(xspi->irq, xspi); +unmap_io: + iounmap(xspi->regs); +put_master: + spi_master_put(master); + return rc; +} + +static int __devexit xilinx_spi_remove(struct of_device *ofdev) +{ + struct xilinx_spi *xspi; + struct spi_master *master; + + master = platform_get_drvdata(ofdev); + xspi = spi_master_get_devdata(master); + + spi_bitbang_stop(&xspi->bitbang); + free_irq(xspi->irq, xspi); + iounmap(xspi->regs); + dev_set_drvdata(&ofdev->dev, 0); + spi_master_put(xspi->bitbang.master); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:" XILINX_SPI_NAME); + +static int __exit xilinx_spi_of_remove(struct of_device *op) +{ + return xilinx_spi_remove(op); +} + +static struct of_device_id xilinx_spi_of_match[] = { + { .compatible = "xlnx,xps-spi-2.00.a", }, + { .compatible = "xlnx,xps-spi-2.00.b", }, + {} +}; + +MODULE_DEVICE_TABLE(of, xilinx_spi_of_match); + +static struct of_platform_driver xilinx_spi_of_driver = { + .owner = THIS_MODULE, + .name = "xilinx-xps-spi", + .match_table = xilinx_spi_of_match, + .probe = xilinx_spi_of_probe, + .remove = __exit_p(xilinx_spi_of_remove), + .driver = { + .name = "xilinx-xps-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init xilinx_spi_init(void) +{ + return of_register_platform_driver(&xilinx_spi_of_driver); +} +module_init(xilinx_spi_init); + +static void __exit xilinx_spi_exit(void) +{ + of_unregister_platform_driver(&xilinx_spi_of_driver); +} +module_exit(xilinx_spi_exit); +MODULE_AUTHOR("MontaVista Software, Inc. "); +MODULE_DESCRIPTION("Xilinx SPI driver"); +MODULE_LICENSE("GPL"); diff -uNr linux-2.6.29-clean/drivers/spi/xilinx_spi_pltfm.c linux-2.6.29/drivers/spi/xilinx_spi_pltfm.c --- linux-2.6.29-clean/drivers/spi/xilinx_spi_pltfm.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/drivers/spi/xilinx_spi_pltfm.c 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,184 @@ +/* + * xilinx_spi_pltfm.c Support for Xilinx SPI platform devices + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Xilinx SPI devices as platform devices + * + * Inspired by xilinx_spi.c, 2002-2007 (c) MontaVista Software, Inc. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xilinx_spi.h" + +static int __init xilinx_spi_probe(struct platform_device *dev) +{ + int ret = 0; + struct spi_master *master; + struct xilinx_spi *xspi; + struct xspi_platform_data *pdata; + struct resource *r; + + master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi)); + + if (master == NULL) + return -ENOMEM; + + + platform_set_drvdata(dev, master); + pdata = dev->dev.platform_data; + if (pdata == NULL) { + ret = -ENODEV; + goto put_master; + } + + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + goto put_master; + } + + xspi = spi_master_get_devdata(master); + xspi->bitbang.master = spi_master_get(master); + xspi->bitbang.chipselect = xilinx_spi_chipselect; + xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; + xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs; + xspi->bitbang.master->setup = xilinx_spi_setup; + init_completion(&xspi->done); + + if (!request_mem_region(r->start, resource_size(r), XILINX_SPI_NAME)) { + ret = -ENXIO; + goto put_master; + } + + xspi->regs = ioremap(r->start, resource_size(r)); + if (xspi->regs == NULL) { + ret = -ENOMEM; + goto map_failed; + } + + ret = platform_get_irq(dev, 0); + if (ret < 0) { + ret = -ENXIO; + goto unmap_io; + } + xspi->irq = ret; + + master->bus_num = pdata->bus_num; + master->num_chipselect = pdata->num_chipselect; + xspi->speed_hz = pdata->speed_hz; + xilinx_spi_set_default_reg_offsets(xspi); + if (pdata->cr_offset) + xspi->cr_offset = pdata->cr_offset; + if (pdata->sr_offset) + xspi->sr_offset = pdata->sr_offset; + if (pdata->txd_offset) + xspi->txd_offset = pdata->txd_offset; + if (pdata->rxd_offset) + xspi->rxd_offset = pdata->rxd_offset; + if (pdata->ssr_offset) + xspi->ssr_offset = pdata->ssr_offset; + + /* SPI controller initializations */ + xspi_init_hw(xspi); + + /* Register for SPI Interrupt */ + ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi); + if (ret != 0) + goto unmap_io; + + ret = spi_bitbang_start(&xspi->bitbang); + if (ret != 0) { + dev_err(&dev->dev, "spi_bitbang_start FAILED\n"); + goto free_irq; + } + + dev_info(&dev->dev, "at 0x%08X mapped to 0x%08X, irq=%d\n", + (u32)r->start, (u32)xspi->regs, xspi->irq); + return ret; + +free_irq: + free_irq(xspi->irq, xspi); +unmap_io: + iounmap(xspi->regs); +map_failed: + release_mem_region(r->start, resource_size(r)); +put_master: + spi_master_put(master); + return ret; +} + +static int __devexit xilinx_spi_remove(struct platform_device *dev) +{ + struct xilinx_spi *xspi; + struct spi_master *master; + struct resource *r; + + master = platform_get_drvdata(dev); + xspi = spi_master_get_devdata(master); + r = platform_get_resource(dev, IORESOURCE_MEM, 0); + + spi_bitbang_stop(&xspi->bitbang); + free_irq(xspi->irq, xspi); + iounmap(xspi->regs); + + if (r) + release_mem_region(r->start, resource_size(r)); + + platform_set_drvdata(dev, 0); + spi_master_put(xspi->bitbang.master); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:" XILINX_SPI_NAME); + +static struct platform_driver xilinx_spi_driver = { + .probe = xilinx_spi_probe, + .remove = __devexit_p(xilinx_spi_remove), + .driver = { + .name = XILINX_SPI_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init xilinx_spi_init(void) +{ + return platform_driver_register(&xilinx_spi_driver); +} +module_init(xilinx_spi_init); + +static void __exit xilinx_spi_exit(void) +{ + platform_driver_unregister(&xilinx_spi_driver); +} +module_exit(xilinx_spi_exit); + +MODULE_AUTHOR("Mocean Laboratories "); +MODULE_DESCRIPTION("Xilinx SPI platform driver"); +MODULE_LICENSE("GPL v2"); + diff -uNr linux-2.6.29-clean/include/linux/i2c-ocores.h linux-2.6.29/include/linux/i2c-ocores.h --- linux-2.6.29-clean/include/linux/i2c-ocores.h 2009-04-01 09:20:20.000000000 -0700 +++ linux-2.6.29/include/linux/i2c-ocores.h 2009-04-06 13:51:47.000000000 -0700 @@ -14,6 +14,8 @@ struct ocores_i2c_platform_data { u32 regstep; /* distance between registers */ u32 clock_khz; /* input clock in kHz */ + u8 num_devices; /* number of devices in the devices list */ + struct i2c_board_info const *devices; /* devices connected to the bus */ }; #endif /* _LINUX_I2C_OCORES_H */ diff -uNr linux-2.6.29-clean/include/linux/mfd/timbdma.h linux-2.6.29/include/linux/mfd/timbdma.h --- linux-2.6.29-clean/include/linux/mfd/timbdma.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/include/linux/mfd/timbdma.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,80 @@ +/* + * timbdma.h timberdale FPGA DMA driver defines + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA DMA engine + */ + +#ifndef _TIMBDMA_H +#define _TIMBDMA_H + +#include + + +#define DMA_IRQ_UART_RX 0x01 +#define DMA_IRQ_UART_TX 0x02 +#define DMA_IRQ_MLB_RX 0x04 +#define DMA_IRQ_MLB_TX 0x08 +#define DMA_IRQ_VIDEO_RX 0x10 +#define DMA_IRQ_VIDEO_DROP 0x20 +#define DMA_IRQS 6 + + +typedef int (*timbdma_interruptcb)(u32 flag, void *data); + +enum timbdma_ctrlmap { + timbdma_ctrlmap_DMACFGBTUART = 0x000000, + timbdma_ctrlmap_DMACFGMLBSY = 0x000040, + timbdma_ctrlmap_DMACFGVIDEO = 0x000080, + timbdma_ctrlmap_TIMBSTATUS = 0x080000, + timbdma_ctrlmap_TIMBPEND = 0x080004, + timbdma_ctrlmap_TIMBENABLE = 0x080008, + timbdma_ctrlmap_VIDEOBUFFER = 0x200000 +}; + +enum timbdma_dmacfg { + timbdma_dmacfg_RXSTARTH = 0x00, + timbdma_dmacfg_RXSTARTL = 0x04, + timbdma_dmacfg_RXLENGTH = 0x08, + timbdma_dmacfg_RXFPGAWP = 0x0C, + timbdma_dmacfg_RXSWRP = 0x10, + timbdma_dmacfg_RXENABLE = 0x14, + timbdma_dmacfg_TXSTARTH = 0x18, + timbdma_dmacfg_TXSTARTL = 0x1C, + timbdma_dmacfg_TXLENGTH = 0x20, + timbdma_dmacfg_TXSWWP = 0x24, + timbdma_dmacfg_TXFPGARP = 0x28, + timbdma_dmacfg_TXBEFINT = 0x2C, + timbdma_dmacfg_BPERROW = 0x30 +}; + +struct timbdma_dev { + void __iomem *membase; + timbdma_interruptcb callbacks[DMA_IRQS]; + void *callback_data[DMA_IRQS]; + spinlock_t lock; /* mutual exclusion */ +}; + +void timb_start_dma(u32 flag, unsigned long buf, int len, int bytes_per_row); + +void *timb_stop_dma(u32 flags); + +void timb_set_dma_interruptcb(u32 flags, timbdma_interruptcb icb, void *data); + +#endif /* _TIMBDMA_H */ + diff -uNr linux-2.6.29-clean/include/linux/mfd/timbi2s.h linux-2.6.29/include/linux/mfd/timbi2s.h --- linux-2.6.29-clean/include/linux/mfd/timbi2s.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/include/linux/mfd/timbi2s.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,66 @@ +/* + * timbi2s.h timberdale FPGA I2S driver + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * Timberdale FPGA I2S + */ + +struct timbi2s_bus_control { + struct list_head list; +}; + +struct timbi2s_bus { + void __iomem *membase; + u32 irq; + struct timbi2s_bus_control *control; + struct workqueue_struct *workqueue; + struct work_struct work; +}; + +struct timbi2s_dev { + void __iomem *membase; + u32 irq; + struct timbi2s_bus *bus; + struct workqueue_struct *workqueue; + struct work_struct work; + u32 ioctrl; + u32 devid; + u8 timbi2s_rx; + u8 timbi2s_tx; + struct circ_buf *buffer; + /* Register access */ + spinlock_t lock; + + int in_use; + u8 pscale_offset; /* Prescale */ + u8 icr_offset; /* Clear register */ + u8 isr_offset; /* Status */ + u8 ipr_offset; /* Pending register */ + u8 ier_offset; /* Interrupt Enable register */ + u8 ctrl_offset; + u8 fifo; + + struct list_head item; +}; + +static struct timbi2s_dev *timbi2s_get_tx(void); +static struct timbi2s_dev *timbi2s_get_rx(void); +static void timbi2s_put(struct timbi2s_dev *tdev); + +static int timbi2s_ioctrl(struct timbi2s_dev *i2sdev); + diff -uNr linux-2.6.29-clean/include/linux/serial_core.h linux-2.6.29/include/linux/serial_core.h --- linux-2.6.29-clean/include/linux/serial_core.h 2009-04-01 09:20:20.000000000 -0700 +++ linux-2.6.29/include/linux/serial_core.h 2009-04-06 13:51:47.000000000 -0700 @@ -164,6 +164,9 @@ /* NWPSERIAL */ #define PORT_NWPSERIAL 85 +/* Timberdale UART */ +#define PORT_TIMBUART 86 + #ifdef __KERNEL__ #include diff -uNr linux-2.6.29-clean/include/linux/spi/xilinx_spi.h linux-2.6.29/include/linux/spi/xilinx_spi.h --- linux-2.6.29-clean/include/linux/spi/xilinx_spi.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/include/linux/spi/xilinx_spi.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,17 @@ +#ifndef __LINUX_SPI_XILINX_SPI_H +#define __LINUX_SPI_XILINX_SPI_H + +/* SPI Controller IP */ +struct xspi_platform_data { + s16 bus_num; + u16 num_chipselect; + u32 speed_hz; + u8 cr_offset; + u8 sr_offset; + u8 txd_offset; + u8 rxd_offset; + u8 ssr_offset; +}; + +#endif /* __LINUX_SPI_XILINX_SPI_H */ + diff -uNr linux-2.6.29-clean/include/media/adv7180.h linux-2.6.29/include/media/adv7180.h --- linux-2.6.29-clean/include/media/adv7180.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.29/include/media/adv7180.h 2009-04-06 13:51:47.000000000 -0700 @@ -0,0 +1,127 @@ +/* + * adv7180.h Analog Devices ADV7180 video decoder driver defines + * Copyright (c) 2009 Intel Corporation + * + * 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 program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define DRIVER_NAME "adv7180" + +#define I2C_ADV7180 0x42 +#define ADV7180_NR_REG 0xfc + +#define ADV7180_IN_CTRL 0x00 /* Input CR */ +#define ADV7180_OUT_CTRL 0x03 /* Output CR */ +#define ADV7180_EXT_OUT_CTRL 0x04 /* Extended Output CR */ + +#define ADV7180_ADI_CTRL 0x0e /* ADI CR */ +# define ADI_ENABLE 0x20 /* Enable access to sub-regs */ + +#define ADV7180_SR_1 0x10 /* Status Register 1 */ +#define ADV7180_SR_2 0x12 +#define ADV7180_SR_3 0x13 + +/* Interrupt and VDP sub-registers */ +#define ADV7180_ISR_1 0x42 /* Interrupt Status Register 1 */ +#define ADV7180_ICR_1 0x43 /* Interrupt Clear Register 1 */ + +#define ADV7180_ISR_2 0x46 +#define ADV7180_ICR_2 0x47 + +#define ADV7180_ISR_3 0x4a +#define ADV7180_ICR_3 0x4b + +#define ADV7180_ISR_4 0x4e +#define ADV7180_ICR_4 0x4f +/* */ + +#define ADV7180_SR 0x10 +#define ADV7180_STATUS_NTSM 0x00 /* NTSM M/J */ +#define ADV7180_STATUS_NTSC 0x10 /* NTSC 4.43 */ +#define ADV7180_STATUS_PAL_M 0x20 /* PAL M */ +#define ADV7180_STATUS_PAL_60 0x30 /* PAL 60 */ +#define ADV7180_STATUS_PAL 0x40 /* PAL B/G/H/I/D */ +#define ADV7180_STATUS_SECAM 0x50 /* SECAM */ +#define ADV7180_STATUS_PAL_N 0x60 /* PAL Combination N */ +#define ADV7180_STATUS_SECAM_525 0x70 /* SECAM 525 */ + +enum input_mode { + CVBS, /* Composite */ + SVIDEO, /* S-video */ + YPbPr, /* Component */ +}; + +struct adv7180 { + unsigned char reg[ADV7180_NR_REG]; + int norm; + enum input_mode input; + int enable; + struct i2c_client *client; +}; + +static const unsigned char reset_icr[] = { + ADV7180_ICR_1, 0x00, + ADV7180_ICR_2, 0x00, + ADV7180_ICR_3, 0x00, + ADV7180_ICR_4, 0x00, +}; + +/* ADV7180 LQFP-64. ADV7180.pdf, page 104 */ +static const unsigned char init_cvbs_64[] = { + 0x00, 0x01, /* INSEL = CVBS in on Ain2 */ + 0x04, 0x57, /* Enable SFL */ + 0x17, 0x41, /* Select SH1 */ + + 0x31, 0x02, /* Clear NEWAV_MODE, SAV/EAV to + * suit ADV video encoders + */ + 0x3d, 0xa2, /* MWE enable manual window, + * color kill threshold to 2 + */ + 0x3e, 0x6a, /* BLM optimization */ + 0x3f, 0xa0, /* BGB optimization */ + 0x0e, 0x80, /* Hidden space */ + 0x55, 0x81, /* ADC configuration */ + 0x0e, 0x00, /* User space */ +}; + +static const unsigned char init_svideo_64[] = { + 0x00, 0x08, /* Insel = Y/C, Y = AIN3, C = AIN6 */ + 0x04, 0x57, /* Enable SFL */ + 0x31, 0x02, /* Clear NEWAV_MODE, SAV/EAV to + * suit ADV video encoders + */ + 0x3d, 0xa2, /* MWE enable manual window, + * color kill threshold to 2 + */ + 0x3e, 0x6a, /* BLM optimization */ + 0x3f, 0xa0, /* BGB optimization */ + 0x58, 0x04, /* Mandatory write. This must be + * performed for correct operation. + */ + 0x0e, 0x80, /* Hidden space */ + 0x55, 0x81, /* ADC configuration */ + 0x0e, 0x00, /* User space */ +}; + +static const unsigned char init_ypbpr_64[] = { + 0x00, 0x09, /* INSEL = YPrPb, Y = AIN1, Pr = AIN4, Pb = AIN5 */ + 0x31, 0x02, /* Clear NEWAV_MODE, SAV/EAV to suit ADV video encoders */ + 0x3d, 0xa2, /* MWE enable manual window */ + 0x3e, 0x6a, /* BLM optimization */ + 0x3f, 0xa0, /* ADI recommended */ + 0x0e, 0x80, /* Hidden space */ + 0x55, 0x81, /* ADC configuration */ + 0x0e, 0x00, /* User space */ +};