[alsa-devel] [RFC v2 3/3] ALSA: hda: add hda controller to hda

Takashi Iwai tiwai at suse.de
Wed Mar 25 08:05:21 CET 2015


At Wed, 25 Mar 2015 11:31:39 +0530,
Vinod Koul wrote:
> 
> From: Jeeja KP <jeeja.kp at intel.com>
> 
> This adds hdac_controller into hda core.
> 
> Signed-off-by: Jeeja KP <jeeja.kp at intel.com>
> Signed-off-by: Vinod Koul <vinod.koul at intel.com>

Now the question is whether we need all codes.
For example, the bus reset isn't needed for Intel chips but it's a
workaround for old AMD chips.  Also, polling and single_cmd fallbacks
are also for buggy chips, and I don't think we'd need them for SKL.

The rirb_error flag can be removed and converted to the normal error
code propagation with the new code.  It's there just because the old
interface didn't give the error code in get_response op.  And, of
course, there are superfluous DSP loader for CA0132...

You can find some code I wrote in a couple of weeks ago in
test/hda-dev2 branch of sound-unstable git tree.  I rebased it on top
of hda-regmap branch now, so it's mostly clean.  There you can find
the subset of HDA controller codes that should be enough for a clean
controller driver implementation.

I don't mean that mine is necessarily the best.  But my point is that
we don't have to copy the whole in sound/hda.  Rather start from a
smaller and mandatory subset for the new driver, then integrate this
into the existing legacy driver.


thanks,

Takashi

> ---
>  include/sound/hdaudio.h     |  215 ++++++++
>  sound/hda/Makefile          |    2 +-
>  sound/hda/hdac_controller.c | 1284 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1500 insertions(+), 1 deletion(-)
>  create mode 100644 sound/hda/hdac_controller.c
> 
> diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
> index 675614dc2b88..2843471efc7a 100644
> --- a/include/sound/hdaudio.h
> +++ b/include/sound/hdaudio.h
> @@ -5,8 +5,11 @@
>  #ifndef __SOUND_HDAUDIO_H
>  #define __SOUND_HDAUDIO_H
>  
> +#include <linux/timecounter.h>
> +#include <sound/core.h>
>  #include <linux/device.h>
>  #include <sound/hda_verbs.h>
> +#include <sound/pcm.h>
>  
>  /* codec node id */
>  typedef u16 hda_nid_t;
> @@ -15,6 +18,7 @@ struct hdac_bus;
>  struct hdac_device;
>  struct hdac_driver;
>  struct hdac_widget_tree;
> +struct hda;
>  
>  /*
>   * exported bus type
> @@ -125,6 +129,8 @@ struct hdac_bus_ops {
>  	/* get a response from the last command */
>  	int (*get_response)(struct hdac_bus *bus, unsigned int addr,
>  			    unsigned int *res);
> +	/* reset bus for retry verb */
> +	void (*bus_reset)(struct hdac_bus *bus);
>  };
>  
>  #define HDA_UNSOL_QUEUE_SIZE	64
> @@ -144,6 +150,7 @@ struct hdac_bus {
>  	u32 unsol_queue[HDA_UNSOL_QUEUE_SIZE * 2]; /* ring buffer */
>  	unsigned int unsol_rp, unsol_wp;
>  	struct work_struct unsol_work;
> +	struct workqueue_struct *workq; /* common workqueue for codecs */
>  
>  	/* bit flags of powered codecs */
>  	unsigned long codec_powered;
> @@ -153,6 +160,17 @@ struct hdac_bus {
>  
>  	/* locks */
>  	struct mutex cmd_mutex;
> +
> +	void *private_data;
> +
> +	/* msic op flags */
> +	unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
> +
> +	/*status for controller */
> +	unsigned int rirb_error:1;      /* error in codec communication */
> +	unsigned int response_reset:1;  /* controller was reset */
> +	unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
> +	unsigned int in_reset:1;        /* during reset operation */
>  };
>  
>  int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
> @@ -178,4 +196,201 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
>  	clear_bit(codec->addr, &codec->bus->codec_powered);
>  }
>  
> +/*
> + * HD-audio contoller base device
> + */
> +struct hda_device {
> +	struct snd_dma_buffer bdl;	/* BDL buffer */
> +	u32 *posbuf;			/* position buffer pointer */
> +
> +	unsigned int bufsize;		/* size of the play buffer in bytes */
> +	unsigned int period_bytes;	 /* size of the period in bytes */
> +	unsigned int frags;		/* number for period in the play buffer */
> +	unsigned int fifo_size;		/* FIFO size */
> +	unsigned long start_wallclk;	/* start + minimum wallclk */
> +	unsigned long period_wallclk;	/* wallclk for period */
> +
> +	void __iomem *sd_addr;		/* stream descriptor pointer */
> +
> +	u32 sd_int_sta_mask;		/* stream int status mask */
> +
> +	/* pcm support */
> +	struct snd_pcm_substream *substream;	/* assigned substream,
> +						 * set in PCM open
> +						 */
> +	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 */
> +	int assigned_key;		/* last device# key assigned to */
> +
> +	unsigned int opened:1;
> +	unsigned int running:1;
> +	unsigned int irq_pending:1;
> +	unsigned int prepared:1;
> +	unsigned int locked:1;
> +	unsigned int wc_marked:1;
> +	unsigned int no_period_wakeup:1;
> +
> +	struct timecounter  tc;
> +	struct cyclecounter cc;
> +
> +	int delay_negative_threshold;
> +
> +	/* Allows dsp load to have sole access to the playback stream. */
> +	struct mutex dsp_mutex;
> +};
> +
> +/* CORB/RIRB */
> +struct hda_rb {
> +	u32 *buf;			/* CORB/RIRB buffer
> +					 * Each CORB entry is 4byte, RIRB is 8byte
> +					 */
> +	dma_addr_t addr;		/* physical address of CORB/RIRB buffer */
> +
> +	/* for RIRB */
> +	unsigned short rp, wp;		/* read/write pointers */
> +	int cmds[HDA_MAX_CODECS];	/* number of pending requests */
> +	u32 res[HDA_MAX_CODECS];	/* last read value */
> +};
> +
> +/* Functions to read/write to hda registers. */
> +/* FIXME: should we name this something else ??? */
> +struct hda_ops {
> +	void (*reg_writel)(u32 value, u32 __iomem *addr);
> +	u32 (*reg_readl)(u32 __iomem *addr);
> +	void (*reg_writew)(u16 value, u16 __iomem *addr);
> +	u16 (*reg_readw)(u16 __iomem *addr);
> +	void (*reg_writeb)(u8 value, u8 __iomem *addr);
> +	u8 (*reg_readb)(u8 __iomem *addr);
> +	/* Disable msi if supported, PCI only */
> +	int (*disable_msi_reset_irq)(struct hda *);
> +	/* Allocation ops */
> +	int (*dma_alloc_pages)(struct hda *chip,
> +			       int type,
> +			       size_t size,
> +			       struct snd_dma_buffer *buf);
> +	void (*dma_free_pages)(struct hda *chip, struct snd_dma_buffer *buf);
> +	int (*substream_alloc_pages)(struct hda *chip,
> +				     struct snd_pcm_substream *substream,
> +				     size_t size);
> +	int (*substream_free_pages)(struct hda *chip,
> +				    struct snd_pcm_substream *substream);
> +	void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream,
> +				 struct vm_area_struct *area);
> +	/* Check if current position is acceptable */
> +	int (*position_check)(struct hda *chip, struct hda_device *hda_dev);
> +};
> +
> +typedef unsigned int (*hda_get_pos_callback_t)(struct hda *, struct hda_device *);
> +typedef int (*hda_get_delay_callback_t)(struct hda *, struct hda_device *,
> +			 unsigned int pos);
> +
> +struct hda {
> +	struct pci_dev *pci; /* FIXME: should we remove PCI assumption, right now its true for us always */
> +	struct device *dev;
> +	int dev_index;
> +
> +	/* chip type specific */
> +	int driver_type;
> +	unsigned int driver_caps;
> +	int playback_streams;
> +	int playback_index_offset;
> +	int capture_streams;
> +	int capture_index_offset;
> +	int num_streams;
> +
> +	/* Register interaction. */
> +	const struct hda_ops *ops;
> +
> +	/* position adjustment callbacks */
> +	hda_get_pos_callback_t get_position[2];
> +	hda_get_delay_callback_t get_delay[2];
> +
> +	/* pci resources */
> +	unsigned long addr;
> +	void __iomem *remap_addr;
> +	int irq;
> +
> +	/* locks */
> +	spinlock_t reg_lock;
> +	struct mutex open_mutex; /* Prevents concurrent open/close operations */
> +	struct completion probe_wait;
> +
> +	/* streams (x num_streams) */
> +	struct hda_device *hda_dev;
> +
> +	/* HD codec */
> +	unsigned short codec_mask;
> +	int  codec_probe_mask; /* copied from probe_mask option */
> +	struct hdac_bus *bus;
> +	unsigned int beep_mode;
> +
> +	/* CORB/RIRB */
> +	struct hda_rb corb;
> +	struct hda_rb rirb;
> +
> +	/* CORB/RIRB and position buffers */
> +	struct snd_dma_buffer rb;
> +	struct snd_dma_buffer posbuf;
> +
> +	/* flags */
> +	const int *bdl_pos_adj;
> +	int poll_count;
> +	unsigned int running:1;
> +	unsigned int initialized:1;
> +	unsigned int single_cmd:1;
> +	unsigned int polling_mode:1;
> +	unsigned int msi:1;
> +	unsigned int probing:1; /* codec probing phase */
> +	unsigned int snoop:1;
> +	unsigned int align_buffer_size:1;
> +	unsigned int region_requested:1;
> +	unsigned int disabled:1; /* disabled by VGA-switcher */
> +
> +	/* for debugging */
> +	unsigned int last_cmd[HDA_MAX_CODECS];
> +
> +	/* reboot notifier (for mysterious hangup problem at power-down) */
> +	struct notifier_block reboot_notifier;
> +
> +	struct hda_device saved_hda_dev; /* FIXME: check if we need this */
> +};
> +
> +/* Allocation functions. */
> +int hda_alloc_stream_pages(struct hda *chip);
> +void hda_free_stream_pages(struct hda *chip);
> +
> +/* pcm helper functions */
> +int hda_setup_periods(struct hda *chip,
> +			struct snd_pcm_substream *substream,
> +			struct hda_device *dev);
> +void hda_reset_device(struct hda *chip, struct hda_device *hda_dev);
> +int hda_set_device_params(struct hda *chip, struct snd_pcm_substream *substream,
> +				unsigned int format_val);
> +void hda_set_pcm_constrains(struct hda *chip, struct snd_pcm_runtime *runtime);
> +
> +/* PCM setup */
> +static inline struct hda_device *get_hda_dev(struct snd_pcm_substream *substream)
> +{
> +	return substream->runtime->private_data;
> +}
> +unsigned int hda_get_position(struct hda *chip, struct hda_device *hda_dev,
> +		int codec_delay);
> +unsigned int hda_get_pos_lpib(struct hda *chip, struct hda_device *hda_dev);
> +unsigned int hda_get_pos_posbuf(struct hda *chip, struct hda_device *hda_dev);
> +
> +/* Stream control. */
> +void hda_stream_stop(struct hda *chip, struct hda_device *hda_dev);
> +
> +/* Allocation functions. */
> +int hda_alloc_stream_pages(struct hda *chip);
> +void hda_free_stream_pages(struct hda *chip);
> +
> +/* Low level azx interface */
> +void hda_init_chip(struct hda *chip, bool full_reset);
> +void hda_stop_chip(struct hda *chip);
> +void hda_enter_link_reset(struct hda *chip);
> +
>  #endif /* __SOUND_HDAUDIO_H */
> diff --git a/sound/hda/Makefile b/sound/hda/Makefile
> index eec5da03b41f..836c5f34b4dd 100644
> --- a/sound/hda/Makefile
> +++ b/sound/hda/Makefile
> @@ -1,4 +1,4 @@
> -snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o
> +snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o hdac_controller.o
>  
>  snd-hda-core-objs += trace.o
>  CFLAGS_trace.o := -I$(src)
> diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
> new file mode 100644
> index 000000000000..3b14f7be43da
> --- /dev/null
> +++ b/sound/hda/hdac_controller.c
> @@ -0,0 +1,1284 @@
> +/*
> + *
> + *  Implementation of Common HDA driver funcitons for Intel HD Audio.
> + *
> + *  Copyright (c) 2014 Intel Corporation
> + *  Copyright(c) 2004 Intel Corporation. All rights reserved.
> + *
> + *  Copyright (c) 2004 Takashi Iwai <tiwai at suse.de>
> + *                     PeiSen Hou <pshou at realtek.com.tw>
> + *
> + *  Modified by: KP Jeeja <jeeja.kp at intel.com>
> + *
> + *  This program is free software; you can redistribute it and/or modify it
> + *  under the terms of the GNU General Public License as published by the Free
> + *  Software Foundation; either version 2 of the License, or (at your option)
> + *  any later version.
> + *
> + *  This program is distributed in the hope that it will be useful, but WITHOUT
> + *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
> + *  more details.
> + *
> + *
> + */
> +
> +#include <linux/clocksource.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/reboot.h>
> +#include <sound/core.h>
> +#include <sound/initval.h>
> +#include <sound/hdaudio.h>
> +#include <sound/hda_registers.h>
> +
> +/* DSP lock helpers */
> +#ifdef CONFIG_SND_HDA_DSP_LOADER
> +#define dsp_lock_init(dev)	mutex_init(&(dev)->dsp_mutex)
> +#define dsp_lock(dev)		mutex_lock(&(dev)->dsp_mutex)
> +#define dsp_unlock(dev)		mutex_unlock(&(dev)->dsp_mutex)
> +#define dsp_is_locked(dev)	((dev)->locked)
> +#else
> +#define dsp_lock_init(dev)	do {} while (0)
> +#define dsp_lock(dev)		do {} while (0)
> +#define dsp_unlock(dev)		do {} while (0)
> +#define dsp_is_locked(dev)	0
> +#endif
> +
> +/*
> + * AZX stream operations.
> + */
> +
> +/* start a stream */
> +void hda_stream_start(struct hda *chip, struct hda_device *azx_dev)
> +{
> +	/* enable SIE */
> +	azx_writel(chip, INTCTL,
> +		   azx_readl(chip, INTCTL) | (1 << azx_dev->index));
> +	/* set DMA start and interrupt mask */
> +	azx_sd_writeb(chip, azx_dev, SD_CTL,
> +		      azx_sd_readb(chip, azx_dev, SD_CTL) |
> +		      SD_CTL_DMA_START | SD_INT_MASK);
> +}
> +EXPORT_SYMBOL_GPL(hda_stream_start);
> +/* stop DMA */
> +static void hda_stream_clear(struct hda *chip, struct hda_device *azx_dev)
> +{
> +	azx_sd_writeb(chip, azx_dev, SD_CTL,
> +		      azx_sd_readb(chip, azx_dev, SD_CTL) &
> +		      ~(SD_CTL_DMA_START | SD_INT_MASK));
> +	azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); /* to be sure */
> +}
> +
> +/* stop a stream */
> +void hda_stream_stop(struct hda *chip, struct hda_device *azx_dev)
> +{
> +	hda_stream_clear(chip, azx_dev);
> +	/* disable SIE */
> +	azx_writel(chip, INTCTL,
> +		   azx_readl(chip, INTCTL) & ~(1 << azx_dev->index));
> +}
> +EXPORT_SYMBOL_GPL(hda_stream_stop);
> +
> +/* reset stream */
> +void hda_stream_reset(struct hda *chip, struct hda_device *azx_dev)
> +{
> +	unsigned char val;
> +	int timeout;
> +
> +	hda_stream_clear(chip, azx_dev);
> +
> +	azx_sd_writeb(chip, azx_dev, SD_CTL,
> +		      azx_sd_readb(chip, azx_dev, SD_CTL) |
> +		      SD_CTL_STREAM_RESET);
> +	udelay(3);
> +	timeout = 300;
> +	while (!((val = azx_sd_readb(chip, azx_dev, SD_CTL)) &
> +		 SD_CTL_STREAM_RESET) && --timeout)
> +		;
> +	val &= ~SD_CTL_STREAM_RESET;
> +	azx_sd_writeb(chip, azx_dev, SD_CTL, val);
> +	udelay(3);
> +
> +	timeout = 300;
> +	/* waiting for hardware to report that the stream is out of reset */
> +	while (((val = azx_sd_readb(chip, azx_dev, SD_CTL)) &
> +		SD_CTL_STREAM_RESET) && --timeout)
> +		;
> +
> +	/* reset first position - may not be synced with hw at this time */
> +	*azx_dev->posbuf = 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_stream_reset);
> +
> +/*
> + * set up the SD for streaming
> + */
> +int hda_setup_controller(struct hda *chip, struct hda_device *azx_dev)
> +{
> +	unsigned int val;
> +	/* make sure the run bit is zero for SD */
> +	hda_stream_clear(chip, azx_dev);
> +	/* program the stream_tag */
> +	val = azx_sd_readl(chip, azx_dev, SD_CTL);
> +	val = (val & ~SD_CTL_STREAM_TAG_MASK) |
> +		(azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT);
> +	azx_sd_writel(chip, azx_dev, SD_CTL, val);
> +
> +	/* program the length of samples in cyclic buffer */
> +	azx_sd_writel(chip, azx_dev, SD_CBL, azx_dev->bufsize);
> +
> +	/* program the stream format */
> +	/* this value needs to be the same as the one programmed */
> +	azx_sd_writew(chip, azx_dev, SD_FORMAT, azx_dev->format_val);
> +
> +	/* program the stream LVI (last valid index) of the BDL */
> +	azx_sd_writew(chip, azx_dev, SD_LVI, azx_dev->frags - 1);
> +
> +	/* program the BDL address */
> +	/* lower BDL address */
> +	azx_sd_writel(chip, azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr);
> +	/* upper BDL address */
> +	azx_sd_writel(chip, azx_dev, SD_BDLPU,
> +		      upper_32_bits(azx_dev->bdl.addr));
> +
> +	/* enable the position buffer */
> +	if (chip->get_position[0] != hda_get_pos_lpib ||
> +	    chip->get_position[1] != hda_get_pos_lpib) {
> +		if (!(azx_readl(chip, DPLBASE) & AZX_DPLBASE_ENABLE))
> +			azx_writel(chip, DPLBASE,
> +				(u32)chip->posbuf.addr | AZX_DPLBASE_ENABLE);
> +	}
> +
> +	/* set the interrupt enable bits in the descriptor control register */
> +	azx_sd_writel(chip, azx_dev, SD_CTL,
> +		      azx_sd_readl(chip, azx_dev, SD_CTL) | SD_INT_MASK);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_setup_controller);
> +
> +/* assign a stream for the PCM */
> +struct hda_device *
> +hda_assign_device(struct hda *chip, struct snd_pcm_substream *substream)
> +{
> +	int dev, i, nums;
> +	struct hda_device *res = NULL;
> +	/* make a non-zero unique key for the substream */
> +	int key = (substream->pcm->device << 16) | (substream->number << 2) |
> +		(substream->stream + 1);
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		dev = chip->playback_index_offset;
> +		nums = chip->playback_streams;
> +	} else {
> +		dev = chip->capture_index_offset;
> +		nums = chip->capture_streams;
> +	}
> +	for (i = 0; i < nums; i++, dev++) {
> +		struct hda_device *azx_dev = &chip->hda_dev[dev];
> +
> +		dsp_lock(azx_dev);
> +		if (!azx_dev->opened && !dsp_is_locked(azx_dev)) {
> +			if (azx_dev->assigned_key == key) {
> +				azx_dev->opened = 1;
> +				azx_dev->assigned_key = key;
> +				dsp_unlock(azx_dev);
> +				return azx_dev;
> +			}
> +			if (!res ||
> +			    (chip->driver_caps & AZX_DCAPS_REVERSE_ASSIGN))
> +				res = azx_dev;
> +		}
> +		dsp_unlock(azx_dev);
> +	}
> +	if (res) {
> +		dsp_lock(res);
> +		res->opened = 1;
> +		res->assigned_key = key;
> +		dsp_unlock(res);
> +	}
> +	return res;
> +}
> +EXPORT_SYMBOL_GPL(hda_assign_device);
> +
> +/* release the assigned stream */
> +void hda_release_device(struct hda_device *hda_dev)
> +{
> +	hda_dev->opened = 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_release_device);
> +
> +/*
> + * set up a BDL entry
> + */
> +int hda_setup_bdle(struct hda *chip,
> +		      struct snd_dma_buffer *dmab,
> +		      struct hda_device *azx_dev, u32 **bdlp,
> +		      int ofs, int size, int with_ioc)
> +{
> +	u32 *bdl = *bdlp;
> +
> +	while (size > 0) {
> +		dma_addr_t addr;
> +		int chunk;
> +
> +		if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES)
> +			return -EINVAL;
> +
> +		addr = snd_sgbuf_get_addr(dmab, ofs);
> +		dev_dbg(chip->dev, "buffer address=%#llx\n", (u64)addr);
> +		/* program the address field of the BDL entry */
> +		bdl[0] = cpu_to_le32((u32)addr);
> +		bdl[1] = cpu_to_le32(upper_32_bits(addr));
> +		/* program the size field of the BDL entry */
> +		chunk = snd_sgbuf_get_chunk_size(dmab, ofs, size);
> +		/* one BDLE cannot cross 4K boundary on CTHDA chips */
> +		if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
> +			u32 remain = 0x1000 - (ofs & 0xfff);
> +
> +			if (chunk > remain)
> +				chunk = remain;
> +		}
> +		bdl[2] = cpu_to_le32(chunk);
> +		/* program the IOC to enable interrupt
> +		 * only when the whole fragment is processed
> +		 */
> +		size -= chunk;
> +		bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01);
> +		bdl += 4;
> +		azx_dev->frags++;
> +		ofs += chunk;
> +	}
> +	*bdlp = bdl;
> +	dev_dbg(chip->dev, "bdl: 0x%p, ofs:0x%x\n", bdl, ofs);
> +	return ofs;
> +}
> +EXPORT_SYMBOL_GPL(hda_setup_bdle);
> +
> +/*
> + * set up BDL entries
> + */
> +int hda_setup_periods(struct hda *chip,
> +			     struct snd_pcm_substream *substream,
> +			     struct hda_device *azx_dev)
> +{
> +	u32 *bdl;
> +	int i, ofs, periods, period_bytes;
> +	int pos_adj = 0;
> +
> +	/* reset BDL address */
> +	azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
> +	azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
> +
> +	period_bytes = azx_dev->period_bytes;
> +	periods = azx_dev->bufsize / period_bytes;
> +
> +	/* program the initial BDL entries */
> +	bdl = (u32 *)azx_dev->bdl.area;
> +	ofs = 0;
> +	azx_dev->frags = 0;
> +
> +	if (chip->bdl_pos_adj)
> +		pos_adj = chip->bdl_pos_adj[chip->dev_index];
> +	if (!azx_dev->no_period_wakeup && pos_adj > 0) {
> +		struct snd_pcm_runtime *runtime = substream->runtime;
> +		int pos_align = pos_adj;
> +
> +		pos_adj = (pos_adj * runtime->rate + 47999) / 48000;
> +		if (!pos_adj)
> +			pos_adj = pos_align;
> +		else
> +			pos_adj = ((pos_adj + pos_align - 1) / pos_align) *
> +				pos_align;
> +		pos_adj = frames_to_bytes(runtime, pos_adj);
> +		if (pos_adj >= period_bytes) {
> +			dev_warn(chip->dev, "Too big adjustment %d\n",
> +				 pos_adj);
> +			pos_adj = 0;
> +		} else {
> +			ofs = hda_setup_bdle(chip, snd_pcm_get_dma_buf(substream),
> +					 azx_dev,
> +					 &bdl, ofs, pos_adj, true);
> +			if (ofs < 0)
> +				goto error;
> +		}
> +	} else
> +		pos_adj = 0;
> +
> +	for (i = 0; i < periods; i++) {
> +		if (i == periods - 1 && pos_adj)
> +			ofs = hda_setup_bdle(chip, snd_pcm_get_dma_buf(substream),
> +					 azx_dev, &bdl, ofs,
> +					 period_bytes - pos_adj, 0);
> +		else
> +			ofs = hda_setup_bdle(chip, snd_pcm_get_dma_buf(substream),
> +					 azx_dev, &bdl, ofs,
> +					 period_bytes,
> +					 !azx_dev->no_period_wakeup);
> +		if (ofs < 0)
> +			goto error;
> +	}
> +	return 0;
> +
> + error:
> +	dev_err(chip->dev, "Too many BDL entries: buffer=%d, period=%d\n",
> +		azx_dev->bufsize, period_bytes);
> +	return -EINVAL;
> +}
> +EXPORT_SYMBOL_GPL(hda_setup_periods);
> +
> +unsigned int hda_get_pos_lpib(struct hda *chip, struct hda_device *azx_dev)
> +{
> +	return azx_sd_readl(chip, azx_dev, SD_LPIB);
> +}
> +EXPORT_SYMBOL_GPL(hda_get_pos_lpib);
> +
> +unsigned int hda_get_pos_posbuf(struct hda *chip, struct hda_device *azx_dev)
> +{
> +	return le32_to_cpu(*azx_dev->posbuf);
> +}
> +EXPORT_SYMBOL_GPL(hda_get_pos_posbuf);
> +
> +unsigned int hda_get_position(struct hda *chip,
> +			      struct hda_device *azx_dev, int codec_delay)
> +{
> +	struct snd_pcm_substream *substream = azx_dev->substream;
> +	unsigned int pos;
> +	int stream = substream->stream;
> +	int delay = 0;
> +
> +	if (chip->get_position[stream])
> +		pos = chip->get_position[stream](chip, azx_dev);
> +	else /* use the position buffer as default */
> +		pos = hda_get_pos_posbuf(chip, azx_dev);
> +
> +	if (pos >= azx_dev->bufsize)
> +		pos = 0;
> +
> +	if (substream->runtime) {
> +		if (chip->get_delay[stream])
> +			delay += chip->get_delay[stream](chip, azx_dev, pos);
> +			delay += codec_delay;
> +		substream->runtime->delay = delay;
> +	}
> +
> +	return pos;
> +}
> +EXPORT_SYMBOL_GPL(hda_get_position);
> +
> +void hda_reset_device(struct hda *chip, struct hda_device *azx_dev)
> +{
> +	azx_sd_writel(chip, azx_dev, SD_BDLPL, 0);
> +	azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
> +	azx_sd_writel(chip, azx_dev, SD_CTL, 0);
> +	azx_dev->bufsize = 0;
> +	azx_dev->period_bytes = 0;
> +	azx_dev->format_val = 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_reset_device);
> +
> +int hda_set_device_params(struct hda *chip, struct snd_pcm_substream *substream,
> +				unsigned int format_val)
> +{
> +
> +	unsigned int bufsize, period_bytes, stream_tag;
> +	struct hda_device *azx_dev = get_hda_dev(substream);
> +	struct snd_pcm_runtime *runtime = substream->runtime;
> +	int err;
> +
> +	bufsize = snd_pcm_lib_buffer_bytes(substream);
> +	period_bytes = snd_pcm_lib_period_bytes(substream);
> +
> +	dev_dbg(chip->dev, "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
> +		bufsize, format_val);
> +
> +	if (bufsize != azx_dev->bufsize ||
> +	    period_bytes != azx_dev->period_bytes ||
> +	    format_val != azx_dev->format_val ||
> +	    runtime->no_period_wakeup != azx_dev->no_period_wakeup) {
> +		azx_dev->bufsize = bufsize;
> +		azx_dev->period_bytes = period_bytes;
> +		azx_dev->format_val = format_val;
> +		azx_dev->no_period_wakeup = runtime->no_period_wakeup;
> +		err = hda_setup_periods(chip, substream, azx_dev);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	/* when LPIB delay correction gives a small negative value,
> +	 * we ignore it; currently set the threshold statically to
> +	 * 64 frames
> +	 */
> +	if (runtime->period_size > 64)
> +		azx_dev->delay_negative_threshold = -frames_to_bytes(runtime, 64);
> +	else
> +		azx_dev->delay_negative_threshold = 0;
> +
> +	/* wallclk has 24Mhz clock source */
> +	azx_dev->period_wallclk = (((runtime->period_size * 24000) /
> +						runtime->rate) * 1000);
> +	hda_setup_controller(chip, azx_dev);
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		azx_dev->fifo_size =
> +			azx_sd_readw(chip, azx_dev, SD_FIFOSIZE) + 1;
> +	else
> +		azx_dev->fifo_size = 0;
> +
> +	stream_tag = azx_dev->stream_tag;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_set_device_params);
> +
> +void hda_set_pcm_constrains(struct hda *chip, struct snd_pcm_runtime *runtime)
> +{
> +	int buff_step;
> +
> +	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
> +
> +	/* avoid wrap-around with wall-clock */
> +	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_TIME,
> +				     20,
> +				     178000000);
> +
> +	if (chip->align_buffer_size)
> +		/* constrain buffer sizes to be multiple of 128
> +		   bytes. This is more efficient in terms of memory
> +		   access but isn't required by the HDA spec and
> +		   prevents users from specifying exact period/buffer
> +		   sizes. For example for 44.1kHz, a period size set
> +		   to 20ms will be rounded to 19.59ms. */
> +		buff_step = 128;
> +	else
> +		/* Don't enforce steps on buffer sizes, still need to
> +		   be multiple of 4 bytes (HDA spec). Tested on Intel
> +		   HDA controllers, may not work on all devices where
> +		   option needs to be disabled */
> +		buff_step = 4;
> +
> +	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
> +				   buff_step);
> +	snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
> +				   buff_step);
> +
> +}
> +EXPORT_SYMBOL_GPL(hda_set_pcm_constrains);
> +
> +/*
> + * CORB / RIRB interface
> + */
> +static int hda_alloc_cmd_io(struct hda *chip)
> +{
> +	int err;
> +
> +	/* single page (at least 4096 bytes) must suffice for both ringbuffes */
> +	err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
> +					 PAGE_SIZE, &chip->rb);
> +	if (err < 0)
> +		dev_err(chip->dev, "cannot allocate CORB/RIRB\n");
> +	return err;
> +}
> +EXPORT_SYMBOL_GPL(hda_alloc_cmd_io);
> +
> +static void hda_init_cmd_io(struct hda *chip)
> +{
> +	int timeout;
> +
> +	spin_lock_irq(&chip->reg_lock);
> +	/* CORB set up */
> +	chip->corb.addr = chip->rb.addr;
> +	chip->corb.buf = (u32 *)chip->rb.area;
> +	azx_writel(chip, CORBLBASE, (u32)chip->corb.addr);
> +	azx_writel(chip, CORBUBASE, upper_32_bits(chip->corb.addr));
> +
> +	/* set the corb size to 256 entries (ULI requires explicitly) */
> +	azx_writeb(chip, CORBSIZE, 0x02);
> +	/* set the corb write pointer to 0 */
> +	azx_writew(chip, CORBWP, 0);
> +
> +	/* reset the corb hw read pointer */
> +	azx_writew(chip, CORBRP, AZX_CORBRP_RST);
> +	if (!(chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR)) {
> +		for (timeout = 1000; timeout > 0; timeout--) {
> +			if ((azx_readw(chip, CORBRP) & AZX_CORBRP_RST) == AZX_CORBRP_RST)
> +				break;
> +			udelay(1);
> +		}
> +		if (timeout <= 0)
> +			dev_err(chip->dev, "CORB reset timeout#1, CORBRP = %d\n",
> +				azx_readw(chip, CORBRP));
> +
> +		azx_writew(chip, CORBRP, 0);
> +		for (timeout = 1000; timeout > 0; timeout--) {
> +			if (azx_readw(chip, CORBRP) == 0)
> +				break;
> +			udelay(1);
> +		}
> +		if (timeout <= 0)
> +			dev_err(chip->dev, "CORB reset timeout#2, CORBRP = %d\n",
> +				azx_readw(chip, CORBRP));
> +	}
> +
> +	/* enable corb dma */
> +	azx_writeb(chip, CORBCTL, AZX_CORBCTL_RUN);
> +
> +	/* RIRB set up */
> +	chip->rirb.addr = chip->rb.addr + 2048;
> +	chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
> +	chip->rirb.wp = chip->rirb.rp = 0;
> +	memset(chip->rirb.cmds, 0, sizeof(chip->rirb.cmds));
> +	azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
> +	azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
> +
> +	/* set the rirb size to 256 entries (ULI requires explicitly) */
> +	azx_writeb(chip, RIRBSIZE, 0x02);
> +	/* reset the rirb hw write pointer */
> +	azx_writew(chip, RIRBWP, AZX_RIRBWP_RST);
> +	/* set N=1, get RIRB response interrupt for new entry */
> +	if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND)
> +		azx_writew(chip, RINTCNT, 0xc0);
> +	else
> +		azx_writew(chip, RINTCNT, 1);
> +	/* enable rirb dma and response irq */
> +	azx_writeb(chip, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
> +	spin_unlock_irq(&chip->reg_lock);
> +}
> +EXPORT_SYMBOL_GPL(hda_init_cmd_io);
> +
> +static void hda_free_cmd_io(struct hda *chip)
> +{
> +	spin_lock_irq(&chip->reg_lock);
> +	/* disable ringbuffer DMAs */
> +	azx_writeb(chip, RIRBCTL, 0);
> +	azx_writeb(chip, CORBCTL, 0);
> +	spin_unlock_irq(&chip->reg_lock);
> +}
> +EXPORT_SYMBOL_GPL(hda_free_cmd_io);
> +
> +static unsigned int hda_command_addr(u32 cmd)
> +{
> +	unsigned int addr = cmd >> 28;
> +
> +	if (addr >= HDA_MAX_CODECS) {
> +		snd_BUG();
> +		addr = 0;
> +	}
> +
> +	return addr;
> +}
> +
> +/* send a command */
> +static int hda_corb_send_cmd(struct hdac_bus *bus, u32 val)
> +{
> +	struct hda *chip = bus->private_data;
> +	unsigned int addr = hda_command_addr(val);
> +	unsigned int wp, rp;
> +
> +	spin_lock_irq(&chip->reg_lock);
> +
> +	/* add command to corb */
> +	wp = azx_readw(chip, CORBWP);
> +	if (wp == 0xffff) {
> +		/* something wrong, controller likely turned to D3 */
> +		spin_unlock_irq(&chip->reg_lock);
> +		return -EIO;
> +	}
> +	wp++;
> +	wp %= AZX_MAX_CORB_ENTRIES;
> +
> +	rp = azx_readw(chip, CORBRP);
> +	if (wp == rp) {
> +		/* oops, it's full */
> +		spin_unlock_irq(&chip->reg_lock);
> +		return -EAGAIN;
> +	}
> +
> +	chip->rirb.cmds[addr]++;
> +	chip->corb.buf[wp] = cpu_to_le32(val);
> +	azx_writew(chip, CORBWP, wp);
> +
> +	spin_unlock_irq(&chip->reg_lock);
> +
> +	return 0;
> +}
> +
> +#define AZX_RIRB_EX_UNSOL_EV	(1<<4)
> +
> +/**
> + * hda_queue_unsol_event - add an unsolicited event to queue
> + * @bus: the BUS
> + * @res: unsolicited event (lower 32bit of RIRB entry)
> + * @res_ex: codec addr and flags (upper 32bit or RIRB entry)
> + *
> + * Adds the given event to the queue.  The events are processed in
> + * the workqueue asynchronously.  Call this function in the interrupt
> + * hanlder when RIRB receives an unsolicited event.
> + *
> + * Returns 0 if successful, or a negative error code.
> + */
> +int hda_queue_unsol_event(struct hdac_bus *bus, u32 res, u32 res_ex)
> +{
> +	unsigned int wp;
> +
> +	if (!bus)
> +		return 0;
> +
> +	wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE;
> +
> +	wp <<= 1;
> +	bus->unsol_queue[wp] = res;
> +	bus->unsol_queue[wp + 1] = res_ex;
> +
> +	queue_work(bus->workq, &bus->unsol_work);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_queue_unsol_event);
> +
> +/* retrieve RIRB entry - called from interrupt handler */
> +static void hda_update_rirb(struct hda *chip)
> +{
> +	unsigned int rp, wp;
> +	unsigned int addr;
> +	u32 res, res_ex;
> +
> +	wp = azx_readw(chip, RIRBWP);
> +	if (wp == 0xffff) {
> +		/* something wrong, controller likely turned to D3 */
> +		return;
> +	}
> +
> +	if (wp == chip->rirb.wp)
> +		return;
> +	chip->rirb.wp = wp;
> +
> +	while (chip->rirb.rp != wp) {
> +		chip->rirb.rp++;
> +		chip->rirb.rp %= AZX_MAX_RIRB_ENTRIES;
> +
> +		rp = chip->rirb.rp << 1; /* an RIRB entry is 8-bytes */
> +		res_ex = le32_to_cpu(chip->rirb.buf[rp + 1]);
> +		res = le32_to_cpu(chip->rirb.buf[rp]);
> +		addr = res_ex & 0xf;
> +		if ((addr >= HDA_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) {
> +			dev_err(chip->dev, "spurious response %#x:%#x, rp = %d, wp = %d",
> +				res, res_ex,
> +				chip->rirb.rp, wp);
> +			snd_BUG();
> +		} else if (res_ex & AZX_RIRB_EX_UNSOL_EV)
> +			hda_queue_unsol_event(chip->bus, res, res_ex);
> +		else if (chip->rirb.cmds[addr]) {
> +			chip->rirb.res[addr] = res;
> +			smp_wmb();
> +			chip->rirb.cmds[addr]--;
> +		} else if (printk_ratelimit()) {
> +			dev_err(chip->dev, "spurious response %#x:%#x, last cmd=%#08x\n",
> +				res, res_ex,
> +				chip->last_cmd[addr]);
> +		}
> +	}
> +}
> +
> +/* receive a response */
> +static unsigned int hda_rirb_get_response(struct hdac_bus *bus,
> +					  unsigned int addr)
> +{
> +	struct hda *chip = bus->private_data;
> +	unsigned long timeout;
> +	unsigned long loopcounter;
> +	int do_poll = 0;
> +
> + again:
> +	timeout = jiffies + msecs_to_jiffies(1000);
> +
> +	for (loopcounter = 0;; loopcounter++) {
> +		if (chip->polling_mode || do_poll) {
> +			spin_lock_irq(&chip->reg_lock);
> +			hda_update_rirb(chip);
> +			spin_unlock_irq(&chip->reg_lock);
> +		}
> +		if (!chip->rirb.cmds[addr]) {
> +			smp_rmb();
> +			bus->rirb_error = 0;
> +
> +			if (!do_poll)
> +				chip->poll_count = 0;
> +			return chip->rirb.res[addr]; /* the last value */
> +		}
> +		if (time_after(jiffies, timeout))
> +			break;
> +		else {
> +			udelay(10);
> +			cond_resched();
> +		}
> +	}
> +
> +	if (!bus->no_response_fallback)
> +		return -1;
> +
> +	if (!chip->polling_mode && chip->poll_count < 2) {
> +		dev_dbg(chip->dev,
> +			"azx_get_response timeout, polling the codec once: last cmd=0x%08x\n",
> +			chip->last_cmd[addr]);
> +		do_poll = 1;
> +		chip->poll_count++;
> +		goto again;
> +	}
> +
> +
> +	if (!chip->polling_mode) {
> +		dev_warn(chip->dev,
> +			 "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n",
> +			 chip->last_cmd[addr]);
> +		chip->polling_mode = 1;
> +		goto again;
> +	}
> +
> +	if (chip->msi) {
> +		dev_warn(chip->dev,
> +			 "No response from codec, disabling MSI: last cmd=0x%08x\n",
> +			 chip->last_cmd[addr]);
> +		if (chip->ops->disable_msi_reset_irq(chip) &&
> +		    chip->ops->disable_msi_reset_irq(chip) < 0) {
> +			bus->rirb_error = 1;
> +			return -1;
> +		}
> +		goto again;
> +	}
> +
> +	if (chip->probing) {
> +		/* If this critical timeout happens during the codec probing
> +		 * phase, this is likely an access to a non-existing codec
> +		 * slot.  Better to return an error and reset the system.
> +		 */
> +		return -1;
> +	}
> +
> +	/* a fatal communication error; need either to reset or to fallback
> +	 * to the single_cmd mode
> +	 */
> +	bus->rirb_error = 1;
> +	if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
> +		bus->response_reset = 1;
> +		return -1; /* give a chance to retry */
> +	}
> +
> +	dev_err(chip->dev,
> +		"azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n",
> +		chip->last_cmd[addr]);
> +	chip->single_cmd = 1;
> +	bus->response_reset = 0;
> +	/* release CORB/RIRB */
> +	hda_free_cmd_io(chip);
> +	/* disable unsolicited responses */
> +	azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_UNSOL);
> +	return -1;
> +}
> +
> +/*
> + * Use the single immediate command instead of CORB/RIRB for simplicity
> + *
> + * Note: according to Intel, this is not preferred use.  The command was
> + *       intended for the BIOS only, and may get confused with unsolicited
> + *       responses.  So, we shouldn't use it for normal operation from the
> + *       driver.
> + *       I left the codes, however, for debugging/testing purposes.
> + */
> +
> +/* receive a response */
> +static int hda_single_wait_for_response(struct hda *chip, unsigned int addr)
> +{
> +	int timeout = 50;
> +
> +	while (timeout--) {
> +		/* check IRV busy bit */
> +		if (azx_readw(chip, IRS) & AZX_IRS_VALID) {
> +			/* reuse rirb.res as the response return value */
> +			chip->rirb.res[addr] = azx_readl(chip, IR);
> +			return 0;
> +		}
> +		udelay(1);
> +	}
> +	if (printk_ratelimit())
> +		dev_dbg(chip->dev, "get_response timeout: IRS=0x%x\n",
> +			azx_readw(chip, IRS));
> +	chip->rirb.res[addr] = -1;
> +	return -EIO;
> +}
> +
> +/* send a command */
> +static int hda_single_send_cmd(struct hdac_bus *bus, u32 val)
> +{
> +	struct hda *chip = bus->private_data;
> +	unsigned int addr = hda_command_addr(val);
> +	int timeout = 50;
> +
> +	bus->rirb_error = 0;
> +	while (timeout--) {
> +		/* check ICB busy bit */
> +		if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) {
> +			/* Clear IRV valid bit */
> +			azx_writew(chip, IRS, azx_readw(chip, IRS) |
> +				   AZX_IRS_VALID);
> +			azx_writel(chip, IC, val);
> +			azx_writew(chip, IRS, azx_readw(chip, IRS) |
> +				   AZX_IRS_BUSY);
> +			return hda_single_wait_for_response(chip, addr);
> +		}
> +		udelay(1);
> +	}
> +	if (printk_ratelimit())
> +		dev_dbg(chip->dev,
> +			"send_cmd timeout: IRS=0x%x, val=0x%x\n",
> +			azx_readw(chip, IRS), val);
> +	return -EIO;
> +}
> +
> +/* receive a response */
> +static unsigned int hda_single_get_response(struct hdac_bus *bus,
> +					    unsigned int addr)
> +{
> +	struct hda *chip = bus->private_data;
> +
> +	return chip->rirb.res[addr];
> +}
> +
> +/*
> + * 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 */
> +int hda_send_cmd(struct hdac_bus *bus, unsigned int val)
> +{
> +	struct hda *chip = bus->private_data;
> +
> +	if (chip->disabled)
> +		return 0;
> +	chip->last_cmd[hda_command_addr(val)] = val;
> +	if (chip->single_cmd)
> +		return hda_single_send_cmd(bus, val);
> +	else
> +		return hda_corb_send_cmd(bus, val);
> +}
> +EXPORT_SYMBOL_GPL(hda_send_cmd);
> +
> +/* get a response */
> +unsigned int hda_get_response(struct hdac_bus *bus,
> +				     unsigned int addr)
> +{
> +	struct hda *chip = bus->private_data;
> +
> +	if (chip->disabled)
> +		return 0;
> +	if (chip->single_cmd)
> +		return hda_single_get_response(bus, addr);
> +	else
> +		return hda_rirb_get_response(bus, addr);
> +}
> +EXPORT_SYMBOL_GPL(hda_get_response);
> +
> +void hda_bus_reset(struct hdac_bus *bus)
> +{
> +	struct hda *chip = bus->private_data;
> +
> +	bus->in_reset = 1;
> +	hda_stop_chip(chip);
> +	hda_init_chip(chip, true);
> +	bus->in_reset = 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_bus_reset);
> +
> +int hda_alloc_stream_pages(struct hda *chip)
> +{
> +	int i, err;
> +
> +	for (i = 0; i < chip->num_streams; i++) {
> +		dsp_lock_init(&chip->hda_dev[i]);
> +		/* allocate memory for the BDL for each stream */
> +		err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
> +						 BDL_SIZE,
> +						 &chip->hda_dev[i].bdl);
> +		if (err < 0) {
> +			dev_err(chip->dev, "cannot allocate BDL\n");
> +			return -ENOMEM;
> +		}
> +	}
> +	/* allocate memory for the position buffer */
> +	err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV,
> +					 chip->num_streams * 8, &chip->posbuf);
> +	if (err < 0) {
> +		dev_err(chip->dev, "cannot allocate posbuf\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* allocate CORB/RIRB */
> +	err = hda_alloc_cmd_io(chip);
> +	if (err < 0)
> +		return err;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_alloc_stream_pages);
> +
> +void hda_free_stream_pages(struct hda *chip)
> +{
> +	int i;
> +
> +	if (chip->hda_dev) {
> +		for (i = 0; i < chip->num_streams; i++)
> +			if (chip->hda_dev[i].bdl.area)
> +				chip->ops->dma_free_pages(
> +					chip, &chip->hda_dev[i].bdl);
> +	}
> +	if (chip->rb.area)
> +		chip->ops->dma_free_pages(chip, &chip->rb);
> +	if (chip->posbuf.area)
> +		chip->ops->dma_free_pages(chip, &chip->posbuf);
> +}
> +EXPORT_SYMBOL_GPL(hda_free_stream_pages);
> +
> +/*
> + * Lowlevel interface
> + */
> +
> +/* enter link reset */
> +void hda_enter_link_reset(struct hda *chip)
> +{
> +	unsigned long timeout;
> +
> +	/* reset controller */
> +	azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_RESET);
> +
> +	timeout = jiffies + msecs_to_jiffies(100);
> +	while ((azx_readb(chip, GCTL) & AZX_GCTL_RESET) &&
> +			time_before(jiffies, timeout))
> +		usleep_range(500, 1000);
> +}
> +EXPORT_SYMBOL_GPL(hda_enter_link_reset);
> +
> +/* exit link reset */
> +void hda_exit_link_reset(struct hda *chip)
> +{
> +	unsigned long timeout;
> +
> +	azx_writeb(chip, GCTL, azx_readb(chip, GCTL) | AZX_GCTL_RESET);
> +
> +	timeout = jiffies + msecs_to_jiffies(100);
> +	while (!azx_readb(chip, GCTL) &&
> +			time_before(jiffies, timeout))
> +		usleep_range(500, 1000);
> +}
> +EXPORT_SYMBOL_GPL(hda_exit_link_reset);
> +
> +/* reset codec link */
> +static int hda_reset(struct hda *chip, bool full_reset)
> +{
> +	if (!full_reset)
> +		goto __skip;
> +
> +	/* clear STATESTS */
> +	azx_writew(chip, STATESTS, STATESTS_INT_MASK);
> +
> +	/* reset controller */
> +	hda_enter_link_reset(chip);
> +
> +	/* delay for >= 100us for codec PLL to settle per spec
> +	 * Rev 0.9 section 5.5.1
> +	 */
> +	usleep_range(500, 1000);
> +
> +	/* Bring controller out of reset */
> +	hda_exit_link_reset(chip);
> +
> +	/* Brent Chartrand said to wait >= 540us for codecs to initialize */
> +	usleep_range(1000, 1200);
> +
> +__skip:
> +	/* check to see if controller is ready */
> +	if (!azx_readb(chip, GCTL)) {
> +		dev_dbg(chip->dev, "azx_reset: controller not ready!\n");
> +		return -EBUSY;
> +	}
> +
> +	/* Accept unsolicited responses */
> +	if (!chip->single_cmd)
> +		azx_writel(chip, GCTL, azx_readl(chip, GCTL) |
> +			   AZX_GCTL_UNSOL);
> +
> +	/* detect codecs */
> +	if (!chip->codec_mask) {
> +		chip->codec_mask = azx_readw(chip, STATESTS);
> +		dev_dbg(chip->dev, "codec_mask = 0x%x\n",
> +			chip->codec_mask);
> +	}
> +
> +	return 0;
> +}
> +
> +/* enable interrupts */
> +static void hda_int_enable(struct hda *chip)
> +{
> +	/* enable controller CIE and GIE */
> +	azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) |
> +		   AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN);
> +}
> +
> +/* disable interrupts */
> +static void hda_int_disable(struct hda *chip)
> +{
> +	int i;
> +
> +	/* disable interrupts in stream descriptor */
> +	for (i = 0; i < chip->num_streams; i++) {
> +		struct hda_device *azx_dev = &chip->hda_dev[i];
> +
> +		azx_sd_writeb(chip, azx_dev, SD_CTL,
> +			      azx_sd_readb(chip, azx_dev, SD_CTL) &
> +					~SD_INT_MASK);
> +	}
> +
> +	/* disable SIE for all streams */
> +	azx_writeb(chip, INTCTL, 0);
> +
> +	/* disable controller CIE and GIE */
> +	azx_writel(chip, INTCTL, azx_readl(chip, INTCTL) &
> +		   ~(AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN));
> +}
> +
> +/* clear interrupts */
> +static void hda_int_clear(struct hda *chip)
> +{
> +	int i;
> +
> +	/* clear stream status */
> +	for (i = 0; i < chip->num_streams; i++) {
> +		struct hda_device *azx_dev = &chip->hda_dev[i];
> +
> +		azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK);
> +	}
> +
> +	/* clear STATESTS */
> +	azx_writew(chip, STATESTS, STATESTS_INT_MASK);
> +
> +	/* clear rirb status */
> +	azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
> +
> +	/* clear int status */
> +	azx_writel(chip, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM);
> +}
> +
> +/*
> + * reset and start the controller registers
> + */
> +void hda_init_chip(struct hda *chip, bool full_reset)
> +{
> +	if (chip->initialized)
> +		return;
> +
> +	/* reset controller */
> +	hda_reset(chip, full_reset);
> +
> +	/* initialize interrupts */
> +	hda_int_clear(chip);
> +	hda_int_enable(chip);
> +
> +	/* initialize the codec command I/O */
> +	if (!chip->single_cmd)
> +		hda_init_cmd_io(chip);
> +
> +	/* program the position buffer */
> +	azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
> +	azx_writel(chip, DPUBASE, upper_32_bits(chip->posbuf.addr));
> +
> +	chip->initialized = 1;
> +}
> +EXPORT_SYMBOL_GPL(hda_init_chip);
> +
> +void hda_stop_chip(struct hda *chip)
> +{
> +	if (!chip->initialized)
> +		return;
> +
> +	/* disable interrupts */
> +	hda_int_disable(chip);
> +	hda_int_clear(chip);
> +
> +	/* disable CORB/RIRB */
> +	hda_free_cmd_io(chip);
> +
> +	/* disable position buffer */
> +	azx_writel(chip, DPLBASE, 0);
> +	azx_writel(chip, DPUBASE, 0);
> +
> +	chip->initialized = 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_stop_chip);
> +
> +/*
> + * interrupt handler
> + */
> +irqreturn_t hda_interrupt(int irq, void *dev_id)
> +{
> +	struct hda *chip = dev_id;
> +	u32 status;
> +
> +#ifdef CONFIG_PM_RUNTIME
> +	if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME)
> +		if (!pm_runtime_active(chip->dev))
> +			return IRQ_NONE;
> +#endif
> +
> +	spin_lock(&chip->reg_lock);
> +
> +	if (chip->disabled) {
> +		spin_unlock(&chip->reg_lock);
> +		return IRQ_NONE;
> +	}
> +
> +	status = azx_readl(chip, INTSTS);
> +	if (status == 0 || status == 0xffffffff) {
> +		spin_unlock(&chip->reg_lock);
> +		return IRQ_NONE;
> +	}
> +	spin_unlock(&chip->reg_lock);
> +
> +	return IRQ_WAKE_THREAD;
> +}
> +EXPORT_SYMBOL_GPL(hda_interrupt);
> +
> +
> +irqreturn_t hda_threaded_handler(int irq, void *dev_id)
> +{
> +	struct hda *chip = dev_id;
> +	struct hda_device *azx_dev;
> +	u32 status;
> +	u8 sd_status;
> +	int i;
> +	unsigned long cookie;
> +
> +	status = azx_readl(chip, INTSTS);
> +	spin_lock_irqsave(&chip->reg_lock, cookie);
> +	for (i = 0; i < chip->num_streams; i++) {
> +		azx_dev = &chip->hda_dev[i];
> +		if (status & azx_dev->sd_int_sta_mask) {
> +			sd_status = azx_sd_readb(chip, azx_dev, SD_STS);
> +			azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK);
> +			if (!azx_dev->substream || !azx_dev->running ||
> +			    !(sd_status & SD_INT_COMPLETE))
> +				continue;
> +			/* check whether this IRQ is really acceptable */
> +			if (!chip->ops->position_check ||
> +			    chip->ops->position_check(chip, azx_dev)) {
> +				spin_unlock_irqrestore(&chip->reg_lock, cookie);
> +				snd_pcm_period_elapsed(azx_dev->substream);
> +				spin_lock_irqsave(&chip->reg_lock, cookie);
> +			}
> +		}
> +	}
> +
> +	/* clear rirb int */
> +	status = azx_readb(chip, RIRBSTS);
> +	if (status & RIRB_INT_MASK) {
> +		if (status & RIRB_INT_RESPONSE) {
> +			if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY)
> +				udelay(80);
> +			hda_update_rirb(chip);
> +		}
> +		azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
> +	}
> +
> +	spin_unlock_irqrestore(&chip->reg_lock, cookie);
> +
> +	return IRQ_HANDLED;
> +}
> +EXPORT_SYMBOL_GPL(hda_threaded_handler);
> +
> +static bool is_input_stream(struct hda *chip, unsigned char index)
> +{
> +	return (index >= chip->capture_index_offset &&
> +		 index < chip->capture_index_offset + chip->capture_streams);
> +}
> +
> +/* initialize SD streams */
> +int hda_init_stream(struct hda *chip)
> +{
> +	int i;
> +	int in_stream_tag = 0;
> +	int out_stream_tag = 0;
> +
> +	/* initialize each stream (aka device)
> +	 * assign the starting bdl address to each stream (device)
> +	 * and initialize
> +	 */
> +	for (i = 0; i < chip->num_streams; i++) {
> +		struct hda_device *azx_dev = &chip->hda_dev[i];
> +
> +		azx_dev->posbuf = (u32 __iomem *)(chip->posbuf.area + i * 8);
> +		/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
> +		azx_dev->sd_addr = chip->remap_addr + (0x20 * i + 0x80);
> +		/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
> +		azx_dev->sd_int_sta_mask = 1 << i;
> +		azx_dev->index = i;
> +
> +		/* stream tag must be unique throughout
> +		 * the stream direction group,
> +		 * valid values 1...15
> +		 * use separate stream tag if the flag
> +		 * AZX_DCAPS_SEPARATE_STREAM_TAG is used
> +		 */
> +		if (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG)
> +			azx_dev->stream_tag =
> +				is_input_stream(chip, i) ?
> +				++in_stream_tag :
> +				++out_stream_tag;
> +		else
> +			azx_dev->stream_tag = i + 1;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(hda_init_stream);
> +
> +void hda_timecounter_init(struct snd_pcm_substream *substream,
> +				bool force, cycle_t last, void *read_fn)
> +{
> +	struct hda_device *azx_dev = get_hda_dev(substream);
> +	struct timecounter *tc = &azx_dev->tc;
> +	struct cyclecounter *cc = &azx_dev->cc;
> +	u64 nsec;
> +
> +	cc->read = read_fn;
> +	cc->mask = CLOCKSOURCE_MASK(32);
> +
> +	/*
> +	 * Converting from 24 MHz to ns means applying a 125/3 factor.
> +	 * To avoid any saturation issues in intermediate operations,
> +	 * the 125 factor is applied first. The division is applied
> +	 * last after reading the timecounter value.
> +	 * Applying the 1/3 factor as part of the multiplication
> +	 * requires at least 20 bits for a decent precision, however
> +	 * overflows occur after about 4 hours or less, not a option.
> +	 */
> +
> +	cc->mult = 125; /* saturation after 195 years */
> +	cc->shift = 0;
> +
> +	nsec = 0; /* audio time is elapsed time since trigger */
> +	timecounter_init(tc, cc, nsec);
> +	if (force)
> +		/*
> +		 * force timecounter to use predefined value,
> +		 * used for synchronized starts
> +		 */
> +		tc->cycle_last = last;
> +}
> +EXPORT_SYMBOL_GPL(hda_timecounter_init);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Common HDA driver funcitons");
> -- 
> 1.7.9.5
> 
> 


More information about the Alsa-devel mailing list