#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"
"{Intel, ESB2},"
"{Intel, ICH8},"
"{ATI, SB450},"
+ "{ATI, SB600},"
+ "{ATI, RS600},"
"{VIA, VT8251},"
"{VIA, VT8237A},"
"{SiS, SIS966},"
#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
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 */
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 */
/* locks */
spinlock_t reg_lock;
- struct semaphore open_mutex;
+ struct mutex open_mutex;
/* streams (x num_streams) */
struct azx_dev *azx_dev;
enum {
AZX_DRIVER_ICH,
AZX_DRIVER_ATI,
+ AZX_DRIVER_ATIHDMI,
AZX_DRIVER_VIA,
AZX_DRIVER_SIS,
AZX_DRIVER_ULI,
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",
}
/* 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;
}
/* 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);
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)
{
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;
}
}
}
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;
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;
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);
spin_unlock_irqrestore(&chip->reg_lock, flags);
runtime->private_data = azx_dev;
- up(&chip->open_mutex);
+ mutex_unlock(&chip->open_mutex);
return 0;
}
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;
}
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);
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)
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;
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);
}
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
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;
}
/* 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 */