ALSA: HDA: Don't power up hda codec from resume
[linux-2.6.git] / sound / pci / hda / hda_intel.c
index be69822..2407913 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/pci.h>
 #include <linux/mutex.h>
 #include <linux/reboot.h>
+#include <linux/clk.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include "hda_codec.h"
@@ -321,6 +322,32 @@ enum {
 #define NVIDIA_HDA_OSTRM_COH          0x4c
 #define NVIDIA_HDA_ENABLE_COHBIT      0x01
 
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+/* Defines for Nvidia Tegra HDA support */
+#define NVIDIA_TEGRA_HDA_BAR0_OFFSET           0x8000
+
+#define NVIDIA_TEGRA_HDA_CFG_CMD_OFFSET        0x1004
+#define NVIDIA_TEGRA_HDA_CFG_BAR0_OFFSET       0x1010
+
+#define NVIDIA_TEGRA_HDA_ENABLE_IO_SPACE       (1 << 0)
+#define NVIDIA_TEGRA_HDA_ENABLE_MEM_SPACE      (1 << 1)
+#define NVIDIA_TEGRA_HDA_ENABLE_BUS_MASTER     (1 << 2)
+#define NVIDIA_TEGRA_HDA_ENABLE_SERR           (1 << 8)
+#define NVIDIA_TEGRA_HDA_DISABLE_INTR          (1 << 10)
+#define NVIDIA_TEGRA_HDA_BAR0_INIT_PROGRAM     0xFFFFFFFF
+#define NVIDIA_TEGRA_HDA_BAR0_FINAL_PROGRAM    (1 << 14)
+
+/* IPFS */
+#define NVIDIA_TEGRA_HDA_IPFS_CONFIG           0x180
+#define NVIDIA_TEGRA_HDA_IPFS_EN_FPCI          0x1
+
+#define NVIDIA_TEGRA_HDA_IPFS_FPCI_BAR0        0x80
+#define NVIDIA_TEGRA_HDA_FPCI_BAR0_START       0x40
+
+#define NVIDIA_TEGRA_HDA_IPFS_INTR_MASK        0x188
+#define NVIDIA_TEGRA_HDA_IPFS_EN_INTR          (1 << 16)
+#endif /* CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA */
+
 /* Defines for Intel SCH HDA snoop control */
 #define INTEL_SCH_HDA_DEVC      0x78
 #define INTEL_SCH_HDA_DEVC_NOSNOOP       (0x1<<11)
@@ -388,6 +415,9 @@ struct azx_rb {
 struct azx {
        struct snd_card *card;
        struct pci_dev *pci;
+       struct platform_device *pdev;
+       struct device *dev;
+       int irq_id;
        int dev_index;
 
        /* chip type specific */
@@ -402,8 +432,18 @@ struct azx {
        /* pci resources */
        unsigned long addr;
        void __iomem *remap_addr;
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+       void __iomem *remap_config_addr;
+#endif
        int irq;
 
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+       /* platform driver clocks */
+       struct clk **platform_clks;
+       int platform_clk_count;
+       int platform_clk_enable;
+#endif
+
        /* locks */
        spinlock_t reg_lock;
        struct mutex open_mutex;
@@ -460,6 +500,7 @@ enum {
        AZX_DRIVER_SIS,
        AZX_DRIVER_ULI,
        AZX_DRIVER_NVIDIA,
+       AZX_DRIVER_NVIDIA_TEGRA,
        AZX_DRIVER_TERA,
        AZX_DRIVER_CTX,
        AZX_DRIVER_GENERIC,
@@ -505,6 +546,7 @@ static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_SIS] = "HDA SIS966",
        [AZX_DRIVER_ULI] = "HDA ULI M5461",
        [AZX_DRIVER_NVIDIA] = "HDA NVidia",
+       [AZX_DRIVER_NVIDIA_TEGRA] = "HDA NVIDIA Tegra",
        [AZX_DRIVER_TERA] = "HDA Teradici", 
        [AZX_DRIVER_CTX] = "HDA Creative", 
        [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
@@ -513,6 +555,48 @@ static char *driver_short_names[] __devinitdata = {
 /*
  * macros for easy use
  */
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+#define MASK_LONG_ALIGN                0x3UL
+#define SHIFT_BYTE             3
+#define SHIFT_BITS(reg)                ((reg & MASK_LONG_ALIGN) << SHIFT_BYTE)
+#define ADDR_ALIGN_L(base, reg)        (base + (reg & ~MASK_LONG_ALIGN))
+#define MASK(bits)             (BIT(bits) - 1)
+#define MASK_REG(reg, bits)    (MASK(bits) << SHIFT_BITS(reg))
+
+#define tegra_write(base, reg, val, bits) \
+       writel((readl(ADDR_ALIGN_L(base, reg)) & ~MASK_REG(reg, bits)) | \
+              ((val) << SHIFT_BITS(reg)), ADDR_ALIGN_L(base, reg))
+
+#define tegra_read(base, reg, bits) \
+       ((readl(ADDR_ALIGN_L(base, reg)) >> SHIFT_BITS(reg)) & MASK(bits))
+
+#define azx_writel(chip, reg, value) \
+       writel(value, (chip)->remap_addr + ICH6_REG_##reg)
+#define azx_readl(chip, reg) \
+       readl((chip)->remap_addr + ICH6_REG_##reg)
+#define azx_writew(chip, reg, value) \
+       tegra_write((chip)->remap_addr, ICH6_REG_##reg, value, 16)
+#define azx_readw(chip, reg) \
+       tegra_read((chip)->remap_addr, ICH6_REG_##reg, 16)
+#define azx_writeb(chip, reg, value) \
+       tegra_write((chip)->remap_addr, ICH6_REG_##reg, value, 8)
+#define azx_readb(chip, reg) \
+       tegra_read((chip)->remap_addr, ICH6_REG_##reg, 8)
+
+#define azx_sd_writel(dev, reg, value) \
+       writel(value, (dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_readl(dev, reg) \
+       readl((dev)->sd_addr + ICH6_REG_##reg)
+#define azx_sd_writew(dev, reg, value) \
+       tegra_write((dev)->sd_addr, ICH6_REG_##reg, value, 16)
+#define azx_sd_readw(dev, reg) \
+       tegra_read((dev)->sd_addr, ICH6_REG_##reg, 16)
+#define azx_sd_writeb(dev, reg, value) \
+       tegra_write((dev)->sd_addr, ICH6_REG_##reg, value, 8)
+#define azx_sd_readb(dev, reg) \
+       tegra_read((dev)->sd_addr, ICH6_REG_##reg, 8)
+
+#else /* CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA */
 #define azx_writel(chip,reg,value) \
        writel(value, (chip)->remap_addr + ICH6_REG_##reg)
 #define azx_readl(chip,reg) \
@@ -539,6 +623,8 @@ static char *driver_short_names[] __devinitdata = {
 #define azx_sd_readb(dev,reg) \
        readb((dev)->sd_addr + ICH6_REG_##reg)
 
+#endif /* CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA */
+
 /* for pcm support */
 #define get_azx_dev(substream) (substream->runtime->private_data)
 
@@ -557,7 +643,7 @@ static int azx_alloc_cmd_io(struct azx *chip)
 
        /* single page (at least 4096 bytes) must suffice for both ringbuffes */
        err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
-                                 snd_dma_pci_data(chip->pci),
+                                 chip->dev,
                                  PAGE_SIZE, &chip->rb);
        if (err < 0) {
                snd_printk(KERN_ERR SFX "cannot allocate CORB/RIRB\n");
@@ -1131,6 +1217,83 @@ static void azx_init_pci(struct azx *chip)
         }
 }
 
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+/*
+ * initialize the platform specific registers
+ */
+static void reg_update_bits(void __iomem *base, unsigned int reg,
+                           unsigned int mask, unsigned int val)
+{
+       unsigned int data;
+
+       data = readl(base + reg);
+       data &= ~mask;
+       data |= (val & mask);
+       writel(data, base + reg);
+}
+
+static void azx_init_platform(struct azx *chip)
+{
+       switch (chip->driver_type) {
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+       case AZX_DRIVER_NVIDIA_TEGRA:
+               /*Enable the PCI access */
+               reg_update_bits(chip->remap_config_addr,
+                               NVIDIA_TEGRA_HDA_IPFS_CONFIG,
+                               NVIDIA_TEGRA_HDA_IPFS_EN_FPCI,
+                               NVIDIA_TEGRA_HDA_IPFS_EN_FPCI);
+               /* Enable MEM/IO space and bus master */
+               reg_update_bits(chip->remap_config_addr,
+                               NVIDIA_TEGRA_HDA_CFG_CMD_OFFSET, 0x507,
+                               NVIDIA_TEGRA_HDA_ENABLE_MEM_SPACE |
+                               NVIDIA_TEGRA_HDA_ENABLE_IO_SPACE |
+                               NVIDIA_TEGRA_HDA_ENABLE_BUS_MASTER |
+                               NVIDIA_TEGRA_HDA_ENABLE_SERR);
+               reg_update_bits(chip->remap_config_addr,
+                               NVIDIA_TEGRA_HDA_CFG_BAR0_OFFSET, 0xFFFFFFFF,
+                               NVIDIA_TEGRA_HDA_BAR0_INIT_PROGRAM);
+               reg_update_bits(chip->remap_config_addr,
+                               NVIDIA_TEGRA_HDA_CFG_BAR0_OFFSET, 0xFFFFFFFF,
+                               NVIDIA_TEGRA_HDA_BAR0_FINAL_PROGRAM);
+               reg_update_bits(chip->remap_config_addr,
+                               NVIDIA_TEGRA_HDA_IPFS_FPCI_BAR0, 0xFFFFFFFF,
+                               NVIDIA_TEGRA_HDA_FPCI_BAR0_START);
+               reg_update_bits(chip->remap_config_addr,
+                               NVIDIA_TEGRA_HDA_IPFS_INTR_MASK,
+                               NVIDIA_TEGRA_HDA_IPFS_EN_INTR,
+                               NVIDIA_TEGRA_HDA_IPFS_EN_INTR);
+               break;
+#endif
+       default:
+               break;
+       }
+
+       return;
+}
+
+static void azx_platform_enable_clocks(struct azx *chip)
+{
+       int i;
+
+       for (i = 0; i < chip->platform_clk_count; i++)
+               clk_enable(chip->platform_clks[i]);
+
+       chip->platform_clk_enable++;
+}
+
+static void azx_platform_disable_clocks(struct azx *chip)
+{
+       int i;
+
+       if (!chip->platform_clk_enable)
+               return;
+
+       for (i = 0; i < chip->platform_clk_count; i++)
+               clk_disable(chip->platform_clks[i]);
+
+       chip->platform_clk_enable--;
+}
+#endif /* CONFIG_SND_HDA_PLATFORM_DRIVER */
 
 static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
 
@@ -1437,6 +1600,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model)
        bus_temp.private_data = chip;
        bus_temp.modelname = model;
        bus_temp.pci = chip->pci;
+       bus_temp.pdev = chip->pdev;
        bus_temp.ops.command = azx_send_cmd;
        bus_temp.ops.get_response = azx_get_response;
        bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
@@ -1924,7 +2088,8 @@ static unsigned int azx_via_get_position(struct azx *chip,
 }
 
 static unsigned int azx_get_position(struct azx *chip,
-                                    struct azx_dev *azx_dev)
+                                    struct azx_dev *azx_dev,
+                                    bool with_check)
 {
        unsigned int pos;
        int stream = azx_dev->substream->stream;
@@ -1940,7 +2105,7 @@ static unsigned int azx_get_position(struct azx *chip,
        default:
                /* use the position buffer */
                pos = le32_to_cpu(*azx_dev->posbuf);
-               if (chip->position_fix[stream] == POS_FIX_AUTO) {
+               if (with_check && chip->position_fix[stream] == POS_FIX_AUTO) {
                        if (!pos || pos == (u32)-1) {
                                printk(KERN_WARNING
                                       "hda-intel: Invalid position buffer, "
@@ -1964,7 +2129,7 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
        struct azx *chip = apcm->chip;
        struct azx_dev *azx_dev = get_azx_dev(substream);
        return bytes_to_frames(substream->runtime,
-                              azx_get_position(chip, azx_dev));
+                              azx_get_position(chip, azx_dev, false));
 }
 
 /*
@@ -1987,7 +2152,7 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
                return -1;      /* bogus (too early) interrupt */
 
        stream = azx_dev->substream->stream;
-       pos = azx_get_position(chip, azx_dev);
+       pos = azx_get_position(chip, azx_dev, true);
 
        if (WARN_ONCE(!azx_dev->period_bytes,
                      "hda-intel: zero azx_dev->period_bytes"))
@@ -2125,7 +2290,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
        if (size > MAX_PREALLOC_SIZE)
                size = MAX_PREALLOC_SIZE;
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
-                                             snd_dma_pci_data(chip->pci),
+                                             chip->dev,
                                              size, MAX_PREALLOC_SIZE);
        return 0;
 }
@@ -2167,17 +2332,19 @@ static int __devinit azx_init_stream(struct azx *chip)
 
 static int azx_acquire_irq(struct azx *chip, int do_disconnect)
 {
-       if (request_irq(chip->pci->irq, azx_interrupt,
+       if (request_irq(chip->irq_id, azx_interrupt,
                        chip->msi ? 0 : IRQF_SHARED,
                        KBUILD_MODNAME, chip)) {
                printk(KERN_ERR "hda-intel: unable to grab IRQ %d, "
-                      "disabling device\n", chip->pci->irq);
+                      "disabling device\n", chip->irq_id);
                if (do_disconnect)
                        snd_card_disconnect(chip->card);
                return -1;
        }
-       chip->irq = chip->pci->irq;
-       pci_intx(chip->pci, !chip->msi);
+       chip->irq = chip->irq_id;
+       if (chip->pci)
+               pci_intx(chip->pci, !chip->msi);
+
        return 0;
 }
 
@@ -2215,11 +2382,19 @@ static void azx_power_notify(struct hda_bus *bus)
                        break;
                }
        }
-       if (power_on)
+       if (power_on) {
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+               azx_platform_enable_clocks(chip);
+#endif
                azx_init_chip(chip, 1);
+       }
        else if (chip->running && power_save_controller &&
-                !bus->power_keep_link_on)
+                !bus->power_keep_link_on) {
                azx_stop_chip(chip);
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+               azx_platform_disable_clocks(chip);
+#endif
+       }
 }
 #endif /* CONFIG_SND_HDA_POWER_SAVE */
 
@@ -2239,12 +2414,17 @@ static int snd_hda_codecs_inuse(struct hda_bus *bus)
        return 0;
 }
 
-static int azx_suspend(struct pci_dev *pci, pm_message_t state)
+static int azx_suspend(struct azx *chip, pm_message_t state)
 {
-       struct snd_card *card = pci_get_drvdata(pci);
-       struct azx *chip = card->private_data;
+       struct snd_card *card = chip->card;
        int i;
 
+#if defined(CONFIG_SND_HDA_PLATFORM_DRIVER) && \
+       defined(CONFIG_SND_HDA_POWER_SAVE)
+       if (chip->pdev)
+               azx_platform_enable_clocks(chip);
+#endif
+
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
        azx_clear_irq_pending(chip);
        for (i = 0; i < HDA_MAX_PCMS; i++)
@@ -2256,42 +2436,111 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
                free_irq(chip->irq, chip);
                chip->irq = -1;
        }
-       if (chip->msi)
-               pci_disable_msi(chip->pci);
-       pci_disable_device(pci);
-       pci_save_state(pci);
-       pci_set_power_state(pci, pci_choose_state(pci, state));
+
+       if (chip->pci) {
+               if (chip->msi)
+                       pci_disable_msi(chip->pci);
+               pci_disable_device(chip->pci);
+               pci_save_state(chip->pci);
+               pci_set_power_state(chip->pci,
+                                   pci_choose_state(chip->pci, state));
+       }
+
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+       if (chip->pdev) {
+               /* Disable all clk references */
+               while (chip->platform_clk_enable)
+                       azx_platform_disable_clocks(chip);
+       }
+#endif
+
        return 0;
 }
 
-static int azx_resume(struct pci_dev *pci)
+static int azx_resume(struct azx *chip)
 {
-       struct snd_card *card = pci_get_drvdata(pci);
-       struct azx *chip = card->private_data;
+       struct snd_card *card = chip->card;
 
-       pci_set_power_state(pci, PCI_D0);
-       pci_restore_state(pci);
-       if (pci_enable_device(pci) < 0) {
-               printk(KERN_ERR "hda-intel: pci_enable_device failed, "
-                      "disabling device\n");
-               snd_card_disconnect(card);
-               return -EIO;
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+       if (chip->pdev)
+               azx_platform_enable_clocks(chip);
+#endif
+
+       if (chip->pci) {
+               pci_set_power_state(chip->pci, PCI_D0);
+               pci_restore_state(chip->pci);
+               if (pci_enable_device(chip->pci) < 0) {
+                       printk(KERN_ERR "hda-intel: pci_enable_device failed, "
+                              "disabling device\n");
+                       snd_card_disconnect(card);
+                       return -EIO;
+               }
+               pci_set_master(chip->pci);
+               if (chip->msi)
+                       if (pci_enable_msi(chip->pci) < 0)
+                               chip->msi = 0;
        }
-       pci_set_master(pci);
-       if (chip->msi)
-               if (pci_enable_msi(pci) < 0)
-                       chip->msi = 0;
+
        if (azx_acquire_irq(chip, 1) < 0)
                return -EIO;
-       azx_init_pci(chip);
+
+       if (chip->pci)
+               azx_init_pci(chip);
+
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+       if (chip->pdev)
+               azx_init_platform(chip);
+#endif
 
        if (snd_hda_codecs_inuse(chip->bus))
                azx_init_chip(chip, 1);
 
        snd_hda_resume(chip->bus);
        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+#if defined(CONFIG_SND_HDA_PLATFORM_DRIVER) && \
+       defined(CONFIG_SND_HDA_POWER_SAVE)
+       if (chip->pdev)
+               azx_platform_disable_clocks(chip);
+#endif
+
        return 0;
 }
+
+static int azx_suspend_pci(struct pci_dev *pci, pm_message_t state)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct azx *chip = card->private_data;
+
+       return azx_suspend(chip, state);
+}
+
+static int azx_resume_pci(struct pci_dev *pci)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct azx *chip = card->private_data;
+
+       return azx_resume(chip);
+}
+
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+static int azx_suspend_platform(struct platform_device *pdev,
+                               pm_message_t state)
+{
+       struct snd_card *card = dev_get_drvdata(&pdev->dev);
+       struct azx *chip = card->private_data;
+
+       return azx_suspend(chip, state);
+}
+
+static int azx_resume_platform(struct platform_device *pdev)
+{
+       struct snd_card *card = dev_get_drvdata(&pdev->dev);
+       struct azx *chip = card->private_data;
+
+       return azx_resume(chip);
+}
+#endif /* CONFIG_SND_HDA_PLATFORM_DRIVER */
 #endif /* CONFIG_PM */
 
 
@@ -2301,8 +2550,22 @@ static int azx_resume(struct pci_dev *pci)
 static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf)
 {
        struct azx *chip = container_of(nb, struct azx, reboot_notifier);
+
+#if defined(CONFIG_SND_HDA_PLATFORM_DRIVER) && \
+       defined(CONFIG_SND_HDA_POWER_SAVE)
+       if (chip->pdev)
+               azx_platform_enable_clocks(chip);
+#endif
+
        snd_hda_bus_reboot_notify(chip->bus);
        azx_stop_chip(chip);
+
+#if defined(CONFIG_SND_HDA_PLATFORM_DRIVER) && \
+       defined(CONFIG_SND_HDA_POWER_SAVE)
+       if (chip->pdev)
+               azx_platform_disable_clocks(chip);
+#endif
+
        return NOTIFY_OK;
 }
 
@@ -2334,9 +2597,15 @@ static int azx_free(struct azx *chip)
                azx_stop_chip(chip);
        }
 
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+       azx_platform_disable_clocks(chip);
+       for (i = 0; i < chip->platform_clk_count; i++)
+               clk_put(chip->platform_clks[i]);
+#endif
+
        if (chip->irq >= 0)
                free_irq(chip->irq, (void*)chip);
-       if (chip->msi)
+       if (chip->pci && chip->msi)
                pci_disable_msi(chip->pci);
        if (chip->remap_addr)
                iounmap(chip->remap_addr);
@@ -2350,8 +2619,10 @@ static int azx_free(struct azx *chip)
                snd_dma_free_pages(&chip->rb);
        if (chip->posbuf.area)
                snd_dma_free_pages(&chip->posbuf);
-       pci_release_regions(chip->pci);
-       pci_disable_device(chip->pci);
+       if (chip->pci) {
+               pci_release_regions(chip->pci);
+               pci_disable_device(chip->pci);
+       }
        kfree(chip->azx_dev);
        kfree(chip);
 
@@ -2369,10 +2640,12 @@ static int azx_dev_free(struct snd_device *device)
 static struct snd_pci_quirk position_fix_list[] __devinitdata = {
        SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x1028, 0x02c6, "Dell Inspiron 1010", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB),
+       SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS 1101HA", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
        SND_PCI_QUIRK(0x1297, 0x3166, "Shuttle", POS_FIX_LPIB),
@@ -2395,13 +2668,15 @@ static int __devinit check_position_fix(struct azx *chip, int fix)
                return fix;
        }
 
-       q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
-       if (q) {
-               printk(KERN_INFO
-                      "hda_intel: position_fix set to %d "
-                      "for device %04x:%04x\n",
-                      q->value, q->subvendor, q->subdevice);
-               return q->value;
+       if (chip->pci) {
+               q = snd_pci_quirk_lookup(chip->pci, position_fix_list);
+               if (q) {
+                       printk(KERN_INFO
+                              "hda_intel: position_fix set to %d "
+                              "for device %04x:%04x\n",
+                              q->value, q->subvendor, q->subdevice);
+                       return q->value;
+               }
        }
 
        /* Check VIA/ATI HD Audio Controller exist */
@@ -2443,7 +2718,7 @@ static void __devinit check_probe_mask(struct azx *chip, int dev)
        const struct snd_pci_quirk *q;
 
        chip->codec_probe_mask = probe_mask[dev];
-       if (chip->codec_probe_mask == -1) {
+       if (chip->pci && (chip->codec_probe_mask == -1)) {
                q = snd_pci_quirk_lookup(chip->pci, probe_mask_list);
                if (q) {
                        printk(KERN_INFO
@@ -2479,6 +2754,12 @@ static void __devinit check_msi(struct azx *chip)
 {
        const struct snd_pci_quirk *q;
 
+       /* Disable MSI if chip is not a pci device */
+       if (!chip->pci) {
+               chip->msi = 0;
+               return;
+       }
+
        if (enable_msi >= 0) {
                chip->msi = !!enable_msi;
                return;
@@ -2500,16 +2781,25 @@ static void __devinit check_msi(struct azx *chip)
        }
 }
 
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+static const char *tegra_clk_names[] __devinitdata = {
+       "hda",
+       "hda2codec",
+       "hda2hdmi",
+};
+static struct clk *tegra_clks[ARRAY_SIZE(tegra_clk_names)];
+#endif
 
 /*
  * constructor
  */
 static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
+                               struct platform_device *pdev,
                                int dev, unsigned int driver_caps,
                                struct azx **rchip)
 {
        struct azx *chip;
-       int i, err;
+       int i, err = 0;
        unsigned short gcap;
        static struct snd_device_ops ops = {
                .dev_free = azx_dev_free,
@@ -2517,14 +2807,17 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
 
        *rchip = NULL;
 
-       err = pci_enable_device(pci);
-       if (err < 0)
-               return err;
+       if (pci) {
+               err = pci_enable_device(pci);
+               if (err < 0)
+                       return err;
+       }
 
        chip = kzalloc(sizeof(*chip), GFP_KERNEL);
        if (!chip) {
                snd_printk(KERN_ERR SFX "cannot allocate chip\n");
-               pci_disable_device(pci);
+               if (pci)
+                       pci_disable_device(pci);
                return -ENOMEM;
        }
 
@@ -2532,6 +2825,9 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        mutex_init(&chip->open_mutex);
        chip->card = card;
        chip->pci = pci;
+       chip->pdev = pdev;
+       chip->dev = pci ? snd_dma_pci_data(pci) : &pdev->dev;
+       chip->irq_id = pci ? pci->irq : platform_get_irq(pdev, 0);
        chip->irq = -1;
        chip->driver_caps = driver_caps;
        chip->driver_type = driver_caps & 0xff;
@@ -2567,38 +2863,105 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        }
 #endif
 
-       err = pci_request_regions(pci, "ICH HD audio");
-       if (err < 0) {
-               kfree(chip);
-               pci_disable_device(pci);
-               return err;
-       }
+       if (chip->pci) {
+               err = pci_request_regions(pci, "ICH HD audio");
+               if (err < 0) {
+                       kfree(chip);
+                       pci_disable_device(pci);
+                       return err;
+               }
 
-       chip->addr = pci_resource_start(pci, 0);
-       chip->remap_addr = pci_ioremap_bar(pci, 0);
-       if (chip->remap_addr == NULL) {
-               snd_printk(KERN_ERR SFX "ioremap error\n");
-               err = -ENXIO;
-               goto errout;
+               chip->addr = pci_resource_start(pci, 0);
+               chip->remap_addr = pci_ioremap_bar(pci, 0);
+               if (chip->remap_addr == NULL) {
+                       snd_printk(KERN_ERR SFX "ioremap error\n");
+                       err = -ENXIO;
+                       goto errout;
+               }
+
+               if (chip->msi)
+                       if (pci_enable_msi(pci) < 0)
+                               chip->msi = 0;
        }
 
-       if (chip->msi)
-               if (pci_enable_msi(pci) < 0)
-                       chip->msi = 0;
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+       if (chip->pdev) {
+               struct resource *res, *region;
+
+               /* Do platform specific initialization */
+               switch (chip->driver_type) {
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+               case AZX_DRIVER_NVIDIA_TEGRA:
+                       chip->platform_clk_count = ARRAY_SIZE(tegra_clk_names);
+                       for (i = 0; i < chip->platform_clk_count; i++) {
+                               tegra_clks[i] = clk_get(&pdev->dev,
+                                                       tegra_clk_names[i]);
+                               if (IS_ERR_OR_NULL(tegra_clks[i])) {
+                                       err = PTR_ERR(tegra_clks[i]);
+                                       goto errout;
+                               }
+                       }
+                       chip->platform_clks = tegra_clks;
+                       break;
+#endif
+               default:
+                       break;
+               }
+
+               azx_platform_enable_clocks(chip);
+
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               if (res == NULL) {
+                       err = EINVAL;
+                       goto errout;
+               }
+
+               region = devm_request_mem_region(chip->dev, res->start,
+                                                resource_size(res),
+                                                pdev->name);
+               if (!region) {
+                       snd_printk(KERN_ERR SFX "Mem region already claimed\n");
+                       err = -EINVAL;
+                       goto errout;
+               }
+
+               chip->addr = res->start;
+               chip->remap_addr = devm_ioremap(chip->dev,
+                                               res->start,
+                                               resource_size(res));
+               if (chip->remap_addr == NULL) {
+                       snd_printk(KERN_ERR SFX "ioremap error\n");
+                       err = -ENXIO;
+                       goto errout;
+               }
+
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+               if (chip->driver_type == AZX_DRIVER_NVIDIA_TEGRA) {
+                       chip->remap_config_addr = chip->remap_addr;
+                       chip->remap_addr += NVIDIA_TEGRA_HDA_BAR0_OFFSET;
+                       chip->addr += NVIDIA_TEGRA_HDA_BAR0_OFFSET;
+               }
+#endif
+
+               azx_init_platform(chip);
+       }
+#endif /* CONFIG_SND_HDA_PLATFORM_DRIVER */
 
        if (azx_acquire_irq(chip, 0) < 0) {
                err = -EBUSY;
                goto errout;
        }
 
-       pci_set_master(pci);
+       if (chip->pci)
+               pci_set_master(pci);
+
        synchronize_irq(chip->irq);
 
        gcap = azx_readw(chip, GCAP);
        snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
 
        /* disable SB600 64bit support for safety */
-       if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
+       if (chip->pci && chip->pci->vendor == PCI_VENDOR_ID_ATI) {
                struct pci_dev *p_smbus;
                p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
                                         PCI_DEVICE_ID_ATI_SBX00_SMBUS,
@@ -2616,12 +2979,15 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
                gcap &= ~ICH6_GCAP_64OK;
        }
 
-       /* allow 64bit DMA address if supported by H/W */
-       if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
-               pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
-       else {
-               pci_set_dma_mask(pci, DMA_BIT_MASK(32));
-               pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
+       if (chip->pci) {
+               /* allow 64bit DMA address if supported by H/W */
+               if ((gcap & ICH6_GCAP_64OK) &&
+                   !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
+                       pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
+               else {
+                       pci_set_dma_mask(pci, DMA_BIT_MASK(32));
+                       pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
+               }
        }
 
        /* read number of streams from GCAP register instead of using
@@ -2661,7 +3027,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        for (i = 0; i < chip->num_streams; i++) {
                /* allocate memory for the BDL for each stream */
                err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
-                                         snd_dma_pci_data(chip->pci),
+                                         chip->dev,
                                          BDL_SIZE, &chip->azx_dev[i].bdl);
                if (err < 0) {
                        snd_printk(KERN_ERR SFX "cannot allocate BDL\n");
@@ -2670,7 +3036,7 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        }
        /* allocate memory for the position buffer */
        err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
-                                 snd_dma_pci_data(chip->pci),
+                                 chip->dev,
                                  chip->num_streams * 8, &chip->posbuf);
        if (err < 0) {
                snd_printk(KERN_ERR SFX "cannot allocate posbuf\n");
@@ -2685,7 +3051,8 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        azx_init_stream(chip);
 
        /* initialize chip */
-       azx_init_pci(chip);
+       if (chip->pci)
+               azx_init_pci(chip);
        azx_init_chip(chip, (probe_only[dev] & 2) == 0);
 
        /* codec detection */
@@ -2730,11 +3097,13 @@ static void power_down_all_codecs(struct azx *chip)
 }
 
 static int __devinit azx_probe(struct pci_dev *pci,
-                              const struct pci_device_id *pci_id)
+                              struct platform_device *pdev,
+                              int driver_data)
 {
        static int dev;
        struct snd_card *card;
        struct azx *chip;
+       struct device *azx_dev = pci ? &pci->dev : &pdev->dev;
        int err;
 
        if (dev >= SNDRV_CARDS)
@@ -2751,9 +3120,9 @@ static int __devinit azx_probe(struct pci_dev *pci,
        }
 
        /* set this here since it's referred in snd_hda_load_patch() */
-       snd_card_set_dev(card, &pci->dev);
+       snd_card_set_dev(card, azx_dev);
 
-       err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
+       err = azx_create(card, pci, pdev, dev, driver_data, &chip);
        if (err < 0)
                goto out_free;
        card->private_data = chip;
@@ -2795,7 +3164,11 @@ static int __devinit azx_probe(struct pci_dev *pci,
        if (err < 0)
                goto out_free;
 
-       pci_set_drvdata(pci, card);
+       if (pci)
+               pci_set_drvdata(pci, card);
+       else
+               dev_set_drvdata(&pdev->dev, card);
+
        chip->running = 1;
        power_down_all_codecs(chip);
        azx_notifier_register(chip);
@@ -2807,14 +3180,20 @@ out_free:
        return err;
 }
 
-static void __devexit azx_remove(struct pci_dev *pci)
+static int __devinit azx_probe_pci(struct pci_dev *pci,
+                                  const struct pci_device_id *pci_id)
+{
+       return azx_probe(pci, NULL, pci_id->driver_data);
+}
+
+static void __devexit azx_remove_pci(struct pci_dev *pci)
 {
        snd_card_free(pci_get_drvdata(pci));
        pci_set_drvdata(pci, NULL);
 }
 
 /* PCI IDs */
-static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
+static DEFINE_PCI_DEVICE_TABLE(azx_pci_ids) = {
        /* CPT */
        { PCI_DEVICE(0x8086, 0x1c20),
          .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP },
@@ -2910,12 +3289,12 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
          .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
          .class_mask = 0xffffff,
          .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
-         AZX_DCAPS_RIRB_PRE_DELAY },
+         AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
 #else
        /* this entry seems still valid -- i.e. without emu20kx chip */
        { PCI_DEVICE(0x1102, 0x0009),
          .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
-         AZX_DCAPS_RIRB_PRE_DELAY },
+         AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
 #endif
        /* Vortex86MX */
        { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
@@ -2932,27 +3311,85 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
          .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
        { 0, }
 };
-MODULE_DEVICE_TABLE(pci, azx_ids);
+MODULE_DEVICE_TABLE(pci, azx_pci_ids);
 
 /* pci_driver definition */
 static struct pci_driver driver = {
        .name = KBUILD_MODNAME,
-       .id_table = azx_ids,
-       .probe = azx_probe,
-       .remove = __devexit_p(azx_remove),
+       .id_table = azx_pci_ids,
+       .probe = azx_probe_pci,
+       .remove = __devexit_p(azx_remove_pci),
+#ifdef CONFIG_PM
+       .suspend = azx_suspend_pci,
+       .resume = azx_resume_pci,
+#endif
+};
+
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+static int __devinit azx_probe_platform(struct platform_device *pdev)
+{
+       const struct platform_device_id *pdev_id = platform_get_device_id(pdev);
+
+       return azx_probe(NULL, pdev, pdev_id->driver_data);
+}
+
+static int __devexit azx_remove_platform(struct platform_device *pdev)
+{
+       return snd_card_free(dev_get_drvdata(&pdev->dev));
+}
+
+static const struct platform_device_id azx_platform_ids[] = {
+#ifdef CONFIG_SND_HDA_PLATFORM_NVIDIA_TEGRA
+       { "tegra30-hda",
+         .driver_data = AZX_DRIVER_NVIDIA_TEGRA | AZX_DCAPS_RIRB_DELAY },
+#endif
+       { },
+};
+MODULE_DEVICE_TABLE(platform, azx_platform_ids);
+
+/* platform_driver definition */
+static struct platform_driver hda_platform_driver = {
+       .driver = {
+               .name = "hda-platform"
+       },
+       .probe = azx_probe_platform,
+       .remove = __devexit_p(azx_remove_platform),
+       .id_table = azx_platform_ids,
 #ifdef CONFIG_PM
-       .suspend = azx_suspend,
-       .resume = azx_resume,
+       .suspend = azx_suspend_platform,
+       .resume = azx_resume_platform,
 #endif
 };
+#endif /* CONFIG_SND_HDA_PLATFORM_DRIVER */
 
 static int __init alsa_card_azx_init(void)
 {
-       return pci_register_driver(&driver);
+       int err = 0;
+
+       err = pci_register_driver(&driver);
+       if (err < 0) {
+               snd_printk(KERN_ERR SFX "Failed to register pci driver\n");
+               return err;
+       }
+
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+       err = platform_driver_register(&hda_platform_driver);
+       if (err < 0) {
+               snd_printk(KERN_ERR SFX "Failed to register platform driver\n");
+               pci_unregister_driver(&driver);
+               return err;
+       }
+#endif
+
+       return 0;
 }
 
 static void __exit alsa_card_azx_exit(void)
 {
+#ifdef CONFIG_SND_HDA_PLATFORM_DRIVER
+       platform_driver_unregister(&hda_platform_driver);
+#endif
+
        pci_unregister_driver(&driver);
 }