diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-03-16 12:44:20 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-03-16 12:44:20 +0000 |
commit | dc2e1a589aa53a62f60136a4eb727fa770d2e02e (patch) | |
tree | 53d0723062766eb7a33421dc50a80e154901c133 /drivers | |
parent | 83b63cbde17a215fad5303e85a4546e2d31c7613 (diff) |
Update ITE 8712 code.
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/Config.in | 13 | ||||
-rw-r--r-- | drivers/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/ite_gpio.c | 428 | ||||
-rw-r--r-- | drivers/char/qtronix.c | 18 | ||||
-rw-r--r-- | drivers/i2c/Config.in | 7 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 12 | ||||
-rw-r--r-- | drivers/i2c/i2c-adap-ite.c | 319 | ||||
-rw-r--r-- | drivers/i2c/i2c-algo-ite.c | 872 | ||||
-rw-r--r-- | drivers/i2c/i2c-ite.h | 117 | ||||
-rw-r--r-- | drivers/ide/it8172.c | 88 | ||||
-rw-r--r-- | drivers/sound/ite8172.c | 1957 |
11 files changed, 3775 insertions, 57 deletions
diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 21935083b..fbce24791 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -60,6 +60,16 @@ if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then tristate ' Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION fi fi +if [ "$CONFIG_IT8712" = "y" ]; then + bool 'Enable Qtronix 990P Keyboard Support' CONFIG_QTRONIX_KEYBOARD + if [ "$CONFIG_QTRONIX_KEYBOARD" = "y" ]; then + define_bool CONFIG_IT8172_CIR y + else + bool ' Enable PS2 Keyboard Support' CONFIG_PC_KEYB + fi + bool 'Enable Smart Card Reader 0 Support ' CONFIG_IT8172_SCR0 + bool 'Enable Smart Card Reader 1 Support ' CONFIG_IT8172_SCR1 +fi if [ "$CONFIG_FOOTBRIDGE" = "y" ]; then bool 'DC21285 serial port support' CONFIG_SERIAL_21285 if [ "$CONFIG_SERIAL_21285" = "y" ]; then @@ -191,4 +201,7 @@ source drivers/char/drm/Config.in if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then source drivers/char/pcmcia/Config.in fi +if [ "$CONFIG_MIPS_ITE8172" = "y" ]; then + tristate ' ITE GPIO' CONFIG_ITE_GPIO +fi endmenu diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 830f8a8ec..20d718501 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -168,6 +168,7 @@ endif obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_INTEL_RNG) += i810_rng.o +obj-$(CONFIG_ITE_GPIO) += ite_gpio.o obj-$(CONFIG_QIC02_TAPE) += tpqic02.o diff --git a/drivers/char/ite_gpio.c b/drivers/char/ite_gpio.c new file mode 100644 index 000000000..e1bcab574 --- /dev/null +++ b/drivers/char/ite_gpio.c @@ -0,0 +1,428 @@ +/* + * FILE NAME ite_gpio.c + * + * BRIEF MODULE DESCRIPTION + * API for ITE GPIO device. + * Driver for ITE GPIO device. + * + * Author: MontaVista Software, Inc. <source@mvista.com> + * Hai-Pao Fan <haipao@mvista.com> + * + * Copyright 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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 <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <asm/uaccess.h> +#include <asm/addrspace.h> +#include <asm/it8172/it8172_int.h> +#include <linux/sched.h> +#include <linux/ite_gpio.h> + +#define ite_gpio_base 0x14013800 + +#define ITE_GPADR (*(volatile __u8 *)(0x14013800 + KSEG1)) +#define ITE_GPBDR (*(volatile __u8 *)(0x14013808 + KSEG1)) +#define ITE_GPCDR (*(volatile __u8 *)(0x14013810 + KSEG1)) +#define ITE_GPACR (*(volatile __u16 *)(0x14013802 + KSEG1)) +#define ITE_GPBCR (*(volatile __u16 *)(0x1401380a + KSEG1)) +#define ITE_GPCCR (*(volatile __u16 *)(0x14013812 + KSEG1)) +#define ITE_GPAICR (*(volatile __u16 *)(0x14013804 + KSEG1)) +#define ITE_GPBICR (*(volatile __u16 *)(0x1401380c + KSEG1)) +#define ITE_GPCICR (*(volatile __u16 *)(0x14013814 + KSEG1)) +#define ITE_GPAISR (*(volatile __u8 *)(0x14013806 + KSEG1)) +#define ITE_GPBISR (*(volatile __u8 *)(0x1401380e + KSEG1)) +#define ITE_GPCISR (*(volatile __u8 *)(0x14013816 + KSEG1)) +#define ITE_GCR (*(volatile __u8 *)(0x14013818 + KSEG1)) + +#define MAX_GPIO_LINE 21 +static int ite_gpio_irq=IT8172_GPIO_IRQ; + +static long ite_irq_counter[MAX_GPIO_LINE]; +wait_queue_head_t ite_gpio_wait[MAX_GPIO_LINE]; +static int ite_gpio_irq_pending[MAX_GPIO_LINE]; + +static int ite_gpio_debug=0; +#define DEB(x) if (ite_gpio_debug>=1) x + +int ite_gpio_in(__u32 device, __u32 mask, volatile __u32 *data) +{ + DEB(printk("ite_gpio_in mask=0x%x\n",mask)); + + switch (device) { + case ITE_GPIO_PORTA: + ITE_GPACR = (__u16)mask; /* 0xffff */ + *data = ITE_GPADR; + break; + case ITE_GPIO_PORTB: + ITE_GPBCR = (__u16)mask; /* 0xffff */ + *data = ITE_GPBDR; + break; + case ITE_GPIO_PORTC: + ITE_GPCCR = (__u16)mask; /* 0x03ff */ + *data = ITE_GPCDR; + break; + default: + return -EFAULT; + } + + return 0; +} + + +int ite_gpio_out(__u32 device, __u32 mask, __u32 data) +{ + switch (device) { + case ITE_GPIO_PORTA: + ITE_GPACR = (__u16)mask; /* 0x5555 */ + ITE_GPADR = (__u8)data; + break; + case ITE_GPIO_PORTB: + ITE_GPBCR = (__u16)mask; /* 0x5555 */ + ITE_GPBDR = (__u8)data; + break; + case ITE_GPIO_PORTC: + ITE_GPCCR = (__u16)mask; /* 0x0155 */ + ITE_GPCDR = (__u8)data; + break; + default: + return -EFAULT; + } + + return 0; +} + +int ite_gpio_int_ctrl(__u32 device, __u32 mask, __u32 data) +{ + switch (device) { + case ITE_GPIO_PORTA: + ITE_GPAICR = (ITE_GPAICR & ~mask) | (data & mask); + break; + case ITE_GPIO_PORTB: + ITE_GPBICR = (ITE_GPBICR & ~mask) | (data & mask); + break; + case ITE_GPIO_PORTC: + ITE_GPCICR = (ITE_GPCICR & ~mask) | (data & mask); + break; + default: + return -EFAULT; + } + + return 0; +} + +int ite_gpio_in_status(__u32 device, __u32 mask, volatile __u32 *data) +{ + int ret=-1; + + if (MAX_GPIO_LINE > *data >= 0) + ret=ite_gpio_irq_pending[*data]; + + DEB(printk("ite_gpio_in_status %d ret=%d\n",*data, ret)); + + switch (device) { + case ITE_GPIO_PORTA: + *data = ITE_GPAISR & mask; + break; + case ITE_GPIO_PORTB: + *data = ITE_GPBISR & mask; + break; + case ITE_GPIO_PORTC: + *data = ITE_GPCISR & mask; + break; + default: + return -EFAULT; + } + + return ret; +} + +int ite_gpio_out_status(__u32 device, __u32 mask, __u32 data) +{ + switch (device) { + case ITE_GPIO_PORTA: + ITE_GPAISR = (ITE_GPAISR & ~mask) | (data & mask); + break; + case ITE_GPIO_PORTB: + ITE_GPBISR = (ITE_GPBISR & ~mask) | (data & mask); + break; + case ITE_GPIO_PORTC: + ITE_GPCISR = (ITE_GPCISR & ~mask) | (data & mask); + break; + default: + return -EFAULT; + } + + return 0; +} + +int ite_gpio_gen_ctrl(__u32 device, __u32 mask, __u32 data) +{ + ITE_GCR = (ITE_GCR & ~mask) | (data & mask); + + return 0; +} + +int ite_gpio_int_wait (__u32 device, __u32 mask, __u32 data) +{ + int i,line=0, ret=0; + unsigned long flags; + + switch (device) { + case ITE_GPIO_PORTA: + line = data & mask; + break; + case ITE_GPIO_PORTB: + line = (data & mask) <<8; + break; + case ITE_GPIO_PORTC: + line = (data & mask) <<16; + break; + } + for (i=MAX_GPIO_LINE-1; i >= 0; i--) { + if ( (line) & (1 << i)) + break; + } + + DEB(printk("wait device=0x%d mask=0x%x data=0x%x index %d\n", + device, mask, data, i)); + + if (line & ~(1<<i)) + return -EFAULT; + + if (ite_gpio_irq_pending[i]==1) + return -EFAULT; + + save_flags (flags); + cli(); + ite_gpio_irq_pending[i] = 1; + ret = interruptible_sleep_on_timeout(&ite_gpio_wait[i], 3*HZ); + restore_flags (flags); + ite_gpio_irq_pending[i] = 0; + + return ret; +} + +EXPORT_SYMBOL(ite_gpio_in); +EXPORT_SYMBOL(ite_gpio_out); +EXPORT_SYMBOL(ite_gpio_int_ctrl); +EXPORT_SYMBOL(ite_gpio_in_status); +EXPORT_SYMBOL(ite_gpio_out_status); +EXPORT_SYMBOL(ite_gpio_gen_ctrl); +EXPORT_SYMBOL(ite_gpio_int_wait); + +static int ite_gpio_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + if (minor != GPIO_MINOR) + return -ENODEV; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + return 0; +} + + +static int ite_gpio_release(struct inode *inode, struct file *file) +{ + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + return 0; +} + + +static int ite_gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + + static struct ite_gpio_ioctl_data ioctl_data; + + if (copy_from_user(&ioctl_data, (struct ite_gpio_ioctl_data *)arg, + sizeof(ioctl_data))) + return -EFAULT; + if ((ioctl_data.device < ITE_GPIO_PORTA) || + (ioctl_data.device > ITE_GPIO_PORTC) ) + return -EFAULT; + + switch(cmd) { + case ITE_GPIO_IN: + if (ite_gpio_in(ioctl_data.device, ioctl_data.mask, + &ioctl_data.data)) + return -EFAULT; + + if (copy_to_user((struct ite_gpio_ioctl_data *)arg, + &ioctl_data, sizeof(ioctl_data))) + return -EFAULT; + break; + + case ITE_GPIO_OUT: + return ite_gpio_out(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + case ITE_GPIO_INT_CTRL: + return ite_gpio_int_ctrl(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + case ITE_GPIO_IN_STATUS: + if (ite_gpio_in_status(ioctl_data.device, ioctl_data.mask, + &ioctl_data.data)) + return -EFAULT; + if (copy_to_user((struct ite_gpio_ioctl_data *)arg, + &ioctl_data, sizeof(ioctl_data))) + return -EFAULT; + break; + + case ITE_GPIO_OUT_STATUS: + return ite_gpio_out_status(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + case ITE_GPIO_GEN_CTRL: + return ite_gpio_gen_ctrl(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + case ITE_GPIO_INT_WAIT: + return ite_gpio_int_wait(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + break; + + default: + return -ENOIOCTLCMD; + + } + return 0; +} + +static void ite_gpio_irq_handler(int this_irq, void *dev_id, struct pt_regs *regs) +{ + int i,line; + + line = ITE_GPCISR & 0x1f; + for (i=4; i >=0; i--) { + if ( line & (1 << i)) { + ++ite_irq_counter[i+16]; + ite_gpio_irq_pending[i+16] = 2; + wake_up_interruptible(&ite_gpio_wait[i+16]); + +DEB(printk("interrupt 0x%x %d\n", &ite_gpio_wait[i+16], i+16)); + + ITE_GPCISR = ITE_GPCISR & (1<<i); + return; + } + } + line = ITE_GPBISR; + for (i=7; i >= 0; i--) { + if ( line & (1 << i)) { + ++ite_irq_counter[i+8]; + ite_gpio_irq_pending[i+8] = 2; + wake_up_interruptible(&ite_gpio_wait[i+8]); + +DEB(printk("interrupt 0x%x %d\n",ITE_GPBISR, i+8)); + + ITE_GPBISR = ITE_GPBISR & (1<<i); + return; + } + } + line = ITE_GPAISR; + for (i=7; i >= 0; i--) { + if ( line & (1 << i)) { + ++ite_irq_counter[i]; + ite_gpio_irq_pending[i] = 2; + wake_up_interruptible(&ite_gpio_wait[i]); + +DEB(printk("interrupt 0x%x %d\n",ITE_GPAISR, i)); + + ITE_GPAISR = ITE_GPAISR & (1<<i); + return; + } + } +} + +static struct file_operations ite_gpio_fops = +{ + owner: THIS_MODULE, + ioctl: ite_gpio_ioctl, + open: ite_gpio_open, + release: ite_gpio_release, +}; + +/* GPIO_MINOR in include/linux/miscdevice.h */ +static struct miscdevice ite_gpio_miscdev = +{ + GPIO_MINOR, + "ite_gpio", + &ite_gpio_fops +}; + +int __init ite_gpio_init(void) +{ + int i; + + misc_register(&ite_gpio_miscdev); + + if (check_region(ite_gpio_base, 0x1c) < 0 ) { + return -ENODEV; + } else { + request_region(ite_gpio_base, 0x1c, "ITE GPIO"); + } + + /* initialize registers */ + ITE_GPACR = 0xffff; + ITE_GPBCR = 0xffff; + ITE_GPCCR = 0xffff; + ITE_GPAICR = 0x00ff; + ITE_GPBICR = 0x00ff; + ITE_GPCICR = 0x00ff; + ITE_GCR = 0; + + for (i = 0; i < MAX_GPIO_LINE; i++) { + ite_gpio_irq_pending[i]=0; + init_waitqueue_head(&ite_gpio_wait[i]); + } + if (request_irq(ite_gpio_irq, ite_gpio_irq_handler, SA_SHIRQ, "gpio", 0) < 0) + return 0; + printk("GPIO at 0x%x (irq = %d)\n", ite_gpio_base, ite_gpio_irq); + + return 0; +} + +void __exit ite_gpio_exit(void) +{ + misc_deregister(&ite_gpio_miscdev); +} + +module_init(ite_gpio_init); +module_exit(ite_gpio_exit); diff --git a/drivers/char/qtronix.c b/drivers/char/qtronix.c index c74aabf2b..b560ecbca 100644 --- a/drivers/char/qtronix.c +++ b/drivers/char/qtronix.c @@ -6,7 +6,7 @@ * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. - * ppopov@mvista.com or support@mvista.com + * ppopov@mvista.com or source@mvista.com * * * The bottom portion of this driver was take from @@ -353,19 +353,19 @@ spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; static unsigned char handle_kbd_event(void); -int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) +int kbd_setkeycode(unsigned int scancode, unsigned int keycode) { - printk("pckbd_setkeycode scancode %x keycode %x\n", scancode, keycode); + printk("kbd_setkeycode scancode %x keycode %x\n", scancode, keycode); return 0; } -int pckbd_getkeycode(unsigned int scancode) +int kbd_getkeycode(unsigned int scancode) { return scancode; } -int pckbd_translate(unsigned char scancode, unsigned char *keycode, +int kbd_translate(unsigned char scancode, unsigned char *keycode, char raw_mode) { static int prev_scancode = 0; @@ -406,9 +406,9 @@ int pckbd_translate(unsigned char scancode, unsigned char *keycode, return 1; } -char pckbd_unexpected_up(unsigned char keycode) +char kbd_unexpected_up(unsigned char keycode) { - //printk("pckbd_unexpected_up\n"); + //printk("kbd_unexpected_up\n"); return 0; } @@ -422,12 +422,12 @@ static inline void handle_keyboard_event(unsigned char scancode, int down) } -void pckbd_leds(unsigned char leds) +void kbd_leds(unsigned char leds) { } /* dummy */ -void pckbd_init_hw(void) +void kbd_init_hw(void) { } diff --git a/drivers/i2c/Config.in b/drivers/i2c/Config.in index 405121edc..7e168eeff 100644 --- a/drivers/i2c/Config.in +++ b/drivers/i2c/Config.in @@ -20,6 +20,13 @@ if [ "$CONFIG_I2C" != "n" ]; then dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF fi + if [ "$CONFIG_MIPS_ITE8172" = "y" ]; then + dep_tristate 'ITE I2C Algorithm' CONFIG_ITE_I2C_ALGO $CONFIG_I2C + if [ "CONFIG_ITE_I2C_ALGO" != "n" ]; then + dep_tristate ' ITE I2C Adapter' CONFIG_ITE_I2C_ADAP $CONFIG_ITE_I2C_ALGO + fi + fi + # This is needed for automatic patch generation: sensors code starts here # This is needed for automatic patch generation: sensors code ends here diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index d25ed14f3..9ed18abc1 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -2,9 +2,10 @@ # Makefile for the kernel i2c bus driver. # +MOD_LIST_NAME := I2C_MODULES O_TARGET := i2c.o -export-objs := i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o +export-objs := i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o i2c-algo-ite.o obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o @@ -14,9 +15,18 @@ obj-$(CONFIG_I2C_ELV) += i2c-elv.o obj-$(CONFIG_I2C_VELLEMAN) += i2c-velleman.o obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o +obj-$(CONFIG_I2C_ALGO8XX) += i2c-algo-8xx.o +obj-$(CONFIG_I2C_RPXLITE) += i2c-rpx.o +obj-$(CONFIG_ITE_I2C_ALGO) += i2c-algo-ite.o +obj-$(CONFIG_ITE_I2C_ADAP) += i2c-adap-ite.o # This is needed for automatic patch generation: sensors code starts here # This is needed for automatic patch generation: sensors code ends here +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) + include $(TOPDIR)/Rules.make diff --git a/drivers/i2c/i2c-adap-ite.c b/drivers/i2c/i2c-adap-ite.c new file mode 100644 index 000000000..c6a35dafc --- /dev/null +++ b/drivers/i2c/i2c-adap-ite.c @@ -0,0 +1,319 @@ +/* + ------------------------------------------------------------------------- + i2c-adap-ite.c i2c-hw access for the IIC peripheral on the ITE MIPS system + ------------------------------------------------------------------------- + Hai-Pao Fan, MontaVista Software, Inc. + hpfan@mvista.com or source@mvista.com + + Copyright 2001 MontaVista Software Inc. + + ---------------------------------------------------------------------------- + This file was highly leveraged from i2c-elektor.c, which was created + by Simon G. Vogl and Hans Berglund: + + + Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even + Frodo Looijaard <frodol@dds.nl> */ + +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/version.h> +#include <linux/init.h> +#include <asm/irq.h> +#include <asm/io.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-ite.h> +#include <linux/i2c-adap-ite.h> +#include "i2c-ite.h" + +#define DEFAULT_BASE 0x14014030 +#define ITE_IIC_IO_SIZE 0x40 +#define DEFAULT_IRQ 0 +#define DEFAULT_CLOCK 0x1b0e /* default 16MHz/(27+14) = 400KHz */ +#define DEFAULT_OWN 0x55 + +static int base = 0; +static int irq = 0; +static int clock = 0; +static int own = 0; + +static int i2c_debug=0; +static struct iic_ite gpi; +#if (LINUX_VERSION_CODE < 0x020301) +static struct wait_queue *iic_wait = NULL; +#else +static wait_queue_head_t iic_wait; +#endif +static int iic_pending; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x +#define DEBE(x) x /* error messages */ + + +/* ----- local functions ---------------------------------------------- */ + +static void iic_ite_setiic(void *data, int ctl, short val) +{ + unsigned long j = jiffies + 10; + + DEB3(printk(" Write 0x%02x to 0x%x\n",(unsigned short)val, ctl&0xff)); + DEB3({while (jiffies < j) schedule();}) + outw(val,ctl); +} + +static short iic_ite_getiic(void *data, int ctl) +{ + short val; + + val = inw(ctl); + DEB3(printk("Read 0x%02x from 0x%x\n",(unsigned short)val, ctl&0xff)); + return (val); +} + +/* Return our slave address. This is the address + * put on the I2C bus when another master on the bus wants to address us + * as a slave + */ +static int iic_ite_getown(void *data) +{ + return (gpi.iic_own); +} + + +static int iic_ite_getclock(void *data) +{ + return (gpi.iic_clock); +} + + +#if 0 +static void iic_ite_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} +#endif + + +/* Put this process to sleep. We will wake up when the + * IIC controller interrupts. + */ +static void iic_ite_waitforpin(void) { + + int timeout = 2; + + /* If interrupts are enabled (which they are), then put the process to + * sleep. This process will be awakened by two events -- either the + * the IIC peripheral interrupts or the timeout expires. + * If interrupts are not enabled then delay for a reasonable amount + * of time and return. + */ + if (gpi.iic_irq > 0) { + cli(); + if (iic_pending == 0) { + interruptible_sleep_on_timeout(&iic_wait, timeout*HZ ); + } else + iic_pending = 0; + sti(); + } else { + udelay(100); + } +} + + +static void iic_ite_handler(int this_irq, void *dev_id, struct pt_regs *regs) +{ + + iic_pending = 1; + + DEB2(printk("iic_ite_handler: in interrupt handler\n")); + wake_up_interruptible(&iic_wait); +} + + +/* Lock the region of memory where I/O registers exist. Request our + * interrupt line and register its associated handler. + */ +static int iic_hw_resrc_init(void) +{ + if (check_region(gpi.iic_base, ITE_IIC_IO_SIZE) < 0 ) { + return -ENODEV; + } else { + request_region(gpi.iic_base, ITE_IIC_IO_SIZE, + "i2c (i2c bus adapter)"); + } + if (gpi.iic_irq > 0) { + if (request_irq(gpi.iic_irq, iic_ite_handler, 0, "ITE IIC", 0) < 0) { + gpi.iic_irq = 0; + } else + DEB3(printk("Enabled IIC IRQ %d\n", gpi.iic_irq)); + enable_irq(gpi.iic_irq); + } + return 0; +} + + +static void iic_ite_release(void) +{ + if (gpi.iic_irq > 0) { + disable_irq(gpi.iic_irq); + free_irq(gpi.iic_irq, 0); + } + release_region(gpi.iic_base , 2); +} + + +static int iic_ite_reg(struct i2c_client *client) +{ + return 0; +} + + +static int iic_ite_unreg(struct i2c_client *client) +{ + return 0; +} + + +static void iic_ite_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + + +static void iic_ite_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_iic_data iic_ite_data = { + NULL, + iic_ite_setiic, + iic_ite_getiic, + iic_ite_getown, + iic_ite_getclock, + iic_ite_waitforpin, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter iic_ite_ops = { + "ITE IIC adapter", + I2C_HW_I_IIC, + NULL, + &iic_ite_data, + iic_ite_inc_use, + iic_ite_dec_use, + iic_ite_reg, + iic_ite_unreg, +}; + +/* Called when the module is loaded. This function starts the + * cascade of calls up through the heirarchy of i2c modules (i.e. up to the + * algorithm layer and into to the core layer) + */ +static int __init iic_ite_init(void) +{ + + struct iic_ite *piic = &gpi; + + printk(KERN_INFO "Initialize ITE IIC adapter module\n"); + if (base == 0) + piic->iic_base = DEFAULT_BASE; + else + piic->iic_base = base; + + if (irq == 0) + piic->iic_irq = DEFAULT_IRQ; + else + piic->iic_irq = irq; + + if (clock == 0) + piic->iic_clock = DEFAULT_CLOCK; + else + piic->iic_clock = clock; + + if (own == 0) + piic->iic_own = DEFAULT_OWN; + else + piic->iic_own = own; + + iic_ite_data.data = (void *)piic; +#if (LINUX_VERSION_CODE >= 0x020301) + init_waitqueue_head(&iic_wait); +#endif + if (iic_hw_resrc_init() == 0) { + if (i2c_iic_add_bus(&iic_ite_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + printk(KERN_INFO " found device at %#x irq %d.\n", + piic->iic_base, piic->iic_irq); + return 0; +} + + +static void iic_ite_exit(void) +{ + i2c_iic_del_bus(&iic_ite_ops); + iic_ite_release(); +} + +EXPORT_NO_SYMBOLS; + +/* If modules is NOT defined when this file is compiled, then the MODULE_* + * macros will resolve to nothing + */ +MODULE_AUTHOR("MontaVista Software <www.mvista.com>"); +MODULE_DESCRIPTION("I2C-Bus adapter routines for ITE IIC bus adapter"); + +MODULE_PARM(base, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(clock, "i"); +MODULE_PARM(own, "i"); +MODULE_PARM(i2c_debug,"i"); + + +/* Called when module is loaded or when kernel is intialized. + * If MODULES is defined when this file is compiled, then this function will + * resolve to init_module (the function called when insmod is invoked for a + * module). Otherwise, this function is called early in the boot, when the + * kernel is intialized. Check out /include/init.h to see how this works. + */ +module_init(iic_ite_init); + +/* Resolves to module_cleanup when MODULES is defined. */ +module_exit(iic_ite_exit); diff --git a/drivers/i2c/i2c-algo-ite.c b/drivers/i2c/i2c-algo-ite.c new file mode 100644 index 000000000..194d1f78a --- /dev/null +++ b/drivers/i2c/i2c-algo-ite.c @@ -0,0 +1,872 @@ +/* + ------------------------------------------------------------------------- + i2c-algo-ite.c i2c driver algorithms for ITE adapters + + Hai-Pao Fan, MontaVista Software, Inc. + hpfan@mvista.com or source@mvista.com + + Copyright 2000 MontaVista Software Inc. + + --------------------------------------------------------------------------- + This file was highly leveraged from i2c-algo-pcf.c, which was created + by Simon G. Vogl and Hans Berglund: + + + Copyright (C) 1995-1997 Simon G. Vogl + 1998-2000 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and + Frodo Looijaard <frodol@dds.nl> ,and also from Martin Bailey + <mbailey@littlefeet-inc.com> */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/malloc.h> +#include <linux/version.h> +#include <linux/init.h> +#include <asm/uaccess.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/sched.h> + +#include <linux/i2c.h> +#include <linux/i2c-algo-ite.h> +#include "i2c-ite.h" + +#define PM_DSR IT8172_PCI_IO_BASE + IT_PM_DSR +#define PM_IBSR IT8172_PCI_IO_BASE + IT_PM_DSR + 0x04 +#define GPIO_CCR IT8172_PCI_IO_BASE + IT_GPCCR + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) x; + /* debug the protocol by showing transferred bits */ +#define DEF_TIMEOUT 16 + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=1; +static int iic_test=0; /* see if the line-setting functions work */ +static int iic_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define get_clock(adap) adap->getclock(adap->data) +#define iic_outw(adap, reg, val) adap->setiic(adap->data, reg, val) +#define iic_inw(adap, reg) adap->getiic(adap->data, reg) + + +/* --- other auxiliary functions -------------------------------------- */ + +static void iic_start(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap,ITE_I2CHCR,ITE_CMD); +} + +static void iic_stop(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap,ITE_I2CHCR,0); + iic_outw(adap,ITE_I2CHSR,ITE_I2CHSR_TDI); +} + +static void iic_reset(struct i2c_algo_iic_data *adap) +{ + iic_outw(adap, PM_IBSR, iic_inw(adap, PM_IBSR) | 0x80); +} + + +static int wait_for_bb(struct i2c_algo_iic_data *adap) +{ + int timeout = DEF_TIMEOUT; + short status; + + status = iic_inw(adap, ITE_I2CHSR); +#ifndef STUB_I2C + while (timeout-- && (status & ITE_I2CHSR_HB)) { + udelay(1000); /* How much is this? */ + status = iic_inw(adap, ITE_I2CHSR); + } +#endif + if (timeout<=0) { + printk(KERN_ERR "Timeout, host is busy\n"); + iic_reset(adap); + } + return(timeout<=0); +} + +/* + * Puts this process to sleep for a period equal to timeout + */ +static inline void iic_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} + +/* After we issue a transaction on the IIC bus, this function + * is called. It puts this process to sleep until we get an interrupt from + * from the controller telling us that the transaction we requested in complete. + */ +static int wait_for_pin(struct i2c_algo_iic_data *adap, short *status) { + + int timeout = DEF_TIMEOUT; + + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("Timeout waiting for host not busy\n");) + return -EIO; + } + timeout = DEF_TIMEOUT; + + *status = iic_inw(adap, ITE_I2CHSR); +#ifndef STUB_I2C + while (timeout-- && !(*status & ITE_I2CHSR_TDI)) { + adap->waitforpin(); + *status = iic_inw(adap, ITE_I2CHSR); + } +#endif + if (timeout <= 0) + return(-1); + else + return(0); +} + +static int wait_for_fe(struct i2c_algo_iic_data *adap, short *status) +{ + int timeout = DEF_TIMEOUT; + + *status = iic_inw(adap, ITE_I2CFSR); +#ifndef STUB_I2C + while (timeout-- && (*status & ITE_I2CFSR_FE)) { + udelay(1000); + iic_inw(adap, ITE_I2CFSR); + } +#endif + if (timeout <= 0) + return(-1); + else + return(0); +} + +static int iic_init (struct i2c_algo_iic_data *adap) +{ + short i; + + /* Clear bit 7 to set I2C to normal operation mode */ + i=iic_inw(adap, PM_DSR)& 0xff7f; + iic_outw(adap, PM_DSR, i); + + /* set IT_GPCCR port C bit 2&3 as function 2 */ + i = iic_inw(adap, GPIO_CCR) & 0xfc0f; + iic_outw(adap,GPIO_CCR,i); + + /* Clear slave address/sub-address */ + iic_outw(adap,ITE_I2CSAR, 0); + iic_outw(adap,ITE_I2CSSAR, 0); + + /* Set clock counter register */ + iic_outw(adap,ITE_I2CCKCNT, get_clock(adap)); + + /* Set START/reSTART/STOP time registers */ + iic_outw(adap,ITE_I2CSHDR, 0x0a); + iic_outw(adap,ITE_I2CRSUR, 0x0a); + iic_outw(adap,ITE_I2CPSUR, 0x0a); + + /* Enable interrupts on completing the current transaction */ + iic_outw(adap,ITE_I2CHCR, ITE_I2CHCR_IE | ITE_I2CHCR_HCE); + + /* Clear transfer count */ + iic_outw(adap,ITE_I2CFBCR, 0x0); + + DEB2(printk("iic_init: Initialized IIC on ITE 0x%x\n", + iic_inw(adap, ITE_I2CHSR))); + return 0; +} + + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_iic_data *adap, char *name) { +#if 0 + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("test_bus: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("test_bus: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("test_bus: %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("test_bus:1 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("test_bus: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("test_bus:2 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL unexpected low while SDA high!\n", + adap->name); + goto bailout; + } + scllo(adap); + printk("test_bus:3 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getscl(adap) ) { + + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("test_bus:4 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("test_bus: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("test_bus: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("test_bus: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +#endif + return (0); +} + +/* ----- Utility functions + */ + + +/* Verify the device we want to talk to on the IIC bus really exists. */ +static inline int try_address(struct i2c_algo_iic_data *adap, + unsigned int addr, int retries) +{ + int i, ret = -1; + short status; + + for (i=0;i<retries;i++) { + iic_outw(adap, ITE_I2CSAR, addr); + iic_start(adap); + if (wait_for_pin(adap, &status) == 0) { + if ((status & ITE_I2CHSR_DNE) == 0) { + iic_stop(adap); + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + ret=1; + break; /* success! */ + } + } + iic_stop(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("try_address: needed %d retries for 0x%x\n",i, + addr)); + return ret; +} + + +static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, + int count) +{ + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + int wrcount=0, timeout; + short status; + int loops, remainder, i, j; + union { + char byte[2]; + unsigned short word; + } tmp; + + iic_outw(adap, ITE_I2CSSAR, (unsigned short)buf[wrcount++]); + count--; + if (count == 0) + return -EIO; + + loops = count / 32; /* 32-byte FIFO */ + remainder = count % 32; + + if(loops) { + for(i=0; i<loops; i++) { + + iic_outw(adap, ITE_I2CFBCR, 32); + for(j=0; j<32/2; j++) { + tmp.byte[1] = buf[wrcount++]; + tmp.byte[0] = buf[wrcount++]; + iic_outw(adap, ITE_I2CFDR, tmp.word); + } + + /* status FIFO overrun */ + iic_inw(adap, ITE_I2CFSR); + iic_inw(adap, ITE_I2CFBCR); + + iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */ + + /* Wait for transmission to complete */ + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } + } + } + if(remainder) { + iic_outw(adap, ITE_I2CFBCR, remainder); + for(i=0; i<remainder/2; i++) { + tmp.byte[1] = buf[wrcount++]; + tmp.byte[0] = buf[wrcount++]; + iic_outw(adap, ITE_I2CFDR, tmp.word); + } + + /* status FIFO overrun */ + iic_inw(adap, ITE_I2CFSR); + iic_inw(adap, ITE_I2CFBCR); + + iic_outw(adap, ITE_I2CHCR, ITE_WRITE); /* Issue WRITE command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_sendbytes: %s write timeout.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_sendbytes: %s write error - no ack.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } +#endif + } + iic_stop(adap); + return wrcount; +} + + +static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count, + int sread) +{ + int rdcount=0, i, timeout; + short status; + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + int loops, remainder, j; + union { + char byte[2]; + unsigned short word; + } tmp; + + loops = count / 32; /* 32-byte FIFO */ + remainder = count % 32; + + if(loops) { + for(i=0; i<loops; i++) { + iic_outw(adap, ITE_I2CFBCR, 32); + if (sread) + iic_outw(adap, ITE_I2CHCR, ITE_SREAD); + else + iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s read timeout.\n", i2c_adap->name); + return (-1); + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name); + return (-1); + } +#endif + + timeout = wait_for_fe(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name); + return (-1); + } + + for(j=0; j<32/2; j++) { + tmp.word = iic_inw(adap, ITE_I2CFDR); + buf[rdcount++] = tmp.byte[1]; + buf[rdcount++] = tmp.byte[0]; + } + + /* status FIFO underrun */ + iic_inw(adap, ITE_I2CFSR); + + } + } + + + if(remainder) { + remainder=(remainder+1)/2 * 2; + iic_outw(adap, ITE_I2CFBCR, remainder); + if (sread) + iic_outw(adap, ITE_I2CHCR, ITE_SREAD); + else + iic_outw(adap, ITE_I2CHCR, ITE_READ); /* Issue READ command */ + + timeout = wait_for_pin(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s read timeout.\n", i2c_adap->name); + return (-1); + } +#ifndef STUB_I2C + if (status & ITE_I2CHSR_DB) { + iic_stop(adap); + printk("iic_readbytes: %s read error - no ack.\n", i2c_adap->name); + return (-1); + } +#endif + timeout = wait_for_fe(adap, &status); + if(timeout) { + iic_stop(adap); + printk("iic_readbytes: %s FIFO is empty\n", i2c_adap->name); + return (-1); + } + + for(i=0; i<(remainder+1)/2; i++) { + tmp.word = iic_inw(adap, ITE_I2CFDR); + buf[rdcount++] = tmp.byte[1]; + buf[rdcount++] = tmp.byte[0]; + } + + /* status FIFO underrun */ + iic_inw(adap, ITE_I2CFSR); + + } + + iic_stop(adap); + return rdcount; +} + + +/* This function implements combined transactions. Combined + * transactions consist of combinations of reading and writing blocks of data. + * Each transfer (i.e. a read or a write) is separated by a repeated start + * condition. + */ +#if 0 +static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + int i; + struct i2c_msg *pmsg; + int ret; + + DEB2(printk("Beginning combined transaction\n")); + + for(i=0; i<(num-1); i++) { + pmsg = &msgs[i]; + if(pmsg->flags & I2C_M_RD) { + DEB2(printk(" This one is a read\n")); + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER); + } + else if(!(pmsg->flags & I2C_M_RD)) { + DEB2(printk("This one is a write\n")); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_COMBINED_XFER); + } + } + /* Last read or write segment needs to be terminated with a stop */ + pmsg = &msgs[i]; + + if(pmsg->flags & I2C_M_RD) { + DEB2(printk("Doing the last read\n")); + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); + } + else if(!(pmsg->flags & I2C_M_RD)) { + DEB2(printk("Doing the last write\n")); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); + } + + return ret; +} +#endif + + +/* Whenever we initiate a transaction, the first byte clocked + * onto the bus after the start condition is the address (7 bit) of the + * device we want to talk to. This function manipulates the address specified + * so that it makes sense to the hardware when written to the IIC peripheral. + * + * Note: 10 bit addresses are not supported in this driver, although they are + * supported by the hardware. This functionality needs to be implemented. + */ +static inline int iic_doAddress(struct i2c_algo_iic_data *adap, + struct i2c_msg *msg, int retries) +{ + unsigned short flags = msg->flags; + unsigned int addr; + int ret; + +/* Ten bit addresses not supported right now */ + if ( (flags & I2C_M_TEN) ) { +#if 0 + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at extended address code.\n"); + return -EREMOTEIO; + } + iic_outw(adap,msg->addr & 0x7f); + if (ret != 1) { + printk("iic_doAddress: died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at extended address code.\n"); + return -EREMOTEIO; + } + } +#endif + } else { + + addr = ( msg->addr << 1 ); + +#if 0 + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; +#endif + + if (iic_inw(adap, ITE_I2CSAR) != addr) { + iic_outw(adap, ITE_I2CSAR, addr); + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("iic_doAddress: died at address code.\n"); + return -EREMOTEIO; + } + } + + } + + return 0; +} + + +/* Description: Prepares the controller for a transaction (clearing status + * registers, data buffers, etc), and then calls either iic_readbytes or + * iic_sendbytes to do the actual transaction. + * + * still to be done: Before we issue a transaction, we should + * verify that the bus is not busy or in some unknown state. + */ +static int iic_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i = 0; + int ret, timeout; + + pmsg = &msgs[i]; + + if(!pmsg->len) { + DEB2(printk("iic_xfer: read/write length is 0\n");) + return -EIO; + } + if(!(pmsg->flags & I2C_M_RD) && (!(pmsg->len)%2) ) { + DEB2(printk("iic_xfer: write buffer length is not odd\n");) + return -EIO; + } + + /* Wait for any pending transfers to complete */ + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("iic_xfer: Timeout waiting for host not busy\n");) + return -EIO; + } + + /* Flush FIFO */ + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + + /* Load address */ + ret = iic_doAddress(adap, pmsg, i2c_adap->retries); + if (ret) + return -EIO; + +#if 0 + /* Combined transaction (read and write) */ + if(num > 1) { + DEB2(printk("iic_xfer: Call combined transaction\n")); + ret = iic_combined_transaction(i2c_adap, msgs, num); + } +#endif + + DEB3(printk("iic_xfer: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", + i, msgs[i].addr, msgs[i].flags, msgs[i].len);) + + if(pmsg->flags & I2C_M_RD) /* Read */ + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, 0); + else { /* Write */ + udelay(1000); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len); + } + + if (ret != pmsg->len) + DEB3(printk("iic_xfer: error or fail on read/write %d bytes.\n",ret)); + else + DEB3(printk("iic_xfer: read/write %d bytes.\n",ret)); + + return ret; +} + + +/* Implements device specific ioctls. Higher level ioctls can + * be found in i2c-core.c and are typical of any i2c controller (specifying + * slave address, timeouts, etc). These ioctls take advantage of any hardware + * features built into the controller for which this algorithm-adapter set + * was written. These ioctls allow you to take control of the data and clock + * lines and set the either high or low, + * similar to a GPIO pin. + */ +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + + struct i2c_algo_iic_data *adap = adapter->algo_data; + struct i2c_iic_msg s_msg; + char *buf; + int ret; + + if (cmd == I2C_SREAD) { + if(copy_from_user(&s_msg, (struct i2c_iic_msg *)arg, + sizeof(struct i2c_iic_msg))) + return -EFAULT; + buf = kmalloc(s_msg.len, GFP_KERNEL); + if (buf== NULL) + return -ENOMEM; + + /* Flush FIFO */ + iic_outw(adap, ITE_I2CFCR, ITE_I2CFCR_FLUSH); + + /* Load address */ + iic_outw(adap, ITE_I2CSAR,s_msg.addr<<1); + iic_outw(adap, ITE_I2CSSAR,s_msg.waddr & 0xff); + + ret = iic_readbytes(adapter, buf, s_msg.len, 1); + if (ret>=0) { + if(copy_to_user( s_msg.buf, buf, s_msg.len) ) + ret = -EFAULT; + } + kfree(buf); + } + return 0; +} + + +static u32 iic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm iic_algo = { + "ITE IIC algorithm", + I2C_ALGO_IIC, + iic_xfer, /* master_xfer */ + NULL, /* smbus_xfer */ + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + iic_func, /* functionality */ +}; + + +/* + * registering functions to load algorithms at runtime + */ +int i2c_iic_add_bus(struct i2c_adapter *adap) +{ + int i; + short status; + struct i2c_algo_iic_data *iic_adap = adap->algo_data; + + if (iic_test) { + int ret = test_bus(iic_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-ite: hw routines for %s registered.\n", + adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= iic_algo.id; + adap->algo = &iic_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + adap->flags = 0; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + i2c_add_adapter(adap); + iic_init(iic_adap); + + /* scan bus */ + /* By default scanning the bus is turned off. */ + if (iic_scan) { + printk(KERN_INFO " i2c-algo-ite: scanning bus %s.\n", + adap->name); + for (i = 0x00; i < 0xff; i+=2) { + iic_outw(iic_adap, ITE_I2CSAR, i); + iic_start(iic_adap); + if ( (wait_for_pin(iic_adap, &status) == 0) && + ((status & ITE_I2CHSR_DNE) == 0) ) { + printk(KERN_INFO "\n(%02x)\n",i>>1); + } else { + printk(KERN_INFO "."); + iic_reset(iic_adap); + } + udelay(iic_adap->udelay); + } + } + return 0; +} + + +int i2c_iic_del_bus(struct i2c_adapter *adap) +{ + int res; + if ((res = i2c_del_adapter(adap)) < 0) + return res; + DEB2(printk("i2c-algo-ite: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + + +int __init i2c_algo_iic_init (void) +{ + printk(KERN_INFO "ITE iic (i2c) algorithm module\n"); + return 0; +} + + +void i2c_algo_iic_exit(void) +{ + return; +} + + +EXPORT_SYMBOL(i2c_iic_add_bus); +EXPORT_SYMBOL(i2c_iic_del_bus); + +/* The MODULE_* macros resolve to nothing if MODULES is not defined + * when this file is compiled. + */ +MODULE_AUTHOR("MontaVista Software <www.mvista.com>"); +MODULE_DESCRIPTION("ITE iic algorithm"); + +MODULE_PARM(iic_test, "i"); +MODULE_PARM(iic_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available"); +MODULE_PARM_DESC(iic_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol"); + + +/* This function resolves to init_module (the function invoked when a module + * is loaded via insmod) when this file is compiled with MODULES defined. + * Otherwise (i.e. if you want this driver statically linked to the kernel), + * a pointer to this function is stored in a table and called + * during the intialization of the kernel (in do_basic_setup in /init/main.c) + * + * All this functionality is complements of the macros defined in linux/init.h + */ +module_init(i2c_algo_iic_init); + + +/* If MODULES is defined when this file is compiled, then this function will + * resolved to cleanup_module. + */ +module_exit(i2c_algo_iic_exit); diff --git a/drivers/i2c/i2c-ite.h b/drivers/i2c/i2c-ite.h new file mode 100644 index 000000000..a8ca3c9b5 --- /dev/null +++ b/drivers/i2c/i2c-ite.h @@ -0,0 +1,117 @@ +/* + -------------------------------------------------------------------- + i2c-ite.h: Global defines for the I2C controller on board the + ITE MIPS processor. + -------------------------------------------------------------------- + Hai-Pao Fan, MontaVista Software, Inc. + hpfan@mvista.com or source@mvista.com + + Copyright 2001 MontaVista Software Inc. + + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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. + + */ + +#ifndef I2C_ITE_H +#define I2C_ITE_H 1 + +#include <asm/it8172/it8172.h> + +/* I2C Registers */ +#define ITE_I2CHCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x30 +#define ITE_I2CHSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x34 +#define ITE_I2CSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x38 +#define ITE_I2CSSAR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x3c +#define ITE_I2CCKCNT IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x48 +#define ITE_I2CSHDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x4c +#define ITE_I2CRSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x50 +#define ITE_I2CPSUR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x54 + +#define ITE_I2CFDR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x70 +#define ITE_I2CFBCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x74 +#define ITE_I2CFCR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x78 +#define ITE_I2CFSR IT8172_PCI_IO_BASE + IT_I2C_BASE + 0x7c + + +/* Host Control Register ITE_I2CHCR */ +#define ITE_I2CHCR_HCE 0x01 /* Enable I2C Host Controller */ +#define ITE_I2CHCR_IE 0x02 /* Enable the interrupt after completing + the current transaction */ +#define ITE_I2CHCR_CP_W 0x00 /* bit2-4 000 - Write */ +#define ITE_I2CHCR_CP_R 0x08 /* 010 - Current address read */ +#define ITE_I2CHCR_CP_S 0x10 /* 100 - Sequential read */ +#define ITE_I2CHCR_ST 0x20 /* Initiates the I2C host controller to execute + the command and send the data programmed in + all required registers to I2C bus */ +#define ITE_CMD ITE_I2CHCR_HCE | ITE_I2CHCR_IE | ITE_I2CHCR_ST +#define ITE_WRITE ITE_CMD | ITE_I2CHCR_CP_W +#define ITE_READ ITE_CMD | ITE_I2CHCR_CP_R +#define ITE_SREAD ITE_CMD | ITE_I2CHCR_CP_S + +/* Host Status Register ITE_I2CHSR */ +#define ITE_I2CHSR_DB 0x01 /* Device is busy, receives NACK response except + in the first and last bytes */ +#define ITE_I2CHSR_DNE 0x02 /* Target address on I2C bus does not exist */ +#define ITE_I2CHSR_TDI 0x04 /* R/W Transaction on I2C bus was completed */ +#define ITE_I2CHSR_HB 0x08 /* Host controller is processing transactions */ +#define ITE_I2CHSR_FER 0x10 /* Error occurs in the FIFO */ + +/* Slave Address Register ITE_I2CSAR */ +#define ITE_I2CSAR_SA_MASK 0xfe /* Target I2C device address */ +#define ITE_I2CSAR_ASO 0x0100 /* Output 1/0 to I2CAS port when the + next slave address is addressed */ + +/* Slave Sub-address Register ITE_I2CSSAR */ +#define ITE_I2CSSAR_SUBA_MASK 0xff /* Target I2C device sub-address */ + +/* Clock Counter Register ITE_I2CCKCNT */ +#define ITE_I2CCKCNT_STOP 0x00 /* stop I2C clock */ +#define ITE_I2CCKCNT_HPCC_MASK 0x7f /* SCL high period counter */ +#define ITE_I2CCKCNT_LPCC_MASK 0x7f00 /* SCL low period counter */ + +/* START Hold Time Register ITE_I2CSHDR */ +/* value is counted based on 16 MHz internal clock */ +#define ITE_I2CSHDR_FM 0x0a /* START condition at fast mode */ +#define ITE_I2CSHDR_SM 0x47 /* START contition at standard mode */ + +/* (Repeated) START Setup Time Register ITE_I2CRSUR */ +/* value is counted based on 16 MHz internal clock */ +#define ITE_I2CRSUR_FM 0x0a /* repeated START condition at fast mode */ +#define ITE_I2CRSUR_SM 0x50 /* repeated START condition at standard mode */ + +/* STOP setup Time Register ITE_I2CPSUR */ + +/* FIFO Data Register ITE_I2CFDR */ +#define ITE_I2CFDR_MASK 0xff + +/* FIFO Byte Count Register ITE_I2CFBCR */ +#define ITE_I2CFBCR_MASK 0x3f + +/* FIFO Control Register ITE_I2CFCR */ +#define ITE_I2CFCR_FLUSH 0x01 /* Flush FIFO and reset the FIFO point + and I2CFSR */ +/* FIFO Status Register ITE_I2CFSR */ +#define ITE_I2CFSR_FO 0x01 /* FIFO is overrun when write */ +#define ITE_I2CFSR_FU 0x02 /* FIFO is underrun when read */ +#define ITE_I2CFSR_FF 0x04 /* FIFO is full when write */ +#define ITE_I2CFSR_FE 0x08 /* FIFO is empty when read */ + +#endif /* I2C_ITE_H */ diff --git a/drivers/ide/it8172.c b/drivers/ide/it8172.c index 937d3e6d1..e40952e08 100644 --- a/drivers/ide/it8172.c +++ b/drivers/ide/it8172.c @@ -5,7 +5,7 @@ * * Copyright 2000 MontaVista Software Inc. * Author: MontaVista Software, Inc. - * stevel@mvista.com or support@mvista.com + * stevel@mvista.com or source@mvista.com * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -57,50 +57,41 @@ unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name); void __init ide_init_it8172 (ide_hwif_t *hwif); -/* - * Based on settings done by AMI BIOS - * (might be usefull if drive is not registered in CMOS for any reason). - */ static void it8172_tune_drive (ide_drive_t *drive, byte pio) { unsigned long flags; u16 master_data; - byte slave_data; + u32 slave_data; int is_slave = (&HWIF(drive)->drives[1] == drive); - int master_port = HWIF(drive)->index ? 0x42 : 0x40; - int slave_port = 0x44; - /* ISP RTC */ - byte timings[][2] = { { 0, 0 }, - { 0, 0 }, - { 1, 0 }, - { 2, 1 }, - { 2, 3 }, }; - + int master_port = 0x40; + int slave_port = 0x44; + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data); + pci_read_config_dword(HWIF(drive)->pci_dev, slave_port, &slave_data); + + /* + * FIX! The DIOR/DIOW pulse width and recovery times in port 0x44 + * are being left at the default values of 8 PCI clocks (242 nsec + * for a 33 MHz clock). These can be safely shortened at higher + * PIO modes. + */ + if (is_slave) { - master_data = master_data | 0x4000; + master_data |= 0x4000; if (pio > 1) - /* enable PPE, IE and TIME */ - master_data = master_data | 0x0070; - pci_read_config_byte(HWIF(drive)->pci_dev, slave_port, &slave_data); - slave_data = slave_data & (HWIF(drive)->index ? 0x0f : 0xf0); - slave_data = slave_data | - ((timings[pio][0] << 2) | (timings[pio][1] - << (HWIF(drive)->index ? 4 : 0))); + /* enable PPE and IE */ + master_data |= 0x0060; } else { - master_data = master_data & 0xccf8; + master_data &= 0xc060; if (pio > 1) - /* enable PPE, IE and TIME */ - master_data = master_data | 0x0007; - master_data = master_data | (timings[pio][0] << 12) | - (timings[pio][1] << 8); + /* enable PPE and IE */ + master_data |= 0x0006; } + save_flags(flags); cli(); pci_write_config_word(HWIF(drive)->pci_dev, master_port, master_data); - if (is_slave) - pci_write_config_byte(HWIF(drive)->pci_dev, slave_port, slave_data); restore_flags(flags); } @@ -150,12 +141,22 @@ static int it8172_tune_chipset (ide_drive_t *drive, byte speed) pci_read_config_byte(dev, 0x48, ®48); pci_read_config_byte(dev, 0x4a, ®4a); + /* + * Setting the DMA cycle time to 2 or 3 PCI clocks (60 and 91 nsec + * at 33 MHz PCI clock) seems to cause BadCRC errors during DMA + * transfers on some drives, even though both numbers meet the minimum + * ATAPI-4 spec of 73 and 54 nsec for UDMA 1 and 2 respectively. + * So the faster times are just commented out here. The good news is + * that the slower cycle time has very little affect on transfer + * performance. + */ + switch(speed) { case XFER_UDMA_4: - case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_2: //u_speed = 2 << (drive->dn * 4); break; case XFER_UDMA_5: case XFER_UDMA_3: - case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_1: //u_speed = 1 << (drive->dn * 4); break; case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; case XFER_MW_DMA_2: case XFER_MW_DMA_1: @@ -164,25 +165,16 @@ static int it8172_tune_chipset (ide_drive_t *drive, byte speed) } if (speed >= XFER_UDMA_0) { - if (!(reg48 & u_flag)) - pci_write_config_byte(dev, 0x48, reg48|u_flag); - if (!(reg4a & u_speed)) { - pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); - pci_write_config_byte(dev, 0x4a, reg4a|u_speed); - } - } - if (speed < XFER_UDMA_0) { - if (reg48 & u_flag) - pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); - if (reg4a & a_speed) - pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); + pci_write_config_byte(dev, 0x48, reg48 | u_flag); + reg4a &= ~a_speed; + pci_write_config_byte(dev, 0x4a, reg4a | u_speed); + } else { + pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); + pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); } it8172_tune_drive(drive, it8172_dma_2_pio(speed)); -#if IT8172_DEBUG - printk("%s: %s drive%d\n", drive->name, ide_xfer_verbose(speed), drive->dn); -#endif if (!drive->init_speed) drive->init_speed = speed; err = ide_config_drive_speed(drive, speed); @@ -236,8 +228,10 @@ static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive) /* Other cases are done by generic IDE-DMA code. */ return ide_dmaproc(func, drive); } + #endif /* defined(CONFIG_BLK_DEV_IDEDMA) && (CONFIG_IT8172_TUNING) */ + unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name) { unsigned char progif; diff --git a/drivers/sound/ite8172.c b/drivers/sound/ite8172.c new file mode 100644 index 000000000..bf07c16ea --- /dev/null +++ b/drivers/sound/ite8172.c @@ -0,0 +1,1957 @@ +/* + * ite8172.c -- ITE IT8172G Sound Driver. + * + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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. + * + * + * Module command line parameters: + * + * Supported devices: + * /dev/dsp standard OSS /dev/dsp device + * /dev/mixer standard OSS /dev/mixer device + * + * Notes: + * + * 1. Much of the OSS buffer allocation, ioctl's, and mmap'ing are + * taken, slightly modified or not at all, from the ES1371 driver, + * so refer to the credits in es1371.c for those. The rest of the + * code (probe, open, read, write, the ISR, etc.) is new. + * 2. The following support is untested: + * * Memory mapping the audio buffers, and the ioctl controls that go + * with it. + * * S/PDIF output. + * 3. The following is not supported: + * * I2S input. + * * legacy audio mode. + * 4. Support for volume button interrupts is implemented but doesn't + * work yet. + * + * Revision history + * 02.08.2001 0.1 Initial release + */ +#include <linux/version.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/sound.h> +#include <linux/malloc.h> +#include <linux/soundcard.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/bitops.h> +#include <linux/proc_fs.h> +#include <linux/spinlock.h> +#include <linux/smp_lock.h> +#include <linux/ac97_codec.h> +#include <linux/wrapper.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <asm/uaccess.h> +#include <asm/hardirq.h> +#include <asm/it8172/it8172.h> + +/* --------------------------------------------------------------------- */ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define IT8172_DEBUG +#undef IT8172_VERBOSE_DEBUG +#define DBG(x) {} + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + + +/* + * Audio Controller register bit definitions follow. See + * include/asm/it8172/it8172.h for register offsets. + */ + +/* PCM Out Volume Reg */ +#define PCMOV_PCMOM (1<<15) /* PCM Out Mute default 1: mute */ +#define PCMOV_PCMRCG_BIT 8 /* PCM Right channel Gain */ +#define PCMOV_PCMRCG_MASK (0x1f<<PCMOV_PCMRCG_BIT) +#define PCMOV_PCMLCG_BIT 0 /* PCM Left channel gain */ +#define PCMOV_PCMLCG_MASK 0x1f + +/* FM Out Volume Reg */ +#define FMOV_FMOM (1<<15) /* FM Out Mute default 1: mute */ +#define FMOV_FMRCG_BIT 8 /* FM Right channel Gain */ +#define FMOV_FMRCG_MASK (0x1f<<FMOV_FMRCG_BIT) +#define FMOV_FMLCG_BIT 0 /* FM Left channel gain */ +#define FMOV_FMLCG_MASK 0x1f + +/* I2S Out Volume Reg */ +#define I2SV_I2SOM (1<<15) /* I2S Out Mute default 1: mute */ +#define I2SV_I2SRCG_BIT 8 /* I2S Right channel Gain */ +#define I2SV_I2SRCG_MASK (0x1f<<I2SV_I2SRCG_BIT) +#define I2SV_I2SLCG_BIT 0 /* I2S Left channel gain */ +#define I2SV_I2SLCG_MASK 0x1f + +/* Digital Recording Source Select Reg */ +#define DRSS_BIT 0 +#define DRSS_MASK 0x07 +#define DRSS_AC97_PRIM 0 +#define DRSS_FM 1 +#define DRSS_I2S 2 +#define DRSS_PCM 3 +#define DRSS_AC97_SEC 4 + +/* Playback/Capture Channel Control Registers */ +#define CC_SM (1<<15) /* Stereo, Mone 0: mono 1: stereo */ +#define CC_DF (1<<14) /* Data Format 0: 8 bit 1: 16 bit */ +#define CC_FMT_BIT 14 +#define CC_FMT_MASK (0x03<<CC_FMT_BIT) +#define CC_CF_BIT 12 /* Channel format (Playback only) */ +#define CC_CF_MASK (0x03<<CC_CF_BIT) +#define CC_CF_2 0 +#define CC_CF_4 (1<<CC_CF_BIT) +#define CC_CF_6 (2<<CC_CF_BIT) +#define CC_SR_BIT 8 /* sample Rate */ +#define CC_SR_MASK (0x0f<<CC_SR_BIT) +#define CC_SR_5500 0 +#define CC_SR_8000 (1<<CC_SR_BIT) +#define CC_SR_9600 (2<<CC_SR_BIT) +#define CC_SR_11025 (3<<CC_SR_BIT) +#define CC_SR_16000 (4<<CC_SR_BIT) +#define CC_SR_19200 (5<<CC_SR_BIT) +#define CC_SR_22050 (6<<CC_SR_BIT) +#define CC_SR_32000 (7<<CC_SR_BIT) +#define CC_SR_38400 (8<<CC_SR_BIT) +#define CC_SR_44100 (9<<CC_SR_BIT) +#define CC_SR_48000 (10<<CC_SR_BIT) +#define CC_CSP (1<<7) /* Channel stop + * 0: End of Current buffer + * 1: Immediately stop when rec stop */ +#define CC_CP (1<<6) /* Channel pause 0: normal, 1: pause */ +#define CC_CA (1<<5) /* Channel Action 0: Stop , 1: start */ +#define CC_CB2L (1<<2) /* Cur. buf. 2 xfr is last 0: No, 1: Yes */ +#define CC_CB1L (1<<1) /* Cur. buf. 1 xfr is last 0: No, 1: Yes */ +#define CC_DE 1 /* DFC/DFIFO Data Empty 1: empty, 0: not empty + * (Playback only) + */ + +/* Codec Control Reg */ +#define CODECC_GME (1<<9) /* AC97 GPIO Mode enable */ +#define CODECC_ATM (1<<8) /* AC97 ATE test mode 0: test 1: normal */ +#define CODECC_WR (1<<6) /* AC97 Warn reset 1: warm reset , 0: Normal */ +#define CODECC_CR (1<<5) /* AC97 Cold reset 1: Cold reset , 0: Normal */ + + +/* I2S Control Reg */ +#define I2SMC_SR_BIT 6 /* I2S Sampling rate + * 00: 48KHz, 01: 44.1 KHz, 10: 32 32 KHz */ +#define I2SMC_SR_MASK (0x03<<I2SMC_SR_BIT) +#define I2SMC_SR_48000 0 +#define I2SMC_SR_44100 (1<<I2SMC_SR_BIT) +#define I2SMC_SR_32000 (2<<I2SMC_SR_BIT) +#define I2SMC_SRSS (1<<5) /* Sample Rate Source Select 1:S/W, 0: H/W */ +#define I2SMC_I2SF_BIT 0 /* I2S Format */ +#define I2SMC_I2SF_MASK 0x03 +#define I2SMC_I2SF_DAC 0 +#define I2SMC_I2SF_ADC 2 +#define I2SMC_I2SF_I2S 3 + + +/* Volume up, Down, Mute */ +#define VS_VMP (1<<2) /* Volume mute 1: pushed, 0: not */ +#define VS_VDP (1<<1) /* Volume Down 1: pushed, 0: not */ +#define VS_VUP 1 /* Volime Up 1: pushed, 0: not */ + +/* SRC, Mixer test control/DFC status reg */ +#define SRCS_DPUSC (1<<5) /* DFC Playback underrun Status/clear */ +#define SRCS_DCOSC (1<<4) /* DFC Capture Overrun Status/clear */ +#define SRCS_SIS (1<<3) /* SRC input select 1: Mixer, 0: Codec I/F */ +#define SRCS_CDIS_BIT 0 /* Codec Data Input Select */ +#define SRCS_CDIS_MASK 0x07 +#define SRCS_CDIS_MIXER 0 +#define SRCS_CDIS_PCM 1 +#define SRCS_CDIS_I2S 2 +#define SRCS_CDIS_FM 3 +#define SRCS_CDIS_DFC 4 + + +/* Codec Index Reg command Port */ +#define CIRCP_CID_BIT 10 +#define CIRCP_CID_MASK (0x03<<CIRCP_CID_BIT) +#define CIRCP_CPS (1<<9) /* Command Port Status 0: ready, 1: busy */ +#define CIRCP_DPVF (1<<8) /* Data Port Valid Flag 0: invalis, 1: valid */ +#define CIRCP_RWC (1<<7) /* Read/write command */ +#define CIRCP_CIA_BIT 0 +#define CIRCP_CIA_MASK 0x007F /* Codec Index Address */ + +/* Test Mode Control/Test group Select Control */ + +/* General Control Reg */ +#define GC_VDC_BIT 6 /* Volume Division Control */ +#define GC_VDC_MASK (0x03<<GC_VDC_BIT) +#define GC_VDC_NONE 0 +#define GC_VDC_DIV2 (1<<GC_VDC_BIT) +#define GC_VDC_DIV4 (2<<GC_VDC_BIT) +#define GC_SOE (1<<2) /* S/PDIF Output enable */ +#define GC_SWR 1 /* Software warn reset */ + +/* Interrupt mask Control Reg */ +#define IMC_VCIM (1<<6) /* Volume CNTL interrupt mask */ +#define IMC_CCIM (1<<1) /* Capture Chan. iterrupt mask */ +#define IMC_PCIM 1 /* Playback Chan. interrupt mask */ + +/* Interrupt status/clear reg */ +#define ISC_VCI (1<<6) /* Volume CNTL interrupt 1: clears */ +#define ISC_CCI (1<<1) /* Capture Chan. interrupt 1: clears */ +#define ISC_PCI 1 /* Playback Chan. interrupt 1: clears */ + +/* misc stuff */ +#define POLL_COUNT 0x5000 + + +#define IT8172_MODULE_NAME "IT8172 audio" +#define PFX IT8172_MODULE_NAME ": " + + +/* --------------------------------------------------------------------- */ + +struct it8172_state { + /* list of it8172 devices */ + struct list_head devs; + + /* the corresponding pci_dev structure */ + struct pci_dev *dev; + + /* soundcore stuff */ + int dev_audio; + + /* hardware resources */ + unsigned long io; + unsigned int irq; + + /* PCI ID's */ + u16 vendor; + u16 device; + u8 rev; /* the chip revision */ + + /* options */ + int spdif_volume; /* S/PDIF output is enabled if != -1 */ + +#ifdef IT8172_DEBUG + /* debug /proc entry */ + struct proc_dir_entry *ps; + struct proc_dir_entry *ac97_ps; +#endif /* IT8172_DEBUG */ + + struct ac97_codec codec; + + unsigned short pcc, capcc; + unsigned dacrate, adcrate; + + spinlock_t lock; + struct semaphore open_sem; + mode_t open_mode; + wait_queue_head_t open_wait; + + struct dmabuf { + void *rawbuf; + dma_addr_t dmaaddr; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + void* nextIn; + void* nextOut; + int count; + int curBufPtr; + unsigned total_bytes; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned stopped:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dma_dac, dma_adc; +}; + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(devs); + +/* --------------------------------------------------------------------- */ + +extern inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +static void it8172_delay(int msec) +{ + unsigned long tmo; + signed long tmo2; + + if (in_interrupt()) + return; + + tmo = jiffies + (msec*HZ)/1000; + for (;;) { + tmo2 = tmo - jiffies; + if (tmo2 <= 0) + break; + schedule_timeout(tmo2); + } +} + + +static unsigned short +get_compat_rate(unsigned* rate) +{ + unsigned rate_out = *rate; + unsigned short sr; + + if (rate_out >= 46050) { + sr = CC_SR_48000; rate_out = 48000; + } else if (rate_out >= 41250) { + sr = CC_SR_44100; rate_out = 44100; + } else if (rate_out >= 35200) { + sr = CC_SR_38400; rate_out = 38400; + } else if (rate_out >= 27025) { + sr = CC_SR_32000; rate_out = 32000; + } else if (rate_out >= 20625) { + sr = CC_SR_22050; rate_out = 22050; + } else if (rate_out >= 17600) { + sr = CC_SR_19200; rate_out = 19200; + } else if (rate_out >= 13513) { + sr = CC_SR_16000; rate_out = 16000; + } else if (rate_out >= 10313) { + sr = CC_SR_11025; rate_out = 11025; + } else if (rate_out >= 8800) { + sr = CC_SR_9600; rate_out = 9600; + } else if (rate_out >= 6750) { + sr = CC_SR_8000; rate_out = 8000; + } else { + sr = CC_SR_5500; rate_out = 5500; + } + + *rate = rate_out; + return sr; +} + +static void set_adc_rate(struct it8172_state *s, unsigned rate) +{ + unsigned long flags; + unsigned short sr; + + sr = get_compat_rate(&rate); + + spin_lock_irqsave(&s->lock, flags); + s->capcc &= ~CC_SR_MASK; + s->capcc |= sr; + outw(s->capcc, s->io+IT_AC_CAPCC); + spin_unlock_irqrestore(&s->lock, flags); + + s->adcrate = rate; +} + + +static void set_dac_rate(struct it8172_state *s, unsigned rate) +{ + unsigned long flags; + unsigned short sr; + + sr = get_compat_rate(&rate); + + spin_lock_irqsave(&s->lock, flags); + s->pcc &= ~CC_SR_MASK; + s->pcc |= sr; + outw(s->pcc, s->io+IT_AC_PCC); + spin_unlock_irqrestore(&s->lock, flags); + + s->dacrate = rate; +} + + +/* --------------------------------------------------------------------- */ + +static u16 rdcodec(struct ac97_codec *codec, u8 addr) +{ + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned long flags; + unsigned short circp, data; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) + break; + if (i == POLL_COUNT) + printk(KERN_INFO PFX "rdcodec: codec ready poll expired!\n"); + + circp = addr & CIRCP_CIA_MASK; + circp |= (codec->id << CIRCP_CID_BIT); + circp |= CIRCP_RWC; // read command + outw(circp, s->io+IT_AC_CIRCP); + + /* now wait for the data */ + for (i = 0; i < POLL_COUNT; i++) + if (inw(s->io+IT_AC_CIRCP) & CIRCP_DPVF) + break; + if (i == POLL_COUNT) + printk(KERN_INFO PFX "rdcodec: read poll expired!\n"); + + data = inw(s->io+IT_AC_CIRDP); + spin_unlock_irqrestore(&s->lock, flags); + + return data; +} + + +static void wrcodec(struct ac97_codec *codec, u8 addr, u16 data) +{ + struct it8172_state *s = (struct it8172_state *)codec->private_data; + unsigned long flags; + unsigned short circp; + int i; + + spin_lock_irqsave(&s->lock, flags); + + for (i = 0; i < POLL_COUNT; i++) + if (!(inw(s->io+IT_AC_CIRCP) & CIRCP_CPS)) + break; + if (i == POLL_COUNT) + printk(KERN_INFO PFX "wrcodec: codec ready poll expired!\n"); + + circp = addr & CIRCP_CIA_MASK; + circp |= (codec->id << CIRCP_CID_BIT); + circp &= ~CIRCP_RWC; // write command + + outw(data, s->io+IT_AC_CIRDP); // send data first + outw(circp, s->io+IT_AC_CIRCP); + + spin_unlock_irqrestore(&s->lock, flags); +} + + +static void waitcodec(struct ac97_codec *codec) +{ + unsigned short temp; + + /* codec_wait is used to wait for a ready state after + an AC97_RESET. */ + it8172_delay(10); + + temp = rdcodec(codec, 0x26); + + // If power down, power up + if (temp & 0x3f00) { + // Power on + wrcodec(codec, 0x26, 0); + it8172_delay(100); + // Reread + temp = rdcodec(codec, 0x26); + } + + // Check if Codec REF,ANL,DAC,ADC ready***/ + if ((temp & 0x3f0f) != 0x000f) { + printk(KERN_INFO PFX "codec reg 26 status (0x%x) not ready!!\n", + temp); + return; + } +} + + +/* --------------------------------------------------------------------- */ + +extern inline void stop_adc(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + s->capcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); + s->capcc |= CC_CSP; + outw(s->capcc, s->io+IT_AC_CAPCC); + + // disable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_CCIM, s->io+IT_AC_IMC); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +extern inline void stop_dac(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; + + if (db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + s->pcc &= ~(CC_CA | CC_CP | CC_CB2L | CC_CB1L); + s->pcc |= CC_CSP; + outw(s->pcc, s->io+IT_AC_PCC); + + // disable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc | IMC_PCIM, s->io+IT_AC_IMC); + + db->stopped = 1; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_dac(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_dac; + unsigned long flags; + unsigned char imc; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + // reset Buffer 1 and 2 pointers to nextOut and nextOut+fragsize + buf1 = virt_to_bus(db->nextOut); + buf2 = buf1 + db->fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + outl(buf1, s->io+IT_AC_PCB1STA); + outl(buf2, s->io+IT_AC_PCB2STA); + db->curBufPtr = IT_AC_PCB1STA; + + // enable playback interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_PCIM, s->io+IT_AC_IMC); + + s->pcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); + s->pcc |= CC_CA; + outw(s->pcc, s->io+IT_AC_PCC); + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +static void start_adc(struct it8172_state *s) +{ + struct dmabuf* db = &s->dma_adc; + unsigned long flags; + unsigned char imc; + unsigned long buf1, buf2; + + if (!db->stopped) + return; + + spin_lock_irqsave(&s->lock, flags); + + // reset Buffer 1 and 2 pointers to nextIn and nextIn+fragsize + buf1 = virt_to_bus(db->nextIn); + buf2 = buf1 + db->fragsize; + if (buf2 >= db->dmaaddr + db->dmasize) + buf2 -= db->dmasize; + + outl(buf1, s->io+IT_AC_CAPB1STA); + outl(buf2, s->io+IT_AC_CAPB2STA); + db->curBufPtr = IT_AC_CAPB1STA; + + // enable capture interrupt + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_CCIM, s->io+IT_AC_IMC); + + s->capcc &= ~(CC_CSP | CC_CP | CC_CB2L | CC_CB1L); + s->capcc |= CC_CA; + outw(s->capcc, s->io+IT_AC_CAPCC); + + db->stopped = 0; + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* --------------------------------------------------------------------- */ + +#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +extern inline void dealloc_dmabuf(struct it8172_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_unreserve(page); + pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, + db->rawbuf, db->dmaaddr); + } + db->rawbuf = db->nextIn = db->nextOut = NULL; + db->mapped = db->ready = 0; +} + +static int prog_dmabuf(struct it8172_state *s, struct dmabuf *db, + unsigned rate, unsigned fmt, unsigned reg) +{ + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + if (!db->rawbuf) { + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((db->rawbuf = pci_alloc_consistent(s->dev, + PAGE_SIZE << order, + &db->dmaaddr))) + break; + if (!db->rawbuf) + return -ENOMEM; + db->buforder = order; + /* now mark the pages as reserved; + otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(db->rawbuf); page <= pend; page++) + mem_map_reserve(page); + } + + db->count = 0; + db->nextIn = db->nextOut = db->rawbuf; + + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? + db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + memset(db->rawbuf, (fmt & (CC_DF>>CC_FMT_BIT)) ? 0 : 0x80, db->dmasize); + + // set data length register + outw(db->fragsize, s->io+reg+2); + db->ready = 1; + + return 0; +} + +extern inline int prog_dmabuf_adc(struct it8172_state *s) +{ + stop_adc(s); + return prog_dmabuf(s, &s->dma_adc, s->adcrate, + (s->capcc & CC_FMT_MASK) >> CC_FMT_BIT, + IT_AC_CAPCC); +} + +extern inline int prog_dmabuf_dac(struct it8172_state *s) +{ + stop_dac(s); + return prog_dmabuf(s, &s->dma_dac, s->dacrate, + (s->pcc & CC_FMT_MASK) >> CC_FMT_BIT, + IT_AC_PCC); +} + + +/* hold spinlock for the following! */ + +static void it8172_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct it8172_state *s = (struct it8172_state *)dev_id; + struct dmabuf* dac = &s->dma_dac; + struct dmabuf* adc = &s->dma_adc; + unsigned char isc, vs; + unsigned short vol, mute; + unsigned long newptr; + + spin_lock(&s->lock); + + isc = inb(s->io+IT_AC_ISC); + + /* fastpath out, to ease interrupt sharing */ + if (!(isc & (ISC_VCI | ISC_CCI | ISC_PCI))) + return; + + /* clear audio interrupts first */ + outb(isc | ISC_VCI | ISC_CCI | ISC_PCI, s->io+IT_AC_ISC); + + /* handle volume button events */ + if (isc & ISC_VCI) { + vs = inb(s->io+IT_AC_VS); + outb(0, s->io+IT_AC_VS); + vol = inw(s->io+IT_AC_PCMOV); + mute = vol & PCMOV_PCMOM; + vol &= PCMOV_PCMLCG_MASK; + if ((vs & VS_VUP) && vol > 0) + vol--; + if ((vs & VS_VDP) && vol < 0x1f) + vol++; + vol |= (vol << PCMOV_PCMRCG_BIT); + if (vs & VS_VMP) + vol |= (mute ^ PCMOV_PCMOM); + outw(vol, s->io+IT_AC_PCMOV); + } + + /* update capture pointers */ + if (isc & ISC_CCI) { + if (adc->count > adc->dmasize - adc->fragsize) { + // Overrun. Stop ADC and log the error + stop_adc(s); + adc->error++; + printk(KERN_INFO PFX "adc overrun\n"); + } else { + newptr = virt_to_bus(adc->nextIn) + 2*adc->fragsize; + if (newptr >= adc->dmaaddr + adc->dmasize) + newptr -= adc->dmasize; + + outl(newptr, s->io+adc->curBufPtr); + adc->curBufPtr = (adc->curBufPtr == IT_AC_CAPB1STA) ? + IT_AC_CAPB2STA : IT_AC_CAPB1STA; + + adc->nextIn += adc->fragsize; + if (adc->nextIn >= adc->rawbuf + adc->dmasize) + adc->nextIn -= adc->dmasize; + + adc->count += adc->fragsize; + adc->total_bytes += adc->fragsize; + + /* wake up anybody listening */ + if (waitqueue_active(&adc->wait)) + wake_up_interruptible(&adc->wait); + } + } + + /* update playback pointers */ + if (isc & ISC_PCI) { + newptr = virt_to_bus(dac->nextOut) + 2*dac->fragsize; + if (newptr >= dac->dmaaddr + dac->dmasize) + newptr -= dac->dmasize; + + outl(newptr, s->io+dac->curBufPtr); + dac->curBufPtr = (dac->curBufPtr == IT_AC_PCB1STA) ? + IT_AC_PCB2STA : IT_AC_PCB1STA; + + dac->nextOut += dac->fragsize; + if (dac->nextOut >= dac->rawbuf + dac->dmasize) + dac->nextOut -= dac->dmasize; + + dac->count -= dac->fragsize; + dac->total_bytes += dac->fragsize; + + /* wake up anybody listening */ + if (waitqueue_active(&dac->wait)) + wake_up_interruptible(&dac->wait); + + if (dac->count <= 0) + stop_dac(s); + } + + spin_unlock(&s->lock); +} + +/* --------------------------------------------------------------------- */ + +static loff_t it8172_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + + +static int it8172_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct it8172_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct it8172_state, devs); + if (s->codec.dev_mixer == minor) + break; + } + file->private_data = s; + return 0; +} + +static int it8172_release_mixdev(struct inode *inode, struct file *file) +{ + return 0; +} + + +static int mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd, + unsigned long arg) +{ + return codec->mixer_ioctl(codec, cmd, arg); +} + +static int it8172_ioctl_mixdev(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct ac97_codec *codec = &s->codec; + + return mixdev_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations it8172_mixer_fops = { + owner: THIS_MODULE, + llseek: it8172_llseek, + ioctl: it8172_ioctl_mixdev, + open: it8172_open_mixdev, + release: it8172_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct it8172_state *s, int nonblock) +{ + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + + for (;;) { + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) + return -EBUSY; + tmo = 1000 * count / s->dacrate; + tmo >>= sample_shift[(s->pcc & CC_FMT_MASK) >> CC_FMT_BIT]; + it8172_delay(tmo); + } + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t it8172_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db = &s->dma_adc; + ssize_t ret; + unsigned long flags; + int cnt, bufcnt, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + // wait for samples in capture buffer + do { + spin_lock_irqsave(&s->lock, flags); + if (db->stopped) + start_adc(s); + avail = db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); + + cnt = count > avail ? avail : count; + bufcnt = cnt; + if (cnt % db->fragsize) { + // round count up to nearest fragment + int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize); + cnt = newcnt; + } + + // copy from nextOut to user + if (copy_to_user(buffer, db->nextOut, bufcnt)) { + if (!ret) + ret = -EFAULT; + return ret; + } + + spin_lock_irqsave(&s->lock, flags); + db->count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + + db->nextOut += cnt; + if (db->nextOut >= db->rawbuf + db->dmasize) + db->nextOut -= db->dmasize; + + count -= bufcnt; + buffer += bufcnt; + ret += bufcnt; + } // while (count > 0) + + return ret; +} + +static ssize_t it8172_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db = &s->dma_dac; + ssize_t ret; + unsigned long flags; + int cnt, bufcnt, avail; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (db->mapped) + return -ENXIO; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + // wait for space in playback buffer + do { + spin_lock_irqsave(&s->lock, flags); + avail = db->dmasize - db->count; + spin_unlock_irqrestore(&s->lock, flags); + if (avail <= 0) { + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + return ret; + } + interruptible_sleep_on(&db->wait); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + return ret; + } + } + } while (avail <= 0); + + cnt = count > avail ? avail : count; + // copy to nextIn + if (copy_from_user(db->nextIn, buffer, cnt)) { + if (!ret) + ret = -EFAULT; + return ret; + } + + bufcnt = cnt; + if (cnt % db->fragsize) { + // round count up to nearest fragment, and fill remainder of + // fragment with silence + int newcnt = db->fragsize * ((cnt + db->fragsize) / db->fragsize); + memset(db->nextIn + cnt, (s->pcc & CC_DF) ? 0 : 0x80, newcnt - cnt); + cnt = newcnt; + } + + spin_lock_irqsave(&s->lock, flags); + db->count += cnt; + if (db->stopped) + start_dac(s); + spin_unlock_irqrestore(&s->lock, flags); + + db->nextIn += cnt; + if (db->nextIn >= db->rawbuf + db->dmasize) + db->nextIn -= db->dmasize; + + count -= bufcnt; + buffer += bufcnt; + ret += bufcnt; + } // while (count > 0) + + return ret; +} + +/* No kernel lock - we have our own spinlock */ +static unsigned int it8172_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= + s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int it8172_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + struct dmabuf *db; + unsigned long size; + + lock_kernel(); + if (vma->vm_flags & VM_WRITE) + db = &s->dma_dac; + else if (vma->vm_flags & VM_READ) + db = &s->dma_adc; + else { + unlock_kernel(); + return -EINVAL; + } + if (vma->vm_pgoff != 0) { + unlock_kernel(); + return -EINVAL; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + unlock_kernel(); + return -EINVAL; + } + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), + size, vma->vm_page_prot)) { + unlock_kernel(); + return -EAGAIN; + } + db->mapped = 1; + unlock_kernel(); + return 0; +} + + +#ifdef IT8172_VERBOSE_DEBUG +static struct ioctl_str_t { + unsigned int cmd; + const char* str; +} ioctl_str[] = { + {SNDCTL_DSP_RESET, "SNDCTL_DSP_RESET"}, + {SNDCTL_DSP_SYNC, "SNDCTL_DSP_SYNC"}, + {SNDCTL_DSP_SPEED, "SNDCTL_DSP_SPEED"}, + {SNDCTL_DSP_STEREO, "SNDCTL_DSP_STEREO"}, + {SNDCTL_DSP_GETBLKSIZE, "SNDCTL_DSP_GETBLKSIZE"}, + {SNDCTL_DSP_SAMPLESIZE, "SNDCTL_DSP_SAMPLESIZE"}, + {SNDCTL_DSP_CHANNELS, "SNDCTL_DSP_CHANNELS"}, + {SOUND_PCM_WRITE_CHANNELS, "SOUND_PCM_WRITE_CHANNELS"}, + {SOUND_PCM_WRITE_FILTER, "SOUND_PCM_WRITE_FILTER"}, + {SNDCTL_DSP_POST, "SNDCTL_DSP_POST"}, + {SNDCTL_DSP_SUBDIVIDE, "SNDCTL_DSP_SUBDIVIDE"}, + {SNDCTL_DSP_SETFRAGMENT, "SNDCTL_DSP_SETFRAGMENT"}, + {SNDCTL_DSP_GETFMTS, "SNDCTL_DSP_GETFMTS"}, + {SNDCTL_DSP_SETFMT, "SNDCTL_DSP_SETFMT"}, + {SNDCTL_DSP_GETOSPACE, "SNDCTL_DSP_GETOSPACE"}, + {SNDCTL_DSP_GETISPACE, "SNDCTL_DSP_GETISPACE"}, + {SNDCTL_DSP_NONBLOCK, "SNDCTL_DSP_NONBLOCK"}, + {SNDCTL_DSP_GETCAPS, "SNDCTL_DSP_GETCAPS"}, + {SNDCTL_DSP_GETTRIGGER, "SNDCTL_DSP_GETTRIGGER"}, + {SNDCTL_DSP_SETTRIGGER, "SNDCTL_DSP_SETTRIGGER"}, + {SNDCTL_DSP_GETIPTR, "SNDCTL_DSP_GETIPTR"}, + {SNDCTL_DSP_GETOPTR, "SNDCTL_DSP_GETOPTR"}, + {SNDCTL_DSP_MAPINBUF, "SNDCTL_DSP_MAPINBUF"}, + {SNDCTL_DSP_MAPOUTBUF, "SNDCTL_DSP_MAPOUTBUF"}, + {SNDCTL_DSP_SETSYNCRO, "SNDCTL_DSP_SETSYNCRO"}, + {SNDCTL_DSP_SETDUPLEX, "SNDCTL_DSP_SETDUPLEX"}, + {SNDCTL_DSP_GETODELAY, "SNDCTL_DSP_GETODELAY"}, + {SNDCTL_DSP_GETCHANNELMASK, "SNDCTL_DSP_GETCHANNELMASK"}, + {SNDCTL_DSP_BIND_CHANNEL, "SNDCTL_DSP_BIND_CHANNEL"}, + {OSS_GETVERSION, "OSS_GETVERSION"}, + {SOUND_PCM_READ_RATE, "SOUND_PCM_READ_RATE"}, + {SOUND_PCM_READ_CHANNELS, "SOUND_PCM_READ_CHANNELS"}, + {SOUND_PCM_READ_BITS, "SOUND_PCM_READ_BITS"}, + {SOUND_PCM_READ_FILTER, "SOUND_PCM_READ_FILTER"} +}; +#endif + +static int it8172_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret, diff; + + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + +#ifdef IT8172_VERBOSE_DEBUG + for (count=0; count<sizeof(ioctl_str)/sizeof(ioctl_str[0]); count++) { + if (ioctl_str[count].cmd == cmd) + break; + } + if (count < sizeof(ioctl_str)/sizeof(ioctl_str[0])) + printk(KERN_INFO PFX "ioctl %s\n", ioctl_str[count].str); + else + printk(KERN_INFO PFX "ioctl unknown, 0x%x\n", cmd); +#endif + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | + DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.count = s->dma_dac.total_bytes = 0; + s->dma_dac.nextIn = s->dma_dac.nextOut = s->dma_dac.rawbuf; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.count = s->dma_adc.total_bytes = 0; + s->dma_adc.nextIn = s->dma_adc.nextOut = s->dma_adc.rawbuf; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + set_adc_rate(s, val); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + set_dac_rate(s, val); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user((file->f_mode & FMODE_READ) ? + s->adcrate : s->dacrate, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val) + s->capcc |= CC_SM; + else + s->capcc &= ~CC_SM; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val) + s->pcc |= CC_SM; + else + s->pcc &= ~CC_SM; + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val >= 2) { + val = 2; + s->capcc |= CC_SM; + } + else + s->capcc &= ~CC_SM; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + switch (val) { + case 1: + s->pcc &= ~CC_SM; + break; + case 2: + s->pcc |= CC_SM; + break; + default: + // FIX! support multichannel??? + val = 2; + s->pcc |= CC_SM; + break; + } + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + if (val == AFMT_S16_LE) + s->capcc |= CC_DF; + else { + val = AFMT_U8; + s->capcc &= ~CC_DF; + } + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + if (val == AFMT_S16_LE) + s->pcc |= CC_DF; + else { + val = AFMT_U8; + s->pcc &= ~CC_DF; + } + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + } else { + if (file->f_mode & FMODE_READ) + val = (s->capcc & CC_DF) ? AFMT_S16_LE : AFMT_U8; + else + val = (s->pcc & CC_DF) ? AFMT_S16_LE : AFMT_U8; + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + spin_lock_irqsave(&s->lock, flags); + if (file->f_mode & FMODE_READ && !s->dma_adc.stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && !s->dma_dac.stopped) + val |= PCM_ENABLE_OUTPUT; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) + start_adc(s); + else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) + start_dac(s); + else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + abinfo.fragsize = s->dma_dac.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + if (!s->dma_dac.stopped) + count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + abinfo.fragsize = s->dma_adc.fragsize; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_adc.count; + if (!s->dma_adc.stopped) + count += (s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + if (!s->dma_dac.stopped) + count -= (s->dma_dac.fragsize - inw(s->io+IT_AC_PCDL)); + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (!s->dma_adc.stopped) { + diff = s->dma_adc.fragsize - inw(s->io+IT_AC_CAPCDL); + count += diff; + cinfo.bytes += diff; + cinfo.ptr = inl(s->io+s->dma_adc.curBufPtr) - s->dma_adc.dmaaddr; + } else + cinfo.ptr = virt_to_bus(s->dma_adc.nextIn) - s->dma_adc.dmaaddr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (!s->dma_dac.stopped) { + diff = s->dma_dac.fragsize - inw(s->io+IT_AC_CAPCDL); + count -= diff; + cinfo.bytes += diff; + cinfo.ptr = inl(s->io+s->dma_dac.curBufPtr) - s->dma_dac.dmaaddr; + } else + cinfo.ptr = virt_to_bus(s->dma_dac.nextOut) - s->dma_dac.dmaaddr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(s->dma_dac.fragsize, (int *)arg); + else + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.subdivision = val; + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.subdivision = val; + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? + s->adcrate : s->dacrate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + if (file->f_mode & FMODE_READ) + return put_user((s->capcc & CC_SM) ? 2 : 1, (int *)arg); + else + return put_user((s->pcc & CC_SM) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + if (file->f_mode & FMODE_READ) + return put_user((s->capcc & CC_DF) ? 16 : 8, (int *)arg); + else + return put_user((s->pcc & CC_DF) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + + return mixdev_ioctl(&s->codec, cmd, arg); +} + + +static int it8172_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct it8172_state *s; + int ret; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct it8172_state, devs); + if (!((s->dev_audio ^ minor) & ~0xf)) + break; + } + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + spin_lock_irqsave(&s->lock, flags); + + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = + s->dma_adc.subdivision = s->dma_adc.total_bytes = 0; + s->capcc &= ~(CC_SM | CC_DF); + set_adc_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->capcc |= CC_DF; + outw(s->capcc, s->io+IT_AC_CAPCC); + if ((ret = prog_dmabuf_adc(s))) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = + s->dma_dac.subdivision = s->dma_dac.total_bytes = 0; + s->pcc &= ~(CC_SM | CC_DF); + set_dac_rate(s, 8000); + if ((minor & 0xf) == SND_DEV_DSP16) + s->pcc |= CC_DF; + outw(s->pcc, s->io+IT_AC_PCC); + if ((ret = prog_dmabuf_dac(s))) + return ret; + } + + spin_unlock_irqrestore(&s->lock, flags); + + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&s->open_sem); + return 0; +} + +static int it8172_release(struct inode *inode, struct file *file) +{ + struct it8172_state *s = (struct it8172_state *)file->private_data; + + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + up(&s->open_sem); + wake_up(&s->open_wait); + unlock_kernel(); + return 0; +} + +static /*const*/ struct file_operations it8172_audio_fops = { + owner: THIS_MODULE, + llseek: it8172_llseek, + read: it8172_read, + write: it8172_write, + poll: it8172_poll, + ioctl: it8172_ioctl, + mmap: it8172_mmap, + open: it8172_open, + release: it8172_release, +}; + + +/* --------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------- */ + +/* + * for debugging purposes, we'll create a proc device that dumps the + * CODEC chipstate + */ + +#ifdef IT8172_DEBUG +static int proc_it8172_dump (char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + struct it8172_state *s; + int cnt, len = 0; + + if (list_empty(&devs)) + return 0; + s = list_entry(devs.next, struct it8172_state, devs); + + /* print out header */ + len += sprintf(buf + len, "\n\t\tIT8172 Audio Debug\n\n"); + + // print out digital controller state + len += sprintf (buf + len, "IT8172 Audio Controller registers\n"); + len += sprintf (buf + len, "---------------------------------\n"); + cnt=0; + while (cnt < 0x72) { + if (cnt == IT_AC_PCB1STA || cnt == IT_AC_PCB2STA || + cnt == IT_AC_CAPB1STA || cnt == IT_AC_CAPB2STA || + cnt == IT_AC_PFDP) { + len+= sprintf (buf + len, "reg %02x = %08x\n", + cnt, inl(s->io+cnt)); + cnt += 4; + } else { + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, inw(s->io+cnt)); + cnt += 2; + } + } + + /* print out CODEC state */ + len += sprintf (buf + len, "\nAC97 CODEC registers\n"); + len += sprintf (buf + len, "----------------------\n"); + for (cnt=0; cnt <= 0x7e; cnt = cnt +2) + len+= sprintf (buf + len, "reg %02x = %04x\n", + cnt, rdcodec(&s->codec, cnt)); + + if (fpos >=len){ + *start = buf; + *eof =1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof =1; + return len; + +} +#endif /* IT8172_DEBUG */ + +/* --------------------------------------------------------------------- */ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 5 + +static int spdif[NR_DEVICE] = { 0, }; + +static unsigned int devindex = 0; + +MODULE_PARM(spdif, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(spdif, "if 1 the S/PDIF digital output is enabled"); + +MODULE_AUTHOR("Monta Vista Software, stevel@mvista.com"); +MODULE_DESCRIPTION("IT8172 AudioPCI97 Driver"); + +/* --------------------------------------------------------------------- */ + +static int __devinit it8172_probe(struct pci_dev *pcidev, + const struct pci_device_id *pciid) +{ + struct it8172_state *s; + int i, val; + unsigned short pcisr, vol; + unsigned char legacy, imc; + char proc_str[80]; + + if (pcidev->irq == 0) + return -1; + + if (!(s = kmalloc(sizeof(struct it8172_state), GFP_KERNEL))) { + printk(KERN_ERR PFX "alloc of device struct failed\n"); + return -1; + } + + memset(s, 0, sizeof(struct it8172_state)); + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->dev = pcidev; + s->io = pci_resource_start(pcidev, 0); + s->irq = pcidev->irq; + s->vendor = pcidev->vendor; + s->device = pcidev->device; + pci_read_config_byte(pcidev, PCI_REVISION_ID, &s->rev); + s->codec.private_data = s; + s->codec.id = 0; + s->codec.codec_read = rdcodec; + s->codec.codec_write = wrcodec; + s->codec.codec_wait = waitcodec; + + if (!request_region(s->io, pci_resource_len(pcidev,0), + IT8172_MODULE_NAME)) { + printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n", + s->io, s->io + pci_resource_len(pcidev,0)-1); + goto err_region; + } + if (request_irq(s->irq, it8172_interrupt, SA_INTERRUPT, + IT8172_MODULE_NAME, s)) { + printk(KERN_ERR PFX "irq %u in use\n", s->irq); + goto err_irq; + } + + printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq); + + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&it8172_audio_fops, -1)) < 0) + goto err_dev1; + if ((s->codec.dev_mixer = + register_sound_mixer(&it8172_mixer_fops, -1)) < 0) + goto err_dev2; + +#ifdef IT8172_DEBUG + /* intialize the debug proc device */ + s->ps = create_proc_read_entry(IT8172_MODULE_NAME, 0, NULL, + proc_it8172_dump, NULL); +#endif /* IT8172_DEBUG */ + + /* + * Reset the Audio device using the IT8172 PCI Reset register. This + * creates an audible double click on a speaker connected to Line-out. + */ + IT_IO_READ16(IT_PM_PCISR, pcisr); + pcisr |= IT_PM_PCISR_ACSR; + IT_IO_WRITE16(IT_PM_PCISR, pcisr); + /* wait up to 100msec for reset to complete */ + for (i=0; pcisr & IT_PM_PCISR_ACSR; i++) { + it8172_delay(10); + if (i == 10) + break; + IT_IO_READ16(IT_PM_PCISR, pcisr); + } + if (i == 10) { + printk(KERN_ERR PFX "chip reset timeout!\n"); + goto err_dev3; + } + + /* enable pci io and bus mastering */ + if (pci_enable_device(pcidev)) + goto err_dev3; + pci_set_master(pcidev); + + /* get out of legacy mode */ + pci_read_config_byte (pcidev, 0x40, &legacy); + pci_write_config_byte (pcidev, 0x40, legacy & ~1); + + s->spdif_volume = -1; + /* check to see if s/pdif mode is being requested */ + if (spdif[devindex]) { + printk(KERN_INFO PFX "enabling S/PDIF output\n"); + s->spdif_volume = 0; + outb(GC_SOE, s->io+IT_AC_GC); + } else { + printk(KERN_INFO PFX "disabling S/PDIF output\n"); + outb(0, s->io+IT_AC_GC); + } + + /* cold reset the AC97 */ + outw(CODECC_CR, s->io+IT_AC_CODECC); + udelay(1000); + outw(0, s->io+IT_AC_CODECC); + /* need to delay around 500msec(bleech) to give + some CODECs enough time to wakeup */ + it8172_delay(500); + + /* AC97 warm reset to start the bitclk */ + outw(CODECC_WR, s->io+IT_AC_CODECC); + udelay(1000); + outw(0, s->io+IT_AC_CODECC); + + /* codec init */ + if (!ac97_probe_codec(&s->codec)) + goto err_dev3; + + /* Enable Volume button interrupts */ + imc = inb(s->io+IT_AC_IMC); + outb(imc & ~IMC_VCIM, s->io+IT_AC_IMC); + + /* Un-mute PCM and FM out on the controller */ + vol = inw(s->io+IT_AC_PCMOV); + outw(vol & ~PCMOV_PCMOM, s->io+IT_AC_PCMOV); + vol = inw(s->io+IT_AC_FMOV); + outw(vol & ~FMOV_FMOM, s->io+IT_AC_FMOV); + + /* set channel defaults to 8-bit, mono, 8 Khz */ + s->pcc = 0; + s->capcc = 0; + set_dac_rate(s, 8000); + set_adc_rate(s, 8000); + + /* set mic to be the recording source */ + val = SOUND_MASK_MIC; + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val); + + /* mute master and PCM when in S/PDIF mode */ + if (s->spdif_volume != -1) { + val = 0x0000; + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_VOLUME, + (unsigned long)&val); + mixdev_ioctl(&s->codec, SOUND_MIXER_WRITE_PCM, + (unsigned long)&val); + } + +#ifdef IT8172_DEBUG + sprintf(proc_str, "driver/%s/%d/ac97", IT8172_MODULE_NAME, s->codec.id); + s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, + ac97_read_proc, &s->codec); +#endif + + /* store it in the driver field */ + pci_set_drvdata(pcidev, s); + pcidev->dma_mask = 0xffffffff; + /* put it into driver list */ + list_add_tail(&s->devs, &devs); + /* increment devindex */ + if (devindex < NR_DEVICE-1) + devindex++; + return 0; + + err_dev3: + unregister_sound_mixer(s->codec.dev_mixer); + err_dev2: + unregister_sound_dsp(s->dev_audio); + err_dev1: + printk(KERN_ERR PFX "cannot register misc device\n"); + free_irq(s->irq, s); + err_irq: + release_region(s->io, pci_resource_len(pcidev,0)); + err_region: + kfree(s); + return -1; +} + +static void __devinit it8172_remove(struct pci_dev *dev) +{ + struct it8172_state *s = pci_get_drvdata(dev); + + if (!s) + return; + list_del(&s->devs); +#ifdef IT8172_DEBUG + if (s->ps) + remove_proc_entry(IT8172_MODULE_NAME, NULL); +#endif /* IT8172_DEBUG */ + synchronize_irq(); + free_irq(s->irq, s); + release_region(s->io, pci_resource_len(dev,0)); + unregister_sound_dsp(s->dev_audio); + unregister_sound_mixer(s->codec.dev_mixer); + kfree(s); + pci_set_drvdata(dev, NULL); +} + + + +static struct pci_device_id id_table[] __devinitdata = { + { PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G_AUDIO, PCI_ANY_ID, + PCI_ANY_ID, 0, 0 }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, id_table); + +static struct pci_driver it8172_driver = { + name: IT8172_MODULE_NAME, + id_table: id_table, + probe: it8172_probe, + remove: it8172_remove +}; + +static int __init init_it8172(void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk("version v0.26 time " __TIME__ " " __DATE__ "\n"); + return pci_module_init(&it8172_driver); +} + +static void __exit cleanup_it8172(void) +{ + printk(KERN_INFO PFX "unloading\n"); + pci_unregister_driver(&it8172_driver); +} + +module_init(init_it8172); +module_exit(cleanup_it8172); + |