1dfe66a18SJeeja KP /* 2dfe66a18SJeeja KP * hdac-ext-bus.c - HD-audio extended core bus functions. 3dfe66a18SJeeja KP * 4dfe66a18SJeeja KP * Copyright (C) 2014-2015 Intel Corp 5dfe66a18SJeeja KP * Author: Jeeja KP <jeeja.kp@intel.com> 6dfe66a18SJeeja KP * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7dfe66a18SJeeja KP * 8dfe66a18SJeeja KP * This program is free software; you can redistribute it and/or modify 9dfe66a18SJeeja KP * it under the terms of the GNU General Public License as published by 10dfe66a18SJeeja KP * the Free Software Foundation; version 2 of the License. 11dfe66a18SJeeja KP * 12dfe66a18SJeeja KP * This program is distributed in the hope that it will be useful, but 13dfe66a18SJeeja KP * WITHOUT ANY WARRANTY; without even the implied warranty of 14dfe66a18SJeeja KP * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15dfe66a18SJeeja KP * General Public License for more details. 16dfe66a18SJeeja KP * 17dfe66a18SJeeja KP * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18dfe66a18SJeeja KP */ 19dfe66a18SJeeja KP 20dfe66a18SJeeja KP #include <linux/module.h> 21dfe66a18SJeeja KP #include <linux/slab.h> 22dfe66a18SJeeja KP #include <sound/hdaudio_ext.h> 23dfe66a18SJeeja KP 24dfe66a18SJeeja KP MODULE_DESCRIPTION("HDA extended core"); 25dfe66a18SJeeja KP MODULE_LICENSE("GPL v2"); 26dfe66a18SJeeja KP 2799463b3aSVinod Koul static void hdac_ext_writel(u32 value, u32 __iomem *addr) 2899463b3aSVinod Koul { 2999463b3aSVinod Koul writel(value, addr); 3099463b3aSVinod Koul } 3199463b3aSVinod Koul 3299463b3aSVinod Koul static u32 hdac_ext_readl(u32 __iomem *addr) 3399463b3aSVinod Koul { 3499463b3aSVinod Koul return readl(addr); 3599463b3aSVinod Koul } 3699463b3aSVinod Koul 3799463b3aSVinod Koul static void hdac_ext_writew(u16 value, u16 __iomem *addr) 3899463b3aSVinod Koul { 3999463b3aSVinod Koul writew(value, addr); 4099463b3aSVinod Koul } 4199463b3aSVinod Koul 4299463b3aSVinod Koul static u16 hdac_ext_readw(u16 __iomem *addr) 4399463b3aSVinod Koul { 4499463b3aSVinod Koul return readw(addr); 4599463b3aSVinod Koul } 4699463b3aSVinod Koul 4799463b3aSVinod Koul static void hdac_ext_writeb(u8 value, u8 __iomem *addr) 4899463b3aSVinod Koul { 4999463b3aSVinod Koul writeb(value, addr); 5099463b3aSVinod Koul } 5199463b3aSVinod Koul 5299463b3aSVinod Koul static u8 hdac_ext_readb(u8 __iomem *addr) 5399463b3aSVinod Koul { 5499463b3aSVinod Koul return readb(addr); 5599463b3aSVinod Koul } 5699463b3aSVinod Koul 5799463b3aSVinod Koul static int hdac_ext_dma_alloc_pages(struct hdac_bus *bus, int type, 5899463b3aSVinod Koul size_t size, struct snd_dma_buffer *buf) 5999463b3aSVinod Koul { 6099463b3aSVinod Koul return snd_dma_alloc_pages(type, bus->dev, size, buf); 6199463b3aSVinod Koul } 6299463b3aSVinod Koul 6399463b3aSVinod Koul static void hdac_ext_dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) 6499463b3aSVinod Koul { 6599463b3aSVinod Koul snd_dma_free_pages(buf); 6699463b3aSVinod Koul } 6799463b3aSVinod Koul 6899463b3aSVinod Koul static const struct hdac_io_ops hdac_ext_default_io = { 6999463b3aSVinod Koul .reg_writel = hdac_ext_writel, 7099463b3aSVinod Koul .reg_readl = hdac_ext_readl, 7199463b3aSVinod Koul .reg_writew = hdac_ext_writew, 7299463b3aSVinod Koul .reg_readw = hdac_ext_readw, 7399463b3aSVinod Koul .reg_writeb = hdac_ext_writeb, 7499463b3aSVinod Koul .reg_readb = hdac_ext_readb, 7599463b3aSVinod Koul .dma_alloc_pages = hdac_ext_dma_alloc_pages, 7699463b3aSVinod Koul .dma_free_pages = hdac_ext_dma_free_pages, 7799463b3aSVinod Koul }; 7899463b3aSVinod Koul 79dfe66a18SJeeja KP /** 80dfe66a18SJeeja KP * snd_hdac_ext_bus_init - initialize a HD-audio extended bus 81dfe66a18SJeeja KP * @ebus: the pointer to extended bus object 82dfe66a18SJeeja KP * @dev: device pointer 83dfe66a18SJeeja KP * @ops: bus verb operators 8499463b3aSVinod Koul * @io_ops: lowlevel I/O operators, can be NULL. If NULL core will use 8599463b3aSVinod Koul * default ops 86dfe66a18SJeeja KP * 87dfe66a18SJeeja KP * Returns 0 if successful, or a negative error code. 88dfe66a18SJeeja KP */ 89dfe66a18SJeeja KP int snd_hdac_ext_bus_init(struct hdac_ext_bus *ebus, struct device *dev, 90dfe66a18SJeeja KP const struct hdac_bus_ops *ops, 91dfe66a18SJeeja KP const struct hdac_io_ops *io_ops) 92dfe66a18SJeeja KP { 93dfe66a18SJeeja KP int ret; 94dfe66a18SJeeja KP static int idx; 95dfe66a18SJeeja KP 9699463b3aSVinod Koul /* check if io ops are provided, if not load the defaults */ 9799463b3aSVinod Koul if (io_ops == NULL) 9899463b3aSVinod Koul io_ops = &hdac_ext_default_io; 9999463b3aSVinod Koul 100dfe66a18SJeeja KP ret = snd_hdac_bus_init(&ebus->bus, dev, ops, io_ops); 101dfe66a18SJeeja KP if (ret < 0) 102dfe66a18SJeeja KP return ret; 103dfe66a18SJeeja KP 104dfe66a18SJeeja KP INIT_LIST_HEAD(&ebus->hlink_list); 105dfe66a18SJeeja KP ebus->idx = idx++; 106dfe66a18SJeeja KP 107dfe66a18SJeeja KP return 0; 108dfe66a18SJeeja KP } 109dfe66a18SJeeja KP EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_init); 110dfe66a18SJeeja KP 111dfe66a18SJeeja KP /** 112dfe66a18SJeeja KP * snd_hdac_ext_bus_exit - clean up a HD-audio extended bus 113dfe66a18SJeeja KP * @ebus: the pointer to extended bus object 114dfe66a18SJeeja KP */ 115dfe66a18SJeeja KP void snd_hdac_ext_bus_exit(struct hdac_ext_bus *ebus) 116dfe66a18SJeeja KP { 117dfe66a18SJeeja KP snd_hdac_bus_exit(&ebus->bus); 118dfe66a18SJeeja KP WARN_ON(!list_empty(&ebus->hlink_list)); 119dfe66a18SJeeja KP } 120dfe66a18SJeeja KP EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_exit); 121dfe66a18SJeeja KP 122dfe66a18SJeeja KP static void default_release(struct device *dev) 123dfe66a18SJeeja KP { 124dfe66a18SJeeja KP snd_hdac_ext_bus_device_exit(container_of(dev, struct hdac_device, dev)); 125dfe66a18SJeeja KP } 126dfe66a18SJeeja KP 127dfe66a18SJeeja KP /** 128a512f561SVinod Koul * snd_hdac_ext_bus_device_init - initialize the HDA extended codec base device 129dfe66a18SJeeja KP * @ebus: hdac extended bus to attach to 130dfe66a18SJeeja KP * @addr: codec address 131dfe66a18SJeeja KP * 132dfe66a18SJeeja KP * Returns zero for success or a negative error code. 133dfe66a18SJeeja KP */ 134dfe66a18SJeeja KP int snd_hdac_ext_bus_device_init(struct hdac_ext_bus *ebus, int addr) 135dfe66a18SJeeja KP { 136a512f561SVinod Koul struct hdac_ext_device *edev; 137dfe66a18SJeeja KP struct hdac_device *hdev = NULL; 138dfe66a18SJeeja KP struct hdac_bus *bus = ebus_to_hbus(ebus); 139dfe66a18SJeeja KP char name[15]; 140dfe66a18SJeeja KP int ret; 141dfe66a18SJeeja KP 142a512f561SVinod Koul edev = kzalloc(sizeof(*hdev), GFP_KERNEL); 143a512f561SVinod Koul if (!edev) 144dfe66a18SJeeja KP return -ENOMEM; 145a512f561SVinod Koul hdev = &edev->hdac; 146dfe66a18SJeeja KP 147dfe66a18SJeeja KP snprintf(name, sizeof(name), "ehdaudio%dD%d", ebus->idx, addr); 148dfe66a18SJeeja KP 149dfe66a18SJeeja KP ret = snd_hdac_device_init(hdev, bus, name, addr); 150dfe66a18SJeeja KP if (ret < 0) { 151dfe66a18SJeeja KP dev_err(bus->dev, "device init failed for hdac device\n"); 152dfe66a18SJeeja KP return ret; 153dfe66a18SJeeja KP } 154dfe66a18SJeeja KP hdev->type = HDA_DEV_ASOC; 155dfe66a18SJeeja KP hdev->dev.release = default_release; 156dfe66a18SJeeja KP 157dfe66a18SJeeja KP ret = snd_hdac_device_register(hdev); 158dfe66a18SJeeja KP if (ret) { 159dfe66a18SJeeja KP dev_err(bus->dev, "failed to register hdac device\n"); 160dfe66a18SJeeja KP snd_hdac_ext_bus_device_exit(hdev); 161dfe66a18SJeeja KP return ret; 162dfe66a18SJeeja KP } 163a512f561SVinod Koul 164dfe66a18SJeeja KP return 0; 165dfe66a18SJeeja KP } 166dfe66a18SJeeja KP EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_init); 167dfe66a18SJeeja KP 168dfe66a18SJeeja KP /** 169dfe66a18SJeeja KP * snd_hdac_ext_bus_device_exit - clean up a HD-audio extended codec base device 170dfe66a18SJeeja KP * @hdev: hdac device to clean up 171dfe66a18SJeeja KP */ 172dfe66a18SJeeja KP void snd_hdac_ext_bus_device_exit(struct hdac_device *hdev) 173dfe66a18SJeeja KP { 174a512f561SVinod Koul struct hdac_ext_device *edev = to_ehdac_device(hdev); 175a512f561SVinod Koul 176dfe66a18SJeeja KP snd_hdac_device_exit(hdev); 177a512f561SVinod Koul kfree(edev); 178dfe66a18SJeeja KP } 179dfe66a18SJeeja KP EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_exit); 180ee2d51b3SVinod Koul 181ee2d51b3SVinod Koul /** 182ee2d51b3SVinod Koul * snd_hdac_ext_bus_device_remove - remove HD-audio extended codec base devices 183ee2d51b3SVinod Koul * 184ee2d51b3SVinod Koul * @ebus: HD-audio extended bus 185ee2d51b3SVinod Koul */ 186ee2d51b3SVinod Koul void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus) 187ee2d51b3SVinod Koul { 188ee2d51b3SVinod Koul struct hdac_device *codec, *__codec; 189ee2d51b3SVinod Koul /* 190ee2d51b3SVinod Koul * we need to remove all the codec devices objects created in the 191ee2d51b3SVinod Koul * snd_hdac_ext_bus_device_init 192ee2d51b3SVinod Koul */ 193ee2d51b3SVinod Koul list_for_each_entry_safe(codec, __codec, &ebus->bus.codec_list, list) { 194ee2d51b3SVinod Koul snd_hdac_device_unregister(codec); 195ee2d51b3SVinod Koul put_device(&codec->dev); 196ee2d51b3SVinod Koul } 197ee2d51b3SVinod Koul } 198ee2d51b3SVinod Koul EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_device_remove); 199*d51783c1SVinod Koul #define dev_to_hdac(dev) (container_of((dev), \ 200*d51783c1SVinod Koul struct hdac_device, dev)) 201*d51783c1SVinod Koul 202*d51783c1SVinod Koul static inline struct hdac_ext_driver *get_edrv(struct device *dev) 203*d51783c1SVinod Koul { 204*d51783c1SVinod Koul struct hdac_driver *hdrv = drv_to_hdac_driver(dev->driver); 205*d51783c1SVinod Koul struct hdac_ext_driver *edrv = to_ehdac_driver(hdrv); 206*d51783c1SVinod Koul 207*d51783c1SVinod Koul return edrv; 208*d51783c1SVinod Koul } 209*d51783c1SVinod Koul 210*d51783c1SVinod Koul static inline struct hdac_ext_device *get_edev(struct device *dev) 211*d51783c1SVinod Koul { 212*d51783c1SVinod Koul struct hdac_device *hdev = dev_to_hdac_dev(dev); 213*d51783c1SVinod Koul struct hdac_ext_device *edev = to_ehdac_device(hdev); 214*d51783c1SVinod Koul 215*d51783c1SVinod Koul return edev; 216*d51783c1SVinod Koul } 217*d51783c1SVinod Koul 218*d51783c1SVinod Koul static int hda_ext_drv_probe(struct device *dev) 219*d51783c1SVinod Koul { 220*d51783c1SVinod Koul return (get_edrv(dev))->probe(get_edev(dev)); 221*d51783c1SVinod Koul } 222*d51783c1SVinod Koul 223*d51783c1SVinod Koul static int hdac_ext_drv_remove(struct device *dev) 224*d51783c1SVinod Koul { 225*d51783c1SVinod Koul return (get_edrv(dev))->remove(get_edev(dev)); 226*d51783c1SVinod Koul } 227*d51783c1SVinod Koul 228*d51783c1SVinod Koul static void hdac_ext_drv_shutdown(struct device *dev) 229*d51783c1SVinod Koul { 230*d51783c1SVinod Koul return (get_edrv(dev))->shutdown(get_edev(dev)); 231*d51783c1SVinod Koul } 232*d51783c1SVinod Koul 233*d51783c1SVinod Koul /** 234*d51783c1SVinod Koul * snd_hda_ext_driver_register - register a driver for ext hda devices 235*d51783c1SVinod Koul * 236*d51783c1SVinod Koul * @drv: ext hda driver structure 237*d51783c1SVinod Koul */ 238*d51783c1SVinod Koul int snd_hda_ext_driver_register(struct hdac_ext_driver *drv) 239*d51783c1SVinod Koul { 240*d51783c1SVinod Koul drv->hdac.type = HDA_DEV_ASOC; 241*d51783c1SVinod Koul drv->hdac.driver.bus = &snd_hda_bus_type; 242*d51783c1SVinod Koul /* we use default match */ 243*d51783c1SVinod Koul 244*d51783c1SVinod Koul if (drv->probe) 245*d51783c1SVinod Koul drv->hdac.driver.probe = hda_ext_drv_probe; 246*d51783c1SVinod Koul if (drv->remove) 247*d51783c1SVinod Koul drv->hdac.driver.remove = hdac_ext_drv_remove; 248*d51783c1SVinod Koul if (drv->shutdown) 249*d51783c1SVinod Koul drv->hdac.driver.shutdown = hdac_ext_drv_shutdown; 250*d51783c1SVinod Koul 251*d51783c1SVinod Koul return driver_register(&drv->hdac.driver); 252*d51783c1SVinod Koul } 253*d51783c1SVinod Koul EXPORT_SYMBOL_GPL(snd_hda_ext_driver_register); 254*d51783c1SVinod Koul 255*d51783c1SVinod Koul /** 256*d51783c1SVinod Koul * snd_hda_ext_driver_unregister - unregister a driver for ext hda devices 257*d51783c1SVinod Koul * 258*d51783c1SVinod Koul * @drv: ext hda driver structure 259*d51783c1SVinod Koul */ 260*d51783c1SVinod Koul void snd_hda_ext_driver_unregister(struct hdac_ext_driver *drv) 261*d51783c1SVinod Koul { 262*d51783c1SVinod Koul driver_unregister(&drv->hdac.driver); 263*d51783c1SVinod Koul } 264*d51783c1SVinod Koul EXPORT_SYMBOL_GPL(snd_hda_ext_driver_unregister); 265