1*8e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2dfe66a18SJeeja KP /* 3dfe66a18SJeeja KP * hdac-ext-bus.c - HD-audio extended core bus functions. 4dfe66a18SJeeja KP * 5dfe66a18SJeeja KP * Copyright (C) 2014-2015 Intel Corp 6dfe66a18SJeeja KP * Author: Jeeja KP <jeeja.kp@intel.com> 7dfe66a18SJeeja KP * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8dfe66a18SJeeja KP * 9dfe66a18SJeeja KP * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10dfe66a18SJeeja KP */ 11dfe66a18SJeeja KP 12dfe66a18SJeeja KP #include <linux/module.h> 13dfe66a18SJeeja KP #include <linux/slab.h> 1442f2bb1cSVinod Koul #include <linux/io.h> 15dfe66a18SJeeja KP #include <sound/hdaudio_ext.h> 16dfe66a18SJeeja KP 17dfe66a18SJeeja KP MODULE_DESCRIPTION("HDA extended core"); 18dfe66a18SJeeja KP MODULE_LICENSE("GPL v2"); 19dfe66a18SJeeja KP 2099463b3aSVinod Koul static void hdac_ext_writel(u32 value, u32 __iomem *addr) 2199463b3aSVinod Koul { 2299463b3aSVinod Koul writel(value, addr); 2399463b3aSVinod Koul } 2499463b3aSVinod Koul 2599463b3aSVinod Koul static u32 hdac_ext_readl(u32 __iomem *addr) 2699463b3aSVinod Koul { 2799463b3aSVinod Koul return readl(addr); 2899463b3aSVinod Koul } 2999463b3aSVinod Koul 3099463b3aSVinod Koul static void hdac_ext_writew(u16 value, u16 __iomem *addr) 3199463b3aSVinod Koul { 3299463b3aSVinod Koul writew(value, addr); 3399463b3aSVinod Koul } 3499463b3aSVinod Koul 3599463b3aSVinod Koul static u16 hdac_ext_readw(u16 __iomem *addr) 3699463b3aSVinod Koul { 3799463b3aSVinod Koul return readw(addr); 3899463b3aSVinod Koul } 3999463b3aSVinod Koul 4099463b3aSVinod Koul static void hdac_ext_writeb(u8 value, u8 __iomem *addr) 4199463b3aSVinod Koul { 4299463b3aSVinod Koul writeb(value, addr); 4399463b3aSVinod Koul } 4499463b3aSVinod Koul 4599463b3aSVinod Koul static u8 hdac_ext_readb(u8 __iomem *addr) 4699463b3aSVinod Koul { 4799463b3aSVinod Koul return readb(addr); 4899463b3aSVinod Koul } 4999463b3aSVinod Koul 5099463b3aSVinod Koul static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type, 5199463b3aSVinod Koul size_t size, struct snd_dma_buffer *buf) 5299463b3aSVinod Koul { 5399463b3aSVinod Koul return snd_dma_alloc_pages(type, bus->dev, size, buf); 5499463b3aSVinod Koul } 5599463b3aSVinod Koul 5699463b3aSVinod Koul static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) 5799463b3aSVinod Koul { 5899463b3aSVinod Koul snd_dma_free_pages(buf); 5999463b3aSVinod Koul } 6099463b3aSVinod Koul 6199463b3aSVinod Koul static const struct hdac_io_ops hdac_ext_default_io = { 6299463b3aSVinod Koul .reg_writel = hdac_ext_writel, 6399463b3aSVinod Koul .reg_readl = hdac_ext_readl, 6499463b3aSVinod Koul .reg_writew = hdac_ext_writew, 6599463b3aSVinod Koul .reg_readw = hdac_ext_readw, 6699463b3aSVinod Koul .reg_writeb = hdac_ext_writeb, 6799463b3aSVinod Koul .reg_readb = hdac_ext_readb, 6899463b3aSVinod Koul .dma_alloc_pages = hdac_ext_dma_alloc_pages, 6999463b3aSVinod Koul .dma_free_pages = hdac_ext_dma_free_pages, 7099463b3aSVinod Koul }; 7199463b3aSVinod Koul 72dfe66a18SJeeja KP /** 73dfe66a18SJeeja KP * snd_hdac_ext_bus_init - initialize a HD-audio extended bus 74dfe66a18SJeeja KP * @ebus: the pointer to extended bus object 75dfe66a18SJeeja KP * @dev: device pointer 76dfe66a18SJeeja KP * @ops: bus verb operators 7799463b3aSVinod Koul * @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use 7899463b3aSVinod Koul * default ops 79dfe66a18SJeeja KP * 80dfe66a18SJeeja KP * Returns 0 if successful, or a negative error code. 81dfe66a18SJeeja KP */ 8276f56faeSRakesh Ughreja int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev, 83dfe66a18SJeeja KP const struct hdac_bus_ops *ops, 84cb04ba33SRakesh Ughreja const struct hdac_io_ops *io_ops, 85cb04ba33SRakesh Ughreja const struct hdac_ext_bus_ops *ext_ops) 86dfe66a18SJeeja KP { 87dfe66a18SJeeja KP int ret; 88dfe66a18SJeeja KP static int idx; 89dfe66a18SJeeja KP 9099463b3aSVinod Koul /* check if io ops are provided, if not load the defaults */ 9199463b3aSVinod Koul if (io_ops == NULL) 9299463b3aSVinod Koul io_ops = &hdac_ext_default_io; 9399463b3aSVinod Koul 9476f56faeSRakesh Ughreja ret = snd_hdac_bus_init(bus, dev, ops, io_ops); 95dfe66a18SJeeja KP if (ret < 0) 96dfe66a18SJeeja KP return ret; 97dfe66a18SJeeja KP 98cb04ba33SRakesh Ughreja bus->ext_ops = ext_ops; 9976f56faeSRakesh Ughreja bus->idx = idx++; 10076f56faeSRakesh Ughreja bus->cmd_dma_state = true; 1014446085dSVinod Koul 102dfe66a18SJeeja KP return 0; 103dfe66a18SJeeja KP } 104dfe66a18SJeeja KP EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); 105dfe66a18SJeeja KP 106dfe66a18SJeeja KP /** 107dfe66a18SJeeja KP * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus 108dfe66a18SJeeja KP * @ebus: the pointer to extended bus object 109dfe66a18SJeeja KP */ 11076f56faeSRakesh Ughreja void snd_hdac_ext_bus_exit(struct hdac_bus *bus) 111dfe66a18SJeeja KP { 11276f56faeSRakesh Ughreja snd_hdac_bus_exit(bus); 11376f56faeSRakesh Ughreja WARN_ON(!list_empty(&bus->hlink_list)); 114dfe66a18SJeeja KP } 115dfe66a18SJeeja KP EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); 116dfe66a18SJeeja KP 117dfe66a18SJeeja KP static void default_release(struct device *dev) 118dfe66a18SJeeja KP { 119dfe66a18SJeeja KP snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev)); 120dfe66a18SJeeja KP } 121dfe66a18SJeeja KP 122dfe66a18SJeeja KP /** 123a512f561SVinod Koul * snd_hdac_ext_bus_device_init - initialize the HDA extended codec base device 124dfe66a18SJeeja KP * @ebus: hdac extended bus to attach to 125dfe66a18SJeeja KP * @addr: codec address 126dfe66a18SJeeja KP * 127dfe66a18SJeeja KP * Returns zero for success or a negative error code. 128dfe66a18SJeeja KP */ 1296298542fSRakesh Ughreja int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, 1306298542fSRakesh Ughreja struct hdac_device *hdev) 131dfe66a18SJeeja KP { 132dfe66a18SJeeja KP char name[15]; 133dfe66a18SJeeja KP int ret; 134dfe66a18SJeeja KP 1353787a398SRakesh Ughreja hdev->bus = bus; 136dfe66a18SJeeja KP 13776f56faeSRakesh Ughreja snprintf(name, sizeof(name), "ehdaudio%dD%d", bus->idx, addr); 138dfe66a18SJeeja KP 139dfe66a18SJeeja KP ret = snd_hdac_device_init(hdev, bus, name, addr); 140dfe66a18SJeeja KP if (ret < 0) { 141dfe66a18SJeeja KP dev_err(bus->dev, "device init failed for hdac device\n"); 142dfe66a18SJeeja KP return ret; 143dfe66a18SJeeja KP } 144dfe66a18SJeeja KP hdev->type = HDA_DEV_ASOC; 145dfe66a18SJeeja KP hdev->dev.release = default_release; 146dfe66a18SJeeja KP 147dfe66a18SJeeja KP ret = snd_hdac_device_register(hdev); 148dfe66a18SJeeja KP if (ret) { 149dfe66a18SJeeja KP dev_err(bus->dev, "failed to register hdac device\n"); 150dfe66a18SJeeja KP snd_hdac_ext_bus_device_exit(hdev); 151dfe66a18SJeeja KP return ret; 152dfe66a18SJeeja KP } 153a512f561SVinod Koul 154dfe66a18SJeeja KP return 0; 155dfe66a18SJeeja KP } 156dfe66a18SJeeja KP EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); 157dfe66a18SJeeja KP 158dfe66a18SJeeja KP /** 159dfe66a18SJeeja KP * snd_hdac_ext_bus_device_exit - clean up a HD-audio extended codec base device 160dfe66a18SJeeja KP * @hdev: hdac device to clean up 161dfe66a18SJeeja KP */ 162dfe66a18SJeeja KP void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) 163dfe66a18SJeeja KP { 164dfe66a18SJeeja KP snd_hdac_device_exit(hdev); 1653787a398SRakesh Ughreja kfree(hdev); 166dfe66a18SJeeja KP } 167dfe66a18SJeeja KP EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); 168ee2d51b3SVinod Koul 169ee2d51b3SVinod Koul /** 170ee2d51b3SVinod Koul * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices 171ee2d51b3SVinod Koul * 172ee2d51b3SVinod Koul * @ebus: HD-audio extended bus 173ee2d51b3SVinod Koul */ 17476f56faeSRakesh Ughreja void snd_hdac_ext_bus_device_remove(struct hdac_bus *bus) 175ee2d51b3SVinod Koul { 176ee2d51b3SVinod Koul struct hdac_device *codec, *__codec; 177ee2d51b3SVinod Koul /* 178ee2d51b3SVinod Koul * we need to remove all the codec devices objects created in the 179ee2d51b3SVinod Koul * snd_hdac_ext_bus_device_init 180ee2d51b3SVinod Koul */ 18176f56faeSRakesh Ughreja list_for_each_entry_safe(codec, __codec, &bus->codec_list, list) { 182ee2d51b3SVinod Koul snd_hdac_device_unregister(codec); 183ee2d51b3SVinod Koul put_device(&codec->dev); 184ee2d51b3SVinod Koul } 185ee2d51b3SVinod Koul } 186ee2d51b3SVinod Koul EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove); 187d51783c1SVinod Koul #define dev_to_hdac(dev) (container_of((dev), \ 188d51783c1SVinod Koul struct hdac_device, dev)) 189d51783c1SVinod Koul 190e1df9317SRakesh Ughreja static inline struct hdac_driver *get_hdrv(struct device *dev) 191d51783c1SVinod Koul { 192d51783c1SVinod Koul struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver); 193e1df9317SRakesh Ughreja return hdrv; 194d51783c1SVinod Koul } 195d51783c1SVinod Koul 1963787a398SRakesh Ughreja static inline struct hdac_device *get_hdev(struct device *dev) 197d51783c1SVinod Koul { 198d51783c1SVinod Koul struct hdac_device *hdev = dev_to_hdac_dev(dev); 1993787a398SRakesh Ughreja return hdev; 200d51783c1SVinod Koul } 201d51783c1SVinod Koul 202d51783c1SVinod Koul static int hda_ext_drv_probe(struct device *dev) 203d51783c1SVinod Koul { 204e1df9317SRakesh Ughreja return (get_hdrv(dev))->probe(get_hdev(dev)); 205d51783c1SVinod Koul } 206d51783c1SVinod Koul 207d51783c1SVinod Koul static int hdac_ext_drv_remove(struct device *dev) 208d51783c1SVinod Koul { 209e1df9317SRakesh Ughreja return (get_hdrv(dev))->remove(get_hdev(dev)); 210d51783c1SVinod Koul } 211d51783c1SVinod Koul 212d51783c1SVinod Koul static void hdac_ext_drv_shutdown(struct device *dev) 213d51783c1SVinod Koul { 214e1df9317SRakesh Ughreja return (get_hdrv(dev))->shutdown(get_hdev(dev)); 215d51783c1SVinod Koul } 216d51783c1SVinod Koul 217d51783c1SVinod Koul /** 218d51783c1SVinod Koul * snd_hda_ext_driver_register - register a driver for ext hda devices 219d51783c1SVinod Koul * 220d51783c1SVinod Koul * @drv: ext hda driver structure 221d51783c1SVinod Koul */ 222e1df9317SRakesh Ughreja int snd_hda_ext_driver_register(struct hdac_driver *drv) 223d51783c1SVinod Koul { 224e1df9317SRakesh Ughreja drv->type = HDA_DEV_ASOC; 225e1df9317SRakesh Ughreja drv->driver.bus = &snd_hda_bus_type; 226d51783c1SVinod Koul /* we use default match */ 227d51783c1SVinod Koul 228d51783c1SVinod Koul if (drv->probe) 229e1df9317SRakesh Ughreja drv->driver.probe = hda_ext_drv_probe; 230d51783c1SVinod Koul if (drv->remove) 231e1df9317SRakesh Ughreja drv->driver.remove = hdac_ext_drv_remove; 232d51783c1SVinod Koul if (drv->shutdown) 233e1df9317SRakesh Ughreja drv->driver.shutdown = hdac_ext_drv_shutdown; 234d51783c1SVinod Koul 235e1df9317SRakesh Ughreja return driver_register(&drv->driver); 236d51783c1SVinod Koul } 237d51783c1SVinod Koul EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); 238d51783c1SVinod Koul 239d51783c1SVinod Koul /** 240d51783c1SVinod Koul * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices 241d51783c1SVinod Koul * 242d51783c1SVinod Koul * @drv: ext hda driver structure 243d51783c1SVinod Koul */ 244e1df9317SRakesh Ughreja void snd_hda_ext_driver_unregister(struct hdac_driver *drv) 245d51783c1SVinod Koul { 246e1df9317SRakesh Ughreja driver_unregister(&drv->driver); 247d51783c1SVinod Koul } 248d51783c1SVinod Koul EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister); 249