From 14752412721c61d9ac1e8d8fb51d7148cb15f85b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 14 Apr 2015 12:15:47 +0200 Subject: ALSA: hda - Add the controller helper codes to hda-core module 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 --- include/sound/hda_register.h | 152 +++++++++++++++++++++++++++++ include/sound/hdaudio.h | 224 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 include/sound/hda_register.h (limited to 'include/sound') 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 +#include + +#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 +#include +#include +#include +#include #include /* 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 */ -- cgit v1.2.3 From 8f3f600b52b100f254fc16a60af1261d2e4dc239 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 14 Apr 2015 12:53:28 +0200 Subject: ALSA: hda - Add DSP loader to core library code Copied from the legacy driver code, no transition done yet. Signed-off-by: Takashi Iwai --- 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(-) (limited to 'include/sound') 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 -- cgit v1.2.3 From 304dad30388d017544bc2e90fe4fefcca94263d3 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Sun, 12 Apr 2015 18:06:13 +0530 Subject: ALSA: hda - moved alloc/free stream pages function to controller library Moved azx_alloc_stream_pages and azx_free_stream_pages to controller library. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 3 +++ sound/hda/hdac_controller.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ sound/hda/hdac_stream.c | 2 -- 3 files changed, 61 insertions(+), 2 deletions(-) (limited to 'include/sound') 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 */ -- cgit v1.2.3 From b7d023e11434131e5a7ceb4be33c3afa2c855e89 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 16 Apr 2015 08:19:06 +0200 Subject: ALSA: hda - Move PCM format and rate handling code to core library Signed-off-by: Takashi Iwai --- 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(-) (limited to 'include/sound') 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 #include #include +#include #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 @@ -3191,311 +3191,6 @@ int snd_hda_codec_build_controls(struct hda_codec *codec) return 0; } -/* - * 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 */ 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; -- cgit v1.2.3 From 6d23c8f5440e33cb854e394d38b8c19315f21428 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 17 Apr 2015 13:34:30 +0200 Subject: ALSA: hda - Move prepared flag into struct hdac_stream This flag seems used commonly, so deserves to be located there. Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 1 + sound/pci/hda/hda_controller.c | 8 ++++---- sound/pci/hda/hda_controller.h | 1 - 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include/sound') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 15bc039de78d..dbeb195eb4e8 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -390,6 +390,7 @@ struct hdac_stream { bool opened:1; bool running:1; + bool prepared:1; bool no_period_wakeup:1; bool locked:1; diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 9bc8eaf0e5ac..14ffb6bd986c 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -145,7 +145,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) snd_hda_codec_cleanup(apcm->codec, hinfo, substream); err = chip->ops->substream_free_pages(chip, substream); - azx_dev->prepared = 0; + azx_stream(azx_dev)->prepared = 0; dsp_unlock(azx_dev); return err; } @@ -214,7 +214,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) unlock: if (!err) - azx_dev->prepared = 1; + azx_stream(azx_dev)->prepared = 1; dsp_unlock(azx_dev); return err; } @@ -240,7 +240,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) else sync_reg = AZX_REG_SSYNC; - if (dsp_is_locked(azx_dev) || !azx_dev->prepared) + if (dsp_is_locked(azx_dev) || !hstr->prepared) return -EPIPE; switch (cmd) { @@ -843,7 +843,7 @@ int snd_hda_codec_load_dsp_prepare(struct hda_codec *codec, unsigned int format, return err; } - azx_dev->prepared = 0; + hstr->prepared = 0; return err; } EXPORT_SYMBOL_GPL(snd_hda_codec_load_dsp_prepare); diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index 407cba6577b8..173bf7c85b6e 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -62,7 +62,6 @@ struct azx_dev { struct hdac_stream core; unsigned int irq_pending:1; - unsigned int prepared:1; /* * For VIA: * A flag to ensure DMA position is 0 -- cgit v1.2.3 From c1cc18b1ca01530a40ace0c9ec48124ff1340125 Mon Sep 17 00:00:00 2001 From: Ramesh Babu Date: Fri, 17 Apr 2015 17:58:57 +0530 Subject: ALSA: hda - add ASoC device type for hda core Add HDA_DEV_ASOC device/driver type to support ASoC HDA drivers. Signed-off-by: Ramesh Babu Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/sound') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index dbeb195eb4e8..d05931fc6f28 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -90,6 +90,7 @@ struct hdac_device { enum { HDA_DEV_CORE, HDA_DEV_LEGACY, + HDA_DEV_ASOC, }; /* direction */ -- cgit v1.2.3 From 86f6501bf4c13e805e48497aaffab86ad7a98c44 Mon Sep 17 00:00:00 2001 From: Jeeja KP Date: Fri, 17 Apr 2015 17:58:58 +0530 Subject: ALSA: hda - add generic functions to set hdac stream params This will be used by hda controller driver to setup stream params in prepare. This function will setup the bdl and periods. Signed-off-by: Jeeja KP Signed-off-by: Subhransu S. Prusty Signed-off-by: Vinod Koul Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 2 ++ sound/hda/hdac_stream.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) (limited to 'include/sound') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index d05931fc6f28..6a2e030c836c 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -418,6 +418,8 @@ 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); +int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, + unsigned int format_val); 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); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 9ffff6d9ba8e..1ba0462ef7ca 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -394,6 +394,44 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) } EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods); +/* snd_hdac_stream_set_params - set stream parameters + * @azx_dev: HD-audio core stream for which parameters are to be set + * @format_val: format value parameter + * + * Setup the HD-audio core stream parameters from substream of the stream + * and passed format value + */ +int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, + unsigned int format_val) +{ + + unsigned int bufsize, period_bytes; + struct snd_pcm_substream *substream = azx_dev->substream; + struct snd_pcm_runtime *runtime; + int err; + + if (!substream) + return -EINVAL; + runtime = substream->runtime; + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + + if (bufsize != azx_dev->bufsize || + period_bytes != azx_dev->period_bytes || + format_val != azx_dev->format_val || + runtime->no_period_wakeup != azx_dev->no_period_wakeup) { + azx_dev->bufsize = bufsize; + azx_dev->period_bytes = period_bytes; + azx_dev->format_val = format_val; + azx_dev->no_period_wakeup = runtime->no_period_wakeup; + err = snd_hdac_stream_setup_periods(azx_dev); + if (err < 0) + return err; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_stream_set_params); + static cycle_t azx_cc_read(const struct cyclecounter *cc) { struct hdac_stream *azx_dev = container_of(cc, struct hdac_stream, cc); -- cgit v1.2.3