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: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2019-08-07 20:32:08 +02:00
parent 619a1f195f
commit 19abfefd4c
13 changed files with 75 additions and 248 deletions

View File

@ -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) \

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

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

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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,

View File

@ -75,72 +75,6 @@ MODULE_PARM_DESC(power_save,
#define power_save 0
#endif
/*
* Register access ops. Tegra HDA register access is DWORD only.
*/
static void hda_tegra_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}
static u32 hda_tegra_readl(u32 __iomem *addr)
{
return readl(addr);
}
static void hda_tegra_writew(u16 value, u16 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
v &= ~(0xffff << shift);
v |= value << shift;
writel(v, dword_addr);
}
static u16 hda_tegra_readw(u16 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
return (v >> shift) & 0xffff;
}
static void hda_tegra_writeb(u8 value, u8 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
v &= ~(0xff << shift);
v |= value << shift;
writel(v, dword_addr);
}
static u8 hda_tegra_readb(u8 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
return (v >> shift) & 0xff;
}
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,
.reg_readw = hda_tegra_readw,
.reg_writeb = hda_tegra_writeb,
.reg_readb = hda_tegra_readb,
};
static const struct hda_controller_ops hda_tegra_ops; /* nothing special */
static void hda_tegra_init(struct hda_tegra *hda)
@ -459,7 +393,7 @@ static int hda_tegra_create(struct snd_card *card,
INIT_WORK(&hda->probe_work, hda_tegra_probe_work);
err = azx_bus_init(chip, NULL, &hda_tegra_io_ops);
err = azx_bus_init(chip, NULL);
if (err < 0)
return err;

View File

@ -132,7 +132,7 @@ static int skl_init_chip(struct hdac_bus *bus, bool full_reset)
/* Reset stream-to-link mapping */
list_for_each_entry(hlink, &bus->hlink_list, list)
bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
skl_enable_miscbdcge(bus->dev, true);
@ -854,7 +854,6 @@ out_err:
* constructor
*/
static int skl_create(struct pci_dev *pci,
const struct hdac_io_ops *io_ops,
struct skl **rskl)
{
struct hdac_ext_bus_ops *ext_ops = NULL;
@ -884,7 +883,7 @@ static int skl_create(struct pci_dev *pci,
#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC)
ext_ops = snd_soc_hdac_hda_get_ops();
#endif
snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, ext_ops);
snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, ext_ops);
bus->use_posbuf = 1;
skl->pci = pci;
INIT_WORK(&skl->probe_work, skl_probe_work);
@ -1013,7 +1012,7 @@ static int skl_probe(struct pci_dev *pci,
}
/* we use ext core ops, so provide NULL for ops here */
err = skl_create(pci, NULL, &skl);
err = skl_create(pci, &skl);
if (err < 0)
return err;

View File

@ -21,45 +21,6 @@ static const struct hdac_bus_ops bus_ops = {
#endif
static void sof_hda_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}
static u32 sof_hda_readl(u32 __iomem *addr)
{
return readl(addr);
}
static void sof_hda_writew(u16 value, u16 __iomem *addr)
{
writew(value, addr);
}
static u16 sof_hda_readw(u16 __iomem *addr)
{
return readw(addr);
}
static void sof_hda_writeb(u8 value, u8 __iomem *addr)
{
writeb(value, addr);
}
static u8 sof_hda_readb(u8 __iomem *addr)
{
return readb(addr);
}
static const struct hdac_io_ops io_ops = {
.reg_writel = sof_hda_writel,
.reg_readl = sof_hda_readl,
.reg_writew = sof_hda_writew,
.reg_readw = sof_hda_readw,
.reg_writeb = sof_hda_writeb,
.reg_readb = sof_hda_readb,
};
/*
* This can be used for both with/without hda link support.
*/
@ -69,7 +30,6 @@ void sof_hda_bus_init(struct hdac_bus *bus, struct device *dev,
memset(bus, 0, sizeof(*bus));
bus->dev = dev;
bus->io_ops = &io_ops;
INIT_LIST_HEAD(&bus->stream_list);
bus->irq = -1;

View File

@ -356,7 +356,7 @@ static int hda_resume(struct snd_sof_dev *sdev)
/* Reset stream-to-link mapping */
list_for_each_entry(hlink, &bus->hlink_list, list)
bus->io_ops->reg_writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
hda_dsp_ctrl_misc_clock_gating(sdev, true);
#else