xburst: Add 3.10 support

Missing files from the previous commit.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>

SVN-Revision: 37510
v19.07.3_mercusys_ac12_duma
Lars-Peter Clausen 11 years ago
parent e7d77883c7
commit 32a83db347

@ -0,0 +1,331 @@
CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
CONFIG_ARCH_DISCARD_MEMBLOCK=y
CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
CONFIG_ARCH_REQUIRE_GPIOLIB=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
# CONFIG_ARPD is not set
# CONFIG_BACKLIGHT_ADP8860 is not set
# CONFIG_BACKLIGHT_ADP8870 is not set
CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_BACKLIGHT_GENERIC is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
# CONFIG_BACKLIGHT_PWM is not set
CONFIG_BATTERY_JZ4740=y
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_BRIDGE is not set
CONFIG_CHARGER_GPIO=y
CONFIG_CLONE_BACKWARDS=y
CONFIG_CONFIGFS_FS=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_CPU_GENERIC_DUMP_TLB=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_CPU_MIPS32=y
CONFIG_CPU_MIPS32_R1=y
CONFIG_CPU_MIPSR1=y
CONFIG_CPU_R4K_CACHE_TLB=y
CONFIG_CPU_R4K_FPU=y
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
CONFIG_CRC16=y
CONFIG_CRYPTO_CRC32C=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_HASH=y
CONFIG_CRYPTO_HASH2=y
CONFIG_CRYPTO_LZO=y
CONFIG_CRYPTO_XZ=y
CONFIG_DMADEVICES=y
CONFIG_DMA_ENGINE=y
CONFIG_DMA_JZ4740=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_DMA_VIRTUAL_CHANNELS=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_EARLY_PRINTK=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_EXT4_FS=y
CONFIG_FAT_FS=y
CONFIG_FB=y
CONFIG_FB_JZ4740=y
CONFIG_FB_SYS_COPYAREA=y
CONFIG_FB_SYS_FILLRECT=y
CONFIG_FB_SYS_IMAGEBLIT=y
CONFIG_FONTS=y
# CONFIG_FONT_10x18 is not set
CONFIG_FONT_6x11=y
# CONFIG_FONT_7x14 is not set
# CONFIG_FONT_8x16 is not set
# CONFIG_FONT_8x8 is not set
# CONFIG_FONT_ACORN_8x8 is not set
# CONFIG_FONT_MINI_4x6 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_SUN8x16 is not set
CONFIG_FORCE_MAX_ZONEORDER=12
CONFIG_FRAMEBUFFER_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FREEZER=y
CONFIG_FS_MBCACHE=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_IO=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GENERIC_SMP_IDLE_THREAD=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_DEVRES=y
# CONFIG_HAMRADIO is not set
CONFIG_HARDWARE_WATCHPOINTS=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
CONFIG_HAVE_ARCH_JUMP_LABEL=y
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
CONFIG_HAVE_CLK=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_HAVE_DEBUG_KMEMLEAK=y
CONFIG_HAVE_DMA_API_DEBUG=y
CONFIG_HAVE_DMA_ATTRS=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_HAVE_GENERIC_HARDIRQS=y
CONFIG_HAVE_IDE=y
CONFIG_HAVE_KERNEL_BZIP2=y
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_HAVE_MEMBLOCK=y
CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
CONFIG_HAVE_NET_DSA=y
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_PERF_EVENTS=y
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_HW_CONSOLE=y
CONFIG_HZ_PERIODIC=y
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_INPUT=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_KEYBOARD=y
CONFIG_INPUT_MATRIXKMAP=y
CONFIG_INPUT_MOUSE=y
CONFIG_INPUT_MOUSEDEV=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
CONFIG_INPUT_PWM_BEEPER=y
CONFIG_INPUT_UINPUT=y
CONFIG_IRQ_CPU=y
CONFIG_IRQ_DOMAIN=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_IRQ_WORK=y
CONFIG_JBD2=y
CONFIG_JZ4740_QI_LB60=y
CONFIG_KALLSYMS=y
CONFIG_KERNEL_GZIP=y
CONFIG_KEXEC=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_MATRIX=y
# CONFIG_LCD_AMS369FG06 is not set
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_ILI8960=y
# CONFIG_LCD_L4F00242T03 is not set
# CONFIG_LCD_LD9040 is not set
# CONFIG_LCD_LMS283GF05 is not set
# CONFIG_LCD_LTV350QV is not set
# CONFIG_LCD_PLATFORM is not set
# CONFIG_LCD_S6E63M0 is not set
# CONFIG_LCD_TDO24M is not set
# CONFIG_LCD_VGG2432A4 is not set
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=2
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_MACH_JZ4740=y
CONFIG_MFD_CORE=y
CONFIG_MFD_JZ4740_ADC=y
CONFIG_MIPS=y
CONFIG_MIPS_FPU_EMU=y
# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set
CONFIG_MIPS_L1_CACHE_SHIFT=5
# CONFIG_MIPS_MACHINE is not set
CONFIG_MIPS_MT_DISABLED=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK=y
# CONFIG_MMC_BLOCK_BOUNCE is not set
CONFIG_MMC_JZ4740=y
CONFIG_MMC_UNSAFE_RESUME=y
CONFIG_MODULES_USE_ELF_REL=y
# CONFIG_MOUSE_BCM5974 is not set
# CONFIG_MOUSE_CYAPA is not set
# CONFIG_MOUSE_PS2 is not set
# CONFIG_MOUSE_SERIAL is not set
# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_MTD_CFI is not set
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ECC=y
CONFIG_MTD_NAND_JZ4740=y
# CONFIG_MTD_SM_COMMON is not set
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BEB_LIMIT=20
# CONFIG_MTD_UBI_FASTMAP is not set
# CONFIG_MTD_UBI_GLUEBI is not set
CONFIG_MTD_UBI_WL_THRESHOLD=4096
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_PER_CPU_KM=y
# CONFIG_NETWORK_FILESYSTEMS is not set
# CONFIG_NET_DMA is not set
# CONFIG_NEW_LEDS is not set
CONFIG_NLS=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_CODEPAGE_1250=y
CONFIG_NLS_CODEPAGE_1251=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_CODEPAGE_737=y
CONFIG_NLS_CODEPAGE_775=y
CONFIG_NLS_CODEPAGE_850=y
CONFIG_NLS_CODEPAGE_852=y
CONFIG_NLS_CODEPAGE_855=y
CONFIG_NLS_CODEPAGE_857=y
CONFIG_NLS_CODEPAGE_860=y
CONFIG_NLS_CODEPAGE_861=y
CONFIG_NLS_CODEPAGE_862=y
CONFIG_NLS_CODEPAGE_863=y
CONFIG_NLS_CODEPAGE_864=y
CONFIG_NLS_CODEPAGE_865=y
CONFIG_NLS_CODEPAGE_866=y
CONFIG_NLS_CODEPAGE_869=y
CONFIG_NLS_CODEPAGE_874=y
CONFIG_NLS_CODEPAGE_932=y
CONFIG_NLS_CODEPAGE_936=y
CONFIG_NLS_CODEPAGE_949=y
CONFIG_NLS_CODEPAGE_950=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_ISO8859_13=y
CONFIG_NLS_ISO8859_14=y
CONFIG_NLS_ISO8859_15=y
CONFIG_NLS_ISO8859_2=y
CONFIG_NLS_ISO8859_3=y
CONFIG_NLS_ISO8859_4=y
CONFIG_NLS_ISO8859_5=y
CONFIG_NLS_ISO8859_6=y
CONFIG_NLS_ISO8859_7=y
CONFIG_NLS_ISO8859_8=y
CONFIG_NLS_ISO8859_9=y
CONFIG_NLS_KOI8_R=y
CONFIG_NLS_KOI8_U=y
CONFIG_NLS_UTF8=y
CONFIG_PAGEFLAGS_EXTENDED=y
# CONFIG_PDA_POWER is not set
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PM=y
CONFIG_PM_CLK=y
# CONFIG_PM_DEBUG is not set
CONFIG_PM_SLEEP=y
CONFIG_POWER_SUPPLY=y
CONFIG_PREEMPT=y
CONFIG_PREEMPT_COUNT=y
# CONFIG_PREEMPT_NONE is not set
CONFIG_PREEMPT_RCU=y
CONFIG_PRINTK_TIME=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_PWM=y
CONFIG_PWM_JZ4740=y
# CONFIG_RCU_BOOST is not set
CONFIG_RCU_CPU_STALL_VERBOSE=y
CONFIG_RCU_STALL_COMMON=y
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_MMIO=y
CONFIG_REGMAP_SPI=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_CMOS is not set
CONFIG_RTC_DRV_JZ4740=y
# CONFIG_SCSI_DMA is not set
CONFIG_SERIO=y
CONFIG_SERIO_LIBPS2=y
CONFIG_SND=y
CONFIG_SND_COMPRESS_OFFLOAD=y
# CONFIG_SND_EMU10K1_SEQ is not set
CONFIG_SND_JACK=y
CONFIG_SND_JZ4740_SOC=y
CONFIG_SND_JZ4740_SOC_I2S=y
CONFIG_SND_JZ4740_SOC_QI_LB60=y
# CONFIG_SND_OPL3_LIB_SEQ is not set
# CONFIG_SND_OPL4_LIB_SEQ is not set
CONFIG_SND_PCM=y
# CONFIG_SND_RAWMIDI_SEQ is not set
# CONFIG_SND_SBAWE_SEQ is not set
CONFIG_SND_SOC=y
# CONFIG_SND_SOC_ALL_CODECS is not set
CONFIG_SND_SOC_DMAENGINE_PCM=y
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y
CONFIG_SND_SOC_I2C_AND_SPI=y
CONFIG_SND_SOC_JZ4740_CODEC=y
CONFIG_SND_TIMER=y
CONFIG_SOUND=y
# CONFIG_SOUND_OSS_CORE is not set
CONFIG_SPI=y
CONFIG_SPI_BITBANG=y
CONFIG_SPI_GPIO=y
CONFIG_SPI_MASTER=y
# CONFIG_SQUASHFS is not set
# CONFIG_STAGING is not set
CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
# CONFIG_SYN_COOKIES is not set
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
CONFIG_SYS_SUPPORTS_ZBOOT=y
CONFIG_SYS_SUPPORTS_ZBOOT_UART16550=y
CONFIG_TICK_CPU_ACCOUNTING=y
# CONFIG_TINY_PREEMPT_RCU is not set
CONFIG_TREE_PREEMPT_RCU=y
CONFIG_UBIFS_FS=y
CONFIG_UBIFS_FS_ADVANCED_COMPR=y
CONFIG_UBIFS_FS_LZO=y
CONFIG_UBIFS_FS_XZ=y
CONFIG_UBIFS_FS_ZLIB=y
CONFIG_UIDGID_CONVERTED=y
CONFIG_UNINLINE_SPIN_UNLOCK=y
# CONFIG_USB_ARCH_HAS_EHCI is not set
# CONFIG_USB_ARCH_HAS_XHCI is not set
# CONFIG_USB_AUDIO is not set
CONFIG_USB_COMMON=y
CONFIG_USB_ETH=y
# CONFIG_USB_ETH_EEM is not set
# CONFIG_USB_ETH_RNDIS is not set
CONFIG_USB_GADGET=y
CONFIG_USB_JZ4740=y
CONFIG_USB_LIBCOMPOSITE=y
# CONFIG_USB_MV_U3D is not set
# CONFIG_USB_PXA27X is not set
CONFIG_USB_SUPPORT=y
CONFIG_VFAT_FS=y
# CONFIG_VGA_CONSOLE is not set
# CONFIG_VLAN_8021Q is not set
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_VT_CONSOLE_SLEEP=y
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_WATCHDOG is not set
CONFIG_ZLIB_DEFLATE=y
CONFIG_ZLIB_INFLATE=y
CONFIG_ZONE_DMA_FLAG=0

@ -0,0 +1,27 @@
From 10fd63ee59dd9de4174f92f2f292b82fe46f1546 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Tue, 15 Mar 2011 12:49:15 +0100
Subject: [PATCH 01/16] ubi: Read only the vid header instead of the whole
page
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
drivers/mtd/ubi/io.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index bf79def..bc82962 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -1019,7 +1019,7 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
p = (char *)vid_hdr - ubi->vid_hdr_shift;
read_err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
- ubi->vid_hdr_alsize);
+ UBI_VID_HDR_SIZE + ubi->vid_hdr_shift);
if (read_err && read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err))
return read_err;
--
1.7.10.4

@ -0,0 +1,50 @@
From f2b7099d3986e59aab5792cecc052e440caf2cdd Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Sat, 26 Feb 2011 15:30:07 +0100
Subject: [PATCH 02/16] NAND: Optimize NAND_ECC_HW_OOB_FIRST read
Avoid sending unnecessary READ commands to the chip.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
drivers/mtd/nand/nand_base.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index dfcd0a5..287c433 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1278,9 +1278,16 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
unsigned int max_bitflips = 0;
/* Read the OOB area first */
- chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ /* Read the OOB area first */
+ if (mtd->writesize > 512) {
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ }
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1455,7 +1462,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (realpage != chip->pagebuf || oob) {
bufpoi = aligned ? buf : chip->buffers->databuf;
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ if (chip->ecc.mode != NAND_ECC_HW_OOB_FIRST) {
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+ }
/*
* Now read the page into the buffer. Absent an error,
--
1.7.10.4

@ -0,0 +1,141 @@
From 90e325c5e16db262818bca442b00f5ac10b9c852 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Tue, 15 Mar 2011 12:33:41 +0100
Subject: [PATCH 03/16] NAND: Add support for subpage reads for
NAND_ECC_HW_OOB_FIRST
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
drivers/mtd/nand/nand_base.c | 80 ++++++++++++++++++++++++++++++++++++++++--
include/linux/mtd/nand.h | 2 +-
2 files changed, 78 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 287c433..0fd6f2e 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1118,7 +1118,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @bufpoi: buffer to store read data
*/
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page)
{
int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
@@ -1311,6 +1311,75 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
}
/**
+ * nand_read_subpage_hwecc_oob_first - [REPLACABLE] hw ecc based sub-page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen: data length
+ * @bufpoi: buffer to store read data
+ * @page: page number to read
+ *
+ * Hardware ECC for large page chips, require OOB to be read first.
+ * For this ECC mode, the write_page method is re-used from ECC_HW.
+ * These methods read/write ECC from the OOB area, unlike the
+ * ECC_HW_SYNDROME support with multiple ECC steps, follows the
+ * "infix ECC" scheme and reads/writes ECC from the data area, by
+ * overwriting the NAND manufacturer bad block markings.
+ */
+static int nand_read_subpage_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page)
+{
+ int start_step, end_step, num_steps;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p;
+ int data_col_addr;
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ int i;
+
+ /* Column address wihin the page aligned to ECC size */
+ start_step = data_offs / chip->ecc.size;
+ end_step = (data_offs + readlen - 1) / chip->ecc.size;
+ num_steps = end_step - start_step + 1;
+
+ data_col_addr = start_step * chip->ecc.size;
+
+ /* Read the OOB area first */
+ if (mtd->writesize > 512) {
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, data_col_addr, page);
+ }
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ p = bufpoi + data_col_addr;
+
+ for (i = eccbytes * start_step; num_steps; num_steps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+
+ return 0;
+}
+
+/**
* nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
* @mtd: mtd info structure
* @chip: nand chip info structure
@@ -1477,7 +1546,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
!oob)
ret = chip->ecc.read_subpage(mtd, chip,
- col, bytes, bufpoi);
+ col, bytes, bufpoi, page);
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
oob_required, page);
@@ -3475,8 +3544,13 @@ int nand_scan_tail(struct mtd_info *mtd)
"hardware ECC not possible\n");
BUG();
}
- if (!chip->ecc.read_page)
+ if (!chip->ecc.read_page) {
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
+ if (!chip->ecc.read_subpage) {
+ chip->ecc.read_subpage = nand_read_subpage_hwecc_oob_first;
+ chip->options |= NAND_SUBPAGE_READ;
+ }
+ }
case NAND_ECC_HW:
/* Use standard hwecc read page function? */
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index ab63634..ff5b62e 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -349,7 +349,7 @@ struct nand_ecc_ctrl {
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t offs, uint32_t len, uint8_t *buf);
+ uint32_t offs, uint32_t len, uint8_t *buf, int page);
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offset, uint32_t data_len,
const uint8_t *data_buf, int oob_required);
--
1.7.10.4

@ -0,0 +1,29 @@
From 3e46e1ea552c3cc5fb64dfeab22124a0e8b94495 Mon Sep 17 00:00:00 2001
From: Paul Cercueil <paul@crapouillou.net>
Date: Sun, 3 Jun 2012 12:45:23 +0200
Subject: [PATCH 04/16] MMC: JZ4740: Remove duplicated code.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
drivers/mmc/host/jz4740_mmc.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 2391c6b..5a6ac99 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -560,11 +560,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
if (cmd->data)
cmd->data->error = -EIO;
cmd->error = -EIO;
- } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR |
- JZ_MMC_STATUS_CRC_WRITE_ERROR)) {
- if (cmd->data)
- cmd->data->error = -EIO;
- cmd->error = -EIO;
}
jz4740_mmc_set_irq_enabled(host, irq_reg, false);
--
1.7.10.4

@ -0,0 +1,35 @@
From 81d112d6c0e16fd0136ce2c5f511ba81ed1f23d2 Mon Sep 17 00:00:00 2001
From: Paul Cercueil <paul@crapouillou.net>
Date: Sun, 3 Jun 2012 13:07:19 +0200
Subject: [PATCH 05/16] MMC: JZ4740: Fix handling of read errors.
For no reason, the code handling write errors was implemented while
the code handling read errors was missing.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
drivers/mmc/host/jz4740_mmc.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 5a6ac99..ffe1181 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -231,6 +231,14 @@ static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host,
host->req->cmd->error = -EIO;
data->error = -EIO;
}
+ } else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) {
+ if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) {
+ host->req->cmd->error = -ETIMEDOUT;
+ data->error = -ETIMEDOUT;
+ } else {
+ host->req->cmd->error = -EIO;
+ data->error = -EIO;
+ }
}
}
--
1.7.10.4

@ -0,0 +1,40 @@
From 46c628ae74578382ec324405dd4caafdbf3838bd Mon Sep 17 00:00:00 2001
From: Paul Cercueil <paul@crapouillou.net>
Date: Sat, 16 Jun 2012 19:36:31 +0200
Subject: [PATCH 06/16] ASoC: JZ4740: delay activation of the DAC to work
around a sound bug.
A proper fix of that bug would require a big rewrite of the driver,
which (I hope) will be done eventually.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
sound/soc/codecs/jz4740.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 5f607b3..76ff580 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -249,12 +249,15 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- mask = JZ4740_CODEC_1_VREF_DISABLE |
- JZ4740_CODEC_1_VREF_AMP_DISABLE |
- JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
+ mask = JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
value = 0;
regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+
+ msleep(500);
+ mask = JZ4740_CODEC_1_VREF_DISABLE |
+ JZ4740_CODEC_1_VREF_AMP_DISABLE;
+ regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, 0);
break;
case SND_SOC_BIAS_STANDBY:
/* The only way to clear the suspend flag is to reset the codec */
--
1.7.10.4

@ -0,0 +1,61 @@
From aff616f4a33bd3a9ab1506fdbe97fcfe285cb7b0 Mon Sep 17 00:00:00 2001
From: Paul Cercueil <paul@crapouillou.net>
Date: Thu, 13 Sep 2012 00:09:20 +0200
Subject: [PATCH 07/16] RTC: JZ4740: Init the "regulator" register on startup.
This register controls the accuracy of the RTC. uC/OS-II use
the RTC as a 100Hz clock, and writes a completely wrong value
on that register, that we have to overwrite if we want a working
real-time clock.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
drivers/rtc/rtc-jz4740.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 1e48686..722fb0c 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -14,6 +14,7 @@
*
*/
+#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -215,6 +216,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
int ret;
struct jz4740_rtc *rtc;
uint32_t scratchpad;
+ struct clk *rtc_clk;
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (!rtc)
@@ -280,6 +282,21 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
}
}
+ rtc_clk = clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(rtc_clk)) {
+ dev_err(&pdev->dev, "Failed to get RTC clock\n");
+ goto err_free_irq;
+ }
+
+ /* TODO: initialize the ADJC bits (25:16) to fine-tune
+ * the accuracy of the RTC */
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR,
+ (clk_get_rate(rtc_clk) - 1) & 0xffff);
+ clk_put(rtc_clk);
+
+ if (ret)
+ dev_warn(&pdev->dev, "Could not update RTC regulator register\n");
+
return 0;
err_free_irq:
--
1.7.10.4

@ -0,0 +1,314 @@
From ee3a5c779be4d1d270388203cd1c6a734a0bf4ed Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Sun, 1 Aug 2010 21:19:40 +0200
Subject: [PATCH 09/16] Add ili8960 lcd driver
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
drivers/video/backlight/Kconfig | 7 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/ili8960.c | 262 +++++++++++++++++++++++++++++++++++++
3 files changed, 270 insertions(+)
create mode 100644 drivers/video/backlight/ili8960.c
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index d5ab658..7cd68d0 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -59,6 +59,13 @@ config LCD_LTV350QV
The LTV350QV panel is present on all ATSTK1000 boards.
+config LCD_ILI8960
+ tristate "Ilitek ili8960 LCD driver"
+ depends on LCD_CLASS_DEVICE && SPI
+ default n
+ help
+ Driver for the Ilitek ili8960 LCD controller chip.
+
config LCD_ILI922X
tristate "ILI Technology ILI9221/ILI9222 support"
depends on SPI
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 92711fe..81d3e93 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o
obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o
obj-$(CONFIG_LCD_HX8357) += hx8357.o
+obj-$(CONFIG_LCD_ILI8960) += ili8960.o
obj-$(CONFIG_LCD_ILI922X) += ili922x.o
obj-$(CONFIG_LCD_ILI9320) += ili9320.o
obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o
diff --git a/drivers/video/backlight/ili8960.c b/drivers/video/backlight/ili8960.c
new file mode 100644
index 0000000..61eb815
--- /dev/null
+++ b/drivers/video/backlight/ili8960.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * Driver for Ilitek ili8960 LCD
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/lcd.h>
+#include <linux/delay.h>
+
+struct ili8960 {
+ struct spi_device *spi;
+ struct lcd_device *lcd;
+ bool enabled;
+ unsigned int brightness;
+};
+
+#define ILI8960_REG_BRIGHTNESS 0x03
+#define ILI8960_REG_POWER 0x05
+#define ILI8960_REG_CONTRAST 0x0d
+
+static int ili8960_write_reg(struct spi_device *spi, uint8_t reg,
+ uint8_t data)
+{
+ uint8_t buf[2];
+ buf[0] = ((reg & 0x40) << 1) | (reg & 0x3f);
+ buf[1] = data;
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static int ili8960_programm_power(struct spi_device *spi, bool enabled)
+{
+ int ret;
+
+ if (enabled)
+ mdelay(20);
+
+ ret = ili8960_write_reg(spi, ILI8960_REG_POWER, enabled ? 0xc7 : 0xc6);
+
+ if (!enabled)
+ mdelay(20);
+
+ return ret;
+}
+
+static int ili8960_set_power(struct lcd_device *lcd, int power)
+{
+ struct ili8960 *ili8960 = lcd_get_data(lcd);
+
+ switch (power) {
+ case FB_BLANK_UNBLANK:
+ ili8960->enabled = true;
+ break;
+ default:
+ return 0;
+ }
+
+ return ili8960_programm_power(ili8960->spi, ili8960->enabled);
+}
+
+static int ili8960_early_set_power(struct lcd_device *lcd, int power)
+{
+ struct ili8960 *ili8960 = lcd_get_data(lcd);
+
+ switch (power) {
+ case FB_BLANK_UNBLANK:
+ return 0;
+ default:
+ ili8960->enabled = false;
+ break;
+ }
+
+ return ili8960_programm_power(ili8960->spi, ili8960->enabled);
+}
+
+static int ili8960_get_power(struct lcd_device *lcd)
+{
+ struct ili8960 *ili8960 = lcd_get_data(lcd);
+ return ili8960->enabled ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+}
+
+static int ili8960_set_contrast(struct lcd_device *lcd, int contrast)
+{
+ struct ili8960 *ili8960 = lcd_get_data(lcd);
+
+ return ili8960_write_reg(ili8960->spi, ILI8960_REG_CONTRAST, contrast);
+}
+
+static int ili8960_set_mode(struct lcd_device *lcd, struct fb_videomode *mode)
+{
+ if (mode->xres != 320 && mode->yres != 240)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ili8960_set_brightness(struct ili8960 *ili8960, int brightness)
+{
+ int ret;
+
+ ret = ili8960_write_reg(ili8960->spi, ILI8960_REG_BRIGHTNESS, brightness);
+
+ if (ret == 0)
+ ili8960->brightness = brightness;
+
+ return ret;
+}
+
+static ssize_t ili8960_show_brightness(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lcd_device *ld = to_lcd_device(dev);
+ struct ili8960 *ili8960 = lcd_get_data(ld);
+
+ return sprintf(buf, "%u\n", ili8960->brightness);
+}
+
+static ssize_t ili8960_store_brightness(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lcd_device *ld = to_lcd_device(dev);
+ struct ili8960 *ili8960 = lcd_get_data(ld);
+ unsigned long brightness;
+ int ret;
+
+ ret = kstrtoul(buf, 0, &brightness);
+ if (ret)
+ return ret;
+
+ if (brightness > 255)
+ return -EINVAL;
+
+ ili8960_set_brightness(ili8960, brightness);
+
+ return count;
+}
+
+
+static DEVICE_ATTR(brightness, 0644, ili8960_show_brightness,
+ ili8960_store_brightness);
+
+static struct lcd_ops ili8960_lcd_ops = {
+ .set_power = ili8960_set_power,
+ .early_set_power = ili8960_early_set_power,
+ .get_power = ili8960_get_power,
+ .set_contrast = ili8960_set_contrast,
+ .set_mode = ili8960_set_mode,
+};
+
+static int ili8960_probe(struct spi_device *spi)
+{
+ int ret;
+ struct ili8960 *ili8960;
+
+ ili8960 = devm_kzalloc(&spi->dev, sizeof(*ili8960), GFP_KERNEL);
+ if (!ili8960)
+ return -ENOMEM;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_3;
+
+ ret = spi_setup(spi);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to setup spi\n");
+ return ret;
+ }
+
+ ili8960->spi = spi;
+
+ ili8960->lcd = lcd_device_register("ili8960-lcd", &spi->dev, ili8960,
+ &ili8960_lcd_ops);
+
+ if (IS_ERR(ili8960->lcd)) {
+ ret = PTR_ERR(ili8960->lcd);
+ dev_err(&spi->dev, "Failed to register lcd device: %d\n", ret);
+ return ret;
+ }
+
+ ili8960->lcd->props.max_contrast = 255;
+
+ ret = device_create_file(&ili8960->lcd->dev, &dev_attr_brightness);
+ if (ret)
+ goto err_unregister_lcd;
+
+ ili8960_programm_power(ili8960->spi, true);
+ ili8960->enabled = true;
+
+ spi_set_drvdata(spi, ili8960);
+
+ ili8960_write_reg(spi, 0x13, 0x01);
+
+ return 0;
+err_unregister_lcd:
+ lcd_device_unregister(ili8960->lcd);
+ return ret;
+}
+
+static int ili8960_remove(struct spi_device *spi)
+{
+ struct ili8960 *ili8960 = spi_get_drvdata(spi);
+
+ device_remove_file(&ili8960->lcd->dev, &dev_attr_brightness);
+ lcd_device_unregister(ili8960->lcd);
+
+ spi_set_drvdata(spi, NULL);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int ili8960_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct ili8960 *ili8960 = spi_get_drvdata(spi);
+
+ if (ili8960->enabled)
+ ili8960_programm_power(ili8960->spi, false);
+
+ return 0;
+}
+
+static int ili8960_resume(struct spi_device *spi)
+{
+ struct ili8960 *ili8960 = spi_get_drvdata(spi);
+
+ if (ili8960->enabled)
+ ili8960_programm_power(ili8960->spi, true);
+
+ return 0;
+}
+
+#else
+#define ili8960_suspend NULL
+#define ili8960_resume NULL
+#endif
+
+static struct spi_driver ili8960_driver = {
+ .driver = {
+ .name = "ili8960",
+ .owner = THIS_MODULE,
+ },
+ .probe = ili8960_probe,
+ .remove = ili8960_remove,
+ .suspend = ili8960_suspend,
+ .resume = ili8960_resume,
+};
+module_spi_driver(ili8960_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LCD driver for Ilitek ili8960");
+MODULE_ALIAS("spi:ili8960");
--
1.7.10.4

@ -0,0 +1,28 @@
From 590ceec7028084ebd9d643b4929ceae9fd806fbe Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 13 Oct 2010 01:17:24 +0200
Subject: [PATCH 10/16] qi_lb60: Don't use 3-wire spi mode for the display for
now
The spi_gpio driver does not support 3-wire mode.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
arch/mips/jz4740/board-qi_lb60.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index be2b3de..bc454e6 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -311,7 +311,6 @@ static struct spi_board_info qi_lb60_spi_board_info[] = {
.chip_select = 0,
.bus_num = 1,
.max_speed_hz = 30 * 1000,
- .mode = SPI_3WIRE,
},
};
--
1.7.10.4

@ -0,0 +1,488 @@
From 8c53b6491806a37d6999886d22c34bfed310034c Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Thu, 30 May 2013 18:25:02 +0200
Subject: [PATCH 11/16] dma: Add a jz4740 dmaengine driver
This patch adds dmaengine support for the JZ4740 DMA controller. For now the
driver will be a wrapper around the custom JZ4740 DMA API. Once all users of the
custom JZ4740 DMA API have been converted to the dmaengine API the custom API
will be removed and direct hardware access will be added to the dmaengine
driver.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
drivers/dma/Kconfig | 6 +
drivers/dma/Makefile | 1 +
drivers/dma/dma-jz4740.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 440 insertions(+)
create mode 100644 drivers/dma/dma-jz4740.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index e992489..b3e8952 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -312,6 +312,12 @@ config MMP_PDMA
help
Support the MMP PDMA engine for PXA and MMP platfrom.
+config DMA_JZ4740
+ tristate "JZ4740 DMA support"
+ depends on MACH_JZ4740
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+
config DMA_ENGINE
bool
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index a2b0df5..6127a61 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
+obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c
new file mode 100644
index 0000000..3d42434
--- /dev/null
+++ b/drivers/dma/dma-jz4740.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2013, Lars-Peter Clausen <lars@metafoo.de>
+ * JZ4740 DMAC support
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm/mach-jz4740/dma.h>
+
+#include "virt-dma.h"
+
+#define JZ_DMA_NR_CHANS 6
+
+struct jz4740_dma_sg {
+ dma_addr_t addr;
+ unsigned int len;
+};
+
+struct jz4740_dma_desc {
+ struct virt_dma_desc vdesc;
+
+ enum dma_transfer_direction direction;
+ bool cyclic;
+
+ unsigned int num_sgs;
+ struct jz4740_dma_sg sg[];
+};
+
+struct jz4740_dmaengine_chan {
+ struct virt_dma_chan vchan;
+ struct jz4740_dma_chan *jz_chan;
+
+ dma_addr_t fifo_addr;
+
+ struct jz4740_dma_desc *desc;
+ unsigned int next_sg;
+};
+
+struct jz4740_dma_dev {
+ struct dma_device ddev;
+
+ struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS];
+};
+
+static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct jz4740_dmaengine_chan, vchan.chan);
+}
+
+static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc)
+{
+ return container_of(vdesc, struct jz4740_dma_desc, vdesc);
+}
+
+static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs)
+{
+ return kzalloc(sizeof(struct jz4740_dma_desc) +
+ sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC);
+}
+
+static enum jz4740_dma_width jz4740_dma_width(enum dma_slave_buswidth width)
+{
+ switch (width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ return JZ4740_DMA_WIDTH_8BIT;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ return JZ4740_DMA_WIDTH_16BIT;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ return JZ4740_DMA_WIDTH_32BIT;
+ default:
+ return JZ4740_DMA_WIDTH_32BIT;
+ }
+}
+
+static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst)
+{
+ if (maxburst <= 1)
+ return JZ4740_DMA_TRANSFER_SIZE_1BYTE;
+ else if (maxburst <= 3)
+ return JZ4740_DMA_TRANSFER_SIZE_2BYTE;
+ else if (maxburst <= 15)
+ return JZ4740_DMA_TRANSFER_SIZE_4BYTE;
+ else if (maxburst <= 31)
+ return JZ4740_DMA_TRANSFER_SIZE_16BYTE;
+
+ return JZ4740_DMA_TRANSFER_SIZE_32BYTE;
+}
+
+static int jz4740_dma_slave_config(struct dma_chan *c,
+ const struct dma_slave_config *config)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+ struct jz4740_dma_config jzcfg;
+
+ switch (config->direction) {
+ case DMA_MEM_TO_DEV:
+ jzcfg.flags = JZ4740_DMA_SRC_AUTOINC;
+ jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst);
+ chan->fifo_addr = config->dst_addr;
+ break;
+ case DMA_DEV_TO_MEM:
+ jzcfg.flags = JZ4740_DMA_DST_AUTOINC;
+ jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst);
+ chan->fifo_addr = config->src_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ jzcfg.src_width = jz4740_dma_width(config->src_addr_width);
+ jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width);
+ jzcfg.mode = JZ4740_DMA_MODE_SINGLE;
+ jzcfg.request_type = config->slave_id;
+
+ jz4740_dma_configure(chan->jz_chan, &jzcfg);
+
+ return 0;
+}
+
+static int jz4740_dma_terminate_all(struct dma_chan *c)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ jz4740_dma_disable(chan->jz_chan);
+ chan->desc = NULL;
+ vchan_get_all_descriptors(&chan->vchan, &head);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ vchan_dma_desc_free_list(&chan->vchan, &head);
+
+ return 0;
+}
+
+static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct dma_slave_config *config = (struct dma_slave_config *)arg;
+
+ switch (cmd) {
+ case DMA_SLAVE_CONFIG:
+ return jz4740_dma_slave_config(chan, config);
+ case DMA_TERMINATE_ALL:
+ return jz4740_dma_terminate_all(chan);
+ default:
+ return -ENOSYS;
+ }
+}
+
+static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan)
+{
+ dma_addr_t src_addr, dst_addr;
+ struct virt_dma_desc *vdesc;
+ struct jz4740_dma_sg *sg;
+
+ jz4740_dma_disable(chan->jz_chan);
+
+ if (!chan->desc) {
+ vdesc = vchan_next_desc(&chan->vchan);
+ if (!vdesc)
+ return 0;
+ chan->desc = to_jz4740_dma_desc(vdesc);
+ chan->next_sg = 0;
+ }
+
+ if (chan->next_sg == chan->desc->num_sgs)
+ chan->next_sg = 0;
+
+ sg = &chan->desc->sg[chan->next_sg];
+
+ if (chan->desc->direction == DMA_MEM_TO_DEV) {
+ src_addr = sg->addr;
+ dst_addr = chan->fifo_addr;
+ } else {
+ src_addr = chan->fifo_addr;
+ dst_addr = sg->addr;
+ }
+ jz4740_dma_set_src_addr(chan->jz_chan, src_addr);
+ jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr);
+ jz4740_dma_set_transfer_count(chan->jz_chan, sg->len);
+
+ chan->next_sg++;
+
+ jz4740_dma_enable(chan->jz_chan);
+
+ return 0;
+}
+
+static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error,
+ void *devid)
+{
+ struct jz4740_dmaengine_chan *chan = devid;
+
+ spin_lock(&chan->vchan.lock);
+ if (chan->desc) {
+ if (chan->desc && chan->desc->cyclic) {
+ vchan_cyclic_callback(&chan->desc->vdesc);
+ } else {
+ if (chan->next_sg == chan->desc->num_sgs) {
+ chan->desc = NULL;
+ vchan_cookie_complete(&chan->desc->vdesc);
+ }
+ }
+ }
+ jz4740_dma_start_transfer(chan);
+ spin_unlock(&chan->vchan.lock);
+}
+
+static void jz4740_dma_issue_pending(struct dma_chan *c)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+ unsigned long flags;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ if (vchan_issue_pending(&chan->vchan) && !chan->desc)
+ jz4740_dma_start_transfer(chan);
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+}
+
+static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg(
+ struct dma_chan *c, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+ struct jz4740_dma_desc *desc;
+ struct scatterlist *sg;
+ unsigned int i;
+
+ desc = jz4740_dma_alloc_desc(sg_len);
+ if (!desc)
+ return NULL;
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ desc->sg[i].addr = sg_dma_address(sg);
+ desc->sg[i].len = sg_dma_len(sg);
+ }
+
+ desc->num_sgs = sg_len;
+ desc->direction = direction;
+ desc->cyclic = false;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic(
+ struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction direction,
+ unsigned long flags, void *context)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+ struct jz4740_dma_desc *desc;
+ unsigned int num_periods, i;
+
+ if (buf_len % period_len)
+ return NULL;
+
+ num_periods = buf_len / period_len;
+
+ desc = jz4740_dma_alloc_desc(num_periods);
+ if (!desc)
+ return NULL;
+
+ for (i = 0; i < num_periods; i++) {
+ desc->sg[i].addr = buf_addr;
+ desc->sg[i].len = period_len;
+ buf_addr += period_len;
+ }
+
+ desc->num_sgs = num_periods;
+ desc->direction = direction;
+ desc->cyclic = true;
+
+ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
+}
+
+static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan,
+ struct jz4740_dma_desc *desc, unsigned int next_sg)
+{
+ size_t residue = 0;
+ unsigned int i;
+
+ residue = 0;
+
+ for (i = next_sg; i < desc->num_sgs; i++)
+ residue += desc->sg[i].len;
+
+ if (next_sg != 0)
+ residue += jz4740_dma_get_residue(chan->jz_chan);
+
+ return residue;
+}
+
+static enum dma_status jz4740_dma_tx_status(struct dma_chan *c,
+ dma_cookie_t cookie, struct dma_tx_state *state)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+ struct virt_dma_desc *vdesc;
+ enum dma_status status;
+ unsigned long flags;
+
+ status = dma_cookie_status(c, cookie, state);
+ if (status == DMA_SUCCESS || !state)
+ return status;
+
+ spin_lock_irqsave(&chan->vchan.lock, flags);
+ vdesc = vchan_find_desc(&chan->vchan, cookie);
+ if (cookie == chan->desc->vdesc.tx.cookie) {
+ state->residue = jz4740_dma_desc_residue(chan, chan->desc,
+ chan->next_sg);
+ } else if (vdesc) {
+ state->residue = jz4740_dma_desc_residue(chan,
+ to_jz4740_dma_desc(vdesc), 0);
+ } else {
+ state->residue = 0;
+ }
+ spin_unlock_irqrestore(&chan->vchan.lock, flags);
+
+ return status;
+}
+
+static int jz4740_dma_alloc_chan_resources(struct dma_chan *c)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+
+ chan->jz_chan = jz4740_dma_request(chan, NULL);
+ if (!chan->jz_chan)
+ return -EBUSY;
+
+ jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
+
+ return 0;
+}
+
+static void jz4740_dma_free_chan_resources(struct dma_chan *c)
+{
+ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+
+ vchan_free_chan_resources(&chan->vchan);
+ jz4740_dma_free(chan->jz_chan);
+ chan->jz_chan = NULL;
+}
+
+static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc)
+{
+ kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc));
+}
+
+static int jz4740_dma_probe(struct platform_device *pdev)
+{
+ struct jz4740_dmaengine_chan *chan;
+ struct jz4740_dma_dev *dmadev;
+ struct dma_device *dd;
+ unsigned int i;
+ int ret;
+
+ dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL);
+ if (!dmadev)
+ return -EINVAL;
+
+ dd = &dmadev->ddev;
+
+ dma_cap_set(DMA_SLAVE, dd->cap_mask);
+ dma_cap_set(DMA_CYCLIC, dd->cap_mask);
+ dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources;
+ dd->device_free_chan_resources = jz4740_dma_free_chan_resources;
+ dd->device_tx_status = jz4740_dma_tx_status;
+ dd->device_issue_pending = jz4740_dma_issue_pending;
+ dd->device_prep_slave_sg = jz4740_dma_prep_slave_sg;
+ dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic;
+ dd->device_control = jz4740_dma_control;
+ dd->dev = &pdev->dev;
+ dd->chancnt = JZ_DMA_NR_CHANS;
+ INIT_LIST_HEAD(&dd->channels);
+
+ for (i = 0; i < dd->chancnt; i++) {
+ chan = &dmadev->chan[i];
+ chan->vchan.desc_free = jz4740_dma_desc_free;
+ vchan_init(&chan->vchan, dd);
+ }
+
+ ret = dma_async_device_register(dd);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, dmadev);
+
+ return 0;
+}
+
+static int jz4740_dma_remove(struct platform_device *pdev)
+{
+ struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev);
+
+ dma_async_device_unregister(&dmadev->ddev);
+
+ return 0;
+}
+
+static struct platform_driver jz4740_dma_driver = {
+ .probe = jz4740_dma_probe,
+ .remove = jz4740_dma_remove,
+ .driver = {
+ .name = "jz4740-dma",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(jz4740_dma_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("JZ4740 DMA driver");
+MODULE_LICENSE("GPLv2");
--
1.7.10.4

@ -0,0 +1,27 @@
From 1a0086ae912b81a1e6c7213e97059c50a7bfe636 Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Sun, 6 May 2012 01:51:09 +0200
Subject: [PATCH 12/16] MIPS: JZ4740: Correct clock gate bit for DMA
controller
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
arch/mips/jz4740/clock.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c
index 484d38a..1b5f554 100644
--- a/arch/mips/jz4740/clock.c
+++ b/arch/mips/jz4740/clock.c
@@ -687,7 +687,7 @@ static struct clk jz4740_clock_simple_clks[] = {
[3] = {
.name = "dma",
.parent = &jz_clk_high_speed_peripheral.clk,
- .gate_bit = JZ_CLOCK_GATE_UART0,
+ .gate_bit = JZ_CLOCK_GATE_DMAC,
.ops = &jz_clk_simple_ops,
},
[4] = {
--
1.7.10.4

@ -0,0 +1,80 @@
From b1ab71156cd49b2389397d3be54b93fe394a1cb2 Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Tue, 9 Oct 2012 13:09:57 +0200
Subject: [PATCH 13/16] MIPS: JZ4740: Acquire and enable DMA controller clock
Previously, it was assumed that the DMA controller clock is not gated
when the kernel starts running. While that is the power-on state, it is
safer to not rely on that.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
---
arch/mips/jz4740/clock.c | 2 +-
arch/mips/jz4740/dma.c | 24 ++++++++++++++++++++++--
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c
index 1b5f554..b3eba60 100644
--- a/arch/mips/jz4740/clock.c
+++ b/arch/mips/jz4740/clock.c
@@ -921,4 +921,4 @@ static int jz4740_clock_init(void)
return 0;
}
-arch_initcall(jz4740_clock_init);
+postcore_initcall(jz4740_clock_init);
diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c
index 317ec6f..fb5266c 100644
--- a/arch/mips/jz4740/dma.c
+++ b/arch/mips/jz4740/dma.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
+#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
@@ -268,6 +269,7 @@ static irqreturn_t jz4740_dma_irq(int irq, void *dev_id)
static int jz4740_dma_init(void)
{
+ struct clk *clk;
unsigned int ret;
jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400);
@@ -277,11 +279,29 @@ static int jz4740_dma_init(void)
spin_lock_init(&jz4740_dma_lock);
- ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL);
+ clk = clk_get(NULL, "dma");
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ printk(KERN_ERR "JZ4740 DMA: Failed to request clock: %d\n",
+ ret);
+ goto err_iounmap;
+ }
- if (ret)
+ ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL);
+ if (ret) {
printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret);
+ goto err_clkput;
+ }
+
+ clk_enable(clk);
+
+ return 0;
+
+err_clkput:
+ clk_put(clk);
+err_iounmap:
+ iounmap(jz4740_dma_base);
return ret;
}
arch_initcall(jz4740_dma_init);
--
1.7.10.4

@ -0,0 +1,73 @@
From 734bdfd446b1eba0c54ad07703a192e6093417fd Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Thu, 30 May 2013 18:25:03 +0200
Subject: [PATCH 14/16] MIPS: jz4740: Register jz4740 DMA device
Register a device for the newly added jz4740 dmaengine driver.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
[manually edited to align struct assignment]
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
arch/mips/include/asm/mach-jz4740/platform.h | 1 +
arch/mips/jz4740/board-qi_lb60.c | 1 +
arch/mips/jz4740/platform.c | 21 +++++++++++++++++++++
3 files changed, 23 insertions(+)
diff --git a/arch/mips/include/asm/mach-jz4740/platform.h b/arch/mips/include/asm/mach-jz4740/platform.h
index 72cfebd..05988c2 100644
--- a/arch/mips/include/asm/mach-jz4740/platform.h
+++ b/arch/mips/include/asm/mach-jz4740/platform.h
@@ -32,6 +32,7 @@ extern struct platform_device jz4740_codec_device;
extern struct platform_device jz4740_adc_device;
extern struct platform_device jz4740_wdt_device;
extern struct platform_device jz4740_pwm_device;
+extern struct platform_device jz4740_dma_device;
void jz4740_serial_device_register(void);
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index bc454e6..b857521 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -437,6 +437,7 @@ static struct platform_device *jz_platform_devices[] __initdata = {
&jz4740_rtc_device,
&jz4740_adc_device,
&jz4740_pwm_device,
+ &jz4740_dma_device,
&qi_lb60_gpio_keys,
&qi_lb60_pwm_beeper,
&qi_lb60_charger_device,
diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c
index e9348fd..df65677 100644
--- a/arch/mips/jz4740/platform.c
+++ b/arch/mips/jz4740/platform.c
@@ -329,3 +329,24 @@ struct platform_device jz4740_pwm_device = {
.name = "jz4740-pwm",
.id = -1,
};
+
+/* DMA */
+static struct resource jz4740_dma_resources[] = {
+ {
+ .start = JZ4740_DMAC_BASE_ADDR,
+ .end = JZ4740_DMAC_BASE_ADDR + 0x400 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = JZ4740_IRQ_DMAC,
+ .end = JZ4740_IRQ_DMAC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device jz4740_dma_device = {
+ .name = "jz4740-dma",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(jz4740_dma_resources),
+ .resource = jz4740_dma_resources,
+};
--
1.7.10.4

@ -0,0 +1,854 @@
From 7b91fca454e66ac1bc1fe5b68de0bf55f799bd41 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Thu, 30 May 2013 18:25:05 +0200
Subject: [PATCH 15/16] MIPS: jz4740: Remove custom DMA API
Now that all users of the custom jz4740 DMA API have been converted to use
the dmaengine API instead we can remove the custom API and move all the code
talking to the hardware to the dmaengine driver.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Ralf Baechle <ralf@linux-mips.org>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
---
arch/mips/include/asm/mach-jz4740/dma.h | 56 ------
arch/mips/jz4740/Makefile | 2 +-
arch/mips/jz4740/dma.c | 307 -------------------------------
drivers/dma/dma-jz4740.c | 258 ++++++++++++++++++++++----
4 files changed, 222 insertions(+), 401 deletions(-)
delete mode 100644 arch/mips/jz4740/dma.c
diff --git a/arch/mips/include/asm/mach-jz4740/dma.h b/arch/mips/include/asm/mach-jz4740/dma.h
index 98b4e7c..509cd58 100644
--- a/arch/mips/include/asm/mach-jz4740/dma.h
+++ b/arch/mips/include/asm/mach-jz4740/dma.h
@@ -16,8 +16,6 @@
#ifndef __ASM_MACH_JZ4740_DMA_H__
#define __ASM_MACH_JZ4740_DMA_H__
-struct jz4740_dma_chan;
-
enum jz4740_dma_request_type {
JZ4740_DMA_TYPE_AUTO_REQUEST = 8,
JZ4740_DMA_TYPE_UART_TRANSMIT = 20,
@@ -33,58 +31,4 @@ enum jz4740_dma_request_type {
JZ4740_DMA_TYPE_SLCD = 30,
};
-enum jz4740_dma_width {
- JZ4740_DMA_WIDTH_32BIT = 0,
- JZ4740_DMA_WIDTH_8BIT = 1,
- JZ4740_DMA_WIDTH_16BIT = 2,
-};
-
-enum jz4740_dma_transfer_size {
- JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0,
- JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1,
- JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2,
- JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3,
- JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4,
-};
-
-enum jz4740_dma_flags {
- JZ4740_DMA_SRC_AUTOINC = 0x2,
- JZ4740_DMA_DST_AUTOINC = 0x1,
-};
-
-enum jz4740_dma_mode {
- JZ4740_DMA_MODE_SINGLE = 0,
- JZ4740_DMA_MODE_BLOCK = 1,
-};
-
-struct jz4740_dma_config {
- enum jz4740_dma_width src_width;
- enum jz4740_dma_width dst_width;
- enum jz4740_dma_transfer_size transfer_size;
- enum jz4740_dma_request_type request_type;
- enum jz4740_dma_flags flags;
- enum jz4740_dma_mode mode;
-};
-
-typedef void (*jz4740_dma_complete_callback_t)(struct jz4740_dma_chan *, int, void *);
-
-struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name);
-void jz4740_dma_free(struct jz4740_dma_chan *dma);
-
-void jz4740_dma_configure(struct jz4740_dma_chan *dma,
- const struct jz4740_dma_config *config);
-
-
-void jz4740_dma_enable(struct jz4740_dma_chan *dma);
-void jz4740_dma_disable(struct jz4740_dma_chan *dma);
-
-void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src);
-void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst);
-void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count);
-
-uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma);
-
-void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma,
- jz4740_dma_complete_callback_t cb);
-
#endif /* __ASM_JZ4740_DMA_H__ */
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
index 63bad0e..28e5535 100644
--- a/arch/mips/jz4740/Makefile
+++ b/arch/mips/jz4740/Makefile
@@ -4,7 +4,7 @@
# Object file lists.
-obj-y += prom.o irq.o time.o reset.o setup.o dma.o \
+obj-y += prom.o irq.o time.o reset.o setup.o \
gpio.o clock.o platform.o timer.o serial.o
obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o
diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c
deleted file mode 100644
index fb5266c..0000000
--- a/arch/mips/jz4740/dma.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
- * JZ4740 SoC DMA support
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-
-#include <linux/dma-mapping.h>
-#include <asm/mach-jz4740/dma.h>
-#include <asm/mach-jz4740/base.h>
-
-#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20)
-#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20)
-#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20)
-#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20)
-#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20)
-#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20)
-#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20)
-
-#define JZ_REG_DMA_CTRL 0x300
-#define JZ_REG_DMA_IRQ 0x304
-#define JZ_REG_DMA_DOORBELL 0x308
-#define JZ_REG_DMA_DOORBELL_SET 0x30C
-
-#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31)
-#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6)
-#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4)
-#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3)
-#define JZ_DMA_STATUS_CTRL_HALT BIT(2)
-#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1)
-#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0)
-
-#define JZ_DMA_CMD_SRC_INC BIT(23)
-#define JZ_DMA_CMD_DST_INC BIT(22)
-#define JZ_DMA_CMD_RDIL_MASK (0xf << 16)
-#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14)
-#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12)
-#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8)
-#define JZ_DMA_CMD_BLOCK_MODE BIT(7)
-#define JZ_DMA_CMD_DESC_VALID BIT(4)
-#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3)
-#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2)
-#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1)
-#define JZ_DMA_CMD_LINK_ENABLE BIT(0)
-
-#define JZ_DMA_CMD_FLAGS_OFFSET 22
-#define JZ_DMA_CMD_RDIL_OFFSET 16
-#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14
-#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12
-#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8
-#define JZ_DMA_CMD_MODE_OFFSET 7
-
-#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8)
-#define JZ_DMA_CTRL_HALT BIT(3)
-#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2)
-#define JZ_DMA_CTRL_ENABLE BIT(0)
-
-
-static void __iomem *jz4740_dma_base;
-static spinlock_t jz4740_dma_lock;
-
-static inline uint32_t jz4740_dma_read(size_t reg)
-{
- return readl(jz4740_dma_base + reg);
-}
-
-static inline void jz4740_dma_write(size_t reg, uint32_t val)
-{
- writel(val, jz4740_dma_base + reg);
-}
-
-static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask)
-{
- uint32_t val2;
- val2 = jz4740_dma_read(reg);
- val2 &= ~mask;
- val2 |= val;
- jz4740_dma_write(reg, val2);
-}
-
-struct jz4740_dma_chan {
- unsigned int id;
- void *dev;
- const char *name;
-
- enum jz4740_dma_flags flags;
- uint32_t transfer_shift;
-
- jz4740_dma_complete_callback_t complete_cb;
-
- unsigned used:1;
-};
-
-#define JZ4740_DMA_CHANNEL(_id) { .id = _id }
-
-struct jz4740_dma_chan jz4740_dma_channels[] = {
- JZ4740_DMA_CHANNEL(0),
- JZ4740_DMA_CHANNEL(1),
- JZ4740_DMA_CHANNEL(2),
- JZ4740_DMA_CHANNEL(3),
- JZ4740_DMA_CHANNEL(4),
- JZ4740_DMA_CHANNEL(5),
-};
-
-struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name)
-{
- unsigned int i;
- struct jz4740_dma_chan *dma = NULL;
-
- spin_lock(&jz4740_dma_lock);
-
- for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) {
- if (!jz4740_dma_channels[i].used) {
- dma = &jz4740_dma_channels[i];
- dma->used = 1;
- break;
- }
- }
-
- spin_unlock(&jz4740_dma_lock);
-
- if (!dma)
- return NULL;
-
- dma->dev = dev;
- dma->name = name;
-
- return dma;
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_request);
-
-void jz4740_dma_configure(struct jz4740_dma_chan *dma,
- const struct jz4740_dma_config *config)
-{
- uint32_t cmd;
-
- switch (config->transfer_size) {
- case JZ4740_DMA_TRANSFER_SIZE_2BYTE:
- dma->transfer_shift = 1;
- break;
- case JZ4740_DMA_TRANSFER_SIZE_4BYTE:
- dma->transfer_shift = 2;
- break;
- case JZ4740_DMA_TRANSFER_SIZE_16BYTE:
- dma->transfer_shift = 4;
- break;
- case JZ4740_DMA_TRANSFER_SIZE_32BYTE:
- dma->transfer_shift = 5;
- break;
- default:
- dma->transfer_shift = 0;
- break;
- }
-
- cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET;
- cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET;
- cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET;
- cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET;
- cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET;
- cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE;
-
- jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd);
- jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0);
- jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type);
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_configure);
-
-void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src)
-{
- jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src);
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr);
-
-void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst)
-{
- jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst);
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr);
-
-void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count)
-{
- count >>= dma->transfer_shift;
- jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count);
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count);
-
-void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma,
- jz4740_dma_complete_callback_t cb)
-{
- dma->complete_cb = cb;
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb);
-
-void jz4740_dma_free(struct jz4740_dma_chan *dma)
-{
- dma->dev = NULL;
- dma->complete_cb = NULL;
- dma->used = 0;
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_free);
-
-void jz4740_dma_enable(struct jz4740_dma_chan *dma)
-{
- jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id),
- JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE,
- JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC |
- JZ_DMA_STATUS_CTRL_ENABLE);
-
- jz4740_dma_write_mask(JZ_REG_DMA_CTRL,
- JZ_DMA_CTRL_ENABLE,
- JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE);
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_enable);
-
-void jz4740_dma_disable(struct jz4740_dma_chan *dma)
-{
- jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
- JZ_DMA_STATUS_CTRL_ENABLE);
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_disable);
-
-uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma)
-{
- uint32_t residue;
- residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id));
- return residue << dma->transfer_shift;
-}
-EXPORT_SYMBOL_GPL(jz4740_dma_get_residue);
-
-static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma)
-{
- (void) jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id));
-
- jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0,
- JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE);
-
- if (dma->complete_cb)
- dma->complete_cb(dma, 0, dma->dev);
-}
-
-static irqreturn_t jz4740_dma_irq(int irq, void *dev_id)
-{
- uint32_t irq_status;
- unsigned int i;
-
- irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ);
-
- for (i = 0; i < 6; ++i) {
- if (irq_status & (1 << i))
- jz4740_dma_chan_irq(&jz4740_dma_channels[i]);
- }
-
- return IRQ_HANDLED;
-}
-
-static int jz4740_dma_init(void)
-{
- struct clk *clk;
- unsigned int ret;
-
- jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400);
-
- if (!jz4740_dma_base)
- return -EBUSY;
-
- spin_lock_init(&jz4740_dma_lock);
-
- clk = clk_get(NULL, "dma");
- if (IS_ERR(clk)) {
- ret = PTR_ERR(clk);
- printk(KERN_ERR "JZ4740 DMA: Failed to request clock: %d\n",
- ret);
- goto err_iounmap;
- }
-
- ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL);
- if (ret) {
- printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret);
- goto err_clkput;
- }
-
- clk_enable(clk);
-
- return 0;
-
-err_clkput:
- clk_put(clk);
-
-err_iounmap:
- iounmap(jz4740_dma_base);
- return ret;
-}
-arch_initcall(jz4740_dma_init);
diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c
index 3d42434..b0c0c82 100644
--- a/drivers/dma/dma-jz4740.c
+++ b/drivers/dma/dma-jz4740.c
@@ -22,6 +22,8 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
#include <asm/mach-jz4740/dma.h>
@@ -29,6 +31,76 @@
#define JZ_DMA_NR_CHANS 6
+#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20)
+#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20)
+#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20)
+#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20)
+#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20)
+#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20)
+#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20)
+
+#define JZ_REG_DMA_CTRL 0x300
+#define JZ_REG_DMA_IRQ 0x304
+#define JZ_REG_DMA_DOORBELL 0x308
+#define JZ_REG_DMA_DOORBELL_SET 0x30C
+
+#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31)
+#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6)
+#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4)
+#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3)
+#define JZ_DMA_STATUS_CTRL_HALT BIT(2)
+#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1)
+#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0)
+
+#define JZ_DMA_CMD_SRC_INC BIT(23)
+#define JZ_DMA_CMD_DST_INC BIT(22)
+#define JZ_DMA_CMD_RDIL_MASK (0xf << 16)
+#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14)
+#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12)
+#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8)
+#define JZ_DMA_CMD_BLOCK_MODE BIT(7)
+#define JZ_DMA_CMD_DESC_VALID BIT(4)
+#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3)
+#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2)
+#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1)
+#define JZ_DMA_CMD_LINK_ENABLE BIT(0)
+
+#define JZ_DMA_CMD_FLAGS_OFFSET 22
+#define JZ_DMA_CMD_RDIL_OFFSET 16
+#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14
+#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12
+#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8
+#define JZ_DMA_CMD_MODE_OFFSET 7
+
+#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8)
+#define JZ_DMA_CTRL_HALT BIT(3)
+#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2)
+#define JZ_DMA_CTRL_ENABLE BIT(0)
+
+enum jz4740_dma_width {
+ JZ4740_DMA_WIDTH_32BIT = 0,
+ JZ4740_DMA_WIDTH_8BIT = 1,
+ JZ4740_DMA_WIDTH_16BIT = 2,
+};
+
+enum jz4740_dma_transfer_size {
+ JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0,
+ JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1,
+ JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2,
+ JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3,
+ JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4,
+};
+
+enum jz4740_dma_flags {
+ JZ4740_DMA_SRC_AUTOINC = 0x2,
+ JZ4740_DMA_DST_AUTOINC = 0x1,
+};
+
+enum jz4740_dma_mode {
+ JZ4740_DMA_MODE_SINGLE = 0,
+ JZ4740_DMA_MODE_BLOCK = 1,
+};
+
struct jz4740_dma_sg {
dma_addr_t addr;
unsigned int len;
@@ -46,9 +118,10 @@ struct jz4740_dma_desc {
struct jz4740_dmaengine_chan {
struct virt_dma_chan vchan;
- struct jz4740_dma_chan *jz_chan;
+ unsigned int id;
dma_addr_t fifo_addr;
+ unsigned int transfer_shift;
struct jz4740_dma_desc *desc;
unsigned int next_sg;
@@ -56,10 +129,19 @@ struct jz4740_dmaengine_chan {
struct jz4740_dma_dev {
struct dma_device ddev;
+ void __iomem *base;
+ struct clk *clk;
struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS];
};
+static struct jz4740_dma_dev *jz4740_dma_chan_get_dev(
+ struct jz4740_dmaengine_chan *chan)
+{
+ return container_of(chan->vchan.chan.device, struct jz4740_dma_dev,
+ ddev);
+}
+
static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c)
{
return container_of(c, struct jz4740_dmaengine_chan, vchan.chan);
@@ -70,6 +152,29 @@ static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc)
return container_of(vdesc, struct jz4740_dma_desc, vdesc);
}
+static inline uint32_t jz4740_dma_read(struct jz4740_dma_dev *dmadev,
+ unsigned int reg)
+{
+ return readl(dmadev->base + reg);
+}
+
+static inline void jz4740_dma_write(struct jz4740_dma_dev *dmadev,
+ unsigned reg, uint32_t val)
+{
+ writel(val, dmadev->base + reg);
+}
+
+static inline void jz4740_dma_write_mask(struct jz4740_dma_dev *dmadev,
+ unsigned int reg, uint32_t val, uint32_t mask)
+{
+ uint32_t tmp;
+
+ tmp = jz4740_dma_read(dmadev, reg);
+ tmp &= ~mask;
+ tmp |= val;
+ jz4740_dma_write(dmadev, reg, tmp);
+}
+
static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs)
{
return kzalloc(sizeof(struct jz4740_dma_desc) +
@@ -108,30 +213,60 @@ static int jz4740_dma_slave_config(struct dma_chan *c,
const struct dma_slave_config *config)
{
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
- struct jz4740_dma_config jzcfg;
+ struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
+ enum jz4740_dma_width src_width;
+ enum jz4740_dma_width dst_width;
+ enum jz4740_dma_transfer_size transfer_size;
+ enum jz4740_dma_flags flags;
+ uint32_t cmd;
switch (config->direction) {
case DMA_MEM_TO_DEV:
- jzcfg.flags = JZ4740_DMA_SRC_AUTOINC;
- jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst);
+ flags = JZ4740_DMA_SRC_AUTOINC;
+ transfer_size = jz4740_dma_maxburst(config->dst_maxburst);
chan->fifo_addr = config->dst_addr;
break;
case DMA_DEV_TO_MEM:
- jzcfg.flags = JZ4740_DMA_DST_AUTOINC;
- jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst);
+ flags = JZ4740_DMA_DST_AUTOINC;
+ transfer_size = jz4740_dma_maxburst(config->src_maxburst);
chan->fifo_addr = config->src_addr;
break;
default:
return -EINVAL;
}
+ src_width = jz4740_dma_width(config->src_addr_width);
+ dst_width = jz4740_dma_width(config->dst_addr_width);
+
+ switch (transfer_size) {
+ case JZ4740_DMA_TRANSFER_SIZE_2BYTE:
+ chan->transfer_shift = 1;
+ break;
+ case JZ4740_DMA_TRANSFER_SIZE_4BYTE:
+ chan->transfer_shift = 2;
+ break;
+ case JZ4740_DMA_TRANSFER_SIZE_16BYTE:
+ chan->transfer_shift = 4;
+ break;
+ case JZ4740_DMA_TRANSFER_SIZE_32BYTE:
+ chan->transfer_shift = 5;
+ break;
+ default:
+ chan->transfer_shift = 0;
+ break;
+ }
- jzcfg.src_width = jz4740_dma_width(config->src_addr_width);
- jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width);
- jzcfg.mode = JZ4740_DMA_MODE_SINGLE;
- jzcfg.request_type = config->slave_id;
+ cmd = flags << JZ_DMA_CMD_FLAGS_OFFSET;
+ cmd |= src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET;
+ cmd |= dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET;
+ cmd |= transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET;
+ cmd |= JZ4740_DMA_MODE_SINGLE << JZ_DMA_CMD_MODE_OFFSET;
+ cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE;
- jz4740_dma_configure(chan->jz_chan, &jzcfg);
+ jz4740_dma_write(dmadev, JZ_REG_DMA_CMD(chan->id), cmd);
+ jz4740_dma_write(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0);
+ jz4740_dma_write(dmadev, JZ_REG_DMA_REQ_TYPE(chan->id),
+ config->slave_id);
return 0;
}
@@ -139,11 +274,13 @@ static int jz4740_dma_slave_config(struct dma_chan *c,
static int jz4740_dma_terminate_all(struct dma_chan *c)
{
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
+ struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
unsigned long flags;
LIST_HEAD(head);
spin_lock_irqsave(&chan->vchan.lock, flags);
- jz4740_dma_disable(chan->jz_chan);
+ jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0,
+ JZ_DMA_STATUS_CTRL_ENABLE);
chan->desc = NULL;
vchan_get_all_descriptors(&chan->vchan, &head);
spin_unlock_irqrestore(&chan->vchan.lock, flags);
@@ -170,11 +307,13 @@ static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan)
{
+ struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
dma_addr_t src_addr, dst_addr;
struct virt_dma_desc *vdesc;
struct jz4740_dma_sg *sg;
- jz4740_dma_disable(chan->jz_chan);
+ jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0,
+ JZ_DMA_STATUS_CTRL_ENABLE);
if (!chan->desc) {
vdesc = vchan_next_desc(&chan->vchan);
@@ -196,22 +335,27 @@ static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan)
src_addr = chan->fifo_addr;
dst_addr = sg->addr;
}
- jz4740_dma_set_src_addr(chan->jz_chan, src_addr);
- jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr);
- jz4740_dma_set_transfer_count(chan->jz_chan, sg->len);
+ jz4740_dma_write(dmadev, JZ_REG_DMA_SRC_ADDR(chan->id), src_addr);
+ jz4740_dma_write(dmadev, JZ_REG_DMA_DST_ADDR(chan->id), dst_addr);
+ jz4740_dma_write(dmadev, JZ_REG_DMA_TRANSFER_COUNT(chan->id),
+ sg->len >> chan->transfer_shift);
chan->next_sg++;
- jz4740_dma_enable(chan->jz_chan);
+ jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id),
+ JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE,
+ JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC |
+ JZ_DMA_STATUS_CTRL_ENABLE);
+
+ jz4740_dma_write_mask(dmadev, JZ_REG_DMA_CTRL,
+ JZ_DMA_CTRL_ENABLE,
+ JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE);
return 0;
}
-static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error,
- void *devid)
+static void jz4740_dma_chan_irq(struct jz4740_dmaengine_chan *chan)
{
- struct jz4740_dmaengine_chan *chan = devid;
-
spin_lock(&chan->vchan.lock);
if (chan->desc) {
if (chan->desc && chan->desc->cyclic) {
@@ -227,6 +371,28 @@ static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error,
spin_unlock(&chan->vchan.lock);
}
+static irqreturn_t jz4740_dma_irq(int irq, void *devid)
+{
+ struct jz4740_dma_dev *dmadev = devid;
+ uint32_t irq_status;
+ unsigned int i;
+
+ irq_status = readl(dmadev->base + JZ_REG_DMA_IRQ);
+
+ for (i = 0; i < 6; ++i) {
+ if (irq_status & (1 << i)) {
+ jz4740_dma_write_mask(dmadev,
+ JZ_REG_DMA_STATUS_CTRL(i), 0,
+ JZ_DMA_STATUS_CTRL_ENABLE |
+ JZ_DMA_STATUS_CTRL_TRANSFER_DONE);
+
+ jz4740_dma_chan_irq(&dmadev->chan[i]);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
static void jz4740_dma_issue_pending(struct dma_chan *c)
{
struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
@@ -298,7 +464,8 @@ static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic(
static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan,
struct jz4740_dma_desc *desc, unsigned int next_sg)
{
- size_t residue = 0;
+ struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan);
+ unsigned int residue, count;
unsigned int i;
residue = 0;
@@ -306,8 +473,11 @@ static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan,
for (i = next_sg; i < desc->num_sgs; i++)
residue += desc->sg[i].len;
- if (next_sg != 0)
- residue += jz4740_dma_get_residue(chan->jz_chan);
+ if (next_sg != 0) {
+ count = jz4740_dma_read(dmadev,
+ JZ_REG_DMA_TRANSFER_COUNT(chan->id));
+ residue += count << chan->transfer_shift;
+ }
return residue;
}
@@ -342,24 +512,12 @@ static enum dma_status jz4740_dma_tx_status(struct dma_chan *c,
static int jz4740_dma_alloc_chan_resources(struct dma_chan *c)
{
- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
-
- chan->jz_chan = jz4740_dma_request(chan, NULL);
- if (!chan->jz_chan)
- return -EBUSY;
-
- jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb);
-
return 0;
}
static void jz4740_dma_free_chan_resources(struct dma_chan *c)
{
- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c);
-
- vchan_free_chan_resources(&chan->vchan);
- jz4740_dma_free(chan->jz_chan);
- chan->jz_chan = NULL;
+ vchan_free_chan_resources(to_virt_chan(c));
}
static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc)
@@ -373,7 +531,9 @@ static int jz4740_dma_probe(struct platform_device *pdev)
struct jz4740_dma_dev *dmadev;
struct dma_device *dd;
unsigned int i;
+ struct resource *res;
int ret;
+ int irq;
dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL);
if (!dmadev)
@@ -381,6 +541,17 @@ static int jz4740_dma_probe(struct platform_device *pdev)
dd = &dmadev->ddev;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dmadev->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dmadev->base))
+ return PTR_ERR(dmadev->base);
+
+ dmadev->clk = clk_get(&pdev->dev, "dma");
+ if (IS_ERR(dmadev->clk))
+ return PTR_ERR(dmadev->clk);
+
+ clk_prepare_enable(dmadev->clk);
+
dma_cap_set(DMA_SLAVE, dd->cap_mask);
dma_cap_set(DMA_CYCLIC, dd->cap_mask);
dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources;
@@ -396,6 +567,7 @@ static int jz4740_dma_probe(struct platform_device *pdev)
for (i = 0; i < dd->chancnt; i++) {
chan = &dmadev->chan[i];
+ chan->id = i;
chan->vchan.desc_free = jz4740_dma_desc_free;
vchan_init(&chan->vchan, dd);
}
@@ -404,16 +576,28 @@ static int jz4740_dma_probe(struct platform_device *pdev)
if (ret)
return ret;
+ irq = platform_get_irq(pdev, 0);
+ ret = request_irq(irq, jz4740_dma_irq, 0, dev_name(&pdev->dev), dmadev);
+ if (ret)
+ goto err_unregister;
+
platform_set_drvdata(pdev, dmadev);
return 0;
+
+err_unregister:
+ dma_async_device_unregister(dd);
+ return ret;
}
static int jz4740_dma_remove(struct platform_device *pdev)
{
struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+ free_irq(irq, dmadev);
dma_async_device_unregister(&dmadev->ddev);
+ clk_disable_unprepare(dmadev->clk);
return 0;
}
--
1.7.10.4

@ -0,0 +1,519 @@
From a20f54fdd500e5bccc9bd1ca4ac9f150addf2e64 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Sat, 27 Apr 2013 21:26:30 +0200
Subject: [PATCH 16/16] ASoC: jz4740: Use the generic dmaengine PCM driver
Since there is a dmaengine driver for the jz4740 DMA controller now we can use
the generic dmaengine PCM driver instead of a custom one.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
sound/soc/jz4740/Kconfig | 1 +
sound/soc/jz4740/jz4740-i2s.c | 48 +++----
sound/soc/jz4740/jz4740-pcm.c | 310 ++---------------------------------------
sound/soc/jz4740/jz4740-pcm.h | 20 ---
4 files changed, 27 insertions(+), 352 deletions(-)
delete mode 100644 sound/soc/jz4740/jz4740-pcm.h
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index 5351cba..29f76af 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -1,6 +1,7 @@
config SND_JZ4740_SOC
tristate "SoC Audio for Ingenic JZ4740 SoC"
depends on MACH_JZ4740 && SND_SOC
+ select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for codecs attached to
the JZ4740 I2S interface. You will also need to select the audio
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index 9a12644..5d04134 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -29,9 +29,12 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+#include <asm/mach-jz4740/dma.h>
#include "jz4740-i2s.h"
-#include "jz4740-pcm.h"
+
#define JZ_REG_AIC_CONF 0x00
#define JZ_REG_AIC_CTRL 0x04
@@ -89,8 +92,8 @@ struct jz4740_i2s {
struct clk *clk_aic;
struct clk *clk_i2s;
- struct jz4740_pcm_config pcm_config_playback;
- struct jz4740_pcm_config pcm_config_capture;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
};
static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
@@ -233,8 +236,6 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- enum jz4740_dma_width dma_width;
- struct jz4740_pcm_config *pcm_config;
unsigned int sample_size;
uint32_t ctrl;
@@ -243,11 +244,9 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
sample_size = 0;
- dma_width = JZ4740_DMA_WIDTH_8BIT;
break;
case SNDRV_PCM_FORMAT_S16:
sample_size = 1;
- dma_width = JZ4740_DMA_WIDTH_16BIT;
break;
default:
return -EINVAL;
@@ -260,22 +259,13 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
else
ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
-
- pcm_config = &i2s->pcm_config_playback;
- pcm_config->dma_config.dst_width = dma_width;
-
} else {
ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
-
- pcm_config = &i2s->pcm_config_capture;
- pcm_config->dma_config.src_width = dma_width;
}
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
- snd_soc_dai_set_dma_data(dai, substream, pcm_config);
-
return 0;
}
@@ -342,25 +332,19 @@ static int jz4740_i2s_resume(struct snd_soc_dai *dai)
static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
{
- struct jz4740_dma_config *dma_config;
+ struct snd_dmaengine_dai_dma_data *dma_data;
/* Playback */
- dma_config = &i2s->pcm_config_playback.dma_config;
- dma_config->src_width = JZ4740_DMA_WIDTH_32BIT;
- dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
- dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
- dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
- dma_config->mode = JZ4740_DMA_MODE_SINGLE;
- i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+ dma_data = &i2s->playback_dma_data;
+ dma_data->maxburst = 16;
+ dma_data->slave_id = JZ4740_DMA_TYPE_AIC_TRANSMIT;
+ dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
/* Capture */
- dma_config = &i2s->pcm_config_capture.dma_config;
- dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT;
- dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
- dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
- dma_config->flags = JZ4740_DMA_DST_AUTOINC;
- dma_config->mode = JZ4740_DMA_MODE_SINGLE;
- i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
+ dma_data = &i2s->capture_dma_data;
+ dma_data->maxburst = 16;
+ dma_data->slave_id = JZ4740_DMA_TYPE_AIC_RECEIVE;
+ dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
}
static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
@@ -371,6 +355,8 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
clk_enable(i2s->clk_aic);
jz4740_i2c_init_pcm_config(i2s);
+ dai->playback_dma_data = &i2s->playback_dma_data;
+ dai->capture_dma_data = &i2s->capture_dma_data;
conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
(8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
index 7100592..79fcade 100644
--- a/sound/soc/jz4740/jz4740-pcm.c
+++ b/sound/soc/jz4740/jz4740-pcm.c
@@ -19,38 +19,14 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/dma-mapping.h>
+#include <sound/dmaengine_pcm.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/mach-jz4740/dma.h>
-#include "jz4740-pcm.h"
-
-struct jz4740_runtime_data {
- unsigned long dma_period;
- dma_addr_t dma_start;
- dma_addr_t dma_pos;
- dma_addr_t dma_end;
-
- struct jz4740_dma_chan *dma;
-
- dma_addr_t fifo_addr;
-};
-
-/* identify hardware playback capabilities */
static const struct snd_pcm_hardware jz4740_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
-
- .rates = SNDRV_PCM_RATE_8000_48000,
- .channels_min = 1,
- .channels_max = 2,
.period_bytes_min = 16,
.period_bytes_max = 2 * PAGE_SIZE,
.periods_min = 2,
@@ -59,290 +35,22 @@ static const struct snd_pcm_hardware jz4740_pcm_hardware = {
.fifo_size = 32,
};
-static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
- struct snd_pcm_substream *substream)
-{
- unsigned long count;
-
- if (prtd->dma_pos == prtd->dma_end)
- prtd->dma_pos = prtd->dma_start;
-
- if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
- count = prtd->dma_end - prtd->dma_pos;
- else
- count = prtd->dma_period;
-
- jz4740_dma_disable(prtd->dma);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
- jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
- } else {
- jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
- jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
- }
-
- jz4740_dma_set_transfer_count(prtd->dma, count);
-
- prtd->dma_pos += count;
-
- jz4740_dma_enable(prtd->dma);
-}
-
-static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
- void *dev_id)
-{
- struct snd_pcm_substream *substream = dev_id;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
-
- snd_pcm_period_elapsed(substream);
-
- jz4740_pcm_start_transfer(prtd, substream);
-}
-
-static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct jz4740_pcm_config *config;
-
- config = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
- if (!config)
- return 0;
-
- if (!prtd->dma) {
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
- prtd->dma = jz4740_dma_request(substream, "PCM Capture");
- else
- prtd->dma = jz4740_dma_request(substream, "PCM Playback");
- }
-
- if (!prtd->dma)
- return -EBUSY;
-
- jz4740_dma_configure(prtd->dma, &config->dma_config);
- prtd->fifo_addr = config->fifo_addr;
-
- jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
-
- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
- runtime->dma_bytes = params_buffer_bytes(params);
-
- prtd->dma_period = params_period_bytes(params);
- prtd->dma_start = runtime->dma_addr;
- prtd->dma_pos = prtd->dma_start;
- prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
-
- return 0;
-}
-
-static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct jz4740_runtime_data *prtd = substream->runtime->private_data;
-
- snd_pcm_set_runtime_buffer(substream, NULL);
- if (prtd->dma) {
- jz4740_dma_free(prtd->dma);
- prtd->dma = NULL;
- }
-
- return 0;
-}
-
-static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct jz4740_runtime_data *prtd = substream->runtime->private_data;
-
- if (!prtd->dma)
- return -EBUSY;
-
- prtd->dma_pos = prtd->dma_start;
-
- return 0;
-}
-
-static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- jz4740_pcm_start_transfer(prtd, substream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- jz4740_dma_disable(prtd->dma);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
- unsigned long byte_offset;
- snd_pcm_uframes_t offset;
- struct jz4740_dma_chan *dma = prtd->dma;
-
- /* prtd->dma_pos points to the end of the current transfer. So by
- * subtracting prdt->dma_start we get the offset to the end of the
- * current period in bytes. By subtracting the residue of the transfer
- * we get the current offset in bytes. */
- byte_offset = prtd->dma_pos - prtd->dma_start;
- byte_offset -= jz4740_dma_get_residue(dma);
-
- offset = bytes_to_frames(runtime, byte_offset);
- if (offset >= runtime->buffer_size)
- offset = 0;
-
- return offset;
-}
-
-static int jz4740_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd;
-
- prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
- if (prtd == NULL)
- return -ENOMEM;
-
- snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
-
- runtime->private_data = prtd;
-
- return 0;
-}
-
-static int jz4740_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct jz4740_runtime_data *prtd = runtime->private_data;
-
- kfree(prtd);
-
- return 0;
-}
-
-static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
- struct vm_area_struct *vma)
-{
- return remap_pfn_range(vma, vma->vm_start,
- substream->dma_buffer.addr >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot);
-}
-
-static struct snd_pcm_ops jz4740_pcm_ops = {
- .open = jz4740_pcm_open,
- .close = jz4740_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = jz4740_pcm_hw_params,
- .hw_free = jz4740_pcm_hw_free,
- .prepare = jz4740_pcm_prepare,
- .trigger = jz4740_pcm_trigger,
- .pointer = jz4740_pcm_pointer,
- .mmap = jz4740_pcm_mmap,
-};
-
-static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
-{
- struct snd_pcm_substream *substream = pcm->streams[stream].substream;
- struct snd_dma_buffer *buf = &substream->dma_buffer;
- size_t size = jz4740_pcm_hardware.buffer_bytes_max;
-
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
-
- buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
- &buf->addr, GFP_KERNEL);
- if (!buf->area)
- return -ENOMEM;
-
- buf->bytes = size;
-
- return 0;
-}
-
-static void jz4740_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_pcm_substream *substream;
- struct snd_dma_buffer *buf;
- int stream;
-
- for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) {
- substream = pcm->streams[stream].substream;
- if (!substream)
- continue;
-
- buf = &substream->dma_buffer;
- if (!buf->area)
- continue;
-
- dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area,
- buf->addr);
- buf->area = NULL;
- }
-}
-
-static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32);
-
-static int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_card *card = rtd->card->snd_card;
- struct snd_pcm *pcm = rtd->pcm;
- int ret = 0;
-
- if (!card->dev->dma_mask)
- card->dev->dma_mask = &jz4740_pcm_dmamask;
-
- if (!card->dev->coherent_dma_mask)
- card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
- ret = jz4740_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_PLAYBACK);
- if (ret)
- goto err;
- }
-
- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
- ret = jz4740_pcm_preallocate_dma_buffer(pcm,
- SNDRV_PCM_STREAM_CAPTURE);
- if (ret)
- goto err;
- }
-
-err:
- return ret;
-}
-
-static struct snd_soc_platform_driver jz4740_soc_platform = {
- .ops = &jz4740_pcm_ops,
- .pcm_new = jz4740_pcm_new,
- .pcm_free = jz4740_pcm_free,
+static const struct snd_dmaengine_pcm_config jz4740_dmaengine_pcm_config = {
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+ .pcm_hardware = &jz4740_pcm_hardware,
+ .prealloc_buffer_size = 256 * PAGE_SIZE,
};
static int jz4740_pcm_probe(struct platform_device *pdev)
{
- return snd_soc_register_platform(&pdev->dev, &jz4740_soc_platform);
+ return snd_dmaengine_pcm_register(&pdev->dev,
+ &jz4740_dmaengine_pcm_config,
+ SND_DMAENGINE_PCM_FLAG_COMPAT);
}
static int jz4740_pcm_remove(struct platform_device *pdev)
{
- snd_soc_unregister_platform(&pdev->dev);
+ snd_dmaengine_pcm_unregister(&pdev->dev);
return 0;
}
diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h
deleted file mode 100644
index 1220cbb..0000000
--- a/sound/soc/jz4740/jz4740-pcm.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * 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.
- */
-
-#ifndef _JZ4740_PCM_H
-#define _JZ4740_PCM_H
-
-#include <linux/dma-mapping.h>
-#include <asm/mach-jz4740/dma.h>
-
-
-struct jz4740_pcm_config {
- struct jz4740_dma_config dma_config;
- phys_addr_t fifo_addr;
-};
-
-#endif
--
1.7.10.4
Loading…
Cancel
Save