[ALSA] hda - Add support for the ATI RS600 HDMI audio device
[pandora-kernel.git] / sound / pci / hda / hda_intel.c
index b3f37e7..0154389 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include "hda_codec.h"
@@ -80,6 +81,8 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
                         "{Intel, ESB2},"
                         "{Intel, ICH8},"
                         "{ATI, SB450},"
+                        "{ATI, SB600},"
+                        "{ATI, RS600},"
                         "{VIA, VT8251},"
                         "{VIA, VT8237A},"
                         "{SiS, SIS966},"
@@ -165,6 +168,12 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define ULI_PLAYBACK_INDEX     5
 #define ULI_NUM_PLAYBACK       6
 
+/* ATI HDMI has 1 playback and 0 capture */
+#define ATIHDMI_CAPTURE_INDEX  0
+#define ATIHDMI_NUM_CAPTURE    0
+#define ATIHDMI_PLAYBACK_INDEX 0
+#define ATIHDMI_NUM_PLAYBACK   1
+
 /* this number is statically defined for simplicity */
 #define MAX_AZX_DEV            16
 
@@ -249,7 +258,6 @@ struct azx_dev {
        unsigned int fragsize;          /* size of each period in bytes */
        unsigned int frags;             /* number for period in the play buffer */
        unsigned int fifo_size;         /* FIFO size */
-       unsigned int last_pos;          /* last updated period position */
 
        void __iomem *sd_addr;          /* stream descriptor pointer */
 
@@ -260,10 +268,11 @@ struct azx_dev {
        unsigned int format_val;        /* format value to be set in the controller and the codec */
        unsigned char stream_tag;       /* assigned stream */
        unsigned char index;            /* stream index */
+       /* for sanity check of position buffer */
+       unsigned int period_intr;
 
        unsigned int opened: 1;
        unsigned int running: 1;
-       unsigned int period_updating: 1;
 };
 
 /* CORB/RIRB */
@@ -297,7 +306,7 @@ struct azx {
 
        /* locks */
        spinlock_t reg_lock;
-       struct semaphore open_mutex;
+       struct mutex open_mutex;
 
        /* streams (x num_streams) */
        struct azx_dev *azx_dev;
@@ -329,6 +338,7 @@ struct azx {
 enum {
        AZX_DRIVER_ICH,
        AZX_DRIVER_ATI,
+       AZX_DRIVER_ATIHDMI,
        AZX_DRIVER_VIA,
        AZX_DRIVER_SIS,
        AZX_DRIVER_ULI,
@@ -338,6 +348,7 @@ enum {
 static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_ICH] = "HDA Intel",
        [AZX_DRIVER_ATI] = "HDA ATI SB",
+       [AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
        [AZX_DRIVER_VIA] = "HDA VIA VT82xx",
        [AZX_DRIVER_SIS] = "HDA SIS966",
        [AZX_DRIVER_ULI] = "HDA ULI M5461",
@@ -445,8 +456,8 @@ static void azx_free_cmd_io(struct azx *chip)
 }
 
 /* send a command */
-static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
-                       unsigned int verb, unsigned int para)
+static int azx_corb_send_cmd(struct hda_codec *codec, hda_nid_t nid, int direct,
+                            unsigned int verb, unsigned int para)
 {
        struct azx *chip = codec->bus->private_data;
        unsigned int wp;
@@ -502,18 +513,21 @@ static void azx_update_rirb(struct azx *chip)
 }
 
 /* receive a response */
-static unsigned int azx_get_response(struct hda_codec *codec)
+static unsigned int azx_rirb_get_response(struct hda_codec *codec)
 {
        struct azx *chip = codec->bus->private_data;
        int timeout = 50;
 
        while (chip->rirb.cmds) {
                if (! --timeout) {
-                       if (printk_ratelimit())
-                               snd_printk(KERN_ERR
-                                       "azx_get_response timeout\n");
+                       snd_printk(KERN_ERR
+                                  "hda_intel: azx_get_response timeout, "
+                                  "switching to single_cmd mode...\n");
                        chip->rirb.rp = azx_readb(chip, RIRBWP);
                        chip->rirb.cmds = 0;
+                       /* switch to single_cmd mode */
+                       chip->single_cmd = 1;
+                       azx_free_cmd_io(chip);
                        return -1;
                }
                msleep(1);
@@ -577,6 +591,36 @@ static unsigned int azx_single_get_response(struct hda_codec *codec)
        return (unsigned int)-1;
 }
 
+/*
+ * The below are the main callbacks from hda_codec.
+ *
+ * They are just the skeleton to call sub-callbacks according to the
+ * current setting of chip->single_cmd.
+ */
+
+/* send a command */
+static int azx_send_cmd(struct hda_codec *codec, hda_nid_t nid,
+                       int direct, unsigned int verb,
+                       unsigned int para)
+{
+       struct azx *chip = codec->bus->private_data;
+       if (chip->single_cmd)
+               return azx_single_send_cmd(codec, nid, direct, verb, para);
+       else
+               return azx_corb_send_cmd(codec, nid, direct, verb, para);
+}
+
+/* get a response */
+static unsigned int azx_get_response(struct hda_codec *codec)
+{
+       struct azx *chip = codec->bus->private_data;
+       if (chip->single_cmd)
+               return azx_single_get_response(codec);
+       else
+               return azx_rirb_get_response(codec);
+}
+
+
 /* reset codec link */
 static int azx_reset(struct azx *chip)
 {
@@ -770,11 +814,10 @@ static irqreturn_t azx_interrupt(int irq, void* dev_id, struct pt_regs *regs)
                if (status & azx_dev->sd_int_sta_mask) {
                        azx_sd_writeb(azx_dev, SD_STS, SD_INT_MASK);
                        if (azx_dev->substream && azx_dev->running) {
-                               azx_dev->period_updating = 1;
+                               azx_dev->period_intr++;
                                spin_unlock(&chip->reg_lock);
                                snd_pcm_period_elapsed(azx_dev->substream);
                                spin_lock(&chip->reg_lock);
-                               azx_dev->period_updating = 0;
                        }
                }
        }
@@ -899,13 +942,8 @@ 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;
-       if (chip->single_cmd) {
-               bus_temp.ops.command = azx_single_send_cmd;
-               bus_temp.ops.get_response = azx_single_get_response;
-       } else {
-               bus_temp.ops.command = azx_send_cmd;
-               bus_temp.ops.get_response = azx_get_response;
-       }
+       bus_temp.ops.command = azx_send_cmd;
+       bus_temp.ops.get_response = azx_get_response;
 
        if ((err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus)) < 0)
                return err;
@@ -993,10 +1031,10 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
        unsigned long flags;
        int err;
 
-       down(&chip->open_mutex);
+       mutex_lock(&chip->open_mutex);
        azx_dev = azx_assign_device(chip, substream->stream);
        if (azx_dev == NULL) {
-               up(&chip->open_mutex);
+               mutex_unlock(&chip->open_mutex);
                return -EBUSY;
        }
        runtime->hw = azx_pcm_hw;
@@ -1008,7 +1046,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
        snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
        if ((err = hinfo->ops.open(hinfo, apcm->codec, substream)) < 0) {
                azx_release_device(azx_dev);
-               up(&chip->open_mutex);
+               mutex_unlock(&chip->open_mutex);
                return err;
        }
        spin_lock_irqsave(&chip->reg_lock, flags);
@@ -1017,7 +1055,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
        spin_unlock_irqrestore(&chip->reg_lock, flags);
 
        runtime->private_data = azx_dev;
-       up(&chip->open_mutex);
+       mutex_unlock(&chip->open_mutex);
        return 0;
 }
 
@@ -1029,14 +1067,14 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
        struct azx_dev *azx_dev = get_azx_dev(substream);
        unsigned long flags;
 
-       down(&chip->open_mutex);
+       mutex_lock(&chip->open_mutex);
        spin_lock_irqsave(&chip->reg_lock, flags);
        azx_dev->substream = NULL;
        azx_dev->running = 0;
        spin_unlock_irqrestore(&chip->reg_lock, flags);
        azx_release_device(azx_dev);
        hinfo->ops.close(hinfo, apcm->codec, substream);
-       up(&chip->open_mutex);
+       mutex_unlock(&chip->open_mutex);
        return 0;
 }
 
@@ -1090,7 +1128,6 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream)
                azx_dev->fifo_size = azx_sd_readw(azx_dev, SD_FIFOSIZE) + 1;
        else
                azx_dev->fifo_size = 0;
-       azx_dev->last_pos = 0;
 
        return hinfo->ops.prepare(hinfo, apcm->codec, azx_dev->stream_tag,
                                  azx_dev->format_val, substream);
@@ -1138,10 +1175,20 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
        struct azx_dev *azx_dev = get_azx_dev(substream);
        unsigned int pos;
 
-       if (chip->position_fix == POS_FIX_POSBUF) {
+       if (chip->position_fix == POS_FIX_POSBUF ||
+           chip->position_fix == POS_FIX_AUTO) {
                /* use the position buffer */
                pos = *azx_dev->posbuf;
+               if (chip->position_fix == POS_FIX_AUTO &&
+                   azx_dev->period_intr == 1 && ! pos) {
+                       printk(KERN_WARNING
+                              "hda-intel: Invalid position buffer, "
+                              "using LPIB read method instead.\n");
+                       chip->position_fix = POS_FIX_NONE;
+                       goto read_lpib;
+               }
        } else {
+       read_lpib:
                /* read LPIB */
                pos = azx_sd_readl(azx_dev, SD_LPIB);
                if (chip->position_fix == POS_FIX_FIFO)
@@ -1307,8 +1354,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
        for (i = 0; i < chip->pcm_devs; i++)
                snd_pcm_suspend_all(chip->pcm[i]);
        snd_hda_suspend(chip->bus, state);
-       if (! chip->single_cmd)
-               azx_free_cmd_io(chip);
+       azx_free_cmd_io(chip);
        pci_disable_device(pci);
        pci_save_state(pci);
        return 0;
@@ -1346,8 +1392,7 @@ static int azx_free(struct azx *chip)
                azx_int_clear(chip);
 
                /* disable CORB/RIRB */
-               if (! chip->single_cmd)
-                       azx_free_cmd_io(chip);
+               azx_free_cmd_io(chip);
 
                /* disable position buffer */
                azx_writel(chip, DPLBASE, 0);
@@ -1408,13 +1453,13 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
        }
 
        spin_lock_init(&chip->reg_lock);
-       init_MUTEX(&chip->open_mutex);
+       mutex_init(&chip->open_mutex);
        chip->card = card;
        chip->pci = pci;
        chip->irq = -1;
        chip->driver_type = driver_type;
 
-       chip->position_fix = position_fix ? position_fix : POS_FIX_POSBUF;
+       chip->position_fix = position_fix;
        chip->single_cmd = single_cmd;
 
 #if BITS_PER_LONG != 64
@@ -1459,6 +1504,12 @@ static int __devinit azx_create(struct snd_card *card, struct pci_dev *pci,
                chip->playback_index_offset = ULI_PLAYBACK_INDEX;
                chip->capture_index_offset = ULI_CAPTURE_INDEX;
                break;
+       case AZX_DRIVER_ATIHDMI:
+               chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
+               chip->capture_streams = ATIHDMI_NUM_CAPTURE;
+               chip->playback_index_offset = ATIHDMI_PLAYBACK_INDEX;
+               chip->capture_index_offset = ATIHDMI_CAPTURE_INDEX;
+               break;
        default:
                chip->playback_streams = ICH6_NUM_PLAYBACK;
                chip->capture_streams = ICH6_NUM_CAPTURE;
@@ -1578,12 +1629,14 @@ static void __devexit azx_remove(struct pci_dev *pci)
 }
 
 /* PCI IDs */
-static struct pci_device_id azx_ids[] = {
+static struct pci_device_id azx_ids[] __devinitdata = {
        { 0x8086, 0x2668, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH6 */
        { 0x8086, 0x27d8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH7 */
        { 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */
        { 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
        { 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
+       { 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
+       { 0x1002, 0x793b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATIHDMI }, /* ATI RS600 HDMI */
        { 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
        { 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
        { 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */