mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-10 01:12:45 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
414
sound/soc/samsung/seiren/seiren-dma.c
Normal file
414
sound/soc/samsung/seiren/seiren-dma.c
Normal file
|
@ -0,0 +1,414 @@
|
|||
/* sound/soc/samsung/seiren/seiren-dma.c
|
||||
*
|
||||
* Exynos Seiren DMA driver for Exynos5430
|
||||
*
|
||||
* Copyright (c) 2014 Samsung Electronics
|
||||
* http://www.samsungsemi.com/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/dma/dma-pl330.h>
|
||||
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/exynos.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
|
||||
#include "seiren.h"
|
||||
#include "seiren-dma.h"
|
||||
#include "../dma.h"
|
||||
|
||||
/* DMAC source/destination addr */
|
||||
#define _SA 0x400
|
||||
#define SA(n) (_SA + (n)*0x20)
|
||||
|
||||
#define _DA 0x404
|
||||
#define DA(n) (_DA + (n)*0x20)
|
||||
|
||||
static struct seiren_dma_info {
|
||||
struct platform_device *pdev;
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
#ifdef CONFIG_SND_SAMSUNG_IOMMU
|
||||
struct iommu_domain *domain;
|
||||
#endif
|
||||
struct runtime *rtd[DMA_CH_MAX];
|
||||
} sdi;
|
||||
|
||||
struct runtime {
|
||||
int ch;
|
||||
u32 peri;
|
||||
void __iomem *regs;
|
||||
void __iomem *reg_ack;
|
||||
void __iomem *reg_sa;
|
||||
void __iomem *reg_da;
|
||||
};
|
||||
|
||||
static int of_dma_match_channel(struct device_node *np, const char *name,
|
||||
int index, struct of_phandle_args *dma_spec)
|
||||
{
|
||||
const char *s;
|
||||
|
||||
if (of_property_read_string_index(np, "dma-names", index, &s))
|
||||
return -ENODEV;
|
||||
|
||||
if (strcmp(name, s))
|
||||
return -ENODEV;
|
||||
|
||||
if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
|
||||
dma_spec))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long esa_dma_request(enum dma_ch dma_ch,
|
||||
struct samsung_dma_req *param,
|
||||
struct device *dev, char *ch_name)
|
||||
{
|
||||
struct device_node *np;
|
||||
struct of_phandle_args dma_spec;
|
||||
struct runtime *rtd = NULL;
|
||||
int ch, count, n;
|
||||
|
||||
ch = esa_dma_open();
|
||||
if (ch < 0) {
|
||||
esa_err("%s: dma ch alloc fails!!!\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rtd = kzalloc(sizeof(struct runtime), GFP_KERNEL);
|
||||
if (!rtd) {
|
||||
esa_err("%s: runtime data alloc fails!!!\n", __func__);
|
||||
esa_dma_close(ch);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(&sdi.pdev->dev);
|
||||
|
||||
sdi.rtd[ch] = rtd;
|
||||
rtd->ch = ch;
|
||||
rtd->regs = esa_dma_get_mem() + (ch * DMA_PARAM_SIZE);
|
||||
rtd->reg_ack = rtd->regs + DMA_ACK;
|
||||
rtd->reg_sa = sdi.regs + SA(ch);
|
||||
rtd->reg_da = sdi.regs + DA(ch);
|
||||
memset(rtd->regs, 0, DMA_PARAM_SIZE);
|
||||
|
||||
np = dev->of_node;
|
||||
if (!np) {
|
||||
esa_err("%s: of_node not found!!!\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
count = of_property_count_strings(np, "dma-names");
|
||||
if (count < 0) {
|
||||
esa_err("%s: dma-names property missing\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (n = 0; n < count; n++) {
|
||||
if (!of_dma_match_channel(np, ch_name, n, &dma_spec)) {
|
||||
of_property_read_u32_index(np, "dmas",
|
||||
n * 2 + 1, &rtd->peri);
|
||||
esa_debug("%s: ch %d (peri %d)\n",
|
||||
__func__, rtd->ch, rtd->peri);
|
||||
writel(rtd->peri, rtd->regs + DMA_PERI);
|
||||
|
||||
return (unsigned long)ch;
|
||||
}
|
||||
}
|
||||
esa_err("%s: dmas property of %s missing\n", __func__, ch_name);
|
||||
|
||||
err:
|
||||
esa_dma_close(ch);
|
||||
kfree(rtd);
|
||||
sdi.rtd[ch] = NULL;
|
||||
pm_runtime_put_sync(&sdi.pdev->dev);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int esa_dma_release(unsigned long ch, void *param)
|
||||
{
|
||||
struct runtime *rtd = sdi.rtd[ch];
|
||||
|
||||
esa_dma_close(rtd->ch);
|
||||
kfree(rtd);
|
||||
sdi.rtd[ch] = NULL;
|
||||
pm_runtime_put_sync(&sdi.pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esa_dma_config(unsigned long ch, struct samsung_dma_config *param)
|
||||
{
|
||||
struct runtime *rtd = sdi.rtd[ch];
|
||||
void __iomem *regs = rtd->regs;
|
||||
u32 mode_1 = DMA_MODE_NOP;
|
||||
u32 src_pa = 0;
|
||||
u32 dst_pa = 0;
|
||||
|
||||
if (param->direction == DMA_MEM_TO_DEV) {
|
||||
mode_1 = DMA_MODE_MEM2DEV;
|
||||
dst_pa = param->fifo;
|
||||
} else if (param->direction == DMA_DEV_TO_MEM) {
|
||||
mode_1 = DMA_MODE_DEV2MEM;
|
||||
src_pa = param->fifo;
|
||||
}
|
||||
|
||||
writel(mode_1, regs + DMA_MODE_1);
|
||||
writel(src_pa, regs + DMA_SRC_PA);
|
||||
writel(dst_pa, regs + DMA_DST_PA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esa_dma_prepare(unsigned long ch, struct samsung_dma_prep *param)
|
||||
{
|
||||
struct runtime *rtd = sdi.rtd[ch];
|
||||
void __iomem *regs = rtd->regs;
|
||||
u32 mode_2;
|
||||
|
||||
mode_2 = (param->infiniteloop) ? DMA_MODE_LOOP : DMA_MODE_ONCE;
|
||||
writel(mode_2, regs + DMA_MODE_2);
|
||||
|
||||
if (param->direction == DMA_MEM_TO_DEV)
|
||||
writel(param->buf, regs + DMA_SRC_PA);
|
||||
else if (param->direction == DMA_DEV_TO_MEM)
|
||||
writel(param->buf, regs + DMA_DST_PA);
|
||||
|
||||
writel(param->period, regs + DMA_PERIOD);
|
||||
writel(param->len / param->period, regs + DMA_PERIOD_CNT);
|
||||
|
||||
esa_dma_set_callback(param->fp, param->fp_param, rtd->ch);
|
||||
esa_dma_send_cmd(CMD_DMA_PREPARE, rtd->ch, rtd->reg_ack);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esa_dma_trigger(unsigned long ch)
|
||||
{
|
||||
struct runtime *rtd = sdi.rtd[ch];
|
||||
|
||||
esa_dma_send_cmd(CMD_DMA_START, rtd->ch, rtd->reg_ack);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esa_dma_getposition(unsigned long ch, dma_addr_t *src, dma_addr_t *dst)
|
||||
{
|
||||
struct runtime *rtd = sdi.rtd[ch];
|
||||
|
||||
*src = readl(rtd->reg_sa);
|
||||
*dst = readl(rtd->reg_da);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esa_dma_flush(unsigned long ch)
|
||||
{
|
||||
struct runtime *rtd = sdi.rtd[ch];
|
||||
|
||||
esa_dma_send_cmd(CMD_DMA_STOP, rtd->ch, rtd->reg_ack);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct samsung_dma_ops esa_dma_ops = {
|
||||
.request = esa_dma_request,
|
||||
.release = esa_dma_release,
|
||||
.config = esa_dma_config,
|
||||
.prepare = esa_dma_prepare,
|
||||
.trigger = esa_dma_trigger,
|
||||
.started = NULL,
|
||||
.getposition = esa_dma_getposition,
|
||||
.flush = esa_dma_flush,
|
||||
.stop = esa_dma_flush,
|
||||
};
|
||||
|
||||
void *samsung_esa_dma_get_ops(void)
|
||||
{
|
||||
return &esa_dma_ops;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_esa_dma_get_ops);
|
||||
|
||||
static const char banner[] =
|
||||
KERN_INFO "Exynos Seiren ADMA driver, (c)2014 Samsung Electronics\n";
|
||||
|
||||
static int esa_dma_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
printk(banner);
|
||||
|
||||
spin_lock_init(&sdi.lock);
|
||||
|
||||
sdi.pdev = pdev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "Unable to get LPASS SFRs\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
sdi.regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (!sdi.regs) {
|
||||
dev_err(dev, "SFR ioremap failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
esa_debug("regs_base = %08X (%08X bytes)\n",
|
||||
(unsigned int)res->start, (unsigned int)resource_size(res));
|
||||
|
||||
if (np) {
|
||||
if (of_find_property(np, "samsung,lpass-subip", NULL))
|
||||
lpass_register_subip(dev, "dmac");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_SAMSUNG_IOMMU
|
||||
sdi.domain = lpass_get_iommu_domain();
|
||||
if (!sdi.domain) {
|
||||
dev_err(dev, "iommu not available\n");
|
||||
goto err;
|
||||
}
|
||||
#else
|
||||
dev_err(dev, "iommu not available\n");
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_put_sync(dev);
|
||||
#else
|
||||
esa_dma_do_resume(dev);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int esa_dma_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#ifndef CONFIG_PM_RUNTIME
|
||||
lpass_put_sync(&pdev->dev);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int esa_dma_do_suspend(struct device *dev)
|
||||
{
|
||||
lpass_put_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esa_dma_do_resume(struct device *dev)
|
||||
{
|
||||
lpass_get_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_PM_RUNTIME) && defined(CONFIG_PM_SLEEP)
|
||||
static int esa_dma_suspend(struct device *dev)
|
||||
{
|
||||
esa_debug("%s entered\n", __func__);
|
||||
|
||||
return esa_dma_do_suspend(dev);
|
||||
}
|
||||
|
||||
static int esa_dma_resume(struct device *dev)
|
||||
{
|
||||
esa_debug("%s entered\n", __func__);
|
||||
|
||||
return esa_dma_do_resume(dev);
|
||||
}
|
||||
#else
|
||||
#define esa_dma_suspend NULL
|
||||
#define esa_dma_resume NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int esa_dma_runtime_suspend(struct device *dev)
|
||||
{
|
||||
esa_debug("%s entered\n", __func__);
|
||||
|
||||
return esa_dma_do_suspend(dev);
|
||||
}
|
||||
|
||||
static int esa_dma_runtime_resume(struct device *dev)
|
||||
{
|
||||
esa_debug("%s entered\n", __func__);
|
||||
|
||||
return esa_dma_do_resume(dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct platform_device_id esa_dma_driver_ids[] = {
|
||||
{
|
||||
.name = "samsung-seiren-dma",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, esa_dma_driver_ids);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id exynos_esa_dma_match[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos5430-seiren-dma",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_esa_dma_match);
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops esa_dma_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(
|
||||
esa_dma_suspend,
|
||||
esa_dma_resume
|
||||
)
|
||||
SET_RUNTIME_PM_OPS(
|
||||
esa_dma_runtime_suspend,
|
||||
esa_dma_runtime_resume,
|
||||
NULL
|
||||
)
|
||||
};
|
||||
|
||||
static struct platform_driver esa_dma_driver = {
|
||||
.probe = esa_dma_probe,
|
||||
.remove = esa_dma_remove,
|
||||
.id_table = esa_dma_driver_ids,
|
||||
.driver = {
|
||||
.name = "samsung-seiren-dma",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(exynos_esa_dma_match),
|
||||
.pm = &esa_dma_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(esa_dma_driver);
|
||||
|
||||
MODULE_AUTHOR("Yeongman Seo <yman.seo@samsung.com>");
|
||||
MODULE_DESCRIPTION("Exynos Seiren DMA Driver");
|
||||
MODULE_LICENSE("GPL");
|
Loading…
Add table
Add a link
Reference in a new issue