Skip to content

Commit

Permalink
ALSA: hda: Direct MMIO accesses
Browse files Browse the repository at this point in the history
HD-audio drivers access to the mmio registers indirectly via the
corresponding bus->io_ops callbacks.  This is because some platform
(notably Tegra SoC) requires the word-aligned access.  But it's rather
a rare case, and other platforms suffer from the penalties by indirect
calls unnecessarily.

This patch is an attempt to optimize and cleanup for this situation.
Now the special aligned access is used only when a new kconfig
CONFIG_SND_HDA_ALIGNED_MMIO is set.  And the HD-audio core itself
provides the aligned MMIO access helpers instead of the driver side.
If Kconfig isn't set (as default), the standard helpers like readl()
or writel() are used directly.

A couple of places in ASoC Intel drivers have the access via io_ops
reg_writel(), and they are replaced with the direct writel() calls.

And now with this patch, the whole bus->io_ops becomes empty, so it's
dropped completely.  The bus initialization functions are changed
accordingly as well to drop the whole bus->io_ops.

Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Takashi Iwai committed Aug 8, 2019
1 parent 619a1f1 commit 19abfef
Showing 13 changed files with 75 additions and 248 deletions.
63 changes: 30 additions & 33 deletions include/sound/hdaudio.h
Original file line number Diff line number Diff line change
@@ -253,19 +253,6 @@ struct hdac_ext_bus_ops {
int (*hdev_detach)(struct hdac_device *hdev);
};

/*
* 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 */

@@ -299,7 +286,6 @@ struct hdac_rb {
struct hdac_bus {
struct device *dev;
const struct hdac_bus_ops *ops;
const struct hdac_io_ops *io_ops;
const struct hdac_ext_bus_ops *ext_ops;

/* h/w resources */
@@ -380,8 +366,7 @@ struct hdac_bus {
};

int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops);
const struct hdac_bus_ops *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);
@@ -425,21 +410,38 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status,
int snd_hdac_bus_alloc_stream_pages(struct hdac_bus *bus);
void snd_hdac_bus_free_stream_pages(struct hdac_bus *bus);

#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask);
void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
unsigned int mask);
#define snd_hdac_reg_writeb(v, addr) snd_hdac_aligned_write(v, addr, 0xff)
#define snd_hdac_reg_writew(v, addr) snd_hdac_aligned_write(v, addr, 0xffff)
#define snd_hdac_reg_readb(addr) snd_hdac_aligned_read(addr, 0xff)
#define snd_hdac_reg_readw(addr) snd_hdac_aligned_read(addr, 0xffff)
#else /* CONFIG_SND_HDA_ALIGNED_MMIO */
#define snd_hdac_reg_writeb(val, addr) writeb(val, addr)
#define snd_hdac_reg_writew(val, addr) writew(val, addr)
#define snd_hdac_reg_readb(addr) readb(addr)
#define snd_hdac_reg_readw(addr) readw(addr)
#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
#define snd_hdac_reg_writel(val, addr) writel(val, addr)
#define snd_hdac_reg_readl(addr) readl(addr)

/*
* macros for easy use
*/
#define _snd_hdac_chip_writeb(chip, reg, value) \
((chip)->io_ops->reg_writeb(value, (chip)->remap_addr + (reg)))
snd_hdac_reg_writeb(value, (chip)->remap_addr + (reg))
#define _snd_hdac_chip_readb(chip, reg) \
((chip)->io_ops->reg_readb((chip)->remap_addr + (reg)))
snd_hdac_reg_readb((chip)->remap_addr + (reg))
#define _snd_hdac_chip_writew(chip, reg, value) \
((chip)->io_ops->reg_writew(value, (chip)->remap_addr + (reg)))
snd_hdac_reg_writew(value, (chip)->remap_addr + (reg))
#define _snd_hdac_chip_readw(chip, reg) \
((chip)->io_ops->reg_readw((chip)->remap_addr + (reg)))
snd_hdac_reg_readw((chip)->remap_addr + (reg))
#define _snd_hdac_chip_writel(chip, reg, value) \
((chip)->io_ops->reg_writel(value, (chip)->remap_addr + (reg)))
snd_hdac_reg_writel(value, (chip)->remap_addr + (reg))
#define _snd_hdac_chip_readl(chip, reg) \
((chip)->io_ops->reg_readl((chip)->remap_addr + (reg)))
snd_hdac_reg_readl((chip)->remap_addr + (reg))

/* read/write a register, pass without AZX_REG_ prefix */
#define snd_hdac_chip_writel(chip, reg, value) \
@@ -544,24 +546,19 @@ int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
/*
* 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)
snd_hdac_reg_writel(value, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_writew(dev, reg, value) \
_snd_hdac_stream_write(w, dev, AZX_REG_ ## reg, value)
snd_hdac_reg_writew(value, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_writeb(dev, reg, value) \
_snd_hdac_stream_write(b, dev, AZX_REG_ ## reg, value)
snd_hdac_reg_writeb(value, (dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readl(dev, reg) \
_snd_hdac_stream_read(l, dev, AZX_REG_ ## reg)
snd_hdac_reg_readl((dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readw(dev, reg) \
_snd_hdac_stream_read(w, dev, AZX_REG_ ## reg)
snd_hdac_reg_readw((dev)->sd_addr + AZX_REG_ ## reg)
#define snd_hdac_stream_readb(dev, reg) \
_snd_hdac_stream_read(b, dev, AZX_REG_ ## reg)
snd_hdac_reg_readb((dev)->sd_addr + AZX_REG_ ## reg)

/* update a register, pass without AZX_REG_ prefix */
#define snd_hdac_stream_updatel(dev, reg, mask, val) \
1 change: 0 additions & 1 deletion include/sound/hdaudio_ext.h
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@

int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops,
const struct hdac_ext_bus_ops *ext_ops);

void snd_hdac_ext_bus_exit(struct hdac_bus *bus);
3 changes: 3 additions & 0 deletions sound/hda/Kconfig
Original file line number Diff line number Diff line change
@@ -6,6 +6,9 @@ config SND_HDA_CORE
config SND_HDA_DSP_LOADER
bool

config SND_HDA_ALIGNED_MMIO
bool

config SND_HDA_COMPONENT
bool

47 changes: 1 addition & 46 deletions sound/hda/ext/hdac_ext_bus.c
Original file line number Diff line number Diff line change
@@ -17,67 +17,22 @@
MODULE_DESCRIPTION("HDA extended core");
MODULE_LICENSE("GPL v2");

static void hdac_ext_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}

static u32 hdac_ext_readl(u32 __iomem *addr)
{
return readl(addr);
}

static void hdac_ext_writew(u16 value, u16 __iomem *addr)
{
writew(value, addr);
}

static u16 hdac_ext_readw(u16 __iomem *addr)
{
return readw(addr);
}

static void hdac_ext_writeb(u8 value, u8 __iomem *addr)
{
writeb(value, addr);
}

static u8 hdac_ext_readb(u8 __iomem *addr)
{
return readb(addr);
}

static const struct hdac_io_ops hdac_ext_default_io = {
.reg_writel = hdac_ext_writel,
.reg_readl = hdac_ext_readl,
.reg_writew = hdac_ext_writew,
.reg_readw = hdac_ext_readw,
.reg_writeb = hdac_ext_writeb,
.reg_readb = hdac_ext_readb,
};

/**
* snd_hdac_ext_bus_init - initialize a HD-audio extended bus
* @ebus: the pointer to extended bus object
* @dev: device pointer
* @ops: bus verb operators
* @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use
* default ops
*
* Returns 0 if successful, or a negative error code.
*/
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
const struct hdac_bus_ops *ops,
const struct hdac_io_ops *io_ops,
const struct hdac_ext_bus_ops *ext_ops)
{
int ret;

/* check if io ops are provided, if not load the defaults */
if (io_ops == NULL)
io_ops = &hdac_ext_default_io;

ret = snd_hdac_bus_init(bus, dev, ops, io_ops);
ret = snd_hdac_bus_init(bus, dev, ops);
if (ret < 0)
return ret;

35 changes: 31 additions & 4 deletions sound/hda/hdac_bus.c
Original file line number Diff line number Diff line change
@@ -19,21 +19,18 @@ static const struct hdac_bus_ops default_ops = {
* 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_io_ops *io_ops)
const struct hdac_bus_ops *ops)
{
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
if (ops)
bus->ops = ops;
else
bus->ops = &default_ops;
bus->io_ops = io_ops;
bus->dma_type = SNDRV_DMA_TYPE_DEV;
INIT_LIST_HEAD(&bus->stream_list);
INIT_LIST_HEAD(&bus->codec_list);
@@ -218,3 +215,33 @@ void snd_hdac_bus_remove_device(struct hdac_bus *bus,
flush_work(&bus->unsol_work);
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device);

#ifdef CONFIG_SND_HDA_ALIGNED_MMIO
/* Helpers for aligned read/write of mmio space, for Tegra */
unsigned int snd_hdac_aligned_read(void __iomem *addr, unsigned int mask)
{
void __iomem *aligned_addr =
(void __iomem *)((unsigned long)(addr) & ~0x3);
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
unsigned int v;

v = readl(aligned_addr);
return (v >> shift) & mask;
}
EXPORT_SYMBOL_GPL(snd_hdac_aligned_read);

void snd_hdac_aligned_write(unsigned int val, void __iomem *addr,
unsigned int mask)
{
void __iomem *aligned_addr =
(void __iomem *)((unsigned long)(addr) & ~0x3);
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
unsigned int v;

v = readl(aligned_addr);
v &= ~(mask << shift);
v |= val << shift;
writel(v, aligned_addr);
}
EXPORT_SYMBOL_GPL(snd_hdac_aligned_write);
#endif /* CONFIG_SND_HDA_ALIGNED_MMIO */
1 change: 1 addition & 0 deletions sound/pci/hda/Kconfig
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ config SND_HDA_TEGRA
tristate "NVIDIA Tegra HD Audio"
depends on ARCH_TEGRA
select SND_HDA
select SND_HDA_ALIGNED_MMIO
help
Say Y here to support the HDA controller present in NVIDIA
Tegra SoCs
6 changes: 2 additions & 4 deletions sound/pci/hda/hda_controller.c
Original file line number Diff line number Diff line change
@@ -1202,14 +1202,12 @@ void snd_hda_bus_reset(struct hda_bus *bus)
}

/* HD-audio bus initialization */
int azx_bus_init(struct azx *chip, const char *model,
const struct hdac_io_ops *io_ops)
int azx_bus_init(struct azx *chip, const char *model)
{
struct hda_bus *bus = &chip->bus;
int err;

err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops,
io_ops);
err = snd_hdac_bus_init(&bus->core, chip->card->dev, &bus_core_ops);
if (err < 0)
return err;

3 changes: 1 addition & 2 deletions sound/pci/hda/hda_controller.h
Original file line number Diff line number Diff line change
@@ -206,8 +206,7 @@ void azx_stop_chip(struct azx *chip);
irqreturn_t azx_interrupt(int irq, void *dev_id);

/* Codec interface */
int azx_bus_init(struct azx *chip, const char *model,
const struct hdac_io_ops *io_ops);
int azx_bus_init(struct azx *chip, const char *model);
int azx_probe_codecs(struct azx *chip, unsigned int max_slots);
int azx_codec_configure(struct azx *chip);
int azx_init_streams(struct azx *chip);
47 changes: 1 addition & 46 deletions sound/pci/hda/hda_intel.c
Original file line number Diff line number Diff line change
@@ -1627,7 +1627,6 @@ static int default_bdl_pos_adj(struct azx *chip)
/*
* 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,
@@ -1687,7 +1686,7 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci,
else
chip->bdl_pos_adj = bdl_pos_adj[dev];

err = azx_bus_init(chip, model[dev], &pci_hda_io_ops);
err = azx_bus_init(chip, model[dev]);
if (err < 0) {
kfree(hda);
pci_disable_device(pci);
@@ -1932,41 +1931,6 @@ static void azx_firmware_cb(const struct firmware *fw, void *context)
}
#endif

/*
* HDA controller ops.
*/

/* PCI register access. */
static void pci_azx_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}

static u32 pci_azx_readl(u32 __iomem *addr)
{
return readl(addr);
}

static void pci_azx_writew(u16 value, u16 __iomem *addr)
{
writew(value, addr);
}

static u16 pci_azx_readw(u16 __iomem *addr)
{
return readw(addr);
}

static void pci_azx_writeb(u8 value, u8 __iomem *addr)
{
writeb(value, addr);
}

static u8 pci_azx_readb(u8 __iomem *addr)
{
return readb(addr);
}

static int disable_msi_reset_irq(struct azx *chip)
{
struct hdac_bus *bus = azx_bus(chip);
@@ -1994,15 +1958,6 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
#endif
}

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,
};

static const struct hda_controller_ops pci_hda_ops = {
.disable_msi_reset_irq = disable_msi_reset_irq,
.pcm_mmap_prepare = pcm_mmap_prepare,
Loading

0 comments on commit 19abfef

Please sign in to comment.