[alsa-devel] [PATCH 00/16] HD-audio core controller split
Hi,
this is a patch series for splitting the existing HD-audio controller code to a core library. The purose of this split is to help writing other HD-audio controller codes more easily. With these library codes, a controller driver can be written up with small skeleton codes. I have done quick tests, and no obvious regression found, so submitted here for reviews.
The patches are found in topic/hda branch of sound git tree.
Note that this series isn't targeted for 4.1 but for 4.2.
Takashi
===
Jeeja KP (1): ALSA: hda - moved alloc/free stream pages function to controller library
Takashi Iwai (15): ALSA: hda - Handle error from get_response bus ops directly ALSA: hda - Add the controller helper codes to hda-core module ALSA: hda - Add DSP loader to core library code ALSA: hda - Merge codec and controller helpers ALSA: hda - Move send_cmd / get_response to hdac_bus_ops ALSA: hda - Pass bus io_ops directly from the top-level driver ALSA: hda - Migrate hdac_stream into legacy driver ALSA: hda - Migrate more hdac_stream codes ALSA: hda - Embed bus into controller object ALSA: hda - Minor refactoring ALSA: hda - Move PCM format and rate handling code to core library ALSA: hda - Add missing inclusion of <linux/clocksource.h> ALSA: hda - Reenable tracepoints for controller ALSA: hda/tegra - Fix build error and warning ALSA: hda - Drop azx_sd_read*/write*() macros
include/sound/hda_register.h | 152 +++++ include/sound/hdaudio.h | 282 ++++++++- sound/hda/Kconfig | 3 + sound/hda/Makefile | 2 +- sound/hda/hdac_bus.c | 20 +- sound/hda/hdac_controller.c | 507 +++++++++++++++ sound/hda/hdac_device.c | 300 +++++++++ sound/hda/hdac_stream.c | 648 ++++++++++++++++++++ sound/pci/hda/Kconfig | 3 - sound/pci/hda/Makefile | 3 +- sound/pci/hda/hda_codec.c | 409 +------------ sound/pci/hda/hda_codec.h | 31 +- sound/pci/hda/hda_controller.c | 1295 ++++++++------------------------------- sound/pci/hda/hda_controller.h | 269 +------- sound/pci/hda/hda_intel.c | 234 +++---- sound/pci/hda/hda_intel.h | 1 + sound/pci/hda/hda_intel_trace.h | 4 +- sound/pci/hda/hda_tegra.c | 76 +-- sound/pci/hda/patch_ca0132.c | 7 +- 19 files changed, 2395 insertions(+), 1851 deletions(-) create mode 100644 include/sound/hda_register.h create mode 100644 sound/hda/hdac_controller.c create mode 100644 sound/hda/hdac_stream.c
... and drop bus->rirb_error flag. This makes the code simpler.
We treat -EAGAIN from get_response ops as a special meaning: it allows the caller to retry after bus reset.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 17 +++++++-------- sound/pci/hda/hda_codec.h | 3 +-- sound/pci/hda/hda_controller.c | 47 +++++++++++++++++++++--------------------- 3 files changed, 32 insertions(+), 35 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e70a7fb393dd..c13d5c3e1d03 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -146,7 +146,7 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, bus->no_response_fallback = 0; mutex_unlock(&bus->core.cmd_mutex); snd_hda_power_down_pm(codec); - if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) { + if (!codec_in_pm(codec) && res && err == -EAGAIN) { if (bus->response_reset) { codec_dbg(codec, "resetting BUS due to fatal communication error\n"); @@ -436,9 +436,8 @@ static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid) get_wcaps_type(wcaps) != AC_WID_PIN) return 0;
- parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN); - if (parm == -1 && codec->bus->rirb_error) - parm = 0; + if (_snd_hdac_read_parm(&codec->core, nid, AC_PAR_DEVLIST_LEN, &parm)) + return 0; /* error */ return parm & AC_DEV_LIST_LEN_MASK; }
@@ -467,10 +466,9 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
devices = 0; while (devices < dev_len) { - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_DEVICE_LIST, devices); - if (parm == -1 && codec->bus->rirb_error) - break; + if (snd_hdac_read(&codec->core, nid, + AC_VERB_GET_DEVICE_LIST, devices, &parm)) + break; /* error */
for (i = 0; i < 8; i++) { dev_list[devices] = (u8)parm; @@ -520,8 +518,7 @@ static int _hda_bus_get_response(struct hdac_bus *_bus, unsigned int addr, unsigned int *res) { struct hda_bus *bus = container_of(_bus, struct hda_bus, core); - *res = bus->ops.get_response(bus, addr); - return bus->rirb_error ? -EIO : 0; + return bus->ops.get_response(bus, addr, res); }
static const struct hdac_bus_ops bus_ops = { diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 9075ac28dc4b..fc4f76188a1d 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -45,7 +45,7 @@ struct hda_bus_ops { /* send a single command */ int (*command)(struct hda_bus *bus, unsigned int cmd); /* get a response from the last command */ - unsigned int (*get_response)(struct hda_bus *bus, unsigned int addr); + int (*get_response)(struct hda_bus *bus, unsigned int addr, unsigned int *res); /* free the private data */ void (*private_free)(struct hda_bus *); /* attach a PCM stream */ @@ -92,7 +92,6 @@ struct hda_bus { unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ /* status for codec/controller */ unsigned int shutdown :1; /* being unloaded */ - unsigned int rirb_error:1; /* error in codec communication */ unsigned int response_reset:1; /* controller was reset */ unsigned int in_reset:1; /* during reset operation */ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 26ce990592a0..b4474e27631d 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1156,8 +1156,8 @@ static void azx_update_rirb(struct azx *chip) }
/* receive a response */ -static unsigned int azx_rirb_get_response(struct hda_bus *bus, - unsigned int addr) +static int azx_rirb_get_response(struct hda_bus *bus, unsigned int addr, + unsigned int *res) { struct azx *chip = bus->private_data; unsigned long timeout; @@ -1175,11 +1175,12 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, } 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 (res) + *res = chip->rirb.res[addr]; /* the last value */ + return 0; } if (time_after(jiffies, timeout)) break; @@ -1192,7 +1193,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, }
if (bus->no_response_fallback) - return -1; + return -EIO;
if (!chip->polling_mode && chip->poll_count < 2) { dev_dbg(chip->card->dev, @@ -1217,10 +1218,8 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, "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; - } + chip->ops->disable_msi_reset_irq(chip) < 0) + return -EIO; goto again; }
@@ -1229,16 +1228,15 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, * phase, this is likely an access to a non-existing codec * slot. Better to return an error and reset the system. */ - return -1; + return -EIO; }
/* 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 */ + return -EAGAIN; /* give a chance to retry */ }
dev_err(chip->card->dev, @@ -1250,7 +1248,7 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus, azx_free_cmd_io(chip); /* disable unsolicited responses */ azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_UNSOL); - return -1; + return -EIO; }
/* @@ -1291,7 +1289,6 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) unsigned int addr = azx_command_addr(val); int timeout = 50;
- bus->rirb_error = 0; while (timeout--) { /* check ICB busy bit */ if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) { @@ -1313,11 +1310,14 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) }
/* receive a response */ -static unsigned int azx_single_get_response(struct hda_bus *bus, - unsigned int addr) +static int azx_single_get_response(struct hda_bus *bus, unsigned int addr, + unsigned int *res) { struct azx *chip = bus->private_data; - return chip->rirb.res[addr]; + + if (res) + *res = chip->rirb.res[addr]; + return 0; }
/* @@ -1342,16 +1342,16 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val) }
/* get a response */ -static unsigned int azx_get_response(struct hda_bus *bus, - unsigned int addr) +static int azx_get_response(struct hda_bus *bus, unsigned int addr, + unsigned int *res) { struct azx *chip = bus->private_data; if (chip->disabled) return 0; if (chip->single_cmd) - return azx_single_get_response(bus, addr); + return azx_single_get_response(bus, addr, res); else - return azx_rirb_get_response(bus, addr); + return azx_rirb_get_response(bus, addr, res); }
#ifdef CONFIG_SND_HDA_DSP_LOADER @@ -1762,15 +1762,16 @@ static int probe_codec(struct azx *chip, int addr) { unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + int err; unsigned int res;
mutex_lock(&chip->bus->core.cmd_mutex); chip->probing = 1; azx_send_cmd(chip->bus, cmd); - res = azx_get_response(chip->bus, addr); + err = azx_get_response(chip->bus, addr, &res); chip->probing = 0; mutex_unlock(&chip->bus->core.cmd_mutex); - if (res == -1) + if (err < 0 || res == -1) return -EIO; dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr); return 0;
This patch adds the controller helper codes to hda-core library. The I/O access ops are added to the bus ops. The CORB/RIRB, the basic attributes like irq# and iomap address, some locks and the list of streams are added to the bus object, together with the stream object and its helpers.
Currently the codes are just copied from the legacy driver, so you can find duplicated codes in both directories. Only constants are removed from the original hda_controller.h. More integration work will follow in the later patches.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/hda_register.h | 152 ++++++++++++ include/sound/hdaudio.h | 224 ++++++++++++++++- sound/hda/Makefile | 2 +- sound/hda/hdac_bus.c | 20 +- sound/hda/hdac_controller.c | 449 ++++++++++++++++++++++++++++++++++ sound/hda/hdac_stream.c | 536 +++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_codec.c | 2 +- sound/pci/hda/hda_controller.h | 132 +--------- 8 files changed, 1380 insertions(+), 137 deletions(-) create mode 100644 include/sound/hda_register.h create mode 100644 sound/hda/hdac_controller.c create mode 100644 sound/hda/hdac_stream.c
diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h new file mode 100644 index 000000000000..4f6d3fce6ee6 --- /dev/null +++ b/include/sound/hda_register.h @@ -0,0 +1,152 @@ +/* + * HD-audio controller (Azalia) registers and helpers + * + * For traditional reasons, we still use azx_ prefix here + */ + +#ifndef __SOUND_HDA_REGISTER_H +#define __SOUND_HDA_REGISTER_H + +#include <linux/io.h> +#include <sound/hdaudio.h> + +#define AZX_REG_GCAP 0x00 +#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */ +#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */ +#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */ +#define AZX_GCAP_ISS (15 << 8) /* # of input streams */ +#define AZX_GCAP_OSS (15 << 12) /* # of output streams */ +#define AZX_REG_VMIN 0x02 +#define AZX_REG_VMAJ 0x03 +#define AZX_REG_OUTPAY 0x04 +#define AZX_REG_INPAY 0x06 +#define AZX_REG_GCTL 0x08 +#define AZX_GCTL_RESET (1 << 0) /* controller reset */ +#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */ +#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ +#define AZX_REG_WAKEEN 0x0c +#define AZX_REG_STATESTS 0x0e +#define AZX_REG_GSTS 0x10 +#define AZX_GSTS_FSTS (1 << 1) /* flush status */ +#define AZX_REG_INTCTL 0x20 +#define AZX_REG_INTSTS 0x24 +#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */ +#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */ +#define AZX_REG_SSYNC 0x38 +#define AZX_REG_CORBLBASE 0x40 +#define AZX_REG_CORBUBASE 0x44 +#define AZX_REG_CORBWP 0x48 +#define AZX_REG_CORBRP 0x4a +#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */ +#define AZX_REG_CORBCTL 0x4c +#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */ +#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ +#define AZX_REG_CORBSTS 0x4d +#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */ +#define AZX_REG_CORBSIZE 0x4e + +#define AZX_REG_RIRBLBASE 0x50 +#define AZX_REG_RIRBUBASE 0x54 +#define AZX_REG_RIRBWP 0x58 +#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */ +#define AZX_REG_RINTCNT 0x5a +#define AZX_REG_RIRBCTL 0x5c +#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ +#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */ +#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ +#define AZX_REG_RIRBSTS 0x5d +#define AZX_RBSTS_IRQ (1 << 0) /* response irq */ +#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */ +#define AZX_REG_RIRBSIZE 0x5e + +#define AZX_REG_IC 0x60 +#define AZX_REG_IR 0x64 +#define AZX_REG_IRS 0x68 +#define AZX_IRS_VALID (1<<1) +#define AZX_IRS_BUSY (1<<0) + +#define AZX_REG_DPLBASE 0x70 +#define AZX_REG_DPUBASE 0x74 +#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */ + +/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ +enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; + +/* stream register offsets from stream base */ +#define AZX_REG_SD_CTL 0x00 +#define AZX_REG_SD_STS 0x03 +#define AZX_REG_SD_LPIB 0x04 +#define AZX_REG_SD_CBL 0x08 +#define AZX_REG_SD_LVI 0x0c +#define AZX_REG_SD_FIFOW 0x0e +#define AZX_REG_SD_FIFOSIZE 0x10 +#define AZX_REG_SD_FORMAT 0x12 +#define AZX_REG_SD_BDLPL 0x18 +#define AZX_REG_SD_BDLPU 0x1c + +/* PCI space */ +#define AZX_PCIREG_TCSEL 0x44 + +/* + * other constants + */ + +/* max number of fragments - we may use more if allocating more pages for BDL */ +#define BDL_SIZE 4096 +#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) +#define AZX_MAX_FRAG 32 +/* max buffer size - no h/w limit, you can increase as you like */ +#define AZX_MAX_BUF_SIZE (1024*1024*1024) + +/* RIRB int mask: overrun[2], response[0] */ +#define RIRB_INT_RESPONSE 0x01 +#define RIRB_INT_OVERRUN 0x04 +#define RIRB_INT_MASK 0x05 + +/* STATESTS int mask: S3,SD2,SD1,SD0 */ +#define STATESTS_INT_MASK ((1 << HDA_MAX_CODECS) - 1) + +/* SD_CTL bits */ +#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ +#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ +#define SD_CTL_STRIPE (3 << 16) /* stripe control */ +#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ +#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ +#define SD_CTL_STREAM_TAG_MASK (0xf << 20) +#define SD_CTL_STREAM_TAG_SHIFT 20 + +/* SD_CTL and SD_STS */ +#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ +#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ +#define SD_INT_COMPLETE 0x04 /* completion interrupt */ +#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ + SD_INT_COMPLETE) + +/* SD_STS */ +#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ + +/* INTCTL and INTSTS */ +#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */ +#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ +#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ + +/* below are so far hardcoded - should read registers in future */ +#define AZX_MAX_CORB_ENTRIES 256 +#define AZX_MAX_RIRB_ENTRIES 256 + +/* + * helpers to read the stream position + */ +static inline unsigned int +snd_hdac_stream_get_pos_lpib(struct hdac_stream *stream) +{ + return snd_hdac_stream_readl(stream, SD_LPIB); +} + +static inline unsigned int +snd_hdac_stream_get_pos_posbuf(struct hdac_stream *stream) +{ + return le32_to_cpu(*stream->posbuf); +} + +#endif /* __SOUND_HDA_REGISTER_H */ diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 2a8aa9dfb83d..9349ccf15a36 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -6,12 +6,17 @@ #define __SOUND_HDAUDIO_H
#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/timecounter.h> +#include <sound/core.h> +#include <sound/memalloc.h> #include <sound/hda_verbs.h>
/* codec node id */ typedef u16 hda_nid_t;
struct hdac_bus; +struct hdac_stream; struct hdac_device; struct hdac_driver; struct hdac_widget_tree; @@ -161,7 +166,7 @@ struct hdac_driver { #define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver)
/* - * HD-audio bus base driver + * Bus verb operators */ struct hdac_bus_ops { /* send a single command */ @@ -171,11 +176,50 @@ struct hdac_bus_ops { unsigned int *res); };
+/* + * Lowlevel I/O operators + */ +struct hdac_io_ops { + /* mapped register accesses */ + 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); +}; + #define HDA_UNSOL_QUEUE_SIZE 64 +#define HDA_MAX_CODECS 8 /* limit by controller side */ + +/* HD Audio class code */ +#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 + +/* + * CORB/RIRB + * + * Each CORB entry is 4byte, RIRB is 8byte + */ +struct hdac_rb { + __le32 *buf; /* virtual address of CORB/RIRB buffer */ + dma_addr_t addr; /* physical address of CORB/RIRB buffer */ + unsigned short rp, wp; /* RIRB read/write pointers */ + int cmds[HDA_MAX_CODECS]; /* number of pending requests */ + u32 res[HDA_MAX_CODECS]; /* last read value */ +};
+/* + * HD-audio bus base driver + */ struct hdac_bus { struct device *dev; const struct hdac_bus_ops *ops; + const struct hdac_io_ops *io_ops; + + /* h/w resources */ + unsigned long addr; + void __iomem *remap_addr; + int irq;
/* codec linked list */ struct list_head codec_list; @@ -189,18 +233,45 @@ struct hdac_bus { unsigned int unsol_rp, unsol_wp; struct work_struct unsol_work;
+ /* bit flags of detected codecs */ + unsigned long codec_mask; + /* bit flags of powered codecs */ unsigned long codec_powered;
- /* flags */ + /* CORB/RIRB */ + struct hdac_rb corb; + struct hdac_rb rirb; + unsigned int last_cmd[HDA_MAX_CODECS]; /* last sent command */ + + /* CORB/RIRB and position buffers */ + struct snd_dma_buffer rb; + struct snd_dma_buffer posbuf; + + /* hdac_stream linked list */ + struct list_head stream_list; + + /* operation state */ + bool chip_init:1; /* h/w initialized */ + + /* behavior flags */ bool sync_write:1; /* sync after verb write */ + bool use_posbuf:1; /* use position buffer */ + bool snoop:1; /* enable snooping */ + bool align_bdle_4k:1; /* BDLE align 4K boundary */ + bool reverse_assign:1; /* assign devices in reverse order */ + bool corbrp_self_clear:1; /* CORBRP clears itself after reset */ + + int bdl_pos_adj; /* BDL position adjustment */
/* locks */ + spinlock_t reg_lock; struct mutex cmd_mutex; };
int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, - const struct hdac_bus_ops *ops); + const struct hdac_bus_ops *ops, + const struct hdac_io_ops *io_ops); void snd_hdac_bus_exit(struct hdac_bus *bus); int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, unsigned int cmd, unsigned int *res); @@ -222,6 +293,153 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec) clear_bit(codec->addr, &codec->bus->codec_powered); }
+int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val); +int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, + unsigned int *res); + +bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset); +void snd_hdac_bus_stop_chip(struct hdac_bus *bus); +void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus); +void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus); +void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus); +void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus); + +void snd_hdac_bus_update_rirb(struct hdac_bus *bus); +void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, + void (*ack)(struct hdac_bus *, + struct hdac_stream *)); + +/* + * macros for easy use + */ +#define _snd_hdac_chip_write(type, chip, reg, value) \ + ((chip)->io_ops->reg_write ## type(value, (chip)->remap_addr + (reg))) +#define _snd_hdac_chip_read(type, chip, reg) \ + ((chip)->io_ops->reg_read ## type((chip)->remap_addr + (reg))) + +/* read/write a register, pass without AZX_REG_ prefix */ +#define snd_hdac_chip_writel(chip, reg, value) \ + _snd_hdac_chip_write(l, chip, AZX_REG_ ## reg, value) +#define snd_hdac_chip_writew(chip, reg, value) \ + _snd_hdac_chip_write(w, chip, AZX_REG_ ## reg, value) +#define snd_hdac_chip_writeb(chip, reg, value) \ + _snd_hdac_chip_write(b, chip, AZX_REG_ ## reg, value) +#define snd_hdac_chip_readl(chip, reg) \ + _snd_hdac_chip_read(l, chip, AZX_REG_ ## reg) +#define snd_hdac_chip_readw(chip, reg) \ + _snd_hdac_chip_read(w, chip, AZX_REG_ ## reg) +#define snd_hdac_chip_readb(chip, reg) \ + _snd_hdac_chip_read(b, chip, AZX_REG_ ## reg) + +/* update a register, pass without AZX_REG_ prefix */ +#define snd_hdac_chip_updatel(chip, reg, mask, val) \ + snd_hdac_chip_writel(chip, reg, \ + (snd_hdac_chip_readl(chip, reg) & ~(mask)) | (val)) +#define snd_hdac_chip_updatew(chip, reg, mask, val) \ + snd_hdac_chip_writew(chip, reg, \ + (snd_hdac_chip_readw(chip, reg) & ~(mask)) | (val)) +#define snd_hdac_chip_updateb(chip, reg, mask, val) \ + snd_hdac_chip_writeb(chip, reg, \ + (snd_hdac_chip_readb(chip, reg) & ~(mask)) | (val)) + +/* + * HD-audio stream + */ +struct hdac_stream { + struct hdac_bus *bus; + struct snd_dma_buffer bdl; /* BDL buffer */ + __le32 *posbuf; /* position buffer pointer */ + int direction; /* playback / capture (SNDRV_PCM_STREAM_*) */ + + 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 */ + + 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 */ + + bool opened:1; + bool running:1; + bool no_period_wakeup:1; + + /* timestamp */ + unsigned long start_wallclk; /* start + minimum wallclk */ + unsigned long period_wallclk; /* wallclk for period */ + struct timecounter tc; + struct cyclecounter cc; + int delay_negative_threshold; + + struct list_head list; +}; + +void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, + int idx, int direction, int tag); +struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, + struct snd_pcm_substream *substream); +void snd_hdac_stream_release(struct hdac_stream *azx_dev); + +int snd_hdac_stream_setup(struct hdac_stream *azx_dev); +void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev); +int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev); +void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start); +void snd_hdac_stream_clear(struct hdac_stream *azx_dev); +void snd_hdac_stream_stop(struct hdac_stream *azx_dev); +void snd_hdac_stream_reset(struct hdac_stream *azx_dev); +void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set, + unsigned int streams, unsigned int reg); +void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, + unsigned int streams); +void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, + unsigned int streams); +/* + * macros for easy use + */ +#define _snd_hdac_stream_write(type, dev, reg, value) \ + ((dev)->bus->io_ops->reg_write ## type(value, (dev)->sd_addr + (reg))) +#define _snd_hdac_stream_read(type, dev, reg) \ + ((dev)->bus->io_ops->reg_read ## type((dev)->sd_addr + (reg))) + +/* read/write a register, pass without AZX_REG_ prefix */ +#define snd_hdac_stream_writel(dev, reg, value) \ + _snd_hdac_stream_write(l, dev, AZX_REG_ ## reg, value) +#define snd_hdac_stream_writew(dev, reg, value) \ + _snd_hdac_stream_write(w, dev, AZX_REG_ ## reg, value) +#define snd_hdac_stream_writeb(dev, reg, value) \ + _snd_hdac_stream_write(b, dev, AZX_REG_ ## reg, value) +#define snd_hdac_stream_readl(dev, reg) \ + _snd_hdac_stream_read(l, dev, AZX_REG_ ## reg) +#define snd_hdac_stream_readw(dev, reg) \ + _snd_hdac_stream_read(w, dev, AZX_REG_ ## reg) +#define snd_hdac_stream_readb(dev, reg) \ + _snd_hdac_stream_read(b, dev, AZX_REG_ ## reg) + +/* update a register, pass without AZX_REG_ prefix */ +#define snd_hdac_stream_updatel(dev, reg, mask, val) \ + snd_hdac_stream_writel(dev, reg, \ + (snd_hdac_stream_readl(dev, reg) & \ + ~(mask)) | (val)) +#define snd_hdac_stream_updatew(dev, reg, mask, val) \ + snd_hdac_stream_writew(dev, reg, \ + (snd_hdac_stream_readw(dev, reg) & \ + ~(mask)) | (val)) +#define snd_hdac_stream_updateb(dev, reg, mask, val) \ + snd_hdac_stream_writeb(dev, reg, \ + (snd_hdac_stream_readb(dev, reg) & \ + ~(mask)) | (val)) + /* * generic array helpers */ diff --git a/sound/hda/Makefile b/sound/hda/Makefile index 7a359f5b7e25..5b4bb47c16fd 100644 --- a/sound/hda/Makefile +++ b/sound/hda/Makefile @@ -1,5 +1,5 @@ snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ - hdac_regmap.o array.o + hdac_regmap.o hdac_controller.o hdac_stream.o array.o
snd-hda-core-objs += trace.o CFLAGS_trace.o := -I$(src) diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c index 8e262da74f6a..27c447e4fe5c 100644 --- a/sound/hda/hdac_bus.c +++ b/sound/hda/hdac_bus.c @@ -11,21 +11,36 @@
static void process_unsol_events(struct work_struct *work);
+static const struct hdac_bus_ops default_ops = { + .command = snd_hdac_bus_send_cmd, + .get_response = snd_hdac_bus_get_response, +}; + /** * snd_hdac_bus_init - initialize a HD-audio bas bus * @bus: the pointer to bus object + * @ops: bus verb operators + * @io_ops: lowlevel I/O operators * * Returns 0 if successful, or a negative error code. */ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, - const struct hdac_bus_ops *ops) + const struct hdac_bus_ops *ops, + const struct hdac_io_ops *io_ops) { memset(bus, 0, sizeof(*bus)); bus->dev = dev; - bus->ops = ops; + if (ops) + bus->ops = ops; + else + bus->ops = &default_ops; + bus->io_ops = io_ops; + INIT_LIST_HEAD(&bus->stream_list); INIT_LIST_HEAD(&bus->codec_list); INIT_WORK(&bus->unsol_work, process_unsol_events); + spin_lock_init(&bus->reg_lock); mutex_init(&bus->cmd_mutex); + bus->irq = -1; return 0; } EXPORT_SYMBOL_GPL(snd_hdac_bus_init); @@ -36,6 +51,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_init); */ void snd_hdac_bus_exit(struct hdac_bus *bus) { + WARN_ON(!list_empty(&bus->stream_list)); WARN_ON(!list_empty(&bus->codec_list)); cancel_work_sync(&bus->unsol_work); } diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c new file mode 100644 index 000000000000..c0069d0b7875 --- /dev/null +++ b/sound/hda/hdac_controller.c @@ -0,0 +1,449 @@ +/* + * HD-audio controller helpers + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <sound/core.h> +#include <sound/hdaudio.h> +#include <sound/hda_register.h> + +/* clear CORB read pointer properly */ +static void azx_clear_corbrp(struct hdac_bus *bus) +{ + int timeout; + + for (timeout = 1000; timeout > 0; timeout--) { + if (snd_hdac_chip_readw(bus, CORBRP) & AZX_CORBRP_RST) + break; + udelay(1); + } + if (timeout <= 0) + dev_err(bus->dev, "CORB reset timeout#1, CORBRP = %d\n", + snd_hdac_chip_readw(bus, CORBRP)); + + snd_hdac_chip_writew(bus, CORBRP, 0); + for (timeout = 1000; timeout > 0; timeout--) { + if (snd_hdac_chip_readw(bus, CORBRP) == 0) + break; + udelay(1); + } + if (timeout <= 0) + dev_err(bus->dev, "CORB reset timeout#2, CORBRP = %d\n", + snd_hdac_chip_readw(bus, CORBRP)); +} + +/** + * snd_hdac_bus_init_cmd_io - set up CORB/RIRB buffers + * @bus: HD-audio core bus + */ +void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus) +{ + spin_lock_irq(&bus->reg_lock); + /* CORB set up */ + bus->corb.addr = bus->rb.addr; + bus->corb.buf = (__le32 *)bus->rb.area; + snd_hdac_chip_writel(bus, CORBLBASE, (u32)bus->corb.addr); + snd_hdac_chip_writel(bus, CORBUBASE, upper_32_bits(bus->corb.addr)); + + /* set the corb size to 256 entries (ULI requires explicitly) */ + snd_hdac_chip_writeb(bus, CORBSIZE, 0x02); + /* set the corb write pointer to 0 */ + snd_hdac_chip_writew(bus, CORBWP, 0); + + /* reset the corb hw read pointer */ + snd_hdac_chip_writew(bus, CORBRP, AZX_CORBRP_RST); + if (!bus->corbrp_self_clear) + azx_clear_corbrp(bus); + + /* enable corb dma */ + snd_hdac_chip_writeb(bus, CORBCTL, AZX_CORBCTL_RUN); + + /* RIRB set up */ + bus->rirb.addr = bus->rb.addr + 2048; + bus->rirb.buf = (__le32 *)(bus->rb.area + 2048); + bus->rirb.wp = bus->rirb.rp = 0; + memset(bus->rirb.cmds, 0, sizeof(bus->rirb.cmds)); + snd_hdac_chip_writel(bus, RIRBLBASE, (u32)bus->rirb.addr); + snd_hdac_chip_writel(bus, RIRBUBASE, upper_32_bits(bus->rirb.addr)); + + /* set the rirb size to 256 entries (ULI requires explicitly) */ + snd_hdac_chip_writeb(bus, RIRBSIZE, 0x02); + /* reset the rirb hw write pointer */ + snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST); + /* set N=1, get RIRB response interrupt for new entry */ + snd_hdac_chip_writew(bus, RINTCNT, 1); + /* enable rirb dma and response irq */ + snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN); + spin_unlock_irq(&bus->reg_lock); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_init_cmd_io); + +/** + * snd_hdac_bus_stop_cmd_io - clean up CORB/RIRB buffers + * @bus: HD-audio core bus + */ +void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus) +{ + spin_lock_irq(&bus->reg_lock); + /* disable ringbuffer DMAs */ + snd_hdac_chip_writeb(bus, RIRBCTL, 0); + snd_hdac_chip_writeb(bus, CORBCTL, 0); + /* disable unsolicited responses */ + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0); + spin_unlock_irq(&bus->reg_lock); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_cmd_io); + +static unsigned int azx_command_addr(u32 cmd) +{ + unsigned int addr = cmd >> 28; + + if (snd_BUG_ON(addr >= HDA_MAX_CODECS)) + addr = 0; + return addr; +} + +/** + * snd_hdac_bus_send_cmd - send a command verb via CORB + * @bus: HD-audio core bus + * @val: encoded verb value to send + * + * Returns zero for success or a negative error code. + */ +int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val) +{ + unsigned int addr = azx_command_addr(val); + unsigned int wp, rp; + + spin_lock_irq(&bus->reg_lock); + + bus->last_cmd[azx_command_addr(val)] = val; + + /* add command to corb */ + wp = snd_hdac_chip_readw(bus, CORBWP); + if (wp == 0xffff) { + /* something wrong, controller likely turned to D3 */ + spin_unlock_irq(&bus->reg_lock); + return -EIO; + } + wp++; + wp %= AZX_MAX_CORB_ENTRIES; + + rp = snd_hdac_chip_readw(bus, CORBRP); + if (wp == rp) { + /* oops, it's full */ + spin_unlock_irq(&bus->reg_lock); + return -EAGAIN; + } + + bus->rirb.cmds[addr]++; + bus->corb.buf[wp] = cpu_to_le32(val); + snd_hdac_chip_writew(bus, CORBWP, wp); + + spin_unlock_irq(&bus->reg_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_send_cmd); + +#define AZX_RIRB_EX_UNSOL_EV (1<<4) + +/** + * snd_hdac_bus_update_rirb - retrieve RIRB entries + * @bus: HD-audio core bus + * + * Usually called from interrupt handler. + */ +void snd_hdac_bus_update_rirb(struct hdac_bus *bus) +{ + unsigned int rp, wp; + unsigned int addr; + u32 res, res_ex; + + wp = snd_hdac_chip_readw(bus, RIRBWP); + if (wp == 0xffff) { + /* something wrong, controller likely turned to D3 */ + return; + } + + if (wp == bus->rirb.wp) + return; + bus->rirb.wp = wp; + + while (bus->rirb.rp != wp) { + bus->rirb.rp++; + bus->rirb.rp %= AZX_MAX_RIRB_ENTRIES; + + rp = bus->rirb.rp << 1; /* an RIRB entry is 8-bytes */ + res_ex = le32_to_cpu(bus->rirb.buf[rp + 1]); + res = le32_to_cpu(bus->rirb.buf[rp]); + addr = res_ex & 0xf; + if (addr >= HDA_MAX_CODECS) { + dev_err(bus->dev, + "spurious response %#x:%#x, rp = %d, wp = %d", + res, res_ex, bus->rirb.rp, wp); + snd_BUG(); + } else if (res_ex & AZX_RIRB_EX_UNSOL_EV) + snd_hdac_bus_queue_event(bus, res, res_ex); + else if (bus->rirb.cmds[addr]) { + bus->rirb.res[addr] = res; + bus->rirb.cmds[addr]--; + } else { + dev_err_ratelimited(bus->dev, + "spurious response %#x:%#x, last cmd=%#08x\n", + res, res_ex, bus->last_cmd[addr]); + } + } +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_update_rirb); + +/** + * snd_hdac_bus_get_response - receive a response via RIRB + * @bus: HD-audio core bus + * @addr: codec address + * @res: pointer to store the value, NULL when not needed + * + * Returns zero if a value is read, or a negative error code. + */ +int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr, + unsigned int *res) +{ + unsigned long timeout; + unsigned long loopcounter; + + timeout = jiffies + msecs_to_jiffies(1000); + + for (loopcounter = 0;; loopcounter++) { + spin_lock_irq(&bus->reg_lock); + if (!bus->rirb.cmds[addr]) { + if (res) + *res = bus->rirb.res[addr]; /* the last value */ + spin_unlock_irq(&bus->reg_lock); + return 0; + } + spin_unlock_irq(&bus->reg_lock); + if (time_after(jiffies, timeout)) + break; + if (loopcounter > 3000) + msleep(2); /* temporary workaround */ + else { + udelay(10); + cond_resched(); + } + } + + return -EIO; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response); + +/* + * Lowlevel interface + */ + +/** + * snd_hdac_bus_enter_link_reset - enter link reset + * @bus: HD-audio core bus + * + * Enter to the link reset state. + */ +void snd_hdac_bus_enter_link_reset(struct hdac_bus *bus) +{ + unsigned long timeout; + + /* reset controller */ + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_RESET, 0); + + timeout = jiffies + msecs_to_jiffies(100); + while ((snd_hdac_chip_readb(bus, GCTL) & AZX_GCTL_RESET) && + time_before(jiffies, timeout)) + usleep_range(500, 1000); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_enter_link_reset); + +/** + * snd_hdac_bus_exit_link_reset - exit link reset + * @bus: HD-audio core bus + * + * Exit from the link reset state. + */ +void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus) +{ + unsigned long timeout; + + snd_hdac_chip_updateb(bus, GCTL, 0, AZX_GCTL_RESET); + + timeout = jiffies + msecs_to_jiffies(100); + while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout)) + usleep_range(500, 1000); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exit_link_reset); + +/* reset codec link */ +static int azx_reset(struct hdac_bus *bus, bool full_reset) +{ + if (!full_reset) + goto skip_reset; + + /* clear STATESTS */ + snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK); + + /* reset controller */ + snd_hdac_bus_enter_link_reset(bus); + + /* 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 */ + snd_hdac_bus_exit_link_reset(bus); + + /* Brent Chartrand said to wait >= 540us for codecs to initialize */ + usleep_range(1000, 1200); + + skip_reset: + /* check to see if controller is ready */ + if (!snd_hdac_chip_readb(bus, GCTL)) { + dev_dbg(bus->dev, "azx_reset: controller not ready!\n"); + return -EBUSY; + } + + /* Accept unsolicited responses */ + snd_hdac_chip_updatel(bus, GCTL, 0, AZX_GCTL_UNSOL); + + /* detect codecs */ + if (!bus->codec_mask) { + bus->codec_mask = snd_hdac_chip_readw(bus, STATESTS); + dev_dbg(bus->dev, "codec_mask = 0x%lx\n", bus->codec_mask); + } + + return 0; +} + +/* enable interrupts */ +static void azx_int_enable(struct hdac_bus *bus) +{ + /* enable controller CIE and GIE */ + snd_hdac_chip_updatel(bus, INTCTL, 0, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); +} + +/* disable interrupts */ +static void azx_int_disable(struct hdac_bus *bus) +{ + struct hdac_stream *azx_dev; + + /* disable interrupts in stream descriptor */ + list_for_each_entry(azx_dev, &bus->stream_list, list) + snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_INT_MASK, 0); + + /* disable SIE for all streams */ + snd_hdac_chip_writeb(bus, INTCTL, 0); + + /* disable controller CIE and GIE */ + snd_hdac_chip_updatel(bus, INTCTL, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN, 0); +} + +/* clear interrupts */ +static void azx_int_clear(struct hdac_bus *bus) +{ + struct hdac_stream *azx_dev; + + /* clear stream status */ + list_for_each_entry(azx_dev, &bus->stream_list, list) + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); + + /* clear STATESTS */ + snd_hdac_chip_writew(bus, STATESTS, STATESTS_INT_MASK); + + /* clear rirb status */ + snd_hdac_chip_writeb(bus, RIRBSTS, RIRB_INT_MASK); + + /* clear int status */ + snd_hdac_chip_writel(bus, INTSTS, AZX_INT_CTRL_EN | AZX_INT_ALL_STREAM); +} + +/** + * snd_hdac_bus_init_chip - reset and start the controller registers + * @bus: HD-audio core bus + * @full_reset: Do full reset + */ +bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset) +{ + if (bus->chip_init) + return false; + + /* reset controller */ + azx_reset(bus, full_reset); + + /* initialize interrupts */ + azx_int_clear(bus); + azx_int_enable(bus); + + /* initialize the codec command I/O */ + snd_hdac_bus_init_cmd_io(bus); + + /* program the position buffer */ + if (bus->use_posbuf && bus->posbuf.addr) { + snd_hdac_chip_writel(bus, DPLBASE, (u32)bus->posbuf.addr); + snd_hdac_chip_writel(bus, DPUBASE, upper_32_bits(bus->posbuf.addr)); + } + + bus->chip_init = true; + return true; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_init_chip); + +/** + * snd_hdac_bus_stop_chip - disable the whole IRQ and I/Os + * @bus: HD-audio core bus + */ +void snd_hdac_bus_stop_chip(struct hdac_bus *bus) +{ + if (!bus->chip_init) + return; + + /* disable interrupts */ + azx_int_disable(bus); + azx_int_clear(bus); + + /* disable CORB/RIRB */ + snd_hdac_bus_stop_cmd_io(bus); + + /* disable position buffer */ + if (bus->posbuf.addr) { + snd_hdac_chip_writel(bus, DPLBASE, 0); + snd_hdac_chip_writel(bus, DPUBASE, 0); + } + + bus->chip_init = false; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_stop_chip); + +/** + * snd_hdac_bus_handle_stream_irq - interrupt handler for streams + * @bus: HD-audio core bus + * @status: INTSTS register value + * @ask: callback to be called for woken streams + */ +void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, + void (*ack)(struct hdac_bus *, + struct hdac_stream *)) +{ + struct hdac_stream *azx_dev; + u8 sd_status; + + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (status & azx_dev->sd_int_sta_mask) { + sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); + if (!azx_dev->substream || !azx_dev->running || + !(sd_status & SD_INT_COMPLETE)) + continue; + if (ack) + ack(bus, azx_dev); + } + } +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c new file mode 100644 index 000000000000..b513a15c777f --- /dev/null +++ b/sound/hda/hdac_stream.c @@ -0,0 +1,536 @@ +/* + * HD-audio stream operations + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/hdaudio.h> +#include <sound/hda_register.h> + +/** + * snd_hdac_stream_init - initialize each stream (aka device) + * @bus: HD-audio core bus + * @azx_dev: HD-audio core stream object to initialize + * @idx: stream index number + * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) + * @tag: the tag id to assign + * + * Assign the starting bdl address to each stream (device) and initialize. + */ +void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, + int idx, int direction, int tag) +{ + azx_dev->bus = bus; + if (bus->posbuf.area) + azx_dev->posbuf = (__le32 *)(bus->posbuf.area + idx * 8); + /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ + azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80); + /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */ + azx_dev->sd_int_sta_mask = 1 << idx; + azx_dev->index = idx; + azx_dev->direction = direction; + azx_dev->stream_tag = tag; + list_add_tail(&azx_dev->list, &bus->stream_list); +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_init); + +/** + * snd_hdac_stream_start - start a stream + * @azx_dev: HD-audio core stream to start + * @fresh_start: false = wallclock timestamp relative to period wallclock + * + * Start a stream, set start_wallclk and set the running flag. + */ +void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) +{ + struct hdac_bus *bus = azx_dev->bus; + + azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK); + if (!fresh_start) + azx_dev->start_wallclk -= azx_dev->period_wallclk; + + /* enable SIE */ + snd_hdac_chip_updatel(bus, INTCTL, 0, 1 << azx_dev->index); + /* set DMA start and interrupt mask */ + snd_hdac_stream_updateb(azx_dev, SD_CTL, + 0, SD_CTL_DMA_START | SD_INT_MASK); + azx_dev->running = true; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_start); + +/** + * snd_hdac_stream_clear - stop a stream DMA + * @azx_dev: HD-audio core stream to stop + */ +void snd_hdac_stream_clear(struct hdac_stream *azx_dev) +{ + snd_hdac_stream_updateb(azx_dev, SD_CTL, + SD_CTL_DMA_START | SD_INT_MASK, 0); + snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + azx_dev->running = false; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_clear); + +/** + * snd_hdac_stream_stop - stop a stream + * @azx_dev: HD-audio core stream to stop + * + * Stop a stream DMA and disable stream interrupt + */ +void snd_hdac_stream_stop(struct hdac_stream *azx_dev) +{ + snd_hdac_stream_clear(azx_dev); + /* disable SIE */ + snd_hdac_chip_updatel(azx_dev->bus, INTCTL, 1 << azx_dev->index, 0); +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_stop); + +/** + * snd_hdac_stream_reset - reset a stream + * @azx_dev: HD-audio core stream to reset + */ +void snd_hdac_stream_reset(struct hdac_stream *azx_dev) +{ + unsigned char val; + int timeout; + + snd_hdac_stream_clear(azx_dev); + + snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_STREAM_RESET); + udelay(3); + timeout = 300; + do { + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & + SD_CTL_STREAM_RESET; + if (val) + break; + } while (--timeout); + val &= ~SD_CTL_STREAM_RESET; + snd_hdac_stream_writeb(azx_dev, SD_CTL, val); + udelay(3); + + timeout = 300; + /* waiting for hardware to report that the stream is out of reset */ + do { + val = snd_hdac_stream_readb(azx_dev, SD_CTL) & + SD_CTL_STREAM_RESET; + if (!val) + break; + } while (--timeout); + + /* reset first position - may not be synced with hw at this time */ + if (azx_dev->posbuf) + *azx_dev->posbuf = 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_reset); + +/** + * snd_hdac_stream_setup - set up the SD for streaming + * @azx_dev: HD-audio core stream to set up + */ +int snd_hdac_stream_setup(struct hdac_stream *azx_dev) +{ + struct hdac_bus *bus = azx_dev->bus; + struct snd_pcm_runtime *runtime = azx_dev->substream->runtime; + unsigned int val; + + /* make sure the run bit is zero for SD */ + snd_hdac_stream_clear(azx_dev); + /* program the stream_tag */ + val = snd_hdac_stream_readl(azx_dev, SD_CTL); + val = (val & ~SD_CTL_STREAM_TAG_MASK) | + (azx_dev->stream_tag << SD_CTL_STREAM_TAG_SHIFT); + if (!bus->snoop) + val |= SD_CTL_TRAFFIC_PRIO; + snd_hdac_stream_writel(azx_dev, SD_CTL, val); + + /* program the length of samples in cyclic buffer */ + snd_hdac_stream_writel(azx_dev, SD_CBL, azx_dev->bufsize); + + /* program the stream format */ + /* this value needs to be the same as the one programmed */ + snd_hdac_stream_writew(azx_dev, SD_FORMAT, azx_dev->format_val); + + /* program the stream LVI (last valid index) of the BDL */ + snd_hdac_stream_writew(azx_dev, SD_LVI, azx_dev->frags - 1); + + /* program the BDL address */ + /* lower BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); + /* upper BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPU, + upper_32_bits(azx_dev->bdl.addr)); + + /* enable the position buffer */ + if (bus->use_posbuf && bus->posbuf.addr) { + if (!(snd_hdac_chip_readl(bus, DPLBASE) & AZX_DPLBASE_ENABLE)) + snd_hdac_chip_writel(bus, DPLBASE, + (u32)bus->posbuf.addr | AZX_DPLBASE_ENABLE); + } + + /* set the interrupt enable bits in the descriptor control register */ + snd_hdac_stream_updatel(azx_dev, SD_CTL, 0, SD_INT_MASK); + + if (azx_dev->direction == SNDRV_PCM_STREAM_PLAYBACK) + azx_dev->fifo_size = + snd_hdac_stream_readw(azx_dev, SD_FIFOSIZE) + 1; + else + azx_dev->fifo_size = 0; + + /* 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); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_setup); + +/** + * snd_hdac_stream_cleanup - cleanup a stream + * @azx_dev: HD-audio core stream to clean up + */ +void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev) +{ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); + snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); + snd_hdac_stream_writel(azx_dev, SD_CTL, 0); + azx_dev->bufsize = 0; + azx_dev->period_bytes = 0; + azx_dev->format_val = 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_cleanup); + +/** + * snd_hdac_stream_assign - assign a stream for the PCM + * @bus: HD-audio core bus + * @substream: PCM substream to assign + * + * Look for an unused stream for the given PCM substream, assign it + * and return the stream object. If no stream is free, returns NULL. + * The function tries to keep using the same stream object when it's used + * beforehand. Also, when bus->reverse_assign flag is set, the last free + * or matching entry is returned. This is needed for some strange codecs. + */ +struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, + struct snd_pcm_substream *substream) +{ + struct hdac_stream *azx_dev; + struct hdac_stream *res = NULL; + + /* make a non-zero unique key for the substream */ + int key = (substream->pcm->device << 16) | (substream->number << 2) | + (substream->stream + 1); + + list_for_each_entry(azx_dev, &bus->stream_list, list) { + if (azx_dev->direction != substream->stream) + continue; + if (azx_dev->opened) + continue; + if (azx_dev->assigned_key == key) { + res = azx_dev; + break; + } + if (!res || bus->reverse_assign) + res = azx_dev; + } + if (res) { + spin_lock_irq(&bus->reg_lock); + res->opened = 1; + res->running = 0; + res->assigned_key = key; + res->substream = substream; + spin_unlock_irq(&bus->reg_lock); + } + return res; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_assign); + +/** + * snd_hdac_stream_release - release the assigned stream + * @azx_dev: HD-audio core stream to release + * + * Release the stream that has been assigned by snd_hdac_stream_assign(). + */ +void snd_hdac_stream_release(struct hdac_stream *azx_dev) +{ + struct hdac_bus *bus = azx_dev->bus; + + spin_lock_irq(&bus->reg_lock); + azx_dev->opened = 0; + azx_dev->running = 0; + azx_dev->substream = NULL; + spin_unlock_irq(&bus->reg_lock); +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_release); + +/* + * set up a BDL entry + */ +static int setup_bdle(struct hdac_bus *bus, + struct snd_dma_buffer *dmab, + struct hdac_stream *azx_dev, __le32 **bdlp, + int ofs, int size, int with_ioc) +{ + __le32 *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); + /* 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 (bus->align_bdle_4k) { + 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; + return ofs; +} + +/** + * snd_hdac_stream_setup_periods - set up BDL entries + * @azx_dev: HD-audio core stream to set up + * + * Set up the buffer descriptor table of the given stream based on the + * period and buffer sizes of the assigned PCM substream. + */ +int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) +{ + struct hdac_bus *bus = azx_dev->bus; + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + __le32 *bdl; + int i, ofs, periods, period_bytes; + int pos_adj, pos_align; + + /* reset BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); + snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); + + period_bytes = azx_dev->period_bytes; + periods = azx_dev->bufsize / period_bytes; + + /* program the initial BDL entries */ + bdl = (__le32 *)azx_dev->bdl.area; + ofs = 0; + azx_dev->frags = 0; + + pos_adj = bus->bdl_pos_adj; + if (!azx_dev->no_period_wakeup && pos_adj > 0) { + 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(bus->dev, "Too big adjustment %d\n", + pos_adj); + pos_adj = 0; + } else { + ofs = setup_bdle(bus, 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 = setup_bdle(bus, snd_pcm_get_dma_buf(substream), + azx_dev, &bdl, ofs, + period_bytes - pos_adj, 0); + else + ofs = setup_bdle(bus, 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(bus->dev, "Too many BDL entries: buffer=%d, period=%d\n", + azx_dev->bufsize, period_bytes); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods); + +static cycle_t azx_cc_read(const struct cyclecounter *cc) +{ + struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc); + + return snd_hdac_chip_readl(azx_dev->bus, WALLCLK); +} + +static void azx_timecounter_init(struct hdac_stream *azx_dev, + bool force, cycle_t last) +{ + struct timecounter *tc = &azx_dev->tc; + struct cyclecounter *cc = &azx_dev->cc; + u64 nsec; + + cc->read = azx_cc_read; + 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; + } +} + +/** + * snd_hdac_stream_timecounter_init - initialize time counter + * @azx_dev: HD-audio core stream (master stream) + * @streams: bit flags of streams to set up + * + * Initializes the time counter of streams marked by the bit flags (each + * bit corresponds to the stream index). + * The trigger timestamp of PCM substream assigned to the given stream is + * updated accordingly, too. + */ +void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, + unsigned int streams) +{ + struct hdac_bus *bus = azx_dev->bus; + struct snd_pcm_runtime *runtime = azx_dev->substream->runtime; + struct hdac_stream *s; + bool inited = false; + cycle_t cycle_last = 0; + int i = 0; + + list_for_each_entry(s, &bus->stream_list, list) { + if (streams & (1 << i)) { + azx_timecounter_init(s, inited, cycle_last); + if (!inited) { + inited = true; + cycle_last = s->tc.cycle_last; + } + } + i++; + } + + snd_pcm_gettime(runtime, &runtime->trigger_tstamp); + runtime->trigger_tstamp_latched = true; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_timecounter_init); + +/** + * snd_hdac_stream_sync_trigger - turn on/off stream sync register + * @azx_dev: HD-audio core stream (master stream) + * @streams: bit flags of streams to sync + */ +void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set, + unsigned int streams, unsigned int reg) +{ + struct hdac_bus *bus = azx_dev->bus; + unsigned int val; + + if (!reg) + reg = AZX_REG_SSYNC; + val = _snd_hdac_chip_read(l, bus, reg); + if (set) + val |= streams; + else + val &= ~streams; + _snd_hdac_chip_write(l, bus, reg, val); +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger); + +/** + * snd_hdac_stream_sync - sync with start/strop trigger operation + * @azx_dev: HD-audio core stream (master stream) + * @start: true = start, false = stop + * @streams: bit flags of streams to sync + * + * For @start = true, wait until all FIFOs get ready. + * For @start = false, wait until all RUN bits are cleared. + */ +void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, + unsigned int streams) +{ + struct hdac_bus *bus = azx_dev->bus; + int i, nwait, timeout; + struct hdac_stream *s; + + for (timeout = 5000; timeout; timeout--) { + nwait = 0; + i = 0; + list_for_each_entry(s, &bus->stream_list, list) { + if (streams & (1 << i)) { + if (start) { + /* check FIFO gets ready */ + if (!(snd_hdac_stream_readb(s, SD_STS) & + SD_STS_FIFO_READY)) + nwait++; + } else { + /* check RUN bit is cleared */ + if (snd_hdac_stream_readb(s, SD_CTL) & + SD_CTL_DMA_START) + nwait++; + } + } + i++; + } + if (!nwait) + break; + cpu_relax(); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_sync); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index c13d5c3e1d03..b86e2f449e56 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -550,7 +550,7 @@ int snd_hda_bus_new(struct snd_card *card, if (!bus) return -ENOMEM;
- err = snd_hdac_bus_init(&bus->core, card->dev, &bus_ops); + err = snd_hdac_bus_init(&bus->core, card->dev, &bus_ops, NULL); if (err < 0) { kfree(bus); return err; diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index be1b7ded8d82..15a796c21b9d 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -21,135 +21,10 @@ #include <sound/pcm.h> #include <sound/initval.h> #include "hda_codec.h" +#include <sound/hda_register.h>
-/* - * registers - */ -#define AZX_REG_GCAP 0x00 -#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */ -#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */ -#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */ -#define AZX_GCAP_ISS (15 << 8) /* # of input streams */ -#define AZX_GCAP_OSS (15 << 12) /* # of output streams */ -#define AZX_REG_VMIN 0x02 -#define AZX_REG_VMAJ 0x03 -#define AZX_REG_OUTPAY 0x04 -#define AZX_REG_INPAY 0x06 -#define AZX_REG_GCTL 0x08 -#define AZX_GCTL_RESET (1 << 0) /* controller reset */ -#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */ -#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ -#define AZX_REG_WAKEEN 0x0c -#define AZX_REG_STATESTS 0x0e -#define AZX_REG_GSTS 0x10 -#define AZX_GSTS_FSTS (1 << 1) /* flush status */ -#define AZX_REG_INTCTL 0x20 -#define AZX_REG_INTSTS 0x24 -#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */ -#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */ -#define AZX_REG_SSYNC 0x38 -#define AZX_REG_CORBLBASE 0x40 -#define AZX_REG_CORBUBASE 0x44 -#define AZX_REG_CORBWP 0x48 -#define AZX_REG_CORBRP 0x4a -#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */ -#define AZX_REG_CORBCTL 0x4c -#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */ -#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ -#define AZX_REG_CORBSTS 0x4d -#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */ -#define AZX_REG_CORBSIZE 0x4e - -#define AZX_REG_RIRBLBASE 0x50 -#define AZX_REG_RIRBUBASE 0x54 -#define AZX_REG_RIRBWP 0x58 -#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */ -#define AZX_REG_RINTCNT 0x5a -#define AZX_REG_RIRBCTL 0x5c -#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ -#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */ -#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ -#define AZX_REG_RIRBSTS 0x5d -#define AZX_RBSTS_IRQ (1 << 0) /* response irq */ -#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */ -#define AZX_REG_RIRBSIZE 0x5e - -#define AZX_REG_IC 0x60 -#define AZX_REG_IR 0x64 -#define AZX_REG_IRS 0x68 -#define AZX_IRS_VALID (1<<1) -#define AZX_IRS_BUSY (1<<0) - -#define AZX_REG_DPLBASE 0x70 -#define AZX_REG_DPUBASE 0x74 -#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */ - -/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ -enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; - -/* stream register offsets from stream base */ -#define AZX_REG_SD_CTL 0x00 -#define AZX_REG_SD_STS 0x03 -#define AZX_REG_SD_LPIB 0x04 -#define AZX_REG_SD_CBL 0x08 -#define AZX_REG_SD_LVI 0x0c -#define AZX_REG_SD_FIFOW 0x0e -#define AZX_REG_SD_FIFOSIZE 0x10 -#define AZX_REG_SD_FORMAT 0x12 -#define AZX_REG_SD_BDLPL 0x18 -#define AZX_REG_SD_BDLPU 0x1c - -/* PCI space */ -#define AZX_PCIREG_TCSEL 0x44 - -/* - * other constants - */ - -/* max number of fragments - we may use more if allocating more pages for BDL */ -#define BDL_SIZE 4096 -#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) -#define AZX_MAX_FRAG 32 -/* max buffer size - no h/w limit, you can increase as you like */ -#define AZX_MAX_BUF_SIZE (1024*1024*1024) - -/* RIRB int mask: overrun[2], response[0] */ -#define RIRB_INT_RESPONSE 0x01 -#define RIRB_INT_OVERRUN 0x04 -#define RIRB_INT_MASK 0x05 - -/* STATESTS int mask: S3,SD2,SD1,SD0 */ -#define AZX_MAX_CODECS 8 +#define AZX_MAX_CODECS HDA_MAX_CODECS #define AZX_DEFAULT_CODECS 4 -#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) - -/* SD_CTL bits */ -#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ -#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ -#define SD_CTL_STRIPE (3 << 16) /* stripe control */ -#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ -#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ -#define SD_CTL_STREAM_TAG_MASK (0xf << 20) -#define SD_CTL_STREAM_TAG_SHIFT 20 - -/* SD_CTL and SD_STS */ -#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ -#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ -#define SD_INT_COMPLETE 0x04 /* completion interrupt */ -#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ - SD_INT_COMPLETE) - -/* SD_STS */ -#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ - -/* INTCTL and INTSTS */ -#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */ -#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ -#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ - -/* below are so far hardcoded - should read registers in future */ -#define AZX_MAX_CORB_ENTRIES 256 -#define AZX_MAX_RIRB_ENTRIES 256
/* driver quirks (capabilities) */ /* bits 0-7 are used for indicating driver type */ @@ -183,9 +58,6 @@ enum { AZX_SNOOP_TYPE_NVIDIA, };
-/* HD Audio class code */ -#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 - struct azx_dev { struct snd_dma_buffer bdl; /* BDL buffer */ u32 *posbuf; /* position buffer pointer */
Copied from the legacy driver code, no transition done yet.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/hdaudio.h | 46 ++++++++++++++++++++ sound/hda/Kconfig | 3 ++ sound/hda/hdac_stream.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/Kconfig | 3 -- 4 files changed, 162 insertions(+), 3 deletions(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 9349ccf15a36..69f27bc49eb4 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -187,6 +187,11 @@ struct hdac_io_ops { u16 (*reg_readw)(u16 __iomem *addr); void (*reg_writeb)(u8 value, u8 __iomem *addr); u8 (*reg_readb)(u8 __iomem *addr); + /* Allocation ops */ + int (*dma_alloc_pages)(struct hdac_bus *bus, int type, size_t size, + struct snd_dma_buffer *buf); + void (*dma_free_pages)(struct hdac_bus *bus, + struct snd_dma_buffer *buf); };
#define HDA_UNSOL_QUEUE_SIZE 64 @@ -374,6 +379,7 @@ struct hdac_stream { bool opened:1; bool running:1; bool no_period_wakeup:1; + bool locked:1;
/* timestamp */ unsigned long start_wallclk; /* start + minimum wallclk */ @@ -383,6 +389,10 @@ struct hdac_stream { int delay_negative_threshold;
struct list_head list; +#ifdef CONFIG_SND_HDA_DSP_LOADER + /* DSP access mutex */ + struct mutex dsp_mutex; +#endif };
void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, @@ -440,6 +450,42 @@ void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev, (snd_hdac_stream_readb(dev, reg) & \ ~(mask)) | (val))
+#ifdef CONFIG_SND_HDA_DSP_LOADER +/* DSP lock helpers */ +#define snd_hdac_dsp_lock_init(dev) mutex_init(&(dev)->dsp_mutex) +#define snd_hdac_dsp_lock(dev) mutex_lock(&(dev)->dsp_mutex) +#define snd_hdac_dsp_unlock(dev) mutex_unlock(&(dev)->dsp_mutex) +#define snd_hdac_stream_is_locked(dev) ((dev)->locked) +/* DSP loader helpers */ +int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, + unsigned int byte_size, struct snd_dma_buffer *bufp); +void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start); +void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev, + struct snd_dma_buffer *dmab); +#else /* CONFIG_SND_HDA_DSP_LOADER */ +#define snd_hdac_dsp_lock_init(dev) do {} while (0) +#define snd_hdac_dsp_lock(dev) do {} while (0) +#define snd_hdac_dsp_unlock(dev) do {} while (0) +#define snd_hdac_stream_is_locked(dev) 0 + +static inline int +snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, + unsigned int byte_size, struct snd_dma_buffer *bufp) +{ + return 0; +} + +static inline void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start) +{ +} + +static inline void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev, + struct snd_dma_buffer *dmab) +{ +} +#endif /* CONFIG_SND_HDA_DSP_LOADER */ + + /* * generic array helpers */ diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig index 001c6588a5ff..7a17fca4f627 100644 --- a/sound/hda/Kconfig +++ b/sound/hda/Kconfig @@ -1,3 +1,6 @@ config SND_HDA_CORE tristate select REGMAP + +config SND_HDA_DSP_LOADER + bool diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index b513a15c777f..7f6b845d90eb 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -33,6 +33,7 @@ void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, azx_dev->index = idx; azx_dev->direction = direction; azx_dev->stream_tag = tag; + snd_hdac_dsp_lock_init(azx_dev); list_add_tail(&azx_dev->list, &bus->stream_list); } EXPORT_SYMBOL_GPL(snd_hdac_stream_init); @@ -534,3 +535,115 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start, } } EXPORT_SYMBOL_GPL(snd_hdac_stream_sync); + +#ifdef CONFIG_SND_HDA_DSP_LOADER +/** + * snd_hdac_dsp_prepare - prepare for DSP loading + * @azx_dev: HD-audio core stream used for DSP loading + * @format: HD-audio stream format + * @byte_size: data chunk byte size + * @bufp: allocated buffer + * + * Allocate the buffer for the given size and set up the given stream for + * DSP loading. Returns the stream tag (>= 0), or a negative error code. + */ +int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format, + unsigned int byte_size, struct snd_dma_buffer *bufp) +{ + struct hdac_bus *bus = azx_dev->bus; + u32 *bdl; + int err; + + snd_hdac_dsp_lock(azx_dev); + spin_lock_irq(&bus->reg_lock); + if (azx_dev->running || azx_dev->locked) { + spin_unlock_irq(&bus->reg_lock); + err = -EBUSY; + goto unlock; + } + azx_dev->locked = true; + spin_unlock_irq(&bus->reg_lock); + + err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV_SG, + byte_size, bufp); + if (err < 0) + goto err_alloc; + + azx_dev->bufsize = byte_size; + azx_dev->period_bytes = byte_size; + azx_dev->format_val = format; + + snd_hdac_stream_reset(azx_dev); + + /* reset BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); + snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); + + azx_dev->frags = 0; + bdl = (u32 *)azx_dev->bdl.area; + err = setup_bdle(bus, bufp, azx_dev, &bdl, 0, byte_size, 0); + if (err < 0) + goto error; + + snd_hdac_stream_setup(azx_dev); + snd_hdac_dsp_unlock(azx_dev); + return azx_dev->stream_tag; + + error: + bus->io_ops->dma_free_pages(bus, bufp); + err_alloc: + spin_lock_irq(&bus->reg_lock); + azx_dev->locked = false; + spin_unlock_irq(&bus->reg_lock); + unlock: + snd_hdac_dsp_unlock(azx_dev); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare); + +/** + * snd_hdac_dsp_trigger - start / stop DSP loading + * @azx_dev: HD-audio core stream used for DSP loading + * @start: trigger start or stop + */ +void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start) +{ + if (start) + snd_hdac_stream_start(azx_dev, true); + else + snd_hdac_stream_stop(azx_dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_dsp_trigger); + +/** + * snd_hdac_dsp_cleanup - clean up the stream from DSP loading to normal + * @azx_dev: HD-audio core stream used for DSP loading + * @dmab: buffer used by DSP loading + */ +void snd_hdac_dsp_cleanup(struct hdac_stream *azx_dev, + struct snd_dma_buffer *dmab) +{ + struct hdac_bus *bus = azx_dev->bus; + + if (!dmab->area || !azx_dev->locked) + return; + + snd_hdac_dsp_lock(azx_dev); + /* reset BDL address */ + snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); + snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); + snd_hdac_stream_writel(azx_dev, SD_CTL, 0); + azx_dev->bufsize = 0; + azx_dev->period_bytes = 0; + azx_dev->format_val = 0; + + bus->io_ops->dma_free_pages(bus, dmab); + dmab->area = NULL; + + spin_lock_irq(&bus->reg_lock); + azx_dev->locked = false; + spin_unlock_irq(&bus->reg_lock); + snd_hdac_dsp_unlock(azx_dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_dsp_cleanup); +#endif /* CONFIG_SND_HDA_DSP_LOADER */ diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index a5ed1c181784..47aa7b8b7519 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -38,9 +38,6 @@ config SND_HDA_TEGRA
if SND_HDA
-config SND_HDA_DSP_LOADER - bool - config SND_HDA_PREALLOC_SIZE int "Pre-allocated buffer size for HD-audio driver" range 0 32768
From: Jeeja KP jeeja.kp@intel.com
Moved azx_alloc_stream_pages and azx_free_stream_pages to controller library.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Subhransu S. Prusty subhransu.s.prusty@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/hdaudio.h | 3 +++ sound/hda/hdac_controller.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ sound/hda/hdac_stream.c | 2 -- 3 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 69f27bc49eb4..59d21848a472 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -314,6 +314,9 @@ void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, void (*ack)(struct hdac_bus *, struct hdac_stream *));
+int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus); +void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus); + /* * macros for easy use */ diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index c0069d0b7875..b5a17cb510a0 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -447,3 +447,61 @@ void snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, } } EXPORT_SYMBOL_GPL(snd_hdac_bus_handle_stream_irq); + +/** + * snd_hdac_bus_alloc_stream_pages - allocate BDL and other buffers + * @bus: HD-audio core bus + * + * Call this after assigning the all streams. + * Returns zero for success, or a negative error code. + */ +int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus) +{ + struct hdac_stream *s; + int num_streams = 0; + int err; + + list_for_each_entry(s, &bus->stream_list, list) { + /* allocate memory for the BDL for each stream */ + err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + BDL_SIZE, &s->bdl); + num_streams++; + if (err < 0) + return -ENOMEM; + } + + if (WARN_ON(!num_streams)) + return -EINVAL; + /* allocate memory for the position buffer */ + err = bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + num_streams * 8, &bus->posbuf); + if (err < 0) + return -ENOMEM; + list_for_each_entry(s, &bus->stream_list, list) + s->posbuf = (__le32 *)(bus->posbuf.area + s->index * 8); + + /* single page (at least 4096 bytes) must suffice for both ringbuffes */ + return bus->io_ops->dma_alloc_pages(bus, SNDRV_DMA_TYPE_DEV, + PAGE_SIZE, &bus->rb); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_alloc_stream_pages); + +/** + * snd_hdac_bus_free_stream_pages - release BDL and other buffers + * @bus: HD-audio core bus + */ +void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus) +{ + struct hdac_stream *s; + + list_for_each_entry(s, &bus->stream_list, list) { + if (s->bdl.area) + bus->io_ops->dma_free_pages(bus, &s->bdl); + } + + if (bus->rb.area) + bus->io_ops->dma_free_pages(bus, &bus->rb); + if (bus->posbuf.area) + bus->io_ops->dma_free_pages(bus, &bus->posbuf); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_free_stream_pages); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 7f6b845d90eb..8bd67a824b5e 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -24,8 +24,6 @@ void snd_hdac_stream_init(struct hdac_bus *bus, struct hdac_stream *azx_dev, int idx, int direction, int tag) { azx_dev->bus = bus; - if (bus->posbuf.area) - azx_dev->posbuf = (__le32 *)(bus->posbuf.area + idx * 8); /* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ azx_dev->sd_addr = bus->remap_addr + (0x20 * idx + 0x80); /* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
There is no much merit to keep the HD-audio codec and controller helper codes in separate modules any longer. Let's merge them into a single helper module.
This patch just changes Makefile entries to merge two individual modules to one. The only code change is the removal of superfluous MODULE_*() macros in one side.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/Makefile | 3 +-- sound/pci/hda/hda_controller.c | 3 --- 2 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index af78fb33a4fd..c5e6651efb49 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -1,10 +1,10 @@ snd-hda-intel-objs := hda_intel.o -snd-hda-controller-objs := hda_controller.o snd-hda-tegra-objs := hda_tegra.o # for haswell power well snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o
snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o +snd-hda-codec-y += hda_controller.o snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o @@ -27,7 +27,6 @@ snd-hda-codec-hdmi-objs := patch_hdmi.o hda_eld.o
# common driver obj-$(CONFIG_SND_HDA) := snd-hda-codec.o -obj-$(CONFIG_SND_HDA) += snd-hda-controller.o
# codec drivers obj-$(CONFIG_SND_HDA_GENERIC) += snd-hda-codec-generic.o diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index b4474e27631d..6b39f2e8c820 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1966,6 +1966,3 @@ int azx_init_stream(struct azx *chip) return 0; } EXPORT_SYMBOL_GPL(azx_init_stream); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Common HDA driver functions");
One less redirection.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 22 ++-------------------- sound/pci/hda/hda_codec.h | 11 ++++++----- sound/pci/hda/hda_controller.c | 24 +++++++++++++++--------- 3 files changed, 23 insertions(+), 34 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b86e2f449e56..7e3dcaba6365 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -507,25 +507,6 @@ static int snd_hda_bus_dev_disconnect(struct snd_device *device) return 0; }
-/* hdac_bus_ops translations */ -static int _hda_bus_command(struct hdac_bus *_bus, unsigned int cmd) -{ - struct hda_bus *bus = container_of(_bus, struct hda_bus, core); - return bus->ops.command(bus, cmd); -} - -static int _hda_bus_get_response(struct hdac_bus *_bus, unsigned int addr, - unsigned int *res) -{ - struct hda_bus *bus = container_of(_bus, struct hda_bus, core); - return bus->ops.get_response(bus, addr, res); -} - -static const struct hdac_bus_ops bus_ops = { - .command = _hda_bus_command, - .get_response = _hda_bus_get_response, -}; - /** * snd_hda_bus_new - create a HDA bus * @card: the card entry @@ -534,6 +515,7 @@ static const struct hdac_bus_ops bus_ops = { * Returns 0 if successful, or a negative error code. */ int snd_hda_bus_new(struct snd_card *card, + const struct hdac_bus_ops *ops, struct hda_bus **busp) { struct hda_bus *bus; @@ -550,7 +532,7 @@ int snd_hda_bus_new(struct snd_card *card, if (!bus) return -ENOMEM;
- err = snd_hdac_bus_init(&bus->core, card->dev, &bus_ops, NULL); + err = snd_hdac_bus_init(&bus->core, card->dev, ops, NULL); if (err < 0) { kfree(bus); return err; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index fc4f76188a1d..b4261721d8f1 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -42,10 +42,6 @@ struct hda_pcm_stream;
/* bus operators */ struct hda_bus_ops { - /* send a single command */ - int (*command)(struct hda_bus *bus, unsigned int cmd); - /* get a response from the last command */ - int (*get_response)(struct hda_bus *bus, unsigned int addr, unsigned int *res); /* free the private data */ void (*private_free)(struct hda_bus *); /* attach a PCM stream */ @@ -99,6 +95,9 @@ struct hda_bus { int primary_dig_out_type; /* primary digital out PCM type */ };
+/* from hdac_bus to hda_bus */ +#define to_hda_bus(bus) container_of(bus, struct hda_bus, core) + /* * codec preset * @@ -327,7 +326,9 @@ struct hda_codec { /* * constructors */ -int snd_hda_bus_new(struct snd_card *card, struct hda_bus **busp); +int snd_hda_bus_new(struct snd_card *card, + const struct hdac_bus_ops *ops, + struct hda_bus **busp); int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_configure(struct hda_codec *codec); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 6b39f2e8c820..666dee232e95 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -1328,8 +1328,9 @@ static int azx_single_get_response(struct hda_bus *bus, unsigned int addr, */
/* send a command */ -static int azx_send_cmd(struct hda_bus *bus, unsigned int val) +static int azx_send_cmd(struct hdac_bus *_bus, unsigned int val) { + struct hda_bus *bus = to_hda_bus(_bus); struct azx *chip = bus->private_data;
if (chip->disabled) @@ -1342,9 +1343,10 @@ static int azx_send_cmd(struct hda_bus *bus, unsigned int val) }
/* get a response */ -static int azx_get_response(struct hda_bus *bus, unsigned int addr, +static int azx_get_response(struct hdac_bus *_bus, unsigned int addr, unsigned int *res) { + struct hda_bus *bus = to_hda_bus(_bus); struct azx *chip = bus->private_data; if (chip->disabled) return 0; @@ -1354,6 +1356,11 @@ static int azx_get_response(struct hda_bus *bus, unsigned int addr, return azx_rirb_get_response(bus, addr, res); }
+static const struct hdac_bus_ops bus_core_ops = { + .command = azx_send_cmd, + .get_response = azx_get_response, +}; + #ifdef CONFIG_SND_HDA_DSP_LOADER /* * DSP loading code (e.g. for CA0132) @@ -1762,15 +1769,16 @@ static int probe_codec(struct azx *chip, int addr) { unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; + struct hdac_bus *bus = &chip->bus->core; int err; unsigned int res;
- mutex_lock(&chip->bus->core.cmd_mutex); + mutex_lock(&bus->cmd_mutex); chip->probing = 1; - azx_send_cmd(chip->bus, cmd); - err = azx_get_response(chip->bus, addr, &res); + azx_send_cmd(bus, cmd); + err = azx_get_response(bus, addr, &res); chip->probing = 0; - mutex_unlock(&chip->bus->core.cmd_mutex); + mutex_unlock(&bus->cmd_mutex); if (err < 0 || res == -1) return -EIO; dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr); @@ -1811,8 +1819,6 @@ static int get_jackpoll_interval(struct azx *chip) }
static struct hda_bus_ops bus_ops = { - .command = azx_send_cmd, - .get_response = azx_get_response, .attach_pcm = azx_attach_pcm_stream, .bus_reset = azx_bus_reset, #ifdef CONFIG_SND_HDA_DSP_LOADER @@ -1828,7 +1834,7 @@ int azx_bus_create(struct azx *chip, const char *model) struct hda_bus *bus; int err;
- err = snd_hda_bus_new(chip->card, &bus); + err = snd_hda_bus_new(chip->card, &bus_core_ops, &bus); if (err < 0) return err;
One less redirection again. This also requires the change of the call order in the toplevel divers. Namely, the bus has to be created at first before other initializations since the memory allocation ops are called through bus object now.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 3 ++- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_controller.c | 26 +++++++++++++------------- sound/pci/hda/hda_controller.h | 40 +++++++++++++++------------------------- sound/pci/hda/hda_intel.c | 34 +++++++++++++++++++++------------- sound/pci/hda/hda_tegra.c | 25 ++++++++++++++----------- 6 files changed, 66 insertions(+), 63 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7e3dcaba6365..ddebe7541390 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -516,6 +516,7 @@ static int snd_hda_bus_dev_disconnect(struct snd_device *device) */ int snd_hda_bus_new(struct snd_card *card, const struct hdac_bus_ops *ops, + const struct hdac_io_ops *io_ops, struct hda_bus **busp) { struct hda_bus *bus; @@ -532,7 +533,7 @@ int snd_hda_bus_new(struct snd_card *card, if (!bus) return -ENOMEM;
- err = snd_hdac_bus_init(&bus->core, card->dev, ops, NULL); + err = snd_hdac_bus_init(&bus->core, card->dev, ops, io_ops); if (err < 0) { kfree(bus); return err; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index b4261721d8f1..c8031360de90 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -328,6 +328,7 @@ struct hda_codec { */ int snd_hda_bus_new(struct snd_card *card, const struct hdac_bus_ops *ops, + const struct hdac_io_ops *io_ops, struct hda_bus **busp); int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, unsigned int codec_addr, struct hda_codec **codecp); diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 666dee232e95..aadce642aabc 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -985,8 +985,8 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, static int azx_alloc_cmd_io(struct azx *chip) { /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - return chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, - PAGE_SIZE, &chip->rb); + return chip->io_ops->dma_alloc_pages(azx_bus(chip), SNDRV_DMA_TYPE_DEV, + PAGE_SIZE, &chip->rb); }
static void azx_init_cmd_io(struct azx *chip) @@ -1396,8 +1396,8 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, azx_dev->locked = 1; spin_unlock_irq(&chip->reg_lock);
- err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV_SG, - byte_size, bufp); + err = chip->io_ops->dma_alloc_pages(&bus->core, SNDRV_DMA_TYPE_DEV_SG, + byte_size, bufp); if (err < 0) goto err_alloc;
@@ -1422,7 +1422,7 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, return azx_dev->stream_tag;
error: - chip->ops->dma_free_pages(chip, bufp); + chip->io_ops->dma_free_pages(&bus->core, bufp); err_alloc: spin_lock_irq(&chip->reg_lock); if (azx_dev->opened) @@ -1464,7 +1464,7 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, azx_dev->period_bytes = 0; azx_dev->format_val = 0;
- chip->ops->dma_free_pages(chip, dmab); + chip->io_ops->dma_free_pages(&bus->core, dmab); dmab->area = NULL;
spin_lock_irq(&chip->reg_lock); @@ -1483,14 +1483,14 @@ int azx_alloc_stream_pages(struct azx *chip) for (i = 0; i < chip->num_streams; i++) { dsp_lock_init(&chip->azx_dev[i]); /* allocate memory for the BDL for each stream */ - err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, + err = chip->io_ops->dma_alloc_pages(azx_bus(chip), SNDRV_DMA_TYPE_DEV, BDL_SIZE, &chip->azx_dev[i].bdl); if (err < 0) return -ENOMEM; } /* allocate memory for the position buffer */ - err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, + err = chip->io_ops->dma_alloc_pages(azx_bus(chip), SNDRV_DMA_TYPE_DEV, chip->num_streams * 8, &chip->posbuf); if (err < 0) return -ENOMEM; @@ -1509,13 +1509,13 @@ void azx_free_stream_pages(struct azx *chip) if (chip->azx_dev) { for (i = 0; i < chip->num_streams; i++) if (chip->azx_dev[i].bdl.area) - chip->ops->dma_free_pages( - chip, &chip->azx_dev[i].bdl); + chip->io_ops->dma_free_pages(azx_bus(chip), + &chip->azx_dev[i].bdl); } if (chip->rb.area) - chip->ops->dma_free_pages(chip, &chip->rb); + chip->io_ops->dma_free_pages(azx_bus(chip), &chip->rb); if (chip->posbuf.area) - chip->ops->dma_free_pages(chip, &chip->posbuf); + chip->io_ops->dma_free_pages(azx_bus(chip), &chip->posbuf); } EXPORT_SYMBOL_GPL(azx_free_stream_pages);
@@ -1834,7 +1834,7 @@ int azx_bus_create(struct azx *chip, const char *model) struct hda_bus *bus; int err;
- err = snd_hda_bus_new(chip->card, &bus_core_ops, &bus); + err = snd_hda_bus_new(chip->card, &bus_core_ops, chip->io_ops, &bus); if (err < 0) return err;
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 15a796c21b9d..d6b090daa7dc 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -125,21 +125,8 @@ struct azx;
/* Functions to read/write to hda registers. */ struct hda_controller_ops { - /* Register Access */ - 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 azx *); - /* Allocation ops */ - int (*dma_alloc_pages)(struct azx *chip, - int type, - size_t size, - struct snd_dma_buffer *buf); - void (*dma_free_pages)(struct azx *chip, struct snd_dma_buffer *buf); int (*substream_alloc_pages)(struct azx *chip, struct snd_pcm_substream *substream, size_t size); @@ -179,6 +166,7 @@ struct azx {
/* Register interaction. */ const struct hda_controller_ops *ops; + const struct hdac_io_ops *io_ops;
/* position adjustment callbacks */ azx_get_pos_callback_t get_position[2]; @@ -239,6 +227,8 @@ struct azx { #endif };
+#define azx_bus(chip) (&(chip)->bus->core) + #ifdef CONFIG_X86 #define azx_snoop(chip) ((chip)->snoop) #else @@ -250,30 +240,30 @@ struct azx { */
#define azx_writel(chip, reg, value) \ - ((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)) #define azx_readl(chip, reg) \ - ((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)) #define azx_writew(chip, reg, value) \ - ((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)) #define azx_readw(chip, reg) \ - ((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)) #define azx_writeb(chip, reg, value) \ - ((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)) #define azx_readb(chip, reg) \ - ((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_readb((chip)->remap_addr + AZX_REG_##reg))
#define azx_sd_writel(chip, dev, reg, value) \ - ((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg)) #define azx_sd_readl(chip, dev, reg) \ - ((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_readl((dev)->sd_addr + AZX_REG_##reg)) #define azx_sd_writew(chip, dev, reg, value) \ - ((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg)) #define azx_sd_readw(chip, dev, reg) \ - ((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_readw((dev)->sd_addr + AZX_REG_##reg)) #define azx_sd_writeb(chip, dev, reg, value) \ - ((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg)) #define azx_sd_readb(chip, dev, reg) \ - ((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg)) + ((chip)->io_ops->reg_readb((dev)->sd_addr + AZX_REG_##reg))
#define azx_has_pm_runtime(chip) \ (!AZX_DCAPS_PM_RUNTIME || ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index e1c210515581..7492d11fd8ff 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1365,9 +1365,11 @@ static void azx_probe_work(struct work_struct *work) /* * constructor */ +static const struct hdac_io_ops pci_hda_io_ops; +static const struct hda_controller_ops pci_hda_ops; + static int azx_create(struct snd_card *card, struct pci_dev *pci, int dev, unsigned int driver_caps, - const struct hda_controller_ops *hda_ops, struct azx **rchip) { static struct snd_device_ops ops = { @@ -1394,7 +1396,8 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, mutex_init(&chip->open_mutex); chip->card = card; chip->pci = pci; - chip->ops = hda_ops; + chip->ops = &pci_hda_ops; + chip->io_ops = &pci_hda_io_ops; chip->irq = -1; chip->driver_caps = driver_caps; chip->driver_type = driver_caps & 0xff; @@ -1681,15 +1684,16 @@ static int disable_msi_reset_irq(struct azx *chip) }
/* DMA page allocation helpers. */ -static int dma_alloc_pages(struct azx *chip, +static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size, struct snd_dma_buffer *buf) { + struct azx *chip = to_hda_bus(bus)->private_data; int err;
err = snd_dma_alloc_pages(type, - chip->card->dev, + bus->dev, size, buf); if (err < 0) return err; @@ -1697,8 +1701,10 @@ static int dma_alloc_pages(struct azx *chip, return 0; }
-static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf) +static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { + struct azx *chip = to_hda_bus(bus)->private_data; + mark_pages_wc(chip, buf, false); snd_dma_free_pages(buf); } @@ -1740,16 +1746,19 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream, #endif }
-static const struct hda_controller_ops pci_hda_ops = { +static const struct hdac_io_ops pci_hda_io_ops = { .reg_writel = pci_azx_writel, .reg_readl = pci_azx_readl, .reg_writew = pci_azx_writew, .reg_readw = pci_azx_readw, .reg_writeb = pci_azx_writeb, .reg_readb = pci_azx_readb, - .disable_msi_reset_irq = disable_msi_reset_irq, .dma_alloc_pages = dma_alloc_pages, .dma_free_pages = dma_free_pages, +}; + +static const struct hda_controller_ops pci_hda_ops = { + .disable_msi_reset_irq = disable_msi_reset_irq, .substream_alloc_pages = substream_alloc_pages, .substream_free_pages = substream_free_pages, .pcm_mmap_prepare = pcm_mmap_prepare, @@ -1780,8 +1789,7 @@ static int azx_probe(struct pci_dev *pci, return err; }
- err = azx_create(card, pci, dev, pci_id->driver_data, - &pci_hda_ops, &chip); + err = azx_create(card, pci, dev, pci_id->driver_data, &chip); if (err < 0) goto out_free; card->private_data = chip; @@ -1862,6 +1870,10 @@ static int azx_probe_continue(struct azx *chip) #endif }
+ err = azx_bus_create(chip, model[dev]); + if (err < 0) + goto out_free; + err = azx_first_init(chip); if (err < 0) goto out_free; @@ -1871,10 +1883,6 @@ static int azx_probe_continue(struct azx *chip) #endif
/* create codec instances */ - err = azx_bus_create(chip, model[dev]); - if (err < 0) - goto out_free; - err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); if (err < 0) goto out_free; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 2e4fd5c56d3b..b150cb50961c 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -87,13 +87,13 @@ MODULE_PARM_DESC(power_save, /* * DMA page allocation ops. */ -static int dma_alloc_pages(struct azx *chip, int type, size_t size, +static int dma_alloc_pages(struct hdac_bus *bus, int type, size_t size, struct snd_dma_buffer *buf) { - return snd_dma_alloc_pages(type, chip->card->dev, size, buf); + return snd_dma_alloc_pages(type, bus->dev, size, buf); }
-static void dma_free_pages(struct azx *chip, struct snd_dma_buffer *buf) +static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { snd_dma_free_pages(buf); } @@ -173,7 +173,7 @@ static u8 hda_tegra_readb(u8 *addr) return (v >> shift) & 0xff; }
-static const struct hda_controller_ops hda_tegra_ops = { +static const struct hdac_io_ops hda_tegra_io_ops = { .reg_writel = hda_tegra_writel, .reg_readl = hda_tegra_readl, .reg_writew = hda_tegra_writew, @@ -182,6 +182,9 @@ static const struct hda_controller_ops hda_tegra_ops = { .reg_readb = hda_tegra_readb, .dma_alloc_pages = dma_alloc_pages, .dma_free_pages = dma_free_pages, +}; + +static const struct hda_controller_ops hda_tegra_ops = { .substream_alloc_pages = substream_alloc_pages, .substream_free_pages = substream_free_pages, }; @@ -409,7 +412,6 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) */ static int hda_tegra_create(struct snd_card *card, unsigned int driver_caps, - const struct hda_controller_ops *hda_ops, struct hda_tegra *hda) { static struct snd_device_ops ops = { @@ -423,7 +425,8 @@ static int hda_tegra_create(struct snd_card *card, spin_lock_init(&chip->reg_lock); mutex_init(&chip->open_mutex); chip->card = card; - chip->ops = hda_ops; + chip->ops = &hda_tegra_ops; + chip->io_ops = &hda_tegra_io_ops; chip->irq = -1; chip->driver_caps = driver_caps; chip->driver_type = driver_caps & 0xff; @@ -471,7 +474,11 @@ static int hda_tegra_probe(struct platform_device *pdev) return err; }
- err = hda_tegra_create(card, driver_flags, &hda_tegra_ops, hda); + err = azx_bus_create(chip, NULL); + if (err < 0) + goto out_free; + + err = hda_tegra_create(card, driver_flags, hda); if (err < 0) goto out_free; card->private_data = chip; @@ -483,10 +490,6 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free;
/* create codec instances */ - err = azx_bus_create(chip, NULL); - if (err < 0) - goto out_free; - err = azx_probe_codecs(chip, 0); if (err < 0) goto out_free;
Embed hdac_stream object into azx_dev, and use a few basic helper functions. The most of helper codes for hdac_stream aren't still used yet.
Also this commit disables the tracepoints temporarily due to build problems. It'll be enabled again later.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_controller.c | 415 ++++++++++++++++------------------------- sound/pci/hda/hda_controller.h | 61 ++---- sound/pci/hda/hda_intel.c | 80 ++++---- sound/pci/hda/hda_tegra.c | 14 +- 4 files changed, 211 insertions(+), 359 deletions(-)
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index aadce642aabc..0b85c88c75ac 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -31,92 +31,15 @@ #include <sound/initval.h> #include "hda_controller.h"
-#define CREATE_TRACE_POINTS -#include "hda_intel_trace.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 +#define dsp_lock(dev) snd_hdac_dsp_lock(azx_stream(dev)) +#define dsp_unlock(dev) snd_hdac_dsp_unlock(azx_stream(dev)) +#define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev))
/* * AZX stream operations. */
-/* start a stream */ -static void azx_stream_start(struct azx *chip, struct azx_dev *azx_dev) -{ - /* - * Before stream start, initialize parameter - */ - azx_dev->insufficient = 1; - - /* 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); -} - -/* stop DMA */ -static void azx_stream_clear(struct azx *chip, struct azx_dev *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 azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev) -{ - azx_stream_clear(chip, azx_dev); - /* disable SIE */ - azx_writel(chip, INTCTL, - azx_readl(chip, INTCTL) & ~(1 << azx_dev->index)); -} -EXPORT_SYMBOL_GPL(azx_stream_stop); - -/* reset stream */ -static void azx_stream_reset(struct azx *chip, struct azx_dev *azx_dev) -{ - unsigned char val; - int timeout; - - azx_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; -} - /* * set up the SD for streaming */ @@ -124,31 +47,31 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) { unsigned int val; /* make sure the run bit is zero for SD */ - azx_stream_clear(chip, azx_dev); + snd_hdac_stream_clear(azx_stream(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_dev->core.stream_tag << SD_CTL_STREAM_TAG_SHIFT); if (!azx_snoop(chip)) val |= SD_CTL_TRAFFIC_PRIO; 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); + azx_sd_writel(chip, azx_dev, SD_CBL, azx_dev->core.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); + azx_sd_writew(chip, azx_dev, SD_FORMAT, azx_dev->core.format_val);
/* program the stream LVI (last valid index) of the BDL */ - azx_sd_writew(chip, azx_dev, SD_LVI, azx_dev->frags - 1); + azx_sd_writew(chip, azx_dev, SD_LVI, azx_dev->core.frags - 1);
/* program the BDL address */ /* lower BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPL, (u32)azx_dev->bdl.addr); + azx_sd_writel(chip, azx_dev, SD_BDLPL, (u32)azx_dev->core.bdl.addr); /* upper BDL address */ azx_sd_writel(chip, azx_dev, SD_BDLPU, - upper_32_bits(azx_dev->bdl.addr)); + upper_32_bits(azx_dev->core.bdl.addr));
/* enable the position buffer */ if (chip->get_position[0] != azx_get_pos_lpib || @@ -169,54 +92,24 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) static inline struct azx_dev * azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream) { - int dev, i, nums; - struct azx_dev *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 azx_dev *azx_dev = &chip->azx_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; + struct hdac_stream *s; + + s = snd_hdac_stream_assign(azx_bus(chip), substream); + if (!s) + return NULL; + return stream_to_azx_dev(s); }
/* release the assigned stream */ static inline void azx_release_device(struct azx_dev *azx_dev) { - azx_dev->opened = 0; + snd_hdac_stream_release(azx_stream(azx_dev)); }
static cycle_t azx_cc_read(const struct cyclecounter *cc) { - struct azx_dev *azx_dev = container_of(cc, struct azx_dev, azx_cc); - struct snd_pcm_substream *substream = azx_dev->substream; + struct azx_dev *azx_dev = container_of(cc, struct azx_dev, core.cc); + struct snd_pcm_substream *substream = azx_dev->core.substream; struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip;
@@ -227,8 +120,8 @@ static void azx_timecounter_init(struct snd_pcm_substream *substream, bool force, cycle_t last) { struct azx_dev *azx_dev = get_azx_dev(substream); - struct timecounter *tc = &azx_dev->azx_tc; - struct cyclecounter *cc = &azx_dev->azx_cc; + struct timecounter *tc = &azx_dev->core.tc; + struct cyclecounter *cc = &azx_dev->core.cc; u64 nsec;
cc->read = azx_cc_read; @@ -298,7 +191,7 @@ static int setup_bdle(struct azx *chip, dma_addr_t addr; int chunk;
- if (azx_dev->frags >= AZX_MAX_BDL_ENTRIES) + if (azx_dev->core.frags >= AZX_MAX_BDL_ENTRIES) return -EINVAL;
addr = snd_sgbuf_get_addr(dmab, ofs); @@ -320,7 +213,7 @@ static int setup_bdle(struct azx *chip, size -= chunk; bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); bdl += 4; - azx_dev->frags++; + azx_dev->core.frags++; ofs += chunk; } *bdlp = bdl; @@ -342,17 +235,17 @@ static int azx_setup_periods(struct azx *chip, 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; + period_bytes = azx_dev->core.period_bytes; + periods = azx_dev->core.bufsize / period_bytes;
/* program the initial BDL entries */ - bdl = (u32 *)azx_dev->bdl.area; + bdl = (u32 *)azx_dev->core.bdl.area; ofs = 0; - azx_dev->frags = 0; + azx_dev->core.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) { + if (!azx_dev->core.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; @@ -385,7 +278,7 @@ static int azx_setup_periods(struct azx *chip, ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream), azx_dev, &bdl, ofs, period_bytes, - !azx_dev->no_period_wakeup); + !azx_dev->core.no_period_wakeup); if (ofs < 0) goto error; } @@ -393,7 +286,7 @@ static int azx_setup_periods(struct azx *chip,
error: dev_err(chip->card->dev, "Too many BDL entries: buffer=%d, period=%d\n", - azx_dev->bufsize, period_bytes); + azx_dev->core.bufsize, period_bytes); return -EINVAL; }
@@ -411,8 +304,8 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
mutex_lock(&chip->open_mutex); spin_lock_irqsave(&chip->reg_lock, flags); - azx_dev->substream = NULL; - azx_dev->running = 0; + azx_dev->core.substream = NULL; + azx_dev->core.running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); if (hinfo->ops.close) @@ -457,9 +350,9 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) 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; + azx_dev->core.bufsize = 0; + azx_dev->core.period_bytes = 0; + azx_dev->core.format_val = 0; }
snd_hda_codec_cleanup(apcm->codec, hinfo, substream); @@ -489,7 +382,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) goto unlock; }
- azx_stream_reset(chip, azx_dev); + snd_hdac_stream_reset(azx_stream(azx_dev)); format_val = snd_hda_calc_stream_format(apcm->codec, runtime->rate, runtime->channels, @@ -510,14 +403,14 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) dev_dbg(chip->card->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; + if (bufsize != azx_dev->core.bufsize || + period_bytes != azx_dev->core.period_bytes || + format_val != azx_dev->core.format_val || + runtime->no_period_wakeup != azx_dev->core.no_period_wakeup) { + azx_dev->core.bufsize = bufsize; + azx_dev->core.period_bytes = period_bytes; + azx_dev->core.format_val = format_val; + azx_dev->core.no_period_wakeup = runtime->no_period_wakeup; err = azx_setup_periods(chip, substream, azx_dev); if (err < 0) goto unlock; @@ -528,27 +421,27 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) * 64 frames */ if (runtime->period_size > 64) - azx_dev->delay_negative_threshold = -frames_to_bytes(runtime, 64); + azx_dev->core.delay_negative_threshold = -frames_to_bytes(runtime, 64); else - azx_dev->delay_negative_threshold = 0; + azx_dev->core.delay_negative_threshold = 0;
/* wallclk has 24Mhz clock source */ - azx_dev->period_wallclk = (((runtime->period_size * 24000) / + azx_dev->core.period_wallclk = (((runtime->period_size * 24000) / runtime->rate) * 1000); azx_setup_controller(chip, azx_dev); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - azx_dev->fifo_size = + azx_dev->core.fifo_size = azx_sd_readw(chip, azx_dev, SD_FIFOSIZE) + 1; else - azx_dev->fifo_size = 0; + azx_dev->core.fifo_size = 0;
- stream_tag = azx_dev->stream_tag; + stream_tag = azx_dev->core.stream_tag; /* CA-IBG chips need the playback stream starting from 1 */ if ((chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) && stream_tag > chip->capture_streams) stream_tag -= chip->capture_streams; err = snd_hda_codec_prepare(apcm->codec, hinfo, stream_tag, - azx_dev->format_val, substream); + azx_dev->core.format_val, substream);
unlock: if (!err) @@ -567,7 +460,6 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) int nwait, timeout;
azx_dev = get_azx_dev(substream); - trace_azx_pcm_trigger(chip, azx_dev, cmd);
if (dsp_is_locked(azx_dev) || !azx_dev->prepared) return -EPIPE; @@ -592,7 +484,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) if (s->pcm->card != substream->pcm->card) continue; azx_dev = get_azx_dev(s); - sbits |= 1 << azx_dev->index; + sbits |= 1 << azx_dev->core.index; nsync++; snd_pcm_trigger_done(s, substream); } @@ -611,15 +503,11 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) continue; azx_dev = get_azx_dev(s); if (start) { - azx_dev->start_wallclk = azx_readl(chip, WALLCLK); - if (!rstart) - azx_dev->start_wallclk -= - azx_dev->period_wallclk; - azx_stream_start(chip, azx_dev); + azx_dev->insufficient = 1; + snd_hdac_stream_start(azx_stream(azx_dev), true); } else { - azx_stream_stop(chip, azx_dev); + snd_hdac_stream_stop(azx_stream(azx_dev)); } - azx_dev->running = start; } spin_unlock(&chip->reg_lock); if (start) { @@ -672,7 +560,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
/* same start cycle for master and group */ azx_dev = get_azx_dev(substream); - cycle_last = azx_dev->azx_tc.cycle_last; + cycle_last = azx_dev->core.tc.cycle_last;
snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) @@ -687,20 +575,20 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev) { - return azx_sd_readl(chip, azx_dev, SD_LPIB); + return snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); } EXPORT_SYMBOL_GPL(azx_get_pos_lpib);
unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev) { - return le32_to_cpu(*azx_dev->posbuf); + return snd_hdac_stream_get_pos_posbuf(azx_stream(azx_dev)); } EXPORT_SYMBOL_GPL(azx_get_pos_posbuf);
unsigned int azx_get_position(struct azx *chip, struct azx_dev *azx_dev) { - struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_substream *substream = azx_dev->core.substream; unsigned int pos; int stream = substream->stream; int delay = 0; @@ -710,7 +598,7 @@ unsigned int azx_get_position(struct azx *chip, else /* use the position buffer as default */ pos = azx_get_pos_posbuf(chip, azx_dev);
- if (pos >= azx_dev->bufsize) + if (pos >= azx_dev->core.bufsize) pos = 0;
if (substream->runtime) { @@ -725,7 +613,6 @@ unsigned int azx_get_position(struct azx *chip, substream->runtime->delay = delay; }
- trace_azx_get_position(chip, azx_dev, pos, delay); return pos; } EXPORT_SYMBOL_GPL(azx_get_position); @@ -752,7 +639,7 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
snd_pcm_gettime(substream->runtime, system_ts);
- nsec = timecounter_read(&azx_dev->azx_tc); + nsec = timecounter_read(&azx_dev->core.tc); nsec = div_u64(nsec, 3); /* can be optimized */ if (audio_tstamp_config->report_delay) nsec = azx_adjust_codec_delay(substream, nsec); @@ -875,8 +762,8 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) }
spin_lock_irqsave(&chip->reg_lock, flags); - azx_dev->substream = substream; - azx_dev->running = 0; + azx_dev->core.substream = substream; + azx_dev->core.running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags);
runtime->private_data = azx_dev; @@ -1370,7 +1257,14 @@ static const struct hdac_bus_ops bus_core_ops = { static struct azx_dev * azx_get_dsp_loader_dev(struct azx *chip) { - return &chip->azx_dev[chip->playback_index_offset]; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + + list_for_each_entry(s, &bus->stream_list, list) + if (s->index == chip->playback_index_offset) + return stream_to_azx_dev(s); + + return NULL; }
static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, @@ -1386,14 +1280,14 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
dsp_lock(azx_dev); spin_lock_irq(&chip->reg_lock); - if (azx_dev->running || azx_dev->locked) { + if (azx_dev->core.running || azx_dev->core.locked) { spin_unlock_irq(&chip->reg_lock); err = -EBUSY; goto unlock; } azx_dev->prepared = 0; chip->saved_azx_dev = *azx_dev; - azx_dev->locked = 1; + azx_dev->core.locked = 1; spin_unlock_irq(&chip->reg_lock);
err = chip->io_ops->dma_alloc_pages(&bus->core, SNDRV_DMA_TYPE_DEV_SG, @@ -1401,33 +1295,33 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, if (err < 0) goto err_alloc;
- azx_dev->bufsize = byte_size; - azx_dev->period_bytes = byte_size; - azx_dev->format_val = format; + azx_dev->core.bufsize = byte_size; + azx_dev->core.period_bytes = byte_size; + azx_dev->core.format_val = format;
- azx_stream_reset(chip, azx_dev); + snd_hdac_stream_reset(azx_stream(azx_dev));
/* reset BDL address */ azx_sd_writel(chip, azx_dev, SD_BDLPL, 0); azx_sd_writel(chip, azx_dev, SD_BDLPU, 0);
- azx_dev->frags = 0; - bdl = (u32 *)azx_dev->bdl.area; + azx_dev->core.frags = 0; + bdl = (u32 *)azx_dev->core.bdl.area; err = setup_bdle(chip, bufp, azx_dev, &bdl, 0, byte_size, 0); if (err < 0) goto error;
azx_setup_controller(chip, azx_dev); dsp_unlock(azx_dev); - return azx_dev->stream_tag; + return azx_dev->core.stream_tag;
error: chip->io_ops->dma_free_pages(&bus->core, bufp); err_alloc: spin_lock_irq(&chip->reg_lock); - if (azx_dev->opened) + if (azx_dev->core.opened) *azx_dev = chip->saved_azx_dev; - azx_dev->locked = 0; + azx_dev->core.locked = 0; spin_unlock_irq(&chip->reg_lock); unlock: dsp_unlock(azx_dev); @@ -1440,10 +1334,9 @@ static void azx_load_dsp_trigger(struct hda_bus *bus, bool start) struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
if (start) - azx_stream_start(chip, azx_dev); + snd_hdac_stream_start(azx_stream(azx_dev), false); else - azx_stream_stop(chip, azx_dev); - azx_dev->running = start; + snd_hdac_stream_stop(azx_stream(azx_dev)); }
static void azx_load_dsp_cleanup(struct hda_bus *bus, @@ -1452,7 +1345,7 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, struct azx *chip = bus->private_data; struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
- if (!dmab->area || !azx_dev->locked) + if (!dmab->area || !azx_dev->core.locked) return;
dsp_lock(azx_dev); @@ -1460,17 +1353,17 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, 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; + azx_dev->core.bufsize = 0; + azx_dev->core.period_bytes = 0; + azx_dev->core.format_val = 0;
chip->io_ops->dma_free_pages(&bus->core, dmab); dmab->area = NULL;
spin_lock_irq(&chip->reg_lock); - if (azx_dev->opened) + if (azx_dev->core.opened) *azx_dev = chip->saved_azx_dev; - azx_dev->locked = 0; + azx_dev->core.locked = 0; spin_unlock_irq(&chip->reg_lock); dsp_unlock(azx_dev); } @@ -1478,17 +1371,18 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus,
int azx_alloc_stream_pages(struct azx *chip) { - int i, err; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + int err;
- for (i = 0; i < chip->num_streams; i++) { - dsp_lock_init(&chip->azx_dev[i]); + list_for_each_entry(s, &bus->stream_list, list) { /* allocate memory for the BDL for each stream */ err = chip->io_ops->dma_alloc_pages(azx_bus(chip), SNDRV_DMA_TYPE_DEV, - BDL_SIZE, - &chip->azx_dev[i].bdl); + BDL_SIZE, &s->bdl); if (err < 0) return -ENOMEM; } + /* allocate memory for the position buffer */ err = chip->io_ops->dma_alloc_pages(azx_bus(chip), SNDRV_DMA_TYPE_DEV, chip->num_streams * 8, &chip->posbuf); @@ -1505,13 +1399,15 @@ EXPORT_SYMBOL_GPL(azx_alloc_stream_pages);
void azx_free_stream_pages(struct azx *chip) { - int i; - if (chip->azx_dev) { - for (i = 0; i < chip->num_streams; i++) - if (chip->azx_dev[i].bdl.area) - chip->io_ops->dma_free_pages(azx_bus(chip), - &chip->azx_dev[i].bdl); + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s, *next; + + list_for_each_entry_safe(s, next, &bus->stream_list, list) { + if (s->bdl.area) + chip->io_ops->dma_free_pages(azx_bus(chip), &s->bdl); + kfree(s); } + if (chip->rb.area) chip->io_ops->dma_free_pages(azx_bus(chip), &chip->rb); if (chip->posbuf.area) @@ -1607,15 +1503,12 @@ static void azx_int_enable(struct azx *chip) /* disable interrupts */ static void azx_int_disable(struct azx *chip) { - int i; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s;
/* disable interrupts in stream descriptor */ - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_sd_writeb(chip, azx_dev, SD_CTL, - azx_sd_readb(chip, azx_dev, SD_CTL) & - ~SD_INT_MASK); - } + list_for_each_entry(s, &bus->stream_list, list) + snd_hdac_stream_updateb(s, SD_CTL, SD_INT_MASK, 0);
/* disable SIE for all streams */ azx_writeb(chip, INTCTL, 0); @@ -1628,13 +1521,12 @@ static void azx_int_disable(struct azx *chip) /* clear interrupts */ static void azx_int_clear(struct azx *chip) { - int i; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s;
/* clear stream status */ - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; - azx_sd_writeb(chip, azx_dev, SD_STS, SD_INT_MASK); - } + list_for_each_entry(s, &bus->stream_list, list) + snd_hdac_stream_writeb(s, SD_STS, SD_INT_MASK);
/* clear STATESTS */ azx_writew(chip, STATESTS, STATESTS_INT_MASK); @@ -1673,6 +1565,16 @@ void azx_init_chip(struct azx *chip, bool full_reset) } EXPORT_SYMBOL_GPL(azx_init_chip);
+void azx_stop_all_streams(struct azx *chip) +{ + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + + list_for_each_entry(s, &bus->stream_list, list) + snd_hdac_stream_stop(s); +} +EXPORT_SYMBOL_GPL(azx_stop_all_streams); + void azx_stop_chip(struct azx *chip) { if (!chip->initialized) @@ -1696,13 +1598,26 @@ EXPORT_SYMBOL_GPL(azx_stop_chip); /* * interrupt handler */ +static void stream_update(struct hdac_bus *bus, struct hdac_stream *s) +{ + struct hda_bus *hbus = container_of(bus, struct hda_bus, core); + struct azx *chip = hbus->private_data; + struct azx_dev *azx_dev = stream_to_azx_dev(s); + + /* check whether this IRQ is really acceptable */ + if (!chip->ops->position_check || + chip->ops->position_check(chip, azx_dev)) { + spin_unlock(&chip->reg_lock); + snd_pcm_period_elapsed(azx_dev->core.substream); + spin_lock(&chip->reg_lock); + } +} + irqreturn_t azx_interrupt(int irq, void *dev_id) { struct azx *chip = dev_id; - struct azx_dev *azx_dev; + struct hdac_bus *bus = azx_bus(chip); u32 status; - u8 sd_status; - int i;
#ifdef CONFIG_PM if (azx_has_pm_runtime(chip)) @@ -1723,23 +1638,7 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) return IRQ_NONE; }
- for (i = 0; i < chip->num_streams; i++) { - azx_dev = &chip->azx_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(&chip->reg_lock); - snd_pcm_period_elapsed(azx_dev->substream); - spin_lock(&chip->reg_lock); - } - } - } + snd_hdac_bus_handle_stream_irq(bus, status, stream_update);
/* clear rirb int */ status = azx_readb(chip, RIRBSTS); @@ -1769,7 +1668,7 @@ static int probe_codec(struct azx *chip, int addr) { unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; - struct hdac_bus *bus = &chip->bus->core; + struct hdac_bus *bus = azx_bus(chip); int err; unsigned int res;
@@ -1927,33 +1826,32 @@ int azx_codec_configure(struct azx *chip) } EXPORT_SYMBOL_GPL(azx_codec_configure);
- -static bool is_input_stream(struct azx *chip, unsigned char index) +static int stream_direction(struct azx *chip, unsigned char index) { - return (index >= chip->capture_index_offset && - index < chip->capture_index_offset + chip->capture_streams); + if (index >= chip->capture_index_offset && + index < chip->capture_index_offset + chip->capture_streams) + return SNDRV_PCM_STREAM_CAPTURE; + return SNDRV_PCM_STREAM_PLAYBACK; }
/* initialize SD streams */ int azx_init_stream(struct azx *chip) { int i; - int in_stream_tag = 0; - int out_stream_tag = 0; + int stream_tags[2] = { 0, 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 azx_dev *azx_dev = &chip->azx_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; + struct azx_dev *azx_dev = kzalloc(sizeof(*azx_dev), GFP_KERNEL); + int dir, tag; + + if (!azx_dev) + return -ENOMEM;
+ dir = stream_direction(chip, i); /* stream tag must be unique throughout * the stream direction group, * valid values 1...15 @@ -1961,12 +1859,11 @@ int azx_init_stream(struct azx *chip) * 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; + tag = ++stream_tags[dir]; else - azx_dev->stream_tag = i + 1; + tag = i + 1; + snd_hdac_stream_init(azx_bus(chip), azx_stream(azx_dev), + i, dir, tag); }
return 0; diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index d6b090daa7dc..b45568d83860 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -59,36 +59,10 @@ enum { };
struct azx_dev { - 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; + struct hdac_stream core; + unsigned int irq_pending:1; unsigned int prepared:1; - unsigned int locked:1; /* * For VIA: * A flag to ensure DMA position is 0 @@ -96,19 +70,11 @@ struct azx_dev { */ unsigned int insufficient:1; unsigned int wc_marked:1; - unsigned int no_period_wakeup:1; - - struct timecounter azx_tc; - struct cyclecounter azx_cc; - - int delay_negative_threshold; - -#ifdef CONFIG_SND_HDA_DSP_LOADER - /* Allows dsp load to have sole access to the playback stream. */ - struct mutex dsp_mutex; -#endif };
+#define azx_stream(dev) (&(dev)->core) +#define stream_to_azx_dev(s) container_of(s, struct azx_dev, core) + /* CORB/RIRB */ struct azx_rb { u32 *buf; /* CORB/RIRB buffer @@ -181,9 +147,6 @@ struct azx { spinlock_t reg_lock; struct mutex open_mutex; /* Prevents concurrent open/close operations */
- /* streams (x num_streams) */ - struct azx_dev *azx_dev; - /* PCM */ struct list_head pcm_list; /* azx_pcm list */
@@ -253,17 +216,17 @@ struct azx { ((chip)->io_ops->reg_readb((chip)->remap_addr + AZX_REG_##reg))
#define azx_sd_writel(chip, dev, reg, value) \ - ((chip)->io_ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg)) + snd_hdac_stream_writel(&(dev)->core, reg, value) #define azx_sd_readl(chip, dev, reg) \ - ((chip)->io_ops->reg_readl((dev)->sd_addr + AZX_REG_##reg)) + snd_hdac_stream_readl(&(dev)->core, reg) #define azx_sd_writew(chip, dev, reg, value) \ - ((chip)->io_ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg)) + snd_hdac_stream_writew(&(dev)->core, reg, value) #define azx_sd_readw(chip, dev, reg) \ - ((chip)->io_ops->reg_readw((dev)->sd_addr + AZX_REG_##reg)) + snd_hdac_stream_readw(&(dev)->core, reg) #define azx_sd_writeb(chip, dev, reg, value) \ - ((chip)->io_ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg)) + snd_hdac_stream_writeb(&(dev)->core, reg, value) #define azx_sd_readb(chip, dev, reg) \ - ((chip)->io_ops->reg_readb((dev)->sd_addr + AZX_REG_##reg)) + snd_hdac_stream_readb(&(dev)->core, reg)
#define azx_has_pm_runtime(chip) \ (!AZX_DCAPS_PM_RUNTIME || ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)) @@ -278,7 +241,7 @@ unsigned int azx_get_pos_lpib(struct azx *chip, struct azx_dev *azx_dev); unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev);
/* Stream control. */ -void azx_stream_stop(struct azx *chip, struct azx_dev *azx_dev); +void azx_stop_all_streams(struct azx *chip);
/* Allocation functions. */ int azx_alloc_stream_pages(struct azx *chip); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 7492d11fd8ff..c440ac1e34c8 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -492,7 +492,7 @@ static void azx_init_pci(struct azx *chip) static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev, unsigned int pos) { - struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_substream *substream = azx_dev->core.substream; int stream = substream->stream; unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev); int delay; @@ -502,16 +502,16 @@ static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev, else delay = lpib_pos - pos; if (delay < 0) { - if (delay >= azx_dev->delay_negative_threshold) + if (delay >= azx_dev->core.delay_negative_threshold) delay = 0; else - delay += azx_dev->bufsize; + delay += azx_dev->core.bufsize; }
- if (delay >= azx_dev->period_bytes) { + if (delay >= azx_dev->core.period_bytes) { dev_info(chip->card->dev, "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n", - delay, azx_dev->period_bytes); + delay, azx_dev->core.period_bytes); delay = 0; chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY; chip->get_delay[stream] = NULL; @@ -551,13 +551,13 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) */ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) { - struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_substream *substream = azx_dev->core.substream; int stream = substream->stream; u32 wallclk; unsigned int pos;
- wallclk = azx_readl(chip, WALLCLK) - azx_dev->start_wallclk; - if (wallclk < (azx_dev->period_wallclk * 2) / 3) + wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk; + if (wallclk < (azx_dev->core.period_wallclk * 2) / 3) return -1; /* bogus (too early) interrupt */
if (chip->get_position[stream]) @@ -577,17 +577,17 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) } }
- if (pos >= azx_dev->bufsize) + if (pos >= azx_dev->core.bufsize) pos = 0;
- if (WARN_ONCE(!azx_dev->period_bytes, + if (WARN_ONCE(!azx_dev->core.period_bytes, "hda-intel: zero azx_dev->period_bytes")) return -1; /* this shouldn't happen! */ - if (wallclk < (azx_dev->period_wallclk * 5) / 4 && - pos % azx_dev->period_bytes > azx_dev->period_bytes / 2) + if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 && + pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2) /* NG - it's below the first next period boundary */ return chip->bdl_pos_adj[chip->dev_index] ? 0 : -1; - azx_dev->start_wallclk += wallclk; + azx_dev->core.start_wallclk += wallclk; return 1; /* OK, it's fine */ }
@@ -598,7 +598,9 @@ static void azx_irq_pending_work(struct work_struct *work) { struct hda_intel *hda = container_of(work, struct hda_intel, irq_pending_work); struct azx *chip = &hda->chip; - int i, pending, ok; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + int pending, ok;
if (!hda->irq_pending_warned) { dev_info(chip->card->dev, @@ -610,17 +612,17 @@ static void azx_irq_pending_work(struct work_struct *work) for (;;) { pending = 0; spin_lock_irq(&chip->reg_lock); - for (i = 0; i < chip->num_streams; i++) { - struct azx_dev *azx_dev = &chip->azx_dev[i]; + list_for_each_entry(s, &bus->stream_list, list) { + struct azx_dev *azx_dev = stream_to_azx_dev(s); if (!azx_dev->irq_pending || - !azx_dev->substream || - !azx_dev->running) + !s->substream || + !s->running) continue; ok = azx_position_ok(chip, azx_dev); if (ok > 0) { azx_dev->irq_pending = 0; spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(azx_dev->substream); + snd_pcm_period_elapsed(s->substream); spin_lock(&chip->reg_lock); } else if (ok < 0) { pending = 0; /* too early */ @@ -637,11 +639,14 @@ static void azx_irq_pending_work(struct work_struct *work) /* clear irq_pending flags and assure no on-going workq */ static void azx_clear_irq_pending(struct azx *chip) { - int i; + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s;
spin_lock_irq(&chip->reg_lock); - for (i = 0; i < chip->num_streams; i++) - chip->azx_dev[i].irq_pending = 0; + list_for_each_entry(s, &bus->stream_list, list) { + struct azx_dev *azx_dev = stream_to_azx_dev(s); + azx_dev->irq_pending = 0; + } spin_unlock_irq(&chip->reg_lock); }
@@ -671,7 +676,7 @@ static unsigned int azx_via_get_position(struct azx *chip, unsigned int fifo_size;
link_pos = azx_sd_readl(chip, azx_dev, SD_LPIB); - if (azx_dev->substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* Playback, no problem using link position */ return link_pos; } @@ -680,8 +685,8 @@ static unsigned int azx_via_get_position(struct azx *chip, /* For new chipset, * use mod to get the DMA position just like old chipset */ - mod_dma_pos = le32_to_cpu(*azx_dev->posbuf); - mod_dma_pos %= azx_dev->period_bytes; + mod_dma_pos = le32_to_cpu(*azx_dev->core.posbuf); + mod_dma_pos %= azx_dev->core.period_bytes;
/* azx_dev->fifo_size can't get FIFO size of in stream. * Get from base address + offset. @@ -697,20 +702,20 @@ static unsigned int azx_via_get_position(struct azx *chip, }
if (link_pos <= fifo_size) - mini_pos = azx_dev->bufsize + link_pos - fifo_size; + mini_pos = azx_dev->core.bufsize + link_pos - fifo_size; else mini_pos = link_pos - fifo_size;
/* Find nearest previous boudary */ - mod_mini_pos = mini_pos % azx_dev->period_bytes; - mod_link_pos = link_pos % azx_dev->period_bytes; + mod_mini_pos = mini_pos % azx_dev->core.period_bytes; + mod_link_pos = link_pos % azx_dev->core.period_bytes; if (mod_link_pos >= fifo_size) bound_pos = link_pos - mod_link_pos; else if (mod_dma_pos >= mod_mini_pos) bound_pos = mini_pos - mod_mini_pos; else { - bound_pos = mini_pos - mod_mini_pos + azx_dev->period_bytes; - if (bound_pos >= azx_dev->bufsize) + bound_pos = mini_pos - mod_mini_pos + azx_dev->core.period_bytes; + if (bound_pos >= azx_dev->core.bufsize) bound_pos = 0; }
@@ -1063,7 +1068,6 @@ static int azx_free(struct azx *chip) { struct pci_dev *pci = chip->pci; struct hda_intel *hda = container_of(chip, struct hda_intel, chip); - int i;
if (azx_has_pm_runtime(chip) && chip->running) pm_runtime_get_noresume(&pci->dev); @@ -1082,8 +1086,7 @@ static int azx_free(struct azx *chip)
if (chip->initialized) { azx_clear_irq_pending(chip); - for (i = 0; i < chip->num_streams; i++) - azx_stream_stop(chip, &chip->azx_dev[i]); + azx_stop_all_streams(chip); azx_stop_chip(chip); }
@@ -1097,7 +1100,6 @@ static int azx_free(struct azx *chip) if (chip->region_requested) pci_release_regions(chip->pci); pci_disable_device(chip->pci); - kfree(chip->azx_dev); #ifdef CONFIG_SND_HDA_PATCH_LOADER release_firmware(chip->fw); #endif @@ -1566,10 +1568,6 @@ static int azx_first_init(struct azx *chip) chip->capture_index_offset = 0; chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams; - chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), - GFP_KERNEL); - if (!chip->azx_dev) - return -ENOMEM;
err = azx_alloc_stream_pages(chip); if (err < 0) @@ -1717,9 +1715,9 @@ static int substream_alloc_pages(struct azx *chip, int ret;
mark_runtime_wc(chip, azx_dev, substream, false); - azx_dev->bufsize = 0; - azx_dev->period_bytes = 0; - azx_dev->format_val = 0; + azx_dev->core.bufsize = 0; + azx_dev->core.period_bytes = 0; + azx_dev->core.format_val = 0; ret = snd_pcm_lib_malloc_pages(substream, size); if (ret < 0) return ret; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index b150cb50961c..e25e0df7f067 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -104,9 +104,9 @@ static int substream_alloc_pages(struct azx *chip, { struct azx_dev *azx_dev = get_azx_dev(substream);
- azx_dev->bufsize = 0; - azx_dev->period_bytes = 0; - azx_dev->format_val = 0; + azx_dev->core.bufsize = 0; + azx_dev->core.period_bytes = 0; + azx_dev->core.format_val = 0; return snd_pcm_lib_malloc_pages(substream, size); }
@@ -290,12 +290,10 @@ static const struct dev_pm_ops hda_tegra_pm = { */ static int hda_tegra_dev_free(struct snd_device *device) { - int i; struct azx *chip = device->device_data;
if (chip->initialized) { - for (i = 0; i < chip->num_streams; i++) - azx_stream_stop(chip, &chip->azx_dev[i]); + azx_stop_all_streams(chip); azx_stop_chip(chip); }
@@ -377,10 +375,6 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) chip->capture_index_offset = 0; chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams; - chip->azx_dev = devm_kcalloc(card->dev, chip->num_streams, - sizeof(*chip->azx_dev), GFP_KERNEL); - if (!chip->azx_dev) - return -ENOMEM;
err = azx_alloc_stream_pages(chip); if (err < 0)
... including dsp loader helpers. Lots of codes removed.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_controller.c | 428 +++++------------------------------------ sound/pci/hda/hda_intel.c | 4 + sound/pci/hda/hda_tegra.c | 1 + 3 files changed, 54 insertions(+), 379 deletions(-)
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 0b85c88c75ac..32f98f71c1e6 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -36,58 +36,6 @@ #define dsp_unlock(dev) snd_hdac_dsp_unlock(azx_stream(dev)) #define dsp_is_locked(dev) snd_hdac_stream_is_locked(azx_stream(dev))
-/* - * AZX stream operations. - */ - -/* - * set up the SD for streaming - */ -static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev) -{ - unsigned int val; - /* make sure the run bit is zero for SD */ - snd_hdac_stream_clear(azx_stream(azx_dev)); - /* program the stream_tag */ - val = azx_sd_readl(chip, azx_dev, SD_CTL); - val = (val & ~SD_CTL_STREAM_TAG_MASK) | - (azx_dev->core.stream_tag << SD_CTL_STREAM_TAG_SHIFT); - if (!azx_snoop(chip)) - val |= SD_CTL_TRAFFIC_PRIO; - 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->core.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->core.format_val); - - /* program the stream LVI (last valid index) of the BDL */ - azx_sd_writew(chip, azx_dev, SD_LVI, azx_dev->core.frags - 1); - - /* program the BDL address */ - /* lower BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPL, (u32)azx_dev->core.bdl.addr); - /* upper BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPU, - upper_32_bits(azx_dev->core.bdl.addr)); - - /* enable the position buffer */ - if (chip->get_position[0] != azx_get_pos_lpib || - chip->get_position[1] != azx_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; -} - /* assign a stream for the PCM */ static inline struct azx_dev * azx_assign_device(struct azx *chip, struct snd_pcm_substream *substream) @@ -106,50 +54,6 @@ static inline void azx_release_device(struct azx_dev *azx_dev) snd_hdac_stream_release(azx_stream(azx_dev)); }
-static cycle_t azx_cc_read(const struct cyclecounter *cc) -{ - struct azx_dev *azx_dev = container_of(cc, struct azx_dev, core.cc); - struct snd_pcm_substream *substream = azx_dev->core.substream; - struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct azx *chip = apcm->chip; - - return azx_readl(chip, WALLCLK); -} - -static void azx_timecounter_init(struct snd_pcm_substream *substream, - bool force, cycle_t last) -{ - struct azx_dev *azx_dev = get_azx_dev(substream); - struct timecounter *tc = &azx_dev->core.tc; - struct cyclecounter *cc = &azx_dev->core.cc; - u64 nsec; - - cc->read = azx_cc_read; - 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; -} - static inline struct hda_pcm_stream * to_hda_pcm_stream(struct snd_pcm_substream *substream) { @@ -178,119 +82,6 @@ static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream, }
/* - * set up a BDL entry - */ -static int setup_bdle(struct azx *chip, - struct snd_dma_buffer *dmab, - struct azx_dev *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->core.frags >= AZX_MAX_BDL_ENTRIES) - return -EINVAL; - - addr = snd_sgbuf_get_addr(dmab, ofs); - /* 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->core.frags++; - ofs += chunk; - } - *bdlp = bdl; - return ofs; -} - -/* - * set up BDL entries - */ -static int azx_setup_periods(struct azx *chip, - struct snd_pcm_substream *substream, - struct azx_dev *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->core.period_bytes; - periods = azx_dev->core.bufsize / period_bytes; - - /* program the initial BDL entries */ - bdl = (u32 *)azx_dev->core.bdl.area; - ofs = 0; - azx_dev->core.frags = 0; - - if (chip->bdl_pos_adj) - pos_adj = chip->bdl_pos_adj[chip->dev_index]; - if (!azx_dev->core.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->card->dev,"Too big adjustment %d\n", - pos_adj); - pos_adj = 0; - } else { - ofs = 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 = setup_bdle(chip, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes - pos_adj, 0); - else - ofs = setup_bdle(chip, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes, - !azx_dev->core.no_period_wakeup); - if (ofs < 0) - goto error; - } - return 0; - - error: - dev_err(chip->card->dev, "Too many BDL entries: buffer=%d, period=%d\n", - azx_dev->core.bufsize, period_bytes); - return -EINVAL; -} - -/* * PCM ops */
@@ -300,13 +91,8 @@ static int azx_pcm_close(struct snd_pcm_substream *substream) struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); - unsigned long flags;
mutex_lock(&chip->open_mutex); - spin_lock_irqsave(&chip->reg_lock, flags); - azx_dev->core.substream = NULL; - azx_dev->core.running = 0; - spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); if (hinfo->ops.close) hinfo->ops.close(hinfo, apcm->codec, substream); @@ -346,14 +132,8 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream)
/* reset BDL address */ dsp_lock(azx_dev); - if (!dsp_is_locked(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->core.bufsize = 0; - azx_dev->core.period_bytes = 0; - azx_dev->core.format_val = 0; - } + if (!dsp_is_locked(azx_dev)) + snd_hdac_stream_cleanup(azx_stream(azx_dev));
snd_hda_codec_cleanup(apcm->codec, hinfo, substream);
@@ -411,29 +191,12 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) azx_dev->core.period_bytes = period_bytes; azx_dev->core.format_val = format_val; azx_dev->core.no_period_wakeup = runtime->no_period_wakeup; - err = azx_setup_periods(chip, substream, azx_dev); + err = snd_hdac_stream_setup_periods(azx_stream(azx_dev)); if (err < 0) goto unlock; }
- /* 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->core.delay_negative_threshold = -frames_to_bytes(runtime, 64); - else - azx_dev->core.delay_negative_threshold = 0; - - /* wallclk has 24Mhz clock source */ - azx_dev->core.period_wallclk = (((runtime->period_size * 24000) / - runtime->rate) * 1000); - azx_setup_controller(chip, azx_dev); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - azx_dev->core.fifo_size = - azx_sd_readw(chip, azx_dev, SD_FIFOSIZE) + 1; - else - azx_dev->core.fifo_size = 0; + snd_hdac_stream_setup(azx_stream(azx_dev));
stream_tag = azx_dev->core.stream_tag; /* CA-IBG chips need the playback stream starting from 1 */ @@ -456,25 +219,31 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct azx *chip = apcm->chip; struct azx_dev *azx_dev; struct snd_pcm_substream *s; - int rstart = 0, start, nsync = 0, sbits = 0; - int nwait, timeout; + struct hdac_stream *hstr; + bool start; + int sbits = 0; + int sync_reg;
azx_dev = get_azx_dev(substream); + hstr = azx_stream(azx_dev); + if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) + sync_reg = AZX_REG_OLD_SSYNC; + else + sync_reg = AZX_REG_SSYNC;
if (dsp_is_locked(azx_dev) || !azx_dev->prepared) return -EPIPE;
switch (cmd) { case SNDRV_PCM_TRIGGER_START: - rstart = 1; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: - start = 1; + start = true; break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_STOP: - start = 0; + start = false; break; default: return -EINVAL; @@ -485,18 +254,13 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) continue; azx_dev = get_azx_dev(s); sbits |= 1 << azx_dev->core.index; - nsync++; snd_pcm_trigger_done(s, substream); }
spin_lock(&chip->reg_lock);
/* first, set SYNC bits of corresponding streams */ - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - azx_writel(chip, OLD_SSYNC, - azx_readl(chip, OLD_SSYNC) | sbits); - else - azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) | sbits); + snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg);
snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) @@ -510,65 +274,14 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } } spin_unlock(&chip->reg_lock); - if (start) { - /* wait until all FIFOs get ready */ - for (timeout = 5000; timeout; timeout--) { - nwait = 0; - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - if (!(azx_sd_readb(chip, azx_dev, SD_STS) & - SD_STS_FIFO_READY)) - nwait++; - } - if (!nwait) - break; - cpu_relax(); - } - } else { - /* wait until all RUN bits are cleared */ - for (timeout = 5000; timeout; timeout--) { - nwait = 0; - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_dev = get_azx_dev(s); - if (azx_sd_readb(chip, azx_dev, SD_CTL) & - SD_CTL_DMA_START) - nwait++; - } - if (!nwait) - break; - cpu_relax(); - } - } + + snd_hdac_stream_sync(hstr, start, sbits); + spin_lock(&chip->reg_lock); /* reset SYNC bits */ - if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) - azx_writel(chip, OLD_SSYNC, - azx_readl(chip, OLD_SSYNC) & ~sbits); - else - azx_writel(chip, SSYNC, azx_readl(chip, SSYNC) & ~sbits); - if (start) { - azx_timecounter_init(substream, 0, 0); - snd_pcm_gettime(substream->runtime, &substream->runtime->trigger_tstamp); - substream->runtime->trigger_tstamp_latched = true; - - if (nsync > 1) { - cycle_t cycle_last; - - /* same start cycle for master and group */ - azx_dev = get_azx_dev(substream); - cycle_last = azx_dev->core.tc.cycle_last; - - snd_pcm_group_for_each_entry(s, substream) { - if (s->pcm->card != substream->pcm->card) - continue; - azx_timecounter_init(s, 1, cycle_last); - } - } - } + snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg); + if (start) + snd_hdac_stream_timecounter_init(hstr, sbits); spin_unlock(&chip->reg_lock); return 0; } @@ -689,7 +402,6 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) struct azx *chip = apcm->chip; struct azx_dev *azx_dev; struct snd_pcm_runtime *runtime = substream->runtime; - unsigned long flags; int err; int buff_step;
@@ -700,6 +412,7 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) err = -EBUSY; goto unlock; } + runtime->private_data = azx_dev; runtime->hw = azx_pcm_hw; runtime->hw.channels_min = hinfo->channels_min; runtime->hw.channels_max = hinfo->channels_max; @@ -761,12 +474,6 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; }
- spin_lock_irqsave(&chip->reg_lock, flags); - azx_dev->core.substream = substream; - azx_dev->core.running = 0; - spin_unlock_irqrestore(&chip->reg_lock, flags); - - runtime->private_data = azx_dev; snd_pcm_set_sync(substream); mutex_unlock(&chip->open_mutex); return 0; @@ -1271,60 +978,31 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, unsigned int byte_size, struct snd_dma_buffer *bufp) { - u32 *bdl; struct azx *chip = bus->private_data; struct azx_dev *azx_dev; + struct hdac_stream *hstr; + bool saved = false; int err;
azx_dev = azx_get_dsp_loader_dev(chip); - - dsp_lock(azx_dev); + hstr = azx_stream(azx_dev); spin_lock_irq(&chip->reg_lock); - if (azx_dev->core.running || azx_dev->core.locked) { - spin_unlock_irq(&chip->reg_lock); - err = -EBUSY; - goto unlock; + if (hstr->opened) { + chip->saved_azx_dev = *azx_dev; + saved = true; } - azx_dev->prepared = 0; - chip->saved_azx_dev = *azx_dev; - azx_dev->core.locked = 1; spin_unlock_irq(&chip->reg_lock);
- err = chip->io_ops->dma_alloc_pages(&bus->core, SNDRV_DMA_TYPE_DEV_SG, - byte_size, bufp); - if (err < 0) - goto err_alloc; - - azx_dev->core.bufsize = byte_size; - azx_dev->core.period_bytes = byte_size; - azx_dev->core.format_val = format; - - snd_hdac_stream_reset(azx_stream(azx_dev)); - - /* reset BDL address */ - azx_sd_writel(chip, azx_dev, SD_BDLPL, 0); - azx_sd_writel(chip, azx_dev, SD_BDLPU, 0); - - azx_dev->core.frags = 0; - bdl = (u32 *)azx_dev->core.bdl.area; - err = setup_bdle(chip, bufp, azx_dev, &bdl, 0, byte_size, 0); - if (err < 0) - goto error; - - azx_setup_controller(chip, azx_dev); - dsp_unlock(azx_dev); - return azx_dev->core.stream_tag; + err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp); + if (err < 0) { + spin_lock_irq(&chip->reg_lock); + if (saved) + *azx_dev = chip->saved_azx_dev; + spin_unlock_irq(&chip->reg_lock); + return err; + }
- error: - chip->io_ops->dma_free_pages(&bus->core, bufp); - err_alloc: - spin_lock_irq(&chip->reg_lock); - if (azx_dev->core.opened) - *azx_dev = chip->saved_azx_dev; - azx_dev->core.locked = 0; - spin_unlock_irq(&chip->reg_lock); - unlock: - dsp_unlock(azx_dev); + azx_dev->prepared = 0; return err; }
@@ -1333,10 +1011,7 @@ static void azx_load_dsp_trigger(struct hda_bus *bus, bool start) struct azx *chip = bus->private_data; struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
- if (start) - snd_hdac_stream_start(azx_stream(azx_dev), false); - else - snd_hdac_stream_stop(azx_stream(azx_dev)); + snd_hdac_dsp_trigger(azx_stream(azx_dev), start); }
static void azx_load_dsp_cleanup(struct hda_bus *bus, @@ -1344,28 +1019,17 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, { struct azx *chip = bus->private_data; struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); + struct hdac_stream *hstr = azx_stream(azx_dev);
if (!dmab->area || !azx_dev->core.locked) return;
- dsp_lock(azx_dev); - /* reset BDL address */ - 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->core.bufsize = 0; - azx_dev->core.period_bytes = 0; - azx_dev->core.format_val = 0; - - chip->io_ops->dma_free_pages(&bus->core, dmab); - dmab->area = NULL; - + snd_hdac_dsp_cleanup(hstr, dmab); spin_lock_irq(&chip->reg_lock); - if (azx_dev->core.opened) + if (hstr->opened) *azx_dev = chip->saved_azx_dev; - azx_dev->core.locked = 0; + hstr->locked = false; spin_unlock_irq(&chip->reg_lock); - dsp_unlock(azx_dev); } #endif /* CONFIG_SND_HDA_DSP_LOADER */
@@ -1742,6 +1406,12 @@ int azx_bus_create(struct azx *chip, const char *model) bus->pci = chip->pci; bus->modelname = model; bus->ops = bus_ops; + bus->core.snoop = azx_snoop(chip); + if (chip->get_position[0] != azx_get_pos_lpib || + chip->get_position[1] != azx_get_pos_lpib) + bus->core.use_posbuf = true; + if (chip->bdl_pos_adj) + bus->core.bdl_pos_adj = chip->bdl_pos_adj[chip->dev_index];
if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) { dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n"); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index c440ac1e34c8..a55d8504fe00 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -568,6 +568,9 @@ static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev) dev_info(chip->card->dev, "Invalid position buffer, using LPIB read method instead.\n"); chip->get_position[stream] = azx_get_pos_lpib; + if (chip->get_position[0] == azx_get_pos_lpib && + chip->get_position[1] == azx_get_pos_lpib) + azx_bus(chip)->use_posbuf = false; pos = azx_get_pos_lpib(chip, azx_dev); chip->get_delay[stream] = NULL; } else { @@ -1477,6 +1480,7 @@ static int azx_first_init(struct azx *chip) dev_err(card->dev, "ioremap error\n"); return -ENXIO; } + azx_bus(chip)->remap_addr = chip->remap_addr; /* FIXME */
if (chip->msi) { if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index e25e0df7f067..c6fc96afbdc1 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -325,6 +325,7 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) return PTR_ERR(hda->regs);
chip->remap_addr = hda->regs + HDA_BAR0; + azx_bus(chip)->remap_addr = chip->remap_addr; /* FIXME */ chip->addr = res->start + HDA_BAR0;
err = hda_tegra_enable_clocks(hda);
... and replace with the existing hda-core helper codes. This reduces lots of lines, finally.
Since struct hda_bus is now embedded into struct azx, snd_hda_bus_new() is moved and expanded from hda_codec.c to hda_controller.c, accordingly. Also private_free bus ops and private_data field are removed because we no longer need to point azx object from bus (we can use container_of())
The spin locks are consolidated into the single one, bus->reg_lock.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 72 ------ sound/pci/hda/hda_codec.h | 3 - sound/pci/hda/hda_controller.c | 561 ++++++++--------------------------------- sound/pci/hda/hda_controller.h | 65 ++--- sound/pci/hda/hda_intel.c | 133 ++++++---- sound/pci/hda/hda_intel.h | 1 + sound/pci/hda/hda_tegra.c | 43 ++-- 7 files changed, 232 insertions(+), 646 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index ddebe7541390..2abf9f95dcbb 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -482,78 +482,6 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, }
/* - * destructor - */ -static void snd_hda_bus_free(struct hda_bus *bus) -{ - if (!bus) - return; - if (bus->ops.private_free) - bus->ops.private_free(bus); - snd_hdac_bus_exit(&bus->core); - kfree(bus); -} - -static int snd_hda_bus_dev_free(struct snd_device *device) -{ - snd_hda_bus_free(device->device_data); - return 0; -} - -static int snd_hda_bus_dev_disconnect(struct snd_device *device) -{ - struct hda_bus *bus = device->device_data; - bus->shutdown = 1; - return 0; -} - -/** - * snd_hda_bus_new - create a HDA bus - * @card: the card entry - * @busp: the pointer to store the created bus instance - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_bus_new(struct snd_card *card, - const struct hdac_bus_ops *ops, - const struct hdac_io_ops *io_ops, - struct hda_bus **busp) -{ - struct hda_bus *bus; - int err; - static struct snd_device_ops dev_ops = { - .dev_disconnect = snd_hda_bus_dev_disconnect, - .dev_free = snd_hda_bus_dev_free, - }; - - if (busp) - *busp = NULL; - - bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) - return -ENOMEM; - - err = snd_hdac_bus_init(&bus->core, card->dev, ops, io_ops); - if (err < 0) { - kfree(bus); - return err; - } - - bus->card = card; - mutex_init(&bus->prepare_mutex); - - err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); - if (err < 0) { - snd_hda_bus_free(bus); - return err; - } - if (busp) - *busp = bus; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_bus_new); - -/* * read widget caps for each widget and store in cache */ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index c8031360de90..57b9aa0f36c1 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -42,8 +42,6 @@ struct hda_pcm_stream;
/* bus operators */ struct hda_bus_ops { - /* free the private data */ - void (*private_free)(struct hda_bus *); /* attach a PCM stream */ int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, struct hda_pcm *pcm); @@ -73,7 +71,6 @@ struct hda_bus {
struct snd_card *card;
- void *private_data; struct pci_dev *pci; const char *modelname; struct hda_bus_ops ops; diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 32f98f71c1e6..21058b41b2c6 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -217,6 +217,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; + struct hdac_bus *bus = azx_bus(chip); struct azx_dev *azx_dev; struct snd_pcm_substream *s; struct hdac_stream *hstr; @@ -257,7 +258,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) snd_pcm_trigger_done(s, substream); }
- spin_lock(&chip->reg_lock); + spin_lock(&bus->reg_lock);
/* first, set SYNC bits of corresponding streams */ snd_hdac_stream_sync_trigger(hstr, true, sbits, sync_reg); @@ -273,16 +274,16 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) snd_hdac_stream_stop(azx_stream(azx_dev)); } } - spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock);
snd_hdac_stream_sync(hstr, start, sbits);
- spin_lock(&chip->reg_lock); + spin_lock(&bus->reg_lock); /* reset SYNC bits */ snd_hdac_stream_sync_trigger(hstr, false, sbits, sync_reg); if (start) snd_hdac_stream_timecounter_init(hstr, sbits); - spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); return 0; }
@@ -522,10 +523,11 @@ static void azx_pcm_free(struct snd_pcm *pcm)
#define MAX_PREALLOC_SIZE (32 * 1024 * 1024)
-static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, +static int azx_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec, struct hda_pcm *cpcm) { - struct azx *chip = bus->private_data; + struct hdac_bus *bus = &_bus->core; + struct azx *chip = bus_to_azx(bus); struct snd_pcm *pcm; struct azx_pcm *apcm; int pcm_dev = cpcm->device; @@ -573,89 +575,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, return 0; }
-/* - * CORB / RIRB interface - */ -static int azx_alloc_cmd_io(struct azx *chip) -{ - /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - return chip->io_ops->dma_alloc_pages(azx_bus(chip), SNDRV_DMA_TYPE_DEV, - PAGE_SIZE, &chip->rb); -} - -static void azx_init_cmd_io(struct azx *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->card->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->card->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); -} - -static void azx_free_cmd_io(struct azx *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); -} - static unsigned int azx_command_addr(u32 cmd) { unsigned int addr = cmd >> 28; @@ -668,92 +587,12 @@ static unsigned int azx_command_addr(u32 cmd) return addr; }
-/* send a command */ -static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) -{ - struct azx *chip = bus->private_data; - unsigned int addr = azx_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) - -/* retrieve RIRB entry - called from interrupt handler */ -static void azx_update_rirb(struct azx *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 >= AZX_MAX_CODECS) || !(chip->codec_mask & (1 << addr))) { - dev_err(chip->card->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) - snd_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->card->dev, "spurious response %#x:%#x, last cmd=%#08x\n", - res, res_ex, - chip->last_cmd[addr]); - } - } -} - /* receive a response */ -static int azx_rirb_get_response(struct hda_bus *bus, unsigned int addr, +static int azx_rirb_get_response(struct hdac_bus *bus, unsigned int addr, unsigned int *res) { - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(bus); + struct hda_bus *hbus = &chip->bus; unsigned long timeout; unsigned long loopcounter; int do_poll = 0; @@ -762,23 +601,21 @@ static int azx_rirb_get_response(struct hda_bus *bus, unsigned int addr, timeout = jiffies + msecs_to_jiffies(1000);
for (loopcounter = 0;; loopcounter++) { - if (chip->polling_mode || do_poll) { - spin_lock_irq(&chip->reg_lock); - azx_update_rirb(chip); - spin_unlock_irq(&chip->reg_lock); - } - if (!chip->rirb.cmds[addr]) { - smp_rmb(); - + spin_lock_irq(&bus->reg_lock); + if (chip->polling_mode || do_poll) + snd_hdac_bus_update_rirb(bus); + if (!bus->rirb.cmds[addr]) { if (!do_poll) chip->poll_count = 0; if (res) - *res = chip->rirb.res[addr]; /* the last value */ + *res = bus->rirb.res[addr]; /* the last value */ + spin_unlock_irq(&bus->reg_lock); return 0; } + spin_unlock_irq(&bus->reg_lock); if (time_after(jiffies, timeout)) break; - if (bus->needs_damn_long_delay || loopcounter > 3000) + if (hbus->needs_damn_long_delay || loopcounter > 3000) msleep(2); /* temporary workaround */ else { udelay(10); @@ -786,13 +623,13 @@ static int azx_rirb_get_response(struct hda_bus *bus, unsigned int addr, } }
- if (bus->no_response_fallback) + if (hbus->no_response_fallback) return -EIO;
if (!chip->polling_mode && chip->poll_count < 2) { dev_dbg(chip->card->dev, "azx_get_response timeout, polling the codec once: last cmd=0x%08x\n", - chip->last_cmd[addr]); + bus->last_cmd[addr]); do_poll = 1; chip->poll_count++; goto again; @@ -802,7 +639,7 @@ static int azx_rirb_get_response(struct hda_bus *bus, unsigned int addr, if (!chip->polling_mode) { dev_warn(chip->card->dev, "azx_get_response timeout, switching to polling mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); + bus->last_cmd[addr]); chip->polling_mode = 1; goto again; } @@ -810,8 +647,8 @@ static int azx_rirb_get_response(struct hda_bus *bus, unsigned int addr, if (chip->msi) { dev_warn(chip->card->dev, "No response from codec, disabling MSI: last cmd=0x%08x\n", - chip->last_cmd[addr]); - if (chip->ops->disable_msi_reset_irq(chip) && + bus->last_cmd[addr]); + if (chip->ops->disable_msi_reset_irq && chip->ops->disable_msi_reset_irq(chip) < 0) return -EIO; goto again; @@ -828,20 +665,17 @@ static int azx_rirb_get_response(struct hda_bus *bus, unsigned int addr, /* a fatal communication error; need either to reset or to fallback * to the single_cmd mode */ - if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) { - bus->response_reset = 1; + if (hbus->allow_bus_reset && !hbus->response_reset && !hbus->in_reset) { + hbus->response_reset = 1; return -EAGAIN; /* give a chance to retry */ }
dev_err(chip->card->dev, "azx_get_response timeout, switching to single_cmd mode: last cmd=0x%08x\n", - chip->last_cmd[addr]); + bus->last_cmd[addr]); chip->single_cmd = 1; - bus->response_reset = 0; - /* release CORB/RIRB */ - azx_free_cmd_io(chip); - /* disable unsolicited responses */ - azx_writel(chip, GCTL, azx_readl(chip, GCTL) & ~AZX_GCTL_UNSOL); + hbus->response_reset = 0; + snd_hdac_bus_stop_cmd_io(bus); return -EIO; }
@@ -864,7 +698,7 @@ static int azx_single_wait_for_response(struct azx *chip, unsigned int addr) /* 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); + azx_bus(chip)->rirb.res[addr] = azx_readl(chip, IR); return 0; } udelay(1); @@ -872,17 +706,18 @@ static int azx_single_wait_for_response(struct azx *chip, unsigned int addr) if (printk_ratelimit()) dev_dbg(chip->card->dev, "get_response timeout: IRS=0x%x\n", azx_readw(chip, IRS)); - chip->rirb.res[addr] = -1; + azx_bus(chip)->rirb.res[addr] = -1; return -EIO; }
/* send a command */ -static int azx_single_send_cmd(struct hda_bus *bus, u32 val) +static int azx_single_send_cmd(struct hdac_bus *bus, u32 val) { - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(bus); unsigned int addr = azx_command_addr(val); int timeout = 50;
+ bus->last_cmd[azx_command_addr(val)] = val; while (timeout--) { /* check ICB busy bit */ if (!((azx_readw(chip, IRS) & AZX_IRS_BUSY))) { @@ -904,13 +739,11 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val) }
/* receive a response */ -static int azx_single_get_response(struct hda_bus *bus, unsigned int addr, +static int azx_single_get_response(struct hdac_bus *bus, unsigned int addr, unsigned int *res) { - struct azx *chip = bus->private_data; - if (res) - *res = chip->rirb.res[addr]; + *res = bus->rirb.res[addr]; return 0; }
@@ -922,26 +755,24 @@ static int azx_single_get_response(struct hda_bus *bus, unsigned int addr, */
/* send a command */ -static int azx_send_cmd(struct hdac_bus *_bus, unsigned int val) +static int azx_send_cmd(struct hdac_bus *bus, unsigned int val) { - struct hda_bus *bus = to_hda_bus(_bus); - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(bus);
if (chip->disabled) return 0; - chip->last_cmd[azx_command_addr(val)] = val; if (chip->single_cmd) return azx_single_send_cmd(bus, val); else - return azx_corb_send_cmd(bus, val); + return snd_hdac_bus_send_cmd(bus, val); }
/* get a response */ -static int azx_get_response(struct hdac_bus *_bus, unsigned int addr, +static int azx_get_response(struct hdac_bus *bus, unsigned int addr, unsigned int *res) { - struct hda_bus *bus = to_hda_bus(_bus); - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(bus); + if (chip->disabled) return 0; if (chip->single_cmd) @@ -974,11 +805,12 @@ azx_get_dsp_loader_dev(struct azx *chip) return NULL; }
-static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, +static int azx_load_dsp_prepare(struct hda_bus *_bus, unsigned int format, unsigned int byte_size, struct snd_dma_buffer *bufp) { - struct azx *chip = bus->private_data; + struct hdac_bus *bus = &_bus->core; + struct azx *chip = bus_to_azx(bus); struct azx_dev *azx_dev; struct hdac_stream *hstr; bool saved = false; @@ -986,19 +818,19 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format,
azx_dev = azx_get_dsp_loader_dev(chip); hstr = azx_stream(azx_dev); - spin_lock_irq(&chip->reg_lock); + spin_lock_irq(&bus->reg_lock); if (hstr->opened) { chip->saved_azx_dev = *azx_dev; saved = true; } - spin_unlock_irq(&chip->reg_lock); + spin_unlock_irq(&bus->reg_lock);
err = snd_hdac_dsp_prepare(hstr, format, byte_size, bufp); if (err < 0) { - spin_lock_irq(&chip->reg_lock); + spin_lock_irq(&bus->reg_lock); if (saved) *azx_dev = chip->saved_azx_dev; - spin_unlock_irq(&chip->reg_lock); + spin_unlock_irq(&bus->reg_lock); return err; }
@@ -1006,18 +838,20 @@ static int azx_load_dsp_prepare(struct hda_bus *bus, unsigned int format, return err; }
-static void azx_load_dsp_trigger(struct hda_bus *bus, bool start) +static void azx_load_dsp_trigger(struct hda_bus *_bus, bool start) { - struct azx *chip = bus->private_data; + struct hdac_bus *bus = &_bus->core; + struct azx *chip = bus_to_azx(bus); struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip);
snd_hdac_dsp_trigger(azx_stream(azx_dev), start); }
-static void azx_load_dsp_cleanup(struct hda_bus *bus, +static void azx_load_dsp_cleanup(struct hda_bus *_bus, struct snd_dma_buffer *dmab) { - struct azx *chip = bus->private_data; + struct hdac_bus *bus = &_bus->core; + struct azx *chip = bus_to_azx(bus); struct azx_dev *azx_dev = azx_get_dsp_loader_dev(chip); struct hdac_stream *hstr = azx_stream(azx_dev);
@@ -1025,207 +859,24 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, return;
snd_hdac_dsp_cleanup(hstr, dmab); - spin_lock_irq(&chip->reg_lock); + spin_lock_irq(&bus->reg_lock); if (hstr->opened) *azx_dev = chip->saved_azx_dev; hstr->locked = false; - spin_unlock_irq(&chip->reg_lock); + spin_unlock_irq(&bus->reg_lock); } #endif /* CONFIG_SND_HDA_DSP_LOADER */
-int azx_alloc_stream_pages(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - struct hdac_stream *s; - int err; - - list_for_each_entry(s, &bus->stream_list, list) { - /* allocate memory for the BDL for each stream */ - err = chip->io_ops->dma_alloc_pages(azx_bus(chip), SNDRV_DMA_TYPE_DEV, - BDL_SIZE, &s->bdl); - if (err < 0) - return -ENOMEM; - } - - /* allocate memory for the position buffer */ - err = chip->io_ops->dma_alloc_pages(azx_bus(chip), SNDRV_DMA_TYPE_DEV, - chip->num_streams * 8, &chip->posbuf); - if (err < 0) - return -ENOMEM; - - /* allocate CORB/RIRB */ - err = azx_alloc_cmd_io(chip); - if (err < 0) - return err; - return 0; -} -EXPORT_SYMBOL_GPL(azx_alloc_stream_pages); - -void azx_free_stream_pages(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - struct hdac_stream *s, *next; - - list_for_each_entry_safe(s, next, &bus->stream_list, list) { - if (s->bdl.area) - chip->io_ops->dma_free_pages(azx_bus(chip), &s->bdl); - kfree(s); - } - - if (chip->rb.area) - chip->io_ops->dma_free_pages(azx_bus(chip), &chip->rb); - if (chip->posbuf.area) - chip->io_ops->dma_free_pages(azx_bus(chip), &chip->posbuf); -} -EXPORT_SYMBOL_GPL(azx_free_stream_pages); - -/* - * Lowlevel interface - */ - -/* enter link reset */ -void azx_enter_link_reset(struct azx *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(azx_enter_link_reset); - -/* exit link reset */ -static void azx_exit_link_reset(struct azx *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); -} - -/* reset codec link */ -static int azx_reset(struct azx *chip, bool full_reset) -{ - if (!full_reset) - goto __skip; - - /* clear STATESTS */ - azx_writew(chip, STATESTS, STATESTS_INT_MASK); - - /* reset controller */ - azx_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 */ - azx_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->card->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->card->dev, "codec_mask = 0x%x\n", - chip->codec_mask); - } - - return 0; -} - -/* enable interrupts */ -static void azx_int_enable(struct azx *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 azx_int_disable(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - struct hdac_stream *s; - - /* disable interrupts in stream descriptor */ - list_for_each_entry(s, &bus->stream_list, list) - snd_hdac_stream_updateb(s, SD_CTL, SD_INT_MASK, 0); - - /* 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 azx_int_clear(struct azx *chip) -{ - struct hdac_bus *bus = azx_bus(chip); - struct hdac_stream *s; - - /* clear stream status */ - list_for_each_entry(s, &bus->stream_list, list) - snd_hdac_stream_writeb(s, 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 azx_init_chip(struct azx *chip, bool full_reset) { - if (chip->initialized) - return; - - /* reset controller */ - azx_reset(chip, full_reset); - - /* initialize interrupts */ - azx_int_clear(chip); - azx_int_enable(chip); - - /* initialize the codec command I/O */ - if (!chip->single_cmd) - azx_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; + if (snd_hdac_bus_init_chip(azx_bus(chip), full_reset)) { + /* correct RINTCNT for CXT */ + if (chip->driver_caps & AZX_DCAPS_CTX_WORKAROUND) + azx_writew(chip, RINTCNT, 0xc0); + } } EXPORT_SYMBOL_GPL(azx_init_chip);
@@ -1241,21 +892,7 @@ EXPORT_SYMBOL_GPL(azx_stop_all_streams);
void azx_stop_chip(struct azx *chip) { - if (!chip->initialized) - return; - - /* disable interrupts */ - azx_int_disable(chip); - azx_int_clear(chip); - - /* disable CORB/RIRB */ - azx_free_cmd_io(chip); - - /* disable position buffer */ - azx_writel(chip, DPLBASE, 0); - azx_writel(chip, DPUBASE, 0); - - chip->initialized = 0; + snd_hdac_bus_stop_chip(azx_bus(chip)); } EXPORT_SYMBOL_GPL(azx_stop_chip);
@@ -1264,16 +901,15 @@ EXPORT_SYMBOL_GPL(azx_stop_chip); */ static void stream_update(struct hdac_bus *bus, struct hdac_stream *s) { - struct hda_bus *hbus = container_of(bus, struct hda_bus, core); - struct azx *chip = hbus->private_data; + struct azx *chip = bus_to_azx(bus); struct azx_dev *azx_dev = stream_to_azx_dev(s);
/* check whether this IRQ is really acceptable */ if (!chip->ops->position_check || chip->ops->position_check(chip, azx_dev)) { - spin_unlock(&chip->reg_lock); - snd_pcm_period_elapsed(azx_dev->core.substream); - spin_lock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); + snd_pcm_period_elapsed(azx_stream(azx_dev)->substream); + spin_lock(&bus->reg_lock); } }
@@ -1289,16 +925,16 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) return IRQ_NONE; #endif
- spin_lock(&chip->reg_lock); + spin_lock(&bus->reg_lock);
if (chip->disabled) { - spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); return IRQ_NONE; }
status = azx_readl(chip, INTSTS); if (status == 0 || status == 0xffffffff) { - spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); return IRQ_NONE; }
@@ -1310,12 +946,12 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) if (status & RIRB_INT_RESPONSE) { if (chip->driver_caps & AZX_DCAPS_RIRB_PRE_DELAY) udelay(80); - azx_update_rirb(chip); + snd_hdac_bus_update_rirb(bus); } azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); }
- spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock);
return IRQ_HANDLED; } @@ -1334,7 +970,7 @@ static int probe_codec(struct azx *chip, int addr) (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; struct hdac_bus *bus = azx_bus(chip); int err; - unsigned int res; + unsigned int res = -1;
mutex_lock(&bus->cmd_mutex); chip->probing = 1; @@ -1350,13 +986,13 @@ static int probe_codec(struct azx *chip, int addr)
static void azx_bus_reset(struct hda_bus *bus) { - struct azx *chip = bus->private_data; + struct azx *chip = bus_to_azx(&bus->core);
bus->in_reset = 1; azx_stop_chip(chip); azx_init_chip(chip, true); - if (chip->initialized) - snd_hda_bus_reset(chip->bus); + if (bus->core.chip_init) + snd_hda_bus_reset(bus); bus->in_reset = 0; }
@@ -1392,17 +1028,19 @@ static struct hda_bus_ops bus_ops = { };
/* HD-audio bus initialization */ -int azx_bus_create(struct azx *chip, const char *model) +int azx_bus_init(struct azx *chip, const char *model, + const struct hdac_io_ops *io_ops) { - struct hda_bus *bus; + struct hda_bus *bus = &chip->bus; int err;
- err = snd_hda_bus_new(chip->card, &bus_core_ops, chip->io_ops, &bus); + err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops, + io_ops); if (err < 0) return err;
- chip->bus = bus; - bus->private_data = chip; + bus->card = chip->card; + mutex_init(&bus->prepare_mutex); bus->pci = chip->pci; bus->modelname = model; bus->ops = bus_ops; @@ -1412,6 +1050,8 @@ int azx_bus_create(struct azx *chip, const char *model) bus->core.use_posbuf = true; if (chip->bdl_pos_adj) bus->core.bdl_pos_adj = chip->bdl_pos_adj[chip->dev_index]; + if (chip->driver_caps & AZX_DCAPS_CORBRP_SELF_CLEAR) + bus->core.corbrp_self_clear = true;
if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) { dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n"); @@ -1430,12 +1070,12 @@ int azx_bus_create(struct azx *chip, const char *model)
return 0; } -EXPORT_SYMBOL_GPL(azx_bus_create); +EXPORT_SYMBOL_GPL(azx_bus_init);
/* Probe codecs */ int azx_probe_codecs(struct azx *chip, unsigned int max_slots) { - struct hda_bus *bus = chip->bus; + struct hdac_bus *bus = azx_bus(chip); int c, codecs, err;
codecs = 0; @@ -1444,14 +1084,14 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
/* First try to probe all given codec slots */ for (c = 0; c < max_slots; c++) { - if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { + if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) { if (probe_codec(chip, c) < 0) { /* Some BIOSen give you wrong codec addresses * that don't exist */ dev_warn(chip->card->dev, "Codec #%d probe error; disabling it...\n", c); - chip->codec_mask &= ~(1 << c); + bus->codec_mask &= ~(1 << c); /* More badly, accessing to a non-existing * codec often screws up the controller chip, * and disturbs the further communications. @@ -1467,9 +1107,9 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
/* Then create codec instances */ for (c = 0; c < max_slots; c++) { - if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { + if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) { struct hda_codec *codec; - err = snd_hda_codec_new(bus, bus->card, c, &codec); + err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec); if (err < 0) continue; codec->jackpoll_interval = get_jackpoll_interval(chip); @@ -1489,7 +1129,7 @@ EXPORT_SYMBOL_GPL(azx_probe_codecs); int azx_codec_configure(struct azx *chip) { struct hda_codec *codec; - list_for_each_codec(codec, chip->bus) { + list_for_each_codec(codec, &chip->bus) { snd_hda_codec_configure(codec); } return 0; @@ -1505,7 +1145,7 @@ static int stream_direction(struct azx *chip, unsigned char index) }
/* initialize SD streams */ -int azx_init_stream(struct azx *chip) +int azx_init_streams(struct azx *chip) { int i; int stream_tags[2] = { 0, 0 }; @@ -1538,4 +1178,17 @@ int azx_init_stream(struct azx *chip)
return 0; } -EXPORT_SYMBOL_GPL(azx_init_stream); +EXPORT_SYMBOL_GPL(azx_init_streams); + +void azx_free_streams(struct azx *chip) +{ + struct hdac_bus *bus = azx_bus(chip); + struct hdac_stream *s; + + while (!list_empty(&bus->stream_list)) { + s = list_first_entry(&bus->stream_list, struct hdac_stream, list); + list_del(&s->list); + kfree(stream_to_azx_dev(s)); + } +} +EXPORT_SYMBOL_GPL(azx_free_streams); diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index b45568d83860..e8edb02c12d3 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -75,18 +75,6 @@ struct azx_dev { #define azx_stream(dev) (&(dev)->core) #define stream_to_azx_dev(s) container_of(s, struct azx_dev, core)
-/* CORB/RIRB */ -struct azx_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[AZX_MAX_CODECS]; /* number of pending requests */ - u32 res[AZX_MAX_CODECS]; /* last read value */ -}; - struct azx;
/* Functions to read/write to hda registers. */ @@ -116,6 +104,8 @@ typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *); typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos);
struct azx { + struct hda_bus bus; + struct snd_card *card; struct pci_dev *pci; int dev_index; @@ -132,38 +122,21 @@ struct azx {
/* Register interaction. */ const struct hda_controller_ops *ops; - const struct hdac_io_ops *io_ops;
/* position adjustment callbacks */ azx_get_pos_callback_t get_position[2]; azx_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 */
/* PCM */ struct list_head pcm_list; /* azx_pcm list */
/* HD codec */ - unsigned short codec_mask; int codec_probe_mask; /* copied from probe_mask option */ - struct hda_bus *bus; unsigned int beep_mode;
- /* CORB/RIRB */ - struct azx_rb corb; - struct azx_rb rirb; - - /* CORB/RIRB and position buffers */ - struct snd_dma_buffer rb; - struct snd_dma_buffer posbuf; - #ifdef CONFIG_SND_HDA_PATCH_LOADER const struct firmware *fw; #endif @@ -172,7 +145,6 @@ struct azx { 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; @@ -182,15 +154,13 @@ struct azx { unsigned int region_requested:1; unsigned int disabled:1; /* disabled by VGA-switcher */
- /* for debugging */ - unsigned int last_cmd[AZX_MAX_CODECS]; - #ifdef CONFIG_SND_HDA_DSP_LOADER struct azx_dev saved_azx_dev; #endif };
-#define azx_bus(chip) (&(chip)->bus->core) +#define azx_bus(chip) (&(chip)->bus.core) +#define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core)
#ifdef CONFIG_X86 #define azx_snoop(chip) ((chip)->snoop) @@ -203,17 +173,17 @@ struct azx { */
#define azx_writel(chip, reg, value) \ - ((chip)->io_ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_writel(azx_bus(chip), reg, value) #define azx_readl(chip, reg) \ - ((chip)->io_ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_readl(azx_bus(chip), reg) #define azx_writew(chip, reg, value) \ - ((chip)->io_ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_writew(azx_bus(chip), reg, value) #define azx_readw(chip, reg) \ - ((chip)->io_ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_readw(azx_bus(chip), reg) #define azx_writeb(chip, reg, value) \ - ((chip)->io_ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_writeb(azx_bus(chip), reg, value) #define azx_readb(chip, reg) \ - ((chip)->io_ops->reg_readb((chip)->remap_addr + AZX_REG_##reg)) + snd_hdac_chip_readb(azx_bus(chip), reg)
#define azx_sd_writel(chip, dev, reg, value) \ snd_hdac_stream_writel(&(dev)->core, reg, value) @@ -244,19 +214,24 @@ unsigned int azx_get_pos_posbuf(struct azx *chip, struct azx_dev *azx_dev); void azx_stop_all_streams(struct azx *chip);
/* Allocation functions. */ -int azx_alloc_stream_pages(struct azx *chip); -void azx_free_stream_pages(struct azx *chip); +#define azx_alloc_stream_pages(chip) \ + snd_hdac_bus_alloc_stream_pages(azx_bus(chip)) +#define azx_free_stream_pages(chip) \ + snd_hdac_bus_free_stream_pages(azx_bus(chip))
/* Low level azx interface */ void azx_init_chip(struct azx *chip, bool full_reset); void azx_stop_chip(struct azx *chip); -void azx_enter_link_reset(struct azx *chip); +#define azx_enter_link_reset(chip) \ + snd_hdac_bus_enter_link_reset(azx_bus(chip)) irqreturn_t azx_interrupt(int irq, void *dev_id);
/* Codec interface */ -int azx_bus_create(struct azx *chip, const char *model); +int azx_bus_init(struct azx *chip, const char *model, + const struct hdac_io_ops *io_ops); int azx_probe_codecs(struct azx *chip, unsigned int max_slots); int azx_codec_configure(struct azx *chip); -int azx_init_stream(struct azx *chip); +int azx_init_streams(struct azx *chip); +void azx_free_streams(struct azx *chip);
#endif /* __SOUND_HDA_CONTROLLER_H */ diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a55d8504fe00..9dff693005ea 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -614,7 +614,7 @@ static void azx_irq_pending_work(struct work_struct *work)
for (;;) { pending = 0; - spin_lock_irq(&chip->reg_lock); + spin_lock_irq(&bus->reg_lock); list_for_each_entry(s, &bus->stream_list, list) { struct azx_dev *azx_dev = stream_to_azx_dev(s); if (!azx_dev->irq_pending || @@ -624,15 +624,15 @@ static void azx_irq_pending_work(struct work_struct *work) ok = azx_position_ok(chip, azx_dev); if (ok > 0) { azx_dev->irq_pending = 0; - spin_unlock(&chip->reg_lock); + spin_unlock(&bus->reg_lock); snd_pcm_period_elapsed(s->substream); - spin_lock(&chip->reg_lock); + spin_lock(&bus->reg_lock); } else if (ok < 0) { pending = 0; /* too early */ } else pending++; } - spin_unlock_irq(&chip->reg_lock); + spin_unlock_irq(&bus->reg_lock); if (!pending) return; msleep(1); @@ -645,16 +645,18 @@ static void azx_clear_irq_pending(struct azx *chip) struct hdac_bus *bus = azx_bus(chip); struct hdac_stream *s;
- spin_lock_irq(&chip->reg_lock); + spin_lock_irq(&bus->reg_lock); list_for_each_entry(s, &bus->stream_list, list) { struct azx_dev *azx_dev = stream_to_azx_dev(s); azx_dev->irq_pending = 0; } - spin_unlock_irq(&chip->reg_lock); + spin_unlock_irq(&bus->reg_lock); }
static int azx_acquire_irq(struct azx *chip, int do_disconnect) { + struct hdac_bus *bus = azx_bus(chip); + if (request_irq(chip->pci->irq, azx_interrupt, chip->msi ? 0 : IRQF_SHARED, KBUILD_MODNAME, chip)) { @@ -665,7 +667,7 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) snd_card_disconnect(chip->card); return -1; } - chip->irq = chip->pci->irq; + bus->irq = chip->pci->irq; pci_intx(chip->pci, !chip->msi); return 0; } @@ -694,7 +696,8 @@ static unsigned int azx_via_get_position(struct azx *chip, /* azx_dev->fifo_size can't get FIFO size of in stream. * Get from base address + offset. */ - fifo_size = readw(chip->remap_addr + VIA_IN_STREAM0_FIFO_SIZE_OFFSET); + fifo_size = readw(azx_bus(chip)->remap_addr + + VIA_IN_STREAM0_FIFO_SIZE_OFFSET);
if (azx_dev->insufficient) { /* Link position never gather than FIFO size */ @@ -760,9 +763,9 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) mutex_lock(&card_list_lock); list_for_each_entry(hda, &card_list, list) { chip = &hda->chip; - if (!chip->bus || chip->disabled) + if (!hda->probe_continued || chip->disabled) continue; - snd_hda_set_power_save(chip->bus, power_save * 1000); + snd_hda_set_power_save(&chip->bus, power_save * 1000); } mutex_unlock(&card_list_lock); return 0; @@ -781,6 +784,7 @@ static int azx_suspend(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; + struct hdac_bus *bus;
if (!card) return 0; @@ -790,13 +794,14 @@ static int azx_suspend(struct device *dev) if (chip->disabled || hda->init_failed) return 0;
+ bus = azx_bus(chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); azx_clear_irq_pending(chip); azx_stop_chip(chip); azx_enter_link_reset(chip); - if (chip->irq >= 0) { - free_irq(chip->irq, chip); - chip->irq = -1; + if (bus->irq >= 0) { + free_irq(bus->irq, chip); + bus->irq = -1; }
if (chip->msi) @@ -875,7 +880,6 @@ static int azx_runtime_resume(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; - struct hda_bus *bus; struct hda_codec *codec; int status;
@@ -901,9 +905,8 @@ static int azx_runtime_resume(struct device *dev) azx_init_pci(chip); azx_init_chip(chip, true);
- bus = chip->bus; - if (status && bus) { - list_for_each_codec(codec, bus) + if (status) { + list_for_each_codec(codec, &chip->bus) if (status & (1 << codec->addr)) schedule_delayed_work(&codec->jackpoll_work, codec->jackpoll_interval); @@ -931,7 +934,7 @@ static int azx_runtime_idle(struct device *dev) return 0;
if (!power_save_controller || !azx_has_pm_runtime(chip) || - chip->bus->core.codec_powered) + azx_bus(chip)->codec_powered) return -EBUSY;
return 0; @@ -969,7 +972,7 @@ static void azx_vs_set_state(struct pci_dev *pci, if (chip->disabled == disabled) return;
- if (!chip->bus) { + if (!hda->probe_continued) { chip->disabled = disabled; if (!disabled) { dev_info(chip->card->dev, @@ -990,11 +993,11 @@ static void azx_vs_set_state(struct pci_dev *pci, * put ourselves there */ pci->current_state = PCI_D3cold; chip->disabled = true; - if (snd_hda_lock_devices(chip->bus)) + if (snd_hda_lock_devices(&chip->bus)) dev_warn(chip->card->dev, "Cannot lock devices!\n"); } else { - snd_hda_unlock_devices(chip->bus); + snd_hda_unlock_devices(&chip->bus); pm_runtime_get_noresume(card->dev); chip->disabled = false; azx_resume(card->dev); @@ -1011,11 +1014,11 @@ static bool azx_vs_can_switch(struct pci_dev *pci) wait_for_completion(&hda->probe_wait); if (hda->init_failed) return false; - if (chip->disabled || !chip->bus) + if (chip->disabled || !hda->probe_continued) return true; - if (snd_hda_lock_devices(chip->bus)) + if (snd_hda_lock_devices(&chip->bus)) return false; - snd_hda_unlock_devices(chip->bus); + snd_hda_unlock_devices(&chip->bus); return true; }
@@ -1048,7 +1051,7 @@ static int register_vga_switcheroo(struct azx *chip) */ err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, VGA_SWITCHEROO_DIS, - chip->bus != NULL); + hda->probe_continued); if (err < 0) return err; hda->vga_switcheroo_registered = 1; @@ -1071,6 +1074,7 @@ static int azx_free(struct azx *chip) { struct pci_dev *pci = chip->pci; struct hda_intel *hda = container_of(chip, struct hda_intel, chip); + struct hdac_bus *bus = azx_bus(chip);
if (azx_has_pm_runtime(chip) && chip->running) pm_runtime_get_noresume(&pci->dev); @@ -1081,27 +1085,31 @@ static int azx_free(struct azx *chip) complete_all(&hda->probe_wait);
if (use_vga_switcheroo(hda)) { - if (chip->disabled && chip->bus) - snd_hda_unlock_devices(chip->bus); + if (chip->disabled && hda->probe_continued) + snd_hda_unlock_devices(&chip->bus); if (hda->vga_switcheroo_registered) vga_switcheroo_unregister_client(chip->pci); }
- if (chip->initialized) { + if (bus->chip_init) { azx_clear_irq_pending(chip); azx_stop_all_streams(chip); azx_stop_chip(chip); }
- if (chip->irq >= 0) - free_irq(chip->irq, (void*)chip); + if (bus->irq >= 0) + free_irq(bus->irq, (void*)chip); if (chip->msi) pci_disable_msi(chip->pci); - iounmap(chip->remap_addr); + iounmap(bus->remap_addr);
azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(bus); + if (chip->region_requested) pci_release_regions(chip->pci); + pci_disable_device(chip->pci); #ifdef CONFIG_SND_HDA_PATCH_LOADER release_firmware(chip->fw); @@ -1115,6 +1123,14 @@ static int azx_free(struct azx *chip) return 0; }
+static int azx_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + return 0; +} + static int azx_dev_free(struct snd_device *device) { return azx_free(device->device_data); @@ -1281,9 +1297,9 @@ static void check_probe_mask(struct azx *chip, int dev) /* check forced option */ if (chip->codec_probe_mask != -1 && (chip->codec_probe_mask & AZX_FORCE_CODEC_MASK)) { - chip->codec_mask = chip->codec_probe_mask & 0xff; + azx_bus(chip)->codec_mask = chip->codec_probe_mask & 0xff; dev_info(chip->card->dev, "codec_mask forced to 0x%x\n", - chip->codec_mask); + (int)azx_bus(chip)->codec_mask); } }
@@ -1378,6 +1394,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, struct azx **rchip) { static struct snd_device_ops ops = { + .dev_disconnect = azx_dev_disconnect, .dev_free = azx_dev_free, }; struct hda_intel *hda; @@ -1397,13 +1414,10 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, }
chip = &hda->chip; - spin_lock_init(&chip->reg_lock); mutex_init(&chip->open_mutex); chip->card = card; chip->pci = pci; chip->ops = &pci_hda_ops; - chip->io_ops = &pci_hda_io_ops; - chip->irq = -1; chip->driver_caps = driver_caps; chip->driver_type = driver_caps & 0xff; check_msi(chip); @@ -1435,6 +1449,13 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, } chip->bdl_pos_adj = bdl_pos_adj;
+ err = azx_bus_init(chip, model[dev], &pci_hda_io_ops); + if (err < 0) { + kfree(hda); + pci_disable_device(pci); + return err; + } + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); if (err < 0) { dev_err(card->dev, "Error creating device [card]!\n"); @@ -1455,6 +1476,7 @@ static int azx_first_init(struct azx *chip) int dev = chip->dev_index; struct pci_dev *pci = chip->pci; struct snd_card *card = chip->card; + struct hdac_bus *bus = azx_bus(chip); int err; unsigned short gcap; unsigned int dma_bits = 64; @@ -1474,13 +1496,12 @@ static int azx_first_init(struct azx *chip) return err; chip->region_requested = 1;
- chip->addr = pci_resource_start(pci, 0); - chip->remap_addr = pci_ioremap_bar(pci, 0); - if (chip->remap_addr == NULL) { + bus->addr = pci_resource_start(pci, 0); + bus->remap_addr = pci_ioremap_bar(pci, 0); + if (bus->remap_addr == NULL) { dev_err(card->dev, "ioremap error\n"); return -ENXIO; } - azx_bus(chip)->remap_addr = chip->remap_addr; /* FIXME */
if (chip->msi) { if (chip->driver_caps & AZX_DCAPS_NO_MSI64) { @@ -1495,7 +1516,7 @@ static int azx_first_init(struct azx *chip) return -EBUSY;
pci_set_master(pci); - synchronize_irq(chip->irq); + synchronize_irq(bus->irq);
gcap = azx_readw(chip, GCAP); dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); @@ -1573,12 +1594,14 @@ static int azx_first_init(struct azx *chip) chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams;
- err = azx_alloc_stream_pages(chip); + /* initialize streams */ + err = azx_init_streams(chip); if (err < 0) return err;
- /* initialize streams */ - azx_init_stream(chip); + err = azx_alloc_stream_pages(chip); + if (err < 0) + return err;
/* initialize chip */ azx_init_pci(chip); @@ -1593,7 +1616,7 @@ static int azx_first_init(struct azx *chip) azx_init_chip(chip, (probe_only[dev] & 2) == 0);
/* codec detection */ - if (!chip->codec_mask) { + if (!azx_bus(chip)->codec_mask) { dev_err(card->dev, "no codecs found!\n"); return -ENODEV; } @@ -1603,7 +1626,7 @@ static int azx_first_init(struct azx *chip) sizeof(card->shortname)); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", - card->shortname, chip->addr, chip->irq); + card->shortname, bus->addr, bus->irq);
return 0; } @@ -1672,10 +1695,11 @@ static u8 pci_azx_readb(u8 __iomem *addr)
static int disable_msi_reset_irq(struct azx *chip) { + struct hdac_bus *bus = azx_bus(chip); int err;
- free_irq(chip->irq, chip); - chip->irq = -1; + free_irq(bus->irq, chip); + bus->irq = -1; pci_disable_msi(chip->pci); chip->msi = 0; err = azx_acquire_irq(chip, 1); @@ -1691,7 +1715,7 @@ static int dma_alloc_pages(struct hdac_bus *bus, size_t size, struct snd_dma_buffer *buf) { - struct azx *chip = to_hda_bus(bus)->private_data; + struct azx *chip = bus_to_azx(bus); int err;
err = snd_dma_alloc_pages(type, @@ -1705,7 +1729,7 @@ static int dma_alloc_pages(struct hdac_bus *bus,
static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { - struct azx *chip = to_hda_bus(bus)->private_data; + struct azx *chip = bus_to_azx(bus);
mark_pages_wc(chip, buf, false); snd_dma_free_pages(buf); @@ -1857,6 +1881,7 @@ static int azx_probe_continue(struct azx *chip) int dev = chip->dev_index; int err;
+ hda->probe_continued = 1; /* Request power well for Haswell HDA controller and codec */ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { #ifdef CONFIG_SND_HDA_I915 @@ -1872,10 +1897,6 @@ static int azx_probe_continue(struct azx *chip) #endif }
- err = azx_bus_create(chip, model[dev]); - if (err < 0) - goto out_free; - err = azx_first_init(chip); if (err < 0) goto out_free; @@ -1891,7 +1912,7 @@ static int azx_probe_continue(struct azx *chip)
#ifdef CONFIG_SND_HDA_PATCH_LOADER if (chip->fw) { - err = snd_hda_load_patch(chip->bus, chip->fw->size, + err = snd_hda_load_patch(&chip->bus, chip->fw->size, chip->fw->data); if (err < 0) goto out_free; @@ -1913,7 +1934,7 @@ static int azx_probe_continue(struct azx *chip)
chip->running = 1; azx_add_card_list(chip); - snd_hda_set_power_save(chip->bus, power_save * 1000); + snd_hda_set_power_save(&chip->bus, power_save * 1000); if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo) pm_runtime_put_noidle(&pci->dev);
diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index d5231f7216a7..206989878bc6 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -34,6 +34,7 @@ struct hda_intel {
/* extra flags */ unsigned int irq_pending_warned:1; + unsigned int probe_continued:1;
/* VGA-switcheroo setup */ unsigned int use_vga_switcheroo:1; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index c6fc96afbdc1..397e1821020f 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -285,6 +285,14 @@ static const struct dev_pm_ops hda_tegra_pm = { SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) };
+static int hda_tegra_dev_disconnect(struct snd_device *device) +{ + struct azx *chip = device->device_data; + + chip->bus.shutdown = 1; + return 0; +} + /* * destructor */ @@ -292,12 +300,14 @@ static int hda_tegra_dev_free(struct snd_device *device) { struct azx *chip = device->device_data;
- if (chip->initialized) { + if (azx_bus(chip)->chip_init) { azx_stop_all_streams(chip); azx_stop_chip(chip); }
azx_free_stream_pages(chip); + azx_free_streams(chip); + snd_hdac_bus_exit(bus);
return 0; } @@ -305,6 +315,7 @@ static int hda_tegra_dev_free(struct snd_device *device) static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) { struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); + struct hdac_bus *bus = azx_bus(chip); struct device *dev = hda->dev; struct resource *res; int err; @@ -324,9 +335,8 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) if (IS_ERR(hda->regs)) return PTR_ERR(hda->regs);
- chip->remap_addr = hda->regs + HDA_BAR0; - azx_bus(chip)->remap_addr = chip->remap_addr; /* FIXME */ - chip->addr = res->start + HDA_BAR0; + bus->remap_addr = hda->regs + HDA_BAR0; + bus->addr = res->start + HDA_BAR0;
err = hda_tegra_enable_clocks(hda); if (err) @@ -339,6 +349,7 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev)
static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) { + struct hdac_bus *bus = azx_bus(chip); struct snd_card *card = chip->card; int err; unsigned short gcap; @@ -356,9 +367,9 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) irq_id); return err; } - chip->irq = irq_id; + bus->irq = irq_id;
- synchronize_irq(chip->irq); + synchronize_irq(bus->irq);
gcap = azx_readw(chip, GCAP); dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap); @@ -377,18 +388,20 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) chip->playback_index_offset = chip->capture_streams; chip->num_streams = chip->playback_streams + chip->capture_streams;
- err = azx_alloc_stream_pages(chip); + /* initialize streams */ + err = azx_init_streams(chip); if (err < 0) return err;
- /* initialize streams */ - azx_init_stream(chip); + err = azx_alloc_stream_pages(chip); + if (err < 0) + return err;
/* initialize chip */ azx_init_chip(chip, 1);
/* codec detection */ - if (!chip->codec_mask) { + if (!bus->codec_mask) { dev_err(card->dev, "no codecs found!\n"); return -ENODEV; } @@ -397,7 +410,7 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) strcpy(card->shortname, "tegra-hda"); snprintf(card->longname, sizeof(card->longname), "%s at 0x%lx irq %i", - card->shortname, chip->addr, chip->irq); + card->shortname, bus->addr, bus->irq);
return 0; } @@ -410,6 +423,7 @@ static int hda_tegra_create(struct snd_card *card, struct hda_tegra *hda) { static struct snd_device_ops ops = { + .dev_disconnect = hda_tegra_dev_disconnect, .dev_free = hda_tegra_dev_free, }; struct azx *chip; @@ -417,12 +431,9 @@ static int hda_tegra_create(struct snd_card *card,
chip = &hda->chip;
- spin_lock_init(&chip->reg_lock); mutex_init(&chip->open_mutex); chip->card = card; chip->ops = &hda_tegra_ops; - chip->io_ops = &hda_tegra_io_ops; - chip->irq = -1; chip->driver_caps = driver_caps; chip->driver_type = driver_caps & 0xff; chip->dev_index = 0; @@ -469,7 +480,7 @@ static int hda_tegra_probe(struct platform_device *pdev) return err; }
- err = azx_bus_create(chip, NULL); + err = azx_bus_init(chip, NULL, &hda_tegra_io_ops); if (err < 0) goto out_free;
@@ -498,7 +509,7 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free;
chip->running = 1; - snd_hda_set_power_save(chip->bus, power_save * 1000); + snd_hda_set_power_save(&chip->bus, power_save * 1000);
return 0;
Move the small portion of the common sequence in hda_intel.c and hda_tegra.c into hda_controller.c.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_controller.c | 10 +++++++--- sound/pci/hda/hda_intel.c | 3 --- sound/pci/hda/hda_tegra.c | 3 --- 3 files changed, 7 insertions(+), 9 deletions(-)
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 21058b41b2c6..aaf01e841426 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -107,18 +107,22 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; + struct azx_dev *azx_dev = get_azx_dev(substream); int ret;
- dsp_lock(get_azx_dev(substream)); - if (dsp_is_locked(get_azx_dev(substream))) { + dsp_lock(azx_dev); + if (dsp_is_locked(azx_dev)) { ret = -EBUSY; goto unlock; }
+ azx_dev->core.bufsize = 0; + azx_dev->core.period_bytes = 0; + azx_dev->core.format_val = 0; ret = chip->ops->substream_alloc_pages(chip, substream, params_buffer_bytes(hw_params)); unlock: - dsp_unlock(get_azx_dev(substream)); + dsp_unlock(azx_dev); return ret; }
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 9dff693005ea..8a1471851ca7 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1743,9 +1743,6 @@ static int substream_alloc_pages(struct azx *chip, int ret;
mark_runtime_wc(chip, azx_dev, substream, false); - azx_dev->core.bufsize = 0; - azx_dev->core.period_bytes = 0; - azx_dev->core.format_val = 0; ret = snd_pcm_lib_malloc_pages(substream, size); if (ret < 0) return ret; diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 397e1821020f..d5349809f43d 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -104,9 +104,6 @@ static int substream_alloc_pages(struct azx *chip, { struct azx_dev *azx_dev = get_azx_dev(substream);
- azx_dev->core.bufsize = 0; - azx_dev->core.period_bytes = 0; - azx_dev->core.format_val = 0; return snd_pcm_lib_malloc_pages(substream, size); }
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/hdaudio.h | 9 ++ sound/hda/hdac_device.c | 300 ++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_codec.c | 305 ----------------------------------------- sound/pci/hda/hda_codec.h | 15 +- sound/pci/hda/hda_controller.c | 3 +- sound/pci/hda/patch_ca0132.c | 7 +- 6 files changed, 317 insertions(+), 322 deletions(-)
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 59d21848a472..15bc039de78d 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -123,6 +123,15 @@ int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, hda_nid_t *start_id); +unsigned int snd_hdac_calc_stream_format(unsigned int rate, + unsigned int channels, + unsigned int format, + unsigned int maxbps, + unsigned short spdif_ctls); +int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp); +bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, + unsigned int format);
/** * snd_hdac_read_parm - read a codec parameter diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index f75bf5622687..55c7d086b9dd 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -10,6 +10,7 @@ #include <linux/pm_runtime.h> #include <sound/hdaudio.h> #include <sound/hda_regmap.h> +#include <sound/pcm.h> #include "local.h"
static void setup_fg_nodes(struct hdac_device *codec); @@ -597,3 +598,302 @@ static int get_codec_vendor_name(struct hdac_device *codec) codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); return codec->vendor_name ? 0 : -ENOMEM; } + +/* + * stream formats + */ +struct hda_rate_tbl { + unsigned int hz; + unsigned int alsa_bits; + unsigned int hda_fmt; +}; + +/* rate = base * mult / div */ +#define HDA_RATE(base, mult, div) \ + (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ + (((div) - 1) << AC_FMT_DIV_SHIFT)) + +static struct hda_rate_tbl rate_bits[] = { + /* rate in Hz, ALSA rate bitmask, HDA format value */ + + /* autodetected value used in snd_hda_query_supported_pcm */ + { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) }, + { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, + { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, + { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, + { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, + { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, + { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, + { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, + { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, + { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, + { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, +#define AC_PAR_PCM_RATE_BITS 11 + /* up to bits 10, 384kHZ isn't supported properly */ + + /* not autodetected value */ + { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, + + { 0 } /* terminator */ +}; + +/** + * snd_hdac_calc_stream_format - calculate the format bitset + * @rate: the sample rate + * @channels: the number of channels + * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) + * @maxbps: the max. bps + * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) + * + * Calculate the format bitset from the given rate, channels and th PCM format. + * + * Return zero if invalid. + */ +unsigned int snd_hdac_calc_stream_format(unsigned int rate, + unsigned int channels, + unsigned int format, + unsigned int maxbps, + unsigned short spdif_ctls) +{ + int i; + unsigned int val = 0; + + for (i = 0; rate_bits[i].hz; i++) + if (rate_bits[i].hz == rate) { + val = rate_bits[i].hda_fmt; + break; + } + if (!rate_bits[i].hz) + return 0; + + if (channels == 0 || channels > 8) + return 0; + val |= channels - 1; + + switch (snd_pcm_format_width(format)) { + case 8: + val |= AC_FMT_BITS_8; + break; + case 16: + val |= AC_FMT_BITS_16; + break; + case 20: + case 24: + case 32: + if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) + val |= AC_FMT_BITS_32; + else if (maxbps >= 24) + val |= AC_FMT_BITS_24; + else + val |= AC_FMT_BITS_20; + break; + default: + return 0; + } + + if (spdif_ctls & AC_DIG1_NONAUDIO) + val |= AC_FMT_TYPE_NON_PCM; + + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format); + +static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int val = 0; + + if (nid != codec->afg && + (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) + val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM); + if (!val || val == -1) + val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM); + if (!val || val == -1) + return 0; + return val; +} + +static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM); + + if (!streams || streams == -1) + streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM); + if (!streams || streams == -1) + return 0; + return streams; +} + +/** + * snd_hdac_query_supported_pcm - query the supported PCM rates and formats + * @codec: the codec object + * @nid: NID to query + * @ratesp: the pointer to store the detected rate bitflags + * @formatsp: the pointer to store the detected formats + * @bpsp: the pointer to store the detected format widths + * + * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp + * or @bsps argument is ignored. + * + * Returns 0 if successful, otherwise a negative error code. + */ +int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp) +{ + unsigned int i, val, wcaps; + + wcaps = get_wcaps(codec, nid); + val = query_pcm_param(codec, nid); + + if (ratesp) { + u32 rates = 0; + for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { + if (val & (1 << i)) + rates |= rate_bits[i].alsa_bits; + } + if (rates == 0) { + dev_err(&codec->dev, + "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", + nid, val, + (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); + return -EIO; + } + *ratesp = rates; + } + + if (formatsp || bpsp) { + u64 formats = 0; + unsigned int streams, bps; + + streams = query_stream_param(codec, nid); + if (!streams) + return -EIO; + + bps = 0; + if (streams & AC_SUPFMT_PCM) { + if (val & AC_SUPPCM_BITS_8) { + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } + if (val & AC_SUPPCM_BITS_16) { + formats |= SNDRV_PCM_FMTBIT_S16_LE; + bps = 16; + } + if (wcaps & AC_WCAP_DIGITAL) { + if (val & AC_SUPPCM_BITS_32) + formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (val & AC_SUPPCM_BITS_24) + bps = 24; + else if (val & AC_SUPPCM_BITS_20) + bps = 20; + } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24| + AC_SUPPCM_BITS_32)) { + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (val & AC_SUPPCM_BITS_32) + bps = 32; + else if (val & AC_SUPPCM_BITS_24) + bps = 24; + else if (val & AC_SUPPCM_BITS_20) + bps = 20; + } + } +#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ + if (streams & AC_SUPFMT_FLOAT32) { + formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; + if (!bps) + bps = 32; + } +#endif + if (streams == AC_SUPFMT_AC3) { + /* should be exclusive */ + /* temporary hack: we have still no proper support + * for the direct AC3 stream... + */ + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } + if (formats == 0) { + dev_err(&codec->dev, + "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", + nid, val, + (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, + streams); + return -EIO; + } + if (formatsp) + *formatsp = formats; + if (bpsp) + *bpsp = bps; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm); + +/** + * snd_hdac_is_supported_format - Check the validity of the format + * @codec: the codec object + * @nid: NID to check + * @format: the HD-audio format value to check + * + * Check whether the given node supports the format value. + * + * Returns true if supported, false if not. + */ +bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, + unsigned int format) +{ + int i; + unsigned int val = 0, rate, stream; + + val = query_pcm_param(codec, nid); + if (!val) + return false; + + rate = format & 0xff00; + for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) + if (rate_bits[i].hda_fmt == rate) { + if (val & (1 << i)) + break; + return false; + } + if (i >= AC_PAR_PCM_RATE_BITS) + return false; + + stream = query_stream_param(codec, nid); + if (!stream) + return false; + + if (stream & AC_SUPFMT_PCM) { + switch (format & 0xf0) { + case 0x00: + if (!(val & AC_SUPPCM_BITS_8)) + return false; + break; + case 0x10: + if (!(val & AC_SUPPCM_BITS_16)) + return false; + break; + case 0x20: + if (!(val & AC_SUPPCM_BITS_20)) + return false; + break; + case 0x30: + if (!(val & AC_SUPPCM_BITS_24)) + return false; + break; + case 0x40: + if (!(val & AC_SUPPCM_BITS_32)) + return false; + break; + default: + return false; + } + } else { + /* FIXME: check for float32 and AC3? */ + } + + return true; +} +EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2abf9f95dcbb..df3cebc06110 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3192,311 +3192,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) }
/* - * stream formats - */ -struct hda_rate_tbl { - unsigned int hz; - unsigned int alsa_bits; - unsigned int hda_fmt; -}; - -/* rate = base * mult / div */ -#define HDA_RATE(base, mult, div) \ - (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ - (((div) - 1) << AC_FMT_DIV_SHIFT)) - -static struct hda_rate_tbl rate_bits[] = { - /* rate in Hz, ALSA rate bitmask, HDA format value */ - - /* autodetected value used in snd_hda_query_supported_pcm */ - { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) }, - { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, - { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, - { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, - { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, - { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, - { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, - { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, - { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, - { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, - { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, -#define AC_PAR_PCM_RATE_BITS 11 - /* up to bits 10, 384kHZ isn't supported properly */ - - /* not autodetected value */ - { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, - - { 0 } /* terminator */ -}; - -/** - * snd_hda_calc_stream_format - calculate format bitset - * @codec: HD-audio codec - * @rate: the sample rate - * @channels: the number of channels - * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) - * @maxbps: the max. bps - * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) - * - * Calculate the format bitset from the given rate, channels and th PCM format. - * - * Return zero if invalid. - */ -unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, - unsigned int rate, - unsigned int channels, - unsigned int format, - unsigned int maxbps, - unsigned short spdif_ctls) -{ - int i; - unsigned int val = 0; - - for (i = 0; rate_bits[i].hz; i++) - if (rate_bits[i].hz == rate) { - val = rate_bits[i].hda_fmt; - break; - } - if (!rate_bits[i].hz) { - codec_dbg(codec, "invalid rate %d\n", rate); - return 0; - } - - if (channels == 0 || channels > 8) { - codec_dbg(codec, "invalid channels %d\n", channels); - return 0; - } - val |= channels - 1; - - switch (snd_pcm_format_width(format)) { - case 8: - val |= AC_FMT_BITS_8; - break; - case 16: - val |= AC_FMT_BITS_16; - break; - case 20: - case 24: - case 32: - if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) - val |= AC_FMT_BITS_32; - else if (maxbps >= 24) - val |= AC_FMT_BITS_24; - else - val |= AC_FMT_BITS_20; - break; - default: - codec_dbg(codec, "invalid format width %d\n", - snd_pcm_format_width(format)); - return 0; - } - - if (spdif_ctls & AC_DIG1_NONAUDIO) - val |= AC_FMT_TYPE_NON_PCM; - - return val; -} -EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format); - -static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int val = 0; - if (nid != codec->core.afg && - (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) - val = snd_hda_param_read(codec, nid, AC_PAR_PCM); - if (!val || val == -1) - val = snd_hda_param_read(codec, codec->core.afg, AC_PAR_PCM); - if (!val || val == -1) - return 0; - return val; -} - -static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); - if (!streams || streams == -1) - streams = snd_hda_param_read(codec, codec->core.afg, AC_PAR_STREAM); - if (!streams || streams == -1) - return 0; - return streams; -} - -/** - * snd_hda_query_supported_pcm - query the supported PCM rates and formats - * @codec: the HDA codec - * @nid: NID to query - * @ratesp: the pointer to store the detected rate bitflags - * @formatsp: the pointer to store the detected formats - * @bpsp: the pointer to store the detected format widths - * - * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp - * or @bsps argument is ignored. - * - * Returns 0 if successful, otherwise a negative error code. - */ -int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, - u32 *ratesp, u64 *formatsp, unsigned int *bpsp) -{ - unsigned int i, val, wcaps; - - wcaps = get_wcaps(codec, nid); - val = query_pcm_param(codec, nid); - - if (ratesp) { - u32 rates = 0; - for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { - if (val & (1 << i)) - rates |= rate_bits[i].alsa_bits; - } - if (rates == 0) { - codec_err(codec, - "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", - nid, val, - (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); - return -EIO; - } - *ratesp = rates; - } - - if (formatsp || bpsp) { - u64 formats = 0; - unsigned int streams, bps; - - streams = query_stream_param(codec, nid); - if (!streams) - return -EIO; - - bps = 0; - if (streams & AC_SUPFMT_PCM) { - if (val & AC_SUPPCM_BITS_8) { - formats |= SNDRV_PCM_FMTBIT_U8; - bps = 8; - } - if (val & AC_SUPPCM_BITS_16) { - formats |= SNDRV_PCM_FMTBIT_S16_LE; - bps = 16; - } - if (wcaps & AC_WCAP_DIGITAL) { - if (val & AC_SUPPCM_BITS_32) - formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; - if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (val & AC_SUPPCM_BITS_24) - bps = 24; - else if (val & AC_SUPPCM_BITS_20) - bps = 20; - } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24| - AC_SUPPCM_BITS_32)) { - formats |= SNDRV_PCM_FMTBIT_S32_LE; - if (val & AC_SUPPCM_BITS_32) - bps = 32; - else if (val & AC_SUPPCM_BITS_24) - bps = 24; - else if (val & AC_SUPPCM_BITS_20) - bps = 20; - } - } -#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ - if (streams & AC_SUPFMT_FLOAT32) { - formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; - if (!bps) - bps = 32; - } -#endif - if (streams == AC_SUPFMT_AC3) { - /* should be exclusive */ - /* temporary hack: we have still no proper support - * for the direct AC3 stream... - */ - formats |= SNDRV_PCM_FMTBIT_U8; - bps = 8; - } - if (formats == 0) { - codec_err(codec, - "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", - nid, val, - (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, - streams); - return -EIO; - } - if (formatsp) - *formatsp = formats; - if (bpsp) - *bpsp = bps; - } - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_query_supported_pcm); - -/** - * snd_hda_is_supported_format - Check the validity of the format - * @codec: HD-audio codec - * @nid: NID to check - * @format: the HD-audio format value to check - * - * Check whether the given node supports the format value. - * - * Returns 1 if supported, 0 if not. - */ -int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, - unsigned int format) -{ - int i; - unsigned int val = 0, rate, stream; - - val = query_pcm_param(codec, nid); - if (!val) - return 0; - - rate = format & 0xff00; - for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) - if (rate_bits[i].hda_fmt == rate) { - if (val & (1 << i)) - break; - return 0; - } - if (i >= AC_PAR_PCM_RATE_BITS) - return 0; - - stream = query_stream_param(codec, nid); - if (!stream) - return 0; - - if (stream & AC_SUPFMT_PCM) { - switch (format & 0xf0) { - case 0x00: - if (!(val & AC_SUPPCM_BITS_8)) - return 0; - break; - case 0x10: - if (!(val & AC_SUPPCM_BITS_16)) - return 0; - break; - case 0x20: - if (!(val & AC_SUPPCM_BITS_20)) - return 0; - break; - case 0x30: - if (!(val & AC_SUPPCM_BITS_24)) - return 0; - break; - case 0x40: - if (!(val & AC_SUPPCM_BITS_32)) - return 0; - break; - default: - return 0; - } - } else { - /* FIXME: check for float32 and AC3? */ - } - - return 1; -} -EXPORT_SYMBOL_GPL(snd_hda_is_supported_format); - -/* * PCM stuff */ static int hda_pcm_default_open_close(struct hda_pcm_stream *hinfo, diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 57b9aa0f36c1..1ffdd39cb556 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -365,8 +365,6 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive); int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, u8 *dev_list, int max_devices); -int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, - u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
struct hda_verb { hda_nid_t nid; @@ -458,14 +456,11 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid, int do_now); #define snd_hda_codec_cleanup_stream(codec, nid) \ __snd_hda_codec_cleanup_stream(codec, nid, 0) -unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, - unsigned int rate, - unsigned int channels, - unsigned int format, - unsigned int maxbps, - unsigned short spdif_ctls); -int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid, - unsigned int format); + +#define snd_hda_query_supported_pcm(codec, nid, ratesp, fmtsp, bpsp) \ + snd_hdac_query_supported_pcm(&(codec)->core, nid, ratesp, fmtsp, bpsp) +#define snd_hda_is_supported_format(codec, nid, fmt) \ + snd_hdac_is_supported_format(&(codec)->core, nid, fmt)
extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[];
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index aaf01e841426..8f12ad9a4df5 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -167,8 +167,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) }
snd_hdac_stream_reset(azx_stream(azx_dev)); - format_val = snd_hda_calc_stream_format(apcm->codec, - runtime->rate, + format_val = snd_hdac_calc_stream_format(runtime->rate, runtime->channels, runtime->format, hinfo->maxbps, diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 4a4e7b282e4f..2de1a4222a7d 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -2052,11 +2052,8 @@ static int dma_convert_to_hda_format(struct hda_codec *codec, { unsigned int format_val;
- format_val = snd_hda_calc_stream_format(codec, - sample_rate, - channels, - SNDRV_PCM_FORMAT_S32_LE, - 32, 0); + format_val = snd_hdac_calc_stream_format(sample_rate, + channels, SNDRV_PCM_FORMAT_S32_LE, 32, 0);
if (hda_format) *hda_format = (unsigned short)format_val;
For fixing randconfig build errors like:
sound/hda/hdac_stream.c: In function 'azx_timecounter_init': sound/hda/hdac_stream.c:365:2: error: implicit declaration of function 'CLOCKSOURCE_MASK' [-Werror=implicit-function-declaration]
Reported-by: kbuild test robot fengguang.wu@intel.com Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/hda/hdac_stream.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 8bd67a824b5e..9ffff6d9ba8e 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -5,6 +5,7 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/clocksource.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/hdaudio.h>
After correcting the fields to point the right members, tracepoints can be reenabled again for the legacy controller code.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_controller.c | 6 ++++++ sound/pci/hda/hda_intel_trace.h | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 8f12ad9a4df5..0e43f79e1d9b 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -31,6 +31,9 @@ #include <sound/initval.h> #include "hda_controller.h"
+#define CREATE_TRACE_POINTS +#include "hda_intel_trace.h" + /* DSP lock helpers */ #define dsp_lock(dev) snd_hdac_dsp_lock(azx_stream(dev)) #define dsp_unlock(dev) snd_hdac_dsp_unlock(azx_stream(dev)) @@ -229,6 +232,8 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) int sync_reg;
azx_dev = get_azx_dev(substream); + trace_azx_pcm_trigger(chip, azx_dev, cmd); + hstr = azx_stream(azx_dev); if (chip->driver_caps & AZX_DCAPS_OLD_SSYNC) sync_reg = AZX_REG_OLD_SSYNC; @@ -330,6 +335,7 @@ unsigned int azx_get_position(struct azx *chip, substream->runtime->delay = delay; }
+ trace_azx_get_position(chip, azx_dev, pos, delay); return pos; } EXPORT_SYMBOL_GPL(azx_get_position); diff --git a/sound/pci/hda/hda_intel_trace.h b/sound/pci/hda/hda_intel_trace.h index 7b5e4c2cf9d5..ae004737d0fd 100644 --- a/sound/pci/hda/hda_intel_trace.h +++ b/sound/pci/hda/hda_intel_trace.h @@ -24,7 +24,7 @@ TRACE_EVENT(azx_pcm_trigger,
TP_fast_assign( __entry->card = (chip)->card->number; - __entry->idx = (dev)->index; + __entry->idx = (dev)->core.index; __entry->cmd = cmd; ),
@@ -46,7 +46,7 @@ TRACE_EVENT(azx_get_position,
TP_fast_assign( __entry->card = (chip)->card->number; - __entry->idx = (dev)->index; + __entry->idx = (dev)->core.index; __entry->pos = pos; __entry->delay = delay; ),
I seem to have failed to run the build test properly... sound/pci/hda/hda_tegra.c: In function 'hda_tegra_dev_free': sound/pci/hda/hda_tegra.c:310:20: error: 'bus' undeclared (first use in this function) snd_hdac_bus_exit(bus);
Reported-by: kbuild test robot fengguang.wu@intel.com Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_tegra.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index d5349809f43d..801e9fb4a467 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -102,8 +102,6 @@ static int substream_alloc_pages(struct azx *chip, struct snd_pcm_substream *substream, size_t size) { - struct azx_dev *azx_dev = get_azx_dev(substream); - return snd_pcm_lib_malloc_pages(substream, size); }
@@ -304,7 +302,7 @@ static int hda_tegra_dev_free(struct snd_device *device)
azx_free_stream_pages(chip); azx_free_streams(chip); - snd_hdac_bus_exit(bus); + snd_hdac_bus_exit(azx_bus(chip));
return 0; }
They are no longer used (only one place which can be replaced with a proper helper function). Let's drop.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_controller.h | 13 ------------- sound/pci/hda/hda_intel.c | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-)
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index e8edb02c12d3..407cba6577b8 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -185,19 +185,6 @@ struct azx { #define azx_readb(chip, reg) \ snd_hdac_chip_readb(azx_bus(chip), reg)
-#define azx_sd_writel(chip, dev, reg, value) \ - snd_hdac_stream_writel(&(dev)->core, reg, value) -#define azx_sd_readl(chip, dev, reg) \ - snd_hdac_stream_readl(&(dev)->core, reg) -#define azx_sd_writew(chip, dev, reg, value) \ - snd_hdac_stream_writew(&(dev)->core, reg, value) -#define azx_sd_readw(chip, dev, reg) \ - snd_hdac_stream_readw(&(dev)->core, reg) -#define azx_sd_writeb(chip, dev, reg, value) \ - snd_hdac_stream_writeb(&(dev)->core, reg, value) -#define azx_sd_readb(chip, dev, reg) \ - snd_hdac_stream_readb(&(dev)->core, reg) - #define azx_has_pm_runtime(chip) \ (!AZX_DCAPS_PM_RUNTIME || ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME))
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 8a1471851ca7..2b823d4ad888 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -680,7 +680,7 @@ static unsigned int azx_via_get_position(struct azx *chip, unsigned int mod_link_pos, mod_dma_pos, mod_mini_pos; unsigned int fifo_size;
- link_pos = azx_sd_readl(chip, azx_dev, SD_LPIB); + link_pos = snd_hdac_stream_get_pos_lpib(azx_stream(azx_dev)); if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /* Playback, no problem using link position */ return link_pos;
participants (1)
-
Takashi Iwai