Merge branch 'topic/samsung' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into asoc-dma
This commit is contained in:
Коммит
8e6714ac60
|
@ -723,6 +723,7 @@ config ARCH_S3C64XX
|
|||
bool "Samsung S3C64XX"
|
||||
select ARCH_HAS_CPUFREQ
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select ARM_AMBA
|
||||
select ARM_VIC
|
||||
select CLKDEV_LOOKUP
|
||||
select CLKSRC_SAMSUNG_PWM
|
||||
|
|
|
@ -17,9 +17,10 @@ config CPU_S3C6410
|
|||
help
|
||||
Enable S3C6410 CPU support
|
||||
|
||||
config S3C64XX_DMA
|
||||
bool "S3C64XX DMA"
|
||||
select S3C_DMA
|
||||
config S3C64XX_PL080
|
||||
bool "S3C64XX DMA using generic PL08x driver"
|
||||
select AMBA_PL08X
|
||||
select SAMSUNG_DMADEV
|
||||
|
||||
config S3C64XX_SETUP_SDHCI
|
||||
bool
|
||||
|
|
|
@ -26,7 +26,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o
|
|||
|
||||
# DMA support
|
||||
|
||||
obj-$(CONFIG_S3C64XX_DMA) += dma.o
|
||||
obj-$(CONFIG_S3C64XX_PL080) += pl080.o
|
||||
|
||||
# Device support
|
||||
|
||||
|
|
|
@ -58,4 +58,9 @@ int __init s3c64xx_pm_late_initcall(void);
|
|||
static inline int s3c64xx_pm_late_initcall(void) { return 0; }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_S3C64XX_PL080
|
||||
extern struct pl08x_platform_data s3c64xx_dma0_plat_data;
|
||||
extern struct pl08x_platform_data s3c64xx_dma1_plat_data;
|
||||
#endif
|
||||
|
||||
#endif /* __ARCH_ARM_MACH_S3C64XX_COMMON_H */
|
||||
|
|
|
@ -1,762 +0,0 @@
|
|||
/* linux/arch/arm/plat-s3c64xx/dma.c
|
||||
*
|
||||
* Copyright 2009 Openmoko, Inc.
|
||||
* Copyright 2009 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
* http://armlinux.simtec.co.uk/
|
||||
*
|
||||
* S3C64XX DMA core
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* NOTE: Code in this file is not used when booting with Device Tree support.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/amba/pl080.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <mach/dma.h>
|
||||
#include <mach/map.h>
|
||||
#include <mach/irqs.h>
|
||||
|
||||
#include "regs-sys.h"
|
||||
|
||||
/* dma channel state information */
|
||||
|
||||
struct s3c64xx_dmac {
|
||||
struct device dev;
|
||||
struct clk *clk;
|
||||
void __iomem *regs;
|
||||
struct s3c2410_dma_chan *channels;
|
||||
enum dma_ch chanbase;
|
||||
};
|
||||
|
||||
/* pool to provide LLI buffers */
|
||||
static struct dma_pool *dma_pool;
|
||||
|
||||
/* Debug configuration and code */
|
||||
|
||||
static unsigned char debug_show_buffs = 0;
|
||||
|
||||
static void dbg_showchan(struct s3c2410_dma_chan *chan)
|
||||
{
|
||||
pr_debug("DMA%d: %08x->%08x L %08x C %08x,%08x S %08x\n",
|
||||
chan->number,
|
||||
readl(chan->regs + PL080_CH_SRC_ADDR),
|
||||
readl(chan->regs + PL080_CH_DST_ADDR),
|
||||
readl(chan->regs + PL080_CH_LLI),
|
||||
readl(chan->regs + PL080_CH_CONTROL),
|
||||
readl(chan->regs + PL080S_CH_CONTROL2),
|
||||
readl(chan->regs + PL080S_CH_CONFIG));
|
||||
}
|
||||
|
||||
static void show_lli(struct pl080s_lli *lli)
|
||||
{
|
||||
pr_debug("LLI[%p] %08x->%08x, NL %08x C %08x,%08x\n",
|
||||
lli, lli->src_addr, lli->dst_addr, lli->next_lli,
|
||||
lli->control0, lli->control1);
|
||||
}
|
||||
|
||||
static void dbg_showbuffs(struct s3c2410_dma_chan *chan)
|
||||
{
|
||||
struct s3c64xx_dma_buff *ptr;
|
||||
struct s3c64xx_dma_buff *end;
|
||||
|
||||
pr_debug("DMA%d: buffs next %p, curr %p, end %p\n",
|
||||
chan->number, chan->next, chan->curr, chan->end);
|
||||
|
||||
ptr = chan->next;
|
||||
end = chan->end;
|
||||
|
||||
if (debug_show_buffs) {
|
||||
for (; ptr != NULL; ptr = ptr->next) {
|
||||
pr_debug("DMA%d: %08x ",
|
||||
chan->number, ptr->lli_dma);
|
||||
show_lli(ptr->lli);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* End of Debug */
|
||||
|
||||
static struct s3c2410_dma_chan *s3c64xx_dma_map_channel(unsigned int channel)
|
||||
{
|
||||
struct s3c2410_dma_chan *chan;
|
||||
unsigned int start, offs;
|
||||
|
||||
start = 0;
|
||||
|
||||
if (channel >= DMACH_PCM1_TX)
|
||||
start = 8;
|
||||
|
||||
for (offs = 0; offs < 8; offs++) {
|
||||
chan = &s3c2410_chans[start + offs];
|
||||
if (!chan->in_use)
|
||||
goto found;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
s3c_dma_chan_map[channel] = chan;
|
||||
return chan;
|
||||
}
|
||||
|
||||
int s3c2410_dma_config(enum dma_ch channel, int xferunit)
|
||||
{
|
||||
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
||||
|
||||
if (chan == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
switch (xferunit) {
|
||||
case 1:
|
||||
chan->hw_width = 0;
|
||||
break;
|
||||
case 2:
|
||||
chan->hw_width = 1;
|
||||
break;
|
||||
case 4:
|
||||
chan->hw_width = 2;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: illegal width %d\n", __func__, xferunit);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(s3c2410_dma_config);
|
||||
|
||||
static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan,
|
||||
struct pl080s_lli *lli,
|
||||
dma_addr_t data, int size)
|
||||
{
|
||||
dma_addr_t src, dst;
|
||||
u32 control0, control1;
|
||||
|
||||
switch (chan->source) {
|
||||
case DMA_FROM_DEVICE:
|
||||
src = chan->dev_addr;
|
||||
dst = data;
|
||||
control0 = PL080_CONTROL_SRC_AHB2;
|
||||
control0 |= PL080_CONTROL_DST_INCR;
|
||||
break;
|
||||
|
||||
case DMA_TO_DEVICE:
|
||||
src = data;
|
||||
dst = chan->dev_addr;
|
||||
control0 = PL080_CONTROL_DST_AHB2;
|
||||
control0 |= PL080_CONTROL_SRC_INCR;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
/* note, we do not currently setup any of the burst controls */
|
||||
|
||||
control1 = size >> chan->hw_width; /* size in no of xfers */
|
||||
control0 |= PL080_CONTROL_PROT_SYS; /* always in priv. mode */
|
||||
control0 |= PL080_CONTROL_TC_IRQ_EN; /* always fire IRQ */
|
||||
control0 |= (u32)chan->hw_width << PL080_CONTROL_DWIDTH_SHIFT;
|
||||
control0 |= (u32)chan->hw_width << PL080_CONTROL_SWIDTH_SHIFT;
|
||||
|
||||
lli->src_addr = src;
|
||||
lli->dst_addr = dst;
|
||||
lli->next_lli = 0;
|
||||
lli->control0 = control0;
|
||||
lli->control1 = control1;
|
||||
}
|
||||
|
||||
static void s3c64xx_lli_to_regs(struct s3c2410_dma_chan *chan,
|
||||
struct pl080s_lli *lli)
|
||||
{
|
||||
void __iomem *regs = chan->regs;
|
||||
|
||||
pr_debug("%s: LLI %p => regs\n", __func__, lli);
|
||||
show_lli(lli);
|
||||
|
||||
writel(lli->src_addr, regs + PL080_CH_SRC_ADDR);
|
||||
writel(lli->dst_addr, regs + PL080_CH_DST_ADDR);
|
||||
writel(lli->next_lli, regs + PL080_CH_LLI);
|
||||
writel(lli->control0, regs + PL080_CH_CONTROL);
|
||||
writel(lli->control1, regs + PL080S_CH_CONTROL2);
|
||||
}
|
||||
|
||||
static int s3c64xx_dma_start(struct s3c2410_dma_chan *chan)
|
||||
{
|
||||
struct s3c64xx_dmac *dmac = chan->dmac;
|
||||
u32 config;
|
||||
u32 bit = chan->bit;
|
||||
|
||||
dbg_showchan(chan);
|
||||
|
||||
pr_debug("%s: clearing interrupts\n", __func__);
|
||||
|
||||
/* clear interrupts */
|
||||
writel(bit, dmac->regs + PL080_TC_CLEAR);
|
||||
writel(bit, dmac->regs + PL080_ERR_CLEAR);
|
||||
|
||||
pr_debug("%s: starting channel\n", __func__);
|
||||
|
||||
config = readl(chan->regs + PL080S_CH_CONFIG);
|
||||
config |= PL080_CONFIG_ENABLE;
|
||||
config &= ~PL080_CONFIG_HALT;
|
||||
|
||||
pr_debug("%s: writing config %08x\n", __func__, config);
|
||||
writel(config, chan->regs + PL080S_CH_CONFIG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c64xx_dma_stop(struct s3c2410_dma_chan *chan)
|
||||
{
|
||||
u32 config;
|
||||
int timeout;
|
||||
|
||||
pr_debug("%s: stopping channel\n", __func__);
|
||||
|
||||
dbg_showchan(chan);
|
||||
|
||||
config = readl(chan->regs + PL080S_CH_CONFIG);
|
||||
config |= PL080_CONFIG_HALT;
|
||||
writel(config, chan->regs + PL080S_CH_CONFIG);
|
||||
|
||||
timeout = 1000;
|
||||
do {
|
||||
config = readl(chan->regs + PL080S_CH_CONFIG);
|
||||
pr_debug("%s: %d - config %08x\n", __func__, timeout, config);
|
||||
if (config & PL080_CONFIG_ACTIVE)
|
||||
udelay(10);
|
||||
else
|
||||
break;
|
||||
} while (--timeout > 0);
|
||||
|
||||
if (config & PL080_CONFIG_ACTIVE) {
|
||||
printk(KERN_ERR "%s: channel still active\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
config = readl(chan->regs + PL080S_CH_CONFIG);
|
||||
config &= ~PL080_CONFIG_ENABLE;
|
||||
writel(config, chan->regs + PL080S_CH_CONFIG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void s3c64xx_dma_bufffdone(struct s3c2410_dma_chan *chan,
|
||||
struct s3c64xx_dma_buff *buf,
|
||||
enum s3c2410_dma_buffresult result)
|
||||
{
|
||||
if (chan->callback_fn != NULL)
|
||||
(chan->callback_fn)(chan, buf->pw, 0, result);
|
||||
}
|
||||
|
||||
static void s3c64xx_dma_freebuff(struct s3c64xx_dma_buff *buff)
|
||||
{
|
||||
dma_pool_free(dma_pool, buff->lli, buff->lli_dma);
|
||||
kfree(buff);
|
||||
}
|
||||
|
||||
static int s3c64xx_dma_flush(struct s3c2410_dma_chan *chan)
|
||||
{
|
||||
struct s3c64xx_dma_buff *buff, *next;
|
||||
u32 config;
|
||||
|
||||
dbg_showchan(chan);
|
||||
|
||||
pr_debug("%s: flushing channel\n", __func__);
|
||||
|
||||
config = readl(chan->regs + PL080S_CH_CONFIG);
|
||||
config &= ~PL080_CONFIG_ENABLE;
|
||||
writel(config, chan->regs + PL080S_CH_CONFIG);
|
||||
|
||||
/* dump all the buffers associated with this channel */
|
||||
|
||||
for (buff = chan->curr; buff != NULL; buff = next) {
|
||||
next = buff->next;
|
||||
pr_debug("%s: buff %p (next %p)\n", __func__, buff, buff->next);
|
||||
|
||||
s3c64xx_dma_bufffdone(chan, buff, S3C2410_RES_ABORT);
|
||||
s3c64xx_dma_freebuff(buff);
|
||||
}
|
||||
|
||||
chan->curr = chan->next = chan->end = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int s3c2410_dma_ctrl(enum dma_ch channel, enum s3c2410_chan_op op)
|
||||
{
|
||||
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
||||
|
||||
WARN_ON(!chan);
|
||||
if (!chan)
|
||||
return -EINVAL;
|
||||
|
||||
switch (op) {
|
||||
case S3C2410_DMAOP_START:
|
||||
return s3c64xx_dma_start(chan);
|
||||
|
||||
case S3C2410_DMAOP_STOP:
|
||||
return s3c64xx_dma_stop(chan);
|
||||
|
||||
case S3C2410_DMAOP_FLUSH:
|
||||
return s3c64xx_dma_flush(chan);
|
||||
|
||||
/* believe PAUSE/RESUME are no-ops */
|
||||
case S3C2410_DMAOP_PAUSE:
|
||||
case S3C2410_DMAOP_RESUME:
|
||||
case S3C2410_DMAOP_STARTED:
|
||||
case S3C2410_DMAOP_TIMEOUT:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
EXPORT_SYMBOL(s3c2410_dma_ctrl);
|
||||
|
||||
/* s3c2410_dma_enque
|
||||
*
|
||||
*/
|
||||
|
||||
int s3c2410_dma_enqueue(enum dma_ch channel, void *id,
|
||||
dma_addr_t data, int size)
|
||||
{
|
||||
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
||||
struct s3c64xx_dma_buff *next;
|
||||
struct s3c64xx_dma_buff *buff;
|
||||
struct pl080s_lli *lli;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
WARN_ON(!chan);
|
||||
if (!chan)
|
||||
return -EINVAL;
|
||||
|
||||
buff = kzalloc(sizeof(struct s3c64xx_dma_buff), GFP_ATOMIC);
|
||||
if (!buff) {
|
||||
printk(KERN_ERR "%s: no memory for buffer\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
lli = dma_pool_alloc(dma_pool, GFP_ATOMIC, &buff->lli_dma);
|
||||
if (!lli) {
|
||||
printk(KERN_ERR "%s: no memory for lli\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto err_buff;
|
||||
}
|
||||
|
||||
pr_debug("%s: buff %p, dp %08x lli (%p, %08x) %d\n",
|
||||
__func__, buff, data, lli, (u32)buff->lli_dma, size);
|
||||
|
||||
buff->lli = lli;
|
||||
buff->pw = id;
|
||||
|
||||
s3c64xx_dma_fill_lli(chan, lli, data, size);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if ((next = chan->next) != NULL) {
|
||||
struct s3c64xx_dma_buff *end = chan->end;
|
||||
struct pl080s_lli *endlli = end->lli;
|
||||
|
||||
pr_debug("enquing onto channel\n");
|
||||
|
||||
end->next = buff;
|
||||
endlli->next_lli = buff->lli_dma;
|
||||
|
||||
if (chan->flags & S3C2410_DMAF_CIRCULAR) {
|
||||
struct s3c64xx_dma_buff *curr = chan->curr;
|
||||
lli->next_lli = curr->lli_dma;
|
||||
}
|
||||
|
||||
if (next == chan->curr) {
|
||||
writel(buff->lli_dma, chan->regs + PL080_CH_LLI);
|
||||
chan->next = buff;
|
||||
}
|
||||
|
||||
show_lli(endlli);
|
||||
chan->end = buff;
|
||||
} else {
|
||||
pr_debug("enquing onto empty channel\n");
|
||||
|
||||
chan->curr = buff;
|
||||
chan->next = buff;
|
||||
chan->end = buff;
|
||||
|
||||
s3c64xx_lli_to_regs(chan, lli);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
show_lli(lli);
|
||||
|
||||
dbg_showchan(chan);
|
||||
dbg_showbuffs(chan);
|
||||
return 0;
|
||||
|
||||
err_buff:
|
||||
kfree(buff);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(s3c2410_dma_enqueue);
|
||||
|
||||
|
||||
int s3c2410_dma_devconfig(enum dma_ch channel,
|
||||
enum dma_data_direction source,
|
||||
unsigned long devaddr)
|
||||
{
|
||||
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
||||
u32 peripheral;
|
||||
u32 config = 0;
|
||||
|
||||
pr_debug("%s: channel %d, source %d, dev %08lx, chan %p\n",
|
||||
__func__, channel, source, devaddr, chan);
|
||||
|
||||
WARN_ON(!chan);
|
||||
if (!chan)
|
||||
return -EINVAL;
|
||||
|
||||
peripheral = (chan->peripheral & 0xf);
|
||||
chan->source = source;
|
||||
chan->dev_addr = devaddr;
|
||||
|
||||
pr_debug("%s: peripheral %d\n", __func__, peripheral);
|
||||
|
||||
switch (source) {
|
||||
case DMA_FROM_DEVICE:
|
||||
config = 2 << PL080_CONFIG_FLOW_CONTROL_SHIFT;
|
||||
config |= peripheral << PL080_CONFIG_SRC_SEL_SHIFT;
|
||||
break;
|
||||
case DMA_TO_DEVICE:
|
||||
config = 1 << PL080_CONFIG_FLOW_CONTROL_SHIFT;
|
||||
config |= peripheral << PL080_CONFIG_DST_SEL_SHIFT;
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s: bad source\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* allow TC and ERR interrupts */
|
||||
config |= PL080_CONFIG_TC_IRQ_MASK;
|
||||
config |= PL080_CONFIG_ERR_IRQ_MASK;
|
||||
|
||||
pr_debug("%s: config %08x\n", __func__, config);
|
||||
|
||||
writel(config, chan->regs + PL080S_CH_CONFIG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(s3c2410_dma_devconfig);
|
||||
|
||||
|
||||
int s3c2410_dma_getposition(enum dma_ch channel,
|
||||
dma_addr_t *src, dma_addr_t *dst)
|
||||
{
|
||||
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
||||
|
||||
WARN_ON(!chan);
|
||||
if (!chan)
|
||||
return -EINVAL;
|
||||
|
||||
if (src != NULL)
|
||||
*src = readl(chan->regs + PL080_CH_SRC_ADDR);
|
||||
|
||||
if (dst != NULL)
|
||||
*dst = readl(chan->regs + PL080_CH_DST_ADDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(s3c2410_dma_getposition);
|
||||
|
||||
/* s3c2410_request_dma
|
||||
*
|
||||
* get control of an dma channel
|
||||
*/
|
||||
|
||||
int s3c2410_dma_request(enum dma_ch channel,
|
||||
struct s3c2410_dma_client *client,
|
||||
void *dev)
|
||||
{
|
||||
struct s3c2410_dma_chan *chan;
|
||||
unsigned long flags;
|
||||
|
||||
pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",
|
||||
channel, client->name, dev);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
chan = s3c64xx_dma_map_channel(channel);
|
||||
if (chan == NULL) {
|
||||
local_irq_restore(flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
dbg_showchan(chan);
|
||||
|
||||
chan->client = client;
|
||||
chan->in_use = 1;
|
||||
chan->peripheral = channel;
|
||||
chan->flags = 0;
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
/* need to setup */
|
||||
|
||||
pr_debug("%s: channel initialised, %p\n", __func__, chan);
|
||||
|
||||
return chan->number | DMACH_LOW_LEVEL;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(s3c2410_dma_request);
|
||||
|
||||
/* s3c2410_dma_free
|
||||
*
|
||||
* release the given channel back to the system, will stop and flush
|
||||
* any outstanding transfers, and ensure the channel is ready for the
|
||||
* next claimant.
|
||||
*
|
||||
* Note, although a warning is currently printed if the freeing client
|
||||
* info is not the same as the registrant's client info, the free is still
|
||||
* allowed to go through.
|
||||
*/
|
||||
|
||||
int s3c2410_dma_free(enum dma_ch channel, struct s3c2410_dma_client *client)
|
||||
{
|
||||
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
|
||||
unsigned long flags;
|
||||
|
||||
if (chan == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (chan->client != client) {
|
||||
printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n",
|
||||
channel, chan->client, client);
|
||||
}
|
||||
|
||||
/* sort out stopping and freeing the channel */
|
||||
|
||||
|
||||
chan->client = NULL;
|
||||
chan->in_use = 0;
|
||||
|
||||
if (!(channel & DMACH_LOW_LEVEL))
|
||||
s3c_dma_chan_map[channel] = NULL;
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(s3c2410_dma_free);
|
||||
|
||||
static irqreturn_t s3c64xx_dma_irq(int irq, void *pw)
|
||||
{
|
||||
struct s3c64xx_dmac *dmac = pw;
|
||||
struct s3c2410_dma_chan *chan;
|
||||
enum s3c2410_dma_buffresult res;
|
||||
u32 tcstat, errstat;
|
||||
u32 bit;
|
||||
int offs;
|
||||
|
||||
tcstat = readl(dmac->regs + PL080_TC_STATUS);
|
||||
errstat = readl(dmac->regs + PL080_ERR_STATUS);
|
||||
|
||||
for (offs = 0, bit = 1; offs < 8; offs++, bit <<= 1) {
|
||||
struct s3c64xx_dma_buff *buff;
|
||||
|
||||
if (!(errstat & bit) && !(tcstat & bit))
|
||||
continue;
|
||||
|
||||
chan = dmac->channels + offs;
|
||||
res = S3C2410_RES_ERR;
|
||||
|
||||
if (tcstat & bit) {
|
||||
writel(bit, dmac->regs + PL080_TC_CLEAR);
|
||||
res = S3C2410_RES_OK;
|
||||
}
|
||||
|
||||
if (errstat & bit)
|
||||
writel(bit, dmac->regs + PL080_ERR_CLEAR);
|
||||
|
||||
/* 'next' points to the buffer that is next to the
|
||||
* currently active buffer.
|
||||
* For CIRCULAR queues, 'next' will be same as 'curr'
|
||||
* when 'end' is the active buffer.
|
||||
*/
|
||||
buff = chan->curr;
|
||||
while (buff && buff != chan->next
|
||||
&& buff->next != chan->next)
|
||||
buff = buff->next;
|
||||
|
||||
if (!buff)
|
||||
BUG();
|
||||
|
||||
if (buff == chan->next)
|
||||
buff = chan->end;
|
||||
|
||||
s3c64xx_dma_bufffdone(chan, buff, res);
|
||||
|
||||
/* Free the node and update curr, if non-circular queue */
|
||||
if (!(chan->flags & S3C2410_DMAF_CIRCULAR)) {
|
||||
chan->curr = buff->next;
|
||||
s3c64xx_dma_freebuff(buff);
|
||||
}
|
||||
|
||||
/* Update 'next' */
|
||||
buff = chan->next;
|
||||
if (chan->next == chan->end) {
|
||||
chan->next = chan->curr;
|
||||
if (!(chan->flags & S3C2410_DMAF_CIRCULAR))
|
||||
chan->end = NULL;
|
||||
} else {
|
||||
chan->next = buff->next;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct bus_type dma_subsys = {
|
||||
.name = "s3c64xx-dma",
|
||||
.dev_name = "s3c64xx-dma",
|
||||
};
|
||||
|
||||
static int s3c64xx_dma_init1(int chno, enum dma_ch chbase,
|
||||
int irq, unsigned int base)
|
||||
{
|
||||
struct s3c2410_dma_chan *chptr = &s3c2410_chans[chno];
|
||||
struct s3c64xx_dmac *dmac;
|
||||
char clkname[16];
|
||||
void __iomem *regs;
|
||||
void __iomem *regptr;
|
||||
int err, ch;
|
||||
|
||||
dmac = kzalloc(sizeof(struct s3c64xx_dmac), GFP_KERNEL);
|
||||
if (!dmac) {
|
||||
printk(KERN_ERR "%s: failed to alloc mem\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dmac->dev.id = chno / 8;
|
||||
dmac->dev.bus = &dma_subsys;
|
||||
|
||||
err = device_register(&dmac->dev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: failed to register device\n", __func__);
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
regs = ioremap(base, 0x200);
|
||||
if (!regs) {
|
||||
printk(KERN_ERR "%s: failed to ioremap()\n", __func__);
|
||||
err = -ENXIO;
|
||||
goto err_dev;
|
||||
}
|
||||
|
||||
snprintf(clkname, sizeof(clkname), "dma%d", dmac->dev.id);
|
||||
|
||||
dmac->clk = clk_get(NULL, clkname);
|
||||
if (IS_ERR(dmac->clk)) {
|
||||
printk(KERN_ERR "%s: failed to get clock %s\n", __func__, clkname);
|
||||
err = PTR_ERR(dmac->clk);
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
clk_prepare_enable(dmac->clk);
|
||||
|
||||
dmac->regs = regs;
|
||||
dmac->chanbase = chbase;
|
||||
dmac->channels = chptr;
|
||||
|
||||
err = request_irq(irq, s3c64xx_dma_irq, 0, "DMA", dmac);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "%s: failed to get irq\n", __func__);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
regptr = regs + PL080_Cx_BASE(0);
|
||||
|
||||
for (ch = 0; ch < 8; ch++, chptr++) {
|
||||
pr_debug("%s: registering DMA %d (%p)\n",
|
||||
__func__, chno + ch, regptr);
|
||||
|
||||
chptr->bit = 1 << ch;
|
||||
chptr->number = chno + ch;
|
||||
chptr->dmac = dmac;
|
||||
chptr->regs = regptr;
|
||||
regptr += PL080_Cx_STRIDE;
|
||||
}
|
||||
|
||||
/* for the moment, permanently enable the controller */
|
||||
writel(PL080_CONFIG_ENABLE, regs + PL080_CONFIG);
|
||||
|
||||
printk(KERN_INFO "PL080: IRQ %d, at %p, channels %d..%d\n",
|
||||
irq, regs, chno, chno+8);
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(dmac->clk);
|
||||
clk_put(dmac->clk);
|
||||
err_map:
|
||||
iounmap(regs);
|
||||
err_dev:
|
||||
device_unregister(&dmac->dev);
|
||||
err_alloc:
|
||||
kfree(dmac);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __init s3c64xx_dma_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* This driver is not supported when booting with device tree. */
|
||||
if (of_have_populated_dt())
|
||||
return -ENODEV;
|
||||
|
||||
printk(KERN_INFO "%s: Registering DMA channels\n", __func__);
|
||||
|
||||
dma_pool = dma_pool_create("DMA-LLI", NULL, sizeof(struct pl080s_lli), 16, 0);
|
||||
if (!dma_pool) {
|
||||
printk(KERN_ERR "%s: failed to create pool\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = subsys_system_register(&dma_subsys, NULL);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "%s: failed to create subsys\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Set all DMA configuration to be DMA, not SDMA */
|
||||
writel(0xffffff, S3C64XX_SDMA_SEL);
|
||||
|
||||
/* Register standard DMA controllers */
|
||||
s3c64xx_dma_init1(0, DMACH_UART0, IRQ_DMA0, 0x75000000);
|
||||
s3c64xx_dma_init1(8, DMACH_PCM1_TX, IRQ_DMA1, 0x75100000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
arch_initcall(s3c64xx_dma_init);
|
|
@ -11,51 +11,48 @@
|
|||
#ifndef __ASM_ARCH_DMA_H
|
||||
#define __ASM_ARCH_DMA_H __FILE__
|
||||
|
||||
#define S3C_DMA_CHANNELS (16)
|
||||
#define S3C64XX_DMA_CHAN(name) ((unsigned long)(name))
|
||||
|
||||
/* see mach-s3c2410/dma.h for notes on dma channel numbers */
|
||||
/* DMA0/SDMA0 */
|
||||
#define DMACH_UART0 S3C64XX_DMA_CHAN("uart0_tx")
|
||||
#define DMACH_UART0_SRC2 S3C64XX_DMA_CHAN("uart0_rx")
|
||||
#define DMACH_UART1 S3C64XX_DMA_CHAN("uart1_tx")
|
||||
#define DMACH_UART1_SRC2 S3C64XX_DMA_CHAN("uart1_rx")
|
||||
#define DMACH_UART2 S3C64XX_DMA_CHAN("uart2_tx")
|
||||
#define DMACH_UART2_SRC2 S3C64XX_DMA_CHAN("uart2_rx")
|
||||
#define DMACH_UART3 S3C64XX_DMA_CHAN("uart3_tx")
|
||||
#define DMACH_UART3_SRC2 S3C64XX_DMA_CHAN("uart3_rx")
|
||||
#define DMACH_PCM0_TX S3C64XX_DMA_CHAN("pcm0_tx")
|
||||
#define DMACH_PCM0_RX S3C64XX_DMA_CHAN("pcm0_rx")
|
||||
#define DMACH_I2S0_OUT S3C64XX_DMA_CHAN("i2s0_tx")
|
||||
#define DMACH_I2S0_IN S3C64XX_DMA_CHAN("i2s0_rx")
|
||||
#define DMACH_SPI0_TX S3C64XX_DMA_CHAN("spi0_tx")
|
||||
#define DMACH_SPI0_RX S3C64XX_DMA_CHAN("spi0_rx")
|
||||
#define DMACH_HSI_I2SV40_TX S3C64XX_DMA_CHAN("i2s2_tx")
|
||||
#define DMACH_HSI_I2SV40_RX S3C64XX_DMA_CHAN("i2s2_rx")
|
||||
|
||||
/* DMA1/SDMA1 */
|
||||
#define DMACH_PCM1_TX S3C64XX_DMA_CHAN("pcm1_tx")
|
||||
#define DMACH_PCM1_RX S3C64XX_DMA_CHAN("pcm1_rx")
|
||||
#define DMACH_I2S1_OUT S3C64XX_DMA_CHAN("i2s1_tx")
|
||||
#define DMACH_I2S1_IN S3C64XX_DMA_CHAN("i2s1_rx")
|
||||
#define DMACH_SPI1_TX S3C64XX_DMA_CHAN("spi1_tx")
|
||||
#define DMACH_SPI1_RX S3C64XX_DMA_CHAN("spi1_rx")
|
||||
#define DMACH_AC97_PCMOUT S3C64XX_DMA_CHAN("ac97_out")
|
||||
#define DMACH_AC97_PCMIN S3C64XX_DMA_CHAN("ac97_in")
|
||||
#define DMACH_AC97_MICIN S3C64XX_DMA_CHAN("ac97_mic")
|
||||
#define DMACH_PWM S3C64XX_DMA_CHAN("pwm")
|
||||
#define DMACH_IRDA S3C64XX_DMA_CHAN("irda")
|
||||
#define DMACH_EXTERNAL S3C64XX_DMA_CHAN("external")
|
||||
#define DMACH_SECURITY_RX S3C64XX_DMA_CHAN("sec_rx")
|
||||
#define DMACH_SECURITY_TX S3C64XX_DMA_CHAN("sec_tx")
|
||||
|
||||
/* Note, for the S3C64XX architecture we keep the DMACH_
|
||||
* defines in the order they are allocated to [S]DMA0/[S]DMA1
|
||||
* so that is easy to do DHACH_ -> DMA controller conversion
|
||||
*/
|
||||
enum dma_ch {
|
||||
/* DMA0/SDMA0 */
|
||||
DMACH_UART0 = 0,
|
||||
DMACH_UART0_SRC2,
|
||||
DMACH_UART1,
|
||||
DMACH_UART1_SRC2,
|
||||
DMACH_UART2,
|
||||
DMACH_UART2_SRC2,
|
||||
DMACH_UART3,
|
||||
DMACH_UART3_SRC2,
|
||||
DMACH_PCM0_TX,
|
||||
DMACH_PCM0_RX,
|
||||
DMACH_I2S0_OUT,
|
||||
DMACH_I2S0_IN,
|
||||
DMACH_SPI0_TX,
|
||||
DMACH_SPI0_RX,
|
||||
DMACH_HSI_I2SV40_TX,
|
||||
DMACH_HSI_I2SV40_RX,
|
||||
DMACH_MAX = 32
|
||||
};
|
||||
|
||||
/* DMA1/SDMA1 */
|
||||
DMACH_PCM1_TX = 16,
|
||||
DMACH_PCM1_RX,
|
||||
DMACH_I2S1_OUT,
|
||||
DMACH_I2S1_IN,
|
||||
DMACH_SPI1_TX,
|
||||
DMACH_SPI1_RX,
|
||||
DMACH_AC97_PCMOUT,
|
||||
DMACH_AC97_PCMIN,
|
||||
DMACH_AC97_MICIN,
|
||||
DMACH_PWM,
|
||||
DMACH_IRDA,
|
||||
DMACH_EXTERNAL,
|
||||
DMACH_RES1,
|
||||
DMACH_RES2,
|
||||
DMACH_SECURITY_RX, /* SDMA1 only */
|
||||
DMACH_SECURITY_TX, /* SDMA1 only */
|
||||
DMACH_MAX /* the end */
|
||||
struct s3c2410_dma_client {
|
||||
char *name;
|
||||
};
|
||||
|
||||
static inline bool samsung_dma_has_circular(void)
|
||||
|
@ -65,67 +62,10 @@ static inline bool samsung_dma_has_circular(void)
|
|||
|
||||
static inline bool samsung_dma_is_dmadev(void)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#define S3C2410_DMAF_CIRCULAR (1 << 0)
|
||||
|
||||
#include <plat/dma.h>
|
||||
|
||||
#define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware ch no */
|
||||
|
||||
struct s3c64xx_dma_buff;
|
||||
|
||||
/** s3c64xx_dma_buff - S3C64XX DMA buffer descriptor
|
||||
* @next: Pointer to next buffer in queue or ring.
|
||||
* @pw: Client provided identifier
|
||||
* @lli: Pointer to hardware descriptor this buffer is associated with.
|
||||
* @lli_dma: Hardare address of the descriptor.
|
||||
*/
|
||||
struct s3c64xx_dma_buff {
|
||||
struct s3c64xx_dma_buff *next;
|
||||
|
||||
void *pw;
|
||||
struct pl080s_lli *lli;
|
||||
dma_addr_t lli_dma;
|
||||
};
|
||||
|
||||
struct s3c64xx_dmac;
|
||||
|
||||
struct s3c2410_dma_chan {
|
||||
unsigned char number; /* number of this dma channel */
|
||||
unsigned char in_use; /* channel allocated */
|
||||
unsigned char bit; /* bit for enable/disable/etc */
|
||||
unsigned char hw_width;
|
||||
unsigned char peripheral;
|
||||
|
||||
unsigned int flags;
|
||||
enum dma_data_direction source;
|
||||
|
||||
|
||||
dma_addr_t dev_addr;
|
||||
|
||||
struct s3c2410_dma_client *client;
|
||||
struct s3c64xx_dmac *dmac; /* pointer to controller */
|
||||
|
||||
void __iomem *regs;
|
||||
|
||||
/* cdriver callbacks */
|
||||
s3c2410_dma_cbfn_t callback_fn; /* buffer done callback */
|
||||
s3c2410_dma_opfn_t op_fn; /* channel op callback */
|
||||
|
||||
/* buffer list and information */
|
||||
struct s3c64xx_dma_buff *curr; /* current dma buffer */
|
||||
struct s3c64xx_dma_buff *next; /* next buffer to load */
|
||||
struct s3c64xx_dma_buff *end; /* end of queue */
|
||||
|
||||
/* note, when channel is running in circular mode, curr is the
|
||||
* first buffer enqueued, end is the last and curr is where the
|
||||
* last buffer-done event is set-at. The buffers are not freed
|
||||
* and the last buffer hardware descriptor points back to the
|
||||
* first.
|
||||
*/
|
||||
};
|
||||
|
||||
#include <plat/dma-core.h>
|
||||
#include <linux/amba/pl08x.h>
|
||||
#include <plat/dma-ops.h>
|
||||
|
||||
#endif /* __ASM_ARCH_IRQ_H */
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Samsung's S3C64XX generic DMA support using amba-pl08x driver.
|
||||
*
|
||||
* Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.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/kernel.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/amba/pl080.h>
|
||||
#include <linux/amba/pl08x.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <mach/irqs.h>
|
||||
#include <mach/map.h>
|
||||
|
||||
#include "regs-sys.h"
|
||||
|
||||
static int pl08x_get_xfer_signal(const struct pl08x_channel_data *cd)
|
||||
{
|
||||
return cd->min_signal;
|
||||
}
|
||||
|
||||
static void pl08x_put_xfer_signal(const struct pl08x_channel_data *cd, int ch)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* DMA0
|
||||
*/
|
||||
|
||||
static struct pl08x_channel_data s3c64xx_dma0_info[] = {
|
||||
{
|
||||
.bus_id = "uart0_tx",
|
||||
.min_signal = 0,
|
||||
.max_signal = 0,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "uart0_rx",
|
||||
.min_signal = 1,
|
||||
.max_signal = 1,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "uart1_tx",
|
||||
.min_signal = 2,
|
||||
.max_signal = 2,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "uart1_rx",
|
||||
.min_signal = 3,
|
||||
.max_signal = 3,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "uart2_tx",
|
||||
.min_signal = 4,
|
||||
.max_signal = 4,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "uart2_rx",
|
||||
.min_signal = 5,
|
||||
.max_signal = 5,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "uart3_tx",
|
||||
.min_signal = 6,
|
||||
.max_signal = 6,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "uart3_rx",
|
||||
.min_signal = 7,
|
||||
.max_signal = 7,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "pcm0_tx",
|
||||
.min_signal = 8,
|
||||
.max_signal = 8,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "pcm0_rx",
|
||||
.min_signal = 9,
|
||||
.max_signal = 9,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "i2s0_tx",
|
||||
.min_signal = 10,
|
||||
.max_signal = 10,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "i2s0_rx",
|
||||
.min_signal = 11,
|
||||
.max_signal = 11,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "spi0_tx",
|
||||
.min_signal = 12,
|
||||
.max_signal = 12,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "spi0_rx",
|
||||
.min_signal = 13,
|
||||
.max_signal = 13,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "i2s2_tx",
|
||||
.min_signal = 14,
|
||||
.max_signal = 14,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "i2s2_rx",
|
||||
.min_signal = 15,
|
||||
.max_signal = 15,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}
|
||||
};
|
||||
|
||||
struct pl08x_platform_data s3c64xx_dma0_plat_data = {
|
||||
.memcpy_channel = {
|
||||
.bus_id = "memcpy",
|
||||
.cctl_memcpy =
|
||||
(PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT |
|
||||
PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT |
|
||||
PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT |
|
||||
PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT |
|
||||
PL080_CONTROL_PROT_BUFF | PL080_CONTROL_PROT_CACHE |
|
||||
PL080_CONTROL_PROT_SYS),
|
||||
},
|
||||
.lli_buses = PL08X_AHB1,
|
||||
.mem_buses = PL08X_AHB1,
|
||||
.get_xfer_signal = pl08x_get_xfer_signal,
|
||||
.put_xfer_signal = pl08x_put_xfer_signal,
|
||||
.slave_channels = s3c64xx_dma0_info,
|
||||
.num_slave_channels = ARRAY_SIZE(s3c64xx_dma0_info),
|
||||
};
|
||||
|
||||
static AMBA_AHB_DEVICE(s3c64xx_dma0, "dma-pl080s.0", 0,
|
||||
0x75000000, {IRQ_DMA0}, &s3c64xx_dma0_plat_data);
|
||||
|
||||
/*
|
||||
* DMA1
|
||||
*/
|
||||
|
||||
static struct pl08x_channel_data s3c64xx_dma1_info[] = {
|
||||
{
|
||||
.bus_id = "pcm1_tx",
|
||||
.min_signal = 0,
|
||||
.max_signal = 0,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "pcm1_rx",
|
||||
.min_signal = 1,
|
||||
.max_signal = 1,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "i2s1_tx",
|
||||
.min_signal = 2,
|
||||
.max_signal = 2,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "i2s1_rx",
|
||||
.min_signal = 3,
|
||||
.max_signal = 3,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "spi1_tx",
|
||||
.min_signal = 4,
|
||||
.max_signal = 4,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "spi1_rx",
|
||||
.min_signal = 5,
|
||||
.max_signal = 5,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "ac97_out",
|
||||
.min_signal = 6,
|
||||
.max_signal = 6,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "ac97_in",
|
||||
.min_signal = 7,
|
||||
.max_signal = 7,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "ac97_mic",
|
||||
.min_signal = 8,
|
||||
.max_signal = 8,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "pwm",
|
||||
.min_signal = 9,
|
||||
.max_signal = 9,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "irda",
|
||||
.min_signal = 10,
|
||||
.max_signal = 10,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
}, {
|
||||
.bus_id = "external",
|
||||
.min_signal = 11,
|
||||
.max_signal = 11,
|
||||
.periph_buses = PL08X_AHB2,
|
||||
},
|
||||
};
|
||||
|
||||
struct pl08x_platform_data s3c64xx_dma1_plat_data = {
|
||||
.memcpy_channel = {
|
||||
.bus_id = "memcpy",
|
||||
.cctl_memcpy =
|
||||
(PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT |
|
||||
PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT |
|
||||
PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT |
|
||||
PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT |
|
||||
PL080_CONTROL_PROT_BUFF | PL080_CONTROL_PROT_CACHE |
|
||||
PL080_CONTROL_PROT_SYS),
|
||||
},
|
||||
.lli_buses = PL08X_AHB1,
|
||||
.mem_buses = PL08X_AHB1,
|
||||
.get_xfer_signal = pl08x_get_xfer_signal,
|
||||
.put_xfer_signal = pl08x_put_xfer_signal,
|
||||
.slave_channels = s3c64xx_dma1_info,
|
||||
.num_slave_channels = ARRAY_SIZE(s3c64xx_dma1_info),
|
||||
};
|
||||
|
||||
static AMBA_AHB_DEVICE(s3c64xx_dma1, "dma-pl080s.1", 0,
|
||||
0x75100000, {IRQ_DMA1}, &s3c64xx_dma1_plat_data);
|
||||
|
||||
static int __init s3c64xx_pl080_init(void)
|
||||
{
|
||||
/* Set all DMA configuration to be DMA, not SDMA */
|
||||
writel(0xffffff, S3C64XX_SDMA_SEL);
|
||||
|
||||
if (of_have_populated_dt())
|
||||
return 0;
|
||||
|
||||
amba_device_register(&s3c64xx_dma0_device, &iomem_resource);
|
||||
amba_device_register(&s3c64xx_dma1_device, &iomem_resource);
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(s3c64xx_pl080_init);
|
|
@ -1468,6 +1468,8 @@ void __init s3c64xx_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
|
|||
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi0_cfg_gpio;
|
||||
#if defined(CONFIG_PL330_DMA)
|
||||
pd.filter = pl330_filter;
|
||||
#elif defined(CONFIG_S3C64XX_PL080)
|
||||
pd.filter = pl08x_filter_id;
|
||||
#elif defined(CONFIG_S3C24XX_DMAC)
|
||||
pd.filter = s3c24xx_dma_filter;
|
||||
#endif
|
||||
|
@ -1509,8 +1511,10 @@ void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
|
|||
pd.num_cs = num_cs;
|
||||
pd.src_clk_nr = src_clk_nr;
|
||||
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi1_cfg_gpio;
|
||||
#ifdef CONFIG_PL330_DMA
|
||||
#if defined(CONFIG_PL330_DMA)
|
||||
pd.filter = pl330_filter;
|
||||
#elif defined(CONFIG_S3C64XX_PL080)
|
||||
pd.filter = pl08x_filter_id;
|
||||
#endif
|
||||
|
||||
s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi1);
|
||||
|
@ -1550,8 +1554,10 @@ void __init s3c64xx_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
|
|||
pd.num_cs = num_cs;
|
||||
pd.src_clk_nr = src_clk_nr;
|
||||
pd.cfg_gpio = (cfg_gpio) ? cfg_gpio : s3c64xx_spi2_cfg_gpio;
|
||||
#ifdef CONFIG_PL330_DMA
|
||||
#if defined(CONFIG_PL330_DMA)
|
||||
pd.filter = pl330_filter;
|
||||
#elif defined(CONFIG_S3C64XX_PL080)
|
||||
pd.filter = pl08x_filter_id;
|
||||
#endif
|
||||
|
||||
s3c_set_platdata(&pd, sizeof(pd), &s3c64xx_device_spi2);
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
|
||||
#include <mach/dma.h>
|
||||
|
||||
#if defined(CONFIG_PL330_DMA)
|
||||
#define dma_filter pl330_filter
|
||||
#elif defined(CONFIG_S3C64XX_PL080)
|
||||
#define dma_filter pl08x_filter_id
|
||||
#endif
|
||||
|
||||
static unsigned samsung_dmadev_request(enum dma_ch dma_ch,
|
||||
struct samsung_dma_req *param,
|
||||
struct device *dev, char *ch_name)
|
||||
|
@ -30,7 +36,7 @@ static unsigned samsung_dmadev_request(enum dma_ch dma_ch,
|
|||
if (dev->of_node)
|
||||
return (unsigned)dma_request_slave_channel(dev, ch_name);
|
||||
else
|
||||
return (unsigned)dma_request_channel(mask, pl330_filter,
|
||||
return (unsigned)dma_request_channel(mask, dma_filter,
|
||||
(void *)dma_ch);
|
||||
}
|
||||
|
||||
|
|
|
@ -331,8 +331,8 @@ static struct samsung_clock_alias s3c64xx_clock_aliases[] = {
|
|||
ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"),
|
||||
ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"),
|
||||
ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"),
|
||||
ALIAS(HCLK_DMA1, NULL, "dma1"),
|
||||
ALIAS(HCLK_DMA0, NULL, "dma0"),
|
||||
ALIAS(HCLK_DMA1, "dma-pl080s.1", "apb_pclk"),
|
||||
ALIAS(HCLK_DMA0, "dma-pl080s.0", "apb_pclk"),
|
||||
ALIAS(HCLK_CAMIF, "s3c-camif", "camif"),
|
||||
ALIAS(HCLK_LCD, "s3c-fb", "lcd"),
|
||||
ALIAS(PCLK_SPI1, "s3c6410-spi.1", "spi"),
|
||||
|
|
|
@ -395,7 +395,7 @@ config SPI_S3C24XX_FIQ
|
|||
config SPI_S3C64XX
|
||||
tristate "Samsung S3C64XX series type SPI"
|
||||
depends on PLAT_SAMSUNG
|
||||
select S3C64XX_DMA if ARCH_S3C64XX
|
||||
select S3C64XX_PL080 if ARCH_S3C64XX
|
||||
help
|
||||
SPI driver for Samsung S3C64XX and newer SoCs.
|
||||
|
||||
|
|
|
@ -1,13 +1,22 @@
|
|||
config SND_SOC_SAMSUNG
|
||||
tristate "ASoC support for Samsung"
|
||||
depends on PLAT_SAMSUNG
|
||||
select S3C64XX_DMA if ARCH_S3C64XX
|
||||
select S3C24XX_DMA if ARCH_S3C24XX
|
||||
select S3C2410_DMA if ARCH_S3C24XX
|
||||
select S3C64XX_PL080 if ARCH_S3C64XX
|
||||
select SND_S3C_DMA if !ARCH_S3C24XX
|
||||
select SND_S3C_DMA_LEGACY if ARCH_S3C24XX
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM if !ARCH_S3C24XX
|
||||
help
|
||||
Say Y or M if you want to add support for codecs attached to
|
||||
the Samsung SoCs' Audio interfaces. You will also need to
|
||||
select the audio interfaces to support below.
|
||||
|
||||
config SND_S3C_DMA
|
||||
tristate
|
||||
|
||||
config SND_S3C_DMA_LEGACY
|
||||
tristate
|
||||
|
||||
config SND_S3C24XX_I2S
|
||||
tristate
|
||||
select S3C2410_DMA
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# S3c24XX Platform Support
|
||||
snd-soc-s3c24xx-objs := dma.o
|
||||
snd-soc-s3c-dma-objs := dmaengine.o
|
||||
snd-soc-s3c-dma-legacy-objs := dma.o
|
||||
snd-soc-idma-objs := idma.o
|
||||
snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
|
||||
snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
|
||||
|
@ -9,7 +10,8 @@ snd-soc-samsung-spdif-objs := spdif.o
|
|||
snd-soc-pcm-objs := pcm.o
|
||||
snd-soc-i2s-objs := i2s.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_SAMSUNG) += snd-soc-s3c24xx.o
|
||||
obj-$(CONFIG_SND_S3C_DMA) += snd-soc-s3c-dma.o
|
||||
obj-$(CONFIG_SND_S3C_DMA_LEGACY) += snd-soc-s3c-dma-legacy.o
|
||||
obj-$(CONFIG_SND_S3C24XX_I2S) += snd-soc-s3c24xx-i2s.o
|
||||
obj-$(CONFIG_SND_SAMSUNG_AC97) += snd-soc-ac97.o
|
||||
obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
|
||||
|
|
|
@ -221,24 +221,6 @@ static struct snd_ac97_bus_ops s3c_ac97_ops = {
|
|||
.reset = s3c_ac97_cold_reset,
|
||||
};
|
||||
|
||||
static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
struct s3c_dma_params *dma_data;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dma_data = &s3c_ac97_pcm_out;
|
||||
else
|
||||
dma_data = &s3c_ac97_pcm_in;
|
||||
|
||||
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
|
@ -279,21 +261,6 @@ static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
return -ENODEV;
|
||||
else
|
||||
snd_soc_dai_set_dma_data(cpu_dai, substream, &s3c_ac97_mic_in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
|
@ -329,15 +296,27 @@ static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static const struct snd_soc_dai_ops s3c_ac97_dai_ops = {
|
||||
.hw_params = s3c_ac97_hw_params,
|
||||
.trigger = s3c_ac97_trigger,
|
||||
};
|
||||
|
||||
static const struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
|
||||
.hw_params = s3c_ac97_hw_mic_params,
|
||||
.trigger = s3c_ac97_mic_trigger,
|
||||
};
|
||||
|
||||
static int s3c_ac97_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
samsung_asoc_init_dma_data(dai, &s3c_ac97_pcm_out, &s3c_ac97_pcm_in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
samsung_asoc_init_dma_data(dai, NULL, &s3c_ac97_mic_in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_dai_driver s3c_ac97_dai[] = {
|
||||
[S3C_AC97_DAI_PCM] = {
|
||||
.name = "samsung-ac97",
|
||||
|
@ -354,6 +333,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
|
|||
.channels_max = 2,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.probe = s3c_ac97_dai_probe,
|
||||
.ops = &s3c_ac97_dai_ops,
|
||||
},
|
||||
[S3C_AC97_DAI_MIC] = {
|
||||
|
@ -365,6 +345,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
|
|||
.channels_max = 1,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
|
||||
.probe = s3c_ac97_mic_dai_probe,
|
||||
.ops = &s3c_ac97_mic_dai_ops,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -35,12 +35,6 @@ static const struct snd_pcm_hardware dma_hardware = {
|
|||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_U16_LE |
|
||||
SNDRV_PCM_FMTBIT_U8 |
|
||||
SNDRV_PCM_FMTBIT_S8,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 128*1024,
|
||||
.period_bytes_min = PAGE_SIZE,
|
||||
.period_bytes_max = PAGE_SIZE*2,
|
||||
|
@ -441,6 +435,14 @@ static struct snd_soc_platform_driver samsung_asoc_platform = {
|
|||
.pcm_free = dma_free_dma_buffers,
|
||||
};
|
||||
|
||||
void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
|
||||
struct s3c_dma_params *playback,
|
||||
struct s3c_dma_params *capture)
|
||||
{
|
||||
snd_soc_dai_init_dma_data(dai, playback, capture);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
|
||||
|
||||
int samsung_asoc_dma_platform_register(struct device *dev)
|
||||
{
|
||||
return snd_soc_register_platform(dev, &samsung_asoc_platform);
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#ifndef _S3C_AUDIO_H
|
||||
#define _S3C_AUDIO_H
|
||||
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
struct s3c_dma_params {
|
||||
struct s3c2410_dma_client *client; /* stream identifier */
|
||||
int channel; /* Channel ID */
|
||||
|
@ -20,8 +22,12 @@ struct s3c_dma_params {
|
|||
unsigned ch;
|
||||
struct samsung_dma_ops *ops;
|
||||
char *ch_name;
|
||||
struct snd_dmaengine_dai_dma_data dma_data;
|
||||
};
|
||||
|
||||
void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
|
||||
struct s3c_dma_params *playback,
|
||||
struct s3c_dma_params *capture);
|
||||
int samsung_asoc_dma_platform_register(struct device *dev);
|
||||
void samsung_asoc_dma_platform_unregister(struct device *dev);
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* dmaengine.c - Samsung dmaengine wrapper
|
||||
*
|
||||
* Author: Mark Brown <broonie@linaro.org>
|
||||
* Copyright 2013 Linaro
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/amba/pl08x.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#include "dma.h"
|
||||
|
||||
#ifdef CONFIG_ARCH_S3C64XX
|
||||
#define filter_fn pl08x_filter_id
|
||||
#else
|
||||
#define filter_fn NULL
|
||||
#endif
|
||||
|
||||
static const struct snd_dmaengine_pcm_config samsung_dmaengine_pcm_config = {
|
||||
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
|
||||
.compat_filter_fn = filter_fn,
|
||||
};
|
||||
|
||||
void samsung_asoc_init_dma_data(struct snd_soc_dai *dai,
|
||||
struct s3c_dma_params *playback,
|
||||
struct s3c_dma_params *capture)
|
||||
{
|
||||
struct snd_dmaengine_dai_dma_data *playback_data = NULL;
|
||||
struct snd_dmaengine_dai_dma_data *capture_data = NULL;
|
||||
|
||||
if (playback) {
|
||||
playback_data = &playback->dma_data;
|
||||
playback_data->filter_data = (void *)playback->channel;
|
||||
playback_data->chan_name = playback->ch_name;
|
||||
playback_data->addr = playback->dma_addr;
|
||||
playback_data->addr_width = playback->dma_size;
|
||||
}
|
||||
if (capture) {
|
||||
capture_data = &capture->dma_data;
|
||||
capture_data->filter_data = (void *)capture->channel;
|
||||
capture_data->chan_name = capture->ch_name;
|
||||
capture_data->addr = capture->dma_addr;
|
||||
capture_data->addr_width = capture->dma_size;
|
||||
}
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, playback_data, capture_data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_asoc_init_dma_data);
|
||||
|
||||
int samsung_asoc_dma_platform_register(struct device *dev)
|
||||
{
|
||||
return snd_dmaengine_pcm_register(dev, &samsung_dmaengine_pcm_config,
|
||||
SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME |
|
||||
SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
|
||||
SND_DMAENGINE_PCM_FLAG_COMPAT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
|
||||
|
||||
void samsung_asoc_dma_platform_unregister(struct device *dev)
|
||||
{
|
||||
return snd_dmaengine_pcm_unregister(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister);
|
||||
|
||||
MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
|
||||
MODULE_DESCRIPTION("Samsung dmaengine ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -702,6 +702,8 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
writel(mod, i2s->addr + I2SMOD);
|
||||
|
||||
samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
|
||||
|
||||
i2s->frmclk = params_rate(params);
|
||||
|
||||
return 0;
|
||||
|
@ -946,8 +948,11 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
|
|||
struct i2s_dai *i2s = to_info(dai);
|
||||
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
|
||||
|
||||
if (other && other->clk) /* If this is probe on secondary */
|
||||
if (other && other->clk) { /* If this is probe on secondary */
|
||||
samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback,
|
||||
NULL);
|
||||
goto probe_exit;
|
||||
}
|
||||
|
||||
i2s->addr = ioremap(i2s->base, 0x100);
|
||||
if (i2s->addr == NULL) {
|
||||
|
@ -963,7 +968,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
|
|||
}
|
||||
clk_prepare_enable(i2s->clk);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
|
||||
samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
|
||||
|
||||
if (other) {
|
||||
other->addr = i2s->addr;
|
||||
|
|
|
@ -35,14 +35,6 @@ static const struct snd_pcm_hardware idma_hardware = {
|
|||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16_LE |
|
||||
SNDRV_PCM_FMTBIT_U16_LE |
|
||||
SNDRV_PCM_FMTBIT_S24_LE |
|
||||
SNDRV_PCM_FMTBIT_U24_LE |
|
||||
SNDRV_PCM_FMTBIT_U8 |
|
||||
SNDRV_PCM_FMTBIT_S8,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = MAX_IDMA_BUFFER,
|
||||
.period_bytes_min = 128,
|
||||
.period_bytes_max = MAX_IDMA_PERIOD,
|
||||
|
|
|
@ -275,7 +275,6 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(rtd->cpu_dai);
|
||||
struct s3c_dma_params *dma_data;
|
||||
void __iomem *regs = pcm->regs;
|
||||
struct clk *clk;
|
||||
int sclk_div, sync_div;
|
||||
|
@ -284,13 +283,6 @@ static int s3c_pcm_hw_params(struct snd_pcm_substream *substream,
|
|||
|
||||
dev_dbg(pcm->dev, "Entered %s\n", __func__);
|
||||
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dma_data = pcm->dma_playback;
|
||||
else
|
||||
dma_data = pcm->dma_capture;
|
||||
|
||||
snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
|
||||
|
||||
/* Strictly check for sample size */
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S16_LE:
|
||||
|
@ -461,10 +453,20 @@ static const struct snd_soc_dai_ops s3c_pcm_dai_ops = {
|
|||
.set_fmt = s3c_pcm_set_fmt,
|
||||
};
|
||||
|
||||
static int s3c_pcm_dai_probe(struct snd_soc_dai *dai)
|
||||
{
|
||||
struct s3c_pcm_info *pcm = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
snd_soc_dai_init_dma_data(dai, pcm->dma_playback, pcm->dma_capture);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define S3C_PCM_RATES SNDRV_PCM_RATE_8000_96000
|
||||
|
||||
#define S3C_PCM_DAI_DECLARE \
|
||||
.symmetric_rates = 1, \
|
||||
.probe = s3c_pcm_dai_probe, \
|
||||
.ops = &s3c_pcm_dai_ops, \
|
||||
.playback = { \
|
||||
.channels_min = 2, \
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* arch/arm/mach-s3c2410/include/mach/regs-ac97.h
|
||||
*
|
||||
/*
|
||||
* Copyright (c) 2006 Simtec Electronics <linux@simtec.co.uk>
|
||||
* http://www.simtec.co.uk/products/SWLINUX/
|
||||
*
|
||||
|
@ -10,8 +9,8 @@
|
|||
* S3C2440 AC97 Controller
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_REGS_AC97_H
|
||||
#define __ASM_ARCH_REGS_AC97_H __FILE__
|
||||
#ifndef __SAMSUNG_REGS_AC97_H__
|
||||
#define __SAMSUNG_REGS_AC97_H__
|
||||
|
||||
#define S3C_AC97_GLBCTRL (0x00)
|
||||
|
||||
|
@ -64,4 +63,4 @@
|
|||
#define S3C_AC97_PCM_DATA (0x18)
|
||||
#define S3C_AC97_MIC_DATA (0x1C)
|
||||
|
||||
#endif /* __ASM_ARCH_REGS_AC97_H */
|
||||
#endif /* __SAMSUNG_REGS_AC97_H__ */
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* arch/arm/plat-samsung/include/plat/regs-iis.h
|
||||
*
|
||||
/*
|
||||
* Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
|
||||
* http://www.simtec.co.uk/products/SWLINUX/
|
||||
*
|
||||
|
@ -10,8 +9,8 @@
|
|||
* S3C2410 IIS register definition
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_REGS_IIS_H
|
||||
#define __ASM_ARCH_REGS_IIS_H
|
||||
#ifndef __SAMSUNG_REGS_IIS_H__
|
||||
#define __SAMSUNG_REGS_IIS_H__
|
||||
|
||||
#define S3C2410_IISCON (0x00)
|
||||
|
||||
|
@ -67,4 +66,4 @@
|
|||
|
||||
#define S3C2410_IISFIFO (0x10)
|
||||
|
||||
#endif /* __ASM_ARCH_REGS_IIS_H */
|
||||
#endif /* __SAMSUNG_REGS_IIS_H__ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче