linux/drivers/mfd/davinci_voicecodec.c
Mark Brown 0848c94fb4 mfd: core: Push irqdomain mapping out into devices
Currently the MFD core supports remapping MFD cell interrupts using an
irqdomain but only if the MFD is being instantiated using device tree
and only if the device tree bindings use the pattern of registering IPs
in the device tree with compatible properties.  This will be actively
harmful for drivers which support non-DT platforms and use this pattern
for their DT bindings as it will mean that the core will silently change
remapping behaviour and it is also limiting for drivers which don't do
DT with this particular pattern.  There is also a potential fragility if
there are interrupts not associated with MFD cells and all the cells are
omitted from the device tree for some reason.

Instead change the code to take an IRQ domain as an optional argument,
allowing drivers to take the decision about the parent domain for their
interrupts.  The one current user of this feature is ab8500-core, it has
the domain lookup pushed out into the driver.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2012-09-15 23:22:04 +02:00

195 lines
4.9 KiB
C

/*
* DaVinci Voice Codec Core Interface for TI platforms
*
* Copyright (C) 2010 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <sound/pcm.h>
#include <linux/mfd/davinci_voicecodec.h>
u32 davinci_vc_read(struct davinci_vc *davinci_vc, int reg)
{
return __raw_readl(davinci_vc->base + reg);
}
void davinci_vc_write(struct davinci_vc *davinci_vc,
int reg, u32 val)
{
__raw_writel(val, davinci_vc->base + reg);
}
static int __init davinci_vc_probe(struct platform_device *pdev)
{
struct davinci_vc *davinci_vc;
struct resource *res, *mem;
struct mfd_cell *cell = NULL;
int ret;
davinci_vc = kzalloc(sizeof(struct davinci_vc), GFP_KERNEL);
if (!davinci_vc) {
dev_dbg(&pdev->dev,
"could not allocate memory for private data\n");
return -ENOMEM;
}
davinci_vc->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(davinci_vc->clk)) {
dev_dbg(&pdev->dev,
"could not get the clock for voice codec\n");
ret = -ENODEV;
goto fail1;
}
clk_enable(davinci_vc->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no mem resource\n");
ret = -ENODEV;
goto fail2;
}
davinci_vc->pbase = res->start;
davinci_vc->base_size = resource_size(res);
mem = request_mem_region(davinci_vc->pbase, davinci_vc->base_size,
pdev->name);
if (!mem) {
dev_err(&pdev->dev, "VCIF region already claimed\n");
ret = -EBUSY;
goto fail2;
}
davinci_vc->base = ioremap(davinci_vc->pbase, davinci_vc->base_size);
if (!davinci_vc->base) {
dev_err(&pdev->dev, "can't ioremap mem resource.\n");
ret = -ENOMEM;
goto fail3;
}
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
ret = -ENXIO;
goto fail4;
}
davinci_vc->davinci_vcif.dma_tx_channel = res->start;
davinci_vc->davinci_vcif.dma_tx_addr =
(dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_WFIFO);
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_err(&pdev->dev, "no DMA resource\n");
ret = -ENXIO;
goto fail4;
}
davinci_vc->davinci_vcif.dma_rx_channel = res->start;
davinci_vc->davinci_vcif.dma_rx_addr =
(dma_addr_t)(io_v2p(davinci_vc->base) + DAVINCI_VC_RFIFO);
davinci_vc->dev = &pdev->dev;
davinci_vc->pdev = pdev;
/* Voice codec interface client */
cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL];
cell->name = "davinci-vcif";
cell->platform_data = davinci_vc;
cell->pdata_size = sizeof(*davinci_vc);
/* Voice codec CQ93VC client */
cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL];
cell->name = "cq93vc-codec";
cell->platform_data = davinci_vc;
cell->pdata_size = sizeof(*davinci_vc);
ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells,
DAVINCI_VC_CELLS, NULL, 0, NULL);
if (ret != 0) {
dev_err(&pdev->dev, "fail to register client devices\n");
goto fail4;
}
return 0;
fail4:
iounmap(davinci_vc->base);
fail3:
release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
fail2:
clk_disable(davinci_vc->clk);
clk_put(davinci_vc->clk);
davinci_vc->clk = NULL;
fail1:
kfree(davinci_vc);
return ret;
}
static int __devexit davinci_vc_remove(struct platform_device *pdev)
{
struct davinci_vc *davinci_vc = platform_get_drvdata(pdev);
mfd_remove_devices(&pdev->dev);
iounmap(davinci_vc->base);
release_mem_region(davinci_vc->pbase, davinci_vc->base_size);
clk_disable(davinci_vc->clk);
clk_put(davinci_vc->clk);
davinci_vc->clk = NULL;
kfree(davinci_vc);
return 0;
}
static struct platform_driver davinci_vc_driver = {
.driver = {
.name = "davinci_voicecodec",
.owner = THIS_MODULE,
},
.remove = __devexit_p(davinci_vc_remove),
};
static int __init davinci_vc_init(void)
{
return platform_driver_probe(&davinci_vc_driver, davinci_vc_probe);
}
module_init(davinci_vc_init);
static void __exit davinci_vc_exit(void)
{
platform_driver_unregister(&davinci_vc_driver);
}
module_exit(davinci_vc_exit);
MODULE_AUTHOR("Miguel Aguilar");
MODULE_DESCRIPTION("Texas Instruments DaVinci Voice Codec Core Interface");
MODULE_LICENSE("GPL");