Merge branch 'next/devel2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2011 00:42:18 +0000 (17:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 27 Jul 2011 00:42:18 +0000 (17:42 -0700)
* 'next/devel2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/linux-arm-soc: (47 commits)
  OMAP: Add debugfs node to show the summary of all clocks
  OMAP2+: hwmod: Follow the recommended PRCM module enable sequence
  OMAP2+: clock: allow per-SoC clock init code to prevent clockdomain calls from clock code
  OMAP2+: clockdomain: Add per clkdm lock to prevent concurrent state programming
  OMAP2+: PM: idle clkdms only if already in idle
  OMAP2+: clockdomain: add clkdm_in_hwsup()
  OMAP2+: clockdomain: Add 2 APIs to control clockdomain from hwmod framework
  OMAP: clockdomain: Remove redundant call to pwrdm_wait_transition()
  OMAP4: hwmod: Introduce the module control in hwmod control
  OMAP4: cm: Add two new APIs for modulemode control
  OMAP4: hwmod data: Add modulemode entry in omap_hwmod structure
  OMAP4: hwmod data: Add PRM context register offset
  OMAP4: prm: Remove deprecated functions
  OMAP4: prm: Replace warm reset API with the offset based version
  OMAP4: hwmod: Replace RSTCTRL absolute address with offset macros
  OMAP: hwmod: Wait the idle status to be disabled
  OMAP4: hwmod: Replace CLKCTRL absolute address with offset macros
  OMAP2+: hwmod: Init clkdm field at boot time
  OMAP4: hwmod data: Add clock domain attribute
  OMAP4: clock data: Add missing divider selection for auxclks
  ...

1  2 
arch/arm/mach-omap2/board-4430sdp.c
arch/arm/mach-omap2/board-rx51-peripherals.c
arch/arm/mach-omap2/clock.c
arch/arm/mach-omap2/clock44xx_data.c
arch/arm/plat-omap/clock.c
arch/arm/plat-omap/omap_device.c
drivers/mfd/Kconfig
drivers/mfd/Makefile
sound/soc/codecs/Kconfig
sound/soc/codecs/twl6040.c

@@@ -38,7 -38,6 +38,7 @@@
  #include <plat/mmc.h>
  #include <plat/omap4-keypad.h>
  #include <video/omapdss.h>
 +#include <linux/wl12xx.h>
  
  #include "mux.h"
  #include "hsmmc.h"
@@@ -53,9 -52,6 +53,9 @@@
  #define HDMI_GPIO_HPD 60 /* Hot plug pin for HDMI */
  #define HDMI_GPIO_LS_OE 41 /* Level shifter for HDMI */
  
 +#define GPIO_WIFI_PMENA               54
 +#define GPIO_WIFI_IRQ         53
 +
  static const int sdp4430_keymap[] = {
        KEY(0, 0, KEY_E),
        KEY(0, 1, KEY_R),
        KEY(7, 6, KEY_OK),
        KEY(7, 7, KEY_DOWN),
  };
 +static struct omap_device_pad keypad_pads[] __initdata = {
 +      {       .name   = "kpd_col1.kpd_col1",
 +              .enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
 +      },
 +      {       .name   = "kpd_col1.kpd_col1",
 +              .enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
 +      },
 +      {       .name   = "kpd_col2.kpd_col2",
 +              .enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
 +      },
 +      {       .name   = "kpd_col3.kpd_col3",
 +              .enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
 +      },
 +      {       .name   = "kpd_col4.kpd_col4",
 +              .enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
 +      },
 +      {       .name   = "kpd_col5.kpd_col5",
 +              .enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
 +      },
 +      {       .name   = "gpmc_a23.kpd_col7",
 +              .enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
 +      },
 +      {       .name   = "gpmc_a22.kpd_col6",
 +              .enable = OMAP_WAKEUP_EN | OMAP_MUX_MODE1,
 +      },
 +      {       .name   = "kpd_row0.kpd_row0",
 +              .enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
 +                      OMAP_MUX_MODE1 | OMAP_INPUT_EN,
 +      },
 +      {       .name   = "kpd_row1.kpd_row1",
 +              .enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
 +                      OMAP_MUX_MODE1 | OMAP_INPUT_EN,
 +      },
 +      {       .name   = "kpd_row2.kpd_row2",
 +              .enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
 +                      OMAP_MUX_MODE1 | OMAP_INPUT_EN,
 +      },
 +      {       .name   = "kpd_row3.kpd_row3",
 +              .enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
 +                      OMAP_MUX_MODE1 | OMAP_INPUT_EN,
 +      },
 +      {       .name   = "kpd_row4.kpd_row4",
 +              .enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
 +                      OMAP_MUX_MODE1 | OMAP_INPUT_EN,
 +      },
 +      {       .name   = "kpd_row5.kpd_row5",
 +              .enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
 +                      OMAP_MUX_MODE1 | OMAP_INPUT_EN,
 +      },
 +      {       .name   = "gpmc_a18.kpd_row6",
 +              .enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
 +                      OMAP_MUX_MODE1 | OMAP_INPUT_EN,
 +      },
 +      {       .name   = "gpmc_a19.kpd_row7",
 +              .enable = OMAP_PULL_ENA | OMAP_PULL_UP | OMAP_WAKEUP_EN |
 +                      OMAP_MUX_MODE1 | OMAP_INPUT_EN,
 +      },
 +};
  
  static struct matrix_keymap_data sdp4430_keymap_data = {
        .keymap                 = sdp4430_keymap,
@@@ -198,13 -136,6 +198,13 @@@ static struct omap4_keypad_platform_dat
        .rows                   = 8,
        .cols                   = 8,
  };
 +
 +static struct omap_board_data keypad_data = {
 +      .id                     = 1,
 +      .pads                   = keypad_pads,
 +      .pads_cnt               = ARRAY_SIZE(keypad_pads),
 +};
 +
  static struct gpio_led sdp4430_gpio_leds[] = {
        {
                .name   = "omap4:green:debug0",
@@@ -345,11 -276,40 +345,40 @@@ static struct platform_device sdp4430_l
        .id             = -1,
  };
  
+ static struct regulator_consumer_supply sdp4430_vbat_supply[] = {
+       REGULATOR_SUPPLY("vddvibl", "twl6040-vibra"),
+       REGULATOR_SUPPLY("vddvibr", "twl6040-vibra"),
+ };
+ static struct regulator_init_data sdp4430_vbat_data = {
+       .constraints = {
+               .always_on      = 1,
+       },
+       .num_consumer_supplies  = ARRAY_SIZE(sdp4430_vbat_supply),
+       .consumer_supplies      = sdp4430_vbat_supply,
+ };
+ static struct fixed_voltage_config sdp4430_vbat_pdata = {
+       .supply_name    = "VBAT",
+       .microvolts     = 3750000,
+       .init_data      = &sdp4430_vbat_data,
+       .gpio           = -EINVAL,
+ };
+ static struct platform_device sdp4430_vbat = {
+       .name           = "reg-fixed-voltage",
+       .id             = -1,
+       .dev = {
+               .platform_data = &sdp4430_vbat_pdata,
+       },
+ };
  static struct platform_device *sdp4430_devices[] __initdata = {
        &sdp4430_lcd_device,
        &sdp4430_gpio_keys_device,
        &sdp4430_leds_gpio,
        &sdp4430_leds_pwm,
+       &sdp4430_vbat,
  };
  
  static struct omap_lcd_config sdp4430_lcd_config __initdata = {
@@@ -385,17 -345,8 +414,17 @@@ static struct omap2_hsmmc_info mmc[] = 
        {
                .mmc            = 1,
                .caps           = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA,
 +              .gpio_cd        = -EINVAL,
                .gpio_wp        = -EINVAL,
        },
 +      {
 +              .mmc            = 5,
 +              .caps           = MMC_CAP_4_BIT_DATA | MMC_CAP_POWER_OFF_CARD,
 +              .gpio_cd        = -EINVAL,
 +              .gpio_wp        = -EINVAL,
 +              .ocr_mask       = MMC_VDD_165_195,
 +              .nonremovable   = true,
 +      },
        {}      /* Terminator */
  };
  
@@@ -403,37 -354,6 +432,37 @@@ static struct regulator_consumer_suppl
        REGULATOR_SUPPLY("vmmc", "omap_hsmmc.1"),
  };
  
 +static struct regulator_consumer_supply omap4_sdp4430_vmmc5_supply = {
 +      .supply = "vmmc",
 +      .dev_name = "omap_hsmmc.4",
 +};
 +
 +static struct regulator_init_data sdp4430_vmmc5 = {
 +      .constraints = {
 +              .valid_ops_mask = REGULATOR_CHANGE_STATUS,
 +      },
 +      .num_consumer_supplies = 1,
 +      .consumer_supplies = &omap4_sdp4430_vmmc5_supply,
 +};
 +
 +static struct fixed_voltage_config sdp4430_vwlan = {
 +      .supply_name            = "vwl1271",
 +      .microvolts             = 1800000, /* 1.8V */
 +      .gpio                   = GPIO_WIFI_PMENA,
 +      .startup_delay          = 70000, /* 70msec */
 +      .enable_high            = 1,
 +      .enabled_at_boot        = 0,
 +      .init_data              = &sdp4430_vmmc5,
 +};
 +
 +static struct platform_device omap_vwlan_device = {
 +      .name           = "reg-fixed-voltage",
 +      .id             = 1,
 +      .dev = {
 +              .platform_data = &sdp4430_vwlan,
 +      },
 +};
 +
  static int omap4_twl6030_hsmmc_late_init(struct device *dev)
  {
        int ret = 0;
@@@ -505,7 -425,33 +534,33 @@@ static struct regulator_init_data sdp44
        },
  };
  
+ static struct twl4030_codec_data twl6040_codec = {
+       /* single-step ramp for headset and handsfree */
+       .hs_left_step   = 0x0f,
+       .hs_right_step  = 0x0f,
+       .hf_left_step   = 0x1d,
+       .hf_right_step  = 0x1d,
+ };
+ static struct twl4030_vibra_data twl6040_vibra = {
+       .vibldrv_res = 8,
+       .vibrdrv_res = 3,
+       .viblmotor_res = 10,
+       .vibrmotor_res = 10,
+       .vddvibl_uV = 0,        /* fixed volt supply - VBAT */
+       .vddvibr_uV = 0,        /* fixed volt supply - VBAT */
+ };
+ static struct twl4030_audio_data twl6040_audio = {
+       .codec          = &twl6040_codec,
+       .vibra          = &twl6040_vibra,
+       .audpwron_gpio  = 127,
+       .naudint_irq    = OMAP44XX_IRQ_SYS_2N,
+       .irq_base       = TWL6040_CODEC_IRQ_BASE,
+ };
  static struct twl4030_platform_data sdp4430_twldata = {
+       .audio          = &twl6040_audio,
        /* Regulators */
        .vusim          = &sdp4430_vusim,
        .vaux1          = &sdp4430_vaux1,
@@@ -703,41 -649,6 +758,41 @@@ static inline void board_serial_init(vo
  }
   #endif
  
 +static void omap4_sdp4430_wifi_mux_init(void)
 +{
 +      omap_mux_init_gpio(GPIO_WIFI_IRQ, OMAP_PIN_INPUT |
 +                              OMAP_PIN_OFF_WAKEUPENABLE);
 +      omap_mux_init_gpio(GPIO_WIFI_PMENA, OMAP_PIN_OUTPUT);
 +
 +      omap_mux_init_signal("sdmmc5_cmd.sdmmc5_cmd",
 +                              OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
 +      omap_mux_init_signal("sdmmc5_clk.sdmmc5_clk",
 +                              OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
 +      omap_mux_init_signal("sdmmc5_dat0.sdmmc5_dat0",
 +                              OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
 +      omap_mux_init_signal("sdmmc5_dat1.sdmmc5_dat1",
 +                              OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
 +      omap_mux_init_signal("sdmmc5_dat2.sdmmc5_dat2",
 +                              OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
 +      omap_mux_init_signal("sdmmc5_dat3.sdmmc5_dat3",
 +                              OMAP_MUX_MODE0 | OMAP_PIN_INPUT_PULLUP);
 +
 +}
 +
 +static struct wl12xx_platform_data omap4_sdp4430_wlan_data __initdata = {
 +      .irq = OMAP_GPIO_IRQ(GPIO_WIFI_IRQ),
 +      .board_ref_clock = WL12XX_REFCLOCK_26,
 +      .board_tcxo_clock = WL12XX_TCXOCLOCK_26,
 +};
 +
 +static void omap4_sdp4430_wifi_init(void)
 +{
 +      omap4_sdp4430_wifi_mux_init();
 +      if (wl12xx_set_platform_data(&omap4_sdp4430_wlan_data))
 +              pr_err("Error setting wl12xx data\n");
 +      platform_device_register(&omap_vwlan_device);
 +}
 +
  static void __init omap_4430sdp_init(void)
  {
        int status;
        omap_sfh7741prox_init();
        platform_add_devices(sdp4430_devices, ARRAY_SIZE(sdp4430_devices));
        board_serial_init();
 +      omap4_sdp4430_wifi_init();
        omap4_twl6030_hsmmc_init(mmc);
  
        usb_musb_init(&musb_board_data);
                                ARRAY_SIZE(sdp4430_spi_board_info));
        }
  
 -      status = omap4_keyboard_init(&sdp4430_keypad_data);
 +      status = omap4_keyboard_init(&sdp4430_keypad_data, &keypad_data);
        if (status)
                pr_err("Keypad initialization failed: %d\n", status);
  
@@@ -39,7 -39,6 +39,7 @@@
  #include <sound/tpa6130a2-plat.h>
  #include <media/radio-si4713.h>
  #include <media/si4713.h>
 +#include <linux/leds-lp5523.h>
  
  #include <../drivers/staging/iio/light/tsl2563.h>
  
@@@ -54,7 -53,6 +54,7 @@@
  #define RX51_WL1251_IRQ_GPIO          42
  #define RX51_FMTX_RESET_GPIO          163
  #define RX51_FMTX_IRQ                 53
 +#define RX51_LP5523_CHIP_EN_GPIO      41
  
  #define RX51_USB_TRANSCEIVER_RST_GPIO 67
  
@@@ -73,64 -71,6 +73,64 @@@ static struct tsl2563_platform_data rx5
  };
  #endif
  
 +#if defined(CONFIG_LEDS_LP5523) || defined(CONFIG_LEDS_LP5523_MODULE)
 +static struct lp5523_led_config rx51_lp5523_led_config[] = {
 +      {
 +              .chan_nr        = 0,
 +              .led_current    = 50,
 +      }, {
 +              .chan_nr        = 1,
 +              .led_current    = 50,
 +      }, {
 +              .chan_nr        = 2,
 +              .led_current    = 50,
 +      }, {
 +              .chan_nr        = 3,
 +              .led_current    = 50,
 +      }, {
 +              .chan_nr        = 4,
 +              .led_current    = 50,
 +      }, {
 +              .chan_nr        = 5,
 +              .led_current    = 50,
 +      }, {
 +              .chan_nr        = 6,
 +              .led_current    = 50,
 +      }, {
 +              .chan_nr        = 7,
 +              .led_current    = 50,
 +      }, {
 +              .chan_nr        = 8,
 +              .led_current    = 50,
 +      }
 +};
 +
 +static int rx51_lp5523_setup(void)
 +{
 +      return gpio_request_one(RX51_LP5523_CHIP_EN_GPIO, GPIOF_DIR_OUT,
 +                      "lp5523_enable");
 +}
 +
 +static void rx51_lp5523_release(void)
 +{
 +      gpio_free(RX51_LP5523_CHIP_EN_GPIO);
 +}
 +
 +static void rx51_lp5523_enable(bool state)
 +{
 +      gpio_set_value(RX51_LP5523_CHIP_EN_GPIO, !!state);
 +}
 +
 +static struct lp5523_platform_data rx51_lp5523_platform_data = {
 +      .led_config             = rx51_lp5523_led_config,
 +      .num_channels           = ARRAY_SIZE(rx51_lp5523_led_config),
 +      .clock_mode             = LP5523_CLOCK_AUTO,
 +      .setup_resources        = rx51_lp5523_setup,
 +      .release_resources      = rx51_lp5523_release,
 +      .enable                 = rx51_lp5523_enable,
 +};
 +#endif
 +
  static struct omap2_mcspi_device_config wl1251_mcspi_config = {
        .turbo_mode     = 0,
        .single_channel = 1,
@@@ -555,32 -495,6 +555,32 @@@ static struct regulator_init_data rx51_
        .consumer_supplies      = rx51_vmmc2_supplies,
  };
  
 +static struct regulator_init_data rx51_vpll1 = {
 +      .constraints = {
 +              .name                   = "VPLL",
 +              .min_uV                 = 1800000,
 +              .max_uV                 = 1800000,
 +              .apply_uV               = true,
 +              .always_on              = true,
 +              .valid_modes_mask       = REGULATOR_MODE_NORMAL
 +                                      | REGULATOR_MODE_STANDBY,
 +              .valid_ops_mask         = REGULATOR_CHANGE_MODE,
 +      },
 +};
 +
 +static struct regulator_init_data rx51_vpll2 = {
 +      .constraints = {
 +              .name                   = "VSDI_CSI",
 +              .min_uV                 = 1800000,
 +              .max_uV                 = 1800000,
 +              .apply_uV               = true,
 +              .always_on              = true,
 +              .valid_modes_mask       = REGULATOR_MODE_NORMAL
 +                                      | REGULATOR_MODE_STANDBY,
 +              .valid_ops_mask         = REGULATOR_CHANGE_MODE,
 +      },
 +};
 +
  static struct regulator_init_data rx51_vsim = {
        .constraints = {
                .name                   = "VMMC2_IO_18",
@@@ -610,43 -524,6 +610,43 @@@ static struct regulator_init_data rx51_
        .consumer_supplies      = rx51_vio_supplies,
  };
  
 +static struct regulator_init_data rx51_vintana1 = {
 +      .constraints = {
 +              .name                   = "VINTANA1",
 +              .min_uV                 = 1500000,
 +              .max_uV                 = 1500000,
 +              .always_on              = true,
 +              .valid_modes_mask       = REGULATOR_MODE_NORMAL
 +                                      | REGULATOR_MODE_STANDBY,
 +              .valid_ops_mask         = REGULATOR_CHANGE_MODE,
 +      },
 +};
 +
 +static struct regulator_init_data rx51_vintana2 = {
 +      .constraints = {
 +              .name                   = "VINTANA2",
 +              .min_uV                 = 2750000,
 +              .max_uV                 = 2750000,
 +              .apply_uV               = true,
 +              .always_on              = true,
 +              .valid_modes_mask       = REGULATOR_MODE_NORMAL
 +                                      | REGULATOR_MODE_STANDBY,
 +              .valid_ops_mask         = REGULATOR_CHANGE_MODE,
 +      },
 +};
 +
 +static struct regulator_init_data rx51_vintdig = {
 +      .constraints = {
 +              .name                   = "VINTDIG",
 +              .min_uV                 = 1500000,
 +              .max_uV                 = 1500000,
 +              .always_on              = true,
 +              .valid_modes_mask       = REGULATOR_MODE_NORMAL
 +                                      | REGULATOR_MODE_STANDBY,
 +              .valid_ops_mask         = REGULATOR_CHANGE_MODE,
 +      },
 +};
 +
  static struct si4713_platform_data rx51_si4713_i2c_data __initdata_or_module = {
        .gpio_reset     = RX51_FMTX_RESET_GPIO,
  };
@@@ -661,7 -538,7 +661,7 @@@ static struct radio_si4713_platform_dat
        .subdev_board_info = &rx51_si4713_board_info,
  };
  
 -static struct platform_device rx51_si4713_dev __initdata_or_module = {
 +static struct platform_device rx51_si4713_dev = {
        .name   = "radio-si4713",
        .id     = -1,
        .dev    = {
@@@ -864,11 -741,11 +864,11 @@@ static struct twl4030_power_data rx51_t
        .resource_config = twl4030_rconfig,
  };
  
- struct twl4030_codec_vibra_data rx51_vibra_data __initdata = {
+ struct twl4030_vibra_data rx51_vibra_data __initdata = {
        .coexist        = 0,
  };
  
- struct twl4030_codec_data rx51_codec_data __initdata = {
+ struct twl4030_audio_data rx51_audio_data __initdata = {
        .audio_mclk     = 26000000,
        .vibra          = &rx51_vibra_data,
  };
@@@ -878,18 -755,13 +878,18 @@@ static struct twl4030_platform_data rx5
        .gpio                   = &rx51_gpio_data,
        .keypad                 = &rx51_kp_data,
        .power                  = &rx51_t2scripts_data,
-       .codec                  = &rx51_codec_data,
+       .audio                  = &rx51_audio_data,
  
        .vaux1                  = &rx51_vaux1,
        .vaux2                  = &rx51_vaux2,
        .vaux4                  = &rx51_vaux4,
        .vmmc1                  = &rx51_vmmc1,
 +      .vpll1                  = &rx51_vpll1,
 +      .vpll2                  = &rx51_vpll2,
        .vsim                   = &rx51_vsim,
 +      .vintana1               = &rx51_vintana1,
 +      .vintana2               = &rx51_vintana2,
 +      .vintdig                = &rx51_vintdig,
        .vio                    = &rx51_vio,
  };
  
@@@ -927,12 -799,6 +927,12 @@@ static struct i2c_board_info __initdat
                I2C_BOARD_INFO("tsl2563", 0x29),
                .platform_data = &rx51_tsl2563_platform_data,
        },
 +#endif
 +#if defined(CONFIG_LEDS_LP5523) || defined(CONFIG_LEDS_LP5523_MODULE)
 +      {
 +              I2C_BOARD_INFO("lp5523", 0x32),
 +              .platform_data  = &rx51_lp5523_platform_data,
 +      },
  #endif
        {
                I2C_BOARD_INFO("tpa6130a2", 0x60),
@@@ -1084,7 -950,6 +1084,7 @@@ error
  void __init rx51_peripherals_init(void)
  {
        rx51_i2c_init();
 +      regulator_has_full_constraints();
        gpmc_onenand_init(board_onenand_data);
        board_smc91x_init();
        rx51_add_gpio_keys();
  
  u8 cpu_mask;
  
+ /*
+  * clkdm_control: if true, then when a clock is enabled in the
+  * hardware, its clockdomain will first be enabled; and when a clock
+  * is disabled in the hardware, its clockdomain will be disabled
+  * afterwards.
+  */
+ static bool clkdm_control = true;
  /*
   * OMAP2+ specific clock functions
   */
@@@ -99,6 -107,19 +107,19 @@@ void omap2_init_clk_clkdm(struct clk *c
        }
  }
  
+ /**
+  * omap2_clk_disable_clkdm_control - disable clkdm control on clk enable/disable
+  *
+  * Prevent the OMAP clock code from calling into the clockdomain code
+  * when a hardware clock in that clockdomain is enabled or disabled.
+  * Intended to be called at init time from omap*_clk_init().  No
+  * return value.
+  */
+ void __init omap2_clk_disable_clkdm_control(void)
+ {
+       clkdm_control = false;
+ }
  /**
   * omap2_clk_dflt_find_companion - find companion clock to @clk
   * @clk: struct clk * to find the companion clock of
@@@ -268,7 -289,7 +289,7 @@@ void omap2_clk_disable(struct clk *clk
                clk->ops->disable(clk);
        }
  
-       if (clk->clkdm)
+       if (clkdm_control && clk->clkdm)
                clkdm_clk_disable(clk->clkdm, clk);
  
        if (clk->parent)
@@@ -308,7 -329,7 +329,7 @@@ int omap2_clk_enable(struct clk *clk
                }
        }
  
-       if (clk->clkdm) {
+       if (clkdm_control && clk->clkdm) {
                ret = clkdm_clk_enable(clk->clkdm, clk);
                if (ret) {
                        WARN(1, "clock: %s: could not enable clockdomain %s: "
        return 0;
  
  oce_err3:
-       if (clk->clkdm)
+       if (clkdm_control && clk->clkdm)
                clkdm_clk_disable(clk->clkdm, clk);
  oce_err2:
        if (clk->parent)
@@@ -453,7 -474,6 +474,7 @@@ int __init omap2_clk_switch_mpurate_at_
        if (IS_ERR_VALUE(r)) {
                WARN(1, "clock: %s: unable to set MPU rate to %d: %d\n",
                     mpurate_ck->name, mpurate, r);
 +              clk_put(mpurate_ck);
                return -EINVAL;
        }
  
@@@ -1397,40 -1397,6 +1397,40 @@@ static struct clk dss_dss_clk = 
        .recalc         = &followparent_recalc,
  };
  
 +static const struct clksel_rate div3_8to32_rates[] = {
 +      { .div = 8, .val = 0, .flags = RATE_IN_44XX },
 +      { .div = 16, .val = 1, .flags = RATE_IN_44XX },
 +      { .div = 32, .val = 2, .flags = RATE_IN_44XX },
 +      { .div = 0 },
 +};
 +
 +static const struct clksel div_ts_div[] = {
 +      { .parent = &l4_wkup_clk_mux_ck, .rates = div3_8to32_rates },
 +      { .parent = NULL },
 +};
 +
 +static struct clk div_ts_ck = {
 +      .name           = "div_ts_ck",
 +      .parent         = &l4_wkup_clk_mux_ck,
 +      .clksel         = div_ts_div,
 +      .clksel_reg     = OMAP4430_CM_WKUP_BANDGAP_CLKCTRL,
 +      .clksel_mask    = OMAP4430_CLKSEL_24_25_MASK,
 +      .ops            = &clkops_null,
 +      .recalc         = &omap2_clksel_recalc,
 +      .round_rate     = &omap2_clksel_round_rate,
 +      .set_rate       = &omap2_clksel_set_rate,
 +};
 +
 +static struct clk bandgap_ts_fclk = {
 +      .name           = "bandgap_ts_fclk",
 +      .ops            = &clkops_omap2_dflt,
 +      .enable_reg     = OMAP4430_CM_WKUP_BANDGAP_CLKCTRL,
 +      .enable_bit     = OMAP4460_OPTFCLKEN_TS_FCLK_SHIFT,
 +      .clkdm_name     = "l4_wkup_clkdm",
 +      .parent         = &div_ts_ck,
 +      .recalc         = &followparent_recalc,
 +};
 +
  static struct clk dss_48mhz_clk = {
        .name           = "dss_48mhz_clk",
        .ops            = &clkops_omap2_dflt,
@@@ -2808,19 -2774,39 +2808,39 @@@ static struct clk trace_clk_div_ck = 
  
  /* SCRM aux clk nodes */
  
- static const struct clksel auxclk_sel[] = {
+ static const struct clksel auxclk_src_sel[] = {
        { .parent = &sys_clkin_ck, .rates = div_1_0_rates },
        { .parent = &dpll_core_m3x2_ck, .rates = div_1_1_rates },
        { .parent = &dpll_per_m3x2_ck, .rates = div_1_2_rates },
        { .parent = NULL },
  };
  
- static struct clk auxclk0_ck = {
-       .name           = "auxclk0_ck",
+ static const struct clksel_rate div16_1to16_rates[] = {
+       { .div = 1, .val = 0, .flags = RATE_IN_4430 },
+       { .div = 2, .val = 1, .flags = RATE_IN_4430 },
+       { .div = 3, .val = 2, .flags = RATE_IN_4430 },
+       { .div = 4, .val = 3, .flags = RATE_IN_4430 },
+       { .div = 5, .val = 4, .flags = RATE_IN_4430 },
+       { .div = 6, .val = 5, .flags = RATE_IN_4430 },
+       { .div = 7, .val = 6, .flags = RATE_IN_4430 },
+       { .div = 8, .val = 7, .flags = RATE_IN_4430 },
+       { .div = 9, .val = 8, .flags = RATE_IN_4430 },
+       { .div = 10, .val = 9, .flags = RATE_IN_4430 },
+       { .div = 11, .val = 10, .flags = RATE_IN_4430 },
+       { .div = 12, .val = 11, .flags = RATE_IN_4430 },
+       { .div = 13, .val = 12, .flags = RATE_IN_4430 },
+       { .div = 14, .val = 13, .flags = RATE_IN_4430 },
+       { .div = 15, .val = 14, .flags = RATE_IN_4430 },
+       { .div = 16, .val = 15, .flags = RATE_IN_4430 },
+       { .div = 0 },
+ };
+ static struct clk auxclk0_src_ck = {
+       .name           = "auxclk0_src_ck",
        .parent         = &sys_clkin_ck,
        .init           = &omap2_init_clksel_parent,
        .ops            = &clkops_omap2_dflt,
-       .clksel         = auxclk_sel,
+       .clksel         = auxclk_src_sel,
        .clksel_reg     = OMAP4_SCRM_AUXCLK0,
        .clksel_mask    = OMAP4_SRCSELECT_MASK,
        .recalc         = &omap2_clksel_recalc,
        .enable_bit     = OMAP4_ENABLE_SHIFT,
  };
  
- static struct clk auxclk1_ck = {
-       .name           = "auxclk1_ck",
+ static const struct clksel auxclk0_sel[] = {
+       { .parent = &auxclk0_src_ck, .rates = div16_1to16_rates },
+       { .parent = NULL },
+ };
+ static struct clk auxclk0_ck = {
+       .name           = "auxclk0_ck",
+       .parent         = &auxclk0_src_ck,
+       .clksel         = auxclk0_sel,
+       .clksel_reg     = OMAP4_SCRM_AUXCLK0,
+       .clksel_mask    = OMAP4_CLKDIV_MASK,
+       .ops            = &clkops_null,
+       .recalc         = &omap2_clksel_recalc,
+       .round_rate     = &omap2_clksel_round_rate,
+       .set_rate       = &omap2_clksel_set_rate,
+ };
+ static struct clk auxclk1_src_ck = {
+       .name           = "auxclk1_src_ck",
        .parent         = &sys_clkin_ck,
        .init           = &omap2_init_clksel_parent,
        .ops            = &clkops_omap2_dflt,
-       .clksel         = auxclk_sel,
+       .clksel         = auxclk_src_sel,
        .clksel_reg     = OMAP4_SCRM_AUXCLK1,
        .clksel_mask    = OMAP4_SRCSELECT_MASK,
        .recalc         = &omap2_clksel_recalc,
        .enable_bit     = OMAP4_ENABLE_SHIFT,
  };
  
- static struct clk auxclk2_ck = {
-       .name           = "auxclk2_ck",
+ static const struct clksel auxclk1_sel[] = {
+       { .parent = &auxclk1_src_ck, .rates = div16_1to16_rates },
+       { .parent = NULL },
+ };
+ static struct clk auxclk1_ck = {
+       .name           = "auxclk1_ck",
+       .parent         = &auxclk1_src_ck,
+       .clksel         = auxclk1_sel,
+       .clksel_reg     = OMAP4_SCRM_AUXCLK1,
+       .clksel_mask    = OMAP4_CLKDIV_MASK,
+       .ops            = &clkops_null,
+       .recalc         = &omap2_clksel_recalc,
+       .round_rate     = &omap2_clksel_round_rate,
+       .set_rate       = &omap2_clksel_set_rate,
+ };
+ static struct clk auxclk2_src_ck = {
+       .name           = "auxclk2_src_ck",
        .parent         = &sys_clkin_ck,
        .init           = &omap2_init_clksel_parent,
        .ops            = &clkops_omap2_dflt,
-       .clksel         = auxclk_sel,
+       .clksel         = auxclk_src_sel,
        .clksel_reg     = OMAP4_SCRM_AUXCLK2,
        .clksel_mask    = OMAP4_SRCSELECT_MASK,
        .recalc         = &omap2_clksel_recalc,
        .enable_bit     = OMAP4_ENABLE_SHIFT,
  };
  
- static struct clk auxclk3_ck = {
-       .name           = "auxclk3_ck",
+ static const struct clksel auxclk2_sel[] = {
+       { .parent = &auxclk2_src_ck, .rates = div16_1to16_rates },
+       { .parent = NULL },
+ };
+ static struct clk auxclk2_ck = {
+       .name           = "auxclk2_ck",
+       .parent         = &auxclk2_src_ck,
+       .clksel         = auxclk2_sel,
+       .clksel_reg     = OMAP4_SCRM_AUXCLK2,
+       .clksel_mask    = OMAP4_CLKDIV_MASK,
+       .ops            = &clkops_null,
+       .recalc         = &omap2_clksel_recalc,
+       .round_rate     = &omap2_clksel_round_rate,
+       .set_rate       = &omap2_clksel_set_rate,
+ };
+ static struct clk auxclk3_src_ck = {
+       .name           = "auxclk3_src_ck",
        .parent         = &sys_clkin_ck,
        .init           = &omap2_init_clksel_parent,
        .ops            = &clkops_omap2_dflt,
-       .clksel         = auxclk_sel,
+       .clksel         = auxclk_src_sel,
        .clksel_reg     = OMAP4_SCRM_AUXCLK3,
        .clksel_mask    = OMAP4_SRCSELECT_MASK,
        .recalc         = &omap2_clksel_recalc,
        .enable_bit     = OMAP4_ENABLE_SHIFT,
  };
  
- static struct clk auxclk4_ck = {
-       .name           = "auxclk4_ck",
+ static const struct clksel auxclk3_sel[] = {
+       { .parent = &auxclk3_src_ck, .rates = div16_1to16_rates },
+       { .parent = NULL },
+ };
+ static struct clk auxclk3_ck = {
+       .name           = "auxclk3_ck",
+       .parent         = &auxclk3_src_ck,
+       .clksel         = auxclk3_sel,
+       .clksel_reg     = OMAP4_SCRM_AUXCLK3,
+       .clksel_mask    = OMAP4_CLKDIV_MASK,
+       .ops            = &clkops_null,
+       .recalc         = &omap2_clksel_recalc,
+       .round_rate     = &omap2_clksel_round_rate,
+       .set_rate       = &omap2_clksel_set_rate,
+ };
+ static struct clk auxclk4_src_ck = {
+       .name           = "auxclk4_src_ck",
        .parent         = &sys_clkin_ck,
        .init           = &omap2_init_clksel_parent,
        .ops            = &clkops_omap2_dflt,
-       .clksel         = auxclk_sel,
+       .clksel         = auxclk_src_sel,
        .clksel_reg     = OMAP4_SCRM_AUXCLK4,
        .clksel_mask    = OMAP4_SRCSELECT_MASK,
        .recalc         = &omap2_clksel_recalc,
        .enable_bit     = OMAP4_ENABLE_SHIFT,
  };
  
- static struct clk auxclk5_ck = {
-       .name           = "auxclk5_ck",
+ static const struct clksel auxclk4_sel[] = {
+       { .parent = &auxclk4_src_ck, .rates = div16_1to16_rates },
+       { .parent = NULL },
+ };
+ static struct clk auxclk4_ck = {
+       .name           = "auxclk4_ck",
+       .parent         = &auxclk4_src_ck,
+       .clksel         = auxclk4_sel,
+       .clksel_reg     = OMAP4_SCRM_AUXCLK4,
+       .clksel_mask    = OMAP4_CLKDIV_MASK,
+       .ops            = &clkops_null,
+       .recalc         = &omap2_clksel_recalc,
+       .round_rate     = &omap2_clksel_round_rate,
+       .set_rate       = &omap2_clksel_set_rate,
+ };
+ static struct clk auxclk5_src_ck = {
+       .name           = "auxclk5_src_ck",
        .parent         = &sys_clkin_ck,
        .init           = &omap2_init_clksel_parent,
        .ops            = &clkops_omap2_dflt,
-       .clksel         = auxclk_sel,
+       .clksel         = auxclk_src_sel,
        .clksel_reg     = OMAP4_SCRM_AUXCLK5,
        .clksel_mask    = OMAP4_SRCSELECT_MASK,
        .recalc         = &omap2_clksel_recalc,
        .enable_bit     = OMAP4_ENABLE_SHIFT,
  };
  
+ static const struct clksel auxclk5_sel[] = {
+       { .parent = &auxclk5_src_ck, .rates = div16_1to16_rates },
+       { .parent = NULL },
+ };
+ static struct clk auxclk5_ck = {
+       .name           = "auxclk5_ck",
+       .parent         = &auxclk5_src_ck,
+       .clksel         = auxclk5_sel,
+       .clksel_reg     = OMAP4_SCRM_AUXCLK5,
+       .clksel_mask    = OMAP4_CLKDIV_MASK,
+       .ops            = &clkops_null,
+       .recalc         = &omap2_clksel_recalc,
+       .round_rate     = &omap2_clksel_round_rate,
+       .set_rate       = &omap2_clksel_set_rate,
+ };
  static const struct clksel auxclkreq_sel[] = {
        { .parent = &auxclk0_ck, .rates = div_1_0_rates },
        { .parent = &auxclk1_ck, .rates = div_1_1_rates },
@@@ -3063,9 -3151,7 +3185,9 @@@ static struct omap_clk omap44xx_clks[] 
        CLK(NULL,       "aes2_fck",                     &aes2_fck,      CK_443X),
        CLK(NULL,       "aess_fck",                     &aess_fck,      CK_443X),
        CLK(NULL,       "bandgap_fclk",                 &bandgap_fclk,  CK_443X),
 +      CLK(NULL,       "bandgap_ts_fclk",              &bandgap_ts_fclk,       CK_446X),
        CLK(NULL,       "des3des_fck",                  &des3des_fck,   CK_443X),
 +      CLK(NULL,       "div_ts_ck",                    &div_ts_ck,     CK_446X),
        CLK(NULL,       "dmic_sync_mux_ck",             &dmic_sync_mux_ck,      CK_443X),
        CLK(NULL,       "dmic_fck",                     &dmic_fck,      CK_443X),
        CLK(NULL,       "dsp_fck",                      &dsp_fck,       CK_443X),
        CLK(NULL,       "gpio6_ick",                    &gpio6_ick,     CK_443X),
        CLK(NULL,       "gpmc_ick",                     &gpmc_ick,      CK_443X),
        CLK(NULL,       "gpu_fck",                      &gpu_fck,       CK_443X),
-       CLK("omap2_hdq.0",      "fck",                          &hdq1w_fck,     CK_443X),
+       CLK(NULL,       "hdq1w_fck",                    &hdq1w_fck,     CK_443X),
        CLK(NULL,       "hsi_fck",                      &hsi_fck,       CK_443X),
-       CLK("omap_i2c.1",       "fck",                          &i2c1_fck,      CK_443X),
-       CLK("omap_i2c.2",       "fck",                          &i2c2_fck,      CK_443X),
-       CLK("omap_i2c.3",       "fck",                          &i2c3_fck,      CK_443X),
-       CLK("omap_i2c.4",       "fck",                          &i2c4_fck,      CK_443X),
+       CLK(NULL,       "i2c1_fck",                     &i2c1_fck,      CK_443X),
+       CLK(NULL,       "i2c2_fck",                     &i2c2_fck,      CK_443X),
+       CLK(NULL,       "i2c3_fck",                     &i2c3_fck,      CK_443X),
+       CLK(NULL,       "i2c4_fck",                     &i2c4_fck,      CK_443X),
        CLK(NULL,       "ipu_fck",                      &ipu_fck,       CK_443X),
        CLK(NULL,       "iss_ctrlclk",                  &iss_ctrlclk,   CK_443X),
        CLK(NULL,       "iss_fck",                      &iss_fck,       CK_443X),
        CLK(NULL,       "mcasp_sync_mux_ck",            &mcasp_sync_mux_ck,     CK_443X),
        CLK(NULL,       "mcasp_fck",                    &mcasp_fck,     CK_443X),
        CLK(NULL,       "mcbsp1_sync_mux_ck",           &mcbsp1_sync_mux_ck,    CK_443X),
-       CLK("omap-mcbsp.1",     "fck",                          &mcbsp1_fck,    CK_443X),
+       CLK(NULL,       "mcbsp1_fck",                   &mcbsp1_fck,    CK_443X),
        CLK(NULL,       "mcbsp2_sync_mux_ck",           &mcbsp2_sync_mux_ck,    CK_443X),
-       CLK("omap-mcbsp.2",     "fck",                          &mcbsp2_fck,    CK_443X),
+       CLK(NULL,       "mcbsp2_fck",                   &mcbsp2_fck,    CK_443X),
        CLK(NULL,       "mcbsp3_sync_mux_ck",           &mcbsp3_sync_mux_ck,    CK_443X),
-       CLK("omap-mcbsp.3",     "fck",                          &mcbsp3_fck,    CK_443X),
+       CLK(NULL,       "mcbsp3_fck",                   &mcbsp3_fck,    CK_443X),
        CLK(NULL,       "mcbsp4_sync_mux_ck",           &mcbsp4_sync_mux_ck,    CK_443X),
-       CLK("omap-mcbsp.4",     "fck",                          &mcbsp4_fck,    CK_443X),
+       CLK(NULL,       "mcbsp4_fck",                   &mcbsp4_fck,    CK_443X),
        CLK(NULL,       "mcpdm_fck",                    &mcpdm_fck,     CK_443X),
-       CLK("omap2_mcspi.1",    "fck",                          &mcspi1_fck,    CK_443X),
-       CLK("omap2_mcspi.2",    "fck",                          &mcspi2_fck,    CK_443X),
-       CLK("omap2_mcspi.3",    "fck",                          &mcspi3_fck,    CK_443X),
-       CLK("omap2_mcspi.4",    "fck",                          &mcspi4_fck,    CK_443X),
-       CLK("omap_hsmmc.0",     "fck",                          &mmc1_fck,      CK_443X),
-       CLK("omap_hsmmc.1",     "fck",                          &mmc2_fck,      CK_443X),
-       CLK("omap_hsmmc.2",     "fck",                          &mmc3_fck,      CK_443X),
-       CLK("omap_hsmmc.3",     "fck",                          &mmc4_fck,      CK_443X),
-       CLK("omap_hsmmc.4",     "fck",                          &mmc5_fck,      CK_443X),
+       CLK(NULL,       "mcspi1_fck",                   &mcspi1_fck,    CK_443X),
+       CLK(NULL,       "mcspi2_fck",                   &mcspi2_fck,    CK_443X),
+       CLK(NULL,       "mcspi3_fck",                   &mcspi3_fck,    CK_443X),
+       CLK(NULL,       "mcspi4_fck",                   &mcspi4_fck,    CK_443X),
+       CLK(NULL,       "mmc1_fck",                     &mmc1_fck,      CK_443X),
+       CLK(NULL,       "mmc2_fck",                     &mmc2_fck,      CK_443X),
+       CLK(NULL,       "mmc3_fck",                     &mmc3_fck,      CK_443X),
+       CLK(NULL,       "mmc4_fck",                     &mmc4_fck,      CK_443X),
+       CLK(NULL,       "mmc5_fck",                     &mmc5_fck,      CK_443X),
        CLK(NULL,       "ocp2scp_usb_phy_phy_48m",      &ocp2scp_usb_phy_phy_48m,       CK_443X),
        CLK(NULL,       "ocp2scp_usb_phy_ick",          &ocp2scp_usb_phy_ick,   CK_443X),
        CLK(NULL,       "ocp_wp_noc_ick",               &ocp_wp_noc_ick,        CK_443X),
        CLK(NULL,       "usim_ck",                      &usim_ck,       CK_443X),
        CLK(NULL,       "usim_fclk",                    &usim_fclk,     CK_443X),
        CLK(NULL,       "usim_fck",                     &usim_fck,      CK_443X),
-       CLK("omap_wdt", "fck",                          &wd_timer2_fck, CK_443X),
+       CLK(NULL,       "wd_timer2_fck",                &wd_timer2_fck, CK_443X),
        CLK(NULL,       "wd_timer3_fck",                &wd_timer3_fck, CK_443X),
        CLK(NULL,       "stm_clk_div_ck",               &stm_clk_div_ck,        CK_443X),
        CLK(NULL,       "trace_clk_div_ck",             &trace_clk_div_ck,      CK_443X),
+       CLK(NULL,       "auxclk0_src_ck",               &auxclk0_src_ck,        CK_443X),
        CLK(NULL,       "auxclk0_ck",                   &auxclk0_ck,    CK_443X),
-       CLK(NULL,       "auxclk1_ck",                   &auxclk1_ck,    CK_443X),
-       CLK(NULL,       "auxclk2_ck",                   &auxclk2_ck,    CK_443X),
-       CLK(NULL,       "auxclk3_ck",                   &auxclk3_ck,    CK_443X),
-       CLK(NULL,       "auxclk4_ck",                   &auxclk4_ck,    CK_443X),
-       CLK(NULL,       "auxclk5_ck",                   &auxclk5_ck,    CK_443X),
        CLK(NULL,       "auxclkreq0_ck",                &auxclkreq0_ck, CK_443X),
+       CLK(NULL,       "auxclk1_src_ck",               &auxclk1_src_ck,        CK_443X),
+       CLK(NULL,       "auxclk1_ck",                   &auxclk1_ck,    CK_443X),
        CLK(NULL,       "auxclkreq1_ck",                &auxclkreq1_ck, CK_443X),
+       CLK(NULL,       "auxclk2_src_ck",               &auxclk2_src_ck,        CK_443X),
+       CLK(NULL,       "auxclk2_ck",                   &auxclk2_ck,    CK_443X),
        CLK(NULL,       "auxclkreq2_ck",                &auxclkreq2_ck, CK_443X),
+       CLK(NULL,       "auxclk3_src_ck",               &auxclk3_src_ck,        CK_443X),
+       CLK(NULL,       "auxclk3_ck",                   &auxclk3_ck,    CK_443X),
        CLK(NULL,       "auxclkreq3_ck",                &auxclkreq3_ck, CK_443X),
+       CLK(NULL,       "auxclk4_src_ck",               &auxclk4_src_ck,        CK_443X),
+       CLK(NULL,       "auxclk4_ck",                   &auxclk4_ck,    CK_443X),
        CLK(NULL,       "auxclkreq4_ck",                &auxclkreq4_ck, CK_443X),
+       CLK(NULL,       "auxclk5_src_ck",               &auxclk5_src_ck,        CK_443X),
+       CLK(NULL,       "auxclk5_ck",                   &auxclk5_ck,    CK_443X),
        CLK(NULL,       "auxclkreq5_ck",                &auxclkreq5_ck, CK_443X),
        CLK(NULL,       "gpmc_ck",                      &dummy_ck,      CK_443X),
        CLK(NULL,       "gpt1_ick",                     &dummy_ck,      CK_443X),
@@@ -3245,12 -3337,10 +3373,13 @@@ int __init omap4xxx_clk_init(void
        if (cpu_is_omap44xx()) {
                cpu_mask = RATE_IN_4430;
                cpu_clkflg = CK_443X;
 +      } else if (cpu_is_omap446x()) {
 +              cpu_mask = RATE_IN_4460;
 +              cpu_clkflg = CK_446X;
        }
  
        clk_init(&omap2_clk_functions);
+       omap2_clk_disable_clkdm_control();
  
        for (c = omap44xx_clks; c < omap44xx_clks + ARRAY_SIZE(omap44xx_clks);
                                                                          c++)
@@@ -475,15 -475,51 +475,48 @@@ int __init clk_init(struct clk_function
  /*
   *    debugfs support to trace clock tree hierarchy and attributes
   */
+ #include <linux/debugfs.h>
+ #include <linux/seq_file.h>
  static struct dentry *clk_debugfs_root;
  
+ static int clk_dbg_show_summary(struct seq_file *s, void *unused)
+ {
+       struct clk *c;
+       struct clk *pa;
+       seq_printf(s, "%-30s %-30s %-10s %s\n",
+               "clock-name", "parent-name", "rate", "use-count");
+       list_for_each_entry(c, &clocks, node) {
+               pa = c->parent;
+               seq_printf(s, "%-30s %-30s %-10lu %d\n",
+                       c->name, pa ? pa->name : "none", c->rate, c->usecount);
+       }
+       return 0;
+ }
+ static int clk_dbg_open(struct inode *inode, struct file *file)
+ {
+       return single_open(file, clk_dbg_show_summary, inode->i_private);
+ }
+ static const struct file_operations debug_clock_fops = {
+       .open           = clk_dbg_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+ };
  static int clk_debugfs_register_one(struct clk *c)
  {
        int err;
 -      struct dentry *d, *child, *child_tmp;
 +      struct dentry *d;
        struct clk *pa = c->parent;
 -      char s[255];
 -      char *p = s;
  
 -      p += sprintf(p, "%s", c->name);
 -      d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root);
 +      d = debugfs_create_dir(c->name, pa ? pa->dent : clk_debugfs_root);
        if (!d)
                return -ENOMEM;
        c->dent = d;
        return 0;
  
  err_out:
 -      d = c->dent;
 -      list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
 -              debugfs_remove(child);
 -      debugfs_remove(c->dent);
 +      debugfs_remove_recursive(c->dent);
        return err;
  }
  
@@@ -545,6 -584,12 +578,12 @@@ static int __init clk_debugfs_init(void
                if (err)
                        goto err_out;
        }
+       d = debugfs_create_file("summary", S_IRUGO,
+               d, NULL, &debug_clock_fops);
+       if (!d)
+               return -ENOMEM;
        return 0;
  err_out:
        debugfs_remove_recursive(clk_debugfs_root);
@@@ -236,56 -236,71 +236,71 @@@ static int _omap_device_deactivate(stru
        return 0;
  }
  
+ static void _add_clkdev(struct omap_device *od, const char *clk_alias,
+                      const char *clk_name)
+ {
+       struct clk *r;
+       struct clk_lookup *l;
+       if (!clk_alias || !clk_name)
+               return;
+       pr_debug("omap_device: %s: Creating %s -> %s\n",
+                dev_name(&od->pdev.dev), clk_alias, clk_name);
+       r = clk_get_sys(dev_name(&od->pdev.dev), clk_alias);
+       if (!IS_ERR(r)) {
+               pr_warning("omap_device: %s: alias %s already exists\n",
+                          dev_name(&od->pdev.dev), clk_alias);
+               clk_put(r);
+               return;
+       }
+       r = omap_clk_get_by_name(clk_name);
+       if (IS_ERR(r)) {
+               pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
+                      dev_name(&od->pdev.dev), clk_name);
+               return;
+       }
+       l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev.dev));
+       if (!l) {
+               pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
+                      dev_name(&od->pdev.dev), clk_alias);
+               return;
+       }
+       clkdev_add(l);
+ }
  /**
-  * _add_optional_clock_clkdev - Add clkdev entry for hwmod optional clocks
+  * _add_hwmod_clocks_clkdev - Add clkdev entry for hwmod optional clocks
+  * and main clock
   * @od: struct omap_device *od
+  * @oh: struct omap_hwmod *oh
   *
-  * For every optional clock present per hwmod per omap_device, this function
-  * adds an entry in the clkdev table of the form <dev-id=dev_name, con-id=role>
-  * if it does not exist already.
+  * For the main clock and every optional clock present per hwmod per
+  * omap_device, this function adds an entry in the clkdev table of the
+  * form <dev-id=dev_name, con-id=role> if it does not exist already.
   *
   * The function is called from inside omap_device_build_ss(), after
   * omap_device_register.
   *
   * This allows drivers to get a pointer to its optional clocks based on its role
   * by calling clk_get(<dev*>, <role>).
+  * In the case of the main clock, a "fck" alias is used.
   *
   * No return value.
   */
- static void _add_optional_clock_clkdev(struct omap_device *od,
-                                     struct omap_hwmod *oh)
+ static void _add_hwmod_clocks_clkdev(struct omap_device *od,
+                                    struct omap_hwmod *oh)
  {
        int i;
  
-       for (i = 0; i < oh->opt_clks_cnt; i++) {
-               struct omap_hwmod_opt_clk *oc;
-               struct clk *r;
-               struct clk_lookup *l;
-               oc = &oh->opt_clks[i];
-               if (!oc->_clk)
-                       continue;
-               r = clk_get_sys(dev_name(&od->pdev.dev), oc->role);
-               if (!IS_ERR(r))
-                       continue; /* clkdev entry exists */
+       _add_clkdev(od, "fck", oh->main_clk);
  
-               r = omap_clk_get_by_name((char *)oc->clk);
-               if (IS_ERR(r)) {
-                       pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
-                              dev_name(&od->pdev.dev), oc->clk);
-                       continue;
-               }
-               l = clkdev_alloc(r, oc->role, dev_name(&od->pdev.dev));
-               if (!l) {
-                       pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
-                              dev_name(&od->pdev.dev), oc->role);
-                       return;
-               }
-               clkdev_add(l);
-       }
+       for (i = 0; i < oh->opt_clks_cnt; i++)
+               _add_clkdev(od, oh->opt_clks[i].role, oh->opt_clks[i].clk);
  }
  
  
@@@ -492,7 -507,7 +507,7 @@@ struct omap_device *omap_device_build_s
  
        for (i = 0; i < oh_cnt; i++) {
                hwmods[i]->od = od;
-               _add_optional_clock_clkdev(od, hwmods[i]);
+               _add_hwmod_clocks_clkdev(od, hwmods[i]);
        }
  
        if (ret)
@@@ -532,7 -547,6 +547,7 @@@ int omap_early_device_register(struct o
        return 0;
  }
  
 +#ifdef CONFIG_PM_RUNTIME
  static int _od_runtime_suspend(struct device *dev)
  {
        struct platform_device *pdev = to_platform_device(dev);
@@@ -559,55 -573,13 +574,55 @@@ static int _od_runtime_resume(struct de
  
        return pm_generic_runtime_resume(dev);
  }
 +#endif
  
 -static struct dev_power_domain omap_device_power_domain = {
 +#ifdef CONFIG_SUSPEND
 +static int _od_suspend_noirq(struct device *dev)
 +{
 +      struct platform_device *pdev = to_platform_device(dev);
 +      struct omap_device *od = to_omap_device(pdev);
 +      int ret;
 +
 +      if (od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)
 +              return pm_generic_suspend_noirq(dev);
 +
 +      ret = pm_generic_suspend_noirq(dev);
 +
 +      if (!ret && !pm_runtime_status_suspended(dev)) {
 +              if (pm_generic_runtime_suspend(dev) == 0) {
 +                      omap_device_idle(pdev);
 +                      od->flags |= OMAP_DEVICE_SUSPENDED;
 +              }
 +      }
 +
 +      return ret;
 +}
 +
 +static int _od_resume_noirq(struct device *dev)
 +{
 +      struct platform_device *pdev = to_platform_device(dev);
 +      struct omap_device *od = to_omap_device(pdev);
 +
 +      if (od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)
 +              return pm_generic_resume_noirq(dev);
 +
 +      if ((od->flags & OMAP_DEVICE_SUSPENDED) &&
 +          !pm_runtime_status_suspended(dev)) {
 +              od->flags &= ~OMAP_DEVICE_SUSPENDED;
 +              omap_device_enable(pdev);
 +              pm_generic_runtime_resume(dev);
 +      }
 +
 +      return pm_generic_resume_noirq(dev);
 +}
 +#endif
 +
 +static struct dev_pm_domain omap_device_pm_domain = {
        .ops = {
 -              .runtime_suspend = _od_runtime_suspend,
 -              .runtime_idle = _od_runtime_idle,
 -              .runtime_resume = _od_runtime_resume,
 +              SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
 +                                 _od_runtime_idle)
                USE_PLATFORM_PM_SLEEP_OPS
 +              SET_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq, _od_resume_noirq)
        }
  };
  
@@@ -624,7 -596,7 +639,7 @@@ int omap_device_register(struct omap_de
        pr_debug("omap_device: %s: registering\n", od->pdev.name);
  
        od->pdev.dev.parent = &omap_device_parent;
 -      od->pdev.dev.pwr_domain = &omap_device_power_domain;
 +      od->pdev.dev.pm_domain = &omap_device_pm_domain;
        return platform_device_register(&od->pdev);
  }
  
diff --combined drivers/mfd/Kconfig
@@@ -218,7 -218,7 +218,7 @@@ config TWL4030_POWE
          and load scripts controlling which resources are switched off/on
          or reset when a sleep, wakeup or warm reset event occurs.
  
- config TWL4030_CODEC
+ config MFD_TWL4030_AUDIO
        bool
        depends on TWL4030_CORE
        select MFD_CORE
@@@ -233,6 -233,12 +233,12 @@@ config TWL6030_PW
          Say yes here if you want support for TWL6030 PWM.
          This is used to control charging LED brightness.
  
+ config TWL6040_CORE
+       bool
+       depends on TWL4030_CORE && GENERIC_HARDIRQS
+       select MFD_CORE
+       default n
  config MFD_STMPE
        bool "Support STMicroelectronics STMPE"
        depends on I2C=y && GENERIC_HARDIRQS
@@@ -728,9 -734,6 +734,9 @@@ config MFD_TPS6591
          if you say yes here you get support for the TPS65910 series of
          Power Management chips.
  
 +config TPS65911_COMPARATOR
 +      tristate
 +
  endif # MFD_SUPPORT
  
  menu "Multimedia Capabilities Port drivers"
diff --combined drivers/mfd/Makefile
@@@ -40,8 -40,9 +40,9 @@@ obj-$(CONFIG_MENELAUS)                += menelaus.
  obj-$(CONFIG_TWL4030_CORE)    += twl-core.o twl4030-irq.o twl6030-irq.o
  obj-$(CONFIG_TWL4030_MADC)      += twl4030-madc.o
  obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
- obj-$(CONFIG_TWL4030_CODEC)   += twl4030-codec.o
+ obj-$(CONFIG_MFD_TWL4030_AUDIO)       += twl4030-audio.o
  obj-$(CONFIG_TWL6030_PWM)     += twl6030-pwm.o
+ obj-$(CONFIG_TWL6040_CORE)    += twl6040-core.o twl6040-irq.o
  
  obj-$(CONFIG_MFD_MC13XXX)     += mc13xxx-core.o
  
@@@ -94,4 -95,3 +95,4 @@@ obj-$(CONFIG_MFD_OMAP_USB_HOST)       += omap
  obj-$(CONFIG_MFD_PM8921_CORE)         += pm8921-core.o
  obj-$(CONFIG_MFD_PM8XXX_IRQ)  += pm8xxx-irq.o
  obj-$(CONFIG_MFD_TPS65910)    += tps65910.o tps65910-irq.o
 +obj-$(CONFIG_TPS65911_COMPARATOR)     += tps65911-comparator.o
diff --combined sound/soc/codecs/Kconfig
@@@ -17,7 -17,6 +17,7 @@@ config SND_SOC_ALL_CODEC
        select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
        select SND_SOC_AD1980 if SND_SOC_AC97_BUS
        select SND_SOC_AD73311
 +      select SND_SOC_ADAV80X
        select SND_SOC_ADS117X
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
@@@ -43,7 -42,6 +43,7 @@@
        select SND_SOC_SN95031 if INTEL_SCU_IPC
        select SND_SOC_SPDIF
        select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
 +      select SND_SOC_STA32X if I2C
        select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
        select SND_SOC_TLV320AIC23 if I2C
        select SND_SOC_TLV320AIC26 if SPI_MASTER
@@@ -73,7 -71,6 +73,7 @@@
        select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8770 if SPI_MASTER
        select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
 +      select SND_SOC_WM8782
        select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8900 if I2C
        select SND_SOC_WM8903 if I2C
@@@ -87,7 -84,6 +87,7 @@@
        select SND_SOC_WM8971 if I2C
        select SND_SOC_WM8974 if I2C
        select SND_SOC_WM8978 if I2C
 +      select SND_SOC_WM8983 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8990 if I2C
@@@ -134,14 -130,7 +134,14 @@@ config SND_SOC_AD198
  
  config SND_SOC_AD73311
        tristate
 -      
 +
 +config SND_SOC_ADAU1701
 +      select SIGMA
 +      tristate
 +
 +config SND_SOC_ADAV80X
 +      tristate
 +
  config SND_SOC_ADS117X
        tristate
  
@@@ -227,9 -216,6 +227,9 @@@ config SND_SOC_SPDI
  config SND_SOC_SSM2602
        tristate
  
 +config SND_SOC_STA32X
 +      tristate
 +
  config SND_SOC_STAC9766
        tristate
  
@@@ -250,10 -236,11 +250,11 @@@ config SND_SOC_TLV320DAC3
        tristate
  
  config SND_SOC_TWL4030
-       select TWL4030_CODEC
+       select MFD_TWL4030_AUDIO
        tristate
  
  config SND_SOC_TWL6040
+       select TWL6040_CORE
        tristate
  
  config SND_SOC_UDA134X
@@@ -313,9 -300,6 +314,9 @@@ config SND_SOC_WM877
  config SND_SOC_WM8776
        tristate
  
 +config SND_SOC_WM8782
 +      tristate
 +
  config SND_SOC_WM8804
        tristate
  
@@@ -355,9 -339,6 +356,9 @@@ config SND_SOC_WM897
  config SND_SOC_WM8978
        tristate
  
 +config SND_SOC_WM8983
 +      tristate
 +
  config SND_SOC_WM8985
        tristate
  
  #include <linux/init.h>
  #include <linux/delay.h>
  #include <linux/pm.h>
- #include <linux/i2c.h>
- #include <linux/gpio.h>
  #include <linux/platform_device.h>
  #include <linux/slab.h>
  #include <linux/i2c/twl.h>
+ #include <linux/mfd/twl6040.h>
  
  #include <sound/core.h>
  #include <sound/pcm.h>
@@@ -77,14 -76,19 +76,19 @@@ struct twl6040_jack_data 
  
  /* codec private data */
  struct twl6040_data {
-       int audpwron;
-       int naudint;
+       int plug_irq;
        int codec_powered;
        int pll;
        int non_lp;
+       int pll_power_mode;
+       int hs_power_mode;
+       int hs_power_mode_locked;
+       unsigned int clk_in;
        unsigned int sysclk;
-       struct snd_pcm_hw_constraint_list *sysclk_constraints;
-       struct completion ready;
+       u16 hs_left_step;
+       u16 hs_right_step;
+       u16 hf_left_step;
+       u16 hf_right_step;
        struct twl6040_jack_data hs_jack;
        struct snd_soc_codec *codec;
        struct workqueue_struct *workqueue;
@@@ -206,6 -210,32 +210,32 @@@ static const int twl6040_vdd_reg[TWL604
        TWL6040_REG_DLB,
  };
  
+ /* set of rates for each pll: low-power and high-performance */
+ static unsigned int lp_rates[] = {
+       8000,
+       11250,
+       16000,
+       22500,
+       32000,
+       44100,
+       48000,
+       88200,
+       96000,
+ };
+ static unsigned int hp_rates[] = {
+       8000,
+       16000,
+       32000,
+       48000,
+       96000,
+ };
+ static struct snd_pcm_hw_constraint_list sysclk_constraints[] = {
+       { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, },
+       { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, },
+ };
  /*
   * read twl6040 register cache
   */
@@@ -239,12 -269,13 +269,13 @@@ static inline void twl6040_write_reg_ca
  static int twl6040_read_reg_volatile(struct snd_soc_codec *codec,
                        unsigned int reg)
  {
+       struct twl6040 *twl6040 = codec->control_data;
        u8 value;
  
        if (reg >= TWL6040_CACHEREGNUM)
                return -EIO;
  
-       twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg);
+       value = twl6040_reg_read(twl6040, reg);
        twl6040_write_reg_cache(codec, reg, value);
  
        return value;
  static int twl6040_write(struct snd_soc_codec *codec,
                        unsigned int reg, unsigned int value)
  {
+       struct twl6040 *twl6040 = codec->control_data;
        if (reg >= TWL6040_CACHEREGNUM)
                return -EIO;
  
        twl6040_write_reg_cache(codec, reg, value);
-       return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg);
+       return twl6040_reg_write(twl6040, reg, value);
  }
  
  static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
        u8 *cache = codec->reg_cache;
        int reg, i;
  
-       /* allow registers to be accessed by i2c */
-       twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]);
        for (i = 0; i < TWL6040_VIOREGNUM; i++) {
                reg = twl6040_vio_reg[i];
-               /* skip read-only registers (ASICID, ASICREV, STATUS) */
+               /*
+                * skip read-only registers (ASICID, ASICREV, STATUS)
+                * and registers shared among MFD children
+                */
                switch (reg) {
                case TWL6040_REG_ASICID:
                case TWL6040_REG_ASICREV:
+               case TWL6040_REG_INTID:
+               case TWL6040_REG_INTMR:
+               case TWL6040_REG_NCPCTL:
+               case TWL6040_REG_LDOCTL:
+               case TWL6040_REG_GPOCTL:
+               case TWL6040_REG_ACCCTL:
                case TWL6040_REG_STATUS:
                        continue;
                default:
@@@ -293,6 -332,20 +332,20 @@@ static void twl6040_init_vdd_regs(struc
  
        for (i = 0; i < TWL6040_VDDREGNUM; i++) {
                reg = twl6040_vdd_reg[i];
+               /* skip vibra and PLL registers */
+               switch (reg) {
+               case TWL6040_REG_VIBCTLL:
+               case TWL6040_REG_VIBDATL:
+               case TWL6040_REG_VIBCTLR:
+               case TWL6040_REG_VIBDATR:
+               case TWL6040_REG_HPPLLCTL:
+               case TWL6040_REG_LPPLLCTL:
+               case TWL6040_REG_LPPLLDIV:
+                       continue;
+               default:
+                       break;
+               }
                twl6040_write(codec, reg, cache[reg]);
        }
  }
@@@ -317,7 -370,11 +370,11 @@@ static inline int twl6040_hs_ramp_step(
        if (headset->ramp == TWL6040_RAMP_UP) {
                /* ramp step up */
                if (val < headset->left_vol) {
-                       val += left_step;
+                       if (val + left_step > headset->left_vol)
+                               val = headset->left_vol;
+                       else
+                               val += left_step;
                        reg &= ~TWL6040_HSL_VOL_MASK;
                        twl6040_write(codec, TWL6040_REG_HSGAIN,
                                        (reg | (~val & TWL6040_HSL_VOL_MASK)));
        } else if (headset->ramp == TWL6040_RAMP_DOWN) {
                /* ramp step down */
                if (val > 0x0) {
-                       val -= left_step;
+                       if ((int)val - (int)left_step < 0)
+                               val = 0;
+                       else
+                               val -= left_step;
                        reg &= ~TWL6040_HSL_VOL_MASK;
                        twl6040_write(codec, TWL6040_REG_HSGAIN, reg |
                                                (~val & TWL6040_HSL_VOL_MASK));
        if (headset->ramp == TWL6040_RAMP_UP) {
                /* ramp step up */
                if (val < headset->right_vol) {
-                       val += right_step;
+                       if (val + right_step > headset->right_vol)
+                               val = headset->right_vol;
+                       else
+                               val += right_step;
                        reg &= ~TWL6040_HSR_VOL_MASK;
                        twl6040_write(codec, TWL6040_REG_HSGAIN,
                                (reg | (~val << TWL6040_HSR_VOL_SHIFT)));
        } else if (headset->ramp == TWL6040_RAMP_DOWN) {
                /* ramp step down */
                if (val > 0x0) {
-                       val -= right_step;
+                       if ((int)val - (int)right_step < 0)
+                               val = 0;
+                       else
+                               val -= right_step;
                        reg &= ~TWL6040_HSR_VOL_MASK;
                        twl6040_write(codec, TWL6040_REG_HSGAIN,
                                         reg | (~val << TWL6040_HSR_VOL_SHIFT));
@@@ -385,7 -454,11 +454,11 @@@ static inline int twl6040_hf_ramp_step(
        if (handsfree->ramp == TWL6040_RAMP_UP) {
                /* ramp step up */
                if (val < handsfree->left_vol) {
-                       val += left_step;
+                       if (val + left_step > handsfree->left_vol)
+                               val = handsfree->left_vol;
+                       else
+                               val += left_step;
                        reg &= ~TWL6040_HF_VOL_MASK;
                        twl6040_write(codec, TWL6040_REG_HFLGAIN,
                                                reg | (0x1D - val));
        } else if (handsfree->ramp == TWL6040_RAMP_DOWN) {
                /* ramp step down */
                if (val > 0) {
-                       val -= left_step;
+                       if ((int)val - (int)left_step < 0)
+                               val = 0;
+                       else
+                               val -= left_step;
                        reg &= ~TWL6040_HF_VOL_MASK;
                        twl6040_write(codec, TWL6040_REG_HFLGAIN,
                                                reg | (0x1D - val));
        if (handsfree->ramp == TWL6040_RAMP_UP) {
                /* ramp step up */
                if (val < handsfree->right_vol) {
-                       val += right_step;
+                       if (val + right_step > handsfree->right_vol)
+                               val = handsfree->right_vol;
+                       else
+                               val += right_step;
                        reg &= ~TWL6040_HF_VOL_MASK;
                        twl6040_write(codec, TWL6040_REG_HFRGAIN,
                                                reg | (0x1D - val));
        } else if (handsfree->ramp == TWL6040_RAMP_DOWN) {
                /* ramp step down */
                if (val > 0) {
-                       val -= right_step;
+                       if ((int)val - (int)right_step < 0)
+                               val = 0;
+                       else
+                               val -= right_step;
                        reg &= ~TWL6040_HF_VOL_MASK;
                        twl6040_write(codec, TWL6040_REG_HFRGAIN,
                                                reg | (0x1D - val));
@@@ -451,11 -536,9 +536,9 @@@ static void twl6040_pga_hs_work(struct 
  
        /* HS PGA volumes have 4 bits of resolution to ramp */
        for (i = 0; i <= 16; i++) {
-               headset_complete = 1;
-               if (headset->ramp != TWL6040_RAMP_NONE)
-                       headset_complete = twl6040_hs_ramp_step(codec,
-                                                       headset->left_step,
-                                                       headset->right_step);
+               headset_complete = twl6040_hs_ramp_step(codec,
+                                               headset->left_step,
+                                               headset->right_step);
  
                /* ramp finished ? */
                if (headset_complete)
@@@ -496,11 -579,9 +579,9 @@@ static void twl6040_pga_hf_work(struct 
  
        /* HF PGA volumes have 5 bits of resolution to ramp */
        for (i = 0; i <= 32; i++) {
-               handsfree_complete = 1;
-               if (handsfree->ramp != TWL6040_RAMP_NONE)
-                       handsfree_complete = twl6040_hf_ramp_step(codec,
-                                                       handsfree->left_step,
-                                                       handsfree->right_step);
+               handsfree_complete = twl6040_hf_ramp_step(codec,
+                                               handsfree->left_step,
+                                               handsfree->right_step);
  
                /* ramp finished ? */
                if (handsfree_complete)
@@@ -541,12 -622,16 +622,16 @@@ static int pga_event(struct snd_soc_dap
                out = &priv->headset;
                work = &priv->hs_delayed_work;
                queue = priv->hs_workqueue;
+               out->left_step = priv->hs_left_step;
+               out->right_step = priv->hs_right_step;
                out->step_delay = 5;    /* 5 ms between volume ramp steps */
                break;
        case 4:
                out = &priv->handsfree;
                work = &priv->hf_delayed_work;
                queue = priv->hf_workqueue;
+               out->left_step = priv->hf_left_step;
+               out->right_step = priv->hf_right_step;
                out->step_delay = 5;    /* 5 ms between volume ramp steps */
                if (SND_SOC_DAPM_EVENT_ON(event))
                        priv->non_lp++;
  
                if (!delayed_work_pending(work)) {
                        /* use volume ramp for power-down */
-                       out->left_step = 1;
-                       out->right_step = 1;
                        out->ramp = TWL6040_RAMP_DOWN;
                        INIT_COMPLETION(out->ramp_done);
  
        return 0;
  }
  
- /* twl6040 codec manual power-up sequence */
- static void twl6040_power_up(struct snd_soc_codec *codec)
- {
-       u8 ncpctl, ldoctl, lppllctl, accctl;
-       ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL);
-       ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL);
-       lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
-       accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL);
-       /* enable reference system */
-       ldoctl |= TWL6040_REFENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       msleep(10);
-       /* enable internal oscillator */
-       ldoctl |= TWL6040_OSCENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       udelay(10);
-       /* enable high-side ldo */
-       ldoctl |= TWL6040_HSLDOENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       udelay(244);
-       /* enable negative charge pump */
-       ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN;
-       twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl);
-       udelay(488);
-       /* enable low-side ldo */
-       ldoctl |= TWL6040_LSLDOENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       udelay(244);
-       /* enable low-power pll */
-       lppllctl |= TWL6040_LPLLENA;
-       twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-       /* reset state machine */
-       accctl |= TWL6040_RESETSPLIT;
-       twl6040_write(codec, TWL6040_REG_ACCCTL, accctl);
-       mdelay(5);
-       accctl &= ~TWL6040_RESETSPLIT;
-       twl6040_write(codec, TWL6040_REG_ACCCTL, accctl);
-       /* disable internal oscillator */
-       ldoctl &= ~TWL6040_OSCENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
- }
- /* twl6040 codec manual power-down sequence */
- static void twl6040_power_down(struct snd_soc_codec *codec)
- {
-       u8 ncpctl, ldoctl, lppllctl, accctl;
-       ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL);
-       ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL);
-       lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
-       accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL);
-       /* enable internal oscillator */
-       ldoctl |= TWL6040_OSCENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       udelay(10);
-       /* disable low-power pll */
-       lppllctl &= ~TWL6040_LPLLENA;
-       twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-       /* disable low-side ldo */
-       ldoctl &= ~TWL6040_LSLDOENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       udelay(244);
-       /* disable negative charge pump */
-       ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN);
-       twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl);
-       udelay(488);
-       /* disable high-side ldo */
-       ldoctl &= ~TWL6040_HSLDOENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       udelay(244);
-       /* disable internal oscillator */
-       ldoctl &= ~TWL6040_OSCENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       /* disable reference system */
-       ldoctl &= ~TWL6040_REFENA;
-       twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl);
-       msleep(10);
- }
  /* set headset dac and driver power mode */
  static int headset_power_mode(struct snd_soc_codec *codec, int high_perf)
  {
@@@ -713,15 -714,26 +714,26 @@@ static int twl6040_power_mode_event(str
  {
        struct snd_soc_codec *codec = w->codec;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
  
-       if (SND_SOC_DAPM_EVENT_ON(event))
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
                priv->non_lp++;
-       else
+               if (!strcmp(w->name, "Earphone Driver")) {
+                       /* Earphone doesn't support low power mode */
+                       priv->hs_power_mode_locked = 1;
+                       ret = headset_power_mode(codec, 1);
+               }
+       } else {
                priv->non_lp--;
+               if (!strcmp(w->name, "Earphone Driver")) {
+                       priv->hs_power_mode_locked = 0;
+                       ret = headset_power_mode(codec, priv->hs_power_mode);
+               }
+       }
  
        msleep(1);
  
-       return 0;
+       return ret;
  }
  
  static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
@@@ -766,33 -778,19 +778,19 @@@ static void twl6040_accessory_work(stru
  }
  
  /* audio interrupt handler */
- static irqreturn_t twl6040_naudint_handler(int irq, void *data)
+ static irqreturn_t twl6040_audio_handler(int irq, void *data)
  {
        struct snd_soc_codec *codec = data;
+       struct twl6040 *twl6040 = codec->control_data;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
        u8 intid;
  
-       twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID);
-       if (intid & TWL6040_THINT)
-               dev_alert(codec->dev, "die temp over-limit detection\n");
+       intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
  
        if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT))
                queue_delayed_work(priv->workqueue, &priv->delayed_work,
                                                        msecs_to_jiffies(200));
  
-       if (intid & TWL6040_HOOKINT)
-               dev_info(codec->dev, "hook detection\n");
-       if (intid & TWL6040_HFINT)
-               dev_alert(codec->dev, "hf drivers over current detection\n");
-       if (intid & TWL6040_VIBINT)
-               dev_alert(codec->dev, "vib drivers over current detection\n");
-       if (intid & TWL6040_READYINT)
-               complete(&priv->ready);
        return IRQ_HANDLED;
  }
  
@@@ -954,9 -952,9 +952,9 @@@ static DECLARE_TLV_DB_SCALE(mic_preamp_
  
  /*
   * MICGAIN volume control:
 - * from -6 to 30 dB in 6 dB steps
 + * from 6 to 30 dB in 6 dB steps
   */
 -static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -600, 600, 0);
 +static DECLARE_TLV_DB_SCALE(mic_amp_tlv, 600, 600, 0);
  
  /*
   * AFMGAIN volume control:
@@@ -1040,6 -1038,73 +1038,73 @@@ static const struct snd_kcontrol_new hf
  static const struct snd_kcontrol_new ep_driver_switch_controls =
        SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0);
  
+ /* Headset power mode */
+ static const char *twl6040_power_mode_texts[] = {
+       "Low-Power", "High-Perfomance",
+ };
+ static const struct soc_enum twl6040_power_mode_enum =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_power_mode_texts),
+                       twl6040_power_mode_texts);
+ static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+ {
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       ucontrol->value.enumerated.item[0] = priv->hs_power_mode;
+       return 0;
+ }
+ static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+ {
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       int high_perf = ucontrol->value.enumerated.item[0];
+       int ret = 0;
+       if (!priv->hs_power_mode_locked)
+               ret = headset_power_mode(codec, high_perf);
+       if (!ret)
+               priv->hs_power_mode = high_perf;
+       return ret;
+ }
+ static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+ {
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       ucontrol->value.enumerated.item[0] = priv->pll_power_mode;
+       return 0;
+ }
+ static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+ {
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       priv->pll_power_mode = ucontrol->value.enumerated.item[0];
+       return 0;
+ }
+ int twl6040_get_clk_id(struct snd_soc_codec *codec)
+ {
+       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       return priv->pll_power_mode;
+ }
+ EXPORT_SYMBOL_GPL(twl6040_get_clk_id);
  static const struct snd_kcontrol_new twl6040_snd_controls[] = {
        /* Capture gains */
        SOC_DOUBLE_TLV("Capture Preamplifier Volume",
                TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
        SOC_SINGLE_TLV("Earphone Playback Volume",
                TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
+       SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum,
+               twl6040_headset_power_get_enum,
+               twl6040_headset_power_put_enum),
+       SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum,
+               twl6040_pll_get_enum, twl6040_pll_put_enum),
  };
  
  static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
@@@ -1231,36 -1303,11 +1303,11 @@@ static int twl6040_add_widgets(struct s
        return 0;
  }
  
- static int twl6040_power_up_completion(struct snd_soc_codec *codec,
-                                       int naudint)
- {
-       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-       int time_left;
-       u8 intid;
-       time_left = wait_for_completion_timeout(&priv->ready,
-                               msecs_to_jiffies(144));
-       if (!time_left) {
-               twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid,
-                                                       TWL6040_REG_INTID);
-               if (!(intid & TWL6040_READYINT)) {
-                       dev_err(codec->dev, "timeout waiting for READYINT\n");
-                       return -ETIMEDOUT;
-               }
-       }
-       priv->codec_powered = 1;
-       return 0;
- }
  static int twl6040_set_bias_level(struct snd_soc_codec *codec,
                                enum snd_soc_bias_level level)
  {
+       struct twl6040 *twl6040 = codec->control_data;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-       int audpwron = priv->audpwron;
-       int naudint = priv->naudint;
        int ret;
  
        switch (level) {
                if (priv->codec_powered)
                        break;
  
-               if (gpio_is_valid(audpwron)) {
-                       /* use AUDPWRON line */
-                       gpio_set_value(audpwron, 1);
+               ret = twl6040_power(twl6040, 1);
+               if (ret)
+                       return ret;
  
-                       /* wait for power-up completion */
-                       ret = twl6040_power_up_completion(codec, naudint);
-                       if (ret)
-                               return ret;
-                       /* sync registers updated during power-up sequence */
-                       twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
-                       twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
-                       twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL);
-               } else {
-                       /* use manual power-up sequence */
-                       twl6040_power_up(codec);
-                       priv->codec_powered = 1;
-               }
+               priv->codec_powered = 1;
  
                /* initialize vdd/vss registers with reg_cache */
                twl6040_init_vdd_regs(codec);
  
                /* Set external boost GPO */
                twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02);
-               /* Set initial minimal gain values */
-               twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF);
-               twl6040_write(codec, TWL6040_REG_EARCTL, 0x1E);
-               twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D);
-               twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D);
                break;
        case SND_SOC_BIAS_OFF:
                if (!priv->codec_powered)
                        break;
  
-               if (gpio_is_valid(audpwron)) {
-                       /* use AUDPWRON line */
-                       gpio_set_value(audpwron, 0);
-                       /* power-down sequence latency */
-                       udelay(500);
-                       /* sync registers updated during power-down sequence */
-                       twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL);
-                       twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL);
-                       twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL,
-                                               0x00);
-               } else {
-                       /* use manual power-down sequence */
-                       twl6040_power_down(codec);
-               }
+               twl6040_power(twl6040, 0);
                priv->codec_powered = 0;
                break;
        }
        return 0;
  }
  
- /* set of rates for each pll: low-power and high-performance */
- static unsigned int lp_rates[] = {
-       88200,
-       96000,
- };
- static struct snd_pcm_hw_constraint_list lp_constraints = {
-       .count  = ARRAY_SIZE(lp_rates),
-       .list   = lp_rates,
- };
- static unsigned int hp_rates[] = {
-       96000,
- };
- static struct snd_pcm_hw_constraint_list hp_constraints = {
-       .count  = ARRAY_SIZE(hp_rates),
-       .list   = hp_rates,
- };
  static int twl6040_startup(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
  {
  
        snd_pcm_hw_constraint_list(substream->runtime, 0,
                                SNDRV_PCM_HW_PARAM_RATE,
-                               priv->sysclk_constraints);
+                               &sysclk_constraints[priv->pll_power_mode]);
  
        return 0;
  }
@@@ -1375,22 -1366,27 +1366,27 @@@ static int twl6040_hw_params(struct snd
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->codec;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-       u8 lppllctl;
        int rate;
  
-       /* nothing to do for high-perf pll, it supports only 48 kHz */
-       if (priv->pll == TWL6040_HPPLL_ID)
-               return 0;
-       lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
        rate = params_rate(params);
        switch (rate) {
        case 11250:
        case 22500:
        case 44100:
        case 88200:
-               lppllctl |= TWL6040_LPLLFIN;
+               /* These rates are not supported when HPPLL is in use */
+               if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) {
+                       dev_err(codec->dev, "HPPLL does not support rate %d\n",
+                               rate);
+                       return -EINVAL;
+               }
+               /* Capture is not supported with 17.64MHz sysclk */
+               if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+                       dev_err(codec->dev,
+                               "capture mode is not supported at %dHz\n",
+                               rate);
+                       return -EINVAL;
+               }
                priv->sysclk = 17640000;
                break;
        case 8000:
        case 32000:
        case 48000:
        case 96000:
-               lppllctl &= ~TWL6040_LPLLFIN;
                priv->sysclk = 19200000;
                break;
        default:
                return -EINVAL;
        }
  
-       twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
        return 0;
  }
  
@@@ -1416,7 -1409,9 +1409,9 @@@ static int twl6040_prepare(struct snd_p
  {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->codec;
+       struct twl6040 *twl6040 = codec->control_data;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
+       int ret;
  
        if (!priv->sysclk) {
                dev_err(codec->dev,
                return -EINVAL;
        }
  
-       /*
-        * capture is not supported at 17.64 MHz,
-        * it's reserved for headset low-power playback scenario
-        */
-       if ((priv->sysclk == 17640000) &&
-                       substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               dev_err(codec->dev,
-                       "capture mode is not supported at %dHz\n",
-                       priv->sysclk);
-               return -EINVAL;
-       }
        if ((priv->sysclk == 17640000) && priv->non_lp) {
                        dev_err(codec->dev,
                                "some enabled paths aren't supported at %dHz\n",
                                priv->sysclk);
                        return -EPERM;
        }
+       ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk);
+       if (ret) {
+               dev_err(codec->dev, "Can not set PLL (%d)\n", ret);
+               return -EPERM;
+       }
        return 0;
  }
  
@@@ -1450,99 -1440,12 +1440,12 @@@ static int twl6040_set_dai_sysclk(struc
  {
        struct snd_soc_codec *codec = codec_dai->codec;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-       u8 hppllctl, lppllctl;
-       hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL);
-       lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL);
  
        switch (clk_id) {
        case TWL6040_SYSCLK_SEL_LPPLL:
-               switch (freq) {
-               case 32768:
-                       /* headset dac and driver must be in low-power mode */
-                       headset_power_mode(codec, 0);
-                       /* clk32k input requires low-power pll */
-                       lppllctl |= TWL6040_LPLLENA;
-                       twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-                       mdelay(5);
-                       lppllctl &= ~TWL6040_HPLLSEL;
-                       twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-                       hppllctl &= ~TWL6040_HPLLENA;
-                       twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl);
-                       break;
-               default:
-                       dev_err(codec->dev, "unknown mclk freq %d\n", freq);
-                       return -EINVAL;
-               }
-               /* lppll divider */
-               switch (priv->sysclk) {
-               case 17640000:
-                       lppllctl |= TWL6040_LPLLFIN;
-                       break;
-               case 19200000:
-                       lppllctl &= ~TWL6040_LPLLFIN;
-                       break;
-               default:
-                       /* sysclk not yet configured */
-                       lppllctl &= ~TWL6040_LPLLFIN;
-                       priv->sysclk = 19200000;
-                       break;
-               }
-               twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-               priv->pll = TWL6040_LPPLL_ID;
-               priv->sysclk_constraints = &lp_constraints;
-               break;
        case TWL6040_SYSCLK_SEL_HPPLL:
-               hppllctl &= ~TWL6040_MCLK_MSK;
-               switch (freq) {
-               case 12000000:
-                       /* mclk input, pll enabled */
-                       hppllctl |= TWL6040_MCLK_12000KHZ |
-                                   TWL6040_HPLLSQRBP |
-                                   TWL6040_HPLLENA;
-                       break;
-               case 19200000:
-                       /* mclk input, pll disabled */
-                       hppllctl |= TWL6040_MCLK_19200KHZ |
-                                   TWL6040_HPLLSQRENA |
-                                   TWL6040_HPLLBP;
-                       break;
-               case 26000000:
-                       /* mclk input, pll enabled */
-                       hppllctl |= TWL6040_MCLK_26000KHZ |
-                                   TWL6040_HPLLSQRBP |
-                                   TWL6040_HPLLENA;
-                       break;
-               case 38400000:
-                       /* clk slicer, pll disabled */
-                       hppllctl |= TWL6040_MCLK_38400KHZ |
-                                   TWL6040_HPLLSQRENA |
-                                   TWL6040_HPLLBP;
-                       break;
-               default:
-                       dev_err(codec->dev, "unknown mclk freq %d\n", freq);
-                       return -EINVAL;
-               }
-               /* headset dac and driver must be in high-performance mode */
-               headset_power_mode(codec, 1);
-               twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl);
-               udelay(500);
-               lppllctl |= TWL6040_HPLLSEL;
-               twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-               lppllctl &= ~TWL6040_LPLLENA;
-               twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl);
-               /* high-performance pll can provide only 19.2 MHz */
-               priv->pll = TWL6040_HPPLL_ID;
-               priv->sysclk = 19200000;
-               priv->sysclk_constraints = &hp_constraints;
+               priv->pll = clk_id;
+               priv->clk_in = freq;
                break;
        default:
                dev_err(codec->dev, "unknown clk_id %d\n", clk_id);
@@@ -1559,15 -1462,27 +1462,27 @@@ static struct snd_soc_dai_ops twl6040_d
        .set_sysclk     = twl6040_set_dai_sysclk,
  };
  
- static struct snd_soc_dai_driver twl6040_dai = {
+ static struct snd_soc_dai_driver twl6040_dai[] = {
+ {
        .name = "twl6040-hifi",
        .playback = {
                .stream_name = "Playback",
                .channels_min = 1,
-               .channels_max = 4,
+               .channels_max = 2,
+               .rates = TWL6040_RATES,
+               .formats = TWL6040_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
                .rates = TWL6040_RATES,
                .formats = TWL6040_FORMATS,
        },
+       .ops = &twl6040_dai_ops,
+ },
+ {
+       .name = "twl6040-ul",
        .capture = {
                .stream_name = "Capture",
                .channels_min = 1,
                .formats = TWL6040_FORMATS,
        },
        .ops = &twl6040_dai_ops,
+ },
+ {
+       .name = "twl6040-dl1",
+       .playback = {
+               .stream_name = "Headset Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = TWL6040_RATES,
+               .formats = TWL6040_FORMATS,
+       },
+       .ops = &twl6040_dai_ops,
+ },
+ {
+       .name = "twl6040-dl2",
+       .playback = {
+               .stream_name = "Handsfree Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = TWL6040_RATES,
+               .formats = TWL6040_FORMATS,
+       },
+       .ops = &twl6040_dai_ops,
+ },
+ {
+       .name = "twl6040-vib",
+       .playback = {
+               .stream_name = "Vibra Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+               .formats = TWL6040_FORMATS,
+       },
+       .ops = &twl6040_dai_ops,
+ },
  };
  
  #ifdef CONFIG_PM
@@@ -1600,11 -1549,11 +1549,11 @@@ static int twl6040_resume(struct snd_so
  
  static int twl6040_probe(struct snd_soc_codec *codec)
  {
-       struct twl4030_codec_data *twl_codec = codec->dev->platform_data;
        struct twl6040_data *priv;
-       int audpwron, naudint;
+       struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
+       struct platform_device *pdev = container_of(codec->dev,
+                                                  struct platform_device, dev);
        int ret = 0;
-       u8 icrev, intmr = TWL6040_ALLINT_MSK;
  
        priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL);
        if (priv == NULL)
        snd_soc_codec_set_drvdata(codec, priv);
  
        priv->codec = codec;
+       codec->control_data = dev_get_drvdata(codec->dev->parent);
  
-       twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &icrev, TWL6040_REG_ASICREV);
+       if (pdata && pdata->hs_left_step && pdata->hs_right_step) {
+               priv->hs_left_step = pdata->hs_left_step;
+               priv->hs_right_step = pdata->hs_right_step;
+       } else {
+               priv->hs_left_step = 1;
+               priv->hs_right_step = 1;
+       }
  
-       if (twl_codec && (icrev > 0))
-               audpwron = twl_codec->audpwron_gpio;
-       else
-               audpwron = -EINVAL;
+       if (pdata && pdata->hf_left_step && pdata->hf_right_step) {
+               priv->hf_left_step = pdata->hf_left_step;
+               priv->hf_right_step = pdata->hf_right_step;
+       } else {
+               priv->hf_left_step = 1;
+               priv->hf_right_step = 1;
+       }
  
-       if (twl_codec)
-               naudint = twl_codec->naudint_irq;
-       else
-               naudint = 0;
+       priv->plug_irq = platform_get_irq(pdev, 0);
+       if (priv->plug_irq < 0) {
+               dev_err(codec->dev, "invalid irq\n");
+               ret = -EINVAL;
+               goto work_err;
+       }
  
-       priv->audpwron = audpwron;
-       priv->naudint = naudint;
        priv->workqueue = create_singlethread_workqueue("twl6040-codec");
        if (!priv->workqueue) {
                ret = -ENOMEM;
                goto work_err;
  
        mutex_init(&priv->mutex);
  
-       init_completion(&priv->ready);
        init_completion(&priv->headset.ramp_done);
        init_completion(&priv->handsfree.ramp_done);
  
-       if (gpio_is_valid(audpwron)) {
-               ret = gpio_request(audpwron, "audpwron");
-               if (ret)
-                       goto gpio1_err;
-               ret = gpio_direction_output(audpwron, 0);
-               if (ret)
-                       goto gpio2_err;
-               priv->codec_powered = 0;
-               /* enable only codec ready interrupt */
-               intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK);
-               /* reset interrupt status to allow correct power up sequence */
-               twl6040_read_reg_volatile(codec, TWL6040_REG_INTID);
-       }
-       twl6040_write(codec, TWL6040_REG_INTMR, intmr);
-       if (naudint) {
-               /* audio interrupt */
-               ret = request_threaded_irq(naudint, NULL,
-                               twl6040_naudint_handler,
-                               IRQF_TRIGGER_LOW | IRQF_ONESHOT,
-                               "twl6040_codec", codec);
-               if (ret)
-                       goto gpio2_err;
-       }
-       /* init vio registers */
-       twl6040_init_vio_regs(codec);
        priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf");
        if (priv->hf_workqueue == NULL) {
                ret = -ENOMEM;
-               goto irq_err;
+               goto hfwq_err;
        }
        priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs");
        if (priv->hs_workqueue == NULL) {
                ret = -ENOMEM;
-               goto wq_err;
+               goto hswq_err;
        }
  
        INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work);
        INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work);
  
+       ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler,
+                                  0, "twl6040_irq_plug", codec);
+       if (ret) {
+               dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
+               goto plugirq_err;
+       }
+       /* init vio registers */
+       twl6040_init_vio_regs(codec);
        /* power on device */
        ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        if (ret)
        return 0;
  
  bias_err:
+       free_irq(priv->plug_irq, codec);
+ plugirq_err:
        destroy_workqueue(priv->hs_workqueue);
- wq_err:
hswq_err:
        destroy_workqueue(priv->hf_workqueue);
- irq_err:
-       if (naudint)
-               free_irq(naudint, codec);
- gpio2_err:
-       if (gpio_is_valid(audpwron))
-               gpio_free(audpwron);
- gpio1_err:
+ hfwq_err:
        destroy_workqueue(priv->workqueue);
  work_err:
        kfree(priv);
  static int twl6040_remove(struct snd_soc_codec *codec)
  {
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-       int audpwron = priv->audpwron;
-       int naudint = priv->naudint;
  
        twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       if (gpio_is_valid(audpwron))
-               gpio_free(audpwron);
-       if (naudint)
-               free_irq(naudint, codec);
+       free_irq(priv->plug_irq, codec);
        destroy_workqueue(priv->workqueue);
        destroy_workqueue(priv->hf_workqueue);
        destroy_workqueue(priv->hs_workqueue);
@@@ -1753,8 -1676,8 +1676,8 @@@ static struct snd_soc_codec_driver soc_
  
  static int __devinit twl6040_codec_probe(struct platform_device *pdev)
  {
-       return snd_soc_register_codec(&pdev->dev,
-                       &soc_codec_dev_twl6040, &twl6040_dai, 1);
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040,
+                                     twl6040_dai, ARRAY_SIZE(twl6040_dai));
  }
  
  static int __devexit twl6040_codec_remove(struct platform_device *pdev)