Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 8 Mar 2010 18:17:20 +0000 (10:17 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 8 Mar 2010 18:17:20 +0000 (10:17 -0800)
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6: (62 commits)
  msi-laptop: depends on RFKILL
  msi-laptop: Detect 3G device exists by standard ec command
  msi-laptop: Add resume method for set the SCM load again
  msi-laptop: Support some MSI 3G netbook that is need load SCM
  msi-laptop: Add threeg sysfs file for support query 3G state by standard 66/62 ec command
  msi-laptop: Support standard ec 66/62 command on MSI notebook and nebook
  Driver core: create lock/unlock functions for struct device
  sysfs: fix for thinko with sysfs_bin_attr_init()
  sysfs: Kill unused sysfs_sb variable.
  sysfs: Pass super_block to sysfs_get_inode
  driver core: Use sysfs_rename_link in device_rename
  sysfs: Implement sysfs_rename_link
  sysfs: Pack sysfs_dirent more tightly.
  sysfs: Serialize updates to the vfs inode
  sysfs: windfarm: init sysfs attributes
  sysfs: Use sysfs_attr_init and sysfs_bin_attr_init on module dynamic attributes
  sysfs: Document sysfs_attr_init and sysfs_bin_attr_init
  sysfs: Use sysfs_attr_init and sysfs_bin_attr_init on dynamic attributes
  sysfs: Use one lockdep class per sysfs attribute.
  sysfs: Only take active references on attributes.
  ...

114 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
arch/arm/mach-ep93xx/include/mach/ts72xx.h
arch/arm/mach-ep93xx/ts72xx.c
arch/powerpc/platforms/52xx/mpc52xx_gpt.c
arch/s390/include/asm/qdio.h
arch/s390/kernel/time.c
arch/s390/lib/Makefile
arch/s390/mm/cmm.c
drivers/char/hvc_iucv.c
drivers/s390/block/dasd.c
drivers/s390/block/dasd_3990_erp.c
drivers/s390/block/dasd_devmap.c
drivers/s390/block/dasd_diag.c
drivers/s390/block/dasd_eckd.c
drivers/s390/block/dasd_fba.c
drivers/s390/block/dasd_genhd.c
drivers/s390/block/dasd_int.h
drivers/s390/block/dasd_ioctl.c
drivers/s390/cio/device.c
drivers/s390/cio/qdio_debug.c
drivers/s390/cio/qdio_main.c
drivers/s390/net/Kconfig
drivers/s390/net/Makefile
drivers/s390/net/qeth_core_main.c
drivers/s390/net/smsgiucv.c
drivers/s390/net/smsgiucv.h
drivers/s390/net/smsgiucv_app.c [new file with mode: 0644]
drivers/s390/scsi/zfcp_qdio.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/acquirewdt.c
drivers/watchdog/advantechwdt.c
drivers/watchdog/adx_wdt.c
drivers/watchdog/alim1535_wdt.c
drivers/watchdog/alim7101_wdt.c
drivers/watchdog/ar7_wdt.c
drivers/watchdog/at32ap700x_wdt.c
drivers/watchdog/at91rm9200_wdt.c
drivers/watchdog/bcm47xx_wdt.c
drivers/watchdog/bfin_wdt.c
drivers/watchdog/booke_wdt.c
drivers/watchdog/coh901327_wdt.c
drivers/watchdog/cpu5wdt.c
drivers/watchdog/cpwd.c
drivers/watchdog/davinci_wdt.c
drivers/watchdog/ep93xx_wdt.c
drivers/watchdog/eurotechwdt.c
drivers/watchdog/gef_wdt.c
drivers/watchdog/geodewdt.c
drivers/watchdog/hpwdt.c
drivers/watchdog/i6300esb.c
drivers/watchdog/iTCO_wdt.c
drivers/watchdog/ib700wdt.c
drivers/watchdog/indydog.c
drivers/watchdog/it8712f_wdt.c
drivers/watchdog/it87_wdt.c
drivers/watchdog/ixp2000_wdt.c
drivers/watchdog/ixp4xx_wdt.c
drivers/watchdog/ks8695_wdt.c
drivers/watchdog/machzwd.c
drivers/watchdog/max63xx_wdt.c [new file with mode: 0644]
drivers/watchdog/mixcomwd.c
drivers/watchdog/mpc8xxx_wdt.c
drivers/watchdog/mpcore_wdt.c
drivers/watchdog/mv64x60_wdt.c
drivers/watchdog/pc87413_wdt.c
drivers/watchdog/pcwd.c
drivers/watchdog/pcwd_pci.c
drivers/watchdog/pcwd_usb.c
drivers/watchdog/pika_wdt.c
drivers/watchdog/pnx833x_wdt.c
drivers/watchdog/rc32434_wdt.c
drivers/watchdog/rdc321x_wdt.c
drivers/watchdog/riowd.c
drivers/watchdog/sbc_fitpc2_wdt.c
drivers/watchdog/sch311x_wdt.c
drivers/watchdog/stmp3xxx_wdt.c
drivers/watchdog/ts72xx_wdt.c [new file with mode: 0644]
drivers/watchdog/txx9wdt.c
drivers/watchdog/w83627hf_wdt.c
drivers/watchdog/w83977f_wdt.c
drivers/watchdog/wdrtas.c
drivers/watchdog/wdt.c
drivers/watchdog/wdt_pci.c
drivers/watchdog/wm831x_wdt.c
drivers/watchdog/wm8350_wdt.c
fs/fscache/Kconfig
include/linux/usb/audio.h
include/sound/asound.h
sound/core/timer.c
sound/isa/opti9xx/miro.c
sound/isa/opti9xx/opti92x-ad1848.c
sound/isa/sb/jazz16.c
sound/oss/coproc.h
sound/oss/v_midi.h
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_hdmi.c [new file with mode: 0644]
sound/pci/hda/patch_intelhdmi.c
sound/pci/hda/patch_nvhdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/oxygen/xonar_wm87x6.c
sound/pci/riptide/riptide.c
sound/soc/codecs/ak4104.c
sound/soc/soc-core.c
sound/usb/Kconfig
sound/usb/caiaq/midi.h
sound/usb/ua101.c
sound/usb/usbaudio.c
sound/usb/usbaudio.h
sound/usb/usbquirks.h

index 33df82e..bfcbbf8 100644 (file)
@@ -1812,7 +1812,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   Module snd-ua101
   ----------------
 
-    Module for the Edirol UA-101 audio/MIDI interface.
+    Module for the Edirol UA-101/UA-1000 audio/MIDI interfaces.
 
     This module supports multiple devices, autoprobe and hotplugging.
 
index 3bd934e..93107d8 100644 (file)
@@ -65,6 +65,8 @@
 #define TS72XX_RTC_DATA_PHYS_BASE      0x11700000
 #define TS72XX_RTC_DATA_SIZE           0x00001000
 
+#define TS72XX_WDT_CONTROL_PHYS_BASE   0x23800000
+#define TS72XX_WDT_FEED_PHYS_BASE      0x23c00000
 
 #ifndef __ASSEMBLY__
 
index 259f782..fac1ec7 100644 (file)
@@ -166,6 +166,26 @@ static struct platform_device ts72xx_rtc_device = {
        .num_resources  = 0,
 };
 
+static struct resource ts72xx_wdt_resources[] = {
+       {
+               .start  = TS72XX_WDT_CONTROL_PHYS_BASE,
+               .end    = TS72XX_WDT_CONTROL_PHYS_BASE + SZ_4K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+       {
+               .start  = TS72XX_WDT_FEED_PHYS_BASE,
+               .end    = TS72XX_WDT_FEED_PHYS_BASE + SZ_4K - 1,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device ts72xx_wdt_device = {
+       .name           = "ts72xx-wdt",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(ts72xx_wdt_resources),
+       .resource       = ts72xx_wdt_resources,
+};
+
 static struct ep93xx_eth_data ts72xx_eth_data = {
        .phy_id         = 1,
 };
@@ -175,6 +195,7 @@ static void __init ts72xx_init_machine(void)
        ep93xx_init_devices();
        ts72xx_register_flash();
        platform_device_register(&ts72xx_rtc_device);
+       platform_device_register(&ts72xx_wdt_device);
 
        ep93xx_register_eth(&ts72xx_eth_data, 1);
 }
index 6f8ebe1..072b948 100644 (file)
@@ -553,7 +553,7 @@ static ssize_t mpc52xx_wdt_write(struct file *file, const char __user *data,
        return 0;
 }
 
-static struct watchdog_info mpc5200_wdt_info = {
+static const struct watchdog_info mpc5200_wdt_info = {
        .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
        .identity       = WDT_IDENTITY,
 };
index c666bfe..9b04b11 100644 (file)
@@ -321,11 +321,6 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int,
 #define QDIO_ERROR_ACTIVATE_CHECK_CONDITION    0x40
 #define QDIO_ERROR_SLSB_STATE                  0x80
 
-/* for qdio_initialize */
-#define QDIO_INBOUND_0COPY_SBALS               0x01
-#define QDIO_OUTBOUND_0COPY_SBALS              0x02
-#define QDIO_USE_OUTBOUND_PCIS                 0x04
-
 /* for qdio_cleanup */
 #define QDIO_FLAG_CLEANUP_USING_CLEAR          0x01
 #define QDIO_FLAG_CLEANUP_USING_HALT           0x02
@@ -344,7 +339,6 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int,
  * @input_handler: handler to be called for input queues
  * @output_handler: handler to be called for output queues
  * @int_parm: interruption parameter
- * @flags: initialization flags
  * @input_sbal_addr_array:  address of no_input_qs * 128 pointers
  * @output_sbal_addr_array: address of no_output_qs * 128 pointers
  */
@@ -361,7 +355,6 @@ struct qdio_initialize {
        qdio_handler_t *input_handler;
        qdio_handler_t *output_handler;
        unsigned long int_parm;
-       unsigned long flags;
        void **input_sbal_addr_array;
        void **output_sbal_addr_array;
 };
index 75894c2..aa2483e 100644 (file)
@@ -73,15 +73,15 @@ unsigned long long monotonic_clock(void)
 }
 EXPORT_SYMBOL(monotonic_clock);
 
-void tod_to_timeval(__u64 todval, struct timespec *xtime)
+void tod_to_timeval(__u64 todval, struct timespec *xt)
 {
        unsigned long long sec;
 
        sec = todval >> 12;
        do_div(sec, 1000000);
-       xtime->tv_sec = sec;
+       xt->tv_sec = sec;
        todval -= (sec * 1000000) << 12;
-       xtime->tv_nsec = ((todval * 1000) >> 12);
+       xt->tv_nsec = ((todval * 1000) >> 12);
 }
 EXPORT_SYMBOL(tod_to_timeval);
 
@@ -216,8 +216,8 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock,
        ++vdso_data->tb_update_count;
        smp_wmb();
        vdso_data->xtime_tod_stamp = clock->cycle_last;
-       vdso_data->xtime_clock_sec = xtime.tv_sec;
-       vdso_data->xtime_clock_nsec = xtime.tv_nsec;
+       vdso_data->xtime_clock_sec = wall_time->tv_sec;
+       vdso_data->xtime_clock_nsec = wall_time->tv_nsec;
        vdso_data->wtom_clock_sec = wall_to_monotonic.tv_sec;
        vdso_data->wtom_clock_nsec = wall_to_monotonic.tv_nsec;
        smp_wmb();
index cd54a1c..761ab8b 100644 (file)
@@ -2,7 +2,8 @@
 # Makefile for s390-specific library files..
 #
 
-lib-y += delay.o string.o uaccess_std.o uaccess_pt.o usercopy.o
+lib-y += delay.o string.o uaccess_std.o uaccess_pt.o
+obj-y += usercopy.o
 obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o
 lib-$(CONFIG_64BIT) += uaccess_mvcos.o
 lib-$(CONFIG_SMP) += spinlock.o
index 76a3637..f16bd04 100644 (file)
@@ -374,7 +374,7 @@ static struct ctl_table cmm_dir_table[] = {
 #ifdef CONFIG_CMM_IUCV
 #define SMSG_PREFIX "CMM"
 static void
-cmm_smsg_target(char *from, char *msg)
+cmm_smsg_target(const char *from, char *msg)
 {
        long nr, seconds;
 
index 21681a8..37b0542 100644 (file)
@@ -139,6 +139,8 @@ struct hvc_iucv_private *hvc_iucv_get_private(uint32_t num)
  *
  * This function allocates a new struct iucv_tty_buffer element and, optionally,
  * allocates an internal data buffer with the specified size @size.
+ * The internal data buffer is always allocated with GFP_DMA which is
+ * required for receiving and sending data with IUCV.
  * Note: The total message size arises from the internal buffer size and the
  *      members of the iucv_tty_msg structure.
  * The function returns NULL if memory allocation has failed.
@@ -154,7 +156,7 @@ static struct iucv_tty_buffer *alloc_tty_buffer(size_t size, gfp_t flags)
 
        if (size > 0) {
                bufp->msg.length = MSG_SIZE(size);
-               bufp->mbuf = kmalloc(bufp->msg.length, flags);
+               bufp->mbuf = kmalloc(bufp->msg.length, flags | GFP_DMA);
                if (!bufp->mbuf) {
                        mempool_free(bufp, hvc_iucv_mempool);
                        return NULL;
@@ -237,7 +239,7 @@ static int hvc_iucv_write(struct hvc_iucv_private *priv,
        if (!rb->mbuf) { /* message not yet received ... */
                /* allocate mem to store msg data; if no memory is available
                 * then leave the buffer on the list and re-try later */
-               rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC);
+               rb->mbuf = kmalloc(rb->msg.length, GFP_ATOMIC | GFP_DMA);
                if (!rb->mbuf)
                        return -ENOMEM;
 
index 4951aa8..bbea90b 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/ebcdic.h>
 #include <asm/idals.h>
 #include <asm/itcw.h>
+#include <asm/diag.h>
 
 /* This is ugly... */
 #define PRINTK_HEADER "dasd:"
@@ -2212,6 +2213,13 @@ static int dasd_open(struct block_device *bdev, fmode_t mode)
                goto out;
        }
 
+       if ((mode & FMODE_WRITE) &&
+           (test_bit(DASD_FLAG_DEVICE_RO, &base->flags) ||
+            (base->features & DASD_FEATURE_READONLY))) {
+               rc = -EROFS;
+               goto out;
+       }
+
        return 0;
 
 out:
@@ -2289,6 +2297,34 @@ dasd_exit(void)
  * SECTION: common functions for ccw_driver use
  */
 
+/*
+ * Is the device read-only?
+ * Note that this function does not report the setting of the
+ * readonly device attribute, but how it is configured in z/VM.
+ */
+int dasd_device_is_ro(struct dasd_device *device)
+{
+       struct ccw_dev_id dev_id;
+       struct diag210 diag_data;
+       int rc;
+
+       if (!MACHINE_IS_VM)
+               return 0;
+       ccw_device_get_id(device->cdev, &dev_id);
+       memset(&diag_data, 0, sizeof(diag_data));
+       diag_data.vrdcdvno = dev_id.devno;
+       diag_data.vrdclen = sizeof(diag_data);
+       rc = diag210(&diag_data);
+       if (rc == 0 || rc == 2) {
+               return diag_data.vrdcvfla & 0x80;
+       } else {
+               DBF_EVENT(DBF_WARNING, "diag210 failed for dev=%04x with rc=%d",
+                         dev_id.devno, rc);
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(dasd_device_is_ro);
+
 static void dasd_generic_auto_online(void *data, async_cookie_t cookie)
 {
        struct ccw_device *cdev = data;
index 44796ba..51224f7 100644 (file)
@@ -1045,6 +1045,10 @@ dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
 
                erp->retries = 5;
 
+       } else if (sense[1] & SNS1_WRITE_INHIBITED) {
+               dev_err(&device->cdev->dev, "An I/O request was rejected"
+                       " because writing is inhibited\n");
+               erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
        } else {
                /* fatal error -  set status to FAILED
                   internal error 09 - Command Reject */
index d49766f..8e23919 100644 (file)
@@ -742,6 +742,7 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
              const char *buf, size_t count)
 {
        struct dasd_devmap *devmap;
+       struct dasd_device *device;
        int val;
        char *endp;
 
@@ -758,12 +759,14 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
                devmap->features |= DASD_FEATURE_READONLY;
        else
                devmap->features &= ~DASD_FEATURE_READONLY;
-       if (devmap->device)
-               devmap->device->features = devmap->features;
-       if (devmap->device && devmap->device->block
-           && devmap->device->block->gdp)
-               set_disk_ro(devmap->device->block->gdp, val);
+       device = devmap->device;
+       if (device) {
+               device->features = devmap->features;
+               val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags);
+       }
        spin_unlock(&dasd_devmap_lock);
+       if (device && device->block && device->block->gdp)
+               set_disk_ro(device->block->gdp, val);
        return count;
 }
 
index 6e14863..687f323 100644 (file)
@@ -145,12 +145,10 @@ dasd_diag_erp(struct dasd_device *device)
        mdsk_term_io(device);
        rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
        if (rc == 4) {
-               if (!(device->features & DASD_FEATURE_READONLY)) {
+               if (!(test_and_set_bit(DASD_FLAG_DEVICE_RO, &device->flags)))
                        pr_warning("%s: The access mode of a DIAG device "
                                   "changed to read-only\n",
                                   dev_name(&device->cdev->dev));
-                       device->features |= DASD_FEATURE_READONLY;
-               }
                rc = 0;
        }
        if (rc)
@@ -449,7 +447,7 @@ dasd_diag_check_device(struct dasd_device *device)
                rc = -EIO;
        } else {
                if (rc == 4)
-                       device->features |= DASD_FEATURE_READONLY;
+                       set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
                pr_info("%s: New DASD with %ld byte/block, total size %ld "
                        "KB%s\n", dev_name(&device->cdev->dev),
                        (unsigned long) block->bp_block,
index 1cca21a..01f4e7a 100644 (file)
@@ -1089,6 +1089,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        struct dasd_eckd_private *private;
        struct dasd_block *block;
        int is_known, rc;
+       int readonly;
 
        if (!ccw_device_is_pathgroup(device->cdev)) {
                dev_warn(&device->cdev->dev,
@@ -1182,15 +1183,20 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
        else
                private->real_cyl = private->rdc_data.no_cyl;
 
+       readonly = dasd_device_is_ro(device);
+       if (readonly)
+               set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
+
        dev_info(&device->cdev->dev, "New DASD %04X/%02X (CU %04X/%02X) "
-                "with %d cylinders, %d heads, %d sectors\n",
+                "with %d cylinders, %d heads, %d sectors%s\n",
                 private->rdc_data.dev_type,
                 private->rdc_data.dev_model,
                 private->rdc_data.cu_type,
                 private->rdc_data.cu_model.model,
                 private->real_cyl,
                 private->rdc_data.trk_per_cyl,
-                private->rdc_data.sec_per_trk);
+                private->rdc_data.sec_per_trk,
+                readonly ? ", read-only device" : "");
        return 0;
 
 out_err3:
@@ -2839,8 +2845,13 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp)
        char *psf_data, *rssd_result;
        struct dasd_ccw_req *cqr;
        struct ccw1 *ccw;
+       char psf0, psf1;
        int rc;
 
+       if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+               return -EACCES;
+       psf0 = psf1 = 0;
+
        /* Copy parms from caller */
        rc = -EFAULT;
        if (copy_from_user(&usrparm, argp, sizeof(usrparm)))
@@ -2869,12 +2880,8 @@ static int dasd_symm_io(struct dasd_device *device, void __user *argp)
                           (void __user *)(unsigned long) usrparm.psf_data,
                           usrparm.psf_data_len))
                goto out_free;
-
-       /* sanity check on syscall header */
-       if (psf_data[0] != 0x17 && psf_data[1] != 0xce) {
-               rc = -EINVAL;
-               goto out_free;
-       }
+       psf0 = psf_data[0];
+       psf1 = psf_data[1];
 
        /* setup CCWs for PSF + RSSD */
        cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 2 , 0, device);
@@ -2925,7 +2932,9 @@ out_free:
        kfree(rssd_result);
        kfree(psf_data);
 out:
-       DBF_DEV_EVENT(DBF_WARNING, device, "Symmetrix ioctl: rc=%d", rc);
+       DBF_DEV_EVENT(DBF_WARNING, device,
+                     "Symmetrix ioctl (0x%02x 0x%02x): rc=%d",
+                     (int) psf0, (int) psf1, rc);
        return rc;
 }
 
index 0f15244..37282b9 100644 (file)
@@ -124,6 +124,7 @@ dasd_fba_check_characteristics(struct dasd_device *device)
        struct dasd_fba_private *private;
        struct ccw_device *cdev = device->cdev;
        int rc;
+       int readonly;
 
        private = (struct dasd_fba_private *) device->private;
        if (!private) {
@@ -162,16 +163,21 @@ dasd_fba_check_characteristics(struct dasd_device *device)
                return rc;
        }
 
+       readonly = dasd_device_is_ro(device);
+       if (readonly)
+               set_bit(DASD_FLAG_DEVICE_RO, &device->flags);
+
        dev_info(&device->cdev->dev,
                 "New FBA DASD %04X/%02X (CU %04X/%02X) with %d MB "
-                "and %d B/blk\n",
+                "and %d B/blk%s\n",
                 cdev->id.dev_type,
                 cdev->id.dev_model,
                 cdev->id.cu_type,
                 cdev->id.cu_model,
                 ((private->rdc_data.blk_bdsa *
                   (private->rdc_data.blk_size >> 9)) >> 11),
-                private->rdc_data.blk_size);
+                private->rdc_data.blk_size,
+                readonly ? ", read-only device" : "");
        return 0;
 }
 
index 94f92a1..30a1ca3 100644 (file)
@@ -70,7 +70,8 @@ int dasd_gendisk_alloc(struct dasd_block *block)
        }
        len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26));
 
-       if (block->base->features & DASD_FEATURE_READONLY)
+       if (base->features & DASD_FEATURE_READONLY ||
+           test_bit(DASD_FLAG_DEVICE_RO, &base->flags))
                set_disk_ro(gdp, 1);
        gdp->private_data = block;
        gdp->queue = block->request_queue;
index ed73ce5..a91d4a9 100644 (file)
@@ -436,6 +436,10 @@ struct dasd_block {
 #define DASD_FLAG_OFFLINE      3       /* device is in offline processing */
 #define DASD_FLAG_EER_SNSS     4       /* A SNSS is required */
 #define DASD_FLAG_EER_IN_USE   5       /* A SNSS request is running */
+#define DASD_FLAG_DEVICE_RO    6       /* The device itself is read-only. Don't
+                                        * confuse this with the user specified
+                                        * read-only feature.
+                                        */
 
 void dasd_put_device_wake(struct dasd_device *);
 
@@ -609,6 +613,9 @@ char *dasd_get_sense(struct irb *);
 void dasd_device_set_stop_bits(struct dasd_device *, int);
 void dasd_device_remove_stop_bits(struct dasd_device *, int);
 
+int dasd_device_is_ro(struct dasd_device *);
+
+
 /* externals in dasd_devmap.c */
 extern int dasd_max_devindex;
 extern int dasd_probeonly;
index 7039d9c..3479f81 100644 (file)
@@ -199,7 +199,8 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp)
        if (!argp)
                return -EINVAL;
 
-       if (block->base->features & DASD_FEATURE_READONLY)
+       if (block->base->features & DASD_FEATURE_READONLY ||
+           test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
                return -EROFS;
        if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
                return -EFAULT;
@@ -349,7 +350,8 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
                return -EINVAL;
        if (get_user(intval, (int __user *)argp))
                return -EFAULT;
-
+       if (!intval && test_bit(DASD_FLAG_DEVICE_RO, &block->base->flags))
+               return -EROFS;
        set_disk_ro(bdev->bd_disk, intval);
        return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
 }
index c6abb75..6d229f3 100644 (file)
@@ -764,7 +764,7 @@ static void sch_create_and_recog_new_device(struct subchannel *sch)
 static void io_subchannel_register(struct ccw_device *cdev)
 {
        struct subchannel *sch;
-       int ret;
+       int ret, adjust_init_count = 1;
        unsigned long flags;
 
        sch = to_subchannel(cdev->dev.parent);
@@ -793,6 +793,7 @@ static void io_subchannel_register(struct ccw_device *cdev)
                                              cdev->private->dev_id.ssid,
                                              cdev->private->dev_id.devno);
                }
+               adjust_init_count = 0;
                goto out;
        }
        /*
@@ -818,7 +819,7 @@ out:
        cdev->private->flags.recog_done = 1;
        wake_up(&cdev->private->wait_q);
 out_err:
-       if (atomic_dec_and_test(&ccw_device_init_count))
+       if (adjust_init_count && atomic_dec_and_test(&ccw_device_init_count))
                wake_up(&ccw_device_init_wq);
 }
 
index c94eb2a..6ce83f5 100644 (file)
@@ -33,7 +33,6 @@ void qdio_allocate_dbf(struct qdio_initialize *init_data,
        DBF_HEX(&init_data->input_handler, sizeof(void *));
        DBF_HEX(&init_data->output_handler, sizeof(void *));
        DBF_HEX(&init_data->int_parm, sizeof(long));
-       DBF_HEX(&init_data->flags, sizeof(long));
        DBF_HEX(&init_data->input_sbal_addr_array, sizeof(void *));
        DBF_HEX(&init_data->output_sbal_addr_array, sizeof(void *));
        DBF_EVENT("irq:%8lx", (unsigned long)irq_ptr);
index 232ef04..4f8f743 100644 (file)
@@ -588,10 +588,11 @@ static void qdio_kick_handler(struct qdio_q *q)
        if (q->is_input_q) {
                qperf_inc(q, inbound_handler);
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%02x c:%02x", start, count);
-       } else
+       } else {
                qperf_inc(q, outbound_handler);
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x",
                              start, count);
+       }
 
        q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count,
                   q->irq_ptr->int_parm);
index cb909a5..977bb4d 100644 (file)
@@ -43,6 +43,16 @@ config SMSGIUCV
          Select this option if you want to be able to receive SMSG messages
          from other VM guest systems.
 
+config SMSGIUCV_EVENT
+       tristate "Deliver IUCV special messages as uevents (VM only)"
+       depends on SMSGIUCV
+       help
+         Select this option to deliver CP special messages (SMSGs) as
+         uevents.  The driver handles only those special messages that
+         start with "APP".
+
+         To compile as a module, choose M. The module name is "smsgiucv_app".
+
 config CLAW
        tristate "CLAW device support"
        depends on CCW && NETDEVICES
index 6cab5a6..4dfe8c1 100644 (file)
@@ -6,6 +6,7 @@ ctcm-y += ctcm_main.o ctcm_fsms.o ctcm_mpc.o ctcm_sysfs.o ctcm_dbug.o
 obj-$(CONFIG_CTCM) += ctcm.o fsm.o
 obj-$(CONFIG_NETIUCV) += netiucv.o fsm.o
 obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
+obj-$(CONFIG_SMSGIUCV_EVENT) += smsgiucv_app.o
 obj-$(CONFIG_LCS) += lcs.o
 obj-$(CONFIG_CLAW) += claw.o
 qeth-y += qeth_core_sys.o qeth_core_main.o qeth_core_mpc.o
index fa8a519..7d25bdd 100644 (file)
@@ -3805,9 +3805,6 @@ static int qeth_qdio_establish(struct qeth_card *card)
        init_data.input_handler          = card->discipline.input_handler;
        init_data.output_handler         = card->discipline.output_handler;
        init_data.int_parm               = (unsigned long) card;
-       init_data.flags                  = QDIO_INBOUND_0COPY_SBALS |
-                                          QDIO_OUTBOUND_0COPY_SBALS |
-                                          QDIO_USE_OUTBOUND_PCIS;
        init_data.input_sbal_addr_array  = (void **) in_sbal_ptrs;
        init_data.output_sbal_addr_array = (void **) out_sbal_ptrs;
 
index 67f2485..ecef1ed 100644 (file)
@@ -31,9 +31,9 @@
 
 struct smsg_callback {
        struct list_head list;
-       char *prefix;
+       const char *prefix;
        int len;
-       void (*callback)(char *from, char *str);
+       void (*callback)(const char *from, char *str);
 };
 
 MODULE_AUTHOR
@@ -100,8 +100,8 @@ static void smsg_message_pending(struct iucv_path *path,
        kfree(buffer);
 }
 
-int smsg_register_callback(char *prefix,
-                          void (*callback)(char *from, char *str))
+int smsg_register_callback(const char *prefix,
+                          void (*callback)(const char *from, char *str))
 {
        struct smsg_callback *cb;
 
@@ -117,8 +117,9 @@ int smsg_register_callback(char *prefix,
        return 0;
 }
 
-void smsg_unregister_callback(char *prefix,
-                             void (*callback)(char *from, char *str))
+void smsg_unregister_callback(const char *prefix,
+                             void (*callback)(const char *from,
+                                              char *str))
 {
        struct smsg_callback *cb, *tmp;
 
@@ -176,7 +177,7 @@ static const struct dev_pm_ops smsg_pm_ops = {
 
 static struct device_driver smsg_driver = {
        .owner = THIS_MODULE,
-       .name = "SMSGIUCV",
+       .name = SMSGIUCV_DRV_NAME,
        .bus  = &iucv_bus,
        .pm = &smsg_pm_ops,
 };
index 67f5d4f..149a115 100644 (file)
@@ -5,6 +5,10 @@
  * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
  */
 
-int  smsg_register_callback(char *, void (*)(char *, char *));
-void smsg_unregister_callback(char *, void (*)(char *, char *));
+#define SMSGIUCV_DRV_NAME     "SMSGIUCV"
+
+int  smsg_register_callback(const char *,
+                           void (*)(const char *, char *));
+void smsg_unregister_callback(const char *,
+                             void (*)(const char *, char *));
 
diff --git a/drivers/s390/net/smsgiucv_app.c b/drivers/s390/net/smsgiucv_app.c
new file mode 100644 (file)
index 0000000..91579dc
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Deliver z/VM CP special messages (SMSG) as uevents.
+ *
+ * The driver registers for z/VM CP special messages with the
+ * "APP" prefix. Incoming messages are delivered to user space
+ * as uevents.
+ *
+ * Copyright IBM Corp. 2010
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ *
+ */
+#define KMSG_COMPONENT         "smsgiucv_app"
+#define pr_fmt(fmt)            KMSG_COMPONENT ": " fmt
+
+#include <linux/ctype.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <net/iucv/iucv.h>
+#include "smsgiucv.h"
+
+/* prefix used for SMSG registration */
+#define SMSG_PREFIX            "APP"
+
+/* SMSG related uevent environment variables */
+#define ENV_SENDER_STR         "SMSG_SENDER="
+#define ENV_SENDER_LEN         (strlen(ENV_SENDER_STR) + 8 + 1)
+#define ENV_PREFIX_STR         "SMSG_ID="
+#define ENV_PREFIX_LEN         (strlen(ENV_PREFIX_STR) + \
+                                strlen(SMSG_PREFIX) + 1)
+#define ENV_TEXT_STR           "SMSG_TEXT="
+#define ENV_TEXT_LEN(msg)      (strlen(ENV_TEXT_STR) + strlen((msg)) + 1)
+
+/* z/VM user ID which is permitted to send SMSGs
+ * If the value is undefined or empty (""), special messages are
+ * accepted from any z/VM user ID. */
+static char *sender;
+module_param(sender, charp, 0400);
+MODULE_PARM_DESC(sender, "z/VM user ID from which CP SMSGs are accepted");
+
+/* SMSG device representation */
+static struct device *smsg_app_dev;
+
+/* list element for queuing received messages for delivery */
+struct smsg_app_event {
+       struct list_head list;
+       char *buf;
+       char *envp[4];
+};
+
+/* queue for outgoing uevents */
+static LIST_HEAD(smsg_event_queue);
+static DEFINE_SPINLOCK(smsg_event_queue_lock);
+
+static void smsg_app_event_free(struct smsg_app_event *ev)
+{
+       kfree(ev->buf);
+       kfree(ev);
+}
+
+static struct smsg_app_event *smsg_app_event_alloc(const char *from,
+                                                  const char *msg)
+{
+       struct smsg_app_event *ev;
+
+       ev = kzalloc(sizeof(*ev), GFP_ATOMIC);
+       if (!ev)
+               return NULL;
+
+       ev->buf = kzalloc(ENV_SENDER_LEN + ENV_PREFIX_LEN +
+                         ENV_TEXT_LEN(msg), GFP_ATOMIC);
+       if (!ev->buf) {
+               kfree(ev);
+               return NULL;
+       }
+
+       /* setting up environment pointers into buf */
+       ev->envp[0] = ev->buf;
+       ev->envp[1] = ev->envp[0] + ENV_SENDER_LEN;
+       ev->envp[2] = ev->envp[1] + ENV_PREFIX_LEN;
+       ev->envp[3] = NULL;
+
+       /* setting up environment: sender, prefix name, and message text */
+       snprintf(ev->envp[0], ENV_SENDER_LEN, ENV_SENDER_STR "%s", from);
+       snprintf(ev->envp[1], ENV_PREFIX_LEN, ENV_PREFIX_STR "%s", SMSG_PREFIX);
+       snprintf(ev->envp[2], ENV_TEXT_LEN(msg), ENV_TEXT_STR "%s", msg);
+
+       return ev;
+}
+
+static void smsg_event_work_fn(struct work_struct *work)
+{
+       LIST_HEAD(event_queue);
+       struct smsg_app_event *p, *n;
+       struct device *dev;
+
+       dev = get_device(smsg_app_dev);
+       if (!dev)
+               return;
+
+       spin_lock_bh(&smsg_event_queue_lock);
+       list_splice_init(&smsg_event_queue, &event_queue);
+       spin_unlock_bh(&smsg_event_queue_lock);
+
+       list_for_each_entry_safe(p, n, &event_queue, list) {
+               list_del(&p->list);
+               kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, p->envp);
+               smsg_app_event_free(p);
+       }
+
+       put_device(dev);
+}
+static DECLARE_WORK(smsg_event_work, smsg_event_work_fn);
+
+static void smsg_app_callback(const char *from, char *msg)
+{
+       struct smsg_app_event *se;
+
+       /* check if the originating z/VM user ID matches
+        * the configured sender. */
+       if (sender && strlen(sender) > 0 && strcmp(from, sender) != 0)
+               return;
+
+       /* get start of message text (skip prefix and leading blanks) */
+       msg += strlen(SMSG_PREFIX);
+       while (*msg && isspace(*msg))
+               msg++;
+       if (*msg == '\0')
+               return;
+
+       /* allocate event list element and its environment */
+       se = smsg_app_event_alloc(from, msg);
+       if (!se)
+               return;
+
+       /* queue event and schedule work function */
+       spin_lock(&smsg_event_queue_lock);
+       list_add_tail(&se->list, &smsg_event_queue);
+       spin_unlock(&smsg_event_queue_lock);
+
+       schedule_work(&smsg_event_work);
+       return;
+}
+
+static int __init smsgiucv_app_init(void)
+{
+       struct device_driver *smsgiucv_drv;
+       int rc;
+
+       if (!MACHINE_IS_VM)
+               return -ENODEV;
+
+       smsg_app_dev = kzalloc(sizeof(*smsg_app_dev), GFP_KERNEL);
+       if (!smsg_app_dev)
+               return -ENOMEM;
+
+       smsgiucv_drv = driver_find(SMSGIUCV_DRV_NAME, &iucv_bus);
+       if (!smsgiucv_drv) {
+               kfree(smsg_app_dev);
+               return -ENODEV;
+       }
+
+       rc = dev_set_name(smsg_app_dev, KMSG_COMPONENT);
+       if (rc) {
+               kfree(smsg_app_dev);
+               goto fail_put_driver;
+       }
+       smsg_app_dev->bus = &iucv_bus;
+       smsg_app_dev->parent = iucv_root;
+       smsg_app_dev->release = (void (*)(struct device *)) kfree;
+       smsg_app_dev->driver = smsgiucv_drv;
+       rc = device_register(smsg_app_dev);
+       if (rc) {
+               put_device(smsg_app_dev);
+               goto fail_put_driver;
+       }
+
+       /* register with the smsgiucv device driver */
+       rc = smsg_register_callback(SMSG_PREFIX, smsg_app_callback);
+       if (rc) {
+               device_unregister(smsg_app_dev);
+               goto fail_put_driver;
+       }
+
+       rc = 0;
+fail_put_driver:
+       put_driver(smsgiucv_drv);
+       return rc;
+}
+module_init(smsgiucv_app_init);
+
+static void __exit smsgiucv_app_exit(void)
+{
+       /* unregister callback */
+       smsg_unregister_callback(SMSG_PREFIX, smsg_app_callback);
+
+       /* cancel pending work and flush any queued event work */
+       cancel_work_sync(&smsg_event_work);
+       smsg_event_work_fn(&smsg_event_work);
+
+       device_unregister(smsg_app_dev);
+}
+module_exit(smsgiucv_app_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Deliver z/VM CP SMSG as uevents");
+MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");
index 71b97ff..6479273 100644 (file)
@@ -319,8 +319,6 @@ static void zfcp_qdio_setup_init_data(struct qdio_initialize *id,
        id->input_handler = zfcp_qdio_int_resp;
        id->output_handler = zfcp_qdio_int_req;
        id->int_parm = (unsigned long) qdio;
-       id->flags = QDIO_INBOUND_0COPY_SBALS |
-                   QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
        id->input_sbal_addr_array = (void **) (qdio->resp_q.sbal);
        id->output_sbal_addr_array = (void **) (qdio->req_q.sbal);
 
index 3da3f48..bdcdbd5 100644 (file)
@@ -55,6 +55,11 @@ config SOFT_WATCHDOG
          To compile this driver as a module, choose M here: the
          module will be called softdog.
 
+config MAX63XX_WATCHDOG
+       tristate "Max63xx watchdog"
+       help
+         Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
+
 config WM831X_WATCHDOG
        tristate "WM831x watchdog"
        depends on MFD_WM831X
@@ -289,6 +294,17 @@ config ADX_WATCHDOG
          Say Y here if you want support for the watchdog timer on Avionic
          Design Xanthos boards.
 
+config TS72XX_WATCHDOG
+       tristate "TS-72XX SBC Watchdog"
+       depends on MACH_TS72XX
+       help
+         Technologic Systems TS-7200, TS-7250 and TS-7260 boards have
+         watchdog timer implemented in a external CPLD chip. Say Y here
+         if you want to support for the watchdog timer on TS-72XX boards.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ts72xx_wdt.
+
 # AVR32 Architecture
 
 config AT32AP700X_WDT
@@ -845,10 +861,10 @@ config TXX9_WDT
 # POWERPC Architecture
 
 config GEF_WDT
-       tristate "GE Fanuc Watchdog Timer"
+       tristate "GE Watchdog Timer"
        depends on GEF_SBC610 || GEF_SBC310 || GEF_PPC9A
        ---help---
-         Watchdog timer found in a number of GE Fanuc single board computers.
+         Watchdog timer found in a number of GE single board computers.
 
 config MPC5200_WDT
        bool "MPC52xx Watchdog Timer"
index 475c611..5e3cb95 100644 (file)
@@ -46,6 +46,7 @@ obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
 obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
 obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
 obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
+obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
 
 # AVR32 Architecture
 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
@@ -142,4 +143,5 @@ obj-$(CONFIG_WATCHDOG_CP1XXX)               += cpwd.o
 # Architecture Independant
 obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
 obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
+obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
 obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
index 4d18c87..2ffce4d 100644 (file)
@@ -150,7 +150,7 @@ static long acq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        int options, retval = -EINVAL;
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
                .firmware_version = 1,
                .identity = WATCHDOG_NAME,
index 824d076..4d40965 100644 (file)
@@ -137,7 +137,7 @@ static long advwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        int new_timeout;
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_KEEPALIVEPING |
                           WDIOF_SETTIMEOUT |
                           WDIOF_MAGICCLOSE,
index 9d7d155..a5ca7a6 100644 (file)
@@ -37,7 +37,7 @@ struct adx_wdt {
        spinlock_t lock;
 };
 
-static struct watchdog_info adx_wdt_info = {
+static const struct watchdog_info adx_wdt_info = {
        .identity = "Avionic Design Xanthos Watchdog",
        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 };
index 937a80f..1e9caea 100644 (file)
@@ -180,7 +180,7 @@ static long ali_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options =              WDIOF_KEEPALIVEPING |
                                        WDIOF_SETTIMEOUT |
                                        WDIOF_MAGICCLOSE,
index f90afdb..d8d4da9 100644 (file)
@@ -238,7 +238,7 @@ static long fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
                                                        | WDIOF_MAGICCLOSE,
                .firmware_version = 1,
index 2bb95cd..c764c52 100644 (file)
@@ -219,7 +219,7 @@ static ssize_t ar7_wdt_write(struct file *file, const char *data,
 static long ar7_wdt_ioctl(struct file *file,
                                        unsigned int cmd, unsigned long arg)
 {
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .identity = LONGNAME,
                .firmware_version = 1,
                .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
index 0378479..6873376 100644 (file)
@@ -202,7 +202,7 @@ static int at32_wdt_get_status(void)
        return status;
 }
 
-static struct watchdog_info at32_wdt_info = {
+static const struct watchdog_info at32_wdt_info = {
        .identity       = "at32ap700x watchdog",
        .options        = WDIOF_SETTIMEOUT |
                          WDIOF_KEEPALIVEPING |
index b185daf..b3046dc 100644 (file)
@@ -121,7 +121,7 @@ static int at91_wdt_settimeout(int new_time)
        return 0;
 }
 
-static struct watchdog_info at91_wdt_info = {
+static const struct watchdog_info at91_wdt_info = {
        .identity       = "at91 watchdog",
        .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 };
index 751c003..5f24552 100644 (file)
@@ -149,7 +149,7 @@ static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data,
        return len;
 }
 
-static struct watchdog_info bcm47xx_wdt_info = {
+static const struct watchdog_info bcm47xx_wdt_info = {
        .identity       = DRV_NAME,
        .options        = WDIOF_SETTIMEOUT |
                                WDIOF_KEEPALIVEPING |
index 2159e66..9c7ccd1 100644 (file)
@@ -19,8 +19,6 @@
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
 #include <linux/fs.h>
-#include <linux/notifier.h>
-#include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/uaccess.h>
@@ -74,7 +72,7 @@
 
 static unsigned int timeout = WATCHDOG_TIMEOUT;
 static int nowayout = WATCHDOG_NOWAYOUT;
-static struct watchdog_info bfin_wdt_info;
+static const struct watchdog_info bfin_wdt_info;
 static unsigned long open_check;
 static char expect_close;
 static DEFINE_SPINLOCK(bfin_wdt_spinlock);
@@ -309,26 +307,6 @@ static long bfin_wdt_ioctl(struct file *file,
        }
 }
 
-/**
- *     bfin_wdt_notify_sys - Notifier Handler
- *     @this: notifier block
- *     @code: notifier event
- *     @unused: unused
- *
- *     Handles specific events, such as turning off the watchdog during a
- *     shutdown event.
- */
-static int bfin_wdt_notify_sys(struct notifier_block *this,
-                                       unsigned long code, void *unused)
-{
-       stampit();
-
-       if (code == SYS_DOWN || code == SYS_HALT)
-               bfin_wdt_stop();
-
-       return NOTIFY_DONE;
-}
-
 #ifdef CONFIG_PM
 static int state_before_suspend;
 
@@ -388,40 +366,28 @@ static struct miscdevice bfin_wdt_miscdev = {
        .fops     = &bfin_wdt_fops,
 };
 
-static struct watchdog_info bfin_wdt_info = {
+static const struct watchdog_info bfin_wdt_info = {
        .identity = "Blackfin Watchdog",
        .options  = WDIOF_SETTIMEOUT |
                    WDIOF_KEEPALIVEPING |
                    WDIOF_MAGICCLOSE,
 };
 
-static struct notifier_block bfin_wdt_notifier = {
-       .notifier_call = bfin_wdt_notify_sys,
-};
-
 /**
  *     bfin_wdt_probe - Initialize module
  *
- *     Registers the misc device and notifier handler.  Actual device
+ *     Registers the misc device.  Actual device
  *     initialization is handled by bfin_wdt_open().
  */
 static int __devinit bfin_wdt_probe(struct platform_device *pdev)
 {
        int ret;
 
-       ret = register_reboot_notifier(&bfin_wdt_notifier);
-       if (ret) {
-               pr_devinit(KERN_ERR PFX
-                       "cannot register reboot notifier (err=%d)\n", ret);
-               return ret;
-       }
-
        ret = misc_register(&bfin_wdt_miscdev);
        if (ret) {
                pr_devinit(KERN_ERR PFX
                        "cannot register miscdev on minor=%d (err=%d)\n",
                                WATCHDOG_MINOR, ret);
-               unregister_reboot_notifier(&bfin_wdt_notifier);
                return ret;
        }
 
@@ -434,21 +400,33 @@ static int __devinit bfin_wdt_probe(struct platform_device *pdev)
 /**
  *     bfin_wdt_remove - Initialize module
  *
- *     Unregisters the misc device and notifier handler.  Actual device
+ *     Unregisters the misc device.  Actual device
  *     deinitialization is handled by bfin_wdt_close().
  */
 static int __devexit bfin_wdt_remove(struct platform_device *pdev)
 {
        misc_deregister(&bfin_wdt_miscdev);
-       unregister_reboot_notifier(&bfin_wdt_notifier);
        return 0;
 }
 
+/**
+ *     bfin_wdt_shutdown - Soft Shutdown Handler
+ *
+ *     Handles the soft shutdown event.
+ */
+static void bfin_wdt_shutdown(struct platform_device *pdev)
+{
+       stampit();
+
+       bfin_wdt_stop();
+}
+
 static struct platform_device *bfin_wdt_device;
 
 static struct platform_driver bfin_wdt_driver = {
        .probe     = bfin_wdt_probe,
        .remove    = __devexit_p(bfin_wdt_remove),
+       .shutdown  = bfin_wdt_shutdown,
        .suspend   = bfin_wdt_suspend,
        .resume    = bfin_wdt_resume,
        .driver    = {
index e8380ef..8b724aa 100644 (file)
@@ -121,7 +121,7 @@ static ssize_t booke_wdt_write(struct file *file, const char __user *buf,
        return count;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
        .identity = "PowerPC Book-E Watchdog",
 };
index 923cc68..9291506 100644 (file)
@@ -257,7 +257,7 @@ static long coh901327_ioctl(struct file *file, unsigned int cmd,
                struct watchdog_info __user *ident;
                int __user *i;
        } uarg;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options                = WDIOF_CARDRESET |
                                          WDIOF_SETTIMEOUT |
                                          WDIOF_KEEPALIVEPING,
index 71f6d7e..edd3475 100644 (file)
@@ -154,7 +154,7 @@ static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
        unsigned int value;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_CARDRESET,
                .identity = "CPU5 WDT",
        };
index 081f295..37ea052 100644 (file)
@@ -403,7 +403,7 @@ static int cpwd_release(struct inode *inode, struct file *file)
 
 static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-       static struct watchdog_info info = {
+       static const struct watchdog_info info = {
                .options                = WDIOF_SETTIMEOUT,
                .firmware_version       = 1,
                .identity               = DRIVER_NAME,
index 887136d..56162c8 100644 (file)
@@ -142,7 +142,7 @@ davinci_wdt_write(struct file *file, const char *data, size_t len,
        return len;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options = WDIOF_KEEPALIVEPING,
        .identity = "DaVinci Watchdog",
 };
index cdd55e0..88ed54e 100644 (file)
@@ -131,7 +131,7 @@ ep93xx_wdt_write(struct file *file, const char __user *data, size_t len,
        return len;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE,
        .identity = "EP93xx Watchdog",
 };
index 9add354..d1c4e55 100644 (file)
@@ -238,7 +238,7 @@ static long eurwdt_ioctl(struct file *file,
 {
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options          = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
                                                        | WDIOF_MAGICCLOSE,
                .firmware_version = 1,
index 734d980..abdbad0 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * GE Fanuc watchdog userspace interface
+ * GE watchdog userspace interface
  *
- * Author:  Martyn Welch <martyn.welch@gefanuc.com>
+ * Author:  Martyn Welch <martyn.welch@ge.com>
  *
- * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, Inc.
+ * Copyright 2008 GE Intelligent Platforms Embedded Systems, 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
@@ -161,11 +161,11 @@ static long gef_wdt_ioctl(struct file *file, unsigned int cmd,
        int timeout;
        int options;
        void __user *argp = (void __user *)arg;
-       static struct watchdog_info info = {
+       static const struct watchdog_info info = {
                .options =      WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
                                WDIOF_KEEPALIVEPING,
                .firmware_version = 0,
-               .identity = "GE Fanuc watchdog",
+               .identity = "GE watchdog",
        };
 
        switch (cmd) {
@@ -311,7 +311,7 @@ static struct of_platform_driver gef_wdt_driver = {
 
 static int __init gef_wdt_init(void)
 {
-       printk(KERN_INFO "GE Fanuc watchdog driver\n");
+       printk(KERN_INFO "GE watchdog driver\n");
        return of_register_platform_driver(&gef_wdt_driver);
 }
 
@@ -323,8 +323,8 @@ static void __exit gef_wdt_exit(void)
 module_init(gef_wdt_init);
 module_exit(gef_wdt_exit);
 
-MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com>");
-MODULE_DESCRIPTION("GE Fanuc watchdog driver");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
+MODULE_DESCRIPTION("GE watchdog driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_ALIAS("platform: gef_wdt");
index 38252ff..9b49b12 100644 (file)
@@ -142,7 +142,7 @@ static long geodewdt_ioctl(struct file *file, unsigned int cmd,
        int __user *p = argp;
        int interval;
 
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
                | WDIOF_MAGICCLOSE,
                .firmware_version =     1,
index a6c5674..70c2c24 100644 (file)
@@ -554,7 +554,7 @@ static ssize_t hpwdt_write(struct file *file, const char __user *data,
        return len;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options = WDIOF_SETTIMEOUT |
                   WDIOF_KEEPALIVEPING |
                   WDIOF_MAGICCLOSE,
index 7ba0b11..bb9750a 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
-#include <linux/platform_device.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
@@ -42,7 +41,7 @@
 #include <linux/io.h>
 
 /* Module and version information */
-#define ESB_VERSION "0.04"
+#define ESB_VERSION "0.05"
 #define ESB_MODULE_NAME "i6300ESB timer"
 #define ESB_DRIVER_NAME ESB_MODULE_NAME ", v" ESB_VERSION
 #define PFX ESB_MODULE_NAME ": "
@@ -65,7 +64,7 @@
 /* Config register bits */
 #define ESB_WDT_REBOOT  (0x01 << 5)   /* Enable reboot on timeout          */
 #define ESB_WDT_FREQ    (0x01 << 2)   /* Decrement frequency               */
-#define ESB_WDT_INTTYPE (0x11 << 0)   /* Interrupt type on timer1 timeout  */
+#define ESB_WDT_INTTYPE (0x03 << 0)   /* Interrupt type on timer1 timeout  */
 
 /* Reload register bits */
 #define ESB_WDT_TIMEOUT (0x01 << 9)    /* Watchdog timed out                */
@@ -82,7 +81,9 @@ static unsigned long timer_alive;
 static struct pci_dev *esb_pci;
 static unsigned short triggered; /* The status of the watchdog upon boot */
 static char esb_expect_close;
-static struct platform_device *esb_platform_device;
+
+/* We can only use 1 card due to the /dev/watchdog restriction */
+static int cards_found;
 
 /* module parameters */
 /* 30 sec default heartbeat (1 < heartbeat < 2*1023) */
@@ -111,8 +112,8 @@ MODULE_PARM_DESC(nowayout,
  */
 static inline void esb_unlock_registers(void)
 {
-       writeb(ESB_UNLOCK1, ESB_RELOAD_REG);
-       writeb(ESB_UNLOCK2, ESB_RELOAD_REG);
+       writew(ESB_UNLOCK1, ESB_RELOAD_REG);
+       writew(ESB_UNLOCK2, ESB_RELOAD_REG);
 }
 
 static int esb_timer_start(void)
@@ -256,7 +257,7 @@ static long esb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        int new_heartbeat;
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options =              WDIOF_SETTIMEOUT |
                                        WDIOF_KEEPALIVEPING |
                                        WDIOF_MAGICCLOSE,
@@ -332,11 +333,6 @@ static struct miscdevice esb_miscdev = {
 
 /*
  * Data for PCI driver interface
- *
- * This data only exists for exporting the supported
- * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
- * register a pci_driver, because someone else might one day
- * want to register another driver on the same PCI id.
  */
 static struct pci_device_id esb_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), },
@@ -348,29 +344,19 @@ MODULE_DEVICE_TABLE(pci, esb_pci_tbl);
  *      Init & exit routines
  */
 
-static unsigned char __devinit esb_getdevice(void)
+static unsigned char __devinit esb_getdevice(struct pci_dev *pdev)
 {
-       /*
-        *      Find the PCI device
-        */
-
-       esb_pci = pci_get_device(PCI_VENDOR_ID_INTEL,
-                                       PCI_DEVICE_ID_INTEL_ESB_9, NULL);
-
-       if (!esb_pci)
-               return 0;
-
-       if (pci_enable_device(esb_pci)) {
+       if (pci_enable_device(pdev)) {
                printk(KERN_ERR PFX "failed to enable device\n");
                goto err_devput;
        }
 
-       if (pci_request_region(esb_pci, 0, ESB_MODULE_NAME)) {
+       if (pci_request_region(pdev, 0, ESB_MODULE_NAME)) {
                printk(KERN_ERR PFX "failed to request region\n");
                goto err_disable;
        }
 
-       BASEADDR = pci_ioremap_bar(esb_pci, 0);
+       BASEADDR = pci_ioremap_bar(pdev, 0);
        if (BASEADDR == NULL) {
                /* Something's wrong here, BASEADDR has to be set */
                printk(KERN_ERR PFX "failed to get BASEADDR\n");
@@ -378,14 +364,14 @@ static unsigned char __devinit esb_getdevice(void)
        }
 
        /* Done */
+       esb_pci = pdev;
        return 1;
 
 err_release:
-       pci_release_region(esb_pci, 0);
+       pci_release_region(pdev, 0);
 err_disable:
-       pci_disable_device(esb_pci);
+       pci_disable_device(pdev);
 err_devput:
-       pci_dev_put(esb_pci);
        return 0;
 }
 
@@ -430,12 +416,23 @@ static void __devinit esb_initdevice(void)
        esb_timer_set_heartbeat(heartbeat);
 }
 
-static int __devinit esb_probe(struct platform_device *dev)
+static int __devinit esb_probe(struct pci_dev *pdev,
+               const struct pci_device_id *ent)
 {
        int ret;
 
+       cards_found++;
+       if (cards_found == 1)
+               printk(KERN_INFO PFX "Intel 6300ESB WatchDog Timer Driver v%s\n",
+                       ESB_VERSION);
+
+       if (cards_found > 1) {
+               printk(KERN_ERR PFX "This driver only supports 1 device\n");
+               return -ENODEV;
+       }
+
        /* Check whether or not the hardware watchdog is there */
-       if (!esb_getdevice() || esb_pci == NULL)
+       if (!esb_getdevice(pdev) || esb_pci == NULL)
                return -ENODEV;
 
        /* Check that the heartbeat value is within it's range;
@@ -467,11 +464,11 @@ err_unmap:
        iounmap(BASEADDR);
        pci_release_region(esb_pci, 0);
        pci_disable_device(esb_pci);
-       pci_dev_put(esb_pci);
+       esb_pci = NULL;
        return ret;
 }
 
-static int __devexit esb_remove(struct platform_device *dev)
+static void __devexit esb_remove(struct pci_dev *pdev)
 {
        /* Stop the timer before we leave */
        if (!nowayout)
@@ -482,54 +479,30 @@ static int __devexit esb_remove(struct platform_device *dev)
        iounmap(BASEADDR);
        pci_release_region(esb_pci, 0);
        pci_disable_device(esb_pci);
-       pci_dev_put(esb_pci);
-       return 0;
+       esb_pci = NULL;
 }
 
-static void esb_shutdown(struct platform_device *dev)
+static void esb_shutdown(struct pci_dev *pdev)
 {
        esb_timer_stop();
 }
 
-static struct platform_driver esb_platform_driver = {
+static struct pci_driver esb_driver = {
+       .name           = ESB_MODULE_NAME,
+       .id_table       = esb_pci_tbl,
        .probe          = esb_probe,
        .remove         = __devexit_p(esb_remove),
        .shutdown       = esb_shutdown,
-       .driver         = {
-               .owner  = THIS_MODULE,
-               .name   = ESB_MODULE_NAME,
-       },
 };
 
 static int __init watchdog_init(void)
 {
-       int err;
-
-       printk(KERN_INFO PFX "Intel 6300ESB WatchDog Timer Driver v%s\n",
-               ESB_VERSION);
-
-       err = platform_driver_register(&esb_platform_driver);
-       if (err)
-               return err;
-
-       esb_platform_device = platform_device_register_simple(ESB_MODULE_NAME,
-                                                               -1, NULL, 0);
-       if (IS_ERR(esb_platform_device)) {
-               err = PTR_ERR(esb_platform_device);
-               goto unreg_platform_driver;
-       }
-
-       return 0;
-
-unreg_platform_driver:
-       platform_driver_unregister(&esb_platform_driver);
-       return err;
+       return pci_register_driver(&esb_driver);
 }
 
 static void __exit watchdog_cleanup(void)
 {
-       platform_device_unregister(esb_platform_device);
-       platform_driver_unregister(&esb_platform_driver);
+       pci_unregister_driver(&esb_driver);
        printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
 }
 
index 4bdb7f1..44bc6aa 100644 (file)
@@ -584,7 +584,7 @@ static long iTCO_wdt_ioctl(struct file *file, unsigned int cmd,
        int new_heartbeat;
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options =              WDIOF_SETTIMEOUT |
                                        WDIOF_KEEPALIVEPING |
                                        WDIOF_MAGICCLOSE,
@@ -698,7 +698,7 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
        if (iTCO_wdt_private.iTCO_version == 2) {
                pci_read_config_dword(pdev, 0xf0, &base_address);
                if ((base_address & 1) == 0) {
-                       printk(KERN_ERR PFX "RCBA is disabled by harddware\n");
+                       printk(KERN_ERR PFX "RCBA is disabled by hardware\n");
                        ret = -ENODEV;
                        goto out;
                }
@@ -708,8 +708,8 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
 
        /* Check chipset's NO_REBOOT bit */
        if (iTCO_wdt_unset_NO_REBOOT_bit() && iTCO_vendor_check_noreboot_on()) {
-               printk(KERN_ERR PFX "failed to reset NO_REBOOT flag, "
-                                       "reboot disabled by hardware\n");
+               printk(KERN_INFO PFX "unable to reset NO_REBOOT flag, "
+                                       "platform may have disabled it\n");
                ret = -ENODEV;  /* Cannot reset NO_REBOOT bit */
                goto out_unmap;
        }
@@ -805,6 +805,7 @@ static void __devexit iTCO_wdt_cleanup(void)
 
 static int __devinit iTCO_wdt_probe(struct platform_device *dev)
 {
+       int ret = -ENODEV;
        int found = 0;
        struct pci_dev *pdev = NULL;
        const struct pci_device_id *ent;
@@ -814,19 +815,17 @@ static int __devinit iTCO_wdt_probe(struct platform_device *dev)
        for_each_pci_dev(pdev) {
                ent = pci_match_id(iTCO_wdt_pci_tbl, pdev);
                if (ent) {
-                       if (!(iTCO_wdt_init(pdev, ent, dev))) {
-                               found++;
+                       found++;
+                       ret = iTCO_wdt_init(pdev, ent, dev);
+                       if (!ret)
                                break;
-                       }
                }
        }
 
-       if (!found) {
+       if (!found)
                printk(KERN_INFO PFX "No card detected\n");
-               return -ENODEV;
-       }
 
-       return 0;
+       return ret;
 }
 
 static int __devexit iTCO_wdt_remove(struct platform_device *dev)
index 4bef3dd..0149d8d 100644 (file)
@@ -174,7 +174,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
 
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
                                                        | WDIOF_MAGICCLOSE,
                .firmware_version = 1,
index bea8a12..1cc5609 100644 (file)
@@ -111,7 +111,7 @@ static long indydog_ioctl(struct file *file, unsigned int cmd,
                                                        unsigned long arg)
 {
        int options, retval = -EINVAL;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options                = WDIOF_KEEPALIVEPING,
                .firmware_version       = 0,
                .identity               = "Hardware Watchdog for SGI IP22",
index daed48d..f52c162 100644 (file)
@@ -236,7 +236,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
 {
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .identity = "IT8712F Watchdog",
                .firmware_version = 1,
                .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
index cc133c5..b709b3b 100644 (file)
@@ -421,7 +421,7 @@ static ssize_t wdt_write(struct file *file, const char __user *buf,
        return count;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
        .firmware_version =     1,
        .identity = WATCHDOG_NAME,
index 3c79dc5..e86952a 100644 (file)
@@ -100,7 +100,7 @@ static ssize_t ixp2000_wdt_write(struct file *file, const char *data,
 }
 
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options        = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
                                WDIOF_KEEPALIVEPING,
        .identity       = "IXP2000 Watchdog",
index 147b4d5..e02c0ec 100644 (file)
@@ -89,7 +89,7 @@ ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
        return len;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options        = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
                          WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
        .identity       = "IXP4xx Watchdog",
index e1c8276..2852bb2 100644 (file)
@@ -145,7 +145,7 @@ static int ks8695_wdt_close(struct inode *inode, struct file *file)
        return 0;
 }
 
-static struct watchdog_info ks8695_wdt_info = {
+static const struct watchdog_info ks8695_wdt_info = {
        .identity       = "ks8695 watchdog",
        .options        = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 };
index 47d7197..2d118cf 100644 (file)
@@ -101,7 +101,7 @@ MODULE_PARM_DESC(nowayout,
 
 #define PFX "machzwd"
 
-static struct watchdog_info zf_info = {
+static const struct watchdog_info zf_info = {
        .options                = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
        .firmware_version       = 1,
        .identity               = "ZF-Logic watchdog",
diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c
new file mode 100644 (file)
index 0000000..6eb91d7
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * drivers/char/watchdog/max63xx_wdt.c
+ *
+ * Driver for max63{69,70,71,72,73,74} watchdog timers
+ *
+ * Copyright (C) 2009 Marc Zyngier <maz@misterjones.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * This driver assumes the watchdog pins are memory mapped (as it is
+ * the case for the Arcom Zeus). Should it be connected over GPIOs or
+ * another interface, some abstraction will have to be introduced.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/device.h>
+
+#define DEFAULT_HEARTBEAT 60
+#define MAX_HEARTBEAT     60
+
+static int heartbeat = DEFAULT_HEARTBEAT;
+static int nowayout  = WATCHDOG_NOWAYOUT;
+
+/*
+ * Memory mapping: a single byte, 3 first lower bits to select bit 3
+ * to ping the watchdog.
+ */
+#define MAX6369_WDSET  (7 << 0)
+#define MAX6369_WDI    (1 << 3)
+
+static DEFINE_SPINLOCK(io_lock);
+
+static unsigned long wdt_status;
+#define WDT_IN_USE     0
+#define WDT_RUNNING    1
+#define WDT_OK_TO_CLOSE 2
+
+static int nodelay;
+static struct resource *wdt_mem;
+static void __iomem    *wdt_base;
+static struct platform_device *max63xx_pdev;
+
+/*
+ * The timeout values used are actually the absolute minimum the chip
+ * offers. Typical values on my board are slightly over twice as long
+ * (10s setting ends up with a 25s timeout), and can be up to 3 times
+ * the nominal setting (according to the datasheet). So please take
+ * these values with a grain of salt. Same goes for the initial delay
+ * "feature". Only max6373/74 have a few settings without this initial
+ * delay (selected with the "nodelay" parameter).
+ *
+ * I also decided to remove from the tables any timeout smaller than a
+ * second, as it looked completly overkill...
+ */
+
+/* Timeouts in second */
+struct max63xx_timeout {
+       u8 wdset;
+       u8 tdelay;
+       u8 twd;
+};
+
+static struct max63xx_timeout max6369_table[] = {
+       { 5,  1,  1 },
+       { 6, 10, 10 },
+       { 7, 60, 60 },
+       { },
+};
+
+static struct max63xx_timeout max6371_table[] = {
+       { 6, 60,  3 },
+       { 7, 60, 60 },
+       { },
+};
+
+static struct max63xx_timeout max6373_table[] = {
+       { 2, 60,  1 },
+       { 5,  0,  1 },
+       { 1,  3,  3 },
+       { 7, 60, 10 },
+       { 6,  0, 10 },
+       { },
+};
+
+static struct max63xx_timeout *current_timeout;
+
+static struct max63xx_timeout *
+max63xx_select_timeout(struct max63xx_timeout *table, int value)
+{
+       while (table->twd) {
+               if (value <= table->twd) {
+                       if (nodelay && table->tdelay == 0)
+                               return table;
+
+                       if (!nodelay)
+                               return table;
+               }
+
+               table++;
+       }
+
+       return NULL;
+}
+
+static void max63xx_wdt_ping(void)
+{
+       u8 val;
+
+       spin_lock(&io_lock);
+
+       val = __raw_readb(wdt_base);
+
+       __raw_writeb(val | MAX6369_WDI, wdt_base);
+       __raw_writeb(val & ~MAX6369_WDI, wdt_base);
+
+       spin_unlock(&io_lock);
+}
+
+static void max63xx_wdt_enable(struct max63xx_timeout *entry)
+{
+       u8 val;
+
+       if (test_and_set_bit(WDT_RUNNING, &wdt_status))
+               return;
+
+       spin_lock(&io_lock);
+
+       val = __raw_readb(wdt_base);
+       val &= ~MAX6369_WDSET;
+       val |= entry->wdset;
+       __raw_writeb(val, wdt_base);
+
+       spin_unlock(&io_lock);
+
+       /* check for a edge triggered startup */
+       if (entry->tdelay == 0)
+               max63xx_wdt_ping();
+}
+
+static void max63xx_wdt_disable(void)
+{
+       spin_lock(&io_lock);
+
+       __raw_writeb(3, wdt_base);
+
+       spin_unlock(&io_lock);
+
+       clear_bit(WDT_RUNNING, &wdt_status);
+}
+
+static int max63xx_wdt_open(struct inode *inode, struct file *file)
+{
+       if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+               return -EBUSY;
+
+       max63xx_wdt_enable(current_timeout);
+       clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+       return nonseekable_open(inode, file);
+}
+
+static ssize_t max63xx_wdt_write(struct file *file, const char *data,
+                                size_t len, loff_t *ppos)
+{
+       if (len) {
+               if (!nowayout) {
+                       size_t i;
+
+                       clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+                       for (i = 0; i != len; i++) {
+                               char c;
+
+                               if (get_user(c, data + i))
+                                       return -EFAULT;
+
+                               if (c == 'V')
+                                       set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+                       }
+               }
+
+               max63xx_wdt_ping();
+       }
+
+       return len;
+}
+
+static const struct watchdog_info ident = {
+       .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+       .identity = "max63xx Watchdog",
+};
+
+static long max63xx_wdt_ioctl(struct file *file, unsigned int cmd,
+                             unsigned long arg)
+{
+       int ret = -ENOTTY;
+
+       switch (cmd) {
+       case WDIOC_GETSUPPORT:
+               ret = copy_to_user((struct watchdog_info *)arg, &ident,
+                                  sizeof(ident)) ? -EFAULT : 0;
+               break;
+
+       case WDIOC_GETSTATUS:
+       case WDIOC_GETBOOTSTATUS:
+               ret = put_user(0, (int *)arg);
+               break;
+
+       case WDIOC_KEEPALIVE:
+               max63xx_wdt_ping();
+               ret = 0;
+               break;
+
+       case WDIOC_GETTIMEOUT:
+               ret = put_user(heartbeat, (int *)arg);
+               break;
+       }
+       return ret;
+}
+
+static int max63xx_wdt_release(struct inode *inode, struct file *file)
+{
+       if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+               max63xx_wdt_disable();
+       else
+               dev_crit(&max63xx_pdev->dev,
+                        "device closed unexpectedly - timer will not stop\n");
+
+       clear_bit(WDT_IN_USE, &wdt_status);
+       clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+       return 0;
+}
+
+static const struct file_operations max63xx_wdt_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .write          = max63xx_wdt_write,
+       .unlocked_ioctl = max63xx_wdt_ioctl,
+       .open           = max63xx_wdt_open,
+       .release        = max63xx_wdt_release,
+};
+
+static struct miscdevice max63xx_wdt_miscdev = {
+       .minor  = WATCHDOG_MINOR,
+       .name   = "watchdog",
+       .fops   = &max63xx_wdt_fops,
+};
+
+static int __devinit max63xx_wdt_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       int size;
+       struct resource *res;
+       struct device *dev = &pdev->dev;
+       struct max63xx_timeout *table;
+
+       table = (struct max63xx_timeout *)pdev->id_entry->driver_data;
+
+       if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+               heartbeat = DEFAULT_HEARTBEAT;
+
+       dev_info(dev, "requesting %ds heartbeat\n", heartbeat);
+       current_timeout = max63xx_select_timeout(table, heartbeat);
+
+       if (!current_timeout) {
+               dev_err(dev, "unable to satisfy heartbeat request\n");
+               return -EINVAL;
+       }
+
+       dev_info(dev, "using %ds heartbeat with %ds initial delay\n",
+                current_timeout->twd, current_timeout->tdelay);
+
+       heartbeat = current_timeout->twd;
+
+       max63xx_pdev = pdev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(dev, "failed to get memory region resource\n");
+               return -ENOENT;
+       }
+
+       size = resource_size(res);
+       wdt_mem = request_mem_region(res->start, size, pdev->name);
+
+       if (wdt_mem == NULL) {
+               dev_err(dev, "failed to get memory region\n");
+               return -ENOENT;
+       }
+
+       wdt_base = ioremap(res->start, size);
+       if (!wdt_base) {
+               dev_err(dev, "failed to map memory region\n");
+               ret = -ENOMEM;
+               goto out_request;
+       }
+
+       ret = misc_register(&max63xx_wdt_miscdev);
+       if (ret < 0) {
+               dev_err(dev, "cannot register misc device\n");
+               goto out_unmap;
+       }
+
+       return 0;
+
+out_unmap:
+       iounmap(wdt_base);
+out_request:
+       release_resource(wdt_mem);
+       kfree(wdt_mem);
+
+       return ret;
+}
+
+static int __devexit max63xx_wdt_remove(struct platform_device *pdev)
+{
+       misc_deregister(&max63xx_wdt_miscdev);
+       if (wdt_mem) {
+               release_resource(wdt_mem);
+               kfree(wdt_mem);
+               wdt_mem = NULL;
+       }
+
+       if (wdt_base)
+               iounmap(wdt_base);
+
+       return 0;
+}
+
+static struct platform_device_id max63xx_id_table[] = {
+       { "max6369_wdt", (kernel_ulong_t)max6369_table, },
+       { "max6370_wdt", (kernel_ulong_t)max6369_table, },
+       { "max6371_wdt", (kernel_ulong_t)max6371_table, },
+       { "max6372_wdt", (kernel_ulong_t)max6371_table, },
+       { "max6373_wdt", (kernel_ulong_t)max6373_table, },
+       { "max6374_wdt", (kernel_ulong_t)max6373_table, },
+       { },
+};
+MODULE_DEVICE_TABLE(platform, max63xx_id_table);
+
+static struct platform_driver max63xx_wdt_driver = {
+       .probe          = max63xx_wdt_probe,
+       .remove         = __devexit_p(max63xx_wdt_remove),
+       .id_table       = max63xx_id_table,
+       .driver         = {
+               .name   = "max63xx_wdt",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init max63xx_wdt_init(void)
+{
+       return platform_driver_register(&max63xx_wdt_driver);
+}
+
+static void __exit max63xx_wdt_exit(void)
+{
+       platform_driver_unregister(&max63xx_wdt_driver);
+}
+
+module_init(max63xx_wdt_init);
+module_exit(max63xx_wdt_exit);
+
+MODULE_AUTHOR("Marc Zyngier <maz@misterjones.org>");
+MODULE_DESCRIPTION("max63xx Watchdog Driver");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+                "Watchdog heartbeat period in seconds from 1 to "
+                __MODULE_STRING(MAX_HEARTBEAT) ", default "
+                __MODULE_STRING(DEFAULT_HEARTBEAT));
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
+                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+module_param(nodelay, int, 0);
+MODULE_PARM_DESC(nodelay,
+                "Force selection of a timeout setting without initial delay "
+                "(max6373/74 only, default=0)");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
index 407b025..bc820d1 100644 (file)
@@ -201,7 +201,7 @@ static long mixcomwd_ioctl(struct file *file,
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
        int status;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
                .firmware_version = 1,
                .identity = "MixCOM watchdog",
index 38c588e..4e3941c 100644 (file)
@@ -148,7 +148,7 @@ static long mpc8xxx_wdt_ioctl(struct file *file, unsigned int cmd,
 {
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_KEEPALIVEPING,
                .firmware_version = 1,
                .identity = "MPC8xxx",
index a2dc07c..b0646da 100644 (file)
@@ -213,7 +213,7 @@ static ssize_t mpcore_wdt_write(struct file *file, const char *data,
        return len;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options                = WDIOF_SETTIMEOUT |
                                  WDIOF_KEEPALIVEPING |
                                  WDIOF_MAGICCLOSE,
index a51dbe4..97f8a48 100644 (file)
@@ -179,7 +179,7 @@ static long mv64x60_wdt_ioctl(struct file *file,
        int timeout;
        int options;
        void __user *argp = (void __user *)arg;
-       static struct watchdog_info info = {
+       static const struct watchdog_info info = {
                .options =      WDIOF_SETTIMEOUT        |
                                WDIOF_MAGICCLOSE        |
                                WDIOF_KEEPALIVEPING,
index 1a2b916..d3aa2f1 100644 (file)
@@ -407,7 +407,7 @@ static long pc87413_ioctl(struct file *file, unsigned int cmd,
                int __user *i;
        } uarg;
 
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options          = WDIOF_KEEPALIVEPING |
                                    WDIOF_SETTIMEOUT |
                                    WDIOF_MAGICCLOSE,
index aa95123..06f7922 100644 (file)
@@ -606,7 +606,7 @@ static long pcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        int temperature;
        int new_heartbeat;
        int __user *argp = (int __user *)arg;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options =              WDIOF_OVERHEAT |
                                        WDIOF_CARDRESET |
                                        WDIOF_KEEPALIVEPING |
index 698f51b..64374d6 100644 (file)
@@ -481,7 +481,7 @@ static long pcipcwd_ioctl(struct file *file, unsigned int cmd,
 {
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options =              WDIOF_OVERHEAT |
                                        WDIOF_CARDRESET |
                                        WDIOF_KEEPALIVEPING |
index 052fe45..8e4eacc 100644 (file)
@@ -404,7 +404,7 @@ static long usb_pcwd_ioctl(struct file *file, unsigned int cmd,
 {
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options =              WDIOF_KEEPALIVEPING |
                                        WDIOF_SETTIMEOUT |
                                        WDIOF_MAGICCLOSE,
index 2d22e99..435ec2a 100644 (file)
@@ -52,7 +52,7 @@ static struct {
        struct timer_list timer;        /* The timer that pings the watchdog */
 } pikawdt_private;
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .identity       = DRV_NAME,
        .options        = WDIOF_CARDRESET |
                          WDIOF_SETTIMEOUT |
index 538ec2c..09102f0 100644 (file)
@@ -141,7 +141,7 @@ static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd,
        int options, new_timeout = 0;
        uint32_t timeout, timeout_left = 0;
 
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
                .firmware_version = 0,
                .identity = "Hardware Watchdog for PNX833x",
index bf12d06..d4c29b5 100644 (file)
@@ -198,7 +198,7 @@ static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
        void __user *argp = (void __user *)arg;
        int new_timeout;
        unsigned int value;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options =              WDIOF_SETTIMEOUT |
                                        WDIOF_KEEPALIVEPING |
                                        WDIOF_MAGICCLOSE,
index 4976bfd..69c6adb 100644 (file)
@@ -149,7 +149,7 @@ static long rdc321x_wdt_ioctl(struct file *file, unsigned int cmd,
 {
        void __user *argp = (void __user *)arg;
        unsigned int value;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_CARDRESET,
                .identity = "RDC321x WDT",
        };
index c14ae86..ae57bf9 100644 (file)
@@ -85,7 +85,7 @@ static int riowd_release(struct inode *inode, struct file *filp)
 
 static long riowd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
-       static struct watchdog_info info = {
+       static const struct watchdog_info info = {
                .options                = WDIOF_SETTIMEOUT,
                .firmware_version       = 1,
                .identity               = DRIVER_NAME,
index e6763d2..8d44c9b 100644 (file)
@@ -111,7 +111,7 @@ out:
 }
 
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options        = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
                                WDIOF_KEEPALIVEPING,
        .identity       = WATCHDOG_NAME,
index 569eb29..9c40f48 100644 (file)
@@ -250,7 +250,7 @@ static long sch311x_wdt_ioctl(struct file *file, unsigned int cmd,
        int new_timeout;
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options                = WDIOF_KEEPALIVEPING |
                                          WDIOF_SETTIMEOUT |
                                          WDIOF_MAGICCLOSE,
index 5dd9526..b3421fd 100644 (file)
@@ -94,7 +94,7 @@ static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data,
        return len;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options        = WDIOF_CARDRESET |
                          WDIOF_MAGICCLOSE |
                          WDIOF_SETTIMEOUT |
diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c
new file mode 100644 (file)
index 0000000..565a2c3
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * Watchdog driver for Technologic Systems TS-72xx based SBCs
+ * (TS-7200, TS-7250 and TS-7260). These boards have external
+ * glue logic CPLD chip, which includes programmable watchdog
+ * timer.
+ *
+ * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi>
+ *
+ * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/watchdog.h>
+#include <linux/uaccess.h>
+
+#define TS72XX_WDT_FEED_VAL            0x05
+#define TS72XX_WDT_DEFAULT_TIMEOUT     8
+
+static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
+                         "(1 <= timeout <= 8, default="
+                         __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT)
+                         ")");
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+/**
+ * struct ts72xx_wdt - watchdog control structure
+ * @lock: lock that protects this structure
+ * @regval: watchdog timeout value suitable for control register
+ * @flags: flags controlling watchdog device state
+ * @control_reg: watchdog control register
+ * @feed_reg: watchdog feed register
+ * @pdev: back pointer to platform dev
+ */
+struct ts72xx_wdt {
+       struct mutex    lock;
+       int             regval;
+
+#define TS72XX_WDT_BUSY_FLAG           1
+#define TS72XX_WDT_EXPECT_CLOSE_FLAG   2
+       int             flags;
+
+       void __iomem    *control_reg;
+       void __iomem    *feed_reg;
+
+       struct platform_device *pdev;
+};
+
+struct platform_device *ts72xx_wdt_pdev;
+
+/*
+ * TS-72xx Watchdog supports following timeouts (value written
+ * to control register):
+ *     value   description
+ *     -------------------------
+ *     0x00    watchdog disabled
+ *     0x01    250ms
+ *     0x02    500ms
+ *     0x03    1s
+ *     0x04    reserved
+ *     0x05    2s
+ *     0x06    4s
+ *     0x07    8s
+ *
+ * Timeouts below 1s are not very usable so we don't
+ * allow them at all.
+ *
+ * We provide two functions that convert between these:
+ * timeout_to_regval() and regval_to_timeout().
+ */
+static const struct {
+       int     timeout;
+       int     regval;
+} ts72xx_wdt_map[] = {
+       { 1, 3 },
+       { 2, 5 },
+       { 4, 6 },
+       { 8, 7 },
+};
+
+/**
+ * timeout_to_regval() - converts given timeout to control register value
+ * @new_timeout: timeout in seconds to be converted
+ *
+ * Function converts given @new_timeout into valid value that can
+ * be programmed into watchdog control register. When conversion is
+ * not possible, function returns %-EINVAL.
+ */
+static int timeout_to_regval(int new_timeout)
+{
+       int i;
+
+       /* first limit it to 1 - 8 seconds */
+       new_timeout = clamp_val(new_timeout, 1, 8);
+
+       for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
+               if (ts72xx_wdt_map[i].timeout >= new_timeout)
+                       return ts72xx_wdt_map[i].regval;
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * regval_to_timeout() - converts control register value to timeout
+ * @regval: control register value to be converted
+ *
+ * Function converts given @regval to timeout in seconds (1, 2, 4 or 8).
+ * If @regval cannot be converted, function returns %-EINVAL.
+ */
+static int regval_to_timeout(int regval)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
+               if (ts72xx_wdt_map[i].regval == regval)
+                       return ts72xx_wdt_map[i].timeout;
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * ts72xx_wdt_kick() - kick the watchdog
+ * @wdt: watchdog to be kicked
+ *
+ * Called with @wdt->lock held.
+ */
+static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt)
+{
+       __raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg);
+}
+
+/**
+ * ts72xx_wdt_start() - starts the watchdog timer
+ * @wdt: watchdog to be started
+ *
+ * This function programs timeout to watchdog timer
+ * and starts it.
+ *
+ * Called with @wdt->lock held.
+ */
+static void ts72xx_wdt_start(struct ts72xx_wdt *wdt)
+{
+       /*
+        * To program the wdt, it first must be "fed" and
+        * only after that (within 30 usecs) the configuration
+        * can be changed.
+        */
+       ts72xx_wdt_kick(wdt);
+       __raw_writeb((u8)wdt->regval, wdt->control_reg);
+}
+
+/**
+ * ts72xx_wdt_stop() - stops the watchdog timer
+ * @wdt: watchdog to be stopped
+ *
+ * Called with @wdt->lock held.
+ */
+static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt)
+{
+       ts72xx_wdt_kick(wdt);
+       __raw_writeb(0, wdt->control_reg);
+}
+
+static int ts72xx_wdt_open(struct inode *inode, struct file *file)
+{
+       struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev);
+       int regval;
+
+       /*
+        * Try to convert default timeout to valid register
+        * value first.
+        */
+       regval = timeout_to_regval(timeout);
+       if (regval < 0) {
+               dev_err(&wdt->pdev->dev,
+                       "failed to convert timeout (%d) to register value\n",
+                       timeout);
+               return -EINVAL;
+       }
+
+       if (mutex_lock_interruptible(&wdt->lock))
+               return -ERESTARTSYS;
+
+       if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) {
+               mutex_unlock(&wdt->lock);
+               return -EBUSY;
+       }
+
+       wdt->flags = TS72XX_WDT_BUSY_FLAG;
+       wdt->regval = regval;
+       file->private_data = wdt;
+
+       ts72xx_wdt_start(wdt);
+
+       mutex_unlock(&wdt->lock);
+       return nonseekable_open(inode, file);
+}
+
+static int ts72xx_wdt_release(struct inode *inode, struct file *file)
+{
+       struct ts72xx_wdt *wdt = file->private_data;
+
+       if (mutex_lock_interruptible(&wdt->lock))
+               return -ERESTARTSYS;
+
+       if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) {
+               ts72xx_wdt_stop(wdt);
+       } else {
+               dev_warn(&wdt->pdev->dev,
+                        "TS-72XX WDT device closed unexpectly. "
+                        "Watchdog timer will not stop!\n");
+               /*
+                * Kick it one more time, to give userland some time
+                * to recover (for example, respawning the kicker
+                * daemon).
+                */
+               ts72xx_wdt_kick(wdt);
+       }
+
+       wdt->flags = 0;
+
+       mutex_unlock(&wdt->lock);
+       return 0;
+}
+
+static ssize_t ts72xx_wdt_write(struct file *file,
+                               const char __user *data,
+                               size_t len,
+                               loff_t *ppos)
+{
+       struct ts72xx_wdt *wdt = file->private_data;
+
+       if (!len)
+               return 0;
+
+       if (mutex_lock_interruptible(&wdt->lock))
+               return -ERESTARTSYS;
+
+       ts72xx_wdt_kick(wdt);
+
+       /*
+        * Support for magic character closing. User process
+        * writes 'V' into the device, just before it is closed.
+        * This means that we know that the wdt timer can be
+        * stopped after user closes the device.
+        */
+       if (!nowayout) {
+               int i;
+
+               for (i = 0; i < len; i++) {
+                       char c;
+
+                       /* In case it was set long ago */
+                       wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG;
+
+                       if (get_user(c, data + i)) {
+                               mutex_unlock(&wdt->lock);
+                               return -EFAULT;
+                       }
+                       if (c == 'V') {
+                               wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG;
+                               break;
+                       }
+               }
+       }
+
+       mutex_unlock(&wdt->lock);
+       return len;
+}
+
+static const struct watchdog_info winfo = {
+       .options                = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+                                 WDIOF_MAGICCLOSE,
+       .firmware_version       = 1,
+       .identity               = "TS-72XX WDT",
+};
+
+static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
+                            unsigned long arg)
+{
+       struct ts72xx_wdt *wdt = file->private_data;
+       void __user *argp = (void __user *)arg;
+       int __user *p = (int __user *)argp;
+       int error = 0;
+
+       if (mutex_lock_interruptible(&wdt->lock))
+               return -ERESTARTSYS;
+
+       switch (cmd) {
+       case WDIOC_GETSUPPORT:
+               error = copy_to_user(argp, &winfo, sizeof(winfo));
+               break;
+
+       case WDIOC_GETSTATUS:
+       case WDIOC_GETBOOTSTATUS:
+               return put_user(0, p);
+
+       case WDIOC_KEEPALIVE:
+               ts72xx_wdt_kick(wdt);
+               break;
+
+       case WDIOC_SETOPTIONS: {
+               int options;
+
+               if (get_user(options, p)) {
+                       error = -EFAULT;
+                       break;
+               }
+
+               error = -EINVAL;
+
+               if ((options & WDIOS_DISABLECARD) != 0) {
+                       ts72xx_wdt_stop(wdt);
+                       error = 0;
+               }
+               if ((options & WDIOS_ENABLECARD) != 0) {
+                       ts72xx_wdt_start(wdt);
+                       error = 0;
+               }
+
+               break;
+       }
+
+       case WDIOC_SETTIMEOUT: {
+               int new_timeout;
+
+               if (get_user(new_timeout, p)) {
+                       error = -EFAULT;
+               } else {
+                       int regval;
+
+                       regval = timeout_to_regval(new_timeout);
+                       if (regval < 0) {
+                               error = -EINVAL;
+                       } else {
+                               ts72xx_wdt_stop(wdt);
+                               wdt->regval = regval;
+                               ts72xx_wdt_start(wdt);
+                       }
+               }
+               if (error)
+                       break;
+
+               /*FALLTHROUGH*/
+       }
+
+       case WDIOC_GETTIMEOUT:
+               if (put_user(regval_to_timeout(wdt->regval), p))
+                       error = -EFAULT;
+               break;
+
+       default:
+               error = -ENOTTY;
+               break;
+       }
+
+       mutex_unlock(&wdt->lock);
+       return error;
+}
+
+static const struct file_operations ts72xx_wdt_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = no_llseek,
+       .open           = ts72xx_wdt_open,
+       .release        = ts72xx_wdt_release,
+       .write          = ts72xx_wdt_write,
+       .unlocked_ioctl = ts72xx_wdt_ioctl,
+};
+
+static struct miscdevice ts72xx_wdt_miscdev = {
+       .minor          = WATCHDOG_MINOR,
+       .name           = "watchdog",
+       .fops           = &ts72xx_wdt_fops,
+};
+
+static __devinit int ts72xx_wdt_probe(struct platform_device *pdev)
+{
+       struct ts72xx_wdt *wdt;
+       struct resource *r1, *r2;
+       int error = 0;
+
+       wdt = kzalloc(sizeof(struct ts72xx_wdt), GFP_KERNEL);
+       if (!wdt) {
+               dev_err(&pdev->dev, "failed to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r1) {
+               dev_err(&pdev->dev, "failed to get memory resource\n");
+               error = -ENODEV;
+               goto fail;
+       }
+
+       r1 = request_mem_region(r1->start, resource_size(r1), pdev->name);
+       if (!r1) {
+               dev_err(&pdev->dev, "cannot request memory region\n");
+               error = -EBUSY;
+               goto fail;
+       }
+
+       wdt->control_reg = ioremap(r1->start, resource_size(r1));
+       if (!wdt->control_reg) {
+               dev_err(&pdev->dev, "failed to map memory\n");
+               error = -ENODEV;
+               goto fail_free_control;
+       }
+
+       r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!r2) {
+               dev_err(&pdev->dev, "failed to get memory resource\n");
+               error = -ENODEV;
+               goto fail_unmap_control;
+       }
+
+       r2 = request_mem_region(r2->start, resource_size(r2), pdev->name);
+       if (!r2) {
+               dev_err(&pdev->dev, "cannot request memory region\n");
+               error = -EBUSY;
+               goto fail_unmap_control;
+       }
+
+       wdt->feed_reg = ioremap(r2->start, resource_size(r2));
+       if (!wdt->feed_reg) {
+               dev_err(&pdev->dev, "failed to map memory\n");
+               error = -ENODEV;
+               goto fail_free_feed;
+       }
+
+       platform_set_drvdata(pdev, wdt);
+       ts72xx_wdt_pdev = pdev;
+       wdt->pdev = pdev;
+       mutex_init(&wdt->lock);
+
+       error = misc_register(&ts72xx_wdt_miscdev);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register miscdev\n");
+               goto fail_unmap_feed;
+       }
+
+       dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
+
+       return 0;
+
+fail_unmap_feed:
+       platform_set_drvdata(pdev, NULL);
+       iounmap(wdt->feed_reg);
+fail_free_feed:
+       release_mem_region(r2->start, resource_size(r2));
+fail_unmap_control:
+       iounmap(wdt->control_reg);
+fail_free_control:
+       release_mem_region(r1->start, resource_size(r1));
+fail:
+       kfree(wdt);
+       return error;
+}
+
+static __devexit int ts72xx_wdt_remove(struct platform_device *pdev)
+{
+       struct ts72xx_wdt *wdt = platform_get_drvdata(pdev);
+       struct resource *res;
+       int error;
+
+       error = misc_deregister(&ts72xx_wdt_miscdev);
+       platform_set_drvdata(pdev, NULL);
+
+       iounmap(wdt->feed_reg);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       release_mem_region(res->start, resource_size(res));
+
+       iounmap(wdt->control_reg);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       kfree(wdt);
+       return error;
+}
+
+static struct platform_driver ts72xx_wdt_driver = {
+       .probe          = ts72xx_wdt_probe,
+       .remove         = __devexit_p(ts72xx_wdt_remove),
+       .driver         = {
+               .name   = "ts72xx-wdt",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static __init int ts72xx_wdt_init(void)
+{
+       return platform_driver_register(&ts72xx_wdt_driver);
+}
+module_init(ts72xx_wdt_init);
+
+static __exit void ts72xx_wdt_exit(void)
+{
+       platform_driver_unregister(&ts72xx_wdt_driver);
+}
+module_exit(ts72xx_wdt_exit);
+
+MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
+MODULE_DESCRIPTION("TS-72xx SBC Watchdog");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ts72xx-wdt");
index d635566..9e9ed7b 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
 #include <linux/fs.h>
-#include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/uaccess.h>
 #include <linux/platform_device.h>
@@ -166,14 +165,6 @@ static long txx9wdt_ioctl(struct file *file, unsigned int cmd,
        }
 }
 
-static int txx9wdt_notify_sys(struct notifier_block *this, unsigned long code,
-       void *unused)
-{
-       if (code == SYS_DOWN || code == SYS_HALT)
-               txx9wdt_stop();
-       return NOTIFY_DONE;
-}
-
 static const struct file_operations txx9wdt_fops = {
        .owner          =       THIS_MODULE,
        .llseek         =       no_llseek,
@@ -189,10 +180,6 @@ static struct miscdevice txx9wdt_miscdev = {
        .fops   =       &txx9wdt_fops,
 };
 
-static struct notifier_block txx9wdt_notifier = {
-       .notifier_call = txx9wdt_notify_sys,
-};
-
 static int __init txx9wdt_probe(struct platform_device *dev)
 {
        struct resource *res;
@@ -221,13 +208,8 @@ static int __init txx9wdt_probe(struct platform_device *dev)
        if (!txx9wdt_reg)
                goto exit_busy;
 
-       ret = register_reboot_notifier(&txx9wdt_notifier);
-       if (ret)
-               goto exit;
-
        ret = misc_register(&txx9wdt_miscdev);
        if (ret) {
-               unregister_reboot_notifier(&txx9wdt_notifier);
                goto exit;
        }
 
@@ -249,14 +231,19 @@ exit:
 static int __exit txx9wdt_remove(struct platform_device *dev)
 {
        misc_deregister(&txx9wdt_miscdev);
-       unregister_reboot_notifier(&txx9wdt_notifier);
        clk_disable(txx9_imclk);
        clk_put(txx9_imclk);
        return 0;
 }
 
+static void txx9wdt_shutdown(struct platform_device *dev)
+{
+       txx9wdt_stop();
+}
+
 static struct platform_driver txx9wdt_driver = {
        .remove = __exit_p(txx9wdt_remove),
+       .shutdown = txx9wdt_shutdown,
        .driver = {
                .name = "txx9wdt",
                .owner = THIS_MODULE,
index f201acc..0f5288d 100644 (file)
@@ -201,7 +201,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        void __user *argp = (void __user *)arg;
        int __user *p = argp;
        int new_timeout;
-       static struct watchdog_info ident = {
+       static const struct watchdog_info ident = {
                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
                                                        WDIOF_MAGICCLOSE,
                .firmware_version = 1,
index 0560182..6e6743d 100644 (file)
@@ -371,7 +371,7 @@ static ssize_t wdt_write(struct file *file, const char __user *buf,
  *      according to their available features.
  */
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
        .firmware_version =     1,
        .identity = WATCHDOG_NAME,
index 5bfb1f2..94ec22b 100644 (file)
@@ -312,7 +312,7 @@ static long wdrtas_ioctl(struct file *file, unsigned int cmd,
 {
        int __user *argp = (void __user *)arg;
        int i;
-       static struct watchdog_info wdinfo = {
+       static const struct watchdog_info wdinfo = {
                .options = WDRTAS_SUPPORTED_MASK,
                .firmware_version = 0,
                .identity = "wdrtas",
index 3bbefe9..bfda2e9 100644 (file)
@@ -358,7 +358,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        int new_heartbeat;
        int status;
 
-       static struct watchdog_info ident = {
+       struct watchdog_info ident = {
                .options =              WDIOF_SETTIMEOUT|
                                        WDIOF_MAGICCLOSE|
                                        WDIOF_KEEPALIVEPING,
index f368dd8..7b22e3c 100644 (file)
@@ -412,7 +412,7 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd,
        int new_heartbeat;
        int status;
 
-       static struct watchdog_info ident = {
+       struct watchdog_info ident = {
                .options =              WDIOF_SETTIMEOUT|
                                        WDIOF_MAGICCLOSE|
                                        WDIOF_KEEPALIVEPING,
index 775bcd8..8c4b2d5 100644 (file)
@@ -213,7 +213,7 @@ static ssize_t wm831x_wdt_write(struct file *file,
        return count;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
        .identity = "WM831x Watchdog",
 };
index a2d2e8e..89dd7b0 100644 (file)
@@ -177,7 +177,7 @@ static ssize_t wm8350_wdt_write(struct file *file,
        return count;
 }
 
-static struct watchdog_info ident = {
+static const struct watchdog_info ident = {
        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
        .identity = "WM8350 Watchdog",
 };
index 864dac2..cc94bb9 100644 (file)
@@ -1,7 +1,6 @@
 
 config FSCACHE
        tristate "General filesystem local caching manager"
-       depends on EXPERIMENTAL
        select SLOW_WORK
        help
          This option enables a generic filesystem caching manager that can be
index 6bb2936..4d3e450 100644 (file)
@@ -269,8 +269,8 @@ struct uac_format_type_i_ext_descriptor {
        __u8 bLength;
        __u8 bDescriptorType;
        __u8 bDescriptorSubtype;
-       __u8 bSubslotSize;
        __u8 bFormatType;
+       __u8 bSubslotSize;
        __u8 bBitResolution;
        __u8 bHeaderLength;
        __u8 bControlSize;
index 1f57bb9..0985955 100644 (file)
@@ -544,7 +544,7 @@ struct snd_rawmidi_status {
  *  Timer section - /dev/snd/timer
  */
 
-#define SNDRV_TIMER_VERSION            SNDRV_PROTOCOL_VERSION(2, 0, 5)
+#define SNDRV_TIMER_VERSION            SNDRV_PROTOCOL_VERSION(2, 0, 6)
 
 enum {
        SNDRV_TIMER_CLASS_NONE = -1,
index 8f8b17a..7394365 100644 (file)
@@ -393,7 +393,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
            event == SNDRV_TIMER_EVENT_CONTINUE)
                resolution = snd_timer_resolution(ti);
        if (ti->ccallback)
-               ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution);
+               ti->ccallback(ti, event, &tstamp, resolution);
        if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
                return;
        timer = ti->timer;
index b865e45..5913717 100644 (file)
@@ -1558,7 +1558,7 @@ static int __devinit snd_card_miro_pnp(struct snd_miro *chip,
 
        err = pnp_activate_dev(devmc);
        if (err < 0) {
-               snd_printk(KERN_ERR "OPL syntg pnp configure failure: %d\n",
+               snd_printk(KERN_ERR "MC pnp configure failure: %d\n",
                                    err);
                return err;
        }
index a4af53b..becd90d 100644 (file)
@@ -144,12 +144,8 @@ struct snd_opti9xx {
 
        spinlock_t lock;
 
+       long wss_base;
        int irq;
-
-#ifdef CONFIG_PNP
-       struct pnp_dev *dev;
-       struct pnp_dev *devmpu;
-#endif /* CONFIG_PNP */
 };
 
 static int snd_opti9xx_pnp_is_probed;
@@ -159,12 +155,17 @@ static int snd_opti9xx_pnp_is_probed;
 static struct pnp_card_device_id snd_opti9xx_pnpids[] = {
 #ifndef OPTi93X
        /* OPTi 82C924 */
-       { .id = "OPT0924", .devs = { { "OPT0000" }, { "OPT0002" } }, .driver_data = 0x0924 },
+       { .id = "OPT0924",
+         .devs = { { "OPT0000" }, { "OPT0002" }, { "OPT0005" } },
+         .driver_data = 0x0924 },
        /* OPTi 82C925 */
-       { .id = "OPT0925", .devs = { { "OPT9250" }, { "OPT0002" } }, .driver_data = 0x0925 },
+       { .id = "OPT0925",
+         .devs = { { "OPT9250" }, { "OPT0002" }, { "OPT0005" } },
+         .driver_data = 0x0925 },
 #else
        /* OPTi 82C931/3 */
-       { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } }, .driver_data = 0x0931 },
+       { .id = "OPT0931", .devs = { { "OPT9310" }, { "OPT0002" } },
+         .driver_data = 0x0931 },
 #endif /* OPTi93X */
        { .id = "" }
 };
@@ -207,24 +208,34 @@ static int __devinit snd_opti9xx_init(struct snd_opti9xx *chip,
        chip->hardware = hardware;
        strcpy(chip->name, snd_opti9xx_names[hardware]);
 
-       chip->mc_base_size = opti9xx_mc_size[hardware];  
-
        spin_lock_init(&chip->lock);
 
        chip->irq = -1;
 
+#ifndef OPTi93X
+#ifdef CONFIG_PNP
+       if (isapnp && chip->mc_base)
+               /* PnP resource gives the least 10 bits */
+               chip->mc_base |= 0xc00;
+#endif /* CONFIG_PNP */
+       else {
+               chip->mc_base = 0xf8c;
+               chip->mc_base_size = opti9xx_mc_size[hardware];
+       }
+#else
+               chip->mc_base_size = opti9xx_mc_size[hardware];
+#endif
+
        switch (hardware) {
 #ifndef OPTi93X
        case OPTi9XX_HW_82C928:
        case OPTi9XX_HW_82C929:
-               chip->mc_base = 0xf8c;
                chip->password = (hardware == OPTi9XX_HW_82C928) ? 0xe2 : 0xe3;
                chip->pwd_reg = 3;
                break;
 
        case OPTi9XX_HW_82C924:
        case OPTi9XX_HW_82C925:
-               chip->mc_base = 0xf8c;
                chip->password = 0xe5;
                chip->pwd_reg = 3;
                break;
@@ -292,7 +303,7 @@ static unsigned char snd_opti9xx_read(struct snd_opti9xx *chip,
        spin_unlock_irqrestore(&chip->lock, flags);
        return retval;
 }
-       
+
 static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
                              unsigned char value)
 {
@@ -341,7 +352,7 @@ static void snd_opti9xx_write(struct snd_opti9xx *chip, unsigned char reg,
 
 
 static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
-                                          long wss_base,
+                                          long port,
                                           int irq, int dma1, int dma2,
                                           long mpu_port, int mpu_irq)
 {
@@ -354,16 +365,23 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
        switch (chip->hardware) {
 #ifndef OPTi93X
        case OPTi9XX_HW_82C924:
+               /* opti 929 mode (?), OPL3 clock output, audio enable */
                snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0xf0, 0xfc);
+               /* enable wave audio */
                snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x02);
 
        case OPTi9XX_HW_82C925:
+               /* enable WSS mode */
                snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), 0x80, 0x80);
+               /* OPL3 FM synthesis */
                snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(2), 0x00, 0x20);
+               /* disable Sound Blaster IRQ and DMA */
                snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0xf0, 0xff);
 #ifdef CS4231
+               /* cs4231/4248 fix enabled */
                snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x02, 0x02);
 #else
+               /* cs4231/4248 fix disabled */
                snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(5), 0x00, 0x02);
 #endif /* CS4231 */
                break;
@@ -411,21 +429,26 @@ static int __devinit snd_opti9xx_configure(struct snd_opti9xx *chip,
                return -EINVAL;
        }
 
-       switch (wss_base) {
-       case 0x530:
+       /* PnP resource says it decodes only 10 bits of address */
+       switch (port & 0x3ff) {
+       case 0x130:
+               chip->wss_base = 0x530;
                wss_base_bits = 0x00;
                break;
-       case 0x604:
+       case 0x204:
+               chip->wss_base = 0x604;
                wss_base_bits = 0x03;
                break;
-       case 0xe80:
+       case 0x280:
+               chip->wss_base = 0xe80;
                wss_base_bits = 0x01;
                break;
-       case 0xf40:
+       case 0x340:
+               chip->wss_base = 0xf40;
                wss_base_bits = 0x02;
                break;
        default:
-               snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", wss_base);
+               snd_printk(KERN_WARNING "WSS port 0x%lx not valid\n", port);
                goto __skip_base;
        }
        snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(1), wss_base_bits << 4, 0x30);
@@ -487,7 +510,7 @@ __skip_base:
 #endif /* CS4231 || OPTi93X */
 
 #ifndef OPTi93X
-        outb(irq_bits << 3 | dma_bits, wss_base);
+        outb(irq_bits << 3 | dma_bits, chip->wss_base);
 #else /* OPTi93X */
        snd_opti9xx_write(chip, OPTi9XX_MC_REG(3), (irq_bits << 3 | dma_bits));
 #endif /* OPTi93X */
@@ -729,15 +752,15 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
 {
        struct pnp_dev *pdev;
        int err;
+       struct pnp_dev *devmpu;
+#ifndef OPTi93X
+       struct pnp_dev *devmc;
+#endif
 
-       chip->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
-       if (chip->dev == NULL)
+       pdev = pnp_request_card_device(card, pid->devs[0].id, NULL);
+       if (pdev == NULL)
                return -EBUSY;
 
-       chip->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
-
-       pdev = chip->dev;
-
        err = pnp_activate_dev(pdev);
        if (err < 0) {
                snd_printk(KERN_ERR "AUDIO pnp configure failure: %d\n", err);
@@ -750,9 +773,24 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
        chip->mc_indir_index = pnp_port_start(pdev, 3) + 2;
        chip->mc_indir_size = pnp_port_len(pdev, 3) - 2;
 #else
-       if (pid->driver_data != 0x0924)
-               port = pnp_port_start(pdev, 1);
+       devmc = pnp_request_card_device(card, pid->devs[2].id, NULL);
+       if (devmc == NULL)
+               return -EBUSY;
+
+       err = pnp_activate_dev(devmc);
+       if (err < 0) {
+               snd_printk(KERN_ERR "MC pnp configure failure: %d\n", err);
+               return err;
+       }
+
+       port = pnp_port_start(pdev, 1);
        fm_port = pnp_port_start(pdev, 2) + 8;
+       /*
+        * The MC(0) is never accessed and card does not
+        * include it in the PnP resource range. OPTI93x include it.
+        */
+       chip->mc_base = pnp_port_start(devmc, 0) - 1;
+       chip->mc_base_size = pnp_port_len(devmc, 0) + 1;
 #endif /* OPTi93X */
        irq = pnp_irq(pdev, 0);
        dma1 = pnp_dma(pdev, 0);
@@ -760,16 +798,16 @@ static int __devinit snd_card_opti9xx_pnp(struct snd_opti9xx *chip,
        dma2 = pnp_dma(pdev, 1);
 #endif /* CS4231 || OPTi93X */
 
-       pdev = chip->devmpu;
-       if (pdev && mpu_port > 0) {
-               err = pnp_activate_dev(pdev);
+       devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
+
+       if (devmpu && mpu_port > 0) {
+               err = pnp_activate_dev(devmpu);
                if (err < 0) {
-                       snd_printk(KERN_ERR "AUDIO pnp configure failure\n");
+                       snd_printk(KERN_ERR "MPU401 pnp configure failure\n");
                        mpu_port = -1;
-                       chip->devmpu = NULL;
                } else {
-                       mpu_port = pnp_port_start(pdev, 0);
-                       mpu_irq = pnp_irq(pdev, 0);
+                       mpu_port = pnp_port_start(devmpu, 0);
+                       mpu_irq = pnp_irq(devmpu, 0);
                }
        }
        return pid->driver_data;
@@ -824,7 +862,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
        if (error)
                return error;
 
-       error = snd_wss_create(card, port + 4, -1, irq, dma1, xdma2,
+       error = snd_wss_create(card, chip->wss_base + 4, -1, irq, dma1, xdma2,
 #ifdef OPTi93X
                               WSS_HW_OPTI93X, WSS_HWSHARE_IRQ,
 #else
@@ -865,10 +903,11 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
        sprintf(card->shortname, "OPTi %s", card->driver);
 #if defined(CS4231) || defined(OPTi93X)
        sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
-               card->shortname, pcm->name, port + 4, irq, dma1, xdma2);
+               card->shortname, pcm->name,
+               chip->wss_base + 4, irq, dma1, xdma2);
 #else
        sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
-               card->shortname, pcm->name, port + 4, irq, dma1);
+               card->shortname, pcm->name, chip->wss_base + 4, irq, dma1);
 #endif /* CS4231 || OPTi93X */
 
        if (mpu_port <= 0 || mpu_port == SNDRV_AUTO_PORT)
@@ -1062,9 +1101,6 @@ static int __devinit snd_opti9xx_pnp_probe(struct pnp_card_link *pcard,
                snd_card_free(card);
                return error;
        }
-       if (hw <= OPTi9XX_HW_82C930)
-               chip->mc_base -= 0x80;
-
        error = snd_opti9xx_read_check(chip);
        if (error) {
                snd_printk(KERN_ERR "OPTI chip not found\n");
index 8d21a3f..8ccbcdd 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/io.h>
+#include <linux/delay.h>
 #include <asm/dma.h>
 #include <linux/isa.h>
 #include <sound/core.h>
index 7306346..7bec21b 100644 (file)
@@ -4,7 +4,7 @@
  */
 
 /*
- * Coprocessor access types 
+ * Coprocessor access types
  */
 #define COPR_CUSTOM            0x0001  /* Custom applications */
 #define COPR_MIDI              0x0002  /* MIDI (MPU-401) emulation */
index 1b86cb4..08e2185 100644 (file)
@@ -2,9 +2,9 @@ typedef struct vmidi_devc {
           int dev;
 
        /* State variables */
-          int opened;
+          int opened;
           spinlock_t lock;
-       
+
        /* MIDI fields */
           int my_mididev;
           int pair_mididev;
@@ -12,4 +12,3 @@ typedef struct vmidi_devc {
           int intr_active;
           void (*midi_input_intr) (int dev, unsigned char data);
        } vmidi_devc;
-
index 556cff9..567348b 100644 (file)
@@ -157,7 +157,7 @@ config SND_HDA_CODEC_INTELHDMI
 
 config SND_HDA_ELD
        def_bool y
-       depends on SND_HDA_CODEC_INTELHDMI
+       depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI
 
 config SND_HDA_CODEC_CIRRUS
        bool "Build Cirrus Logic codec support"
index 315a1c4..24bc195 100644 (file)
@@ -3,7 +3,7 @@ snd-hda-intel-objs := hda_intel.o
 snd-hda-codec-y := hda_codec.o
 snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
 snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
-snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
+snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
 snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
 snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
 
@@ -18,7 +18,7 @@ snd-hda-codec-ca0110-objs :=  patch_ca0110.o
 snd-hda-codec-conexant-objs := patch_conexant.o
 snd-hda-codec-via-objs :=      patch_via.o
 snd-hda-codec-nvhdmi-objs :=   patch_nvhdmi.o
-snd-hda-codec-intelhdmi-objs :=        patch_intelhdmi.o hda_eld.o
+snd-hda-codec-intelhdmi-objs :=        patch_intelhdmi.o
 
 # common driver
 obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
index 76d3c4c..5bd7cf4 100644 (file)
@@ -978,8 +978,9 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
  *
  * Returns 0 if successful, or a negative error code.
  */
-int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-                                   struct hda_codec **codecp)
+int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
+                               unsigned int codec_addr,
+                               struct hda_codec **codecp)
 {
        struct hda_codec *codec;
        char component[31];
@@ -1186,7 +1187,7 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_cleanup_stream);
  */
 
 /* FIXME: more better hash key? */
-#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
+#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
 #define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
 #define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
 #define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
@@ -1356,7 +1357,8 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
        if (!codec->no_trigger_sense) {
                pincap = snd_hda_query_pin_caps(codec, nid);
                if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
-                       snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+                       snd_hda_codec_read(codec, nid, 0,
+                                       AC_VERB_SET_PIN_SENSE, 0);
        }
        return snd_hda_codec_read(codec, nid, 0,
                                  AC_VERB_GET_PIN_SENSE, 0);
@@ -1372,8 +1374,8 @@ EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
  */
 int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
 {
-        u32 sense = snd_hda_pin_sense(codec, nid);
-        return !!(sense & AC_PINSENSE_PRESENCE);
+       u32 sense = snd_hda_pin_sense(codec, nid);
+       return !!(sense & AC_PINSENSE_PRESENCE);
 }
 EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
 
@@ -1952,7 +1954,7 @@ int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
        err = snd_hda_ctl_add(codec, 0, kctl);
        if (err < 0)
                return err;
-       
+
        for (s = slaves; *s; s++) {
                struct snd_kcontrol *sctl;
                int i = 0;
@@ -2439,27 +2441,27 @@ static struct snd_kcontrol_new dig_mixes[] = {
        {
                .access = SNDRV_CTL_ELEM_ACCESS_READ,
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
                .info = snd_hda_spdif_mask_info,
                .get = snd_hda_spdif_cmask_get,
        },
        {
                .access = SNDRV_CTL_ELEM_ACCESS_READ,
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
                .info = snd_hda_spdif_mask_info,
                .get = snd_hda_spdif_pmask_get,
        },
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
                .info = snd_hda_spdif_mask_info,
                .get = snd_hda_spdif_default_get,
                .put = snd_hda_spdif_default_put,
        },
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
                .info = snd_hda_spdif_out_switch_info,
                .get = snd_hda_spdif_out_switch_get,
                .put = snd_hda_spdif_out_switch_put,
@@ -2610,7 +2612,7 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol,
 static struct snd_kcontrol_new dig_in_ctls[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
+               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
                .info = snd_hda_spdif_in_switch_info,
                .get = snd_hda_spdif_in_switch_get,
                .put = snd_hda_spdif_in_switch_put,
@@ -2618,7 +2620,7 @@ static struct snd_kcontrol_new dig_in_ctls[] = {
        {
                .access = SNDRV_CTL_ELEM_ACCESS_READ,
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
                .info = snd_hda_spdif_mask_info,
                .get = snd_hda_spdif_in_status_get,
        },
@@ -2883,7 +2885,7 @@ int /*__devinit*/ snd_hda_build_controls(struct hda_bus *bus)
                int err = snd_hda_codec_build_controls(codec);
                if (err < 0) {
                        printk(KERN_ERR "hda_codec: cannot build controls"
-                              "for #%d (error %d)\n", codec->addr, err); 
+                              "for #%d (error %d)\n", codec->addr, err);
                        err = snd_hda_codec_reset(codec);
                        if (err < 0) {
                                printk(KERN_ERR
@@ -2979,8 +2981,12 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
        val |= channels - 1;
 
        switch (snd_pcm_format_width(format)) {
-       case 8:  val |= 0x00; break;
-       case 16: val |= 0x10; break;
+       case 8:
+               val |= 0x00;
+               break;
+       case 16:
+               val |= 0x10;
+               break;
        case 20:
        case 24:
        case 32:
@@ -3298,7 +3304,8 @@ static int get_empty_pcm_device(struct hda_bus *bus, int type)
                if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
                        return audio_idx[type][i];
 
-       snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
+       snd_printk(KERN_WARNING "Too many %s devices\n",
+               snd_hda_pcm_type_name[type]);
        return -EAGAIN;
 }
 
@@ -3336,7 +3343,7 @@ int snd_hda_codec_build_pcms(struct hda_codec *codec)
                err = codec->patch_ops.build_pcms(codec);
                if (err < 0) {
                        printk(KERN_ERR "hda_codec: cannot build PCMs"
-                              "for #%d (error %d)\n", codec->addr, err); 
+                              "for #%d (error %d)\n", codec->addr, err);
                        err = snd_hda_codec_reset(codec);
                        if (err < 0) {
                                printk(KERN_ERR
@@ -3466,8 +3473,8 @@ EXPORT_SYMBOL_HDA(snd_hda_check_board_config);
 
 /**
  * snd_hda_check_board_codec_sid_config - compare the current codec
-                                         subsystem ID with the
-                                         config table
+                                       subsystem ID with the
+                                       config table
 
           This is important for Gateway notebooks with SB450 HDA Audio
           where the vendor ID of the PCI device is:
@@ -3607,7 +3614,7 @@ void snd_hda_update_power_acct(struct hda_codec *codec)
  *
  * Increment the power-up counter and power up the hardware really when
  * not turned on yet.
- */ 
+ */
 void snd_hda_power_up(struct hda_codec *codec)
 {
        struct hda_bus *bus = codec->bus;
@@ -3636,7 +3643,7 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
  *
  * Decrement the power-up counter and schedules the power-off work if
  * the counter rearches to zero.
- */ 
+ */
 void snd_hda_power_down(struct hda_codec *codec)
 {
        --codec->power_count;
@@ -3662,7 +3669,7 @@ EXPORT_SYMBOL_HDA(snd_hda_power_down);
  *
  * This function is supposed to be set or called from the check_power_status
  * patch ops.
- */ 
+ */
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
                                 struct hda_loopback_check *check,
                                 hda_nid_t nid)
@@ -3830,7 +3837,7 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
 {
        /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
        if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
-               set_dig_out_convert(codec, nid, 
+               set_dig_out_convert(codec, nid,
                                    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
                                    -1);
        snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
@@ -4089,13 +4096,13 @@ static int is_in_nid_list(hda_nid_t nid, hda_nid_t *list)
 /*
  * Sort an associated group of pins according to their sequence numbers.
  */
-static void sort_pins_by_sequence(hda_nid_t * pins, short * sequences,
+static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
                                  int num_pins)
 {
        int i, j;
        short seq;
        hda_nid_t nid;
-       
+
        for (i = 0; i < num_pins; i++) {
                for (j = i + 1; j < num_pins; j++) {
                        if (sequences[i] > sequences[j]) {
@@ -4123,7 +4130,7 @@ static void sort_pins_by_sequence(hda_nid_t * pins, short * sequences,
  * is detected, one of speaker of HP pins is assigned as the primary
  * output, i.e. to line_out_pins[0].  So, line_outs is always positive
  * if any analog output exists.
- * 
+ *
  * The analog input pins are assigned to input_pins array.
  * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
  * respectively.
@@ -4186,9 +4193,9 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                case AC_JACK_SPEAKER:
                        seq = get_defcfg_sequence(def_conf);
                        assoc = get_defcfg_association(def_conf);
-                       if (! assoc)
+                       if (!assoc)
                                continue;
-                       if (! assoc_speaker)
+                       if (!assoc_speaker)
                                assoc_speaker = assoc;
                        else if (assoc_speaker != assoc)
                                continue;
@@ -4286,7 +4293,7 @@ int snd_hda_parse_pin_def_config(struct hda_codec *codec,
                              cfg->speaker_outs);
        sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
                              cfg->hp_outs);
-       
+
        /* if we have only one mic, make it AUTO_PIN_MIC */
        if (!cfg->input_pins[AUTO_PIN_MIC] &&
            cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
@@ -4436,7 +4443,7 @@ EXPORT_SYMBOL_HDA(snd_hda_resume);
 /**
  * snd_array_new - get a new element from the given array
  * @array: the array object
- * 
+ *
  * Get a new element from the given array.  If it exceeds the
  * pre-allocated array size, re-allocate the array.
  *
index 4228f2f..dcd2244 100644 (file)
@@ -331,6 +331,7 @@ int snd_hdmi_get_eld_size(struct hda_codec *codec, hda_nid_t nid)
        return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
                                                 AC_DIPSIZE_ELD_BUF);
 }
+EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
 
 int snd_hdmi_get_eld(struct hdmi_eld *eld,
                     struct hda_codec *codec, hda_nid_t nid)
@@ -366,6 +367,7 @@ int snd_hdmi_get_eld(struct hdmi_eld *eld,
        kfree(buf);
        return ret;
 }
+EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
 
 static void hdmi_show_short_audio_desc(struct cea_sad *a)
 {
@@ -404,6 +406,7 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
        }
        buf[j] = '\0';  /* necessary when j == 0 */
 }
+EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
 
 void snd_hdmi_show_eld(struct hdmi_eld *e)
 {
@@ -422,6 +425,7 @@ void snd_hdmi_show_eld(struct hdmi_eld *e)
        for (i = 0; i < e->sad_count; i++)
                hdmi_show_short_audio_desc(e->sad + i);
 }
+EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
 
 #ifdef CONFIG_PROC_FS
 
@@ -580,6 +584,7 @@ int snd_hda_eld_proc_new(struct hda_codec *codec, struct hdmi_eld *eld,
 
        return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
 
 void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
 {
@@ -588,5 +593,6 @@ void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
                eld->proc_entry = NULL;
        }
 }
+EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
 
 #endif /* CONFIG_PROC_FS */
index d5c93ad..43b7cfb 100644 (file)
@@ -267,7 +267,8 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define RIRB_INT_MASK          0x05
 
 /* STATESTS int mask: S3,SD2,SD1,SD0 */
-#define AZX_MAX_CODECS         4
+#define AZX_MAX_CODECS         8
+#define AZX_DEFAULT_CODECS     4
 #define STATESTS_INT_MASK      ((1 << AZX_MAX_CODECS) - 1)
 
 /* SD_CTL bits */
@@ -1367,6 +1368,7 @@ static void azx_bus_reset(struct hda_bus *bus)
 
 /* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
 static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
+       [AZX_DRIVER_NVIDIA] = 8,
        [AZX_DRIVER_TERA] = 1,
 };
 
@@ -1399,7 +1401,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
        codecs = 0;
        max_slots = azx_max_codecs[chip->driver_type];
        if (!max_slots)
-               max_slots = AZX_MAX_CODECS;
+               max_slots = AZX_DEFAULT_CODECS;
 
        /* First try to probe all given codec slots */
        for (c = 0; c < max_slots; c++) {
@@ -2263,10 +2265,12 @@ static int azx_dev_free(struct snd_device *device)
 static struct snd_pci_quirk position_fix_list[] __devinitdata = {
        SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
        {}
 };
 
@@ -2354,6 +2358,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
 static struct snd_pci_quirk msi_black_list[] __devinitdata = {
        SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
        SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */
+       SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
        {}
 };
 
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
new file mode 100644 (file)
index 0000000..2c2bafb
--- /dev/null
@@ -0,0 +1,849 @@
+/*
+ *
+ *  patch_hdmi.c - routines for HDMI/DisplayPort codecs
+ *
+ *  Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
+ *
+ *  Authors:
+ *                     Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  Maintained by:
+ *                     Wu Fengguang <wfg@linux.intel.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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+
+struct hdmi_spec {
+       int num_cvts;
+       int num_pins;
+       hda_nid_t cvt[MAX_HDMI_CVTS+1];  /* audio sources */
+       hda_nid_t pin[MAX_HDMI_PINS+1];  /* audio sinks */
+
+       /*
+        * source connection for each pin
+        */
+       hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
+
+       /*
+        * HDMI sink attached to each pin
+        */
+       struct hdmi_eld sink_eld[MAX_HDMI_PINS];
+
+       /*
+        * export one pcm per pipe
+        */
+       struct hda_pcm  pcm_rec[MAX_HDMI_CVTS];
+
+       /*
+        * nvhdmi specific
+        */
+       struct hda_multi_out multiout;
+       unsigned int codec_type;
+};
+
+
+struct hdmi_audio_infoframe {
+       u8 type; /* 0x84 */
+       u8 ver;  /* 0x01 */
+       u8 len;  /* 0x0a */
+
+       u8 checksum;    /* PB0 */
+       u8 CC02_CT47;   /* CC in bits 0:2, CT in 4:7 */
+       u8 SS01_SF24;
+       u8 CXT04;
+       u8 CA;
+       u8 LFEPBL01_LSV36_DM_INH7;
+       u8 reserved[5]; /* PB6 - PB10 */
+};
+
+/*
+ * CEA speaker placement:
+ *
+ *        FLH       FCH        FRH
+ *  FLW    FL  FLC   FC   FRC   FR   FRW
+ *
+ *                                  LFE
+ *                     TC
+ *
+ *          RL  RLC   RC   RRC   RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+       FL  = (1 <<  0),        /* Front Left           */
+       FC  = (1 <<  1),        /* Front Center         */
+       FR  = (1 <<  2),        /* Front Right          */
+       FLC = (1 <<  3),        /* Front Left Center    */
+       FRC = (1 <<  4),        /* Front Right Center   */
+       RL  = (1 <<  5),        /* Rear Left            */
+       RC  = (1 <<  6),        /* Rear Center          */
+       RR  = (1 <<  7),        /* Rear Right           */
+       RLC = (1 <<  8),        /* Rear Left Center     */
+       RRC = (1 <<  9),        /* Rear Right Center    */
+       LFE = (1 << 10),        /* Low Frequency Effect */
+       FLW = (1 << 11),        /* Front Left Wide      */
+       FRW = (1 << 12),        /* Front Right Wide     */
+       FLH = (1 << 13),        /* Front Left High      */
+       FCH = (1 << 14),        /* Front Center High    */
+       FRH = (1 << 15),        /* Front Right High     */
+       TC  = (1 << 16),        /* Top Center           */
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static int eld_speaker_allocation_bits[] = {
+       [0] = FL | FR,
+       [1] = LFE,
+       [2] = FC,
+       [3] = RL | RR,
+       [4] = RC,
+       [5] = FLC | FRC,
+       [6] = RLC | RRC,
+       /* the following are not defined in ELD yet */
+       [7] = FLW | FRW,
+       [8] = FLH | FRH,
+       [9] = TC,
+       [10] = FCH,
+};
+
+struct cea_channel_speaker_allocation {
+       int ca_index;
+       int speakers[8];
+
+       /* derived values, just for convenience */
+       int channels;
+       int spk_mask;
+};
+
+/*
+ * ALSA sequence is:
+ *
+ *       surround40   surround41   surround50   surround51   surround71
+ * ch0   front left   =            =            =            =
+ * ch1   front right  =            =            =            =
+ * ch2   rear left    =            =            =            =
+ * ch3   rear right   =            =            =            =
+ * ch4                LFE          center       center       center
+ * ch5                                          LFE          LFE
+ * ch6                                                       side left
+ * ch7                                                       side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+       /* stereo */
+       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* 2.1 */
+       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* Dolby Surround */
+       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* surround40 */
+       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+       /* 4ch */
+       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+       /* surround41 */
+       [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
+       /* surround50 */
+       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+       /* surround51 */
+       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+       /* 7.1 */
+       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_setup_channel_allocation().
+ */
+static struct cea_channel_speaker_allocation channel_allocations[] = {
+/*                       channel:   7     6    5    4    3     2    1    0  */
+{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
+                                /* 2.1 */
+{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
+                                /* Dolby Surround */
+{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
+                                /* surround40 */
+{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
+                                /* surround41 */
+{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
+                                /* surround50 */
+{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+                                /* surround51 */
+{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+                                /* 6.1 */
+{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+                                /* surround71 */
+{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+
+{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
+{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
+};
+
+
+/*
+ * HDMI routines
+ */
+
+static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+{
+       int i;
+
+       for (i = 0; nids[i]; i++)
+               if (nids[i] == nid)
+                       return i;
+
+       snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+       return -EINVAL;
+}
+
+static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
+                             struct hdmi_eld *eld)
+{
+       if (!snd_hdmi_get_eld(eld, codec, pin_nid))
+               snd_hdmi_show_eld(eld);
+}
+
+#ifdef BE_PARANOID
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
+                               int *packet_index, int *byte_index)
+{
+       int val;
+
+       val = snd_hda_codec_read(codec, pin_nid, 0,
+                                AC_VERB_GET_HDMI_DIP_INDEX, 0);
+
+       *packet_index = val >> 5;
+       *byte_index = val & 0x1f;
+}
+#endif
+
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
+                               int packet_index, int byte_index)
+{
+       int val;
+
+       val = (packet_index << 5) | (byte_index & 0x1f);
+
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
+                               unsigned char val)
+{
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+       /* Unmute */
+       if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
+               snd_hda_codec_write(codec, pin_nid, 0,
+                               AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+       /* Enable pin out */
+       snd_hda_codec_write(codec, pin_nid, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+}
+
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
+{
+       return 1 + snd_hda_codec_read(codec, nid, 0,
+                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hda_codec *codec,
+                                  hda_nid_t nid, int chs)
+{
+       if (chs != hdmi_get_channel_count(codec, nid))
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+}
+
+
+/*
+ * Channel mapping routines
+ */
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+       int i, j;
+       struct cea_channel_speaker_allocation *p;
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               p = channel_allocations + i;
+               p->channels = 0;
+               p->spk_mask = 0;
+               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+                       if (p->speakers[j]) {
+                               p->channels++;
+                               p->spk_mask |= p->speakers[j];
+                       }
+       }
+}
+
+/*
+ * The transformation takes two steps:
+ *
+ *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ *           spk_mask => (channel_allocations[])         => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
+                                        struct hdmi_audio_infoframe *ai)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_eld *eld;
+       int i;
+       int spk_mask = 0;
+       int channels = 1 + (ai->CC02_CT47 & 0x7);
+       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+       /*
+        * CA defaults to 0 for basic stereo audio
+        */
+       if (channels <= 2)
+               return 0;
+
+       i = hda_node_index(spec->pin_cvt, nid);
+       if (i < 0)
+               return 0;
+       eld = &spec->sink_eld[i];
+
+       /*
+        * HDMI sink's ELD info cannot always be retrieved for now, e.g.
+        * in console or for audio devices. Assume the highest speakers
+        * configuration, to _not_ prohibit multi-channel audio playback.
+        */
+       if (!eld->spk_alloc)
+               eld->spk_alloc = 0xffff;
+
+       /*
+        * expand ELD's speaker allocation mask
+        *
+        * ELD tells the speaker mask in a compact(paired) form,
+        * expand ELD's notions to match the ones used by Audio InfoFrame.
+        */
+       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+               if (eld->spk_alloc & (1 << i))
+                       spk_mask |= eld_speaker_allocation_bits[i];
+       }
+
+       /* search for the first working match in the CA table */
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if (channels == channel_allocations[i].channels &&
+                   (spk_mask & channel_allocations[i].spk_mask) ==
+                               channel_allocations[i].spk_mask) {
+                       ai->CA = channel_allocations[i].ca_index;
+                       break;
+               }
+       }
+
+       snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
+       snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+                   ai->CA, channels, buf);
+
+       return ai->CA;
+}
+
+static void hdmi_debug_channel_mapping(struct hda_codec *codec,
+                                      hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       int i;
+       int slot;
+
+       for (i = 0; i < 8; i++) {
+               slot = snd_hda_codec_read(codec, pin_nid, 0,
+                                               AC_VERB_GET_HDMI_CHAN_SLOT, i);
+               printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
+                                               slot >> 4, slot & 0xf);
+       }
+#endif
+}
+
+
+static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+                                      hda_nid_t pin_nid,
+                                      struct hdmi_audio_infoframe *ai)
+{
+       int i;
+       int ca = ai->CA;
+       int err;
+
+       if (hdmi_channel_mapping[ca][1] == 0) {
+               for (i = 0; i < channel_allocations[ca].channels; i++)
+                       hdmi_channel_mapping[ca][i] = i | (i << 4);
+               for (; i < 8; i++)
+                       hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
+       }
+
+       for (i = 0; i < 8; i++) {
+               err = snd_hda_codec_write(codec, pin_nid, 0,
+                                         AC_VERB_SET_HDMI_CHAN_SLOT,
+                                         hdmi_channel_mapping[ca][i]);
+               if (err) {
+                       snd_printdd(KERN_NOTICE
+                                   "HDMI: channel mapping failed\n");
+                       break;
+               }
+       }
+
+       hdmi_debug_channel_mapping(codec, pin_nid);
+}
+
+
+/*
+ * Audio InfoFrame routines
+ */
+
+/*
+ * Enable Audio InfoFrame Transmission
+ */
+static void hdmi_start_infoframe_trans(struct hda_codec *codec,
+                                      hda_nid_t pin_nid)
+{
+       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+                                               AC_DIPXMIT_BEST);
+}
+
+/*
+ * Disable Audio InfoFrame Transmission
+ */
+static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
+                                     hda_nid_t pin_nid)
+{
+       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+                                               AC_DIPXMIT_DISABLE);
+}
+
+static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       int i;
+       int size;
+
+       size = snd_hdmi_get_eld_size(codec, pin_nid);
+       printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
+
+       for (i = 0; i < 8; i++) {
+               size = snd_hda_codec_read(codec, pin_nid, 0,
+                                               AC_VERB_GET_HDMI_DIP_SIZE, i);
+               printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+       }
+#endif
+}
+
+static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+#ifdef BE_PARANOID
+       int i, j;
+       int size;
+       int pi, bi;
+       for (i = 0; i < 8; i++) {
+               size = snd_hda_codec_read(codec, pin_nid, 0,
+                                               AC_VERB_GET_HDMI_DIP_SIZE, i);
+               if (size == 0)
+                       continue;
+
+               hdmi_set_dip_index(codec, pin_nid, i, 0x0);
+               for (j = 1; j < 1000; j++) {
+                       hdmi_write_dip_byte(codec, pin_nid, 0x0);
+                       hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
+                       if (pi != i)
+                               snd_printd(KERN_INFO "dip index %d: %d != %d\n",
+                                               bi, pi, i);
+                       if (bi == 0) /* byte index wrapped around */
+                               break;
+               }
+               snd_printd(KERN_INFO
+                       "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
+                       i, size, j);
+       }
+#endif
+}
+
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
+{
+       u8 *bytes = (u8 *)ai;
+       u8 sum = 0;
+       int i;
+
+       ai->checksum = 0;
+
+       for (i = 0; i < sizeof(*ai); i++)
+               sum += bytes[i];
+
+       ai->checksum = -sum;
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+                                     hda_nid_t pin_nid,
+                                     struct hdmi_audio_infoframe *ai)
+{
+       u8 *bytes = (u8 *)ai;
+       int i;
+
+       hdmi_debug_dip_size(codec, pin_nid);
+       hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
+
+       hdmi_checksum_audio_infoframe(ai);
+
+       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+       for (i = 0; i < sizeof(*ai); i++)
+               hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
+}
+
+static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
+                                   struct hdmi_audio_infoframe *ai)
+{
+       u8 *bytes = (u8 *)ai;
+       u8 val;
+       int i;
+
+       if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
+                                                           != AC_DIPXMIT_BEST)
+               return false;
+
+       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+       for (i = 0; i < sizeof(*ai); i++) {
+               val = snd_hda_codec_read(codec, pin_nid, 0,
+                                        AC_VERB_GET_HDMI_DIP_DATA, 0);
+               if (val != bytes[i])
+                       return false;
+       }
+
+       return true;
+}
+
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
+                                       struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       hda_nid_t pin_nid;
+       int i;
+       struct hdmi_audio_infoframe ai = {
+               .type           = 0x84,
+               .ver            = 0x01,
+               .len            = 0x0a,
+               .CC02_CT47      = substream->runtime->channels - 1,
+       };
+
+       hdmi_setup_channel_allocation(codec, nid, &ai);
+
+       for (i = 0; i < spec->num_pins; i++) {
+               if (spec->pin_cvt[i] != nid)
+                       continue;
+               if (!spec->sink_eld[i].monitor_present)
+                       continue;
+
+               pin_nid = spec->pin[i];
+               if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+                       snd_printdd("hdmi_setup_audio_infoframe: "
+                                   "cvt=%d pin=%d channels=%d\n",
+                                   nid, pin_nid,
+                                   substream->runtime->channels);
+                       hdmi_setup_channel_mapping(codec, pin_nid, &ai);
+                       hdmi_stop_infoframe_trans(codec, pin_nid);
+                       hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+                       hdmi_start_infoframe_trans(codec, pin_nid);
+               }
+       }
+}
+
+
+/*
+ * Unsolicited events
+ */
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+       int pind = !!(res & AC_UNSOL_RES_PD);
+       int eldv = !!(res & AC_UNSOL_RES_ELDV);
+       int index;
+
+       printk(KERN_INFO
+               "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+               tag, pind, eldv);
+
+       index = hda_node_index(spec->pin, tag);
+       if (index < 0)
+               return;
+
+       spec->sink_eld[index].monitor_present = pind;
+       spec->sink_eld[index].eld_valid = eldv;
+
+       if (pind && eldv) {
+               hdmi_get_show_eld(codec, spec->pin[index],
+                                 &spec->sink_eld[index]);
+               /* TODO: do real things about ELD */
+       }
+}
+
+static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+       int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+       int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
+       int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
+
+       printk(KERN_INFO
+               "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+               tag,
+               subtag,
+               cp_state,
+               cp_ready);
+
+       /* TODO */
+       if (cp_state)
+               ;
+       if (cp_ready)
+               ;
+}
+
+
+static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+       int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+
+       if (hda_node_index(spec->pin, tag) < 0) {
+               snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
+               return;
+       }
+
+       if (subtag == 0)
+               hdmi_intrinsic_event(codec, res);
+       else
+               hdmi_non_intrinsic_event(codec, res);
+}
+
+/*
+ * Callbacks
+ */
+
+static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+                             u32 stream_tag, int format)
+{
+       int tag;
+       int fmt;
+
+       tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
+       fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
+
+       snd_printdd("hdmi_setup_stream: "
+                   "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
+                   nid,
+                   tag == stream_tag ? "" : "new-",
+                   stream_tag,
+                   fmt == format ? "" : "new-",
+                   format);
+
+       if (tag != stream_tag)
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_CHANNEL_STREAMID,
+                                   stream_tag << 4);
+       if (fmt != format)
+               snd_hda_codec_write(codec, nid, 0,
+                                   AC_VERB_SET_STREAM_FORMAT, format);
+}
+
+/*
+ * HDA/HDMI auto parsing
+ */
+
+static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+       struct hdmi_spec *spec = codec->spec;
+       hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+       int conn_len, curr;
+       int index;
+
+       if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
+               snd_printk(KERN_WARNING
+                          "HDMI: pin %d wcaps %#x "
+                          "does not support connection list\n",
+                          pin_nid, get_wcaps(codec, pin_nid));
+               return -EINVAL;
+       }
+
+       conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
+                                          HDA_MAX_CONNECTIONS);
+       if (conn_len > 1)
+               curr = snd_hda_codec_read(codec, pin_nid, 0,
+                                         AC_VERB_GET_CONNECT_SEL, 0);
+       else
+               curr = 0;
+
+       index = hda_node_index(spec->pin, pin_nid);
+       if (index < 0)
+               return -EINVAL;
+
+       spec->pin_cvt[index] = conn_list[curr];
+
+       return 0;
+}
+
+static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
+                              struct hdmi_eld *eld)
+{
+       int present = snd_hda_pin_sense(codec, pin_nid);
+
+       eld->monitor_present    = !!(present & AC_PINSENSE_PRESENCE);
+       eld->eld_valid          = !!(present & AC_PINSENSE_ELDV);
+
+       if (present & AC_PINSENSE_ELDV)
+               hdmi_get_show_eld(codec, pin_nid, eld);
+}
+
+static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+       struct hdmi_spec *spec = codec->spec;
+
+       if (spec->num_pins >= MAX_HDMI_PINS) {
+               snd_printk(KERN_WARNING
+                          "HDMI: no space for pin %d\n", pin_nid);
+               return -EINVAL;
+       }
+
+       hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+
+       spec->pin[spec->num_pins] = pin_nid;
+       spec->num_pins++;
+
+       /*
+        * It is assumed that converter nodes come first in the node list and
+        * hence have been registered and usable now.
+        */
+       return hdmi_read_pin_conn(codec, pin_nid);
+}
+
+static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+{
+       struct hdmi_spec *spec = codec->spec;
+
+       if (spec->num_cvts >= MAX_HDMI_CVTS) {
+               snd_printk(KERN_WARNING
+                          "HDMI: no space for converter %d\n", nid);
+               return -EINVAL;
+       }
+
+       spec->cvt[spec->num_cvts] = nid;
+       spec->num_cvts++;
+
+       return 0;
+}
+
+static int hdmi_parse_codec(struct hda_codec *codec)
+{
+       hda_nid_t nid;
+       int i, nodes;
+
+       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+       if (!nid || nodes < 0) {
+               snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < nodes; i++, nid++) {
+               unsigned int caps;
+               unsigned int type;
+
+               caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+               type = get_wcaps_type(caps);
+
+               if (!(caps & AC_WCAP_DIGITAL))
+                       continue;
+
+               switch (type) {
+               case AC_WID_AUD_OUT:
+                       if (hdmi_add_cvt(codec, nid) < 0)
+                               return -EINVAL;
+                       break;
+               case AC_WID_PIN:
+                       caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+                       if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+                               continue;
+                       if (hdmi_add_pin(codec, nid) < 0)
+                               return -EINVAL;
+                       break;
+               }
+       }
+
+       /*
+        * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
+        * can be lost and presence sense verb will become inaccurate if the
+        * HDA link is powered off at hot plug or hw initialization time.
+        */
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+       if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
+             AC_PWRST_EPSS))
+               codec->bus->power_keep_link_on = 1;
+#endif
+
+       return 0;
+}
+
index 918f403..88d0351 100644 (file)
  *
  * The HDA correspondence of pipes/ports are converter/pin nodes.
  */
-#define INTEL_HDMI_CVTS        2
-#define INTEL_HDMI_PINS        3
+#define MAX_HDMI_CVTS  2
+#define MAX_HDMI_PINS  3
 
-static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
+#include "patch_hdmi.c"
+
+static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
        "INTEL HDMI 0",
        "INTEL HDMI 1",
 };
 
-struct intel_hdmi_spec {
-       int num_cvts;
-       int num_pins;
-       hda_nid_t cvt[INTEL_HDMI_CVTS+1];  /* audio sources */
-       hda_nid_t pin[INTEL_HDMI_PINS+1];  /* audio sinks */
-
-       /*
-        * source connection for each pin
-        */
-       hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
-
-       /*
-        * HDMI sink attached to each pin
-        */
-       struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
-
-       /*
-        * export one pcm per pipe
-        */
-       struct hda_pcm  pcm_rec[INTEL_HDMI_CVTS];
-};
-
-struct hdmi_audio_infoframe {
-       u8 type; /* 0x84 */
-       u8 ver;  /* 0x01 */
-       u8 len;  /* 0x0a */
-
-       u8 checksum;    /* PB0 */
-       u8 CC02_CT47;   /* CC in bits 0:2, CT in 4:7 */
-       u8 SS01_SF24;
-       u8 CXT04;
-       u8 CA;
-       u8 LFEPBL01_LSV36_DM_INH7;
-       u8 reserved[5]; /* PB6 - PB10 */
-};
-
-/*
- * CEA speaker placement:
- *
- *        FLH       FCH        FRH
- *  FLW    FL  FLC   FC   FRC   FR   FRW
- *
- *                                  LFE
- *                     TC
- *
- *          RL  RLC   RC   RRC   RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
- */
-enum cea_speaker_placement {
-       FL  = (1 <<  0),        /* Front Left           */
-       FC  = (1 <<  1),        /* Front Center         */
-       FR  = (1 <<  2),        /* Front Right          */
-       FLC = (1 <<  3),        /* Front Left Center    */
-       FRC = (1 <<  4),        /* Front Right Center   */
-       RL  = (1 <<  5),        /* Rear Left            */
-       RC  = (1 <<  6),        /* Rear Center          */
-       RR  = (1 <<  7),        /* Rear Right           */
-       RLC = (1 <<  8),        /* Rear Left Center     */
-       RRC = (1 <<  9),        /* Rear Right Center    */
-       LFE = (1 << 10),        /* Low Frequency Effect */
-       FLW = (1 << 11),        /* Front Left Wide      */
-       FRW = (1 << 12),        /* Front Right Wide     */
-       FLH = (1 << 13),        /* Front Left High      */
-       FCH = (1 << 14),        /* Front Center High    */
-       FRH = (1 << 15),        /* Front Right High     */
-       TC  = (1 << 16),        /* Top Center           */
-};
-
-/*
- * ELD SA bits in the CEA Speaker Allocation data block
- */
-static int eld_speaker_allocation_bits[] = {
-       [0] = FL | FR,
-       [1] = LFE,
-       [2] = FC,
-       [3] = RL | RR,
-       [4] = RC,
-       [5] = FLC | FRC,
-       [6] = RLC | RRC,
-       /* the following are not defined in ELD yet */
-       [7] = FLW | FRW,
-       [8] = FLH | FRH,
-       [9] = TC,
-       [10] = FCH,
-};
-
-struct cea_channel_speaker_allocation {
-       int ca_index;
-       int speakers[8];
-
-       /* derived values, just for convenience */
-       int channels;
-       int spk_mask;
-};
-
-/*
- * ALSA sequence is:
- *
- *       surround40   surround41   surround50   surround51   surround71
- * ch0   front left   =            =            =            =
- * ch1   front right  =            =            =            =
- * ch2   rear left    =            =            =            =
- * ch3   rear right   =            =            =            =
- * ch4                LFE          center       center       center
- * ch5                                          LFE          LFE
- * ch6                                                       side left
- * ch7                                                       side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
-       /* stereo */
-       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* 2.1 */
-       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* Dolby Surround */
-       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* surround40 */
-       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
-       /* 4ch */
-       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
-       /* surround41 */
-       [0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
-       /* surround50 */
-       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
-       /* surround51 */
-       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
-       /* 7.1 */
-       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
-
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_setup_channel_allocation().
- */
-static struct cea_channel_speaker_allocation channel_allocations[] = {
-/*                       channel:   7     6    5    4    3     2    1    0  */
-{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
-                                /* 2.1 */
-{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
-                                /* Dolby Surround */
-{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
-                                /* surround40 */
-{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
-                                /* surround41 */
-{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
-                                /* surround50 */
-{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-                                /* surround51 */
-{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* 6.1 */
-{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* surround71 */
-{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-
-{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
-{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
-};
-
-/*
- * HDA/HDMI auto parsing
- */
-
-static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
-{
-       int i;
-
-       for (i = 0; nids[i]; i++)
-               if (nids[i] == nid)
-                       return i;
-
-       snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
-       return -EINVAL;
-}
-
-static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-       hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
-       int conn_len, curr;
-       int index;
-
-       if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
-               snd_printk(KERN_WARNING
-                          "HDMI: pin %d wcaps %#x "
-                          "does not support connection list\n",
-                          pin_nid, get_wcaps(codec, pin_nid));
-               return -EINVAL;
-       }
-
-       conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
-                                          HDA_MAX_CONNECTIONS);
-       if (conn_len > 1)
-               curr = snd_hda_codec_read(codec, pin_nid, 0,
-                                         AC_VERB_GET_CONNECT_SEL, 0);
-       else
-               curr = 0;
-
-       index = hda_node_index(spec->pin, pin_nid);
-       if (index < 0)
-               return -EINVAL;
-
-       spec->pin_cvt[index] = conn_list[curr];
-
-       return 0;
-}
-
-static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
-                             struct hdmi_eld *eld)
-{
-       if (!snd_hdmi_get_eld(eld, codec, pin_nid))
-               snd_hdmi_show_eld(eld);
-}
-
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
-                              struct hdmi_eld *eld)
-{
-       int present = snd_hda_pin_sense(codec, pin_nid);
-
-       eld->monitor_present    = !!(present & AC_PINSENSE_PRESENCE);
-       eld->eld_valid          = !!(present & AC_PINSENSE_ELDV);
-
-       if (present & AC_PINSENSE_ELDV)
-               hdmi_get_show_eld(codec, pin_nid, eld);
-}
-
-static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-
-       if (spec->num_pins >= INTEL_HDMI_PINS) {
-               snd_printk(KERN_WARNING
-                          "HDMI: no space for pin %d \n", pin_nid);
-               return -EINVAL;
-       }
-
-       hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
-
-       spec->pin[spec->num_pins] = pin_nid;
-       spec->num_pins++;
-
-       /*
-        * It is assumed that converter nodes come first in the node list and
-        * hence have been registered and usable now.
-        */
-       return intel_hdmi_read_pin_conn(codec, pin_nid);
-}
-
-static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-
-       if (spec->num_cvts >= INTEL_HDMI_CVTS) {
-               snd_printk(KERN_WARNING
-                          "HDMI: no space for converter %d \n", nid);
-               return -EINVAL;
-       }
-
-       spec->cvt[spec->num_cvts] = nid;
-       spec->num_cvts++;
-
-       return 0;
-}
-
-static int intel_hdmi_parse_codec(struct hda_codec *codec)
-{
-       hda_nid_t nid;
-       int i, nodes;
-
-       nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
-       if (!nid || nodes < 0) {
-               snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
-               return -EINVAL;
-       }
-
-       for (i = 0; i < nodes; i++, nid++) {
-               unsigned int caps;
-               unsigned int type;
-
-               caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
-               type = get_wcaps_type(caps);
-
-               if (!(caps & AC_WCAP_DIGITAL))
-                       continue;
-
-               switch (type) {
-               case AC_WID_AUD_OUT:
-                       if (intel_hdmi_add_cvt(codec, nid) < 0)
-                               return -EINVAL;
-                       break;
-               case AC_WID_PIN:
-                       caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
-                       if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
-                               continue;
-                       if (intel_hdmi_add_pin(codec, nid) < 0)
-                               return -EINVAL;
-                       break;
-               }
-       }
-
-       /*
-        * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
-        * can be lost and presence sense verb will become inaccurate if the
-        * HDA link is powered off at hot plug or hw initialization time.
-        */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
-             AC_PWRST_EPSS))
-               codec->bus->power_keep_link_on = 1;
-#endif
-
-       return 0;
-}
-
-/*
- * HDMI routines
- */
-
-#ifdef BE_PARANOID
-static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
-                               int *packet_index, int *byte_index)
-{
-       int val;
-
-       val = snd_hda_codec_read(codec, pin_nid, 0,
-                                AC_VERB_GET_HDMI_DIP_INDEX, 0);
-
-       *packet_index = val >> 5;
-       *byte_index = val & 0x1f;
-}
-#endif
-
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
-                               int packet_index, int byte_index)
-{
-       int val;
-
-       val = (packet_index << 5) | (byte_index & 0x1f);
-
-       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
-}
-
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
-                               unsigned char val)
-{
-       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
-}
-
-static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-       /* Unmute */
-       if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
-               snd_hda_codec_write(codec, pin_nid, 0,
-                               AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
-       /* Enable pin out */
-       snd_hda_codec_write(codec, pin_nid, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-}
-
-/*
- * Enable Audio InfoFrame Transmission
- */
-static void hdmi_start_infoframe_trans(struct hda_codec *codec,
-                                      hda_nid_t pin_nid)
-{
-       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
-                                               AC_DIPXMIT_BEST);
-}
-
-/*
- * Disable Audio InfoFrame Transmission
- */
-static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
-                                     hda_nid_t pin_nid)
-{
-       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-       snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
-                                               AC_DIPXMIT_DISABLE);
-}
-
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
-{
-       return 1 + snd_hda_codec_read(codec, nid, 0,
-                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
-
-static void hdmi_set_channel_count(struct hda_codec *codec,
-                                  hda_nid_t nid, int chs)
-{
-       if (chs != hdmi_get_channel_count(codec, nid))
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-}
-
-static void hdmi_debug_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       int i;
-       int slot;
-
-       for (i = 0; i < 8; i++) {
-               slot = snd_hda_codec_read(codec, pin_nid, 0,
-                                               AC_VERB_GET_HDMI_CHAN_SLOT, i);
-               printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
-                                               slot >> 4, slot & 0xf);
-       }
-#endif
-}
-
-
-/*
- * Audio InfoFrame routines
- */
-
-static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       int i;
-       int size;
-
-       size = snd_hdmi_get_eld_size(codec, pin_nid);
-       printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
-
-       for (i = 0; i < 8; i++) {
-               size = snd_hda_codec_read(codec, pin_nid, 0,
-                                               AC_VERB_GET_HDMI_DIP_SIZE, i);
-               printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
-       }
-#endif
-}
-
-static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-#ifdef BE_PARANOID
-       int i, j;
-       int size;
-       int pi, bi;
-       for (i = 0; i < 8; i++) {
-               size = snd_hda_codec_read(codec, pin_nid, 0,
-                                               AC_VERB_GET_HDMI_DIP_SIZE, i);
-               if (size == 0)
-                       continue;
-
-               hdmi_set_dip_index(codec, pin_nid, i, 0x0);
-               for (j = 1; j < 1000; j++) {
-                       hdmi_write_dip_byte(codec, pin_nid, 0x0);
-                       hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
-                       if (pi != i)
-                               snd_printd(KERN_INFO "dip index %d: %d != %d\n",
-                                               bi, pi, i);
-                       if (bi == 0) /* byte index wrapped around */
-                               break;
-               }
-               snd_printd(KERN_INFO
-                       "HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
-                       i, size, j);
-       }
-#endif
-}
-
-static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
-{
-       u8 *bytes = (u8 *)ai;
-       u8 sum = 0;
-       int i;
-
-       ai->checksum = 0;
-
-       for (i = 0; i < sizeof(*ai); i++)
-               sum += bytes[i];
-
-       ai->checksum = - sum;
-}
-
-static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
-                                     hda_nid_t pin_nid,
-                                     struct hdmi_audio_infoframe *ai)
-{
-       u8 *bytes = (u8 *)ai;
-       int i;
-
-       hdmi_debug_dip_size(codec, pin_nid);
-       hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
-
-       hdmi_checksum_audio_infoframe(ai);
-
-       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-       for (i = 0; i < sizeof(*ai); i++)
-               hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
-}
-
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
-{
-       int i, j;
-       struct cea_channel_speaker_allocation *p;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               p = channel_allocations + i;
-               p->channels = 0;
-               p->spk_mask = 0;
-               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
-                       if (p->speakers[j]) {
-                               p->channels++;
-                               p->spk_mask |= p->speakers[j];
-                       }
-       }
-}
-
-/*
- * The transformation takes two steps:
- *
- *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- *           spk_mask => (channel_allocations[])         => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
-                                        struct hdmi_audio_infoframe *ai)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-       struct hdmi_eld *eld;
-       int i;
-       int spk_mask = 0;
-       int channels = 1 + (ai->CC02_CT47 & 0x7);
-       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
-       /*
-        * CA defaults to 0 for basic stereo audio
-        */
-       if (channels <= 2)
-               return 0;
-
-       i = hda_node_index(spec->pin_cvt, nid);
-       if (i < 0)
-               return 0;
-       eld = &spec->sink_eld[i];
-
-       /*
-        * HDMI sink's ELD info cannot always be retrieved for now, e.g.
-        * in console or for audio devices. Assume the highest speakers
-        * configuration, to _not_ prohibit multi-channel audio playback.
-        */
-       if (!eld->spk_alloc)
-               eld->spk_alloc = 0xffff;
-
-       /*
-        * expand ELD's speaker allocation mask
-        *
-        * ELD tells the speaker mask in a compact(paired) form,
-        * expand ELD's notions to match the ones used by Audio InfoFrame.
-        */
-       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
-               if (eld->spk_alloc & (1 << i))
-                       spk_mask |= eld_speaker_allocation_bits[i];
-       }
-
-       /* search for the first working match in the CA table */
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channels == channel_allocations[i].channels &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask) {
-                       ai->CA = channel_allocations[i].ca_index;
-                       break;
-               }
-       }
-
-       snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
-       snd_printdd(KERN_INFO
-                       "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
-                       ai->CA, channels, buf);
-
-       return ai->CA;
-}
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid,
-                                      struct hdmi_audio_infoframe *ai)
-{
-       int i;
-       int ca = ai->CA;
-       int err;
-
-       if (hdmi_channel_mapping[ca][1] == 0) {
-               for (i = 0; i < channel_allocations[ca].channels; i++)
-                       hdmi_channel_mapping[ca][i] = i | (i << 4);
-               for (; i < 8; i++)
-                       hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
-       }
-
-       for (i = 0; i < 8; i++) {
-               err = snd_hda_codec_write(codec, pin_nid, 0,
-                                         AC_VERB_SET_HDMI_CHAN_SLOT,
-                                         hdmi_channel_mapping[ca][i]);
-               if (err) {
-                       snd_printdd(KERN_INFO "HDMI: channel mapping failed\n");
-                       break;
-               }
-       }
-
-       hdmi_debug_channel_mapping(codec, pin_nid);
-}
-
-static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
-                                   struct hdmi_audio_infoframe *ai)
-{
-       u8 *bytes = (u8 *)ai;
-       u8 val;
-       int i;
-
-       if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
-                                                           != AC_DIPXMIT_BEST)
-               return false;
-
-       hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-       for (i = 0; i < sizeof(*ai); i++) {
-               val = snd_hda_codec_read(codec, pin_nid, 0,
-                                        AC_VERB_GET_HDMI_DIP_DATA, 0);
-               if (val != bytes[i])
-                       return false;
-       }
-
-       return true;
-}
-
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
-                                       struct snd_pcm_substream *substream)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-       hda_nid_t pin_nid;
-       int i;
-       struct hdmi_audio_infoframe ai = {
-               .type           = 0x84,
-               .ver            = 0x01,
-               .len            = 0x0a,
-               .CC02_CT47      = substream->runtime->channels - 1,
-       };
-
-       hdmi_setup_channel_allocation(codec, nid, &ai);
-
-       for (i = 0; i < spec->num_pins; i++) {
-               if (spec->pin_cvt[i] != nid)
-                       continue;
-               if (!spec->sink_eld[i].monitor_present)
-                       continue;
-
-               pin_nid = spec->pin[i];
-               if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
-                       hdmi_setup_channel_mapping(codec, pin_nid, &ai);
-                       hdmi_stop_infoframe_trans(codec, pin_nid);
-                       hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
-                       hdmi_start_infoframe_trans(codec, pin_nid);
-               }
-       }
-}
-
-
 /*
- * Unsolicited events
+ * HDMI callbacks
  */
 
-static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
-       int pind = !!(res & AC_UNSOL_RES_PD);
-       int eldv = !!(res & AC_UNSOL_RES_ELDV);
-       int index;
-
-       printk(KERN_INFO
-               "HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
-               tag, pind, eldv);
-
-       index = hda_node_index(spec->pin, tag);
-       if (index < 0)
-               return;
-
-       spec->sink_eld[index].monitor_present = pind;
-       spec->sink_eld[index].eld_valid = eldv;
-
-       if (pind && eldv) {
-               hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]);
-               /* TODO: do real things about ELD */
-       }
-}
-
-static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
-{
-       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
-       int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
-       int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
-       int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
-
-       printk(KERN_INFO
-               "HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
-               tag,
-               subtag,
-               cp_state,
-               cp_ready);
-
-       /* TODO */
-       if (cp_state)
-               ;
-       if (cp_ready)
-               ;
-}
-
-
-static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       struct intel_hdmi_spec *spec = codec->spec;
-       int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
-       int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
-
-       if (hda_node_index(spec->pin, tag) < 0) {
-               snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
-               return;
-       }
-
-       if (subtag == 0)
-               hdmi_intrinsic_event(codec, res);
-       else
-               hdmi_non_intrinsic_event(codec, res);
-}
-
-/*
- * Callbacks
- */
-
-static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
-                             u32 stream_tag, int format)
-{
-       int tag;
-       int fmt;
-
-       tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
-       fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
-
-       snd_printdd("hdmi_setup_stream: "
-                   "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
-                   nid,
-                   tag == stream_tag ? "" : "new-",
-                   stream_tag,
-                   fmt == format ? "" : "new-",
-                   format);
-
-       if (tag != stream_tag)
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4);
-       if (fmt != format)
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_STREAM_FORMAT, format);
-}
-
 static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                           struct hda_codec *codec,
                                           unsigned int stream_tag,
@@ -882,7 +87,7 @@ static struct hda_pcm_stream intel_hdmi_pcm_playback = {
 
 static int intel_hdmi_build_pcms(struct hda_codec *codec)
 {
-       struct intel_hdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        struct hda_pcm *info = spec->pcm_rec;
        int i;
 
@@ -908,7 +113,7 @@ static int intel_hdmi_build_pcms(struct hda_codec *codec)
 
 static int intel_hdmi_build_controls(struct hda_codec *codec)
 {
-       struct intel_hdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        int err;
        int i;
 
@@ -923,7 +128,7 @@ static int intel_hdmi_build_controls(struct hda_codec *codec)
 
 static int intel_hdmi_init(struct hda_codec *codec)
 {
-       struct intel_hdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        int i;
 
        for (i = 0; spec->pin[i]; i++) {
@@ -937,7 +142,7 @@ static int intel_hdmi_init(struct hda_codec *codec)
 
 static void intel_hdmi_free(struct hda_codec *codec)
 {
-       struct intel_hdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        int i;
 
        for (i = 0; i < spec->num_pins; i++)
@@ -951,12 +156,12 @@ static struct hda_codec_ops intel_hdmi_patch_ops = {
        .free                   = intel_hdmi_free,
        .build_pcms             = intel_hdmi_build_pcms,
        .build_controls         = intel_hdmi_build_controls,
-       .unsol_event            = intel_hdmi_unsol_event,
+       .unsol_event            = hdmi_unsol_event,
 };
 
 static int patch_intel_hdmi(struct hda_codec *codec)
 {
-       struct intel_hdmi_spec *spec;
+       struct hdmi_spec *spec;
        int i;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -964,7 +169,7 @@ static int patch_intel_hdmi(struct hda_codec *codec)
                return -ENOMEM;
 
        codec->spec = spec;
-       if (intel_hdmi_parse_codec(codec) < 0) {
+       if (hdmi_parse_codec(codec) < 0) {
                codec->spec = NULL;
                kfree(spec);
                return -EINVAL;
index 6afdab0..70669a2 100644 (file)
 #include "hda_codec.h"
 #include "hda_local.h"
 
+#define MAX_HDMI_CVTS  1
+#define MAX_HDMI_PINS  1
+
+#include "patch_hdmi.c"
+
+static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = {
+       "NVIDIA HDMI",
+};
+
 /* define below to restrict the supported rates and formats */
 /* #define LIMITED_RATE_FMT_SUPPORT */
 
-struct nvhdmi_spec {
-       struct hda_multi_out multiout;
-
-       struct hda_pcm pcm_rec;
+enum HDACodec {
+       HDA_CODEC_NVIDIA_MCP7X,
+       HDA_CODEC_NVIDIA_MCP89,
+       HDA_CODEC_NVIDIA_GT21X,
+       HDA_CODEC_INVALID
 };
 
 #define Nv_VERB_SET_Channel_Allocation          0xF79
@@ -43,15 +53,18 @@ struct nvhdmi_spec {
 #define Nv_VERB_SET_Audio_Protection_On         0xF98
 #define Nv_VERB_SET_Audio_Protection_Off        0xF99
 
-#define Nv_Master_Convert_nid   0x04
-#define Nv_Master_Pin_nid       0x05
+#define nvhdmi_master_con_nid_7x       0x04
+#define nvhdmi_master_pin_nid_7x       0x05
 
-static hda_nid_t nvhdmi_convert_nids[4] = {
+#define nvhdmi_master_con_nid_89       0x04
+#define nvhdmi_master_pin_nid_89       0x05
+
+static hda_nid_t nvhdmi_con_nids_7x[4] = {
        /*front, rear, clfe, rear_surr */
        0x6, 0x8, 0xa, 0xc,
 };
 
-static struct hda_verb nvhdmi_basic_init[] = {
+static struct hda_verb nvhdmi_basic_init_7x[] = {
        /* set audio protect on */
        { 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
        /* enable digital output on pin widget */
@@ -84,22 +97,60 @@ static struct hda_verb nvhdmi_basic_init[] = {
  */
 static int nvhdmi_build_controls(struct hda_codec *codec)
 {
-       struct nvhdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        int err;
+       int i;
 
-       err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
-       if (err < 0)
-               return err;
+       if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
+       || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
+               for (i = 0; i < codec->num_pcms; i++) {
+                       err = snd_hda_create_spdif_out_ctls(codec,
+                                                           spec->cvt[i]);
+                       if (err < 0)
+                               return err;
+               }
+       } else {
+               err = snd_hda_create_spdif_out_ctls(codec,
+                                                   spec->multiout.dig_out_nid);
+               if (err < 0)
+                       return err;
+       }
 
        return 0;
 }
 
 static int nvhdmi_init(struct hda_codec *codec)
 {
-       snd_hda_sequence_write(codec, nvhdmi_basic_init);
+       struct hdmi_spec *spec = codec->spec;
+       int i;
+       if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
+       || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
+               for (i = 0; spec->pin[i]; i++) {
+                       hdmi_enable_output(codec, spec->pin[i]);
+                       snd_hda_codec_write(codec, spec->pin[i], 0,
+                                           AC_VERB_SET_UNSOLICITED_ENABLE,
+                                           AC_USRSP_EN | spec->pin[i]);
+               }
+       } else {
+               snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+       }
        return 0;
 }
 
+static void nvhdmi_free(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int i;
+
+       if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
+       || (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
+               for (i = 0; i < spec->num_pins; i++)
+                       snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+       }
+
+       kfree(spec);
+}
+
 /*
  * Digital out
  */
@@ -107,25 +158,25 @@ static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
                                        struct hda_codec *codec,
                                        struct snd_pcm_substream *substream)
 {
-       struct nvhdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        return snd_hda_multi_out_dig_open(codec, &spec->multiout);
 }
 
-static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
+static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo,
                                        struct hda_codec *codec,
                                        struct snd_pcm_substream *substream)
 {
-       struct nvhdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        int i;
 
-       snd_hda_codec_write(codec, Nv_Master_Convert_nid,
+       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
                        0, AC_VERB_SET_CHANNEL_STREAMID, 0);
        for (i = 0; i < 4; i++) {
                /* set the stream id */
-               snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
+               snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
                                AC_VERB_SET_CHANNEL_STREAMID, 0);
                /* set the stream format */
-               snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
+               snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
                                AC_VERB_SET_STREAM_FORMAT, 0);
        }
 
@@ -136,10 +187,25 @@ static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
                                        struct hda_codec *codec,
                                        struct snd_pcm_substream *substream)
 {
-       struct nvhdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
+                                       struct hda_codec *codec,
+                                       unsigned int stream_tag,
+                                       unsigned int format,
+                                       struct snd_pcm_substream *substream)
+{
+       hdmi_set_channel_count(codec, hinfo->nid,
+                              substream->runtime->channels);
+
+       hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+
+       hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+       return 0;
+}
+
 static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
                                        struct hda_codec *codec,
                                        unsigned int stream_tag,
@@ -181,29 +247,29 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
        /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
        if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
                snd_hda_codec_write(codec,
-                               Nv_Master_Convert_nid,
+                               nvhdmi_master_con_nid_7x,
                                0,
                                AC_VERB_SET_DIGI_CONVERT_1,
                                codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
 
        /* set the stream id */
-       snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
+       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
                        AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
 
        /* set the stream format */
-       snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
+       snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
                        AC_VERB_SET_STREAM_FORMAT, format);
 
        /* turn on again (if needed) */
        /* enable and set the channel status audio/data flag */
        if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
                snd_hda_codec_write(codec,
-                               Nv_Master_Convert_nid,
+                               nvhdmi_master_con_nid_7x,
                                0,
                                AC_VERB_SET_DIGI_CONVERT_1,
                                codec->spdif_ctls & 0xff);
                snd_hda_codec_write(codec,
-                               Nv_Master_Convert_nid,
+                               nvhdmi_master_con_nid_7x,
                                0,
                                AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
        }
@@ -220,19 +286,19 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
                if (codec->spdif_status_reset &&
                (codec->spdif_ctls & AC_DIG1_ENABLE))
                        snd_hda_codec_write(codec,
-                               nvhdmi_convert_nids[i],
+                               nvhdmi_con_nids_7x[i],
                                0,
                                AC_VERB_SET_DIGI_CONVERT_1,
                                codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
                /* set the stream id */
                snd_hda_codec_write(codec,
-                               nvhdmi_convert_nids[i],
+                               nvhdmi_con_nids_7x[i],
                                0,
                                AC_VERB_SET_CHANNEL_STREAMID,
                                (stream_tag << 4) | channel_id);
                /* set the stream format */
                snd_hda_codec_write(codec,
-                               nvhdmi_convert_nids[i],
+                               nvhdmi_con_nids_7x[i],
                                0,
                                AC_VERB_SET_STREAM_FORMAT,
                                format);
@@ -241,12 +307,12 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
                if (codec->spdif_status_reset &&
                (codec->spdif_ctls & AC_DIG1_ENABLE)) {
                        snd_hda_codec_write(codec,
-                                       nvhdmi_convert_nids[i],
+                                       nvhdmi_con_nids_7x[i],
                                        0,
                                        AC_VERB_SET_DIGI_CONVERT_1,
                                        codec->spdif_ctls & 0xff);
                        snd_hda_codec_write(codec,
-                                       nvhdmi_convert_nids[i],
+                                       nvhdmi_con_nids_7x[i],
                                        0,
                                        AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
                }
@@ -261,28 +327,47 @@ static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
        return 0;
 }
 
+static int nvhdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+                                          struct hda_codec *codec,
+                                          struct snd_pcm_substream *substream)
+{
+       return 0;
+}
+
 static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
                                        struct hda_codec *codec,
                                        unsigned int stream_tag,
                                        unsigned int format,
                                        struct snd_pcm_substream *substream)
 {
-       struct nvhdmi_spec *spec = codec->spec;
+       struct hdmi_spec *spec = codec->spec;
        return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
                                        format, substream);
 }
 
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
+       .substreams = 1,
+       .channels_min = 2,
+       .rates = SUPPORTED_RATES,
+       .maxbps = SUPPORTED_MAXBPS,
+       .formats = SUPPORTED_FORMATS,
+       .ops = {
+               .prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
+               .cleanup = nvhdmi_playback_pcm_cleanup,
+       },
+};
+
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 8,
-       .nid = Nv_Master_Convert_nid,
+       .nid = nvhdmi_master_con_nid_7x,
        .rates = SUPPORTED_RATES,
        .maxbps = SUPPORTED_MAXBPS,
        .formats = SUPPORTED_FORMATS,
        .ops = {
                .open = nvhdmi_dig_playback_pcm_open,
-               .close = nvhdmi_dig_playback_pcm_close_8ch,
+               .close = nvhdmi_dig_playback_pcm_close_8ch_7x,
                .prepare = nvhdmi_dig_playback_pcm_prepare_8ch
        },
 };
@@ -291,7 +376,7 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
        .substreams = 1,
        .channels_min = 2,
        .channels_max = 2,
-       .nid = Nv_Master_Convert_nid,
+       .nid = nvhdmi_master_con_nid_7x,
        .rates = SUPPORTED_RATES,
        .maxbps = SUPPORTED_MAXBPS,
        .formats = SUPPORTED_FORMATS,
@@ -302,10 +387,36 @@ static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
        },
 };
 
-static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
+static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
+       int i;
+
+       codec->num_pcms = spec->num_cvts;
+       codec->pcm_info = info;
+
+       for (i = 0; i < codec->num_pcms; i++, info++) {
+               unsigned int chans;
+
+               chans = get_wcaps(codec, spec->cvt[i]);
+               chans = get_wcaps_channels(chans);
+
+               info->name = nvhdmi_pcm_names[i];
+               info->pcm_type = HDA_PCM_TYPE_HDMI;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK]
+                                       = nvhdmi_pcm_digital_playback_8ch_89;
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
+               info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
+       }
+
+       return 0;
+}
+
+static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec)
 {
-       struct nvhdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = &spec->pcm_rec;
+       struct hdmi_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
 
        codec->num_pcms = 1;
        codec->pcm_info = info;
@@ -313,15 +424,15 @@ static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
        info->name = "NVIDIA HDMI";
        info->pcm_type = HDA_PCM_TYPE_HDMI;
        info->stream[SNDRV_PCM_STREAM_PLAYBACK]
-                                       = nvhdmi_pcm_digital_playback_8ch;
+                                       = nvhdmi_pcm_digital_playback_8ch_7x;
 
        return 0;
 }
 
 static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
 {
-       struct nvhdmi_spec *spec = codec->spec;
-       struct hda_pcm *info = &spec->pcm_rec;
+       struct hdmi_spec *spec = codec->spec;
+       struct hda_pcm *info = spec->pcm_rec;
 
        codec->num_pcms = 1;
        codec->pcm_info = info;
@@ -334,14 +445,17 @@ static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
        return 0;
 }
 
-static void nvhdmi_free(struct hda_codec *codec)
-{
-       kfree(codec->spec);
-}
+static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
+       .build_controls = nvhdmi_build_controls,
+       .build_pcms = nvhdmi_build_pcms_8ch_89,
+       .init = nvhdmi_init,
+       .free = nvhdmi_free,
+       .unsol_event = hdmi_unsol_event,
+};
 
-static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
+static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
        .build_controls = nvhdmi_build_controls,
-       .build_pcms = nvhdmi_build_pcms_8ch,
+       .build_pcms = nvhdmi_build_pcms_8ch_7x,
        .init = nvhdmi_init,
        .free = nvhdmi_free,
 };
@@ -353,9 +467,36 @@ static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
        .free = nvhdmi_free,
 };
 
-static int patch_nvhdmi_8ch(struct hda_codec *codec)
+static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
+{
+       struct hdmi_spec *spec;
+       int i;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return -ENOMEM;
+
+       codec->spec = spec;
+       spec->codec_type = HDA_CODEC_NVIDIA_MCP89;
+
+       if (hdmi_parse_codec(codec) < 0) {
+               codec->spec = NULL;
+               kfree(spec);
+               return -EINVAL;
+       }
+       codec->patch_ops = nvhdmi_patch_ops_8ch_89;
+
+       for (i = 0; i < spec->num_pins; i++)
+               snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
+
+       init_channel_allocations();
+
+       return 0;
+}
+
+static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
 {
-       struct nvhdmi_spec *spec;
+       struct hdmi_spec *spec;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -365,16 +506,17 @@ static int patch_nvhdmi_8ch(struct hda_codec *codec)
 
        spec->multiout.num_dacs = 0;  /* no analog */
        spec->multiout.max_channels = 8;
-       spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
+       spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
+       spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
 
-       codec->patch_ops = nvhdmi_patch_ops_8ch;
+       codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
 
        return 0;
 }
 
 static int patch_nvhdmi_2ch(struct hda_codec *codec)
 {
-       struct nvhdmi_spec *spec;
+       struct hdmi_spec *spec;
 
        spec = kzalloc(sizeof(*spec), GFP_KERNEL);
        if (spec == NULL)
@@ -384,7 +526,8 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
 
        spec->multiout.num_dacs = 0;  /* no analog */
        spec->multiout.max_channels = 2;
-       spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
+       spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
+       spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
 
        codec->patch_ops = nvhdmi_patch_ops_2ch;
 
@@ -395,13 +538,24 @@ static int patch_nvhdmi_2ch(struct hda_codec *codec)
  * patch entries
  */
 static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-       { .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
-       { .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
-       { .id = 0x10de0005, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
-       { .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
-       { .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
        { .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
        { .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
+       { .id = 0x10de0002, .name = "MCP77/78 HDMI",
+         .patch = patch_nvhdmi_8ch_7x },
+       { .id = 0x10de0003, .name = "MCP77/78 HDMI",
+         .patch = patch_nvhdmi_8ch_7x },
+       { .id = 0x10de0005, .name = "MCP77/78 HDMI",
+         .patch = patch_nvhdmi_8ch_7x },
+       { .id = 0x10de0006, .name = "MCP77/78 HDMI",
+         .patch = patch_nvhdmi_8ch_7x },
+       { .id = 0x10de0007, .name = "MCP79/7A HDMI",
+         .patch = patch_nvhdmi_8ch_7x },
+       { .id = 0x10de000c, .name = "MCP89 HDMI",
+         .patch = patch_nvhdmi_8ch_89 },
+       { .id = 0x10de000b, .name = "GT21x HDMI",
+         .patch = patch_nvhdmi_8ch_89 },
+       { .id = 0x10de000d, .name = "GT240 HDMI",
+         .patch = patch_nvhdmi_8ch_89 },
        {} /* terminator */
 };
 
@@ -412,9 +566,12 @@ MODULE_ALIAS("snd-hda-codec-id:10de0006");
 MODULE_ALIAS("snd-hda-codec-id:10de0007");
 MODULE_ALIAS("snd-hda-codec-id:10de0067");
 MODULE_ALIAS("snd-hda-codec-id:10de8001");
+MODULE_ALIAS("snd-hda-codec-id:10de000c");
+MODULE_ALIAS("snd-hda-codec-id:10de000b");
+MODULE_ALIAS("snd-hda-codec-id:10de000d");
 
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
+MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
 
 static struct hda_codec_preset_list nvhdmi_list = {
        .preset = snd_hda_preset_nvhdmi,
index e8cbe21..5d2fbb8 100644 (file)
@@ -4915,7 +4915,7 @@ static void fixup_automic_adc(struct hda_codec *codec)
 static void fixup_single_adc(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       hda_nid_t pin;
+       hda_nid_t pin = 0;
        int i;
 
        /* search for the input pin; there must be only one */
@@ -13561,6 +13561,8 @@ static void alc269_lifebook_unsol_event(struct hda_codec *codec,
 static void alc269_quanta_fl1_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
        spec->ext_mic.pin = 0x18;
        spec->ext_mic.mux_idx = 0;
        spec->int_mic.pin = 0x19;
@@ -13656,6 +13658,8 @@ static void alc269_laptop_unsol_event(struct hda_codec *codec,
 static void alc269_laptop_dmic_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
        spec->ext_mic.pin = 0x18;
        spec->ext_mic.mux_idx = 0;
        spec->int_mic.pin = 0x12;
@@ -13666,6 +13670,8 @@ static void alc269_laptop_dmic_setup(struct hda_codec *codec)
 static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
        spec->ext_mic.pin = 0x18;
        spec->ext_mic.mux_idx = 0;
        spec->int_mic.pin = 0x12;
@@ -13676,6 +13682,8 @@ static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
 static void alc269_laptop_amic_setup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
+       spec->autocfg.hp_pins[0] = 0x15;
+       spec->autocfg.speaker_pins[0] = 0x14;
        spec->ext_mic.pin = 0x18;
        spec->ext_mic.mux_idx = 0;
        spec->int_mic.pin = 0x19;
index 7754db1..dbc4b89 100644 (file)
@@ -68,7 +68,7 @@ static void wm8776_write(struct oxygen *chip,
                         OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
                         (reg << 9) | value);
        if (reg < ARRAY_SIZE(data->wm8776_regs)) {
-               if (reg >= WM8776_HPLVOL || reg <= WM8776_DACMASTER)
+               if (reg >= WM8776_HPLVOL && reg <= WM8776_DACMASTER)
                        value &= ~WM8776_UPDATE;
                data->wm8776_regs[reg] = value;
        }
index 960a227..ad44626 100644 (file)
@@ -1974,9 +1974,9 @@ snd_riptide_proc_read(struct snd_info_entry *entry,
        }
        snd_iprintf(buffer, "Paths:\n");
        i = getpaths(cif, p);
-       while (i--) {
-               snd_iprintf(buffer, "%x->%x ", p[i - 1], p[i]);
-               i--;
+       while (i >= 2) {
+               i -= 2;
+               snd_iprintf(buffer, "%x->%x ", p[i], p[i + 1]);
        }
        snd_iprintf(buffer, "\n");
 }
index b9ef7e4..b68d99f 100644 (file)
@@ -90,12 +90,10 @@ static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg,
        if (reg >= codec->reg_cache_size)
                return -EINVAL;
 
-       reg &= AK4104_REG_MASK;
-       reg |= AK4104_WRITE;
-
        /* only write to the hardware if value has changed */
        if (cache[reg] != value) {
-               u8 tmp[2] = { reg, value };
+               u8 tmp[2] = { (reg & AK4104_REG_MASK) | AK4104_WRITE, value };
+
                if (spi_write(spi, tmp, sizeof(tmp))) {
                        dev_err(&spi->dev, "SPI write failed\n");
                        return -EIO;
index a03bac9..c8b0556 100644 (file)
@@ -427,24 +427,24 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        if (!runtime->hw.rates) {
                printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
                        codec_dai->name, cpu_dai->name);
-               goto machine_err;
+               goto config_err;
        }
        if (!runtime->hw.formats) {
                printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
                        codec_dai->name, cpu_dai->name);
-               goto machine_err;
+               goto config_err;
        }
        if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
                printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
                        codec_dai->name, cpu_dai->name);
-               goto machine_err;
+               goto config_err;
        }
 
        /* Symmetry only applies if we've already got an active stream. */
        if (cpu_dai->active || codec_dai->active) {
                ret = soc_pcm_apply_symmetry(substream);
                if (ret != 0)
-                       goto machine_err;
+                       goto config_err;
        }
 
        pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
@@ -464,10 +464,14 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
        mutex_unlock(&pcm_mutex);
        return 0;
 
-machine_err:
+config_err:
        if (machine->ops && machine->ops->shutdown)
                machine->ops->shutdown(substream);
 
+machine_err:
+       if (codec_dai->ops->shutdown)
+               codec_dai->ops->shutdown(substream, codec_dai);
+
 codec_dai_err:
        if (platform->pcm_ops->close)
                platform->pcm_ops->close(substream);
index 8c29258..c570ae3 100644 (file)
@@ -22,13 +22,13 @@ config SND_USB_AUDIO
          will be called snd-usb-audio.
 
 config SND_USB_UA101
-       tristate "Edirol UA-101 driver (EXPERIMENTAL)"
+       tristate "Edirol UA-101/UA-1000 driver (EXPERIMENTAL)"
        depends on EXPERIMENTAL
        select SND_PCM
        select SND_RAWMIDI
        help
-         Say Y here to include support for the Edirol UA-101 audio/MIDI
-         interface.
+         Say Y here to include support for the Edirol UA-101 and UA-1000
+         audio/MIDI interfaces.
 
          To compile this driver as a module, choose M here: the module
          will be called snd-ua101.
index 9d16db0..380f984 100644 (file)
@@ -3,6 +3,6 @@
 
 int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *dev);
 void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, int port, const char *buf, int len);
-void snd_usb_caiaq_midi_output_done(struct urburb);
+void snd_usb_caiaq_midi_output_done(struct urb *urb);
 
 #endif /* CAIAQ_MIDI_H */
index 4f4ccdf..3d458d3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Edirol UA-101 driver
+ * Edirol UA-101/UA-1000 driver
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  *
  * This driver is free software: you can redistribute it and/or modify
 #include <sound/pcm_params.h>
 #include "usbaudio.h"
 
-MODULE_DESCRIPTION("Edirol UA-101 driver");
+MODULE_DESCRIPTION("Edirol UA-101/1000 driver");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
-MODULE_SUPPORTED_DEVICE("{{Edirol,UA-101}}");
-
-/* I use my UA-1A for testing because I don't have a UA-101 ... */
-#define UA1A_HACK
+MODULE_SUPPORTED_DEVICE("{{Edirol,UA-101},{Edirol,UA-1000}}");
 
 /*
  * Should not be lower than the minimum scheduling delay of the host
@@ -132,9 +129,6 @@ struct ua101 {
                        dma_addr_t dma;
                } buffers[MAX_MEMORY_BUFFERS];
        } capture, playback;
-
-       unsigned int fps[10];
-       unsigned int frame_counter;
 };
 
 static DEFINE_MUTEX(devices_mutex);
@@ -424,16 +418,6 @@ static void capture_urb_complete(struct urb *urb)
        if (do_period_elapsed)
                snd_pcm_period_elapsed(stream->substream);
 
-       /* for debugging: measure the sample rate relative to the USB clock */
-       ua->fps[ua->frame_counter++ / ua->packets_per_second] += frames;
-       if (ua->frame_counter >= ARRAY_SIZE(ua->fps) * ua->packets_per_second) {
-               printk(KERN_DEBUG "capture rate:");
-               for (frames = 0; frames < ARRAY_SIZE(ua->fps); ++frames)
-                       printk(KERN_CONT " %u", ua->fps[frames]);
-               printk(KERN_CONT "\n");
-               memset(ua->fps, 0, sizeof(ua->fps));
-               ua->frame_counter = 0;
-       }
        return;
 
 stream_stopped:
@@ -1200,13 +1184,30 @@ static int ua101_probe(struct usb_interface *interface,
                .type = QUIRK_MIDI_FIXED_ENDPOINT,
                .data = &midi_ep
        };
+       static const int intf_numbers[2][3] = {
+               {       /* UA-101 */
+                       [INTF_PLAYBACK] = 0,
+                       [INTF_CAPTURE] = 1,
+                       [INTF_MIDI] = 2,
+               },
+               {       /* UA-1000 */
+                       [INTF_CAPTURE] = 1,
+                       [INTF_PLAYBACK] = 2,
+                       [INTF_MIDI] = 3,
+               },
+       };
        struct snd_card *card;
        struct ua101 *ua;
        unsigned int card_index, i;
+       int is_ua1000;
+       const char *name;
        char usb_path[32];
        int err;
 
-       if (interface->altsetting->desc.bInterfaceNumber != 0)
+       is_ua1000 = usb_id->idProduct == 0x0044;
+
+       if (interface->altsetting->desc.bInterfaceNumber !=
+           intf_numbers[is_ua1000][0])
                return -ENODEV;
 
        mutex_lock(&devices_mutex);
@@ -1239,20 +1240,13 @@ static int ua101_probe(struct usb_interface *interface,
        init_waitqueue_head(&ua->rate_feedback_wait);
        init_waitqueue_head(&ua->alsa_playback_wait);
 
-#ifdef UA1A_HACK
-       if (ua->dev->descriptor.idProduct == cpu_to_le16(0x0018)) {
-               ua->intf[2] = interface;
-               ua->intf[0] = usb_ifnum_to_if(ua->dev, 1);
-               ua->intf[1] = usb_ifnum_to_if(ua->dev, 2);
-               usb_driver_claim_interface(&ua101_driver, ua->intf[0], ua);
-               usb_driver_claim_interface(&ua101_driver, ua->intf[1], ua);
-       } else {
-#endif
        ua->intf[0] = interface;
        for (i = 1; i < ARRAY_SIZE(ua->intf); ++i) {
-               ua->intf[i] = usb_ifnum_to_if(ua->dev, i);
+               ua->intf[i] = usb_ifnum_to_if(ua->dev,
+                                             intf_numbers[is_ua1000][i]);
                if (!ua->intf[i]) {
-                       dev_err(&ua->dev->dev, "interface %u not found\n", i);
+                       dev_err(&ua->dev->dev, "interface %u not found\n",
+                               intf_numbers[is_ua1000][i]);
                        err = -ENXIO;
                        goto probe_error;
                }
@@ -1264,39 +1258,19 @@ static int ua101_probe(struct usb_interface *interface,
                        goto probe_error;
                }
        }
-#ifdef UA1A_HACK
-       }
-#endif
 
        snd_card_set_dev(card, &interface->dev);
 
-#ifdef UA1A_HACK
-       if (ua->dev->descriptor.idProduct == cpu_to_le16(0x0018)) {
-               ua->format_bit = SNDRV_PCM_FMTBIT_S16_LE;
-               ua->rate = 44100;
-               ua->packets_per_second = 1000;
-               ua->capture.channels = 2;
-               ua->playback.channels = 2;
-               ua->capture.frame_bytes = 4;
-               ua->playback.frame_bytes = 4;
-               ua->capture.usb_pipe = usb_rcvisocpipe(ua->dev, 2);
-               ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, 1);
-               ua->capture.max_packet_bytes = 192;
-               ua->playback.max_packet_bytes = 192;
-       } else {
-#endif
        err = detect_usb_format(ua);
        if (err < 0)
                goto probe_error;
-#ifdef UA1A_HACK
-       }
-#endif
 
+       name = usb_id->idProduct == 0x0044 ? "UA-1000" : "UA-101";
        strcpy(card->driver, "UA-101");
-       strcpy(card->shortname, "UA-101");
+       strcpy(card->shortname, name);
        usb_make_path(ua->dev, usb_path, sizeof(usb_path));
        snprintf(ua->card->longname, sizeof(ua->card->longname),
-                "EDIROL UA-101 (serial %s), %u Hz at %s, %s speed",
+                "EDIROL %s (serial %s), %u Hz at %s, %s speed", name,
                 ua->dev->serial ? ua->dev->serial : "?", ua->rate, usb_path,
                 ua->dev->speed == USB_SPEED_HIGH ? "high" : "full");
 
@@ -1314,24 +1288,18 @@ static int ua101_probe(struct usb_interface *interface,
        if (err < 0)
                goto probe_error;
 
-       err = snd_pcm_new(card, "UA-101", 0, 1, 1, &ua->pcm);
+       err = snd_pcm_new(card, name, 0, 1, 1, &ua->pcm);
        if (err < 0)
                goto probe_error;
        ua->pcm->private_data = ua;
-       strcpy(ua->pcm->name, "UA-101");
+       strcpy(ua->pcm->name, name);
        snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_pcm_ops);
        snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_pcm_ops);
 
-#ifdef UA1A_HACK
-       if (ua->dev->descriptor.idProduct != cpu_to_le16(0x0018)) {
-#endif
        err = snd_usbmidi_create(card, ua->intf[INTF_MIDI],
                                 &ua->midi_list, &midi_quirk);
        if (err < 0)
                goto probe_error;
-#ifdef UA1A_HACK
-       }
-#endif
 
        err = snd_card_register(card);
        if (err < 0)
@@ -1386,11 +1354,9 @@ static void ua101_disconnect(struct usb_interface *interface)
 }
 
 static struct usb_device_id ua101_ids[] = {
-#ifdef UA1A_HACK
-       { USB_DEVICE(0x0582, 0x0018) },
-#endif
-       { USB_DEVICE(0x0582, 0x007d) },
-       { USB_DEVICE(0x0582, 0x008d) },
+       { USB_DEVICE(0x0582, 0x0044) }, /* UA-1000 high speed */
+       { USB_DEVICE(0x0582, 0x007d) }, /* UA-101 high speed */
+       { USB_DEVICE(0x0582, 0x008d) }, /* UA-101 full speed */
        { }
 };
 MODULE_DEVICE_TABLE(usb, ua101_ids);
index c539f7f..11b0826 100644 (file)
@@ -2483,7 +2483,6 @@ static int parse_audio_format_i_type(struct snd_usb_audio *chip,
                                   sample_width, sample_bytes);
                }
                /* check the format byte size */
-               printk(" XXXXX SAMPLE BYTES %d\n", sample_bytes);
                switch (sample_bytes) {
                case 1:
                        pcm_format = SNDRV_PCM_FORMAT_S8;
@@ -2581,6 +2580,9 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
                             chip->usb_id == USB_ID(0x0d8c, 0x0102)) &&
                            fp->altsetting == 5 && fp->maxpacksize == 392)
                                rate = 96000;
+                       /* Creative VF0470 Live Cam reports 16 kHz instead of 8kHz */
+                       if (rate == 16000 && chip->usb_id == USB_ID(0x041e, 0x4068))
+                               rate = 8000;
                        fp->rate_table[fp->nr_rates] = rate;
                        if (!fp->rate_min || rate < fp->rate_min)
                                fp->rate_min = rate;
@@ -3386,58 +3388,6 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
        return 0;
 }
 
-/*
- * Create a stream for an Edirol UA-1000 interface.
- */
-static int create_ua1000_quirk(struct snd_usb_audio *chip,
-                              struct usb_interface *iface,
-                              const struct snd_usb_audio_quirk *quirk)
-{
-       static const struct audioformat ua1000_format = {
-               .format = SNDRV_PCM_FORMAT_S32_LE,
-               .fmt_type = UAC_FORMAT_TYPE_I,
-               .altsetting = 1,
-               .altset_idx = 1,
-               .attributes = 0,
-               .rates = SNDRV_PCM_RATE_CONTINUOUS,
-       };
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       struct audioformat *fp;
-       int stream, err;
-
-       if (iface->num_altsetting != 2)
-               return -ENXIO;
-       alts = &iface->altsetting[1];
-       altsd = get_iface_desc(alts);
-       if (alts->extralen != 11 || alts->extra[1] != USB_DT_CS_INTERFACE ||
-           altsd->bNumEndpoints != 1)
-               return -ENXIO;
-
-       fp = kmemdup(&ua1000_format, sizeof(*fp), GFP_KERNEL);
-       if (!fp)
-               return -ENOMEM;
-
-       fp->channels = alts->extra[4];
-       fp->iface = altsd->bInterfaceNumber;
-       fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
-       fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-       fp->datainterval = parse_datainterval(chip, alts);
-       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
-       fp->rate_max = fp->rate_min = combine_triple(&alts->extra[8]);
-
-       stream = (fp->endpoint & USB_DIR_IN)
-               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-       err = add_audio_endpoint(chip, stream, fp);
-       if (err < 0) {
-               kfree(fp);
-               return err;
-       }
-       /* FIXME: playback must be synchronized to capture */
-       usb_set_interface(chip->dev, fp->iface, 0);
-       return 0;
-}
-
 static int snd_usb_create_quirk(struct snd_usb_audio *chip,
                                struct usb_interface *iface,
                                const struct snd_usb_audio_quirk *quirk);
@@ -3686,7 +3636,6 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip,
                [QUIRK_MIDI_CME] = create_any_midi_quirk,
                [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
                [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
-               [QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
                [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
                [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
        };
index 6b016d4..42c299c 100644 (file)
@@ -75,7 +75,6 @@ enum quirk_type {
        QUIRK_MIDI_US122L,
        QUIRK_AUDIO_STANDARD_INTERFACE,
        QUIRK_AUDIO_FIXED_ENDPOINT,
-       QUIRK_AUDIO_EDIROL_UA1000,
        QUIRK_AUDIO_EDIROL_UAXX,
        QUIRK_AUDIO_ALIGN_TRANSFER,
 
@@ -112,7 +111,7 @@ struct snd_usb_midi_endpoint_info {
 
 /* for QUIRK_AUDIO/MIDI_STANDARD_INTERFACE, data is NULL */
 
-/* for QUIRK_AUDIO_EDIROL_UA700_UA25/UA1000, data is NULL */
+/* for QUIRK_AUDIO_EDIROL_UAXX, data is NULL */
 
 /* for QUIRK_IGNORE_INTERFACE, data is NULL */
 
index f06faf7..2b426c1 100644 (file)
@@ -1015,36 +1015,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                }
        }
 },
-{
-       USB_DEVICE(0x0582, 0x0044),
-       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
-               .vendor_name = "Roland",
-               .product_name = "UA-1000",
-               .ifnum = QUIRK_ANY_INTERFACE,
-               .type = QUIRK_COMPOSITE,
-               .data = (const struct snd_usb_audio_quirk[]) {
-                       {
-                               .ifnum = 1,
-                               .type = QUIRK_AUDIO_EDIROL_UA1000
-                       },
-                       {
-                               .ifnum = 2,
-                               .type = QUIRK_AUDIO_EDIROL_UA1000
-                       },
-                       {
-                               .ifnum = 3,
-                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-                               .data = & (const struct snd_usb_midi_endpoint_info) {
-                                       .out_cables = 0x0003,
-                                       .in_cables  = 0x0003
-                               }
-                       },
-                       {
-                               .ifnum = -1
-                       }
-               }
-       }
-},
 {
        /* has ID 0x0049 when not in "Advanced Driver" mode */
        USB_DEVICE(0x0582, 0x0047),