diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-21 10:26:23 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-21 10:26:23 -0800 |
commit | d4371f94bc003e912d4825f5c4bdf57959857073 (patch) | |
tree | 919e196d72fc83cba8c67ee720a233671938d265 /arch/arm/mach-s3c64xx | |
parent | a547df99aad777c1807e23991fa2471693c0e4cc (diff) | |
parent | 7552f34a790069a008bd3e2ab4c0954b30c2f63b (diff) | |
download | talos-obmc-linux-d4371f94bc003e912d4825f5c4bdf57959857073.tar.gz talos-obmc-linux-d4371f94bc003e912d4825f5c4bdf57959857073.zip |
Merge tag 'sound-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"It was holiday season, so no wonder that there are little changes in
framework level, although diffstat shows quite many changes spreaded
over sound/* directories. Most of changes are cleanups, code
refactoring and fixes.
Some highlights:
- Removal of OSS sleep_on usages by Arnd
- Simplified memalloc helper codes, drop obsoleted features; now it's
built into PCM driver instead of an individual module
- Warn if PCM buffer preallocation fails, which will show page
allocation issues more clearly
- Compress offload API updates for sample rates by Vinod
- PCM glitch workaround on ctxfi emu20k1 by Sarah
- Drop cs46xx DSP blobs, using firmware loader now
- USB-audio quitks for Plantronics Gamecom 780, Creative VF0420, and
Focusrite Saffire 6
HD-audio specifics:
- Standardize Kconfigs of HD-audio codec drivers; now "make
localmodconfig" recognizes configs properly (finally!)
- Parallel PM implementation by Mengdong
- BayleyBay/ValleyView2 board fixups
- Broadwell audio support
- Runtime PM improvement (PantherPoint, etc)
- Quirks: Dell subwooer, Gigabyte mobo jack detection oddity, Dell
AiO click noise fixes, Dell headset mic fixes, etc
- Automatic bind with HDMI codec parser without generic parser
- More AD codec fixes (since 3.12 regression) including the automatic
stereo mix support
- Common Thinkpad ACPI helper for Realtek and Conexant codecs
ASoC specifics:
- Update to the generic DMA code to support deferred probe and
managed resources
- New drivers for BCM2835 (used in Raspberry Pi), Tegra with MAX98090
and Analog Devices AXI I2S and S/PDIF controller IPs
- Device tree support for the simple card, max98090 and cs42l52
- Conversion of the Samsung drivers to native dmaengine, making them
multiplatform compatible and hopefully helping keep them more
modern and up to date.
- More regmap conversions, including a very welcome one for twl6040
from Peter Ujfalusi
- A big overhaul of the DaVinci drivers also from Peter Ujfalusi
- Lots of DMA updates from Lars-Peter
- Improvements to the constraints handling code from Lars-Peter
- A very helpful conversion of the TWL4030 driver to regmap from Peter
- A new driver for the Freescale ESAI controller from Nicolin Chen
- Conversion of some of the drivers to use params_width()
- Extensions to DPCM for use with compressed audio from Liam"
* tag 'sound-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (396 commits)
ASoC: dapm: Fix double prefix addition
ASoC: compress: Add suport for DPCM into compressed audio
ASoC: DPCM: make some DPCM API calls non static for compressed usage
ASoC: core: Fix possible NULL pointer dereference of pcm->config
ALSA: hda - add headset mic detect quirks for some Dell machines
ASoC: tlv320aic32x4: Fix regmap range_min
ASoC: core: Return -ENOTSUPP from set_sysclk() if no operation provided
ASoC: dapm: Change prototype of soc_widget_read
ASoC: samsung: Remove SND_DMAENGINE_PCM_FLAG_NO_RESIDUE flag
ASoC: axi-{spdif,i2s}: Remove SND_DMAENGINE_PCM_FLAG_NO_RESIDUE flag
ASoC: generic-dmaengine-pcm: Check DMA residue granularity
ASoC: generic-dmaengine-pcm: Check NO_RESIDUE flag at runtime
dma: pl330: Set residue_granularity
dma: Indicate residue granularity in dma_slave_caps
ASoC: simple-card: fix one bug to writing to the platform data
ASoC: pcm: Use snd_pcm_rate_mask_intersect() helper
ALSA: Add helper function for intersecting two rate masks
ASoC: s6000: Don't mix SNDRV_PCM_RATE_CONTINUOUS with specific rates
ASoC: fsl: Don't mix SNDRV_PCM_RATE_CONTINUOUS with specific rates
ASoC: pcm: Properly initialize hw->rate_max
...
Diffstat (limited to 'arch/arm/mach-s3c64xx')
-rw-r--r-- | arch/arm/mach-s3c64xx/Kconfig | 7 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/common.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/dma.c | 762 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/include/mach/dma.h | 144 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/pl080.c | 244 |
6 files changed, 296 insertions, 868 deletions
diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig index 175f01501129..64f04e6f9c31 100644 --- a/arch/arm/mach-s3c64xx/Kconfig +++ b/arch/arm/mach-s3c64xx/Kconfig @@ -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 diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile index 6faedcffce04..58069a702a43 100644 --- a/arch/arm/mach-s3c64xx/Makefile +++ b/arch/arm/mach-s3c64xx/Makefile @@ -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 diff --git a/arch/arm/mach-s3c64xx/common.h b/arch/arm/mach-s3c64xx/common.h index bd3bd562011e..7043e7a3a67e 100644 --- a/arch/arm/mach-s3c64xx/common.h +++ b/arch/arm/mach-s3c64xx/common.h @@ -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 */ diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c deleted file mode 100644 index 7e22c2113816..000000000000 --- a/arch/arm/mach-s3c64xx/dma.c +++ /dev/null @@ -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); diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h index fe1a98cf0e4c..059b1fc85037 100644 --- a/arch/arm/mach-s3c64xx/include/mach/dma.h +++ b/arch/arm/mach-s3c64xx/include/mach/dma.h @@ -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)) + +/* 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") -/* see mach-s3c2410/dma.h for notes on dma channel numbers */ - -/* 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 */ diff --git a/arch/arm/mach-s3c64xx/pl080.c b/arch/arm/mach-s3c64xx/pl080.c new file mode 100644 index 000000000000..901a984bddc2 --- /dev/null +++ b/arch/arm/mach-s3c64xx/pl080.c @@ -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); |