Merge remote-tracking branch 'linux-2.6.32.y/master' into develop
author黄涛 <huangtao@rock-chips.com>
Tue, 28 Jun 2011 01:47:32 +0000 (09:47 +0800)
committer黄涛 <huangtao@rock-chips.com>
Tue, 28 Jun 2011 01:47:32 +0000 (09:47 +0800)
Merge Linux 2.6.32.42
Conflicts:
Makefile
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/p54/p54usb.c

1  2 
block/blk-core.c
drivers/usb/core/hub.c
drivers/usb/gadget/f_rndis.c
fs/block_dev.c
mm/page_alloc.c
sound/soc/codecs/wm_hubs.c

diff --combined block/blk-core.c
index 08452083dd2d9df0d95a5ed093c510cb21d4f5ad,cffd73792633e1cbc1f4d2816cda53374de3a9ec..d7cc40e1bbd52227bbeb394aa2d98d588442bc1f
@@@ -310,6 -310,7 +310,7 @@@ void blk_unplug_timeout(unsigned long d
        trace_block_unplug_timer(q);
        kblockd_schedule_work(q, &q->unplug_work);
  }
+ EXPORT_SYMBOL(blk_put_queue);
  
  void blk_unplug(struct request_queue *q)
  {
@@@ -612,6 -613,7 +613,7 @@@ int blk_get_queue(struct request_queue 
  
        return 1;
  }
+ EXPORT_SYMBOL(blk_get_queue);
  
  static inline void blk_free_request(struct request_queue *q, struct request *rq)
  {
@@@ -1569,12 -1571,11 +1571,12 @@@ void submit_bio(int rw, struct bio *bio
  
                if (unlikely(block_dump)) {
                        char b[BDEVNAME_SIZE];
 -                      printk(KERN_DEBUG "%s(%d): %s block %Lu on %s\n",
 +                      printk(KERN_DEBUG "%s(%d): %s block %Lu on %s (%u sectors)\n",
                        current->comm, task_pid_nr(current),
                                (rw & WRITE) ? "WRITE" : "READ",
                                (unsigned long long)bio->bi_sector,
 -                              bdevname(bio->bi_bdev, b));
 +                              bdevname(bio->bi_bdev, b),
 +                              count);
                }
        }
  
@@@ -1971,7 -1972,7 +1973,7 @@@ bool blk_update_request(struct request 
                req->errors = 0;
  
        if (error && (blk_fs_request(req) && !(req->cmd_flags & REQ_QUIET))) {
 -              printk(KERN_ERR "end_request: I/O error, dev %s, sector %llu\n",
 +              printk(KERN_DEBUG "end_request: I/O error, dev %s, sector %llu\n",
                                req->rq_disk ? req->rq_disk->disk_name : "?",
                                (unsigned long long)blk_rq_pos(req));
        }
diff --combined drivers/usb/core/hub.c
index 00d4085f1d255327b96ba20680e5fea9dbbbfb1a,03eed28de77c79433559f7a1f4677ea56876ad1d..a51e1ffe65ce0ff7c7e1f55ae6423f0b180c2205
mode 100755,100644..100755
@@@ -326,7 -326,8 +326,8 @@@ static int get_hub_status(struct usb_de
  {
        int i, status = -ETIMEDOUT;
  
-       for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
+       for (i = 0; i < USB_STS_RETRIES &&
+                       (status == -ETIMEDOUT || status == -EPIPE); i++) {
                status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                        USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0,
                        data, sizeof(*data), USB_STS_TIMEOUT);
@@@ -342,7 -343,8 +343,8 @@@ static int get_port_status(struct usb_d
  {
        int i, status = -ETIMEDOUT;
  
-       for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
+       for (i = 0; i < USB_STS_RETRIES &&
+                       (status == -ETIMEDOUT || status == -EPIPE); i++) {
                status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                        USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
                        data, sizeof(*data), USB_STS_TIMEOUT);
@@@ -1189,7 -1191,7 +1191,7 @@@ static void hub_disconnect(struct usb_i
  
        kref_put(&hub->kref, hub_release);
  }
 -
 +struct usb_hub *g_root_hub20 = NULL;
  static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
  {
        struct usb_host_interface *desc;
@@@ -1240,10 -1242,7 +1242,10 @@@ descriptor_error
                dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");
                return -ENOMEM;
        }
 -
 +      if(!g_root_hub20)
 +      {
 +              g_root_hub20 = hub;
 +      }
        kref_init(&hub->kref);
        INIT_LIST_HEAD(&hub->event_list);
        hub->intfdev = &intf->dev;
@@@ -1611,7 -1610,6 +1613,7 @@@ void usb_disconnect(struct usb_device *
        spin_unlock_irq(&device_state_lock);
  
        usb_stop_pm(udev);
 +    
  
        hub_free_dev(udev);
  
@@@ -2715,17 -2713,11 +2717,17 @@@ hub_port_init (struct usb_hub *hub, str
                udev->ttport = hdev->ttport;
        } else if (udev->speed != USB_SPEED_HIGH
                        && hdev->speed == USB_SPEED_HIGH) {
 +                      
 +      /* yk@rk 20110617
 +       * parent hub has no TT would not be error in rk29
 +       */
 +              #if 0
                if (!hub->tt.hub) {
                        dev_err(&udev->dev, "parent hub has no TT\n");
                        retval = -EINVAL;
                        goto fail;
                }
 +              #endif
                udev->tt = &hub->tt;
                udev->ttport = port1;
        }
@@@ -3422,14 -3414,6 +3424,14 @@@ loop
          } /* end while (1) */
  }
  
 +/* yk@rk 20100730 
 + * disconnect all devices on root hub
 + */
 +void hub_disconnect_device(struct usb_hub *hub)
 +{
 +      hub_port_connect_change(hub, 1, 0, 0x2);
 +}
 +
  static int hub_thread(void *__unused)
  {
        /* khubd needs to be freezable to avoid intefering with USB-PERSIST
index 8486ade79e424f45df16b2ef4de938d3d1f5afdd,5201374ae13cdae8d5328aa2815cc6d8e133782c..999a43c572bbfe4970dcd1ec83b3f802936261df
@@@ -23,9 -23,8 +23,9 @@@
  /* #define VERBOSE_DEBUG */
  
  #include <linux/kernel.h>
 -#include <linux/device.h>
 +#include <linux/platform_device.h>
  #include <linux/etherdevice.h>
 +#include <linux/usb/android_composite.h>
  
  #include <asm/atomic.h>
  
@@@ -127,16 -126,9 +127,16 @@@ static struct usb_interface_descriptor 
        /* .bInterfaceNumber = DYNAMIC */
        /* status endpoint is optional; this could be patched later */
        .bNumEndpoints =        1,
 +#ifdef CONFIG_USB_ANDROID_RNDIS_WCEIS
 +      /* "Wireless" RNDIS; auto-detected by Windows */
 +      .bInterfaceClass =      USB_CLASS_WIRELESS_CONTROLLER,
 +      .bInterfaceSubClass = 1,
 +      .bInterfaceProtocol =   3,
 +#else
        .bInterfaceClass =      USB_CLASS_COMM,
        .bInterfaceSubClass =   USB_CDC_SUBCLASS_ACM,
        .bInterfaceProtocol =   USB_CDC_ACM_PROTO_VENDOR,
 +#endif
        /* .iInterface = DYNAMIC */
  };
  
@@@ -292,10 -284,6 +292,10 @@@ static struct usb_gadget_strings *rndis
        NULL,
  };
  
 +#ifdef CONFIG_USB_ANDROID_RNDIS
 +static struct usb_ether_platform_data *rndis_pdata;
 +#endif
 +
  /*-------------------------------------------------------------------------*/
  
  static struct sk_buff *rndis_add_header(struct gether *port,
@@@ -412,8 -400,7 +412,7 @@@ rndis_setup(struct usb_function *f, con
         */
        case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
                        | USB_CDC_SEND_ENCAPSULATED_COMMAND:
-               if (w_length > req->length || w_value
-                               || w_index != rndis->ctrl_id)
+               if (w_value || w_index != rndis->ctrl_id)
                        goto invalid;
                /* read the request; process it later */
                value = w_length;
@@@ -479,10 -466,10 +478,10 @@@ static int rndis_set_alt(struct usb_fun
                        usb_ep_disable(rndis->notify);
                } else {
                        VDBG(cdev, "init rndis ctrl %d\n", intf);
 -                      rndis->notify_desc = ep_choose(cdev->gadget,
 -                                      rndis->hs.notify,
 -                                      rndis->fs.notify);
                }
 +              rndis->notify_desc = ep_choose(cdev->gadget,
 +                              rndis->hs.notify,
 +                              rndis->fs.notify);
                usb_ep_enable(rndis->notify, rndis->notify_desc);
                rndis->notify->driver_data = rndis;
  
  
                if (!rndis->port.in) {
                        DBG(cdev, "init rndis\n");
 -                      rndis->port.in = ep_choose(cdev->gadget,
 -                                      rndis->hs.in, rndis->fs.in);
 -                      rndis->port.out = ep_choose(cdev->gadget,
 -                                      rndis->hs.out, rndis->fs.out);
                }
 +              rndis->port.in = ep_choose(cdev->gadget,
 +                              rndis->hs.in, rndis->fs.in);
 +              rndis->port.out = ep_choose(cdev->gadget,
 +                              rndis->hs.out, rndis->fs.out);
  
                /* Avoid ZLPs; they can be troublesome. */
                rndis->port.is_zlp_ok = false;
@@@ -698,12 -685,11 +697,12 @@@ rndis_bind(struct usb_configuration *c
        rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0);
        rndis_set_host_mac(rndis->config, rndis->ethaddr);
  
 -#if 0
 -// FIXME
 -      if (rndis_set_param_vendor(rndis->config, vendorID,
 -                              manufacturer))
 -              goto fail0;
 +#ifdef CONFIG_USB_ANDROID_RNDIS
 +      if (rndis_pdata) {
 +              if (rndis_set_param_vendor(rndis->config, rndis_pdata->vendorID,
 +                                      rndis_pdata->vendorDescr))
 +                      goto fail;
 +      }
  #endif
  
        /* NOTE:  all that is done without knowing or caring about
@@@ -838,11 -824,6 +837,11 @@@ int __init rndis_bind_config(struct usb
        rndis->port.func.setup = rndis_setup;
        rndis->port.func.disable = rndis_disable;
  
 +#ifdef CONFIG_USB_ANDROID_RNDIS
 +      /* start disabled */
 +      rndis->port.func.disabled = 1;
 +#endif
 +
        status = usb_add_function(c, &rndis->port.func);
        if (status) {
                kfree(rndis);
@@@ -851,54 -832,3 +850,54 @@@ fail
        }
        return status;
  }
 +
 +#ifdef CONFIG_USB_ANDROID_RNDIS
 +#include "rndis.c"
 +
 +static int __init rndis_probe(struct platform_device *pdev)
 +{
 +      rndis_pdata = pdev->dev.platform_data;
 +      return 0;
 +}
 +
 +static struct platform_driver rndis_platform_driver = {
 +      .driver = { .name = "rndis", },
 +      .probe = rndis_probe,
 +};
 +
 +int rndis_function_bind_config(struct usb_configuration *c)
 +{
 +      int ret;
 +
 +      if (!rndis_pdata) {
 +              printk(KERN_ERR "rndis_pdata null in rndis_function_bind_config\n");
 +              return -1;
 +      }
 +
 +      printk(KERN_INFO
 +              "rndis_function_bind_config MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
 +              rndis_pdata->ethaddr[0], rndis_pdata->ethaddr[1],
 +              rndis_pdata->ethaddr[2], rndis_pdata->ethaddr[3],
 +              rndis_pdata->ethaddr[4], rndis_pdata->ethaddr[5]);
 +
 +      ret = gether_setup(c->cdev->gadget, rndis_pdata->ethaddr);
 +      if (ret == 0)
 +              ret = rndis_bind_config(c, rndis_pdata->ethaddr);
 +      return ret;
 +}
 +
 +static struct android_usb_function rndis_function = {
 +      .name = "rndis",
 +      .bind_config = rndis_function_bind_config,
 +};
 +
 +static int __init init(void)
 +{
 +      printk(KERN_INFO "f_rndis init\n");
 +      platform_driver_register(&rndis_platform_driver);
 +      android_register_function(&rndis_function);
 +      return 0;
 +}
 +module_init(init);
 +
 +#endif /* CONFIG_USB_ANDROID_RNDIS */
diff --combined fs/block_dev.c
index 8ff421c193579b6eba8fc3d878d81a37b400dd80,16cea865268e98c0fabc30b4361a95cb54f49baa..267c5005e948f56b4163156c7e913d1694b4af3e
mode 100755,100644..100755
@@@ -28,8 -28,6 +28,8 @@@
  #include <linux/kmemleak.h>
  #include <asm/uaccess.h>
  #include "internal.h"
 +#include <linux/mtd/blktrans.h>
 +#include <linux/mtd/mtd.h>
  
  struct bdev_inode {
        struct block_device bdev;
@@@ -1205,6 -1203,7 +1205,7 @@@ static int __blkdev_get(struct block_de
                        if (!bdev->bd_part)
                                goto out_clear;
  
+                       ret = 0;
                        if (disk->fops->open) {
                                ret = disk->fops->open(bdev, mode);
                                if (ret == -ERESTARTSYS) {
                                        mutex_unlock(&bdev->bd_mutex);
                                        goto restart;
                                }
-                               if (ret)
-                                       goto out_clear;
                        }
+                       /*
+                        * If the device is invalidated, rescan partition
+                        * if open succeeded or failed with -ENOMEDIUM.
+                        * The latter is necessary to prevent ghost
+                        * partitions on a removed medium.
+                        */
+                       if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM))
+                               rescan_partitions(disk, bdev);
+                       if (ret)
+                               goto out_clear;
                        if (!bdev->bd_openers) {
                                bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
                                bdi = blk_get_backing_dev_info(bdev);
                                        bdi = &default_backing_dev_info;
                                bdev->bd_inode->i_data.backing_dev_info = bdi;
                        }
-                       if (bdev->bd_invalidated)
-                               rescan_partitions(disk, bdev);
                } else {
                        struct block_device *whole;
                        whole = bdget_disk(disk, 0);
                put_disk(disk);
                disk = NULL;
                if (bdev->bd_contains == bdev) {
-                       if (bdev->bd_disk->fops->open) {
+                       ret = 0;
+                       if (bdev->bd_disk->fops->open)
                                ret = bdev->bd_disk->fops->open(bdev, mode);
-                               if (ret)
-                                       goto out_unlock_bdev;
-                       }
-                       if (bdev->bd_invalidated)
+                       /* the same as first opener case, read comment there */
+                       if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM))
                                rescan_partitions(bdev->bd_disk, bdev);
+                       if (ret)
+                               goto out_unlock_bdev;
                }
        }
        bdev->bd_openers++;
@@@ -1467,85 -1474,12 +1476,85 @@@ static const struct address_space_opera
        .direct_IO      = blkdev_direct_IO,
  };
  
 +
 +ssize_t mydo_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
 +{
 +    unsigned long buf_addr = (unsigned long)buf;
 +    if(memcmp(filp->f_mapping->host->i_bdev->bd_disk->disk_name, "mtdblock", 8) == 0 )// kernel mem is usb tran &&(buf_addr >= 0xc0000000)
 +    {
 +        struct mtd_blktrans_dev *dev;
 +        struct mtd_blktrans_ops *tr;
 +        struct mtd_info *mtd;
 +        
 +        dev = (filp->f_mapping->host->i_bdev->bd_disk->private_data);
 +        mtd = dev->mtd;
 +        if((buf_addr < 0xc0000000)&&(mtd->name[0]=='u' &&mtd->name[3]=='r' && mtd->name[4]==0)) // user part 
 +        {
 +            return(do_sync_read(filp, buf,len,ppos));
 +        }
 +        tr = dev->tr;
 +              if (!tr->readsect)
 +              {
 +                      return(do_sync_read(filp, buf,len,ppos));
 +          }
 +        //printk("mydo_sync_read buf = 0x%lx LBA = 0x%lx len = 0x%x \n",buf, (unsigned long)(*ppos>>9),len);
 +        if(tr->readsect(dev, (unsigned long)(*ppos>>9), len>>9, buf))
 +        {
 +            return 0 ;
 +        }
 +        *ppos += len;
 +        return len;
 +    }
 +
 +    else
 +    {
 +        return(do_sync_read(filp, buf,len,ppos));
 +    }
 +}
 +
 +ssize_t mydo_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
 +{
 +    unsigned long buf_addr = (unsigned long)buf;
 +    if(memcmp(filp->f_mapping->host->i_bdev->bd_disk->disk_name, "mtdblock", 8) == 0)// kernel mem is usb tran &&(buf_addr >= 0xc0000000)
 +    {
 +        struct mtd_blktrans_dev *dev;
 +        struct mtd_blktrans_ops *tr;
 +        struct mtd_info *mtd;
 +        
 +        dev = (filp->f_mapping->host->i_bdev->bd_disk->private_data);
 +        
 +        mtd = dev->mtd;
 +        if((buf_addr < 0xc0000000)&&(mtd->name[0]=='u' &&mtd->name[3]=='r' && mtd->name[4]==0))
 +        {
 +            return(do_sync_write(filp, buf,len,ppos));
 +        }
 +
 +        tr = dev->tr;
 +
 +              if (!tr->writesect)
 +                      return 0;
 +        //printk("mydo_sync_write buf = 0x%lx LBA = 0x%lx len = 0x%x \n",buf, (unsigned long)(*ppos>>9),len);
 +        if(tr->writesect(dev, (unsigned long)(*ppos>>9), len>>9, buf))
 +        {
 +            return 0 ;
 +        }
 +        *ppos += len;
 +        return len;
 +    }
 +
 +    else
 +    {
 +        return(do_sync_write(filp, buf,len,ppos));
 +    }
 +}
 +
 +
  const struct file_operations def_blk_fops = {
        .open           = blkdev_open,
        .release        = blkdev_close,
        .llseek         = block_llseek,
 -      .read           = do_sync_read,
 -      .write          = do_sync_write,
 +      .read           = mydo_sync_read,
 +      .write          = mydo_sync_write,
        .aio_read       = generic_file_aio_read,
        .aio_write      = blkdev_aio_write,
        .mmap           = generic_file_mmap,
diff --combined mm/page_alloc.c
index 35f7fd09fa7245ac46601644ad4d3e1ef868df65,3ecab7e7bbfa9ec72e620c327559a8a0d63f5192..e1881454831f892f4639834a7285d0d2e164de0d
@@@ -122,7 -122,6 +122,7 @@@ static char * const zone_names[MAX_NR_Z
  };
  
  int min_free_kbytes = 1024;
 +int min_free_order_shift = 1;
  
  static unsigned long __meminitdata nr_kernel_pages;
  static unsigned long __meminitdata nr_all_pages;
@@@ -1382,7 -1381,7 +1382,7 @@@ int zone_watermark_ok(struct zone *z, i
                free_pages -= z->free_area[o].nr_free << o;
  
                /* Require fewer higher order pages to be free */
 -              min >>= 1;
 +              min >>= min_free_order_shift;
  
                if (free_pages <= min)
                        return 0;
@@@ -1842,6 -1841,7 +1842,7 @@@ restart
         */
        alloc_flags = gfp_to_alloc_flags(gfp_mask);
  
+ rebalance:
        /* This is the last chance, in general, before the goto nopage. */
        page = get_page_from_freelist(gfp_mask, nodemask, order, zonelist,
                        high_zoneidx, alloc_flags & ~ALLOC_NO_WATERMARKS,
        if (page)
                goto got_pg;
  
- rebalance:
        /* Allocate without watermarks if the context allows */
        if (alloc_flags & ALLOC_NO_WATERMARKS) {
                page = __alloc_pages_high_priority(gfp_mask, order,
@@@ -2872,20 -2871,6 +2872,20 @@@ static inline unsigned long wait_table_
  
  #define LONG_ALIGN(x) (((x)+(sizeof(long))-1)&~((sizeof(long))-1))
  
 +/*
 + * Check if a pageblock contains reserved pages
 + */
 +static int pageblock_is_reserved(unsigned long start_pfn)
 +{
 +      unsigned long end_pfn = start_pfn + pageblock_nr_pages;
 +      unsigned long pfn;
 +
 +      for (pfn = start_pfn; pfn < end_pfn; pfn++)
 +              if (PageReserved(pfn_to_page(pfn)))
 +                      return 1;
 +      return 0;
 +}
 +
  /*
   * Mark a number of pageblocks as MIGRATE_RESERVE. The number
   * of blocks reserved is based on min_wmark_pages(zone). The memory within
@@@ -2925,7 -2910,7 +2925,7 @@@ static void setup_zone_migrate_reserve(
                        continue;
  
                /* Blocks with reserved pages will never free, skip them. */
 -              if (PageReserved(page))
 +              if (pageblock_is_reserved(pfn))
                        continue;
  
                block_migratetype = get_pageblock_migratetype(page);
index b409e60cbe4803ea5a1fd558f9c8a1251ae15e62,12506dc4feb5be06ad92269b6cf351f9d2f798ff..e1924d5dc3fd4fa6e85a5859e71666542cfe485b
mode 100755,100644..100755
@@@ -62,108 -62,36 +62,108 @@@ static const char *speaker_mode_text[] 
  static const struct soc_enum speaker_mode =
        SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
  
 -static void wait_for_dc_servo(struct snd_soc_codec *codec)
 +static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
  {
        unsigned int reg;
        int count = 0;
 +      unsigned int val;
 +
 +      val = op | WM8993_DCS_ENA_CHAN_0 | WM8993_DCS_ENA_CHAN_1;
 +
 +      /* Trigger the command */
 +      snd_soc_write(codec, WM8993_DC_SERVO_0, val);
 +
 +      dev_vdbg(codec->dev, "Waiting for DC servo...\n");
  
 -      dev_dbg(codec->dev, "Waiting for DC servo...\n");
        do {
                count++;
 -              msleep(1);
 -              reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
 -              dev_dbg(codec->dev, "DC servo status: %x\n", reg);
 -      } while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
 -               != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
 -
 -      if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
 -          != WM8993_DCS_CAL_COMPLETE_MASK)
 +              msleep(10);
 +              reg = snd_soc_read(codec, WM8993_DC_SERVO_0);
 +              dev_vdbg(codec->dev, "DC servo: %x\n", reg);
 +      } while (reg & op && count < 400);
 +
 +      if (reg & op)
                dev_err(codec->dev, "Timed out waiting for DC Servo\n");
  }
  
 +/*
 + * Startup calibration of the DC servo
 + */
 +static void calibrate_dc_servo(struct snd_soc_codec *codec)
 +{
 +      struct wm_hubs_data *hubs = codec->private_data;
 +      u16 reg, reg_l, reg_r, dcs_cfg;
 +
 +      /* Set for 32 series updates */
 +      snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
 +                          WM8993_DCS_SERIES_NO_01_MASK,
 +                          32 << WM8993_DCS_SERIES_NO_01_SHIFT);
 +      wait_for_dc_servo(codec,
 +                        WM8993_DCS_TRIG_SERIES_0 | WM8993_DCS_TRIG_SERIES_1);
 +
 +      /* Apply correction to DC servo result */
 +      if (hubs->dcs_codes) {
 +              dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
 +                      hubs->dcs_codes);
 +
 +              /* Different chips in the family support different
 +               * readback methods.
 +               */
 +              switch (hubs->dcs_readback_mode) {
 +              case 0:
 +                      reg_l = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1)
 +                              & WM8993_DCS_INTEG_CHAN_0_MASK;;
 +                      reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
 +                              & WM8993_DCS_INTEG_CHAN_1_MASK;
 +                      break;
 +              case 1:
 +                      reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
 +                      reg_l = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
 +                              >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
 +                      reg_r = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
 +                      break;
 +              default:
 +                      WARN(1, "Unknown DCS readback method");
 +                      break;
 +              }
 +
 +              /* HPOUT1L */
 +              if (reg_l + hubs->dcs_codes > 0 &&
 +                  reg_l + hubs->dcs_codes < 0xff)
 +                      reg_l += hubs->dcs_codes;
 +              dcs_cfg = reg_l << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
 +
 +              /* HPOUT1R */
 +              if (reg_r + hubs->dcs_codes > 0 &&
 +                  reg_r + hubs->dcs_codes < 0xff)
 +                      reg_r += hubs->dcs_codes;
 +              dcs_cfg |= reg_r;
 +
 +              /* Do it */
 +              snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
 +              wait_for_dc_servo(codec,
 +                                WM8993_DCS_TRIG_DAC_WR_0 |
 +                                WM8993_DCS_TRIG_DAC_WR_1);
 +      }
 +}
 +
  /*
   * Update the DC servo calibration on gain changes
   */
  static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
 -                            struct snd_ctl_elem_value *ucontrol)
 +                             struct snd_ctl_elem_value *ucontrol)
  {
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 +      struct wm_hubs_data *hubs = codec->private_data;
        int ret;
  
        ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
  
 +      /* If we're applying an offset correction then updating the
 +       * callibration would be likely to introduce further offsets. */
 +      if (hubs->dcs_codes)
 +              return ret;
 +
        /* Only need to do this if the outputs are active */
        if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1)
            & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
@@@ -323,47 -251,6 +323,47 @@@ SOC_SINGLE_TLV("LINEOUT2 Volume", WM899
               line_tlv),
  };
  
 +static int hp_supply_event(struct snd_soc_dapm_widget *w,
 +                         struct snd_kcontrol *kcontrol, int event)
 +{
 +      struct snd_soc_codec *codec = w->codec;
 +      struct wm_hubs_data *hubs = codec->private_data;
 +
 +      switch (event) {
 +      case SND_SOC_DAPM_PRE_PMU:
 +              switch (hubs->hp_startup_mode) {
 +              case 0:
 +                      break;
 +              case 1:
 +                      /* Enable the headphone amp */
 +                      snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
 +                                          WM8993_HPOUT1L_ENA |
 +                                          WM8993_HPOUT1R_ENA,
 +                                          WM8993_HPOUT1L_ENA |
 +                                          WM8993_HPOUT1R_ENA);
 +
 +                      /* Enable the second stage */
 +                      snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
 +                                          WM8993_HPOUT1L_DLY |
 +                                          WM8993_HPOUT1R_DLY,
 +                                          WM8993_HPOUT1L_DLY |
 +                                          WM8993_HPOUT1R_DLY);
 +                      break;
 +              default:
 +                      dev_err(codec->dev, "Unknown HP startup mode %d\n",
 +                              hubs->hp_startup_mode);
 +                      break;
 +              }
 +
 +      case SND_SOC_DAPM_PRE_PMD:
 +              snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
 +                                  WM8993_CP_ENA, 0);
 +              break;
 +      }
 +
 +      return 0;
 +}
 +
  static int hp_event(struct snd_soc_dapm_widget *w,
                    struct snd_kcontrol *kcontrol, int event)
  {
                snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
                                    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
                                    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);
 -
 +      //      printk("hp_event power1 up: 0x%04x",snd_soc_read(codec,WM8993_POWER_MANAGEMENT_1));
                reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
                snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
  
 -              /* Start the DC servo */
 -              snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
 -                                  0xFFFF,
 -                                  WM8993_DCS_ENA_CHAN_0 |
 -                                  WM8993_DCS_ENA_CHAN_1 |
 -                                  WM8993_DCS_TRIG_STARTUP_1 |
 -                                  WM8993_DCS_TRIG_STARTUP_0);
 -              wait_for_dc_servo(codec);
 +              /* Smallest supported update interval */
 +              snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
 +                                  WM8993_DCS_TIMER_PERIOD_01_MASK, 1);
 +
 +              calibrate_dc_servo(codec);
  
                reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
                        WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
                break;
  
        case SND_SOC_DAPM_PRE_PMD:
 -              reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
 -                       WM8993_HPOUT1L_DLY |
 -                       WM8993_HPOUT1L_OUTP |
 -                       WM8993_HPOUT1R_RMV_SHORT |
 -                       WM8993_HPOUT1R_DLY |
 -                       WM8993_HPOUT1R_OUTP);
 +              snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
 +                                  WM8993_HPOUT1L_DLY |
 +                                  WM8993_HPOUT1R_DLY |
 +                                  WM8993_HPOUT1L_RMV_SHORT |
 +                                  WM8993_HPOUT1R_RMV_SHORT, 0);
  
 -              snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
 -                                  0xffff, 0);
 +              snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
 +                                  WM8993_HPOUT1L_OUTP |
 +                                  WM8993_HPOUT1R_OUTP, 0);
  
 -              snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
                snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
                                    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
                                    0);
 -
 -              snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
 -                                  WM8993_CP_ENA, 0);
 +      //      printk("hp_event power1 down: 0x%04x",snd_soc_read(codec,WM8993_POWER_MANAGEMENT_1));
                break;
        }
  
@@@ -545,11 -438,11 +545,11 @@@ static const struct snd_soc_dapm_widge
  SND_SOC_DAPM_INPUT("IN1LN"),
  SND_SOC_DAPM_INPUT("IN1LP"),
  SND_SOC_DAPM_INPUT("IN2LN"),
 -SND_SOC_DAPM_INPUT("IN2LP/VXRN"),
 +SND_SOC_DAPM_INPUT("IN2LP:VXRN"),
  SND_SOC_DAPM_INPUT("IN1RN"),
  SND_SOC_DAPM_INPUT("IN1RP"),
  SND_SOC_DAPM_INPUT("IN2RN"),
 -SND_SOC_DAPM_INPUT("IN2RP/VXRP"),
 +SND_SOC_DAPM_INPUT("IN2RP:VXRP"),
  
  SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0),
  SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0),
@@@ -580,8 -473,6 +580,8 @@@ SND_SOC_DAPM_MIXER("Right Output Mixer"
  SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
  SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
  
 +SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event, 
 +                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
  SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
                   NULL, 0,
                   hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@@ -640,27 -531,20 +640,27 @@@ SND_SOC_DAPM_OUTPUT("LINEOUT2N")
  };
  
  static const struct snd_soc_dapm_route analogue_routes[] = {
 +      { "IN1L PGA", NULL , "MICBIAS2" },
 +      { "IN1R PGA", NULL , "MICBIAS1" },
 +      { "MICBIAS2", NULL , "IN1LP"},
 +      { "MICBIAS2", NULL , "IN1LN"},
 +      { "MICBIAS1", NULL , "IN1RP"},
 +      { "MICBIAS1", NULL , "IN1RN"},
 +      
        { "IN1L PGA", "IN1LP Switch", "IN1LP" },
        { "IN1L PGA", "IN1LN Switch", "IN1LN" },
  
        { "IN1R PGA", "IN1RP Switch", "IN1RP" },
        { "IN1R PGA", "IN1RN Switch", "IN1RN" },
  
 -      { "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" },
 +      { "IN2L PGA", "IN2LP Switch", "IN2LP:VXRN" },
        { "IN2L PGA", "IN2LN Switch", "IN2LN" },
  
 -      { "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" },
 +      { "IN2R PGA", "IN2RP Switch", "IN2RP:VXRP" },
        { "IN2R PGA", "IN2RN Switch", "IN2RN" },
  
 -      { "Direct Voice", NULL, "IN2LP/VXRN" },
 -      { "Direct Voice", NULL, "IN2RP/VXRP" },
 +      { "Direct Voice", NULL, "IN2LP:VXRN" },
 +      { "Direct Voice", NULL, "IN2RP:VXRP" },
  
        { "MIXINL", "IN1L Switch", "IN1L PGA" },
        { "MIXINL", "IN2L Switch", "IN2L PGA" },
        { "Left Output Mixer", "Right Input Switch", "MIXINR" },
        { "Left Output Mixer", "IN2RN Switch", "IN2RN" },
        { "Left Output Mixer", "IN2LN Switch", "IN2LN" },
 -      { "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" },
 +      { "Left Output Mixer", "IN2LP Switch", "IN2LP:VXRN" },
        { "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
        { "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
  
        { "Right Output Mixer", "Right Input Switch", "MIXINR" },
        { "Right Output Mixer", "IN2LN Switch", "IN2LN" },
        { "Right Output Mixer", "IN2RN Switch", "IN2RN" },
 -      { "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" },
 +      { "Right Output Mixer", "IN2RP Switch", "IN2RP:VXRP" },
        { "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
        { "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
  
        { "Headphone PGA", NULL, "Left Headphone Mux" },
        { "Headphone PGA", NULL, "Right Headphone Mux" },
        { "Headphone PGA", NULL, "CLK_SYS" },
 +      { "Headphone PGA", NULL, "Headphone Supply" },
  
        { "HPOUT1L", NULL, "Headphone PGA" },
        { "HPOUT1R", NULL, "Headphone PGA" },
  static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
        { "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
        { "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
-       { "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" },
+       { "LINEOUT1 Mixer", "Output Switch", "Left Output PGA" },
  
        { "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
        { "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
  };
  
  static const struct snd_soc_dapm_route lineout1_se_routes[] = {
-       { "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
-       { "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
+       { "LINEOUT1N Mixer", "Left Output Switch", "Left Output PGA" },
+       { "LINEOUT1N Mixer", "Right Output Switch", "Right Output PGA" },
  
-       { "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" },
+       { "LINEOUT1P Mixer", "Left Output Switch", "Left Output PGA" },
  
        { "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
        { "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
  static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
        { "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
        { "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
-       { "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" },
+       { "LINEOUT2 Mixer", "Output Switch", "Right Output PGA" },
  
        { "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
        { "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
  };
  
  static const struct snd_soc_dapm_route lineout2_se_routes[] = {
-       { "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
-       { "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
+       { "LINEOUT2N Mixer", "Left Output Switch", "Left Output PGA" },
+       { "LINEOUT2N Mixer", "Right Output Switch", "Right Output PGA" },
  
-       { "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" },
+       { "LINEOUT2P Mixer", "Right Output Switch", "Right Output PGA" },
  
        { "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
        { "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
@@@ -803,17 -686,21 +803,21 @@@ int wm_hubs_add_analogue_controls(struc
        snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
                            WM8993_IN2_VU, WM8993_IN2_VU);
  
+       snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_LEFT,
+                           WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
        snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
                            WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
  
        snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
-                           WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC);
+                           WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC,
+                           WM8993_HPOUT1_VU | WM8993_HPOUT1L_ZC);
        snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
                            WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
                            WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
  
        snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
-                           WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC);
+                           WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU,
+                           WM8993_MIXOUTL_ZC | WM8993_MIXOUT_VU);
        snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
                            WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
                            WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
@@@ -855,47 -742,6 +859,47 @@@ int wm_hubs_add_analogue_routes(struct 
  }
  EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
  
 +int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
 +                                int lineout1_diff, int lineout2_diff,
 +                                int lineout1fb, int lineout2fb,
 +                                int jd_scthr, int jd_thr, int micbias1_lvl,
 +                                int micbias2_lvl)
 +{
 +      if (!lineout1_diff)
 +              snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
 +                                  WM8993_LINEOUT1_MODE,
 +                                  WM8993_LINEOUT1_MODE);
 +      if (!lineout2_diff)
 +              snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
 +                                  WM8993_LINEOUT2_MODE,
 +                                  WM8993_LINEOUT2_MODE);
 +
 +      /* If the line outputs are differential then we aren't presenting
 +       * VMID as an output and can disable it.
 +       */
 +//    if (lineout1_diff && lineout2_diff)
 +//            codec->idle_bias_off = 1;
 +
 +      if (lineout1fb)
 +              snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
 +                                  WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
 +
 +      if (lineout2fb)
 +              snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
 +                                  WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
 +
 +      snd_soc_update_bits(codec, WM8993_MICBIAS,
 +                          WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
 +                          WM8993_MICB1_LVL | WM8993_MICB2_LVL,
 +                          jd_scthr << WM8993_JD_SCTHR_SHIFT |
 +                          jd_thr << WM8993_JD_THR_SHIFT |
 +                          micbias1_lvl |
 +                          micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
 +
 +      return 0;
 +}
 +EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
 +
  MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
  MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
  MODULE_LICENSE("GPL");