Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19abfefd authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda: Direct MMIO accesses



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: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 619a1f19
Loading
Loading
Loading
Loading
+30 −33
Original line number Diff line number Diff line
@@ -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) \
+0 −1
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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

+1 −46
Original line number Diff line number Diff line
@@ -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;

+31 −4
Original line number Diff line number Diff line
@@ -19,13 +19,11 @@ 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;
@@ -33,7 +31,6 @@ int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev,
		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 */
Loading