All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4, 3/7] cdma-sms: Add CDMA SMS Support
       [not found] <Y>
@ 2011-01-18 22:54 ` Lei Yu
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: Lei Yu @ 2011-01-18 22:54 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 30647 bytes --]

---
 Makefile.am        |    3 +-
 src/cdma-smsutil.c |  735 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/cdma-smsutil.h |  299 +++++++++++++++++++++
 3 files changed, 1036 insertions(+), 1 deletions(-)
 create mode 100644 src/cdma-smsutil.c
 create mode 100644 src/cdma-smsutil.h

diff --git a/Makefile.am b/Makefile.am
index da59be7..bd8c058 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -354,7 +354,8 @@ src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) src/ofono.ver \
 			src/nettime.c src/stkagent.c src/stkagent.h \
 			src/simfs.c src/simfs.h src/audio-settings.c \
 			src/smsagent.c src/smsagent.h src/ctm.c \
-			src/cdma-voicecall.c
+			src/cdma-voicecall.c src/cdma-smsutil.h \
+			src/cdma-smsutil.c
 
 src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
 
diff --git a/src/cdma-smsutil.c b/src/cdma-smsutil.c
new file mode 100644
index 0000000..283e1ed
--- /dev/null
+++ b/src/cdma-smsutil.c
@@ -0,0 +1,735 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "cdma-smsutil.h"
+
+#define uninitialized_var(x) x = x
+
+enum cdma_sms_rec_flag {
+	CDMA_SMS_REC_FLAG_MANDATORY =	1,
+};
+
+typedef gboolean (*rec_handler)(const guint8 *, guint8, void *);
+
+struct simple_iter {
+	guint8 max;
+	const guint8 *pdu;
+	guint8 pos;
+	guint8 id;
+	guint8 len;
+	const guint8 *data;
+};
+
+static void simple_iter_init(struct simple_iter *iter,
+				const guint8 *pdu, guint8 len)
+{
+	iter->pdu = pdu;
+	iter->max = len;
+	iter->pos = 0;
+	iter->id = 0;
+	iter->len = 0;
+	iter->data = NULL;
+}
+
+static gboolean simple_iter_next(struct simple_iter *iter)
+{
+	const guint8 *pdu = iter->pdu + iter->pos;
+	const guint8 *end = iter->pdu + iter->max;
+	guint8 id;
+	guint8 len;
+
+	if (pdu == end)
+		return FALSE;
+
+	id = *pdu;
+	pdu++;
+
+	if (pdu == end)
+		return FALSE;
+
+	len = *pdu++;
+
+	if (pdu + len > end)
+		return FALSE;
+
+	iter->id = id;
+	iter->len = len;
+	iter->data = pdu;
+
+	iter->pos = pdu + len - iter->pdu;
+
+	return TRUE;
+}
+
+static guint8 simple_iter_get_id(struct simple_iter *iter)
+{
+	return iter->id;
+}
+
+static guint8 simple_iter_get_length(struct simple_iter *iter)
+{
+	return iter->len;
+}
+
+static const guint8 *simple_iter_get_data(struct simple_iter *iter)
+{
+	return iter->data;
+}
+
+static inline void set_bitmap(guint32 *bitmap, guint8 pos)
+{
+	*bitmap = *bitmap | (1 << pos);
+}
+
+/* Unpacks the byte stream. The field has to be <= 8 bits. */
+static guint8 bit_field_unpack(const guint8 *buf, guint16 offset, guint8 nbit)
+{
+	guint8 bit_pos;
+	guint8 val = 0;
+	const guint8 *pdu;
+
+	pdu = buf + (offset >> 3);
+	bit_pos = 8 - (offset & 0x7);
+
+	/* Field to be extracted is within current byte */
+	if (nbit <= bit_pos)
+		return (*pdu >> (bit_pos - nbit)) & ((1 << nbit) - 1);
+
+	/* Field to be extracted crossing two bytes */
+	val = *pdu & ((1 << bit_pos) - 1);
+	nbit -= bit_pos;
+	pdu++;
+
+	return (val << nbit) | (*pdu >> (8 - nbit));
+}
+
+/* Convert CDMA DTMF digits into a string */
+static gboolean dtmf_to_ascii(char *buf, const guint8 *addr,
+					guint8 num_fields)
+{
+	/*
+	 * Mapping from binary DTMF code to the digit it represents.
+	 * As defined in Table 2.7.1.3.2.4-4 of 3GPP2 C.S0005-E v2.0.
+	 * Note, 0 is NOT a valid value and not mapped to
+	 * any valid DTMF digit.
+	 */
+	static const char dtmf_digits[13] = {0, '1', '2', '3', '4', '5', '6',
+						'7', '8', '9', '0', '*', '#'};
+	guint8 index;
+	guint8 value;
+
+	for (index = 0; index < num_fields; index++) {
+		if (addr[index] == 0 || addr[index] > 12)
+			return FALSE;  /* Invalid digit in address field */
+
+		value = addr[index];
+		buf[index] = dtmf_digits[value];
+	}
+
+	buf[index] = 0; /* Make it NULL terminated string */
+
+	return TRUE;
+}
+
+const char *cdma_sms_address_to_string(const struct cdma_sms_address *addr)
+{
+	static char buf[CDMA_SMS_MAX_ADDR_FIELDS + 1];
+
+	/* TODO: Only support CDMA_SMS_DIGIT_MODE_4BIT_DTMF currently */
+	switch (addr->digit_mode) {
+	case CDMA_SMS_DIGIT_MODE_4BIT_DTMF:
+		if (dtmf_to_ascii(buf, addr->address,
+					addr->num_fields) == TRUE)
+			return buf;
+		else
+			return NULL;
+	case CDMA_SMS_DIGIT_MODE_8BIT_ASCII:
+		return NULL;
+	}
+
+	return NULL;
+}
+
+/* Decode Teleservice ID */
+static gboolean cdma_sms_decode_teleservice(const guint8 *buf, guint8 len,
+								void *data)
+{
+	enum cdma_sms_teleservice_id *id = data;
+
+	*id = bit_field_unpack(buf, 0, 8) << 8 |
+				bit_field_unpack(buf, 8, 8);
+
+	switch (*id) {
+	case CDMA_SMS_TELESERVICE_ID_CMT91:
+	case CDMA_SMS_TELESERVICE_ID_WPT:
+	case CDMA_SMS_TELESERVICE_ID_WMT:
+	case CDMA_SMS_TELESERVICE_ID_VMN:
+	case CDMA_SMS_TELESERVICE_ID_WAP:
+	case CDMA_SMS_TELESERVICE_ID_WEMT:
+	case CDMA_SMS_TELESERVICE_ID_SCPT:
+	case CDMA_SMS_TELESERVICE_ID_CATPT:
+		return TRUE;
+	}
+
+	return FALSE; /* Invalid teleservice type */
+}
+
+/* Decode Address parameter record */
+static gboolean cdma_sms_decode_addr(const guint8 *buf, guint8 len,
+							void *data)
+{
+	struct cdma_sms_address *addr = data;
+	guint16 bit_offset = 0;
+	guint8  chari_len;
+	guint16 total_num_bits = len * 8;
+	guint8  index;
+
+	addr->digit_mode = bit_field_unpack(buf, bit_offset, 1);
+	bit_offset += 1;
+
+	addr->number_mode = bit_field_unpack(buf, bit_offset, 1);
+	bit_offset += 1;
+
+	if (addr->digit_mode == CDMA_SMS_DIGIT_MODE_8BIT_ASCII) {
+		if (addr->number_mode == CDMA_SMS_NUM_MODE_DIGIT)
+			addr->digi_num_type =
+				bit_field_unpack(buf, bit_offset, 3);
+		else
+			addr->data_nw_num_type =
+				bit_field_unpack(buf, bit_offset, 3);
+
+		bit_offset += 3;
+
+		if (addr->number_mode == CDMA_SMS_NUM_MODE_DIGIT) {
+			if (bit_offset + 4 > total_num_bits)
+				return FALSE;
+
+			addr->number_plan =
+				bit_field_unpack(buf, bit_offset, 4);
+			bit_offset += 4;
+		}
+	}
+
+	if (bit_offset + 8 > total_num_bits)
+		return FALSE;
+
+	addr->num_fields = bit_field_unpack(buf, bit_offset, 8);
+	bit_offset += 8;
+
+	if (addr->digit_mode == CDMA_SMS_DIGIT_MODE_4BIT_DTMF)
+		chari_len = 4;
+	else
+		chari_len = 8;
+
+	if ((bit_offset + chari_len * addr->num_fields) > total_num_bits)
+		return FALSE;
+
+	for (index = 0; index < addr->num_fields; index++) {
+		addr->address[index] = bit_field_unpack(buf,
+							bit_offset,
+							chari_len);
+		bit_offset += chari_len;
+	}
+
+	return TRUE;
+}
+
+static char *decode_text_7bit_ascii(const struct cdma_sms_ud *ud)
+{
+	char *buf;
+
+	buf = g_new(char, ud->num_fields + 1);
+	if (buf == NULL)
+		return NULL;
+
+	memcpy(buf, ud->chari, ud->num_fields);
+	buf[ud->num_fields] = 0; /* Make it NULL terminated string */
+
+	return buf;
+}
+
+char *cdma_sms_decode_text(const struct cdma_sms_ud *ud)
+{
+	switch (ud->msg_encoding) {
+	case CDMA_SMS_MSG_ENCODING_OCTET:
+	case CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG:
+		return NULL; /* TODO */
+	case CDMA_SMS_MSG_ENCODING_7BIT_ASCII:
+		return decode_text_7bit_ascii(ud);
+	case CDMA_SMS_MSG_ENCODING_IA5:
+	case CDMA_SMS_MSG_ENCODING_UNICODE:
+	case CDMA_SMS_MSG_ENCODING_SHIFT_JIS:
+	case CDMA_SMS_MSG_ENCODING_KOREAN:
+	case CDMA_SMS_MSG_ENCODING_LATIN_HEBREW:
+	case CDMA_SMS_MSG_ENCODING_LATIN:
+	case CDMA_SMS_MSG_ENCODING_GSM_7BIT:
+	case CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING:
+		return NULL; /* TODO */
+	}
+
+	return NULL;
+}
+
+/* Decode User Data */
+static gboolean cdma_sms_decode_ud(const guint8 *buf, guint8 len, void *data)
+{
+	guint16 bit_offset = 0;
+	guint8  chari_len = 0;
+	guint16 total_num_bits = len * 8;
+	guint8  index;
+	enum cdma_sms_msg_encoding  msg_encoding;
+	struct cdma_sms_ud *ud = data;
+
+	if (total_num_bits < 13)
+		return FALSE;
+
+	msg_encoding = bit_field_unpack(buf, bit_offset, 5);
+	ud->msg_encoding =  msg_encoding;
+	bit_offset += 5;
+
+	if (msg_encoding == CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG ||
+		msg_encoding == CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING) {
+		/*
+		 * Skip message type field for now.
+		 * TODO: Add support for message type field.
+		 */
+		bit_offset += 8;
+	}
+
+	if (bit_offset + 8 > total_num_bits)
+		return FALSE;
+
+	ud->num_fields = bit_field_unpack(buf, bit_offset, 8);
+	bit_offset += 8;
+
+	switch (msg_encoding) {
+	case CDMA_SMS_MSG_ENCODING_OCTET:
+		chari_len = 8;
+		break;
+	case CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG:
+		return FALSE; /* TODO */
+	case CDMA_SMS_MSG_ENCODING_7BIT_ASCII:
+	case CDMA_SMS_MSG_ENCODING_IA5:
+		chari_len = 7;
+		break;
+	case CDMA_SMS_MSG_ENCODING_UNICODE:
+	case CDMA_SMS_MSG_ENCODING_SHIFT_JIS:
+	case CDMA_SMS_MSG_ENCODING_KOREAN:
+		return FALSE; /* TODO */
+	case CDMA_SMS_MSG_ENCODING_LATIN_HEBREW:
+	case CDMA_SMS_MSG_ENCODING_LATIN:
+		chari_len = 8;
+		break;
+	case CDMA_SMS_MSG_ENCODING_GSM_7BIT:
+		chari_len = 7;
+		break;
+	case CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING:
+		return FALSE; /* TODO */
+	}
+
+	/* TODO: Add support for all other encoding types */
+	if (chari_len == 0)
+		return FALSE;
+
+	if (bit_offset + chari_len * ud->num_fields > total_num_bits)
+		return FALSE;
+
+	for (index = 0; index < ud->num_fields; index++) {
+		ud->chari[index] = bit_field_unpack(buf,
+						bit_offset,
+						chari_len);
+		bit_offset += chari_len;
+	}
+
+	return TRUE;
+}
+
+/* Decode Message Identifier */
+static gboolean cdma_sms_decode_message_id(const guint8 *buf, guint8 len,
+						void *data)
+{
+	struct cdma_sms_identifier *id = data;
+
+	if (len != 3)
+		return FALSE;
+
+	id->msg_type = bit_field_unpack(buf, 0, 4);
+
+	if (id->msg_type <= 0 ||
+			id->msg_type > CDMA_SMS_MSG_TYPE_SUBMIT_REPORT)
+		return FALSE; /* Invalid message type */
+
+	id->msg_id = (bit_field_unpack(buf, 4, 8) << 8) |
+			bit_field_unpack(buf, 12, 8);
+
+	id->header_ind = bit_field_unpack(buf, 20, 1);
+
+	return TRUE;
+}
+
+static gboolean find_and_decode(struct simple_iter *iter, guint8 rec_id,
+					rec_handler handler, void *data)
+{
+	guint8 id;
+	guint8 len;
+	const guint8 *buf;
+
+	while (simple_iter_next(iter) == TRUE) {
+		id = simple_iter_get_id(iter);
+		if (id != rec_id)
+			continue;
+
+		len = simple_iter_get_length(iter);
+		buf = simple_iter_get_data(iter);
+
+		return handler(buf, len, data);
+	}
+
+	return FALSE;
+}
+
+static rec_handler subparam_handler_for_id(enum cdma_sms_subparam_id id)
+{
+	switch (id) {
+	case CDMA_SMS_SUBPARAM_ID_MESSAGE_ID:
+		return cdma_sms_decode_message_id;
+	case CDMA_SMS_SUBPARAM_ID_USER_DATA:
+		return cdma_sms_decode_ud;
+	case CDMA_SMS_SUBPARAM_ID_USER_RESPONSE_CODE:
+	case CDMA_SMS_SUBPARAM_ID_MC_TIME_STAMP:
+	case CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_ABSOLUTE:
+	case CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_RELATIVE:
+	case CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE:
+	case CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_RELATIVE:
+	case CDMA_SMS_SUBPARAM_ID_PRIORITY_INDICATOR:
+	case CDMA_SMS_SUBPARAM_ID_PRIVACY_INDICATOR:
+	case CDMA_SMS_SUBPARAM_ID_REPLY_OPTION:
+	case CDMA_SMS_SUBPARAM_ID_NUMBER_OF_MESSAGES:
+	case CDMA_SMS_SUBPARAM_ID_ALERT_ON_MESSAGE_DELIVERY:
+	case CDMA_SMS_SUBPARAM_ID_LANGUAGE_INDICATOR:
+	case CDMA_SMS_SUBPARAM_ID_CALL_BACK_NUMBER:
+	case CDMA_SMS_SUBPARAM_ID_MESSAGE_DISPLAY_MODE:
+	case CDMA_SMS_SUBPARAM_ID_MULTIPLE_ENCODING_USER_DATA:
+	case CDMA_SMS_SUBPARAM_ID_MESSAGE_DEPOSIT_INDEX:
+	case CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_DATA:
+	case CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_RESULT:
+	case CDMA_SMS_SUBPARAM_ID_MESSAGE_STATUS:
+	case CDMA_SMS_SUBPARAM_ID_TP_FAILURE_CAUSE:
+	case CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN:
+	case CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN_ACK:
+		return NULL; /* TODO */
+	}
+
+	return NULL;
+}
+
+struct subparam_handler_entry {
+	enum cdma_sms_subparam_id id;
+	int flags;
+	gboolean found;
+	void *data;
+};
+
+static gboolean decode_subparams(struct simple_iter *iter, guint32 *bitmap,
+					void *data, ...)
+{
+	GSList *entries = NULL;
+	GSList *l;
+	va_list args;
+	gboolean decode_result = TRUE;
+
+	va_start(args, data);
+
+	while (data != NULL) {
+		struct subparam_handler_entry *entry;
+
+		entry = g_new0(struct subparam_handler_entry, 1);
+
+		entry->data = data;
+		entry->id = va_arg(args, enum cdma_sms_subparam_id);
+		entry->flags = va_arg(args, int);
+
+		data = va_arg(args, void *);
+		entries = g_slist_prepend(entries, entry);
+	}
+
+	va_end(args);
+
+	entries = g_slist_reverse(entries);
+
+	l = entries;
+	while (simple_iter_next(iter) == TRUE) {
+		rec_handler handler;
+		struct subparam_handler_entry *entry;
+		guint8 subparam_len;
+		const guint8 *subparam_buf;
+		GSList *l2;
+
+		for (l2 = l; l2; l2 = l2->next) {
+			entry = l2->data;
+
+			if (simple_iter_get_id(iter) == entry->id)
+				break;
+		}
+
+		/* Ignore unexpected subparameter record */
+		if (l2 == NULL)
+			continue;
+
+		entry->found = TRUE;
+
+		subparam_len = simple_iter_get_length(iter);
+		subparam_buf = simple_iter_get_data(iter);
+
+		handler = subparam_handler_for_id(entry->id);
+
+		decode_result = handler(subparam_buf,
+					subparam_len,
+					entry->data);
+		if (decode_result == FALSE)
+			break; /* Stop if decoding failed */
+
+		set_bitmap(bitmap, entry->id);
+	}
+
+	for (; l; l = l->next) {
+		struct subparam_handler_entry *entry = l->data;
+
+		if ((entry->flags & CDMA_SMS_REC_FLAG_MANDATORY) &&
+			(entry->found == FALSE)) {
+			decode_result = FALSE;
+			break;
+		}
+	}
+
+	g_slist_foreach(entries, (GFunc) g_free, NULL);
+	g_slist_free(entries);
+
+	return decode_result;
+}
+
+/* Decode WMT */
+static gboolean cdma_sms_decode_wmt(struct simple_iter *iter,
+					struct cdma_sms_bearer_data *bd)
+{
+	switch (bd->id.msg_type) {
+	case CDMA_SMS_MSG_TYPE_RESERVED:
+		return FALSE; /* Invalid */
+	case CDMA_SMS_MSG_TYPE_DELIVER:
+		/*
+		 * WMT DELIVER, table 4.3.4-1 of C.S0015-B v2.0
+		 * TODO: Not all optional subparameters supported.
+		 */
+		return decode_subparams(iter,
+					&bd->subparam_bitmap,
+					&bd->wmt_deliver.ud,
+					CDMA_SMS_SUBPARAM_ID_USER_DATA,
+					0,
+					NULL);
+		break;
+	case CDMA_SMS_MSG_TYPE_SUBMIT:
+	case CDMA_SMS_MSG_TYPE_CANCEL:
+		return FALSE; /* Invalid for MT WMT */
+	case CDMA_SMS_MSG_TYPE_DELIVER_ACK:
+	case CDMA_SMS_MSG_TYPE_USER_ACK:
+	case CDMA_SMS_MSG_TYPE_READ_ACK:
+		return FALSE; /* TODO: Not supported yet */
+	case CDMA_SMS_MSG_TYPE_DELIVER_REPORT:
+	case CDMA_SMS_MSG_TYPE_SUBMIT_REPORT:
+		return FALSE; /* Invalid for MT WMT */
+	}
+
+	return FALSE;
+}
+
+static gboolean p2p_decode_bearer_data(const guint8 *buf, guint8 len,
+					enum cdma_sms_teleservice_id tele_id,
+					struct cdma_sms_bearer_data *bd)
+{
+	struct simple_iter iter;
+
+	simple_iter_init(&iter, buf, len);
+
+	/* Message Identifier is mandatory, * Section 4 of C.S0015-B v2.0 */
+	if (find_and_decode(&iter,
+				CDMA_SMS_SUBPARAM_ID_MESSAGE_ID,
+				cdma_sms_decode_message_id,
+				&bd->id) != TRUE)
+		return FALSE;
+
+	set_bitmap(&bd->subparam_bitmap, CDMA_SMS_SUBPARAM_ID_MESSAGE_ID);
+
+	simple_iter_init(&iter, buf, len);
+
+	switch (tele_id) {
+	case CDMA_SMS_TELESERVICE_ID_CMT91:
+	case CDMA_SMS_TELESERVICE_ID_WPT:
+		return FALSE; /* TODO */
+	case CDMA_SMS_TELESERVICE_ID_WMT:
+		return cdma_sms_decode_wmt(&iter, bd);
+	case CDMA_SMS_TELESERVICE_ID_VMN:
+	case CDMA_SMS_TELESERVICE_ID_WAP:
+	case CDMA_SMS_TELESERVICE_ID_WEMT:
+	case CDMA_SMS_TELESERVICE_ID_SCPT:
+	case CDMA_SMS_TELESERVICE_ID_CATPT:
+		return FALSE; /* TODO */
+	}
+
+	return FALSE;
+}
+
+/* Decode Bearer Data */
+static gboolean cdma_sms_decode_bearer_data(const guint8 *buf, guint8 len,
+								void *data)
+{
+	struct cdma_sms *msg = data;
+
+	switch (msg->type) {
+	case CDMA_SMS_TP_MSG_TYPE_P2P:
+		return p2p_decode_bearer_data(buf, len,
+						msg->p2p_msg.teleservice_id,
+						&msg->p2p_msg.bd);
+	case CDMA_SMS_TP_MSG_TYPE_BCAST:
+		return FALSE; /* TODO */
+	case CDMA_SMS_TP_MSG_TYPE_ACK:
+		return FALSE; /* Invalid */
+	}
+
+	return FALSE;
+}
+
+static rec_handler param_handler_for_id(enum cdma_sms_param_id id,
+						struct cdma_sms *incoming,
+						void **data)
+{
+	if (incoming->type != CDMA_SMS_TP_MSG_TYPE_P2P)
+		return NULL; /* TODO: Other types not supported yet */
+
+	switch (id) {
+	case CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER:
+		*data = &incoming->p2p_msg.teleservice_id;
+		return cdma_sms_decode_teleservice;
+	case CDMA_SMS_PARAM_ID_SERVICE_CATEGORY:
+		return NULL; /* TODO */
+	case CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS:
+		*data = &incoming->p2p_msg.oaddr;
+		return cdma_sms_decode_addr;
+	case CDMA_SMS_PARAM_ID_ORIGINATING_SUBADDRESS:
+	case CDMA_SMS_PARAM_ID_DESTINATION_ADDRESS:
+	case CDMA_SMS_PARAM_ID_DESTINATION_SUBADDRESS:
+	case CDMA_SMS_PARAM_ID_BEARER_REPLY_OPTION:
+	case CDMA_SMS_PARAM_ID_CAUSE_CODE:
+		return NULL; /* TODO */
+	case CDMA_SMS_PARAM_ID_BEARER_DATA:
+		*data = incoming;
+		return cdma_sms_decode_bearer_data;
+	}
+
+	return NULL;
+}
+
+static gboolean cdma_sms_p2p_decode(const guint8 *pdu, guint8 len,
+					struct cdma_sms *incoming)
+{
+	struct simple_iter iter;
+
+	simple_iter_init(&iter, pdu, len);
+
+	/*
+	 * Teleservice Identifier is mandatory,
+	 * Table 3.4.2.1-1 of C.S0015-B v2.0
+	 */
+	if (find_and_decode(&iter,
+				CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER,
+				cdma_sms_decode_teleservice,
+				&incoming->p2p_msg.teleservice_id) != TRUE)
+		return FALSE;
+
+	set_bitmap(&incoming->p2p_msg.param_bitmap,
+			CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER);
+
+	simple_iter_init(&iter, pdu, len);
+
+	while (simple_iter_next(&iter) == TRUE) {
+		rec_handler handler;
+		enum cdma_sms_param_id rec_id;
+		guint8 rec_len;
+		const guint8 *rec_buf;
+		void *dataobj;
+
+		rec_id = simple_iter_get_id(&iter);
+		if (rec_id == CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER)
+			continue;
+
+		rec_len = simple_iter_get_length(&iter);
+		rec_buf = simple_iter_get_data(&iter);
+
+		handler = param_handler_for_id(rec_id, incoming, &dataobj);
+		if (handler != NULL) {
+			if (handler(rec_buf, rec_len, dataobj) == FALSE)
+				return FALSE;
+
+			set_bitmap(&incoming->p2p_msg.param_bitmap, rec_id);
+		}
+	}
+
+	/*
+	 * Originating Address is mandatory field,
+	 * Table 3.4.2.1-1 of C.S0015-B v2.0
+	 */
+	if ((incoming->p2p_msg.param_bitmap &
+			(1 << CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS)) == 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+gboolean cdma_sms_decode(const guint8 *pdu, guint8 len,
+				struct cdma_sms *incoming)
+{
+	incoming->type = bit_field_unpack(pdu, 0, 8);
+	pdu += 1;
+	len -= 1;
+
+	switch (incoming->type) {
+	case CDMA_SMS_TP_MSG_TYPE_P2P:
+		return cdma_sms_p2p_decode(pdu, len, incoming);
+	case CDMA_SMS_TP_MSG_TYPE_BCAST:
+	case CDMA_SMS_TP_MSG_TYPE_ACK:
+		/* TODO: Not supported yet */
+		return FALSE;
+	}
+
+	return FALSE;
+}
diff --git a/src/cdma-smsutil.h b/src/cdma-smsutil.h
new file mode 100644
index 0000000..0ac6228
--- /dev/null
+++ b/src/cdma-smsutil.h
@@ -0,0 +1,299 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define CDMA_SMS_MAX_ADDR_FIELDS 256
+#define CDMA_SMS_UD_LEN 512
+
+/* 3GPP2 C.S0015-B v2.0, Table 3.4-1 */
+enum cdma_sms_tp_msg_type {
+	CDMA_SMS_TP_MSG_TYPE_P2P =	0,
+	CDMA_SMS_TP_MSG_TYPE_BCAST =	1,
+	CDMA_SMS_TP_MSG_TYPE_ACK =	2
+};
+
+/*
+ * 3GPP2 X.S0004-550-E, Section 2.256
+ * Only supported by 3GPP2 C.S0015-B v2.0 Section 3.4.3.1 listed.
+ */
+enum cdma_sms_teleservice_id {
+	CDMA_SMS_TELESERVICE_ID_CMT91 =	4096,
+	CDMA_SMS_TELESERVICE_ID_WPT =	4097,
+	CDMA_SMS_TELESERVICE_ID_WMT =	4098,
+	CDMA_SMS_TELESERVICE_ID_VMN =	4099,
+	CDMA_SMS_TELESERVICE_ID_WAP =	4100,
+	CDMA_SMS_TELESERVICE_ID_WEMT =	4101,
+	CDMA_SMS_TELESERVICE_ID_SCPT =	4102,
+	CDMA_SMS_TELESERVICE_ID_CATPT =	4103
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */
+enum cdma_sms_num_mode {
+	CDMA_SMS_NUM_MODE_DIGIT =	0,
+	CDMA_SMS_NUM_MODE_DATA_NW =	1
+};
+
+/* 3GPP2 C.S0005-E v2.0 Table 2.7.1.3.2.4-2 */
+enum cdma_sms_digi_num_type {
+	CDMA_SMS_DIGI_NUM_TYPE_UNKNOWN =	0,
+	CDMA_SMS_DIGI_NUM_TYPE_INTERNATIONAL =	1,
+	CDMA_SMS_DIGI_NUM_TYPE_NATIONAL =	2,
+	CDMA_SMS_DIGI_NUM_TYPE_NETWORK =	3,
+	CDMA_SMS_DIGI_NUM_TYPE_SUBSCRIBER =	4,
+	CDMA_SMS_DIGI_NUM_TYPE_RESERVED1 = 	5,
+	CDMA_SMS_DIGI_NUM_TYPE_ABBREVIATED =	6,
+	CDMA_SMS_DIGI_NUM_TYPE_RESERVED2 = 	7
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 3.4.3.3-1 */
+enum cdma_sms_data_nw_num_type {
+	CDMA_SMS_DATA_NW_NUM_TYPE_UNKNOWN =			0,
+	CDMA_SMS_DATA_NW_NUM_TYPE_INTERNET_PROTOCOL =		1,
+	CDMA_SMS_DATA_NW_NUM_TYPE_INTERNET_EMAIL_ADDRESS =	2,
+	/* All Other Values Reserved */
+};
+
+/* 3GPP2 C.S0005-E v2.0 Table 2.7.1.3.2.4-3 */
+enum cdma_sms_numbering_plan {
+	CDMA_SMS_NUMBERING_PLAN_UNKNOWN =	0,
+	CDMA_SMS_NUMBERING_PLAN_ISDN =		1,
+	CDMA_SMS_NUMBERING_PLAN_DATA =		3,
+	CDMA_SMS_NUMBERING_PLAN_TELEX =		4,
+	CDMA_SMS_NUMBERING_PLAN_PRIVATE =	9,
+	CDMA_SMS_NUMBERING_PLAN_RESERVED =	15
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 4.5.1-1 */
+enum cdma_sms_msg_type {
+	CDMA_SMS_MSG_TYPE_RESERVED =		0,
+	CDMA_SMS_MSG_TYPE_DELIVER =		1,
+	CDMA_SMS_MSG_TYPE_SUBMIT =		2,
+	CDMA_SMS_MSG_TYPE_CANCEL =		3,
+	CDMA_SMS_MSG_TYPE_DELIVER_ACK =		4,
+	CDMA_SMS_MSG_TYPE_USER_ACK =		5,
+	CDMA_SMS_MSG_TYPE_READ_ACK =		6,
+	CDMA_SMS_MSG_TYPE_DELIVER_REPORT =	7,
+	CDMA_SMS_MSG_TYPE_SUBMIT_REPORT =	8,
+};
+
+/* C.R1001-G_v1.0 Table 9.1-1 */
+enum cdma_sms_msg_encoding {
+	CDMA_SMS_MSG_ENCODING_OCTET =			0,
+	CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG =	1,
+	CDMA_SMS_MSG_ENCODING_7BIT_ASCII =		2,
+	CDMA_SMS_MSG_ENCODING_IA5 =			3,
+	CDMA_SMS_MSG_ENCODING_UNICODE =			4,
+	CDMA_SMS_MSG_ENCODING_SHIFT_JIS =		5,
+	CDMA_SMS_MSG_ENCODING_KOREAN =			6,
+	CDMA_SMS_MSG_ENCODING_LATIN_HEBREW =		7,
+	CDMA_SMS_MSG_ENCODING_LATIN =			8,
+	CDMA_SMS_MSG_ENCODING_GSM_7BIT =		9,
+	CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING =		10
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 3.4.3-1 */
+enum cdma_sms_param_id {
+	CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER  =	0x00,
+	CDMA_SMS_PARAM_ID_SERVICE_CATEGORY =		0x01,
+	CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS =		0x02,
+	CDMA_SMS_PARAM_ID_ORIGINATING_SUBADDRESS =	0x03,
+	CDMA_SMS_PARAM_ID_DESTINATION_ADDRESS =		0x04,
+	CDMA_SMS_PARAM_ID_DESTINATION_SUBADDRESS =	0x05,
+	CDMA_SMS_PARAM_ID_BEARER_REPLY_OPTION =		0x06,
+	CDMA_SMS_PARAM_ID_CAUSE_CODE =			0x07,
+	CDMA_SMS_PARAM_ID_BEARER_DATA =			0x08
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 4.5-1 */
+enum cdma_sms_subparam_id {
+	CDMA_SMS_SUBPARAM_ID_MESSAGE_ID =			0x00,
+	CDMA_SMS_SUBPARAM_ID_USER_DATA =			0x01,
+	CDMA_SMS_SUBPARAM_ID_USER_RESPONSE_CODE =		0x02,
+	CDMA_SMS_SUBPARAM_ID_MC_TIME_STAMP =			0x03,
+	CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_ABSOLUTE =		0x04,
+	CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_RELATIVE = 	0x05,
+	CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE =	0x06,
+	CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_RELATIVE =	0x07,
+	CDMA_SMS_SUBPARAM_ID_PRIORITY_INDICATOR =		0x08,
+	CDMA_SMS_SUBPARAM_ID_PRIVACY_INDICATOR =		0x09,
+	CDMA_SMS_SUBPARAM_ID_REPLY_OPTION =			0x0A,
+	CDMA_SMS_SUBPARAM_ID_NUMBER_OF_MESSAGES =		0x0B,
+	CDMA_SMS_SUBPARAM_ID_ALERT_ON_MESSAGE_DELIVERY =	0x0C,
+	CDMA_SMS_SUBPARAM_ID_LANGUAGE_INDICATOR =		0x0D,
+	CDMA_SMS_SUBPARAM_ID_CALL_BACK_NUMBER =			0x0E,
+	CDMA_SMS_SUBPARAM_ID_MESSAGE_DISPLAY_MODE =		0x0F,
+	CDMA_SMS_SUBPARAM_ID_MULTIPLE_ENCODING_USER_DATA =	0x10,
+	CDMA_SMS_SUBPARAM_ID_MESSAGE_DEPOSIT_INDEX =		0x11,
+	CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_DATA =	0x12,
+	CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_RESULT =	0x13,
+	CDMA_SMS_SUBPARAM_ID_MESSAGE_STATUS =			0x14,
+	CDMA_SMS_SUBPARAM_ID_TP_FAILURE_CAUSE =			0x15,
+	CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN =			0x16,
+	CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN_ACK =			0x17
+};
+
+/* 3GPP2 C.R1001-G Table 9.3.1-1 and 9.3.3-1 */
+enum cdma_sms_service_cat {
+	CDMA_SMS_SERVICE_CAT_EMERGENCY_BROADCAST =		0x0001,
+	CDMA_SMS_SERVICE_CAT_ADMINISTRATIVE =			0x0002,
+	CDMA_SMS_SERVICE_CAT_MAINTENANCE =			0x0003,
+	CDMA_SMS_SERVICE_CAT_GEN_NEWS_LOCAL =			0x0004,
+	CDMA_SMS_SERVICE_CAT_GEN_NEWS_REGIONAL =		0x0005,
+	CDMA_SMS_SERVICE_CAT_GEN_NEWS_NATIONAL =		0x0006,
+	CDMA_SMS_SERVICE_CAT_GEN_NEWS_INT =			0x0007,
+	CDMA_SMS_SERVICE_CAT_FIN_NEWS_LOCAL =			0x0008,
+	CDMA_SMS_SERVICE_CAT_FIN_NEWS_REGIONAL =		0x0009,
+	CDMA_SMS_SERVICE_CAT_FIN_NEWS_NATIONAL =		0x000A,
+	CDMA_SMS_SERVICE_CAT_FIN_NEWS_INT =			0x000B,
+	CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_LOCAL =		0x000C,
+	CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_REGIONAL =		0x000D,
+	CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_NATIONAL =		0x000E,
+	CDMA_SMS_SERVICE_CAT_SPORTS_NEWS_INT =			0x000F,
+	CDMA_SMS_SERVICE_CAT_ENT_NEWS_LOCAL =			0x0010,
+	CDMA_SMS_SERVICE_CAT_ENT_NEWS_REGIONAL =		0x0011,
+	CDMA_SMS_SERVICE_CAT_ENT_NEWS_NATIONAL =		0x0012,
+	CDMA_SMS_SERVICE_CAT_ENT_NEWS_INT =			0x0013,
+	CDMA_SMS_SERVICE_CAT_LOCAL_WEATHER =			0x0014,
+	CDMA_SMS_SERVICE_CAT_TRAFFIC_REPORT =			0x0015,
+	CDMA_SMS_SERVICE_CAT_FLIGHT_SCHED =			0x0016,
+	CDMA_SMS_SERVICE_CAT_RESTAURANT =			0x0017,
+	CDMA_SMS_SERVICE_CAT_LODGINGS =				0x0018,
+	CDMA_SMS_SERVICE_CAT_RETAIL_DIR =			0x0019,
+	CDMA_SMS_SERVICE_CAT_ADVERTISEMENTS =			0x001A,
+	CDMA_SMS_SERVICE_CAT_STOCK_QUOTES =			0x001B,
+	CDMA_SMS_SERVICE_CAT_EMPLOYMENT =			0x001C,
+	CDMA_SMS_SERVICE_CAT_HOSPITAL =				0x001D,
+	CDMA_SMS_SERVICE_CAT_TECH_NEWS =			0x001E,
+	CDMA_SMS_SERVICE_CAT_MULTICATEGORY =			0x001F,
+	CDMA_SMS_SERVICE_CAT_CAPT =				0x0020,
+	CDMA_SMS_SERVICE_CAT_PRESIDENTIAL_ALERT = 		0x1000,
+	CDMA_SMS_SERVICE_CAT_EXTREME_THREAT = 			0x1001,
+	CDMA_SMS_SERVICE_CAT_SEVERE_THREAT = 			0x1002,
+	CDMA_SMS_SERVICE_CAT_AMBER =	 			0x1003,
+	CDMA_SMS_SERVICE_CAT_CMAS_TEST =	 		0x1004
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */
+enum cdma_sms_digit_mode {
+	CDMA_SMS_DIGIT_MODE_4BIT_DTMF =		0,
+	CDMA_SMS_DIGIT_MODE_8BIT_ASCII =	1
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.3.3 */
+struct cdma_sms_address {
+	enum cdma_sms_digit_mode digit_mode;
+	enum cdma_sms_num_mode number_mode;
+	union {
+		enum cdma_sms_digi_num_type digi_num_type;
+		enum cdma_sms_data_nw_num_type data_nw_num_type;
+	};
+	enum cdma_sms_numbering_plan number_plan;
+	guint8 num_fields;
+	guint8 address[CDMA_SMS_MAX_ADDR_FIELDS];
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.3.6 */
+struct cdma_sms_cause_code {
+	guint8 reply_seq;
+	guint8 error_class;
+	guint8 cause_code;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 4.5.1 */
+struct cdma_sms_identifier {
+	enum cdma_sms_msg_type msg_type;
+	guint16 msg_id;
+	gboolean header_ind;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 4.5.2 */
+struct cdma_sms_ud {
+	enum cdma_sms_msg_encoding msg_encoding;
+	guint8 num_fields;
+	guint8 chari[CDMA_SMS_UD_LEN];
+};
+
+/*
+ * 3GPP2 C.S0015-B v2.0 Table 4.3.4-1.
+ * TODO: Not all subparameter records defined
+ *       and supported yet.
+ */
+struct cdma_sms_wmt_deliver {
+	struct cdma_sms_ud ud;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 4.5 */
+struct cdma_sms_bearer_data {
+	guint32 subparam_bitmap;
+	struct cdma_sms_identifier id;
+	union {
+		struct cdma_sms_wmt_deliver wmt_deliver;
+	};
+};
+
+/*
+ * 3GPP2 C.S0015-B v2.0 Table 3.4.2.1-1.
+ * TODO: Not all parameter records defined
+ *       and supported yet.
+ */
+struct cdma_sms_p2p_msg {
+	guint32 param_bitmap;
+	enum cdma_sms_teleservice_id teleservice_id;
+	struct cdma_sms_address oaddr;
+	struct cdma_sms_bearer_data bd;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Table 3.4.2.2-1 */
+struct cdma_sms_broadcast_msg {
+	enum cdma_sms_service_cat service_category;
+	struct cdma_sms_bearer_data bd;
+};
+
+/*
+ * 3GPP2 C.S0015-B v2.0 Table 3.4.2.3-1
+ * TODO: Not all parameter records defined
+ *       and supported yet.
+ */
+struct cdma_sms_ack_msg {
+	struct cdma_sms_address daddr;
+	struct cdma_sms_cause_code cause_code;
+};
+
+/* 3GPP2 C.S0015-B v2.0 Section 3.4.1 */
+struct cdma_sms {
+	enum cdma_sms_tp_msg_type type;
+	union {
+		struct cdma_sms_p2p_msg p2p_msg;
+		struct cdma_sms_broadcast_msg broadcast_msg;
+		struct cdma_sms_ack_msg ack_msg;
+	};
+};
+
+static inline gboolean check_bitmap(guint32 bitmap, guint32 pos)
+{
+	guint32 mask = 0x1 << pos;
+
+	return bitmap & mask ? TRUE : FALSE;
+}
+
+gboolean cdma_sms_decode(const guint8 *pdu, guint8 len,
+				struct cdma_sms *out);
+char *cdma_sms_decode_text(const struct cdma_sms_ud *ud);
+const char *cdma_sms_address_to_string(const struct cdma_sms_address *addr);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4, 4/7] cdma-sms: Add CDMA SMS Support
       [not found] <Y>
  2011-01-18 22:54 ` [PATCH v4, 3/7] cdma-sms: Add CDMA SMS Support Lei Yu
@ 2011-01-18 22:54 ` Lei Yu
  2011-01-18 22:54 ` [PATCH v4, 5/7] cdmamodem: Add CDMA SMS driver support Lei Yu
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: Lei Yu @ 2011-01-18 22:54 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 10297 bytes --]

---
 Makefile.am    |    2 +-
 src/cdma-sms.c |  330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ofono.h    |    3 +
 3 files changed, 334 insertions(+), 1 deletions(-)
 create mode 100644 src/cdma-sms.c

diff --git a/Makefile.am b/Makefile.am
index bd8c058..dff5553 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -355,7 +355,7 @@ src_ofonod_SOURCES = $(gdbus_sources) $(builtin_sources) src/ofono.ver \
 			src/simfs.c src/simfs.h src/audio-settings.c \
 			src/smsagent.c src/smsagent.h src/ctm.c \
 			src/cdma-voicecall.c src/cdma-smsutil.h \
-			src/cdma-smsutil.c
+			src/cdma-smsutil.c src/cdma-sms.c
 
 src_ofonod_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @CAPNG_LIBS@ -ldl
 
diff --git a/src/cdma-sms.c b/src/cdma-sms.c
new file mode 100644
index 0000000..ea88028
--- /dev/null
+++ b/src/cdma-sms.c
@@ -0,0 +1,330 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <gdbus.h>
+#include <sys/time.h>
+
+#include "ofono.h"
+
+#include "cdma-smsutil.h"
+
+static GSList *g_drivers;
+
+struct ofono_cdma_sms {
+	const struct ofono_cdma_sms_driver *driver;
+	void *driver_data;
+	struct ofono_atom *atom;
+};
+
+static GDBusMethodTable cdma_sms_manager_methods[] = {
+	/* TODO */
+	{ }
+};
+
+static GDBusSignalTable cdma_sms_manager_signals[] = {
+	{ "IncomingMessage",	"sa{sv}"	},
+	/* TODO */
+	{ }
+};
+
+static void cdma_dispatch_text_message(struct ofono_cdma_sms *cdma_sms,
+					const char *message,
+					const char *oaddr)
+{
+	const char *path = __ofono_atom_get_path(cdma_sms->atom);
+	DBusConnection *conn = ofono_dbus_get_connection();
+	DBusMessage *signal;
+	DBusMessageIter iter;
+	DBusMessageIter dict;
+	const char *signal_name;
+
+	/* TODO: Support ImmediateMessage */
+	signal_name = "IncomingMessage";
+
+	signal = dbus_message_new_signal(path,
+					OFONO_CDMA_MESSAGE_MANAGER_INTERFACE,
+					signal_name);
+	if (signal == NULL)
+		return;
+
+	dbus_message_iter_init_append(signal, &iter);
+
+	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &message);
+
+	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+					OFONO_PROPERTIES_ARRAY_SIGNATURE,
+					&dict);
+
+	ofono_dbus_dict_append(&dict, "Sender", DBUS_TYPE_STRING, &oaddr);
+
+	/* TODO: Other properties not supported yet */
+
+	dbus_message_iter_close_container(&iter, &dict);
+
+	g_dbus_send_message(conn, signal);
+
+	/*TODO: Add the message to history*/
+}
+
+static void ofono_cdma_sms_process_wmt_deliver(struct ofono_cdma_sms *cdma_sms,
+						const struct cdma_sms *incoming)
+{
+	char *message;
+	const char *oaddr;
+	const struct cdma_sms_ud *ud;
+
+	ud = &incoming->p2p_msg.bd.wmt_deliver.ud;
+
+	/*
+	 * If incoming message does not contain USER DATA, still
+	 * send indication to upper layer but with empty string.
+	 */
+	if (check_bitmap(incoming->p2p_msg.bd.subparam_bitmap,
+				CDMA_SMS_SUBPARAM_ID_USER_DATA) == FALSE)
+		message = g_new0(char, 1);
+	else
+		message = cdma_sms_decode_text(ud);
+
+	if (message == NULL)
+		return;
+
+	oaddr = cdma_sms_address_to_string(&incoming->p2p_msg.oaddr);
+	if (oaddr == NULL) {
+		g_free(message);
+		return;
+	}
+
+	cdma_dispatch_text_message(cdma_sms, message, oaddr);
+
+	g_free(message);
+}
+
+static void ofono_cdma_sms_process_wmt(struct ofono_cdma_sms *cdma_sms,
+					struct cdma_sms *incoming)
+{
+	/* TODO: Add duplicate detection support */
+
+	switch (incoming->p2p_msg.bd.id.msg_type) {
+	case CDMA_SMS_MSG_TYPE_RESERVED:
+		break;
+	case CDMA_SMS_MSG_TYPE_DELIVER:
+		ofono_cdma_sms_process_wmt_deliver(cdma_sms, incoming);
+		break;
+	case CDMA_SMS_MSG_TYPE_SUBMIT:
+	case CDMA_SMS_MSG_TYPE_CANCEL:
+	case CDMA_SMS_MSG_TYPE_DELIVER_ACK:
+	case CDMA_SMS_MSG_TYPE_USER_ACK:
+	case CDMA_SMS_MSG_TYPE_READ_ACK:
+	case CDMA_SMS_MSG_TYPE_DELIVER_REPORT:
+	case CDMA_SMS_MSG_TYPE_SUBMIT_REPORT:
+		/* TODO */
+		break;
+	}
+}
+
+static void ofono_cdma_sms_process_p2p(struct ofono_cdma_sms *cdma_sms,
+					struct cdma_sms *incoming)
+{
+	switch (incoming->p2p_msg.teleservice_id) {
+	case CDMA_SMS_TELESERVICE_ID_CMT91:
+	case CDMA_SMS_TELESERVICE_ID_WPT:
+		break; /* TODO: Not supported yet */
+	case CDMA_SMS_TELESERVICE_ID_WMT:
+		ofono_cdma_sms_process_wmt(cdma_sms, incoming);
+		break;
+	case CDMA_SMS_TELESERVICE_ID_VMN:
+	case CDMA_SMS_TELESERVICE_ID_WAP:
+	case CDMA_SMS_TELESERVICE_ID_WEMT:
+	case CDMA_SMS_TELESERVICE_ID_SCPT:
+	case CDMA_SMS_TELESERVICE_ID_CATPT:
+		break; /* TODO: Not supported yet */
+	}
+}
+
+void ofono_cdma_sms_deliver_notify(struct ofono_cdma_sms *cdma_sms,
+					unsigned char *pdu, int tpdu_len)
+{
+	static struct cdma_sms s;
+
+	DBG("tpdu len %d", tpdu_len);
+
+	memset(&s, 0, sizeof(struct cdma_sms));
+
+	if (cdma_sms_decode(pdu, tpdu_len, &s) == FALSE)
+		return;
+
+	switch (s.type) {
+	case CDMA_SMS_TP_MSG_TYPE_P2P:
+		ofono_cdma_sms_process_p2p(cdma_sms, &s);
+		break;
+	case CDMA_SMS_TP_MSG_TYPE_BCAST:
+	case CDMA_SMS_TP_MSG_TYPE_ACK:
+		/*
+		 * TODO: Support SMS Broadcast Message and SMS
+		 * Acknowledge Message.
+		 */
+		break;
+	}
+}
+
+int ofono_cdma_sms_driver_register(const struct ofono_cdma_sms_driver *d)
+{
+	DBG("driver: %p, name: %s", d, d->name);
+
+	if (d->probe == NULL)
+		return -EINVAL;
+
+	g_drivers = g_slist_prepend(g_drivers, (void *)d);
+
+	return 0;
+}
+
+void ofono_cdma_sms_driver_unregister(const struct ofono_cdma_sms_driver *d)
+{
+	DBG("driver: %p, name: %s", d, d->name);
+
+	g_drivers = g_slist_remove(g_drivers, (void *)d);
+}
+
+static void cdma_sms_unregister(struct ofono_atom *atom)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	struct ofono_modem *modem = __ofono_atom_get_modem(atom);
+	const char *path = __ofono_atom_get_path(atom);
+
+	g_dbus_unregister_interface(conn, path,
+					OFONO_CDMA_MESSAGE_MANAGER_INTERFACE);
+
+	ofono_modem_remove_interface(modem,
+					OFONO_CDMA_MESSAGE_MANAGER_INTERFACE);
+}
+
+static void cdma_sms_remove(struct ofono_atom *atom)
+{
+	struct ofono_cdma_sms *cdma_sms = __ofono_atom_get_data(atom);
+
+	DBG("atom: %p", atom);
+
+	if (cdma_sms == NULL)
+		return;
+
+	if (cdma_sms->driver && cdma_sms->driver->remove)
+		cdma_sms->driver->remove(cdma_sms);
+
+	g_free(cdma_sms);
+}
+
+/*
+ * Create a CDMA SMS driver
+ *
+ * This creates a CDMA SMS driver that is hung off a @modem
+ * object. However, for the driver to be used by the system, it has to
+ * be registered with the oFono core using ofono_sms_register().
+ *
+ * This is done once the modem driver determines that SMS is properly
+ * supported by the hardware.
+ */
+struct ofono_cdma_sms *ofono_cdma_sms_create(struct ofono_modem *modem,
+						unsigned int vendor,
+						const char *driver,
+						void *data)
+{
+	struct ofono_cdma_sms *cdma_sms;
+	GSList *l;
+
+	if (driver == NULL)
+		return NULL;
+
+	cdma_sms = g_try_new0(struct ofono_cdma_sms, 1);
+	if (cdma_sms == NULL)
+		return NULL;
+
+	cdma_sms->atom = __ofono_modem_add_atom(modem,
+						OFONO_ATOM_TYPE_CDMA_SMS,
+						cdma_sms_remove, cdma_sms);
+
+	for (l = g_drivers; l; l = l->next) {
+		const struct ofono_cdma_sms_driver *drv = l->data;
+
+		if (g_strcmp0(drv->name, driver))
+			continue;
+
+		if (drv->probe(cdma_sms, vendor, data) < 0)
+			continue;
+
+		cdma_sms->driver = drv;
+		break;
+	}
+
+	return cdma_sms;
+}
+
+/*
+ * Indicate oFono that a CDMA SMS driver is ready for operation
+ *
+ * This is called after ofono_cdma_sms_create() was done and the modem
+ * driver determined that a modem supports SMS correctly. Once this
+ * call succeeds, the D-BUS interface for SMS goes live.
+ */
+void ofono_cdma_sms_register(struct ofono_cdma_sms *cdma_sms)
+{
+	DBusConnection *conn = ofono_dbus_get_connection();
+	struct ofono_modem *modem = __ofono_atom_get_modem(cdma_sms->atom);
+	const char *path = __ofono_atom_get_path(cdma_sms->atom);
+
+	if (!g_dbus_register_interface(conn, path,
+					OFONO_CDMA_MESSAGE_MANAGER_INTERFACE,
+					cdma_sms_manager_methods,
+					cdma_sms_manager_signals,
+					NULL, cdma_sms, NULL)) {
+		ofono_error("Could not create %s interface",
+				OFONO_CDMA_MESSAGE_MANAGER_INTERFACE);
+		return;
+	}
+
+	ofono_modem_add_interface(modem, OFONO_CDMA_MESSAGE_MANAGER_INTERFACE);
+
+	__ofono_atom_register(cdma_sms->atom, cdma_sms_unregister);
+}
+
+void ofono_cdma_sms_remove(struct ofono_cdma_sms *cdma_sms)
+{
+	__ofono_atom_free(cdma_sms->atom);
+}
+
+void ofono_cdma_sms_set_data(struct ofono_cdma_sms *cdma_sms, void *data)
+{
+	cdma_sms->driver_data = data;
+}
+
+void *ofono_cdma_sms_get_data(struct ofono_cdma_sms *cdma_sms)
+{
+	return cdma_sms->driver_data;
+}
diff --git a/src/ofono.h b/src/ofono.h
index 77567c2..1d15e95 100644
--- a/src/ofono.h
+++ b/src/ofono.h
@@ -127,6 +127,7 @@ enum ofono_atom_type {
 	OFONO_ATOM_TYPE_NETTIME = 21,
 	OFONO_ATOM_TYPE_CTM = 22,
 	OFONO_ATOM_TYPE_CDMA_VOICECALL_MANAGER = 23,
+	OFONO_ATOM_TYPE_CDMA_SMS = 24,
 };
 
 enum ofono_atom_watch_condition {
@@ -418,3 +419,5 @@ void __ofono_nettime_info_received(struct ofono_modem *modem,
 					struct ofono_network_time *info);
 
 #include <ofono/cdma-voicecall.h>
+
+#include <ofono/cdma-sms.h>
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4, 5/7] cdmamodem: Add CDMA SMS driver support
       [not found] <Y>
  2011-01-18 22:54 ` [PATCH v4, 3/7] cdma-sms: Add CDMA SMS Support Lei Yu
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
@ 2011-01-18 22:54 ` Lei Yu
  2011-01-18 22:54 ` [PATCH v4, 6/7] test: Add CDMA SMS Support Lei Yu
                   ` (17 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: Lei Yu @ 2011-01-18 22:54 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 7303 bytes --]

---
 Makefile.am                   |    3 +-
 drivers/atmodem/atutil.c      |   21 +++++++
 drivers/atmodem/atutil.h      |    3 +
 drivers/atmodem/sms.c         |   21 -------
 drivers/cdmamodem/cdmamodem.c |    2 +
 drivers/cdmamodem/cdmamodem.h |    2 +
 drivers/cdmamodem/sms.c       |  125 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 155 insertions(+), 22 deletions(-)
 create mode 100644 drivers/cdmamodem/sms.c

diff --git a/Makefile.am b/Makefile.am
index dff5553..7548942 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -253,7 +253,8 @@ builtin_modules += cdmamodem
 builtin_sources += drivers/cdmamodem/cdmamodem.h \
 			drivers/cdmamodem/cdmamodem.c \
 			drivers/cdmamodem/voicecall.c \
-			drivers/cdmamodem/devinfo.c
+			drivers/cdmamodem/devinfo.c \
+			drivers/cdmamodem/sms.c
 endif
 
 builtin_modules += g1
diff --git a/drivers/atmodem/atutil.c b/drivers/atmodem/atutil.c
index da17253..d567fbe 100644
--- a/drivers/atmodem/atutil.c
+++ b/drivers/atmodem/atutil.c
@@ -480,3 +480,24 @@ gboolean at_util_parse_attr(GAtResult *result, const char *prefix,
 
 	return TRUE;
 }
+
+gboolean at_parse_pdu_common(GAtResult *result, const char *prefix,
+				const char **pdu, int *pdulen)
+{
+	GAtResultIter iter;
+
+	g_at_result_iter_init(&iter, result);
+
+	if (!g_at_result_iter_next(&iter, prefix))
+		return FALSE;
+
+	if (!strcmp(prefix, "+CMT:") && !g_at_result_iter_skip_next(&iter))
+		return FALSE;
+
+	if (!g_at_result_iter_next_number(&iter, pdulen))
+		return FALSE;
+
+	*pdu = g_at_result_pdu(result);
+
+	return TRUE;
+}
diff --git a/drivers/atmodem/atutil.h b/drivers/atmodem/atutil.h
index 3d13b84..3f70ac0 100644
--- a/drivers/atmodem/atutil.h
+++ b/drivers/atmodem/atutil.h
@@ -74,6 +74,9 @@ gboolean at_util_parse_cscs_query(GAtResult *result,
 gboolean at_util_parse_attr(GAtResult *result, const char *prefix,
 				const char **out_attr);
 
+gboolean at_parse_pdu_common(GAtResult *result, const char *prefix,
+					const char **pdu, int *pdulen);
+
 struct cb_data {
 	void *cb;
 	void *data;
diff --git a/drivers/atmodem/sms.c b/drivers/atmodem/sms.c
index c570886..725ed3c 100644
--- a/drivers/atmodem/sms.c
+++ b/drivers/atmodem/sms.c
@@ -339,27 +339,6 @@ static void at_cnma_cb(gboolean ok, GAtResult *result, gpointer user_data)
 				"Further SMS reception is not guaranteed");
 }
 
-static gboolean at_parse_pdu_common(GAtResult *result, const char *prefix,
-					const char **pdu, int *pdulen)
-{
-	GAtResultIter iter;
-
-	g_at_result_iter_init(&iter, result);
-
-	if (!g_at_result_iter_next(&iter, prefix))
-		return FALSE;
-
-	if (!strcmp(prefix, "+CMT:") && !g_at_result_iter_skip_next(&iter))
-		return FALSE;
-
-	if (!g_at_result_iter_next_number(&iter, pdulen))
-		return FALSE;
-
-	*pdu = g_at_result_pdu(result);
-
-	return TRUE;
-}
-
 static inline void at_ack_delivery(struct ofono_sms *sms)
 {
 	struct sms_data *data = ofono_sms_get_data(sms);
diff --git a/drivers/cdmamodem/cdmamodem.c b/drivers/cdmamodem/cdmamodem.c
index 9eddd88..f133fb0 100644
--- a/drivers/cdmamodem/cdmamodem.c
+++ b/drivers/cdmamodem/cdmamodem.c
@@ -36,6 +36,7 @@ static int cdmamodem_init(void)
 {
 	cdma_voicecall_init();
 	cdma_devinfo_init();
+	cdma_sms_init();
 
 	return 0;
 }
@@ -44,6 +45,7 @@ static void cdmamodem_exit(void)
 {
 	cdma_voicecall_exit();
 	cdma_devinfo_exit();
+	cdma_sms_exit();
 }
 
 OFONO_PLUGIN_DEFINE(cdmamodem, "CDMA AT modem driver", VERSION,
diff --git a/drivers/cdmamodem/cdmamodem.h b/drivers/cdmamodem/cdmamodem.h
index 4365bec..4ce0b3d 100644
--- a/drivers/cdmamodem/cdmamodem.h
+++ b/drivers/cdmamodem/cdmamodem.h
@@ -25,3 +25,5 @@ extern void cdma_voicecall_init(void);
 extern void cdma_voicecall_exit(void);
 extern void cdma_devinfo_init(void);
 extern void cdma_devinfo_exit(void);
+extern void cdma_sms_init(void);
+extern void cdma_sms_exit(void);
diff --git a/drivers/cdmamodem/sms.c b/drivers/cdmamodem/sms.c
new file mode 100644
index 0000000..df61b7d
--- /dev/null
+++ b/drivers/cdmamodem/sms.c
@@ -0,0 +1,125 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2010-2011  Nokia Corporation. All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/cdma-sms.h>
+#include "util.h"
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "cdmamodem.h"
+
+struct cdma_sms_data {
+	GAtChat *chat;
+};
+
+static void cdma_cmt_notify(GAtResult *result, gpointer user_data)
+{
+	struct ofono_cdma_sms *cdma_sms = user_data;
+	const char *hexpdu;
+	long tpdu_len;
+	int len;
+	unsigned char tpdu[256];
+
+	if (at_parse_pdu_common(result, "+CMT:", &hexpdu, &len) != TRUE) {
+		ofono_error("Unable to parse CMT notification");
+		return;
+	}
+
+	if (strlen(hexpdu) > sizeof(tpdu) * 2) {
+		ofono_error("Bad PDU length in CMT notification");
+		return;
+	}
+
+	DBG("Got new SMS Deliver PDU via CMT: %s, %d", hexpdu, len);
+
+	decode_hex_own_buf(hexpdu, -1, &tpdu_len, 0, tpdu);
+	ofono_cdma_sms_deliver_notify(cdma_sms, tpdu, tpdu_len);
+}
+
+static gboolean cdma_sms_initialized(void *user_data)
+{
+	struct ofono_cdma_sms *cdma_sms = user_data;
+	struct cdma_sms_data *data = ofono_cdma_sms_get_data(cdma_sms);
+
+	g_at_chat_register(data->chat, "+CMT:",
+				cdma_cmt_notify, TRUE, cdma_sms, NULL);
+
+	ofono_cdma_sms_register(cdma_sms);
+
+	return FALSE;
+}
+
+static int cdma_sms_probe(struct ofono_cdma_sms *cdma_sms,
+				unsigned int vendor, void *user)
+{
+	GAtChat *chat = user;
+	struct cdma_sms_data *data;
+
+	data = g_new0(struct cdma_sms_data, 1);
+	data->chat = g_at_chat_clone(chat);
+
+	ofono_cdma_sms_set_data(cdma_sms, data);
+
+	g_idle_add(cdma_sms_initialized, cdma_sms);
+
+	return 0;
+}
+
+static void cdma_sms_remove(struct ofono_cdma_sms *cdma_sms)
+{
+	struct cdma_sms_data *data = ofono_cdma_sms_get_data(cdma_sms);
+
+	ofono_cdma_sms_set_data(cdma_sms, NULL);
+
+	g_at_chat_unref(data->chat);
+	g_free(data);
+}
+
+static struct ofono_cdma_sms_driver driver = {
+	.name		= "cdmamodem",
+	.probe		= cdma_sms_probe,
+	.remove		= cdma_sms_remove,
+};
+
+void cdma_sms_init(void)
+{
+	ofono_cdma_sms_driver_register(&driver);
+}
+
+void cdma_sms_exit(void)
+{
+	ofono_cdma_sms_driver_unregister(&driver);
+}
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4, 6/7] test: Add CDMA SMS Support
       [not found] <Y>
                   ` (2 preceding siblings ...)
  2011-01-18 22:54 ` [PATCH v4, 5/7] cdmamodem: Add CDMA SMS driver support Lei Yu
@ 2011-01-18 22:54 ` Lei Yu
  2011-01-18 22:54 ` [PATCH v4, 7/7] cdmaphonesim: " Lei Yu
                   ` (16 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: Lei Yu @ 2011-01-18 22:54 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 4504 bytes --]

---
 Makefile.am         |    6 ++-
 unit/test-cdmasms.c |  116 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 121 insertions(+), 1 deletions(-)
 create mode 100644 unit/test-cdmasms.c

diff --git a/Makefile.am b/Makefile.am
index 7548942..11fc274 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -489,7 +489,7 @@ dist_man_MANS = doc/ofonod.8
 noinst_PROGRAMS = unit/test-common unit/test-util unit/test-idmap \
 					unit/test-sms unit/test-simutil \
 					unit/test-mux unit/test-caif \
-					unit/test-stkutil
+					unit/test-stkutil unit/test-cdmasms
 
 unit_objects =
 
@@ -509,6 +509,10 @@ unit_test_sms_SOURCES = unit/test-sms.c src/util.c src/smsutil.c src/storage.c
 unit_test_sms_LDADD = @GLIB_LIBS@
 unit_objects += $(unit_test_sms_OBJECTS)
 
+unit_test_cdmasms_SOURCES = unit/test-cdmasms.c src/cdma-smsutil.c
+unit_test_cdmasms_LDADD = @GLIB_LIBS@
+unit_objects += $(unit_test_cdmasms_OBJECTS)
+
 unit_test_simutil_SOURCES = unit/test-simutil.c src/util.c \
 				src/simutil.c src/smsutil.c src/storage.c
 unit_test_simutil_LDADD = @GLIB_LIBS@
diff --git a/unit/test-cdmasms.c b/unit/test-cdmasms.c
new file mode 100644
index 0000000..5140bc7
--- /dev/null
+++ b/unit/test-cdmasms.c
@@ -0,0 +1,116 @@
+/*
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2010-2011 Nokia Corporation.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+
+#include "cdma-smsutil.h"
+
+static inline void check_text(const char *decoded, const char *expected)
+{
+	if (expected == NULL) {
+		g_assert(decoded == NULL);
+		return;
+	}
+
+	g_assert(decoded != NULL);
+	g_assert(g_str_equal(decoded, expected));
+}
+
+struct wmt_deliver_test {
+	const guint8 *tpdu;
+	guint8 tpdu_len;
+	const char *text;
+	const char *oaddr;
+};
+
+guint8 wmt_deliver_1[] = {0x00, 0x00, 0x02, 0x10, 0x02, 0x02, 0x05, 0x01,
+				0xC4, 0x8D, 0x15, 0x9C, 0x08, 0x0D, 0x00,
+				0x03, 0x1B, 0xEE, 0xF0, 0x01, 0x06, 0x10,
+				0x2C, 0x8C, 0xBB, 0x36, 0x6F};
+
+guint8 wmt_deliver_2[] = {0x00, 0x00, 0x02, 0x10, 0x02, 0x02, 0x07, 0x02,
+				0xA1, 0x62, 0x51, 0x55, 0xA6, 0x40, 0x08,
+				0x18, 0x00, 0x03, 0x10, 0x00, 0x40, 0x01,
+				0x06, 0x10, 0x25, 0x4C, 0xBC, 0xFA, 0x00,
+				0x03, 0x06, 0x03, 0x08, 0x20, 0x13, 0x43,
+				0x12, 0x0D, 0x01, 0x01};
+
+static struct wmt_deliver_test wmt_deliver_data_1 = {
+	.tpdu = wmt_deliver_1,
+	.tpdu_len = sizeof(wmt_deliver_1),
+	.text = "Hello",
+	.oaddr = "1234567"
+};
+
+static struct wmt_deliver_test wmt_deliver_data_2 = {
+	.tpdu = wmt_deliver_2,
+	.tpdu_len = sizeof(wmt_deliver_2),
+	.text = "Test",
+	.oaddr = "8589455699"
+};
+
+static void test_wmt_deliver(gconstpointer data)
+{
+	const struct wmt_deliver_test *test = data;
+	gboolean ret;
+	struct cdma_sms s;
+	const char *addr;
+	char *message;
+
+	memset(&s, 0, sizeof(struct cdma_sms));
+
+	ret = cdma_sms_decode(test->tpdu, test->tpdu_len, &s);
+
+	g_assert(ret == TRUE);
+
+	g_assert(s.type == CDMA_SMS_TP_MSG_TYPE_P2P);
+
+	g_assert(s.p2p_msg.teleservice_id == CDMA_SMS_TELESERVICE_ID_WMT);
+
+	addr = cdma_sms_address_to_string(&s.p2p_msg.oaddr);
+	check_text(addr, test->oaddr);
+
+	message = cdma_sms_decode_text(&s.p2p_msg.bd.wmt_deliver.ud);
+	check_text(message, test->text);
+
+	g_free(message);
+
+	return;
+}
+
+int main(int argc, char **argv)
+{
+	g_test_init(&argc, &argv, NULL);
+
+	g_test_add_data_func("/test-cdmasms/WMT DELIVER 1",
+			&wmt_deliver_data_1, test_wmt_deliver);
+
+	g_test_add_data_func("/test-cdmasms/WMT DELIVER 2",
+			&wmt_deliver_data_2, test_wmt_deliver);
+
+	return g_test_run();
+}
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH v4, 7/7] cdmaphonesim: Add CDMA SMS Support
       [not found] <Y>
                   ` (3 preceding siblings ...)
  2011-01-18 22:54 ` [PATCH v4, 6/7] test: Add CDMA SMS Support Lei Yu
@ 2011-01-18 22:54 ` Lei Yu
  2011-10-07  0:51 ` [PATCH 1/1] ath6kl: Add WSC IE on the associate message Kevin Fang
                   ` (15 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: Lei Yu @ 2011-01-18 22:54 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 9884 bytes --]

---
 Makefile.am            |    3 +
 plugins/cdmaphonesim.c |  334 ++++++++++++++++++++++++++++++++++++++++++++++++
 plugins/phonesim.c     |    6 +
 plugins/phonesim.conf  |   10 ++
 4 files changed, 353 insertions(+), 0 deletions(-)
 create mode 100644 plugins/cdmaphonesim.c

diff --git a/Makefile.am b/Makefile.am
index 11fc274..708f857 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -243,6 +243,9 @@ if PHONESIM
 builtin_modules += phonesim
 builtin_sources += plugins/phonesim.c
 
+builtin_modules += cdmaphonesim
+builtin_sources += plugins/cdmaphonesim.c
+
 if DATAFILES
 conf_DATA += plugins/phonesim.conf
 endif
diff --git a/plugins/cdmaphonesim.c b/plugins/cdmaphonesim.c
new file mode 100644
index 0000000..dd7c725
--- /dev/null
+++ b/plugins/cdmaphonesim.c
@@ -0,0 +1,334 @@
+/*
+ * This file is part of oFono - Open Source Telephony
+ *
+ * Copyright (C) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <glib.h>
+#include <gatmux.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/cdma-sms.h>
+#include <drivers/atmodem/atutil.h>
+
+struct cdmaphonesim_data {
+	GAtChat *chat;
+};
+
+static int cdmaphonesim_probe(struct ofono_modem *modem)
+{
+	struct cdmaphonesim_data *data;
+
+	DBG("%p", modem);
+
+	data = g_try_new0(struct cdmaphonesim_data, 1);
+	if (data == NULL)
+		return -ENOMEM;
+
+	ofono_modem_set_data(modem, data);
+
+	return 0;
+}
+
+static void cdmaphonesim_remove(struct ofono_modem *modem)
+{
+	struct cdmaphonesim_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	g_free(data);
+	ofono_modem_set_data(modem, NULL);
+}
+
+static void cdmaphonesim_debug(const char *str, void *user_data)
+{
+	ofono_info("%s", str);
+}
+
+static void cfun_set_on_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+
+	DBG("");
+
+	ofono_modem_set_powered(modem, ok);
+}
+
+static void cdmaphonesim_disconnected(gpointer user_data)
+{
+	struct ofono_modem *modem = user_data;
+	struct cdmaphonesim_data *data = ofono_modem_get_data(modem);
+
+	DBG("");
+
+	ofono_modem_set_powered(modem, FALSE);
+
+	g_at_chat_unref(data->chat);
+	data->chat = NULL;
+}
+
+static int cdmaphonesim_enable(struct ofono_modem *modem)
+{
+	struct cdmaphonesim_data *data = ofono_modem_get_data(modem);
+	GIOChannel *io;
+	GAtSyntax *syntax;
+	struct sockaddr_in addr;
+	const char *address;
+	int sk, err, port;
+
+	DBG("%p", modem);
+
+	address = ofono_modem_get_string(modem, "Address");
+	if (address == NULL)
+		return -EINVAL;
+
+	port = ofono_modem_get_integer(modem, "Port");
+	if (port < 0)
+		return -EINVAL;
+
+	sk = socket(PF_INET, SOCK_STREAM, 0);
+	if (sk < 0)
+		return -EINVAL;
+
+	memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = inet_addr(address);
+	addr.sin_port = htons(port);
+
+	err = connect(sk, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0) {
+		close(sk);
+		return err;
+	}
+
+	io = g_io_channel_unix_new(sk);
+	if (io == NULL) {
+		close(sk);
+		return -ENOMEM;
+	}
+
+	syntax = g_at_syntax_new_gsmv1();
+
+	data->chat = g_at_chat_new(io, syntax);
+
+	g_at_syntax_unref(syntax);
+	g_io_channel_unref(io);
+
+	if (data->chat == NULL)
+		return -ENOMEM;
+
+	if (getenv("OFONO_AT_DEBUG"))
+		g_at_chat_set_debug(data->chat, cdmaphonesim_debug, NULL);
+
+	g_at_chat_set_disconnect_function(data->chat,
+					cdmaphonesim_disconnected, modem);
+
+	g_at_chat_send(data->chat, "AT+CFUN=1", NULL,
+					cfun_set_on_cb, modem, NULL);
+
+	return -EINPROGRESS;
+}
+
+static int cdmaphonesim_disable(struct ofono_modem *modem)
+{
+	struct cdmaphonesim_data *data = ofono_modem_get_data(modem);
+
+	DBG("%p", modem);
+
+	g_at_chat_unref(data->chat);
+	data->chat = NULL;
+
+	return 0;
+}
+
+static void cdmaphonesim_pre_sim(struct ofono_modem *modem)
+{
+	DBG("%p", modem);
+}
+
+static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+	struct cb_data *cbd = user_data;
+	ofono_modem_online_cb_t callback = cbd->cb;
+	struct ofono_error error;
+
+	decode_at_error(&error, g_at_result_final_response(result));
+
+	callback(&error, cbd->data);
+}
+
+static void cdmaphonesim_set_online(struct ofono_modem *modem,
+					ofono_bool_t online,
+					ofono_modem_online_cb_t cb,
+					void *user_data)
+{
+	struct cdmaphonesim_data *data = ofono_modem_get_data(modem);
+	struct cb_data *cbd = cb_data_new(cb, user_data);
+	char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
+
+	DBG("modem %p %s", modem, online ? "online" : "offline");
+
+	if (cbd == NULL)
+		goto error;
+
+	if (g_at_chat_send(data->chat, command, NULL,
+				set_online_cb, cbd, g_free))
+		return;
+
+error:
+	g_free(cbd);
+
+	CALLBACK_WITH_FAILURE(cb, cbd->data);
+}
+
+static void cdmaphonesim_post_online(struct ofono_modem *modem)
+{
+	struct cdmaphonesim_data *data = ofono_modem_get_data(modem);
+
+	ofono_cdma_sms_create(modem, 0, "cdmamodem", data->chat);
+}
+
+static struct ofono_modem_driver cdmaphonesim_driver = {
+	.name		= "cdmaphonesim",
+	.probe		= cdmaphonesim_probe,
+	.remove		= cdmaphonesim_remove,
+	.enable		= cdmaphonesim_enable,
+	.disable	= cdmaphonesim_disable,
+	.pre_sim	= cdmaphonesim_pre_sim,
+	.set_online	= cdmaphonesim_set_online,
+	.post_online	= cdmaphonesim_post_online,
+};
+
+static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group)
+{
+	struct ofono_modem *modem;
+	char *value;
+
+	DBG("group %s", group);
+
+	modem = ofono_modem_create(group, "cdmaphonesim");
+	if (modem == NULL)
+		return NULL;
+
+	value = g_key_file_get_string(keyfile, group, "Address", NULL);
+	if (value == NULL)
+		goto error;
+
+	ofono_modem_set_string(modem, "Address", value);
+	g_free(value);
+
+	value = g_key_file_get_string(keyfile, group, "Port", NULL);
+	if (value == NULL)
+		goto error;
+
+	ofono_modem_set_integer(modem, "Port", atoi(value));
+	g_free(value);
+
+	DBG("%p", modem);
+
+	return modem;
+
+error:
+	ofono_error("Missing address or port setting for %s", group);
+
+	ofono_modem_remove(modem);
+
+	return NULL;
+}
+
+static GSList *modem_list;
+
+static void parse_config(const char *filename)
+{
+	GKeyFile *keyfile;
+	GError *err = NULL;
+	char **modems;
+	int i;
+
+	DBG("filename %s", filename);
+
+	keyfile = g_key_file_new();
+
+	g_key_file_set_list_separator(keyfile, ',');
+
+	if (!g_key_file_load_from_file(keyfile, filename, 0, &err)) {
+		ofono_warn("Reading of %s failed: %s", filename, err->message);
+		g_error_free(err);
+		goto done;
+	}
+
+	modems = g_key_file_get_groups(keyfile, NULL);
+
+	for (i = 0; modems[i]; i++) {
+		struct ofono_modem *modem;
+		char *value;
+
+		value = g_key_file_get_string(keyfile, modems[i],
+						"Type", NULL);
+		if (g_strcmp0(value, "cdma") != 0)
+			continue; /* Skip non-CDMA modems */
+
+		modem = create_modem(keyfile, modems[i]);
+		if (modem == NULL)
+			continue;
+
+		modem_list = g_slist_prepend(modem_list, modem);
+
+		ofono_modem_register(modem);
+	}
+
+	g_strfreev(modems);
+
+done:
+	g_key_file_free(keyfile);
+}
+
+static int cdmaphonesim_init(void)
+{
+	int err;
+
+	err = ofono_modem_driver_register(&cdmaphonesim_driver);
+	if (err < 0)
+		return err;
+
+	parse_config(CONFIGDIR "/phonesim.conf");
+	return 0;
+}
+
+static void cdmaphonesim_exit(void)
+{
+	ofono_modem_driver_unregister(&cdmaphonesim_driver);
+}
+
+OFONO_PLUGIN_DEFINE(cdmaphonesim, "CDMA PhoneSIM driver", VERSION,
+	OFONO_PLUGIN_PRIORITY_DEFAULT, cdmaphonesim_init, cdmaphonesim_exit)
diff --git a/plugins/phonesim.c b/plugins/phonesim.c
index b4795f2..a9bfaa3 100644
--- a/plugins/phonesim.c
+++ b/plugins/phonesim.c
@@ -770,6 +770,12 @@ static void parse_config(const char *filename)
 
 	for (i = 0; modems[i]; i++) {
 		struct ofono_modem *modem;
+		char *value;
+
+		value = g_key_file_get_string(keyfile, modems[i],
+						"Type", NULL);
+		if (g_strcmp0(value, "cdma") == 0)
+			continue; /* Skip CDMA modems */
 
 		modem = create_modem(keyfile, modems[i]);
 		if (modem == NULL)
diff --git a/plugins/phonesim.conf b/plugins/phonesim.conf
index 74bb645..8155a39 100644
--- a/plugins/phonesim.conf
+++ b/plugins/phonesim.conf
@@ -8,7 +8,17 @@
 # Each group shall@least define the address and port
 #   Address = <valid IPv4 address format>
 #   Port = <valid TCP port>
+#
+# cdmaphonesim requires following additional attribute and
+# phonesim will still work without following attribute being
+# defined:
+#   Type=cdma
 
 #[phonesim]
 #Address=127.0.0.1
 #Port=12345
+
+#[cdmaphonesim]
+#Type=cdma
+#Address=127.0.0.1
+#Port=12345
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 1/1] ath6kl: Add WSC IE on the associate message
       [not found] <Y>
                   ` (4 preceding siblings ...)
  2011-01-18 22:54 ` [PATCH v4, 7/7] cdmaphonesim: " Lei Yu
@ 2011-10-07  0:51 ` Kevin Fang
  2011-10-11 11:24   ` Kalle Valo
  2012-02-28  7:27   ` zhaoy
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 45+ messages in thread
From: Kevin Fang @ 2011-10-07  0:51 UTC (permalink / raw)
  To: kvalo; +Cc: linux-wireless, Kevin Fang

For some WPS test items, such as item "5.1.14"
STAUT must include the WSC IE in the 802.11 Association Request frame.
Therefore, add the corresponding IE in association message.

Signed-off-by: Kevin Fang <kevin.fang@qca.qualcomm.com>
---
 drivers/net/wireless/ath/ath6kl/cfg80211.c |   53 ++++++++++++++++++++++++++++
 1 files changed, 53 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 2dae5d8..3dad73c 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -237,6 +237,53 @@ static bool ath6kl_cfg80211_ready(struct ath6kl *ar)
 	return true;
 }
 
+static bool ath6kl_is_wpa_ie(const u8 *pos)
+{
+	return pos[0] == WLAN_EID_WPA && pos[1] >= 4 &&
+		pos[2] == 0x00 && pos[3] == 0x50 &&
+		pos[4] == 0xf2 && pos[5] == 0x01;
+}
+
+static bool ath6kl_is_rsn_ie(const u8 *pos)
+{
+	return pos[0] == WLAN_EID_RSN;
+}
+
+static int ath6kl_set_assoc_req_ies(struct ath6kl *ar, const u8 *ies,
+					size_t ies_len)
+{
+	const u8 *pos;
+	u8 *buf = NULL;
+	size_t len = 0;
+	int ret;
+
+	/*
+	 * Filter out RSN/WPA IE(s)
+	 */
+
+	if (ies && ies_len) {
+		buf = kmalloc(ies_len, GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
+		pos = ies;
+
+		while (pos + 1 < ies + ies_len) {
+			if (pos + 2 + pos[1] > ies + ies_len)
+				break;
+			if (!(ath6kl_is_wpa_ie(pos) || ath6kl_is_rsn_ie(pos))) {
+				memcpy(buf + len, pos, 2 + pos[1]);
+				len += 2 + pos[1];
+			}
+			pos += 2 + pos[1];
+		}
+	}
+
+	ret = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_REQ,
+				       buf, len);
+	kfree(buf);
+	return ret;
+}
+
 static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
 				   struct cfg80211_connect_params *sme)
 {
@@ -285,6 +332,12 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
 		}
 	}
 
+	if (sme->ie && (sme->ie_len > 0)) {
+		status = ath6kl_set_assoc_req_ies(ar, sme->ie, sme->ie_len);
+		if (status)
+			return status;
+	}
+
 	if (test_bit(CONNECTED, &ar->flag) &&
 	    ar->ssid_len == sme->ssid_len &&
 	    !memcmp(ar->ssid, sme->ssid, ar->ssid_len)) {
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* Re: [PATCH 1/1] ath6kl: Add WSC IE on the associate message
  2011-10-07  0:51 ` [PATCH 1/1] ath6kl: Add WSC IE on the associate message Kevin Fang
@ 2011-10-11 11:24   ` Kalle Valo
  0 siblings, 0 replies; 45+ messages in thread
From: Kalle Valo @ 2011-10-11 11:24 UTC (permalink / raw)
  To: Kevin Fang; +Cc: linux-wireless

On 10/07/2011 03:51 AM, Kevin Fang wrote:
> For some WPS test items, such as item "5.1.14"
> STAUT must include the WSC IE in the 802.11 Association Request frame.
> Therefore, add the corresponding IE in association message.

Applied, thanks!

Kalle

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 00/13] Enable DMA engine for mmp2 and mmp3
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

These patch is for marvell mmp2 and mmp3 platform to enable
dmaengine, use dmaengine to manage the audio dma, video dma 
and peripheral device dma together. 

zhaoy (13):
  DMA:define second level irq definition
  dmaengine:open dmac and tdma support
  dmaengine: mmp: add two-channel dma driver
  dmaengine:mmp add peripheral dma driver
  mmp2:register dma devices for mmp2 platform
  mmp3:register dma devices for mmp3 platform
  DMA:mmp:remove redundant dma drivers
  DMAC:tty serial pxa use generic dma engine APIs
  DMA:remove unnecessary dma configurations
  dmaengine:audio:mmp audio use generic dma engine APIs
  DMA:update defconfig for mmp3
  mdma:dmaengine:update mdma for tdma
  DMA:update defconfig for mmp2

 arch/arm/configs/mmp2_defconfig           |   23 +-
 arch/arm/configs/mmp3_defconfig           |   21 +-
 arch/arm/mach-mmp/Makefile                |    4 +-
 arch/arm/mach-mmp/include/mach/irqs.h     |   18 +-
 arch/arm/mach-mmp/include/mach/mmp_dma.h  |   89 ++-
 arch/arm/mach-mmp/include/mach/regs-icu.h |   12 +-
 arch/arm/mach-mmp/irq-mmp2.c              |    4 +
 arch/arm/mach-mmp/irq-mmp3.c              |    3 +
 arch/arm/mach-mmp/mmp2.c                  |  122 ++-
 arch/arm/mach-mmp/mmp3.c                  |   78 ++-
 arch/arm/plat-pxa/include/plat/dma.h      |   33 +-
 drivers/char/mmp2_mdma.c                  |  273 ++----
 drivers/dma/Kconfig                       |   19 +
 drivers/dma/Makefile                      |    2 +
 drivers/dma/mmp_tdma.c                    |  858 +++++++++++++++
 drivers/dma/pxa_dmac.c                    | 1077 ++++++++++++++++++
 drivers/tty/serial/Kconfig                |   12 +-
 drivers/tty/serial/Makefile               |    1 +
 drivers/tty/serial/mmp_pxa.c              | 1698 +++++++++++++++++++++++++++++
 sound/soc/pxa/mmp-pcm.c                   |  232 ++--
 sound/soc/pxa/mmp-pcm.h                   |    4 +-
 sound/soc/pxa/mmp2-squ.c                  |  192 ++--
 sound/soc/pxa/mmp2-squ.h                  |    8 +-
 sound/soc/pxa/mmp2-sspa.c                 |   28 +-
 24 files changed, 4361 insertions(+), 450 deletions(-)
 create mode 100644 drivers/dma/mmp_tdma.c
 create mode 100644 drivers/dma/pxa_dmac.c
 create mode 100644 drivers/tty/serial/mmp_pxa.c


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 00/13] Enable DMA engine for mmp2 and mmp3
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

These patch is for marvell mmp2 and mmp3 platform to enable
dmaengine, use dmaengine to manage the audio dma, video dma 
and peripheral device dma together. 

zhaoy (13):
  DMA:define second level irq definition
  dmaengine:open dmac and tdma support
  dmaengine: mmp: add two-channel dma driver
  dmaengine:mmp add peripheral dma driver
  mmp2:register dma devices for mmp2 platform
  mmp3:register dma devices for mmp3 platform
  DMA:mmp:remove redundant dma drivers
  DMAC:tty serial pxa use generic dma engine APIs
  DMA:remove unnecessary dma configurations
  dmaengine:audio:mmp audio use generic dma engine APIs
  DMA:update defconfig for mmp3
  mdma:dmaengine:update mdma for tdma
  DMA:update defconfig for mmp2

 arch/arm/configs/mmp2_defconfig           |   23 +-
 arch/arm/configs/mmp3_defconfig           |   21 +-
 arch/arm/mach-mmp/Makefile                |    4 +-
 arch/arm/mach-mmp/include/mach/irqs.h     |   18 +-
 arch/arm/mach-mmp/include/mach/mmp_dma.h  |   89 ++-
 arch/arm/mach-mmp/include/mach/regs-icu.h |   12 +-
 arch/arm/mach-mmp/irq-mmp2.c              |    4 +
 arch/arm/mach-mmp/irq-mmp3.c              |    3 +
 arch/arm/mach-mmp/mmp2.c                  |  122 ++-
 arch/arm/mach-mmp/mmp3.c                  |   78 ++-
 arch/arm/plat-pxa/include/plat/dma.h      |   33 +-
 drivers/char/mmp2_mdma.c                  |  273 ++----
 drivers/dma/Kconfig                       |   19 +
 drivers/dma/Makefile                      |    2 +
 drivers/dma/mmp_tdma.c                    |  858 +++++++++++++++
 drivers/dma/pxa_dmac.c                    | 1077 ++++++++++++++++++
 drivers/tty/serial/Kconfig                |   12 +-
 drivers/tty/serial/Makefile               |    1 +
 drivers/tty/serial/mmp_pxa.c              | 1698 +++++++++++++++++++++++++++++
 sound/soc/pxa/mmp-pcm.c                   |  232 ++--
 sound/soc/pxa/mmp-pcm.h                   |    4 +-
 sound/soc/pxa/mmp2-squ.c                  |  192 ++--
 sound/soc/pxa/mmp2-squ.h                  |    8 +-
 sound/soc/pxa/mmp2-sspa.c                 |   28 +-
 24 files changed, 4361 insertions(+), 450 deletions(-)
 create mode 100644 drivers/dma/mmp_tdma.c
 create mode 100644 drivers/dma/pxa_dmac.c
 create mode 100644 drivers/tty/serial/mmp_pxa.c

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 01/13] DMA:define second level irq definition
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.add second level irq definition

Change-Id: I9b1d52dbf411c4f79f7dc9891168f9fd7216d18e
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/include/mach/irqs.h     |   18 ++++++++++++++----
 arch/arm/mach-mmp/include/mach/regs-icu.h |   12 +++++++++---
 arch/arm/mach-mmp/irq-mmp2.c              |    4 ++++
 arch/arm/mach-mmp/irq-mmp3.c              |    3 +++
 4 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-mmp/include/mach/irqs.h b/arch/arm/mach-mmp/include/mach/irqs.h
index 7126450..df746f4 100644
--- a/arch/arm/mach-mmp/include/mach/irqs.h
+++ b/arch/arm/mach-mmp/include/mach/irqs.h
@@ -166,7 +166,7 @@
 #define IRQ_MMP2_NAND			45
 #define IRQ_MMP2_UART4			46
 #define IRQ_MMP2_DMA_FIQ		47
-#define IRQ_MMP2_DMA_RIQ		48
+#define IRQ_MMP2_DMA_IRQ		48
 #define IRQ_MMP2_GPIO			49
 #define IRQ_MMP2_MIPI_HSI1_MUX		51
 #define IRQ_MMP2_MMC2			52
@@ -234,8 +234,13 @@
 #define IRQ_MMP2_HSI0_CAWAKE		(IRQ_MMP2_MIPI_HSI0_BASE + 0)
 #define IRQ_MMP2_MIPI_HSI_INT0		(IRQ_MMP2_MIPI_HSI0_BASE + 1)
 
-#define IRQ_MMP2_MUX_END		(IRQ_MMP2_MIPI_HSI0_BASE + 2)
 
+#define IRQ_MMP2_DMA_BASE			128
+#define IRQ_MMP2_DMA_SECONDARY(x)		(IRQ_MMP2_DMA_BASE + (x))
+#define IRQ_MMP2_GPIO_START			(IRQ_MMP2_DMA_BASE + 32)
+#define IRQ_MMP2_DMA_NUM			32
+
+#define IRQ_MMP2_MUX_END		(IRQ_MMP2_DMA_BASE + 32)
 /*
  * Interrupt numbers for MMP3
  */
@@ -440,14 +445,19 @@
 #define IRQ_MMP3_UNIPRO_INT_0		(IRQ_MMP3_HSI0_BASE + 3)
 #define IRQ_MMP3_HSI0			(IRQ_MMP3_HSI0_BASE + 4)
 
-#define IRQ_MMP3_MUX_END		(IRQ_MMP3_HSI0_BASE + 5)
+#define IRQ_MMP3_DMA_BASE			(IRQ_MMP3_HSI0_BASE + 5)
+#define IRQ_MMP3_DMA_SECONDARY(x)		(IRQ_MMP3_DMA_BASE + (x))
+#define IRQ_MMP3_GPIO_START			(IRQ_MMP3_DMA_BASE + 32)
+#define IRQ_MMP3_DMA_NUM			32
 
+#define IRQ_MMP3_MUX_END		(IRQ_MMP3_DMA_BASE + 32)
 
 #ifdef CONFIG_CPU_MMP3
 #define IRQ_GPIO_START			IRQ_MMP3_MUX_END
 #else
-#define IRQ_GPIO_START			128
+#define IRQ_GPIO_START			IRQ_MMP2_MUX_END
 #endif
+
 #define IRQ_GPIO_NUM			192
 #define IRQ_GPIO(x)			(IRQ_GPIO_START + (x))
 
diff --git a/arch/arm/mach-mmp/include/mach/regs-icu.h b/arch/arm/mach-mmp/include/mach/regs-icu.h
index f4cff2b..759a182 100644
--- a/arch/arm/mach-mmp/include/mach/regs-icu.h
+++ b/arch/arm/mach-mmp/include/mach/regs-icu.h
@@ -69,6 +69,10 @@
 #define MMP2_ICU_PJ4_IRQ_SEL		ICU_REG(0x104)
 #define MMP2_ICU_PJ4_FIQ_SEL		ICU_REG(0x108)
 
+#define MMP2_ICU_DMA_IRQ_MASK		ICU_REG(0x11c)
+#define MMP2_ICU_DMA_FIQ_MASK		ICU_REG(0x120)
+#define MMP2_ICU_DMA_IRQ_STATUS		ICU_REG(0x128)
+
 #define MMP2_ICU_INVERT			ICU_REG(0x164)
 
 #define MMP2_ICU_INV_PMIC		(1 << 0)
@@ -107,9 +111,11 @@
 #define MMP3_ICU_GBL_IRQ0_MSK		ICU1_REG(0x10C)
 #define MMP3_ICU_GBL_IRQ1_MSK		ICU1_REG(0x110)
 #define MMP3_ICU_GBL_IRQ2_MSK		ICU1_REG(0x114)
-#define MMP3_ICU_DMA_IRQ0_MSK		ICU1_REG(0x118)
-#define MMP3_ICU_DMA_IRQ1_MSK		ICU1_REG(0x11C)
-#define MMP3_ICU_DMA_IRQ2_MSK		ICU1_REG(0x120)
+
+#define MMP3_ICU_DMA_IRQ0_MASK		ICU1_REG(0x118)
+#define MMP3_ICU_DMA_IRQ1_MASK		ICU1_REG(0x11C)
+#define MMP3_ICU_DMA_IRQ2_MASK		ICU1_REG(0x120)
+
 #define MMP3_ICU_DMA_IRQ0_STATUS	ICU1_REG(0x124)
 #define MMP3_ICU_DMA_IRQ1_STATUS	ICU1_REG(0x128)
 #define MMP3_ICU_DMA_IRQ2_STATUS	ICU1_REG(0x12C)
diff --git a/arch/arm/mach-mmp/irq-mmp2.c b/arch/arm/mach-mmp/irq-mmp2.c
index afd74f2..9daf43e 100644
--- a/arch/arm/mach-mmp/irq-mmp2.c
+++ b/arch/arm/mach-mmp/irq-mmp2.c
@@ -149,6 +149,7 @@ SECOND_IRQ_CHIP(twsi, IRQ_MMP2_TWSI_BASE, MMP2_ICU_INT17);
 SECOND_IRQ_CHIP(misc, IRQ_MMP2_MISC_BASE, MMP2_ICU_INT35);
 SECOND_IRQ_CHIP(mipi_hsi1,  IRQ_MMP2_MIPI_HSI1_BASE,  MMP2_ICU_INT51);
 SECOND_IRQ_CHIP(mipi_hsi0,  IRQ_MMP2_MIPI_HSI0_BASE,  MMP2_ICU_INT55);
+SECOND_IRQ_CHIP(dma, IRQ_MMP2_DMA_BASE, MMP2_ICU_DMA_IRQ);
 
 static void init_mux_irq(struct irq_chip *chip, int start, int num)
 {
@@ -185,6 +186,7 @@ void __init mmp2_init_icu(void)
 		case IRQ_MMP2_MIPI_HSI1_MUX:
 		case IRQ_MMP2_MIPI_HSI0_MUX:
 		case IRQ_MMP2_KEYPAD_MUX:
+		case IRQ_MMP2_DMA_IRQ:
 			break;
 		default:
 			irq_set_handler(irq, handle_level_irq);
@@ -206,6 +208,7 @@ void __init mmp2_init_icu(void)
 	init_mux_irq(&mipi_hsi1_irq_chip, IRQ_MMP2_MIPI_HSI1_BASE, 2);
 	init_mux_irq(&keypad_irq_chip, IRQ_MMP2_KEYPAD_BASE, 3);
 	init_mux_irq(&mipi_hsi0_irq_chip, IRQ_MMP2_MIPI_HSI0_BASE, 2);
+	init_mux_irq(&dma_irq_chip, IRQ_MMP2_DMA_BASE, 24);
 
 	irq_set_chained_handler(IRQ_MMP2_PMIC_MUX, pmic_irq_demux);
 	irq_set_chained_handler(IRQ_MMP2_RTC_MUX, rtc_irq_demux);
@@ -214,4 +217,5 @@ void __init mmp2_init_icu(void)
 	irq_set_chained_handler(IRQ_MMP2_MIPI_HSI1_MUX, mipi_hsi1_irq_demux);
 	irq_set_chained_handler(IRQ_MMP2_KEYPAD_MUX, keypad_irq_demux);
 	irq_set_chained_handler(IRQ_MMP2_MIPI_HSI0_MUX, mipi_hsi0_irq_demux);
+	irq_set_chained_handler(IRQ_MMP2_DMA_IRQ, dma_irq_demux);
 }
diff --git a/arch/arm/mach-mmp/irq-mmp3.c b/arch/arm/mach-mmp/irq-mmp3.c
index 829e63c..0f5bf50 100644
--- a/arch/arm/mach-mmp/irq-mmp3.c
+++ b/arch/arm/mach-mmp/irq-mmp3.c
@@ -107,6 +107,7 @@ DEFINE_ICU_MUX_IRQ(ssp,		IRQ_MMP3_SSP_BASE,	MMP3_ICU_INT_51);
 DEFINE_ICU_MUX_IRQ(hsi1,	IRQ_MMP3_HSI1_BASE,	MMP3_ICU_INT_55);
 DEFINE_ICU_MUX_IRQ(misc2,	IRQ_MMP3_SSP_BASE,	MMP3_ICU_INT_57);
 DEFINE_ICU_MUX_IRQ(hsi0,	IRQ_MMP3_SSP_BASE,	MMP3_ICU_INT_58);
+DEFINE_ICU_MUX_IRQ(dma,		IRQ_MMP3_DMA_BASE,	MMP3_ICU_DMA_IRQ1);
 
 static void init_mux_irq(struct icu_mux_irq_chip_data *chip_data,
 	int mux_irq, int mux_start, int count, irq_flow_handler_t mux_handle)
@@ -188,6 +189,8 @@ void __init mmp3_init_gic(void)
 			IRQ_MMP3_MISC2_BASE, 20, misc2_irq_demux);
 	init_mux_irq(&hsi0_icu_chip_data, IRQ_MMP3_HSI0_MUX,
 			IRQ_MMP3_HSI0_BASE, 5, hsi0_irq_demux);
+	init_mux_irq(&dma_icu_chip_data, IRQ_MMP3_DMA_RIQ,
+			IRQ_MMP3_DMA_BASE, 24, dma_irq_demux);
 
 	/*
 	 * Note: IRQ_MMP3_PMIC requires the PMIC MFPR register
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 01/13] DMA:define second level irq definition
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.add second level irq definition

Change-Id: I9b1d52dbf411c4f79f7dc9891168f9fd7216d18e
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/include/mach/irqs.h     |   18 ++++++++++++++----
 arch/arm/mach-mmp/include/mach/regs-icu.h |   12 +++++++++---
 arch/arm/mach-mmp/irq-mmp2.c              |    4 ++++
 arch/arm/mach-mmp/irq-mmp3.c              |    3 +++
 4 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/arch/arm/mach-mmp/include/mach/irqs.h b/arch/arm/mach-mmp/include/mach/irqs.h
index 7126450..df746f4 100644
--- a/arch/arm/mach-mmp/include/mach/irqs.h
+++ b/arch/arm/mach-mmp/include/mach/irqs.h
@@ -166,7 +166,7 @@
 #define IRQ_MMP2_NAND			45
 #define IRQ_MMP2_UART4			46
 #define IRQ_MMP2_DMA_FIQ		47
-#define IRQ_MMP2_DMA_RIQ		48
+#define IRQ_MMP2_DMA_IRQ		48
 #define IRQ_MMP2_GPIO			49
 #define IRQ_MMP2_MIPI_HSI1_MUX		51
 #define IRQ_MMP2_MMC2			52
@@ -234,8 +234,13 @@
 #define IRQ_MMP2_HSI0_CAWAKE		(IRQ_MMP2_MIPI_HSI0_BASE + 0)
 #define IRQ_MMP2_MIPI_HSI_INT0		(IRQ_MMP2_MIPI_HSI0_BASE + 1)
 
-#define IRQ_MMP2_MUX_END		(IRQ_MMP2_MIPI_HSI0_BASE + 2)
 
+#define IRQ_MMP2_DMA_BASE			128
+#define IRQ_MMP2_DMA_SECONDARY(x)		(IRQ_MMP2_DMA_BASE + (x))
+#define IRQ_MMP2_GPIO_START			(IRQ_MMP2_DMA_BASE + 32)
+#define IRQ_MMP2_DMA_NUM			32
+
+#define IRQ_MMP2_MUX_END		(IRQ_MMP2_DMA_BASE + 32)
 /*
  * Interrupt numbers for MMP3
  */
@@ -440,14 +445,19 @@
 #define IRQ_MMP3_UNIPRO_INT_0		(IRQ_MMP3_HSI0_BASE + 3)
 #define IRQ_MMP3_HSI0			(IRQ_MMP3_HSI0_BASE + 4)
 
-#define IRQ_MMP3_MUX_END		(IRQ_MMP3_HSI0_BASE + 5)
+#define IRQ_MMP3_DMA_BASE			(IRQ_MMP3_HSI0_BASE + 5)
+#define IRQ_MMP3_DMA_SECONDARY(x)		(IRQ_MMP3_DMA_BASE + (x))
+#define IRQ_MMP3_GPIO_START			(IRQ_MMP3_DMA_BASE + 32)
+#define IRQ_MMP3_DMA_NUM			32
 
+#define IRQ_MMP3_MUX_END		(IRQ_MMP3_DMA_BASE + 32)
 
 #ifdef CONFIG_CPU_MMP3
 #define IRQ_GPIO_START			IRQ_MMP3_MUX_END
 #else
-#define IRQ_GPIO_START			128
+#define IRQ_GPIO_START			IRQ_MMP2_MUX_END
 #endif
+
 #define IRQ_GPIO_NUM			192
 #define IRQ_GPIO(x)			(IRQ_GPIO_START + (x))
 
diff --git a/arch/arm/mach-mmp/include/mach/regs-icu.h b/arch/arm/mach-mmp/include/mach/regs-icu.h
index f4cff2b..759a182 100644
--- a/arch/arm/mach-mmp/include/mach/regs-icu.h
+++ b/arch/arm/mach-mmp/include/mach/regs-icu.h
@@ -69,6 +69,10 @@
 #define MMP2_ICU_PJ4_IRQ_SEL		ICU_REG(0x104)
 #define MMP2_ICU_PJ4_FIQ_SEL		ICU_REG(0x108)
 
+#define MMP2_ICU_DMA_IRQ_MASK		ICU_REG(0x11c)
+#define MMP2_ICU_DMA_FIQ_MASK		ICU_REG(0x120)
+#define MMP2_ICU_DMA_IRQ_STATUS		ICU_REG(0x128)
+
 #define MMP2_ICU_INVERT			ICU_REG(0x164)
 
 #define MMP2_ICU_INV_PMIC		(1 << 0)
@@ -107,9 +111,11 @@
 #define MMP3_ICU_GBL_IRQ0_MSK		ICU1_REG(0x10C)
 #define MMP3_ICU_GBL_IRQ1_MSK		ICU1_REG(0x110)
 #define MMP3_ICU_GBL_IRQ2_MSK		ICU1_REG(0x114)
-#define MMP3_ICU_DMA_IRQ0_MSK		ICU1_REG(0x118)
-#define MMP3_ICU_DMA_IRQ1_MSK		ICU1_REG(0x11C)
-#define MMP3_ICU_DMA_IRQ2_MSK		ICU1_REG(0x120)
+
+#define MMP3_ICU_DMA_IRQ0_MASK		ICU1_REG(0x118)
+#define MMP3_ICU_DMA_IRQ1_MASK		ICU1_REG(0x11C)
+#define MMP3_ICU_DMA_IRQ2_MASK		ICU1_REG(0x120)
+
 #define MMP3_ICU_DMA_IRQ0_STATUS	ICU1_REG(0x124)
 #define MMP3_ICU_DMA_IRQ1_STATUS	ICU1_REG(0x128)
 #define MMP3_ICU_DMA_IRQ2_STATUS	ICU1_REG(0x12C)
diff --git a/arch/arm/mach-mmp/irq-mmp2.c b/arch/arm/mach-mmp/irq-mmp2.c
index afd74f2..9daf43e 100644
--- a/arch/arm/mach-mmp/irq-mmp2.c
+++ b/arch/arm/mach-mmp/irq-mmp2.c
@@ -149,6 +149,7 @@ SECOND_IRQ_CHIP(twsi, IRQ_MMP2_TWSI_BASE, MMP2_ICU_INT17);
 SECOND_IRQ_CHIP(misc, IRQ_MMP2_MISC_BASE, MMP2_ICU_INT35);
 SECOND_IRQ_CHIP(mipi_hsi1,  IRQ_MMP2_MIPI_HSI1_BASE,  MMP2_ICU_INT51);
 SECOND_IRQ_CHIP(mipi_hsi0,  IRQ_MMP2_MIPI_HSI0_BASE,  MMP2_ICU_INT55);
+SECOND_IRQ_CHIP(dma, IRQ_MMP2_DMA_BASE, MMP2_ICU_DMA_IRQ);
 
 static void init_mux_irq(struct irq_chip *chip, int start, int num)
 {
@@ -185,6 +186,7 @@ void __init mmp2_init_icu(void)
 		case IRQ_MMP2_MIPI_HSI1_MUX:
 		case IRQ_MMP2_MIPI_HSI0_MUX:
 		case IRQ_MMP2_KEYPAD_MUX:
+		case IRQ_MMP2_DMA_IRQ:
 			break;
 		default:
 			irq_set_handler(irq, handle_level_irq);
@@ -206,6 +208,7 @@ void __init mmp2_init_icu(void)
 	init_mux_irq(&mipi_hsi1_irq_chip, IRQ_MMP2_MIPI_HSI1_BASE, 2);
 	init_mux_irq(&keypad_irq_chip, IRQ_MMP2_KEYPAD_BASE, 3);
 	init_mux_irq(&mipi_hsi0_irq_chip, IRQ_MMP2_MIPI_HSI0_BASE, 2);
+	init_mux_irq(&dma_irq_chip, IRQ_MMP2_DMA_BASE, 24);
 
 	irq_set_chained_handler(IRQ_MMP2_PMIC_MUX, pmic_irq_demux);
 	irq_set_chained_handler(IRQ_MMP2_RTC_MUX, rtc_irq_demux);
@@ -214,4 +217,5 @@ void __init mmp2_init_icu(void)
 	irq_set_chained_handler(IRQ_MMP2_MIPI_HSI1_MUX, mipi_hsi1_irq_demux);
 	irq_set_chained_handler(IRQ_MMP2_KEYPAD_MUX, keypad_irq_demux);
 	irq_set_chained_handler(IRQ_MMP2_MIPI_HSI0_MUX, mipi_hsi0_irq_demux);
+	irq_set_chained_handler(IRQ_MMP2_DMA_IRQ, dma_irq_demux);
 }
diff --git a/arch/arm/mach-mmp/irq-mmp3.c b/arch/arm/mach-mmp/irq-mmp3.c
index 829e63c..0f5bf50 100644
--- a/arch/arm/mach-mmp/irq-mmp3.c
+++ b/arch/arm/mach-mmp/irq-mmp3.c
@@ -107,6 +107,7 @@ DEFINE_ICU_MUX_IRQ(ssp,		IRQ_MMP3_SSP_BASE,	MMP3_ICU_INT_51);
 DEFINE_ICU_MUX_IRQ(hsi1,	IRQ_MMP3_HSI1_BASE,	MMP3_ICU_INT_55);
 DEFINE_ICU_MUX_IRQ(misc2,	IRQ_MMP3_SSP_BASE,	MMP3_ICU_INT_57);
 DEFINE_ICU_MUX_IRQ(hsi0,	IRQ_MMP3_SSP_BASE,	MMP3_ICU_INT_58);
+DEFINE_ICU_MUX_IRQ(dma,		IRQ_MMP3_DMA_BASE,	MMP3_ICU_DMA_IRQ1);
 
 static void init_mux_irq(struct icu_mux_irq_chip_data *chip_data,
 	int mux_irq, int mux_start, int count, irq_flow_handler_t mux_handle)
@@ -188,6 +189,8 @@ void __init mmp3_init_gic(void)
 			IRQ_MMP3_MISC2_BASE, 20, misc2_irq_demux);
 	init_mux_irq(&hsi0_icu_chip_data, IRQ_MMP3_HSI0_MUX,
 			IRQ_MMP3_HSI0_BASE, 5, hsi0_irq_demux);
+	init_mux_irq(&dma_icu_chip_data, IRQ_MMP3_DMA_RIQ,
+			IRQ_MMP3_DMA_BASE, 24, dma_irq_demux);
 
 	/*
 	 * Note: IRQ_MMP3_PMIC requires the PMIC MFPR register
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 02/13] dmaengine:open dmac and tdma support
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.open dmaengine for tdma and dmac support

Change-Id: I3251daa57cf15fc96e90fbe8be3a1ee303629bcc
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 drivers/dma/Kconfig  |   19 +++++++++++++++++++
 drivers/dma/Makefile |    2 ++
 2 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 25cf327..483b782 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -237,6 +237,25 @@ config MXS_DMA
 	  Support the MXS DMA engine. This engine including APBH-DMA
 	  and APBX-DMA is integrated into Freescale i.MX23/28 chips.
 
+config PXA_DMAC
+	bool "PXA DMAC support"
+	depends on ARCH_PXA || ARCH_MMP
+	select DMA_ENGINE
+	help
+	  Support the PXA DMAC engine. This engine mainly used for PXA DMAC,
+	  also is integrated into Marvell MMP chips for peripheral DMA engine.
+
+config MMP_TDMA
+	bool "MMP Two-Channel DMA support"
+	depends on ARCH_MMP
+	select DMA_ENGINE
+	help
+	  Support the MMP Two-Channel DMA engine. This engine mainly used for
+	  Audio DMA and Memory DMA, now is integrated into Marvell MMP chips.
+
+	  Say Y here if you want to use tdma to manage the adma and mdma,
+	  otherwise say N.
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 836095a..ba8e79c 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -25,3 +25,5 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
 obj-$(CONFIG_PL330_DMA) += pl330.o
 obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
+obj-$(CONFIG_PXA_DMAC) += pxa_dmac.o
+obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 02/13] dmaengine:open dmac and tdma support
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.open dmaengine for tdma and dmac support

Change-Id: I3251daa57cf15fc96e90fbe8be3a1ee303629bcc
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 drivers/dma/Kconfig  |   19 +++++++++++++++++++
 drivers/dma/Makefile |    2 ++
 2 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 25cf327..483b782 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -237,6 +237,25 @@ config MXS_DMA
 	  Support the MXS DMA engine. This engine including APBH-DMA
 	  and APBX-DMA is integrated into Freescale i.MX23/28 chips.
 
+config PXA_DMAC
+	bool "PXA DMAC support"
+	depends on ARCH_PXA || ARCH_MMP
+	select DMA_ENGINE
+	help
+	  Support the PXA DMAC engine. This engine mainly used for PXA DMAC,
+	  also is integrated into Marvell MMP chips for peripheral DMA engine.
+
+config MMP_TDMA
+	bool "MMP Two-Channel DMA support"
+	depends on ARCH_MMP
+	select DMA_ENGINE
+	help
+	  Support the MMP Two-Channel DMA engine. This engine mainly used for
+	  Audio DMA and Memory DMA, now is integrated into Marvell MMP chips.
+
+	  Say Y here if you want to use tdma to manage the adma and mdma,
+	  otherwise say N.
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 836095a..ba8e79c 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -25,3 +25,5 @@ obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o
 obj-$(CONFIG_PL330_DMA) += pl330.o
 obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o
+obj-$(CONFIG_PXA_DMAC) += pxa_dmac.o
+obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 03/13] dmaengine: mmp: add two-channel dma driver
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1,Add support for two-channel dma, mainly for audio dma
	and memory dma.

Change-Id: I9b0f26e368c451d30fcfd73b0eda211c6f6c0468
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/include/mach/mmp_dma.h |   89 +++-
 drivers/dma/mmp_tdma.c                   |  858 ++++++++++++++++++++++++++++++
 sound/soc/pxa/mmp-pcm.c                  |   11 +-
 sound/soc/pxa/mmp2-squ.c                 |   12 +-
 4 files changed, 948 insertions(+), 22 deletions(-)
 create mode 100644 drivers/dma/mmp_tdma.c

diff --git a/arch/arm/mach-mmp/include/mach/mmp_dma.h b/arch/arm/mach-mmp/include/mach/mmp_dma.h
index a36cdc1..eb39cd1 100644
--- a/arch/arm/mach-mmp/include/mach/mmp_dma.h
+++ b/arch/arm/mach-mmp/include/mach/mmp_dma.h
@@ -1,8 +1,28 @@
 #ifndef __MACH_MMP_DMA_H
 #define __MACH_MMP_DMA_H
 
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#else
 #include <mach/mmp_audisland.h>
+#endif
 
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+/*
+ * Two-Channel DMA registers
+ */
+#define TDBCR			0x0	/* Byte Count Register */
+#define TDSAR			0x10	/* Src Addr Register */
+#define TDDAR			0x20	/* Dst Addr Register */
+#define TDNDPR			0x30	/* Next Desc Pointer Register */
+#define TDCR			0x40	/* Control Register */
+#define TDCP			0x60	/* Priority Register */
+#define TDCDPR			0x70	/* Current Desc Pointer Register */
+#define TDIMR			0x80	/* Int Mask Register */
+#define TDISR			0xa0	/* Int Status Register */
+#define FILLDATA		0xa8	/* Fill Data Register */
+#else
 #define __DMA_REG(x, y)		(*((volatile u32 *)(x + y)))
 
 #define ADMA1_CH0_BASE		(AUD_VIRT_BASE + 0x800)
@@ -25,6 +45,7 @@
 #define TDIMR(base)		__DMA_REG(base, 0x80)   /* Int Mask Register */
 #define TDISR(base)		__DMA_REG(base, 0xa0)   /* Int Status Register */
 #define VDCR(base)		__DMA_REG(base, 0x28)	/* FIXME: Remove VDMA from this file */
+#endif
 
 /* Two-Channel DMA Control Register */
 #define TDCR_SSZ_8_BITS		(0x0 << 22)	/* Sample Size */
@@ -68,6 +89,26 @@
 /* Two-Channel DMA Int Status Register */
 #define TDISR_COMP		(0x1 << 0)
 
+/*
+ * Two-Channel DMA Descriptor Struct
+ * NOTE: desc's buf must be aligned to 16 bytes.
+ */
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+struct mmp_tdma_desc {
+	u32 byte_cnt;
+	u32 src_addr;
+	u32 dst_addr;
+	u32 nxt_desc;
+};
+#else
+typedef struct mmp_tdma_desc {
+	volatile u32 byte_cnt;	/* byte count */
+	volatile u32 src_addr;	/* source address */
+	volatile u32 dst_addr;	/* target address */
+	volatile u32 nxt_desc;	/* next descriptor dress */
+} mmp_tdma_desc;
+#endif
+
 enum mmp_tdma_type {
 	MDMA1_CH0 = 0,
 	MDMA1_CH1,
@@ -80,22 +121,48 @@ enum mmp_tdma_type {
 	DMA_CH_NUM,
 };
 
-/*
- * Two-Channel DMA Descriptor Struct
- * NOTE: desc's buf must be aligned to 16 bytes.
- */
-typedef struct mmp_tdma_desc {
-	volatile u32 byte_cnt;	/* byte count */
-	volatile u32 src_addr;	/* source address */
-	volatile u32 dst_addr;	/* target address */
-	volatile u32 nxt_desc;	/* next descriptor dress */
-} mmp_tdma_desc;
+struct mmp_tdma_chan_info {
+	enum mmp_tdma_type type;
+	unsigned long reg_base;
+};
 
+struct mmp_tdma_platform_data {
+	unsigned int nr_ch;
+	struct mmp_tdma_chan_info *info;
+};
+
+struct mmp_tdma_data {
+	u32 bus_size;
+	u32 pack_mod;
+	int priority;
+};
+
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+static inline int mmp_tdma_is_this_type(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "mmp-tdma");
+}
+
+static inline int mmp_adma_is_this_type(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "mmp-adma");
+}
+
+static inline int mmp_mdma_is_this_type(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "mmp-mdma");
+}
+
+
+extern unsigned long mmp_tdma_chan_get_ptr(struct dma_chan *dmac);
+extern int mmp_tdma_is_specific_chan(struct dma_chan *chan,
+				     enum mmp_tdma_type type);
+#else
 int __init mmp_init_dma(unsigned int irq);
 unsigned int mmp_request_dma(char *name, unsigned int dma_ch,
 		void (*irq_handler)(int, void *), void *data);
 void mmp_free_dma(unsigned int dma_ch);
-
 u32 mmp_get_dma_reg_base(enum mmp_tdma_type dma_type);
+#endif
 
 #endif /* __MACH_MMP_DMA_H */
diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c
new file mode 100644
index 0000000..29209f0
--- /dev/null
+++ b/drivers/dma/mmp_tdma.c
@@ -0,0 +1,858 @@
+/*
+ * drivers/dma/mmp-tdma.c
+ *
+ * Driver for Marvell Two-channel DMA engine
+ *
+ * Copyright 2011 Leo Yan <leoy@marvell.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include <asm/irq.h>
+#include <mach/dma.h>
+#include <mach/mmp_dma.h>
+#include <mach/regs-icu.h>
+#include <mach/sram.h>
+
+#define TDMA_DESC_SIZE		512
+#define TDMA_DESC_NUM		((int)(TDMA_DESC_SIZE / \
+				sizeof(struct mmp_tdma_desc)))
+#define TDMA_MAX_XFER_BYTES	SZ_64K
+#define TDMA_CYCLIC_LOOP	(1 << 0)
+#define TDMA_ALIGNMENT		3
+
+struct mmp_tdma_chan {
+	struct mmp_tdma_engine		*mmp_tdma;
+	struct dma_chan			chan;
+	struct dma_async_tx_descriptor	desc;
+
+	struct mmp_tdma_desc		*desc_arr;
+	phys_addr_t			desc_arr_phys;
+	enum dma_data_direction		dir;
+	dma_addr_t			dev_addr;
+	u32				burst_sz;
+
+	dma_cookie_t			last_completed;
+	enum dma_status			status;
+	unsigned int			flags;
+
+	enum mmp_tdma_type		type;
+	int				irq;
+	unsigned long			reg_base;
+};
+
+struct mmp_tdma_engine {
+	struct device			*dev;
+	unsigned int			version;
+	void __iomem			*base;
+	unsigned int			irq_shift;
+	struct dma_device		tdma_device;
+	struct device_dma_parameters	tdma_parms;
+	unsigned int			tdmac_nr;
+	struct mmp_tdma_chan		tdmac[0];
+};
+
+static DEFINE_SPINLOCK(lock);
+static void mmp_tdma_dump_tdma_list(struct mmp_tdma_chan *tdma_chan)
+{
+	struct mmp_tdma_desc *desc = tdma_chan->desc_arr;
+	unsigned long flags;
+
+	if (!desc) {
+		dev_dbg(tdma_chan->mmp_tdma->dev,
+		 "dma description list has no node!\n");
+		return;
+	}
+
+	spin_lock_irqsave(&lock, flags);
+
+	dev_dbg(tdma_chan->mmp_tdma->dev, "dma description list nodes:\n");
+	do {
+		dev_dbg(tdma_chan->mmp_tdma->dev, "---------------------\n");
+		dev_dbg(tdma_chan->mmp_tdma->dev, "src_addr = 0x%08x\n",
+			desc->src_addr);
+		dev_dbg(tdma_chan->mmp_tdma->dev, "dst_addr = 0x%08x\n",
+			desc->dst_addr);
+		dev_dbg(tdma_chan->mmp_tdma->dev, "byte_cnt = 0x%08x\n",
+			desc->byte_cnt);
+		dev_dbg(tdma_chan->mmp_tdma->dev, "nxt_desc = 0x%08x\n",
+			desc->nxt_desc);
+
+		if (!desc->nxt_desc)
+			break;
+
+		desc = (struct mmp_tdma_desc *)(desc->nxt_desc -
+				(int)tdma_chan->desc_arr_phys +
+				(int)tdma_chan->desc_arr);
+
+	} while (desc != tdma_chan->desc_arr);
+
+	spin_unlock_irqrestore(&lock, flags);
+	return;
+}
+
+static int mmp_tdma_is_adma(struct mmp_tdma_chan *tdmac)
+{
+	if ((tdmac->type >= ADMA1_CH0) &&
+	    (tdmac->type <= ADMA2_CH1))
+		return 1;
+
+	return 0;
+}
+
+static int mmp_tdma_is_mdma(struct mmp_tdma_chan *tdmac)
+{
+	if ((tdmac->type >= MDMA1_CH0) &&
+	    (tdmac->type <= MDMA1_CH1))
+		return 1;
+
+	return 0;
+}
+
+static struct mmp_tdma_chan *to_mmp_tdma_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct mmp_tdma_chan, chan);
+}
+
+static void mmp_tdma_enable_chan(struct mmp_tdma_chan *tdmac)
+{
+	if (mmp_tdma_is_mdma(tdmac))
+		writel(readl(tdmac->reg_base + TDCR) | TDCR_CLK_GATE_ON,
+			tdmac->reg_base + TDCR);
+	/* set dma desc */
+	writel(tdmac->desc_arr_phys, tdmac->reg_base + TDNDPR);
+
+	/* enable irq */
+	writel(TDIMR_COMP, tdmac->reg_base + TDIMR);
+
+	/* enable dma chan */
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN,
+		tdmac->reg_base + TDCR);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: (%x) TDRC:%x TDNDPR:%x\n", __func__,
+		 (int)tdmac->reg_base, readl(tdmac->reg_base + TDCR),
+		 readl(tdmac->reg_base + TDNDPR));
+}
+
+static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac)
+{
+	/* disable dma chan */
+	writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN,
+		tdmac->reg_base + TDCR);
+
+	if (mmp_tdma_is_mdma(tdmac))
+		writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CLK_GATE_ON,
+			tdmac->reg_base + TDCR);
+
+	tdmac->status = DMA_SUCCESS;
+}
+
+static void mmp_tdma_resume_chan(struct mmp_tdma_chan *tdmac)
+{
+	if (mmp_tdma_is_mdma(tdmac))
+		writel(readl(tdmac->reg_base + TDCR) | TDCR_CLK_GATE_ON,
+			tdmac->reg_base + TDCR);
+
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN,
+		tdmac->reg_base + TDCR);
+
+	tdmac->status = DMA_IN_PROGRESS;
+}
+
+static void mmp_tdma_pause_chan(struct mmp_tdma_chan *tdmac)
+{
+	writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN,
+		tdmac->reg_base + TDCR);
+
+	if (mmp_tdma_is_mdma(tdmac))
+		writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CLK_GATE_ON,
+			tdmac->reg_base + TDCR);
+
+	tdmac->status = DMA_PAUSED;
+}
+
+static dma_cookie_t mmp_tdma_assign_cookie(struct mmp_tdma_chan *tdmac)
+{
+	dma_cookie_t cookie = tdmac->chan.cookie;
+
+	if (++cookie < 0)
+		cookie = 1;
+
+	tdmac->chan.cookie = cookie;
+	tdmac->desc.cookie = cookie;
+
+	return cookie;
+}
+
+unsigned long mmp_tdma_chan_get_ptr(struct dma_chan *dmac)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(dmac);
+	int base = tdmac->reg_base;
+
+	if (tdmac->dir == DMA_TO_DEVICE)
+		return readl(base + TDSAR);
+	else
+		return readl(base + TDDAR);
+}
+EXPORT_SYMBOL(mmp_tdma_chan_get_ptr);
+
+int mmp_tdma_is_specific_chan(struct dma_chan *chan, enum mmp_tdma_type type)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+
+	if (tdmac->type == type)
+		return true;
+
+	return false;
+}
+EXPORT_SYMBOL(mmp_tdma_is_specific_chan);
+
+static dma_cookie_t mmp_tdma_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(tx->chan);
+
+	mmp_tdma_enable_chan(tdmac);
+
+	return mmp_tdma_assign_cookie(tdmac);
+}
+
+static irqreturn_t mmp_tdma_int_handler(int irq, void *dev_id)
+{
+	struct mmp_tdma_chan *tdmac = dev_id;
+
+	if (readl(tdmac->reg_base + TDISR) & TDISR_COMP) {
+		/* clear irq */
+		writel(readl(tdmac->reg_base + TDISR) & ~TDISR_COMP,
+			tdmac->reg_base + TDISR);
+
+		if (tdmac->flags & TDMA_CYCLIC_LOOP)
+			tdmac->status = DMA_IN_PROGRESS;
+		else
+			tdmac->status = DMA_SUCCESS;
+
+		if (tdmac->desc.callback)
+			tdmac->desc.callback(tdmac->desc.callback_param);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int mmp_tdma_alloc_descriptor(struct mmp_tdma_chan *tdmac)
+{
+	struct mmp_tdma_engine *mmp_tdma = tdmac->mmp_tdma;
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: enter\n", __func__);
+
+	if (mmp_tdma_is_adma(tdmac)) {
+		tdmac->desc_arr = (void *)sram_alloc("audio sram",
+				TDMA_DESC_SIZE,
+				(dma_addr_t *)&(tdmac->desc_arr_phys));
+		if (!tdmac->desc_arr)
+			return -ENOMEM;
+	} else {
+		tdmac->desc_arr = dma_alloc_coherent(mmp_tdma->dev,
+			TDMA_DESC_SIZE, &tdmac->desc_arr_phys, GFP_KERNEL);
+
+		if (!tdmac->desc_arr)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void mmp_tdma_free_descriptor(struct mmp_tdma_chan *tdmac)
+{
+	struct mmp_tdma_engine *mmp_tdma = tdmac->mmp_tdma;
+
+	if (mmp_tdma_is_adma(tdmac)) {
+		sram_free("audio sram", (void *)tdmac->desc_arr, PAGE_SIZE);
+	} else {
+		dma_free_coherent(mmp_tdma->dev, TDMA_DESC_SIZE,
+			tdmac->desc_arr, tdmac->desc_arr_phys);
+	}
+}
+
+static int mmp_tdma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+	int ret;
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: enter\n", __func__);
+
+	ret = mmp_tdma_alloc_descriptor(tdmac);
+	if (ret < 0)
+		return ret;
+
+	dma_async_tx_descriptor_init(&tdmac->desc, chan);
+	tdmac->desc.tx_submit = mmp_tdma_tx_submit;
+
+	/* the descriptor is ready */
+	async_tx_ack(&tdmac->desc);
+
+	ret = request_irq(tdmac->irq, mmp_tdma_int_handler, IRQF_DISABLED,
+			  "tdma", tdmac);
+	if (ret)
+		goto err_request_irq;
+
+	if (mmp_tdma_is_mdma(tdmac)) {
+		writel(readl(tdmac->reg_base + TDCR) | TDCR_CLK_GATE_CTL,
+			tdmac->reg_base + TDCR);
+		writel(readl(tdmac->reg_base + TDCR) | TDCR_TRANSMOD,
+			tdmac->reg_base + TDCR);
+	}
+
+	return 0;
+
+err_request_irq:
+	mmp_tdma_free_descriptor(tdmac);
+	return ret;
+}
+
+static void mmp_tdma_free_chan_resources(struct dma_chan *chan)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: enter\n", __func__);
+
+	free_irq(tdmac->irq, tdmac);
+	mmp_tdma_disable_chan(tdmac);
+	mmp_tdma_free_descriptor(tdmac);
+}
+
+static int mmp_tdma_config_chan(struct mmp_tdma_chan *tdmac)
+{
+	struct mmp_tdma_data *tdma_data = tdmac->chan.private;
+	unsigned int tdcr;
+	int ret = 0;
+
+	mmp_tdma_disable_chan(tdmac);
+
+	if (tdmac->dir == DMA_TO_DEVICE)
+		tdcr = TDCR_DSTDIR_ADDR_HOLD | TDCR_SRCDIR_ADDR_INC;
+	else if (tdmac->dir == DMA_FROM_DEVICE)
+		tdcr = TDCR_SRCDIR_ADDR_HOLD | TDCR_DSTDIR_ADDR_INC;
+	else
+		tdcr = TDCR_SRCDIR_ADDR_INC | TDCR_DSTDIR_ADDR_INC;
+
+	tdcr |= tdmac->burst_sz;
+
+	if (tdma_data->pack_mod)
+		tdcr |= TDCR_PACKMOD;
+
+	tdcr |= tdma_data->bus_size;
+
+	writel(tdcr, tdmac->reg_base + TDCR);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: (%x) TDCR:%x\n", __func__,
+		 (int)tdmac->reg_base, readl(tdmac->reg_base + TDCR));
+
+	return ret;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_memcpy(
+		struct dma_chan *chan,
+		dma_addr_t dma_dst, dma_addr_t dma_src,
+		size_t len, unsigned long flags)
+{
+	struct mmp_tdma_chan *tdmac;
+	struct mmp_tdma_desc *desc;
+	size_t copy;
+	int i = 0;
+
+	if (!chan)
+		return NULL;
+
+	if (!len)
+		return NULL;
+
+	tdmac = to_mmp_tdma_chan(chan);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: desc_arr %p desc_arr_phys %x\n",
+		__func__, tdmac->desc_arr, tdmac->desc_arr_phys);
+
+	do {
+		dev_dbg(tdmac->mmp_tdma->dev, "%s: dst %x src %x len %d\n",
+			__func__, dma_dst, dma_src, len);
+
+		desc = &tdmac->desc_arr[i];
+		copy = min(len, (size_t)TDMA_MAX_XFER_BYTES);
+
+		desc->dst_addr = dma_dst;
+		desc->src_addr = dma_src;
+		desc->byte_cnt = copy;
+
+		len -= copy;
+		dma_src += copy;
+		dma_dst += copy;
+
+		if (!len)
+			desc->nxt_desc = 0;
+		else
+			desc->nxt_desc = tdmac->desc_arr_phys +
+				sizeof(*desc) * (i + 1);
+
+		i++;
+	} while (len);
+
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_INTMODE,
+		tdmac->reg_base + TDCR);
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND,
+		tdmac->reg_base + TDCR);
+	/* default burst size is 8B, can modify from control api */
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_BURSTSZ_8B,
+		tdmac->reg_base + TDCR);
+	mmp_tdma_dump_tdma_list(tdmac);
+
+	return &tdmac->desc;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_memset(
+		struct dma_chan *chan, dma_addr_t dest, int value, size_t len,
+		unsigned long flags)
+{
+	struct mmp_tdma_chan *tdmac;
+	struct mmp_tdma_desc *desc;
+	size_t copy;
+	int i = 0;
+
+	if (!chan)
+		return NULL;
+
+	if (!len)
+		return NULL;
+
+	tdmac = to_mmp_tdma_chan(chan);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: desc_arr %p desc_arr_phys %x\n",
+		__func__, tdmac->desc_arr, tdmac->desc_arr_phys);
+
+	do {
+		dev_dbg(tdmac->mmp_tdma->dev, "%s: dst %x value %x len %d\n",
+			__func__, dest, value, len);
+
+		desc = &tdmac->desc_arr[i];
+		copy = min(len, (size_t)TDMA_MAX_XFER_BYTES);
+
+		desc->dst_addr = dest;
+		desc->src_addr = value;
+		desc->byte_cnt = copy;
+
+		len -= copy;
+		dest += copy;
+
+		if (!len)
+			desc->nxt_desc = 0;
+		else
+			desc->nxt_desc = tdmac->desc_arr_phys +
+				sizeof(*desc) * (i + 1);
+
+		i++;
+	} while (len);
+
+	writel(value, tdmac->reg_base + FILLDATA);
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_INTMODE,
+		tdmac->reg_base + TDCR);
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND,
+		tdmac->reg_base + TDCR);
+	/* default burst size is 8B, can modify from control api */
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_BURSTSZ_8B,
+		tdmac->reg_base + TDCR);
+
+	mmp_tdma_dump_tdma_list(tdmac);
+	return &tdmac->desc;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_sg(struct dma_chan *chan,
+	struct scatterlist *dst_sg, unsigned int dst_nents,
+	struct scatterlist *src_sg, unsigned int src_nents,
+	unsigned long flags)
+{
+	struct mmp_tdma_chan *tdmac;
+	struct mmp_tdma_desc *desc, *first = NULL, *prev = NULL;
+	size_t dst_avail, src_avail;
+	dma_addr_t dst, src;
+	size_t len;
+	int i = 0;
+
+	/* basic sanity checks */
+	if (dst_nents == 0 || src_nents == 0)
+		return NULL;
+
+	if (dst_sg == NULL || src_sg == NULL)
+		return NULL;
+
+	tdmac = to_mmp_tdma_chan(chan);
+
+	/* get prepared for the loop */
+	dst_avail = sg_dma_len(dst_sg);
+	src_avail = sg_dma_len(src_sg);
+
+	/* run until we are out of scatterlist entries */
+	while (true) {
+		dev_dbg(tdmac->mmp_tdma->dev, "%s: dst_avail %x src_avail %x\n",
+			__func__, dst_avail, src_avail);
+
+		/* create the largest transaction possible */
+		len = min_t(size_t, src_avail, dst_avail);
+		len = min_t(size_t, len, TDMA_MAX_XFER_BYTES);
+		if (len == 0)
+			goto fetch;
+
+		dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_avail;
+		src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_avail;
+
+		dev_dbg(tdmac->mmp_tdma->dev, "%s: dst %x src %x len %x\n",
+			__func__, dst, src, len);
+
+		if (i >= TDMA_DESC_NUM)
+			goto fail;
+
+		desc = &tdmac->desc_arr[i];
+		desc->dst_addr = dst;
+		desc->src_addr = src;
+		desc->byte_cnt = len;
+		desc->nxt_desc = 0;
+
+		if (!first)
+			first = desc;
+		else
+			prev->nxt_desc = tdmac->desc_arr_phys +
+				sizeof(*desc) * i;
+
+		tdmac->desc.cookie = 0;
+		async_tx_ack(&tdmac->desc);
+		prev = desc;
+
+		/* update metadata */
+		dst_avail -= len;
+		src_avail -= len;
+		i++;
+
+fetch:
+		/* fetch the next dst scatterlist entry */
+		if (dst_avail == 0) {
+
+			/* no more entries: we're done */
+			if (dst_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			dst_sg = sg_next(dst_sg);
+			if (dst_sg == NULL)
+				break;
+
+			dst_nents--;
+			dst_avail = sg_dma_len(dst_sg);
+		}
+
+		/* fetch the next src scatterlist entry */
+		if (src_avail == 0) {
+
+			/* no more entries: we're done */
+			if (src_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			src_sg = sg_next(src_sg);
+			if (src_sg == NULL)
+				break;
+
+			src_nents--;
+			src_avail = sg_dma_len(src_sg);
+		}
+	}
+
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_INTMODE,
+		tdmac->reg_base + TDCR);
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND,
+		tdmac->reg_base + TDCR);
+	/* default burst size is 8B, can modify from control api */
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_BURSTSZ_8B,
+		tdmac->reg_base + TDCR);
+	mmp_tdma_dump_tdma_list(tdmac);
+
+	return &tdmac->desc;
+
+fail:
+	return NULL;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_slave_sg(
+		struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_data_direction direction,
+		unsigned long append)
+{
+	/*
+	 * This operation is not supported on the TDMA controller
+	 *
+	 * However, we need to provide the function pointer to allow the
+	 * device_control() method to work.
+	 */
+	return NULL;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic(
+		struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
+		size_t period_len, enum dma_data_direction direction)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+	struct mmp_tdma_engine *mmp_tdma = tdmac->mmp_tdma;
+	int num_periods = buf_len / period_len;
+	int i = 0, buf = 0;
+
+	if (tdmac->status == DMA_IN_PROGRESS)
+		return NULL;
+
+	tdmac->status = DMA_IN_PROGRESS;
+	tdmac->flags |= TDMA_CYCLIC_LOOP;
+
+	if (num_periods > TDMA_DESC_NUM) {
+		dev_err(mmp_tdma->tdma_device.dev,
+				"maximum number of sg exceeded: %d > %d\n",
+				num_periods, TDMA_DESC_NUM);
+		goto err_out;
+	}
+
+	if (period_len > TDMA_MAX_XFER_BYTES) {
+		dev_err(mmp_tdma->tdma_device.dev,
+				"maximum period size exceeded: %d > %d\n",
+				period_len, TDMA_MAX_XFER_BYTES);
+		goto err_out;
+	}
+
+	while (buf < buf_len) {
+		struct mmp_tdma_desc *desc = &tdmac->desc_arr[i];
+
+		if (i + 1 == num_periods)
+			desc->nxt_desc = tdmac->desc_arr_phys;
+		else
+			desc->nxt_desc = tdmac->desc_arr_phys +
+				sizeof(*desc) * (i + 1);;
+
+		if (direction == DMA_TO_DEVICE) {
+			desc->src_addr = dma_addr;
+			desc->dst_addr = tdmac->dev_addr;
+		} else {
+			desc->src_addr = tdmac->dev_addr;
+			desc->dst_addr = dma_addr;
+		}
+
+		desc->byte_cnt = period_len;
+
+		dma_addr += period_len;
+		buf += period_len;
+
+		i++;
+	}
+
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND,
+		tdmac->reg_base + TDCR);
+	mmp_tdma_dump_tdma_list(tdmac);
+
+	return &tdmac->desc;
+
+err_out:
+	tdmac->status = DMA_ERROR;
+	return NULL;
+}
+
+static int mmp_tdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+		unsigned long arg)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+	struct dma_slave_config *dmaengine_cfg = (void *)arg;
+	int ret = 0;
+
+	switch (cmd) {
+	case DMA_TERMINATE_ALL:
+		mmp_tdma_disable_chan(tdmac);
+		break;
+	case DMA_PAUSE:
+		mmp_tdma_pause_chan(tdmac);
+		break;
+	case DMA_RESUME:
+		mmp_tdma_resume_chan(tdmac);
+		break;
+	case DMA_SLAVE_CONFIG:
+		if (dmaengine_cfg->direction == DMA_FROM_DEVICE) {
+			tdmac->dev_addr = dmaengine_cfg->src_addr;
+			tdmac->burst_sz = dmaengine_cfg->src_maxburst;
+		} else {
+			tdmac->dev_addr = dmaengine_cfg->dst_addr;
+			tdmac->burst_sz = dmaengine_cfg->dst_maxburst;
+		}
+		tdmac->dir = dmaengine_cfg->direction;
+		return mmp_tdma_config_chan(tdmac);
+	default:
+		ret = -ENOSYS;
+	}
+
+	return ret;
+}
+
+static enum dma_status mmp_tdma_tx_status(struct dma_chan *chan,
+			dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+	dma_cookie_t last_used;
+
+	last_used = chan->cookie;
+	dma_set_tx_state(txstate, tdmac->last_completed, last_used, 0);
+
+	return tdmac->status;
+}
+
+static void mmp_tdma_issue_pending(struct dma_chan *chan)
+{
+	/*
+	 * Nothing to do. We only have a single descriptor.
+	 */
+}
+
+static int __devinit mmp_tdma_probe(struct platform_device *pdev)
+{
+	struct mmp_tdma_engine *mmp_tdma;
+	struct mmp_tdma_chan *tdmac;
+	struct resource *iores;
+	struct mmp_tdma_platform_data *pdata = pdev->dev.platform_data;
+	struct mmp_tdma_chan_info *info;
+	int ret;
+	int irq;
+	int i;
+
+	if (!pdata || !pdata->info)
+		return -ENODEV;
+
+	info = pdata->info;
+
+	mmp_tdma = kzalloc(pdata->nr_ch * sizeof(*tdmac) +
+			   sizeof(*mmp_tdma), GFP_KERNEL);
+	if (!mmp_tdma)
+		return -ENOMEM;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!iores || irq < 0) {
+		ret = -EINVAL;
+		goto err_irq;
+	}
+
+	if (!request_mem_region(iores->start, resource_size(iores),
+				pdev->name)) {
+		ret = -EBUSY;
+		goto err_request_region;
+	}
+
+	mmp_tdma->base = ioremap(iores->start, resource_size(iores));
+	if (!mmp_tdma->base) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	dma_cap_set(DMA_MEMCPY, mmp_tdma->tdma_device.cap_mask);
+	dma_cap_set(DMA_MEMSET, mmp_tdma->tdma_device.cap_mask);
+	dma_cap_set(DMA_SLAVE, mmp_tdma->tdma_device.cap_mask);
+	dma_cap_set(DMA_SG, mmp_tdma->tdma_device.cap_mask);
+	dma_cap_set(DMA_CYCLIC, mmp_tdma->tdma_device.cap_mask);
+
+	INIT_LIST_HEAD(&mmp_tdma->tdma_device.channels);
+
+	/* initialize channel parameters */
+	for (i = 0; i < pdata->nr_ch; i++) {
+
+		BUG_ON((info[i].reg_base < iores->start) ||
+		       (info[i].reg_base >= iores->start +
+		       resource_size(iores)));
+
+		tdmac = &mmp_tdma->tdmac[i];
+
+		tdmac->mmp_tdma	   = mmp_tdma;
+		tdmac->chan.device = &mmp_tdma->tdma_device;
+		tdmac->type	   = info[i].type;
+		tdmac->irq	   = irq + i;
+		tdmac->reg_base	   = (unsigned long)mmp_tdma->base +
+				info[i].reg_base - iores->start;
+
+		/* add the channel to tdma_chan list */
+		list_add_tail(&tdmac->chan.device_node,
+			      &mmp_tdma->tdma_device.channels);
+	}
+
+	mmp_tdma->dev = &pdev->dev;
+	mmp_tdma->tdmac_nr  = pdata->nr_ch;
+	mmp_tdma->tdma_device.dev = &pdev->dev;
+	mmp_tdma->tdma_device.device_alloc_chan_resources =
+					mmp_tdma_alloc_chan_resources;
+	mmp_tdma->tdma_device.device_free_chan_resources =
+					mmp_tdma_free_chan_resources;
+	mmp_tdma->tdma_device.device_prep_dma_memcpy = mmp_tdma_prep_memcpy;
+	mmp_tdma->tdma_device.device_prep_dma_memset = mmp_tdma_prep_memset;
+	mmp_tdma->tdma_device.device_prep_slave_sg = mmp_tdma_prep_slave_sg;
+	mmp_tdma->tdma_device.device_prep_dma_sg = mmp_tdma_prep_sg;
+	mmp_tdma->tdma_device.device_prep_dma_cyclic = mmp_tdma_prep_dma_cyclic;
+	mmp_tdma->tdma_device.device_tx_status = mmp_tdma_tx_status;
+	mmp_tdma->tdma_device.device_issue_pending = mmp_tdma_issue_pending;
+	mmp_tdma->tdma_device.device_control = mmp_tdma_control;
+	mmp_tdma->tdma_device.copy_align = TDMA_ALIGNMENT;
+
+	dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+
+	ret = dma_async_device_register(&mmp_tdma->tdma_device);
+	if (ret) {
+		dev_err(mmp_tdma->tdma_device.dev, "unable to register\n");
+		goto err_init;
+	}
+
+	dev_info(mmp_tdma->tdma_device.dev, "initialized\n");
+	return 0;
+
+err_init:
+	iounmap(mmp_tdma->base);
+err_ioremap:
+	release_mem_region(iores->start, resource_size(iores));
+err_request_region:
+err_irq:
+	kfree(mmp_tdma);
+	return ret;
+}
+
+static const struct platform_device_id mmp_tdma_id_table[] = {
+	{ "mmp-adma", },
+	{ "mmp-mdma", },
+	{ },
+};
+
+static struct platform_driver mmp_tdma_driver = {
+	.driver		= {
+		.name	= "mmp-tdma",
+	},
+	.id_table	= mmp_tdma_id_table,
+};
+
+static int __init mmp_tdma_module_init(void)
+{
+	return platform_driver_probe(&mmp_tdma_driver, mmp_tdma_probe);
+}
+subsys_initcall(mmp_tdma_module_init);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("MMP Two-Channel DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 85495f4..7469e19 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -43,7 +43,8 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware = {
 	.period_bytes_min	= 1024,
 	.period_bytes_max	= 2048,
 	.periods_min		= 2,
-	.periods_max		= MMP2_ADMA_DESC_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max		= MMP2_ADMA_DESC_SIZE /
+				sizeof(struct mmp_tdma_desc),
 	.buffer_bytes_max	= MMP2_DDR_BUF_SIZE,
 	.fifo_size		= 32,
 };
@@ -53,7 +54,7 @@ static DECLARE_WAIT_QUEUE_HEAD(dma_wq);
 #ifdef DEBUG
 static void mmp2_pcm_dump_adma_list(struct mmp2_runtime_data *prtd)
 {
-	mmp_tdma_desc *adma_desc;
+	struct mmp_tdma_desc *adma_desc;
 
 	pr_debug("audio dma list description is:\n");
 	adma_desc = prtd->adma_desc_array;
@@ -64,7 +65,7 @@ static void mmp2_pcm_dump_adma_list(struct mmp2_runtime_data *prtd)
 		pr_debug("byte_cnt = 0x%08x\n", adma_desc->byte_cnt);
 		pr_debug("nxt_desc = 0x%08x\n", adma_desc->nxt_desc);
 
-		adma_desc = (mmp_tdma_desc *)(adma_desc->nxt_desc -
+		adma_desc = (struct mmp_tdma_desc *)(adma_desc->nxt_desc -
 				(int)prtd->adma_desc_array_phys +
 				(int)prtd->adma_desc_array);
 
@@ -245,7 +246,7 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	struct mmp2_adma_params *dma;
 	size_t totsize = params_buffer_bytes(params);
 	size_t period = params_period_bytes(params);
-	mmp_tdma_desc *adma_desc;
+	struct mmp_tdma_desc *adma_desc;
 	dma_addr_t dma_buff_phys, next_desc_phys;
 	int ret;
 
@@ -298,7 +299,7 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	adma_desc      = prtd->adma_desc_array;
 
 	do {
-		next_desc_phys += sizeof(mmp_tdma_desc);
+		next_desc_phys += sizeof(struct mmp_tdma_desc);
 
 		adma_desc->nxt_desc = next_desc_phys;
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff --git a/sound/soc/pxa/mmp2-squ.c b/sound/soc/pxa/mmp2-squ.c
index 986cc59..6d3cc10 100644
--- a/sound/soc/pxa/mmp2-squ.c
+++ b/sound/soc/pxa/mmp2-squ.c
@@ -56,7 +56,7 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware_playback = {
 	.period_bytes_min	= 1024,
 	.period_bytes_max	= 2048,
 	.periods_min		= 2,
-	.periods_max		= PAGE_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max		= PAGE_SIZE / sizeof(struct mmp_tdma_desc),
 	.buffer_bytes_max	= PAGE_SIZE,
 	.fifo_size		= 32,
 };
@@ -73,7 +73,7 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware_capture = {
 	.period_bytes_min	= 1024,
 	.period_bytes_max	= 2048,
 	.periods_min		= 2,
-	.periods_max		= PAGE_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max		= PAGE_SIZE / sizeofs(struct mmp_tdma_desc),
 	.buffer_bytes_max	= PAGE_SIZE,
 	.fifo_size		= 32,
 };
@@ -90,7 +90,7 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware_playback = {
 	.period_bytes_min = 32,
 	.period_bytes_max = 10 * 1024,
 	.periods_min = 1,
-	.periods_max = PAGE_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max = PAGE_SIZE / sizeof(struct mmp_tdma_desc),
 	.buffer_bytes_max = 24 * 1024,
 	.fifo_size = 32,
 };
@@ -105,7 +105,7 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware_capture = {
 	.period_bytes_min = 32,
 	.period_bytes_max = 10 * 1024,
 	.periods_min = 1,
-	.periods_max = PAGE_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max = PAGE_SIZE / sizeof(struct mmp_tdma_desc),
 	.buffer_bytes_max = 24 * 1024,
 	.fifo_size = 32,
 };
@@ -157,7 +157,7 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	struct mmp2_adma_params *dma;
 	size_t totsize = params_buffer_bytes(params);
 	size_t period = params_period_bytes(params);
-	mmp_tdma_desc *adma_desc;
+	struct mmp_tdma_desc *adma_desc;
 	dma_addr_t dma_buff_phys, next_desc_phys;
 	int ret;
 
@@ -197,7 +197,7 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 
 	adma_desc = prtd->adma_desc_array;
 	do {
-		next_desc_phys += sizeof(mmp_tdma_desc);
+		next_desc_phys += sizeof(struct mmp_tdma_desc);
 
 		adma_desc->nxt_desc = next_desc_phys;
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 03/13] dmaengine: mmp: add two-channel dma driver
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1,Add support for two-channel dma, mainly for audio dma
	and memory dma.

Change-Id: I9b0f26e368c451d30fcfd73b0eda211c6f6c0468
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/include/mach/mmp_dma.h |   89 +++-
 drivers/dma/mmp_tdma.c                   |  858 ++++++++++++++++++++++++++++++
 sound/soc/pxa/mmp-pcm.c                  |   11 +-
 sound/soc/pxa/mmp2-squ.c                 |   12 +-
 4 files changed, 948 insertions(+), 22 deletions(-)
 create mode 100644 drivers/dma/mmp_tdma.c

diff --git a/arch/arm/mach-mmp/include/mach/mmp_dma.h b/arch/arm/mach-mmp/include/mach/mmp_dma.h
index a36cdc1..eb39cd1 100644
--- a/arch/arm/mach-mmp/include/mach/mmp_dma.h
+++ b/arch/arm/mach-mmp/include/mach/mmp_dma.h
@@ -1,8 +1,28 @@
 #ifndef __MACH_MMP_DMA_H
 #define __MACH_MMP_DMA_H
 
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#else
 #include <mach/mmp_audisland.h>
+#endif
 
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+/*
+ * Two-Channel DMA registers
+ */
+#define TDBCR			0x0	/* Byte Count Register */
+#define TDSAR			0x10	/* Src Addr Register */
+#define TDDAR			0x20	/* Dst Addr Register */
+#define TDNDPR			0x30	/* Next Desc Pointer Register */
+#define TDCR			0x40	/* Control Register */
+#define TDCP			0x60	/* Priority Register */
+#define TDCDPR			0x70	/* Current Desc Pointer Register */
+#define TDIMR			0x80	/* Int Mask Register */
+#define TDISR			0xa0	/* Int Status Register */
+#define FILLDATA		0xa8	/* Fill Data Register */
+#else
 #define __DMA_REG(x, y)		(*((volatile u32 *)(x + y)))
 
 #define ADMA1_CH0_BASE		(AUD_VIRT_BASE + 0x800)
@@ -25,6 +45,7 @@
 #define TDIMR(base)		__DMA_REG(base, 0x80)   /* Int Mask Register */
 #define TDISR(base)		__DMA_REG(base, 0xa0)   /* Int Status Register */
 #define VDCR(base)		__DMA_REG(base, 0x28)	/* FIXME: Remove VDMA from this file */
+#endif
 
 /* Two-Channel DMA Control Register */
 #define TDCR_SSZ_8_BITS		(0x0 << 22)	/* Sample Size */
@@ -68,6 +89,26 @@
 /* Two-Channel DMA Int Status Register */
 #define TDISR_COMP		(0x1 << 0)
 
+/*
+ * Two-Channel DMA Descriptor Struct
+ * NOTE: desc's buf must be aligned to 16 bytes.
+ */
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+struct mmp_tdma_desc {
+	u32 byte_cnt;
+	u32 src_addr;
+	u32 dst_addr;
+	u32 nxt_desc;
+};
+#else
+typedef struct mmp_tdma_desc {
+	volatile u32 byte_cnt;	/* byte count */
+	volatile u32 src_addr;	/* source address */
+	volatile u32 dst_addr;	/* target address */
+	volatile u32 nxt_desc;	/* next descriptor dress */
+} mmp_tdma_desc;
+#endif
+
 enum mmp_tdma_type {
 	MDMA1_CH0 = 0,
 	MDMA1_CH1,
@@ -80,22 +121,48 @@ enum mmp_tdma_type {
 	DMA_CH_NUM,
 };
 
-/*
- * Two-Channel DMA Descriptor Struct
- * NOTE: desc's buf must be aligned to 16 bytes.
- */
-typedef struct mmp_tdma_desc {
-	volatile u32 byte_cnt;	/* byte count */
-	volatile u32 src_addr;	/* source address */
-	volatile u32 dst_addr;	/* target address */
-	volatile u32 nxt_desc;	/* next descriptor dress */
-} mmp_tdma_desc;
+struct mmp_tdma_chan_info {
+	enum mmp_tdma_type type;
+	unsigned long reg_base;
+};
 
+struct mmp_tdma_platform_data {
+	unsigned int nr_ch;
+	struct mmp_tdma_chan_info *info;
+};
+
+struct mmp_tdma_data {
+	u32 bus_size;
+	u32 pack_mod;
+	int priority;
+};
+
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+static inline int mmp_tdma_is_this_type(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "mmp-tdma");
+}
+
+static inline int mmp_adma_is_this_type(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "mmp-adma");
+}
+
+static inline int mmp_mdma_is_this_type(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "mmp-mdma");
+}
+
+
+extern unsigned long mmp_tdma_chan_get_ptr(struct dma_chan *dmac);
+extern int mmp_tdma_is_specific_chan(struct dma_chan *chan,
+				     enum mmp_tdma_type type);
+#else
 int __init mmp_init_dma(unsigned int irq);
 unsigned int mmp_request_dma(char *name, unsigned int dma_ch,
 		void (*irq_handler)(int, void *), void *data);
 void mmp_free_dma(unsigned int dma_ch);
-
 u32 mmp_get_dma_reg_base(enum mmp_tdma_type dma_type);
+#endif
 
 #endif /* __MACH_MMP_DMA_H */
diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c
new file mode 100644
index 0000000..29209f0
--- /dev/null
+++ b/drivers/dma/mmp_tdma.c
@@ -0,0 +1,858 @@
+/*
+ * drivers/dma/mmp-tdma.c
+ *
+ * Driver for Marvell Two-channel DMA engine
+ *
+ * Copyright 2011 Leo Yan <leoy@marvell.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include <asm/irq.h>
+#include <mach/dma.h>
+#include <mach/mmp_dma.h>
+#include <mach/regs-icu.h>
+#include <mach/sram.h>
+
+#define TDMA_DESC_SIZE		512
+#define TDMA_DESC_NUM		((int)(TDMA_DESC_SIZE / \
+				sizeof(struct mmp_tdma_desc)))
+#define TDMA_MAX_XFER_BYTES	SZ_64K
+#define TDMA_CYCLIC_LOOP	(1 << 0)
+#define TDMA_ALIGNMENT		3
+
+struct mmp_tdma_chan {
+	struct mmp_tdma_engine		*mmp_tdma;
+	struct dma_chan			chan;
+	struct dma_async_tx_descriptor	desc;
+
+	struct mmp_tdma_desc		*desc_arr;
+	phys_addr_t			desc_arr_phys;
+	enum dma_data_direction		dir;
+	dma_addr_t			dev_addr;
+	u32				burst_sz;
+
+	dma_cookie_t			last_completed;
+	enum dma_status			status;
+	unsigned int			flags;
+
+	enum mmp_tdma_type		type;
+	int				irq;
+	unsigned long			reg_base;
+};
+
+struct mmp_tdma_engine {
+	struct device			*dev;
+	unsigned int			version;
+	void __iomem			*base;
+	unsigned int			irq_shift;
+	struct dma_device		tdma_device;
+	struct device_dma_parameters	tdma_parms;
+	unsigned int			tdmac_nr;
+	struct mmp_tdma_chan		tdmac[0];
+};
+
+static DEFINE_SPINLOCK(lock);
+static void mmp_tdma_dump_tdma_list(struct mmp_tdma_chan *tdma_chan)
+{
+	struct mmp_tdma_desc *desc = tdma_chan->desc_arr;
+	unsigned long flags;
+
+	if (!desc) {
+		dev_dbg(tdma_chan->mmp_tdma->dev,
+		 "dma description list has no node!\n");
+		return;
+	}
+
+	spin_lock_irqsave(&lock, flags);
+
+	dev_dbg(tdma_chan->mmp_tdma->dev, "dma description list nodes:\n");
+	do {
+		dev_dbg(tdma_chan->mmp_tdma->dev, "---------------------\n");
+		dev_dbg(tdma_chan->mmp_tdma->dev, "src_addr = 0x%08x\n",
+			desc->src_addr);
+		dev_dbg(tdma_chan->mmp_tdma->dev, "dst_addr = 0x%08x\n",
+			desc->dst_addr);
+		dev_dbg(tdma_chan->mmp_tdma->dev, "byte_cnt = 0x%08x\n",
+			desc->byte_cnt);
+		dev_dbg(tdma_chan->mmp_tdma->dev, "nxt_desc = 0x%08x\n",
+			desc->nxt_desc);
+
+		if (!desc->nxt_desc)
+			break;
+
+		desc = (struct mmp_tdma_desc *)(desc->nxt_desc -
+				(int)tdma_chan->desc_arr_phys +
+				(int)tdma_chan->desc_arr);
+
+	} while (desc != tdma_chan->desc_arr);
+
+	spin_unlock_irqrestore(&lock, flags);
+	return;
+}
+
+static int mmp_tdma_is_adma(struct mmp_tdma_chan *tdmac)
+{
+	if ((tdmac->type >= ADMA1_CH0) &&
+	    (tdmac->type <= ADMA2_CH1))
+		return 1;
+
+	return 0;
+}
+
+static int mmp_tdma_is_mdma(struct mmp_tdma_chan *tdmac)
+{
+	if ((tdmac->type >= MDMA1_CH0) &&
+	    (tdmac->type <= MDMA1_CH1))
+		return 1;
+
+	return 0;
+}
+
+static struct mmp_tdma_chan *to_mmp_tdma_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct mmp_tdma_chan, chan);
+}
+
+static void mmp_tdma_enable_chan(struct mmp_tdma_chan *tdmac)
+{
+	if (mmp_tdma_is_mdma(tdmac))
+		writel(readl(tdmac->reg_base + TDCR) | TDCR_CLK_GATE_ON,
+			tdmac->reg_base + TDCR);
+	/* set dma desc */
+	writel(tdmac->desc_arr_phys, tdmac->reg_base + TDNDPR);
+
+	/* enable irq */
+	writel(TDIMR_COMP, tdmac->reg_base + TDIMR);
+
+	/* enable dma chan */
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN,
+		tdmac->reg_base + TDCR);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: (%x) TDRC:%x TDNDPR:%x\n", __func__,
+		 (int)tdmac->reg_base, readl(tdmac->reg_base + TDCR),
+		 readl(tdmac->reg_base + TDNDPR));
+}
+
+static void mmp_tdma_disable_chan(struct mmp_tdma_chan *tdmac)
+{
+	/* disable dma chan */
+	writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN,
+		tdmac->reg_base + TDCR);
+
+	if (mmp_tdma_is_mdma(tdmac))
+		writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CLK_GATE_ON,
+			tdmac->reg_base + TDCR);
+
+	tdmac->status = DMA_SUCCESS;
+}
+
+static void mmp_tdma_resume_chan(struct mmp_tdma_chan *tdmac)
+{
+	if (mmp_tdma_is_mdma(tdmac))
+		writel(readl(tdmac->reg_base + TDCR) | TDCR_CLK_GATE_ON,
+			tdmac->reg_base + TDCR);
+
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_CHANEN,
+		tdmac->reg_base + TDCR);
+
+	tdmac->status = DMA_IN_PROGRESS;
+}
+
+static void mmp_tdma_pause_chan(struct mmp_tdma_chan *tdmac)
+{
+	writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CHANEN,
+		tdmac->reg_base + TDCR);
+
+	if (mmp_tdma_is_mdma(tdmac))
+		writel(readl(tdmac->reg_base + TDCR) & ~TDCR_CLK_GATE_ON,
+			tdmac->reg_base + TDCR);
+
+	tdmac->status = DMA_PAUSED;
+}
+
+static dma_cookie_t mmp_tdma_assign_cookie(struct mmp_tdma_chan *tdmac)
+{
+	dma_cookie_t cookie = tdmac->chan.cookie;
+
+	if (++cookie < 0)
+		cookie = 1;
+
+	tdmac->chan.cookie = cookie;
+	tdmac->desc.cookie = cookie;
+
+	return cookie;
+}
+
+unsigned long mmp_tdma_chan_get_ptr(struct dma_chan *dmac)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(dmac);
+	int base = tdmac->reg_base;
+
+	if (tdmac->dir == DMA_TO_DEVICE)
+		return readl(base + TDSAR);
+	else
+		return readl(base + TDDAR);
+}
+EXPORT_SYMBOL(mmp_tdma_chan_get_ptr);
+
+int mmp_tdma_is_specific_chan(struct dma_chan *chan, enum mmp_tdma_type type)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+
+	if (tdmac->type == type)
+		return true;
+
+	return false;
+}
+EXPORT_SYMBOL(mmp_tdma_is_specific_chan);
+
+static dma_cookie_t mmp_tdma_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(tx->chan);
+
+	mmp_tdma_enable_chan(tdmac);
+
+	return mmp_tdma_assign_cookie(tdmac);
+}
+
+static irqreturn_t mmp_tdma_int_handler(int irq, void *dev_id)
+{
+	struct mmp_tdma_chan *tdmac = dev_id;
+
+	if (readl(tdmac->reg_base + TDISR) & TDISR_COMP) {
+		/* clear irq */
+		writel(readl(tdmac->reg_base + TDISR) & ~TDISR_COMP,
+			tdmac->reg_base + TDISR);
+
+		if (tdmac->flags & TDMA_CYCLIC_LOOP)
+			tdmac->status = DMA_IN_PROGRESS;
+		else
+			tdmac->status = DMA_SUCCESS;
+
+		if (tdmac->desc.callback)
+			tdmac->desc.callback(tdmac->desc.callback_param);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int mmp_tdma_alloc_descriptor(struct mmp_tdma_chan *tdmac)
+{
+	struct mmp_tdma_engine *mmp_tdma = tdmac->mmp_tdma;
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: enter\n", __func__);
+
+	if (mmp_tdma_is_adma(tdmac)) {
+		tdmac->desc_arr = (void *)sram_alloc("audio sram",
+				TDMA_DESC_SIZE,
+				(dma_addr_t *)&(tdmac->desc_arr_phys));
+		if (!tdmac->desc_arr)
+			return -ENOMEM;
+	} else {
+		tdmac->desc_arr = dma_alloc_coherent(mmp_tdma->dev,
+			TDMA_DESC_SIZE, &tdmac->desc_arr_phys, GFP_KERNEL);
+
+		if (!tdmac->desc_arr)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void mmp_tdma_free_descriptor(struct mmp_tdma_chan *tdmac)
+{
+	struct mmp_tdma_engine *mmp_tdma = tdmac->mmp_tdma;
+
+	if (mmp_tdma_is_adma(tdmac)) {
+		sram_free("audio sram", (void *)tdmac->desc_arr, PAGE_SIZE);
+	} else {
+		dma_free_coherent(mmp_tdma->dev, TDMA_DESC_SIZE,
+			tdmac->desc_arr, tdmac->desc_arr_phys);
+	}
+}
+
+static int mmp_tdma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+	int ret;
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: enter\n", __func__);
+
+	ret = mmp_tdma_alloc_descriptor(tdmac);
+	if (ret < 0)
+		return ret;
+
+	dma_async_tx_descriptor_init(&tdmac->desc, chan);
+	tdmac->desc.tx_submit = mmp_tdma_tx_submit;
+
+	/* the descriptor is ready */
+	async_tx_ack(&tdmac->desc);
+
+	ret = request_irq(tdmac->irq, mmp_tdma_int_handler, IRQF_DISABLED,
+			  "tdma", tdmac);
+	if (ret)
+		goto err_request_irq;
+
+	if (mmp_tdma_is_mdma(tdmac)) {
+		writel(readl(tdmac->reg_base + TDCR) | TDCR_CLK_GATE_CTL,
+			tdmac->reg_base + TDCR);
+		writel(readl(tdmac->reg_base + TDCR) | TDCR_TRANSMOD,
+			tdmac->reg_base + TDCR);
+	}
+
+	return 0;
+
+err_request_irq:
+	mmp_tdma_free_descriptor(tdmac);
+	return ret;
+}
+
+static void mmp_tdma_free_chan_resources(struct dma_chan *chan)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: enter\n", __func__);
+
+	free_irq(tdmac->irq, tdmac);
+	mmp_tdma_disable_chan(tdmac);
+	mmp_tdma_free_descriptor(tdmac);
+}
+
+static int mmp_tdma_config_chan(struct mmp_tdma_chan *tdmac)
+{
+	struct mmp_tdma_data *tdma_data = tdmac->chan.private;
+	unsigned int tdcr;
+	int ret = 0;
+
+	mmp_tdma_disable_chan(tdmac);
+
+	if (tdmac->dir == DMA_TO_DEVICE)
+		tdcr = TDCR_DSTDIR_ADDR_HOLD | TDCR_SRCDIR_ADDR_INC;
+	else if (tdmac->dir == DMA_FROM_DEVICE)
+		tdcr = TDCR_SRCDIR_ADDR_HOLD | TDCR_DSTDIR_ADDR_INC;
+	else
+		tdcr = TDCR_SRCDIR_ADDR_INC | TDCR_DSTDIR_ADDR_INC;
+
+	tdcr |= tdmac->burst_sz;
+
+	if (tdma_data->pack_mod)
+		tdcr |= TDCR_PACKMOD;
+
+	tdcr |= tdma_data->bus_size;
+
+	writel(tdcr, tdmac->reg_base + TDCR);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: (%x) TDCR:%x\n", __func__,
+		 (int)tdmac->reg_base, readl(tdmac->reg_base + TDCR));
+
+	return ret;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_memcpy(
+		struct dma_chan *chan,
+		dma_addr_t dma_dst, dma_addr_t dma_src,
+		size_t len, unsigned long flags)
+{
+	struct mmp_tdma_chan *tdmac;
+	struct mmp_tdma_desc *desc;
+	size_t copy;
+	int i = 0;
+
+	if (!chan)
+		return NULL;
+
+	if (!len)
+		return NULL;
+
+	tdmac = to_mmp_tdma_chan(chan);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: desc_arr %p desc_arr_phys %x\n",
+		__func__, tdmac->desc_arr, tdmac->desc_arr_phys);
+
+	do {
+		dev_dbg(tdmac->mmp_tdma->dev, "%s: dst %x src %x len %d\n",
+			__func__, dma_dst, dma_src, len);
+
+		desc = &tdmac->desc_arr[i];
+		copy = min(len, (size_t)TDMA_MAX_XFER_BYTES);
+
+		desc->dst_addr = dma_dst;
+		desc->src_addr = dma_src;
+		desc->byte_cnt = copy;
+
+		len -= copy;
+		dma_src += copy;
+		dma_dst += copy;
+
+		if (!len)
+			desc->nxt_desc = 0;
+		else
+			desc->nxt_desc = tdmac->desc_arr_phys +
+				sizeof(*desc) * (i + 1);
+
+		i++;
+	} while (len);
+
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_INTMODE,
+		tdmac->reg_base + TDCR);
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND,
+		tdmac->reg_base + TDCR);
+	/* default burst size is 8B, can modify from control api */
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_BURSTSZ_8B,
+		tdmac->reg_base + TDCR);
+	mmp_tdma_dump_tdma_list(tdmac);
+
+	return &tdmac->desc;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_memset(
+		struct dma_chan *chan, dma_addr_t dest, int value, size_t len,
+		unsigned long flags)
+{
+	struct mmp_tdma_chan *tdmac;
+	struct mmp_tdma_desc *desc;
+	size_t copy;
+	int i = 0;
+
+	if (!chan)
+		return NULL;
+
+	if (!len)
+		return NULL;
+
+	tdmac = to_mmp_tdma_chan(chan);
+
+	dev_dbg(tdmac->mmp_tdma->dev, "%s: desc_arr %p desc_arr_phys %x\n",
+		__func__, tdmac->desc_arr, tdmac->desc_arr_phys);
+
+	do {
+		dev_dbg(tdmac->mmp_tdma->dev, "%s: dst %x value %x len %d\n",
+			__func__, dest, value, len);
+
+		desc = &tdmac->desc_arr[i];
+		copy = min(len, (size_t)TDMA_MAX_XFER_BYTES);
+
+		desc->dst_addr = dest;
+		desc->src_addr = value;
+		desc->byte_cnt = copy;
+
+		len -= copy;
+		dest += copy;
+
+		if (!len)
+			desc->nxt_desc = 0;
+		else
+			desc->nxt_desc = tdmac->desc_arr_phys +
+				sizeof(*desc) * (i + 1);
+
+		i++;
+	} while (len);
+
+	writel(value, tdmac->reg_base + FILLDATA);
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_INTMODE,
+		tdmac->reg_base + TDCR);
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND,
+		tdmac->reg_base + TDCR);
+	/* default burst size is 8B, can modify from control api */
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_BURSTSZ_8B,
+		tdmac->reg_base + TDCR);
+
+	mmp_tdma_dump_tdma_list(tdmac);
+	return &tdmac->desc;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_sg(struct dma_chan *chan,
+	struct scatterlist *dst_sg, unsigned int dst_nents,
+	struct scatterlist *src_sg, unsigned int src_nents,
+	unsigned long flags)
+{
+	struct mmp_tdma_chan *tdmac;
+	struct mmp_tdma_desc *desc, *first = NULL, *prev = NULL;
+	size_t dst_avail, src_avail;
+	dma_addr_t dst, src;
+	size_t len;
+	int i = 0;
+
+	/* basic sanity checks */
+	if (dst_nents == 0 || src_nents == 0)
+		return NULL;
+
+	if (dst_sg == NULL || src_sg == NULL)
+		return NULL;
+
+	tdmac = to_mmp_tdma_chan(chan);
+
+	/* get prepared for the loop */
+	dst_avail = sg_dma_len(dst_sg);
+	src_avail = sg_dma_len(src_sg);
+
+	/* run until we are out of scatterlist entries */
+	while (true) {
+		dev_dbg(tdmac->mmp_tdma->dev, "%s: dst_avail %x src_avail %x\n",
+			__func__, dst_avail, src_avail);
+
+		/* create the largest transaction possible */
+		len = min_t(size_t, src_avail, dst_avail);
+		len = min_t(size_t, len, TDMA_MAX_XFER_BYTES);
+		if (len == 0)
+			goto fetch;
+
+		dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_avail;
+		src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_avail;
+
+		dev_dbg(tdmac->mmp_tdma->dev, "%s: dst %x src %x len %x\n",
+			__func__, dst, src, len);
+
+		if (i >= TDMA_DESC_NUM)
+			goto fail;
+
+		desc = &tdmac->desc_arr[i];
+		desc->dst_addr = dst;
+		desc->src_addr = src;
+		desc->byte_cnt = len;
+		desc->nxt_desc = 0;
+
+		if (!first)
+			first = desc;
+		else
+			prev->nxt_desc = tdmac->desc_arr_phys +
+				sizeof(*desc) * i;
+
+		tdmac->desc.cookie = 0;
+		async_tx_ack(&tdmac->desc);
+		prev = desc;
+
+		/* update metadata */
+		dst_avail -= len;
+		src_avail -= len;
+		i++;
+
+fetch:
+		/* fetch the next dst scatterlist entry */
+		if (dst_avail == 0) {
+
+			/* no more entries: we're done */
+			if (dst_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			dst_sg = sg_next(dst_sg);
+			if (dst_sg == NULL)
+				break;
+
+			dst_nents--;
+			dst_avail = sg_dma_len(dst_sg);
+		}
+
+		/* fetch the next src scatterlist entry */
+		if (src_avail == 0) {
+
+			/* no more entries: we're done */
+			if (src_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			src_sg = sg_next(src_sg);
+			if (src_sg == NULL)
+				break;
+
+			src_nents--;
+			src_avail = sg_dma_len(src_sg);
+		}
+	}
+
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_INTMODE,
+		tdmac->reg_base + TDCR);
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND,
+		tdmac->reg_base + TDCR);
+	/* default burst size is 8B, can modify from control api */
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_BURSTSZ_8B,
+		tdmac->reg_base + TDCR);
+	mmp_tdma_dump_tdma_list(tdmac);
+
+	return &tdmac->desc;
+
+fail:
+	return NULL;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_slave_sg(
+		struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_data_direction direction,
+		unsigned long append)
+{
+	/*
+	 * This operation is not supported on the TDMA controller
+	 *
+	 * However, we need to provide the function pointer to allow the
+	 * device_control() method to work.
+	 */
+	return NULL;
+}
+
+static struct dma_async_tx_descriptor *mmp_tdma_prep_dma_cyclic(
+		struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
+		size_t period_len, enum dma_data_direction direction)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+	struct mmp_tdma_engine *mmp_tdma = tdmac->mmp_tdma;
+	int num_periods = buf_len / period_len;
+	int i = 0, buf = 0;
+
+	if (tdmac->status == DMA_IN_PROGRESS)
+		return NULL;
+
+	tdmac->status = DMA_IN_PROGRESS;
+	tdmac->flags |= TDMA_CYCLIC_LOOP;
+
+	if (num_periods > TDMA_DESC_NUM) {
+		dev_err(mmp_tdma->tdma_device.dev,
+				"maximum number of sg exceeded: %d > %d\n",
+				num_periods, TDMA_DESC_NUM);
+		goto err_out;
+	}
+
+	if (period_len > TDMA_MAX_XFER_BYTES) {
+		dev_err(mmp_tdma->tdma_device.dev,
+				"maximum period size exceeded: %d > %d\n",
+				period_len, TDMA_MAX_XFER_BYTES);
+		goto err_out;
+	}
+
+	while (buf < buf_len) {
+		struct mmp_tdma_desc *desc = &tdmac->desc_arr[i];
+
+		if (i + 1 == num_periods)
+			desc->nxt_desc = tdmac->desc_arr_phys;
+		else
+			desc->nxt_desc = tdmac->desc_arr_phys +
+				sizeof(*desc) * (i + 1);;
+
+		if (direction == DMA_TO_DEVICE) {
+			desc->src_addr = dma_addr;
+			desc->dst_addr = tdmac->dev_addr;
+		} else {
+			desc->src_addr = tdmac->dev_addr;
+			desc->dst_addr = dma_addr;
+		}
+
+		desc->byte_cnt = period_len;
+
+		dma_addr += period_len;
+		buf += period_len;
+
+		i++;
+	}
+
+	writel(readl(tdmac->reg_base + TDCR) | TDCR_FETCHND,
+		tdmac->reg_base + TDCR);
+	mmp_tdma_dump_tdma_list(tdmac);
+
+	return &tdmac->desc;
+
+err_out:
+	tdmac->status = DMA_ERROR;
+	return NULL;
+}
+
+static int mmp_tdma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+		unsigned long arg)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+	struct dma_slave_config *dmaengine_cfg = (void *)arg;
+	int ret = 0;
+
+	switch (cmd) {
+	case DMA_TERMINATE_ALL:
+		mmp_tdma_disable_chan(tdmac);
+		break;
+	case DMA_PAUSE:
+		mmp_tdma_pause_chan(tdmac);
+		break;
+	case DMA_RESUME:
+		mmp_tdma_resume_chan(tdmac);
+		break;
+	case DMA_SLAVE_CONFIG:
+		if (dmaengine_cfg->direction == DMA_FROM_DEVICE) {
+			tdmac->dev_addr = dmaengine_cfg->src_addr;
+			tdmac->burst_sz = dmaengine_cfg->src_maxburst;
+		} else {
+			tdmac->dev_addr = dmaengine_cfg->dst_addr;
+			tdmac->burst_sz = dmaengine_cfg->dst_maxburst;
+		}
+		tdmac->dir = dmaengine_cfg->direction;
+		return mmp_tdma_config_chan(tdmac);
+	default:
+		ret = -ENOSYS;
+	}
+
+	return ret;
+}
+
+static enum dma_status mmp_tdma_tx_status(struct dma_chan *chan,
+			dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan);
+	dma_cookie_t last_used;
+
+	last_used = chan->cookie;
+	dma_set_tx_state(txstate, tdmac->last_completed, last_used, 0);
+
+	return tdmac->status;
+}
+
+static void mmp_tdma_issue_pending(struct dma_chan *chan)
+{
+	/*
+	 * Nothing to do. We only have a single descriptor.
+	 */
+}
+
+static int __devinit mmp_tdma_probe(struct platform_device *pdev)
+{
+	struct mmp_tdma_engine *mmp_tdma;
+	struct mmp_tdma_chan *tdmac;
+	struct resource *iores;
+	struct mmp_tdma_platform_data *pdata = pdev->dev.platform_data;
+	struct mmp_tdma_chan_info *info;
+	int ret;
+	int irq;
+	int i;
+
+	if (!pdata || !pdata->info)
+		return -ENODEV;
+
+	info = pdata->info;
+
+	mmp_tdma = kzalloc(pdata->nr_ch * sizeof(*tdmac) +
+			   sizeof(*mmp_tdma), GFP_KERNEL);
+	if (!mmp_tdma)
+		return -ENOMEM;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!iores || irq < 0) {
+		ret = -EINVAL;
+		goto err_irq;
+	}
+
+	if (!request_mem_region(iores->start, resource_size(iores),
+				pdev->name)) {
+		ret = -EBUSY;
+		goto err_request_region;
+	}
+
+	mmp_tdma->base = ioremap(iores->start, resource_size(iores));
+	if (!mmp_tdma->base) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	dma_cap_set(DMA_MEMCPY, mmp_tdma->tdma_device.cap_mask);
+	dma_cap_set(DMA_MEMSET, mmp_tdma->tdma_device.cap_mask);
+	dma_cap_set(DMA_SLAVE, mmp_tdma->tdma_device.cap_mask);
+	dma_cap_set(DMA_SG, mmp_tdma->tdma_device.cap_mask);
+	dma_cap_set(DMA_CYCLIC, mmp_tdma->tdma_device.cap_mask);
+
+	INIT_LIST_HEAD(&mmp_tdma->tdma_device.channels);
+
+	/* initialize channel parameters */
+	for (i = 0; i < pdata->nr_ch; i++) {
+
+		BUG_ON((info[i].reg_base < iores->start) ||
+		       (info[i].reg_base >= iores->start +
+		       resource_size(iores)));
+
+		tdmac = &mmp_tdma->tdmac[i];
+
+		tdmac->mmp_tdma	   = mmp_tdma;
+		tdmac->chan.device = &mmp_tdma->tdma_device;
+		tdmac->type	   = info[i].type;
+		tdmac->irq	   = irq + i;
+		tdmac->reg_base	   = (unsigned long)mmp_tdma->base +
+				info[i].reg_base - iores->start;
+
+		/* add the channel to tdma_chan list */
+		list_add_tail(&tdmac->chan.device_node,
+			      &mmp_tdma->tdma_device.channels);
+	}
+
+	mmp_tdma->dev = &pdev->dev;
+	mmp_tdma->tdmac_nr  = pdata->nr_ch;
+	mmp_tdma->tdma_device.dev = &pdev->dev;
+	mmp_tdma->tdma_device.device_alloc_chan_resources =
+					mmp_tdma_alloc_chan_resources;
+	mmp_tdma->tdma_device.device_free_chan_resources =
+					mmp_tdma_free_chan_resources;
+	mmp_tdma->tdma_device.device_prep_dma_memcpy = mmp_tdma_prep_memcpy;
+	mmp_tdma->tdma_device.device_prep_dma_memset = mmp_tdma_prep_memset;
+	mmp_tdma->tdma_device.device_prep_slave_sg = mmp_tdma_prep_slave_sg;
+	mmp_tdma->tdma_device.device_prep_dma_sg = mmp_tdma_prep_sg;
+	mmp_tdma->tdma_device.device_prep_dma_cyclic = mmp_tdma_prep_dma_cyclic;
+	mmp_tdma->tdma_device.device_tx_status = mmp_tdma_tx_status;
+	mmp_tdma->tdma_device.device_issue_pending = mmp_tdma_issue_pending;
+	mmp_tdma->tdma_device.device_control = mmp_tdma_control;
+	mmp_tdma->tdma_device.copy_align = TDMA_ALIGNMENT;
+
+	dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+
+	ret = dma_async_device_register(&mmp_tdma->tdma_device);
+	if (ret) {
+		dev_err(mmp_tdma->tdma_device.dev, "unable to register\n");
+		goto err_init;
+	}
+
+	dev_info(mmp_tdma->tdma_device.dev, "initialized\n");
+	return 0;
+
+err_init:
+	iounmap(mmp_tdma->base);
+err_ioremap:
+	release_mem_region(iores->start, resource_size(iores));
+err_request_region:
+err_irq:
+	kfree(mmp_tdma);
+	return ret;
+}
+
+static const struct platform_device_id mmp_tdma_id_table[] = {
+	{ "mmp-adma", },
+	{ "mmp-mdma", },
+	{ },
+};
+
+static struct platform_driver mmp_tdma_driver = {
+	.driver		= {
+		.name	= "mmp-tdma",
+	},
+	.id_table	= mmp_tdma_id_table,
+};
+
+static int __init mmp_tdma_module_init(void)
+{
+	return platform_driver_probe(&mmp_tdma_driver, mmp_tdma_probe);
+}
+subsys_initcall(mmp_tdma_module_init);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("MMP Two-Channel DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 85495f4..7469e19 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -43,7 +43,8 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware = {
 	.period_bytes_min	= 1024,
 	.period_bytes_max	= 2048,
 	.periods_min		= 2,
-	.periods_max		= MMP2_ADMA_DESC_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max		= MMP2_ADMA_DESC_SIZE /
+				sizeof(struct mmp_tdma_desc),
 	.buffer_bytes_max	= MMP2_DDR_BUF_SIZE,
 	.fifo_size		= 32,
 };
@@ -53,7 +54,7 @@ static DECLARE_WAIT_QUEUE_HEAD(dma_wq);
 #ifdef DEBUG
 static void mmp2_pcm_dump_adma_list(struct mmp2_runtime_data *prtd)
 {
-	mmp_tdma_desc *adma_desc;
+	struct mmp_tdma_desc *adma_desc;
 
 	pr_debug("audio dma list description is:\n");
 	adma_desc = prtd->adma_desc_array;
@@ -64,7 +65,7 @@ static void mmp2_pcm_dump_adma_list(struct mmp2_runtime_data *prtd)
 		pr_debug("byte_cnt = 0x%08x\n", adma_desc->byte_cnt);
 		pr_debug("nxt_desc = 0x%08x\n", adma_desc->nxt_desc);
 
-		adma_desc = (mmp_tdma_desc *)(adma_desc->nxt_desc -
+		adma_desc = (struct mmp_tdma_desc *)(adma_desc->nxt_desc -
 				(int)prtd->adma_desc_array_phys +
 				(int)prtd->adma_desc_array);
 
@@ -245,7 +246,7 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	struct mmp2_adma_params *dma;
 	size_t totsize = params_buffer_bytes(params);
 	size_t period = params_period_bytes(params);
-	mmp_tdma_desc *adma_desc;
+	struct mmp_tdma_desc *adma_desc;
 	dma_addr_t dma_buff_phys, next_desc_phys;
 	int ret;
 
@@ -298,7 +299,7 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	adma_desc      = prtd->adma_desc_array;
 
 	do {
-		next_desc_phys += sizeof(mmp_tdma_desc);
+		next_desc_phys += sizeof(struct mmp_tdma_desc);
 
 		adma_desc->nxt_desc = next_desc_phys;
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
diff --git a/sound/soc/pxa/mmp2-squ.c b/sound/soc/pxa/mmp2-squ.c
index 986cc59..6d3cc10 100644
--- a/sound/soc/pxa/mmp2-squ.c
+++ b/sound/soc/pxa/mmp2-squ.c
@@ -56,7 +56,7 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware_playback = {
 	.period_bytes_min	= 1024,
 	.period_bytes_max	= 2048,
 	.periods_min		= 2,
-	.periods_max		= PAGE_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max		= PAGE_SIZE / sizeof(struct mmp_tdma_desc),
 	.buffer_bytes_max	= PAGE_SIZE,
 	.fifo_size		= 32,
 };
@@ -73,7 +73,7 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware_capture = {
 	.period_bytes_min	= 1024,
 	.period_bytes_max	= 2048,
 	.periods_min		= 2,
-	.periods_max		= PAGE_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max		= PAGE_SIZE / sizeofs(struct mmp_tdma_desc),
 	.buffer_bytes_max	= PAGE_SIZE,
 	.fifo_size		= 32,
 };
@@ -90,7 +90,7 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware_playback = {
 	.period_bytes_min = 32,
 	.period_bytes_max = 10 * 1024,
 	.periods_min = 1,
-	.periods_max = PAGE_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max = PAGE_SIZE / sizeof(struct mmp_tdma_desc),
 	.buffer_bytes_max = 24 * 1024,
 	.fifo_size = 32,
 };
@@ -105,7 +105,7 @@ static const struct snd_pcm_hardware mmp2_pcm_hardware_capture = {
 	.period_bytes_min = 32,
 	.period_bytes_max = 10 * 1024,
 	.periods_min = 1,
-	.periods_max = PAGE_SIZE / sizeof(mmp_tdma_desc),
+	.periods_max = PAGE_SIZE / sizeof(struct mmp_tdma_desc),
 	.buffer_bytes_max = 24 * 1024,
 	.fifo_size = 32,
 };
@@ -157,7 +157,7 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	struct mmp2_adma_params *dma;
 	size_t totsize = params_buffer_bytes(params);
 	size_t period = params_period_bytes(params);
-	mmp_tdma_desc *adma_desc;
+	struct mmp_tdma_desc *adma_desc;
 	dma_addr_t dma_buff_phys, next_desc_phys;
 	int ret;
 
@@ -197,7 +197,7 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 
 	adma_desc = prtd->adma_desc_array;
 	do {
-		next_desc_phys += sizeof(mmp_tdma_desc);
+		next_desc_phys += sizeof(struct mmp_tdma_desc);
 
 		adma_desc->nxt_desc = next_desc_phys;
 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 04/13] dmaengine:mmp add peripheral dma driver
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1,add dmac driver for peripheral device

Change-Id: I65bee3b4b9e8c832f3c9ca607bc91f264fb4ed8e
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/plat-pxa/include/plat/dma.h |   33 +-
 drivers/dma/pxa_dmac.c               | 1077 ++++++++++++++++++++++++++++++++++
 2 files changed, 1109 insertions(+), 1 deletions(-)
 create mode 100644 drivers/dma/pxa_dmac.c

diff --git a/arch/arm/plat-pxa/include/plat/dma.h b/arch/arm/plat-pxa/include/plat/dma.h
index fb4c393..e30880e 100644
--- a/arch/arm/plat-pxa/include/plat/dma.h
+++ b/arch/arm/plat-pxa/include/plat/dma.h
@@ -1,6 +1,11 @@
 #ifndef __PLAT_DMA_H
 #define __PLAT_DMA_H
 
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#endif
+
 #define DMAC_REG(x)	(*((volatile u32 *)(DMAC_REGS_VIRT + (x))))
 
 #define DCSR(n)		DMAC_REG((n) << 2)
@@ -36,6 +41,7 @@
 #define DRCMR_CHLNUM	0x1f		/* mask for Channel Number (read / write) */
 
 #define DDADR_DESCADDR	0xfffffff0	/* Address of next descriptor (mask) */
+#define DDADR_BREN	(2 << 0)	/* Enable Descriptor Branch */
 #define DDADR_STOP	(1 << 0)	/* Stop (read / write) */
 
 #define DCMD_INCSRCADDR	(1 << 31)	/* Source Address Increment Setting. */
@@ -71,13 +77,38 @@ typedef enum {
 	DMA_PRIO_LOW = 2
 } pxa_dma_prio;
 
+struct pxa_dmac_data {
+	int priority;
+	int flow_ctrl;
+	int ch_map;
+};
+
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+static inline int pxa_dmac_is_this_type(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "pxa-dmac");
+}
+
 /*
  * DMA registration
  */
 
+struct pxa_dmac_platform_data {
+	unsigned int irq_base;
+	unsigned int nr_ch;
+};
+
+
+int __init pxa_init_dma(int second_irq_start, int num_ch);
+unsigned long pxa_dmac_chan_get_src_ptr(struct dma_chan *chan);
+unsigned long pxa_dmac_chan_get_dst_ptr(struct dma_chan *chan);
+pxa_dma_prio pxa_dmac_chan_get_prio(struct dma_chan *chan);
+#else
 int __init pxa_init_dma(int irq, int num_ch);
+#endif
+int __init pxa_init_dma_irq(int mux_irq, int second_irq_start, int num_ch);
 
-int pxa_request_dma (char *name,
+int pxa_request_dma(char *name,
 			 pxa_dma_prio prio,
 			 void (*irq_handler)(int, void *),
 			 void *data);
diff --git a/drivers/dma/pxa_dmac.c b/drivers/dma/pxa_dmac.c
new file mode 100644
index 0000000..16446d4
--- /dev/null
+++ b/drivers/dma/pxa_dmac.c
@@ -0,0 +1,1077 @@
+/*
+ * drivers/dma/pxa-dmac.c
+ *
+ * Driver for Xscale PXA DMAC engine and MMP peripheral DMA
+ *
+ * Copyright 2011 Leo Yan <leoy@marvell.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+
+#include <asm/irq.h>
+#include <mach/dma.h>
+#include <plat/dma.h>
+
+#define DMAC_DESC_SIZE		512
+#define DMAC_DESC_NUM		(int)(DMAC_DESC_SIZE / \
+				sizeof(struct pxa_dma_desc))
+
+#define DMAC_ALIGNMENT		3
+#define DMAC_MAX_XFER_BYTES	((SZ_8K - 1) & ~((1 << DMAC_ALIGNMENT) - 1))
+#define DMAC_CYCLIC_LOOP	(1 << 0)
+
+u32 dma_irq_base;
+
+struct pxa_dmac_chan {
+	struct pxa_dmac_engine		*pxa_dmac;
+	struct dma_chan			chan;
+	struct dma_async_tx_descriptor	desc;
+
+	struct pxa_dma_desc		*desc_arr;
+	phys_addr_t			desc_arr_phys;
+	enum dma_data_direction		dir;
+	dma_addr_t			dev_addr;
+	u32				burst_sz;
+	enum dma_slave_buswidth		width;
+	u32				dcmd;
+
+	dma_cookie_t			last_completed;
+	enum dma_status			status;
+	unsigned int			flags;
+
+	pxa_dma_prio			prio;
+	int				irq;
+	int				idx;
+	int				ch_map;
+};
+
+struct pxa_dmac_engine {
+	struct device			*dev;
+	void __iomem			*base;
+	struct dma_device		dmac_device;
+	unsigned int			dmac_nr;
+	struct pxa_dmac_chan		dmac[0];
+};
+
+/*
+ * Debug fs
+ */
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+
+#define DMA_DEBUG_NAME		"pxa_dmac"
+
+static struct dentry *dbgfs_root, *dbgfs_state, **dbgfs_chan;
+static int num_dma_channels;
+static spinlock_t dbgfs_lock;
+
+static int dbg_show_requester_chan(struct seq_file *s, void *p)
+{
+	int pos = 0;
+	struct pxa_dmac_chan *dmac = (struct pxa_dmac_chan *)s->private;
+	int chan = dmac->idx;
+	int i;
+	u32 drcmr;
+
+	pos += seq_printf(s, "DMA channel %d requesters list :\n", chan);
+	for (i = 0; i < (num_dma_channels << 1); i++) {
+		drcmr = DRCMR(i);
+		if (!(drcmr & DRCMR_MAPVLD))
+			continue;
+		if ((drcmr & DRCMR_CHLNUM) == chan)
+			pos += seq_printf(s, "\tRequester %d (MAPVLD=%d)\n", i,
+					  !!(drcmr & DRCMR_MAPVLD));
+	}
+	return pos;
+}
+
+static inline int dbg_burst_from_dcmd(u32 dcmd)
+{
+	int burst = (dcmd >> 16) & 0x3;
+
+	return burst ? 4 << burst : 0;
+}
+
+static int is_phys_valid(unsigned long addr)
+{
+	return pfn_valid(__phys_to_pfn(addr));
+}
+
+#define DCSR_STR(flag) (dcsr & DCSR_##flag ? #flag" " : "")
+#define DCMD_STR(flag) (dcmd & DCMD_##flag ? #flag" " : "")
+
+static int dbg_show_descriptors(struct seq_file *s, void *p)
+{
+	int pos = 0;
+	struct pxa_dmac_chan *dmac = (struct pxa_dmac_chan *)s->private;
+	int chan = dmac->idx;
+	int i, max_show = 20, burst, width;
+	u32 dcsr, dcmd;
+	unsigned long phys_desc;
+	struct pxa_dma_desc *desc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dbgfs_lock, flags);
+
+	dcsr       = DCSR(chan);
+	phys_desc  = DDADR(chan);
+	phys_desc &= ~(DDADR_BREN | DDADR_STOP);
+
+	if ((dcsr & DCSR_NODESC) || !phys_desc) {
+		pos += seq_printf(s, "Config to No-Descriptor Fetch\n");
+		goto out;
+	}
+
+	pos += seq_printf(s, "DMA channel %d descriptors :\n", chan);
+	pos += seq_printf(s, "[%03d] First descriptor unknown\n", 0);
+	for (i = 1; i < max_show && is_phys_valid(phys_desc); i++) {
+
+		/* means the last one */
+		if (!phys_desc)
+			break;
+
+		desc = phys_to_virt(phys_desc);
+		dcmd = desc->dcmd;
+		burst = dbg_burst_from_dcmd(dcmd);
+		width = (1 << ((dcmd >> 14) & 0x3)) >> 1;
+
+		pos += seq_printf(s, "[%03d] Desc at %08lx(virt %p)\n",
+				  i, phys_desc, desc);
+		pos += seq_printf(s, "\tDDADR = %08x\n", desc->ddadr);
+		pos += seq_printf(s, "\tDSADR = %08x\n", desc->dsadr);
+		pos += seq_printf(s, "\tDTADR = %08x\n", desc->dtadr);
+		pos += seq_printf(s, "\tDCMD  = %08x (%s%s%s%s%s%s%sburst=%d"
+				  " width=%d len=%d)\n",
+				  dcmd,
+				  DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR),
+				  DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG),
+				  DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN),
+				  DCMD_STR(ENDIAN), burst, width,
+				  dcmd & DCMD_LENGTH);
+
+		if (phys_desc & DDADR_BREN)
+			phys_desc += 32;
+		else
+			phys_desc = desc->ddadr;
+	}
+	if (i == max_show)
+		pos += seq_printf(s,
+			"[%03d] Desc at %08lx ... max display reached\n",
+				  i, phys_desc);
+	else
+		pos += seq_printf(s, "[%03d] Desc at %08lx is %s\n",
+				  i, phys_desc, phys_desc == DDADR_STOP ?
+				  "DDADR_STOP" : "invalid");
+
+out:
+	spin_unlock_irqrestore(&dbgfs_lock, flags);
+	return pos;
+}
+
+static int dbg_show_chan_state(struct seq_file *s, void *p)
+{
+	int pos = 0;
+	struct pxa_dmac_chan *dmac = (struct pxa_dmac_chan *)s->private;
+	int chan = dmac->idx;
+	u32 dcsr, dcmd;
+	int burst, width;
+	static char *str_prio[] = { "high", "normal", "low" };
+
+	dcsr = DCSR(chan);
+	dcmd = DCMD(chan);
+	burst = dbg_burst_from_dcmd(dcmd);
+	width = (1 << ((dcmd >> 14) & 0x3)) >> 1;
+
+	pos += seq_printf(s, "DMA channel %d\n", chan);
+	pos += seq_printf(s, "\tPriority : %s\n",
+			  str_prio[dmac->prio]);
+	pos += seq_printf(s, "\tUnaligned transfer bit: %s\n",
+			  DALGN & (1 << chan) ? "yes" : "no");
+	pos += seq_printf(s,
+		"\tDCSR  = %08x (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n",
+			  dcsr, DCSR_STR(RUN), DCSR_STR(NODESC),
+			  DCSR_STR(STOPIRQEN), DCSR_STR(EORIRQEN),
+			  DCSR_STR(EORJMPEN), DCSR_STR(EORSTOPEN),
+			  DCSR_STR(SETCMPST), DCSR_STR(CLRCMPST),
+			  DCSR_STR(CMPST), DCSR_STR(EORINTR), DCSR_STR(REQPEND),
+			  DCSR_STR(STOPSTATE), DCSR_STR(ENDINTR),
+			  DCSR_STR(STARTINTR), DCSR_STR(BUSERR));
+
+	pos += seq_printf(s, "\tDCMD  = %08x (%s%s%s%s%s%s%sburst=%d width=%d"
+			  " len=%d)\n",
+			  dcmd,
+			  DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR),
+			  DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG),
+			  DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN),
+			  DCMD_STR(ENDIAN), burst, width, dcmd & DCMD_LENGTH);
+	pos += seq_printf(s, "\tDSADR = %08x\n", DSADR(chan));
+	pos += seq_printf(s, "\tDTADR = %08x\n", DTADR(chan));
+	pos += seq_printf(s, "\tDDADR = %08x\n", DDADR(chan));
+	return pos;
+}
+
+static int dbg_show_state(struct seq_file *s, void *p)
+{
+	int pos = 0;
+
+	/* basic device status */
+	pos += seq_printf(s, "DMA engine status\n");
+	pos += seq_printf(s, "\tChannel number: %d\n", num_dma_channels);
+
+	return pos;
+}
+
+#define DBGFS_FUNC_DECL(name) \
+static int dbg_open_##name(struct inode *inode, struct file *file) \
+{ \
+	return single_open(file, dbg_show_##name, inode->i_private); \
+} \
+static const struct file_operations dbg_fops_##name = { \
+	.owner		= THIS_MODULE, \
+	.open		= dbg_open_##name, \
+	.llseek		= seq_lseek, \
+	.read		= seq_read, \
+	.release	= single_release, \
+}
+
+DBGFS_FUNC_DECL(state);
+DBGFS_FUNC_DECL(chan_state);
+DBGFS_FUNC_DECL(descriptors);
+DBGFS_FUNC_DECL(requester_chan);
+
+static struct dentry *pxa_dmac_dbg_alloc_chan(int ch,
+		struct dentry *chandir, void *dt)
+{
+	char chan_name[11];
+	struct dentry *chan, *chan_state = NULL, *chan_descr = NULL;
+	struct dentry *chan_reqs = NULL;
+
+	scnprintf(chan_name, sizeof(chan_name), "%d", ch);
+	chan = debugfs_create_dir(chan_name, chandir);
+
+	if (chan)
+		chan_state = debugfs_create_file("state", 0400, chan, dt,
+						 &dbg_fops_chan_state);
+	if (chan_state)
+		chan_descr = debugfs_create_file("descriptors", 0400, chan, dt,
+						 &dbg_fops_descriptors);
+	if (chan_descr)
+		chan_reqs = debugfs_create_file("requesters", 0400, chan, dt,
+						&dbg_fops_requester_chan);
+	if (!chan_reqs)
+		goto err_state;
+
+	return chan;
+
+err_state:
+	debugfs_remove_recursive(chan);
+	return NULL;
+}
+
+static void pxa_dmac_init_debugfs(int nr_ch, struct pxa_dmac_chan *dmac)
+{
+	int i;
+	struct dentry *chandir;
+
+	num_dma_channels = nr_ch;
+
+	dbgfs_root = debugfs_create_dir(DMA_DEBUG_NAME, NULL);
+	if (IS_ERR(dbgfs_root) || !dbgfs_root)
+		goto err_root;
+
+	dbgfs_state = debugfs_create_file("state", 0400, dbgfs_root, NULL,
+					  &dbg_fops_state);
+	if (!dbgfs_state)
+		goto err_state;
+
+	dbgfs_chan = kmalloc(sizeof(*dbgfs_state) * num_dma_channels,
+			     GFP_KERNEL);
+	if (!dbgfs_chan)
+		goto err_alloc;
+
+	chandir = debugfs_create_dir("channels", dbgfs_root);
+	if (!chandir)
+		goto err_chandir;
+
+	for (i = 0; i < num_dma_channels; i++) {
+		dbgfs_chan[i] = pxa_dmac_dbg_alloc_chan(i, chandir, &dmac[i]);
+		if (!dbgfs_chan[i])
+			goto err_chans;
+	}
+
+	spin_lock_init(&dbgfs_lock);
+	return;
+err_chans:
+err_chandir:
+	kfree(dbgfs_chan);
+err_alloc:
+err_state:
+	debugfs_remove_recursive(dbgfs_root);
+err_root:
+	dev_dbg(dmac->pxa_dmac->dev, "pxa_dma: debugfs is not available\n");
+}
+
+static void __exit pxa_dmac_cleanup_debugfs(void)
+{
+	debugfs_remove_recursive(dbgfs_root);
+}
+#else
+static inline void pxa_dmac_init_debugfs(nr_ch) {}
+static inline void pxa_dmac_cleanup_debugfs(void) {}
+#endif
+
+#ifdef DEBUG
+static void pxa_dmac_dump_dma_list(struct pxa_dmac_chan *dmac)
+{
+	struct pxa_dma_desc *desc = dmac->desc_arr;
+	unsigned long flags;
+
+	if (!desc) {
+		dev_dbg(dmac->pxa_dmac->dev,
+			"dma description list has no node!\n");
+		return;
+	}
+
+	local_irq_save(flags);
+
+	dev_dbg(dmac->pxa_dmac->dev, "dma description list nodes:\n");
+	do {
+		dev_dbg(dmac->pxa_dmac->dev, ("---------------------\n");
+		dev_dbg(dmac->pxa_dmac->dev, "ddadr = 0x%08x\n", desc->ddadr);
+		dev_dbg(dmac->pxa_dmac->dev, "dsadr = 0x%08x\n", desc->dsadr);
+		dev_dbg(dmac->pxa_dmac->dev, "dtadr = 0x%08x\n", desc->dtadr);
+		dev_dbg(dmac->pxa_dmac->dev, "dcmd  = 0x%08x\n", desc->dcmd);
+
+		if (desc->ddadr & DDADR_STOP)
+			break;
+
+		desc = (pxa_dma_desc *)((desc->ddadr & DDADR_DESCADDR) -
+				(int)dmac->desc_arr_phys +
+				(int)dmac->desc_arr);
+
+	} while (desc != dmac->desc_arr);
+
+	local_irq_restore(flags);
+	return;
+}
+#else
+#define pxa_dmac_dump_dma_list(dmac) do { } while (0)
+#endif
+
+static struct pxa_dmac_chan *to_pxa_dmac_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct pxa_dmac_chan, chan);
+}
+
+static void pxa_dmac_enable_chan(struct pxa_dmac_chan *dmac)
+{
+	DCSR(dmac->idx) = DCSR(dmac->idx) | DCSR_RUN;
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: [%x] DCSR %x\n",
+		__func__, dmac->idx, DCSR(dmac->idx));
+}
+
+static void pxa_dmac_disable_chan(struct pxa_dmac_chan *dmac)
+{
+	DCSR(dmac->idx) = DCSR(dmac->idx) & ~DCSR_RUN;
+
+	dmac->status = DMA_SUCCESS;
+}
+
+static void pxa_dmac_resume_chan(struct pxa_dmac_chan *dmac)
+{
+	DCSR(dmac->idx) = DCSR(dmac->idx) | DCSR_RUN;
+
+	dmac->status = DMA_IN_PROGRESS;
+}
+
+static void pxa_dmac_pause_chan(struct pxa_dmac_chan *dmac)
+{
+	DCSR(dmac->idx) = DCSR(dmac->idx) & ~DCSR_RUN;
+
+	dmac->status = DMA_PAUSED;
+}
+
+static dma_cookie_t pxa_dmac_assign_cookie(struct pxa_dmac_chan *dmac)
+{
+	dma_cookie_t cookie = dmac->chan.cookie;
+
+	if (++cookie < 0)
+		cookie = 1;
+
+	dmac->chan.cookie = cookie;
+	dmac->desc.cookie = cookie;
+
+	return cookie;
+}
+
+unsigned long pxa_dmac_chan_get_src_ptr(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+
+	return DSADR(dmac->idx);
+}
+EXPORT_SYMBOL(pxa_dmac_chan_get_src_ptr);
+
+unsigned long pxa_dmac_chan_get_dst_ptr(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+
+	return DTADR(dmac->idx);
+}
+EXPORT_SYMBOL(pxa_dmac_chan_get_dst_ptr);
+
+pxa_dma_prio pxa_dmac_chan_get_prio(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+
+	return dmac->prio;
+}
+EXPORT_SYMBOL(pxa_dmac_chan_get_prio);
+
+static dma_cookie_t pxa_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(tx->chan);
+
+	pxa_dmac_enable_chan(dmac);
+
+	return pxa_dmac_assign_cookie(dmac);
+}
+
+static irqreturn_t pxa_dmac_int_handler(int irq, void *dev_id)
+{
+	struct pxa_dmac_chan *dmac = dev_id;
+
+	if (DCSR(dmac->idx) & DCSR_BUSERR) {
+		dev_err(dmac->pxa_dmac->dev, "%s: error in channel %d\n",
+			__func__, dmac->idx);
+
+		dmac->status = DMA_ERROR;
+	} else {
+		if (dmac->flags & DMAC_CYCLIC_LOOP)
+			dmac->status = DMA_IN_PROGRESS;
+		else
+			dmac->status = DMA_SUCCESS;
+	}
+
+	if (dmac->status == DMA_SUCCESS)
+		dmac->last_completed = dmac->desc.cookie;
+
+	/* clear irq */
+	DCSR(dmac->idx) = DCSR(dmac->idx) | DCSR_STARTINTR |
+		DCSR_ENDINTR | DCSR_BUSERR;
+
+	if (dmac->desc.callback)
+		dmac->desc.callback(dmac->desc.callback_param);
+
+	return IRQ_HANDLED;
+}
+
+static int pxa_dmac_alloc_descriptor(struct pxa_dmac_chan *dmac)
+{
+	struct pxa_dmac_engine *pxa_dmac = dmac->pxa_dmac;
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: enter\n", __func__);
+
+	dmac->desc_arr = dma_alloc_coherent(pxa_dmac->dev,
+			DMAC_DESC_SIZE, &dmac->desc_arr_phys, GFP_KERNEL);
+	if (!dmac->desc_arr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void pxa_dmac_free_descriptor(struct pxa_dmac_chan *dmac)
+{
+	struct pxa_dmac_engine *pxa_dmac = dmac->pxa_dmac;
+
+	dma_free_coherent(pxa_dmac->dev, DMAC_DESC_SIZE,
+			dmac->desc_arr, dmac->desc_arr_phys);
+}
+
+static int pxa_dmac_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+	int ret;
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: enter\n", __func__);
+
+	ret = pxa_dmac_alloc_descriptor(dmac);
+	if (ret < 0)
+		return ret;
+
+	dma_async_tx_descriptor_init(&dmac->desc, chan);
+	dmac->desc.tx_submit = pxa_dmac_tx_submit;
+
+	/* the descriptor is ready */
+	async_tx_ack(&dmac->desc);
+
+	ret = request_irq(dmac->irq, pxa_dmac_int_handler, IRQF_DISABLED,
+			  "dmac", dmac);
+	if (ret)
+		goto err_request_irq;
+
+	/*
+	 * init command register for default config,
+	 * later user can call dmaengine_slave_config
+	 * to config more settings.
+	 */
+	dmac->dcmd = DCMD_ENDIRQEN | DCMD_BURST8;
+	return 0;
+
+err_request_irq:
+	pxa_dmac_free_descriptor(dmac);
+	return ret;
+}
+
+static void pxa_dmac_free_chan_resources(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: enter\n", __func__);
+
+	free_irq(dmac->irq, dmac);
+	pxa_dmac_disable_chan(dmac);
+	pxa_dmac_free_descriptor(dmac);
+}
+
+static int pxa_dmac_config_chan(struct pxa_dmac_chan *dmac)
+{
+	struct pxa_dmac_engine *pxa_dmac = dmac->pxa_dmac;
+	struct pxa_dmac_data *dmac_data = dmac->chan.private;
+
+	pxa_dmac_disable_chan(dmac);
+
+	if (dmac->dir == DMA_TO_DEVICE) {
+		dmac->dcmd |= DCMD_INCSRCADDR;
+		if (dmac_data->flow_ctrl)
+			dmac->dcmd |= DCMD_FLOWTRG;
+	} else if (dmac->dir == DMA_FROM_DEVICE) {
+		dmac->dcmd |= DCMD_INCTRGADDR;
+		if (dmac_data->flow_ctrl)
+			dmac->dcmd |= DCMD_FLOWSRC;
+	} else
+		dmac->dcmd |= DCMD_INCSRCADDR | DCMD_INCTRGADDR;
+
+	switch (dmac->burst_sz) {
+	case 8: /* 8 bytes */
+		dmac->dcmd |= DCMD_BURST8;
+		break;
+	case 16: /* 16 bytes */
+		dmac->dcmd |= DCMD_BURST16;
+		break;
+	case 32: /* 32 bytes */
+		dmac->dcmd |= DCMD_BURST32;
+		break;
+	default:
+		dev_err(pxa_dmac->dmac_device.dev, "invalid burst size\n");
+		return -EINVAL;
+	}
+
+	switch (dmac->width) {
+	case DMA_SLAVE_BUSWIDTH_UNDEFINED:
+		break;
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		dmac->dcmd |= DCMD_WIDTH1;
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		dmac->dcmd |= DCMD_WIDTH2;
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		dmac->dcmd |= DCMD_WIDTH4;
+		break;
+	default:
+		dev_err(pxa_dmac->dmac_device.dev, "invalid width size\n");
+		return -EINVAL;
+	}
+
+	dmac->ch_map = dmac_data->ch_map;
+	if (dmac->ch_map != -1)
+		DRCMR(dmac->ch_map) = dmac->idx | DRCMR_MAPVLD;
+
+	return 0;
+}
+
+static struct dma_async_tx_descriptor *pxa_dmac_prep_memcpy(
+		struct dma_chan *chan,
+		dma_addr_t dma_dst, dma_addr_t dma_src,
+		size_t len, unsigned long flags)
+{
+	struct pxa_dmac_chan *dmac;
+	struct pxa_dma_desc *desc;
+	size_t copy;
+	int i = 0;
+
+	if (!chan || !len)
+		return NULL;
+
+	dmac = to_pxa_dmac_chan(chan);
+	dmac->status = DMA_IN_PROGRESS;
+
+	dmac->dcmd |= DCMD_INCSRCADDR | DCMD_INCTRGADDR;
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: desc_arr %p desc_arr_phys %x\n",
+		__func__, dmac->desc_arr, dmac->desc_arr_phys);
+
+	do {
+		dev_dbg(dmac->pxa_dmac->dev, "%s: dst %x src %x len %d\n",
+			__func__, dma_dst, dma_src, len);
+
+		desc = &dmac->desc_arr[i];
+		copy = min(len, (size_t)DMAC_MAX_XFER_BYTES);
+
+		desc->ddadr = dmac->desc_arr_phys + sizeof(*desc) * (i + 1);
+		desc->dsadr = dma_src;
+		desc->dtadr = dma_dst;
+		desc->dcmd  = dmac->dcmd | copy;
+
+		len -= copy;
+		dma_src += copy;
+		dma_dst += copy;
+
+		if (!len)
+			desc->ddadr |= DDADR_STOP;
+
+		i++;
+	} while (len);
+
+	DDADR(dmac->idx) = dmac->desc_arr_phys;
+
+	pxa_dmac_dump_dma_list(dmac);
+	return &dmac->desc;
+}
+
+static struct dma_async_tx_descriptor *pxa_dmac_prep_sg(struct dma_chan *chan,
+	struct scatterlist *dst_sg, unsigned int dst_nents,
+	struct scatterlist *src_sg, unsigned int src_nents,
+	unsigned long flags)
+{
+	struct pxa_dmac_chan *dmac;
+	struct pxa_dma_desc *desc = NULL;
+	size_t dst_avail, src_avail;
+	dma_addr_t dst, src;
+	size_t len;
+	int i = 0;
+
+	/* basic sanity checks */
+	if (dst_nents == 0 || src_nents == 0)
+		return NULL;
+
+	if (dst_sg == NULL || src_sg == NULL)
+		return NULL;
+
+	dmac = to_pxa_dmac_chan(chan);
+	dmac->status = DMA_IN_PROGRESS;
+
+	dmac->dcmd |= DCMD_INCSRCADDR | DCMD_INCTRGADDR;
+
+	/* get prepared for the loop */
+	dst_avail = sg_dma_len(dst_sg);
+	src_avail = sg_dma_len(src_sg);
+
+	/* run until we are out of scatterlist entries */
+	while (true) {
+
+		dev_dbg(dmac->pxa_dmac->dev, "%s: dst_avail %x src_avail %x\n",
+			__func__, dst_avail, src_avail);
+
+		/* create the largest transaction possible */
+		len = min_t(size_t, src_avail, dst_avail);
+		len = min_t(size_t, len, DMAC_MAX_XFER_BYTES);
+		if (len == 0)
+			goto fetch;
+
+		dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_avail;
+		src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_avail;
+
+		dev_dbg(dmac->pxa_dmac->dev, "%s: dst %x src %x len %x\n",
+			__func__, dst, src, len);
+
+		if (i >= DMAC_DESC_NUM)
+			goto fail;
+
+		desc = &dmac->desc_arr[i];
+		desc->ddadr = dmac->desc_arr_phys + sizeof(*desc) * (i + 1);
+		desc->dsadr = src;
+		desc->dtadr = dst;
+		desc->dcmd  = dmac->dcmd | len;
+
+		/* update metadata */
+		dst_avail -= len;
+		src_avail -= len;
+		i++;
+
+fetch:
+		/* fetch the next dst scatterlist entry */
+		if (dst_avail == 0) {
+
+			/* no more entries: we're done */
+			if (dst_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			dst_sg = sg_next(dst_sg);
+			if (dst_sg == NULL)
+				break;
+
+			dst_nents--;
+			dst_avail = sg_dma_len(dst_sg);
+		}
+
+		/* fetch the next src scatterlist entry */
+		if (src_avail == 0) {
+
+			/* no more entries: we're done */
+			if (src_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			src_sg = sg_next(src_sg);
+			if (src_sg == NULL)
+				break;
+
+			src_nents--;
+			src_avail = sg_dma_len(src_sg);
+		}
+	}
+
+	if (desc)
+		desc->ddadr |= DDADR_STOP;
+
+	DDADR(dmac->idx) = dmac->desc_arr_phys;
+
+	pxa_dmac_dump_dma_list(dmac);
+	return &dmac->desc;
+
+fail:
+	return NULL;
+}
+
+static struct dma_async_tx_descriptor *pxa_dmac_prep_slave_sg(
+		struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_data_direction direction,
+		unsigned long append)
+{
+	struct pxa_dmac_chan *dmac;
+	struct pxa_dma_desc *desc = NULL;
+	struct scatterlist *sg;
+	size_t avail, len;
+	dma_addr_t slave_addr;
+	unsigned int i;
+
+	if (!sgl || !sg_len)
+		return NULL;
+
+	dmac = to_pxa_dmac_chan(chan);
+	dmac->status = DMA_IN_PROGRESS;
+
+	slave_addr = dmac->dev_addr;
+	for_each_sg(sgl, sg, sg_len, i) {
+		dma_addr_t sg_addr = sg_dma_address(sg);
+		avail = sg_dma_len(sg);
+		do {
+			len = min_t(size_t, avail, DMAC_MAX_XFER_BYTES);
+
+			dev_dbg(dmac->pxa_dmac->dev,
+				"Add SG #%d@%p[%d], dma %llx\n",
+				i, sg, len, (unsigned long long)sg_addr);
+
+			if (i >= DMAC_DESC_NUM)
+				goto fail;
+
+			desc = &dmac->desc_arr[i];
+			desc->ddadr = dmac->desc_arr_phys +
+				sizeof(*desc) * (i + 1);
+			if (direction == DMA_FROM_DEVICE) {
+				desc->dtadr = sg_addr;
+				desc->dsadr = slave_addr;
+				desc->dcmd  = dmac->dcmd | len;
+			} else {
+				desc->dtadr = slave_addr;
+				desc->dsadr = sg_addr;
+				desc->dcmd  = dmac->dcmd | len;
+			}
+
+			avail -= len;
+			sg_addr += len;
+			i++;
+
+		} while (avail);
+	}
+
+	if (desc)
+		desc->ddadr |= DDADR_STOP;
+
+	DDADR(dmac->idx) = dmac->desc_arr_phys;
+
+	pxa_dmac_dump_dma_list(dmac);
+	return &dmac->desc;
+
+fail:
+	return NULL;
+}
+
+static int pxa_dmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+		unsigned long arg)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+	struct dma_slave_config *dmaengine_cfg = (void *)arg;
+	int ret = 0;
+
+	switch (cmd) {
+	case DMA_TERMINATE_ALL:
+		pxa_dmac_disable_chan(dmac);
+		break;
+	case DMA_PAUSE:
+		pxa_dmac_pause_chan(dmac);
+		break;
+	case DMA_RESUME:
+		pxa_dmac_resume_chan(dmac);
+		break;
+	case DMA_SLAVE_CONFIG:
+		if (dmaengine_cfg->direction == DMA_FROM_DEVICE) {
+			dmac->dev_addr = dmaengine_cfg->src_addr;
+			dmac->burst_sz = dmaengine_cfg->src_maxburst;
+			dmac->width    = dmaengine_cfg->src_addr_width;
+		} else {
+			dmac->dev_addr = dmaengine_cfg->dst_addr;
+			dmac->burst_sz = dmaengine_cfg->dst_maxburst;
+			dmac->width    = dmaengine_cfg->dst_addr_width;
+		}
+		dmac->dir = dmaengine_cfg->direction;
+		return pxa_dmac_config_chan(dmac);
+	default:
+		ret = -ENOSYS;
+	}
+
+	return ret;
+}
+
+static enum dma_status pxa_dmac_tx_status(struct dma_chan *chan,
+			dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+	dma_cookie_t last_used;
+
+	last_used = chan->cookie;
+	dma_set_tx_state(txstate, dmac->last_completed, last_used, 0);
+
+	return dmac->status;
+}
+
+static void pxa_dmac_issue_pending(struct dma_chan *chan)
+{
+	/*
+	 * Nothing to do. We only have a single descriptor.
+	 */
+}
+
+static void pxa_dmac_irq_ack(struct irq_data *d)
+{
+	DCSR(d->irq - dma_irq_base) = DCSR(d->irq - dma_irq_base) |
+		DCSR_STARTINTR | DCSR_ENDINTR | DCSR_BUSERR;
+}
+
+static void pxa_dmac_irq_mask(struct irq_data *d)
+{
+	DCSR(d->irq - dma_irq_base) = DCSR(d->irq - dma_irq_base) &
+		~DCSR_STOPIRQEN;
+}
+
+static void pxa_dmac_irq_unmask(struct irq_data *d)
+{
+	/*
+	 * As manual say, if the STOPIRQEN bit is set
+	 * before the channel is started, an interrupt
+	 * will be generated. So let driver set this bit
+	 * after the channel has began to run.
+	 */
+}
+
+static void pxa_dma_demux_handler(unsigned int irq, struct irq_desc *desc)
+{
+	int i, dint = DINT;
+
+	while (dint) {
+		i = __ffs(dint);
+		dint &= (dint - 1);
+		generic_handle_irq(dma_irq_base + i);
+	}
+}
+
+static struct irq_chip pxa_muxed_dma_chip = {
+	.name		= "DMAC",
+	.irq_ack	= pxa_dmac_irq_ack,
+	.irq_mask	= pxa_dmac_irq_mask,
+	.irq_unmask	= pxa_dmac_irq_unmask,
+};
+
+static int __init pxa_dmac_init_irq(int mux_irq, int second_irq_start,
+				    int num_ch)
+{
+	int i = 0, irq;
+	struct irq_data *d;
+	struct irq_chip *chip = &pxa_muxed_dma_chip;
+
+	/* init dma irq */
+	for (irq = second_irq_start; i < num_ch; irq++, i++) {
+		d = irq_get_irq_data(irq);
+
+		/* mask and clear the IRQ */
+		chip->irq_mask(d);
+		if (chip->irq_ack)
+			chip->irq_ack(d);
+
+		irq_set_chip(irq, chip);
+		set_irq_flags(irq, IRQF_VALID);
+		irq_set_handler(irq, handle_level_irq);
+	}
+	irq_set_chained_handler(mux_irq, pxa_dma_demux_handler);
+
+	return 0;
+}
+
+static int __init pxa_dmac_probe(struct platform_device *pdev)
+{
+	struct pxa_dmac_engine *pxa_dmac;
+	struct pxa_dmac_chan *dmac;
+	struct resource *iores;
+	struct pxa_dmac_platform_data *pdata = pdev->dev.platform_data;
+	int i, irq;
+	int ret;
+
+	if (!pdata)
+		return -ENODEV;
+
+	dma_irq_base = pdata->irq_base;
+
+	pxa_dmac = kzalloc(pdata->nr_ch * (sizeof(*dmac)) +
+			   sizeof(*pxa_dmac), GFP_KERNEL);
+	if (!pxa_dmac)
+		return -ENOMEM;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores) {
+		ret = -EINVAL;
+		goto err_irq;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		dev_info(&pdev->dev, "No interrupt specified\n");
+	else
+		pxa_dmac_init_irq(irq, pdata->irq_base, pdata->nr_ch);
+
+	if (!request_mem_region(iores->start, resource_size(iores),
+				pdev->name)) {
+		ret = -EBUSY;
+		goto err_request_region;
+	}
+
+	pxa_dmac->base = ioremap(iores->start, resource_size(iores));
+	if (!pxa_dmac->base) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	dma_cap_set(DMA_MEMCPY, pxa_dmac->dmac_device.cap_mask);
+	dma_cap_set(DMA_SLAVE, pxa_dmac->dmac_device.cap_mask);
+	dma_cap_set(DMA_SG, pxa_dmac->dmac_device.cap_mask);
+
+	INIT_LIST_HEAD(&pxa_dmac->dmac_device.channels);
+
+	/* initialize channel parameters */
+	for (i = 0; i < pdata->nr_ch; i++) {
+
+		dmac = &pxa_dmac->dmac[i];
+
+		/*
+		 * dma channel priorities on pxa2xx processors:
+		 * ch 0 - 3,  16 - 19  <--> (0) DMA_PRIO_HIGH
+		 * ch 4 - 7,  20 - 23  <--> (1) DMA_PRIO_MEDIUM
+		 * ch 8 - 15, 24 - 31  <--> (2) DMA_PRIO_LOW
+		 */
+		dmac->pxa_dmac	  = pxa_dmac;
+		dmac->chan.device = &pxa_dmac->dmac_device;
+		dmac->prio	  = min((i & 0xf) >> 2, DMA_PRIO_LOW);
+		dmac->irq	  = pdata->irq_base + i;
+		dmac->idx	  = i;
+
+		/* add the channel to tdma_chan list */
+		list_add_tail(&dmac->chan.device_node,
+			      &pxa_dmac->dmac_device.channels);
+	}
+
+	pxa_dmac->dev = &pdev->dev;
+	pxa_dmac->dmac_nr = pdata->nr_ch;
+	pxa_dmac->dmac_device.dev = &pdev->dev;
+	pxa_dmac->dmac_device.device_alloc_chan_resources =
+					pxa_dmac_alloc_chan_resources;
+	pxa_dmac->dmac_device.device_free_chan_resources =
+					pxa_dmac_free_chan_resources;
+	pxa_dmac->dmac_device.device_prep_dma_memcpy = pxa_dmac_prep_memcpy;
+	pxa_dmac->dmac_device.device_prep_slave_sg = pxa_dmac_prep_slave_sg;
+	pxa_dmac->dmac_device.device_prep_dma_sg = pxa_dmac_prep_sg;
+	pxa_dmac->dmac_device.device_tx_status = pxa_dmac_tx_status;
+	pxa_dmac->dmac_device.device_issue_pending = pxa_dmac_issue_pending;
+	pxa_dmac->dmac_device.device_control = pxa_dmac_control;
+	pxa_dmac->dmac_device.copy_align = DMAC_ALIGNMENT;
+
+	dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+
+	ret = dma_async_device_register(&pxa_dmac->dmac_device);
+	if (ret) {
+		dev_err(pxa_dmac->dmac_device.dev, "unable to register\n");
+		goto err_init;
+	}
+
+	pxa_dmac_init_debugfs(pdata->nr_ch, pxa_dmac->dmac);
+
+	dev_info(pxa_dmac->dmac_device.dev, "initialized\n");
+	return 0;
+
+err_init:
+	iounmap(pxa_dmac->base);
+err_ioremap:
+	release_mem_region(iores->start, resource_size(iores));
+err_request_region:
+err_irq:
+	kfree(pxa_dmac);
+	return ret;
+}
+
+static struct platform_driver pxa_dmac_driver = {
+	.driver		= {
+		.name	= "pxa-dmac",
+	},
+};
+
+static int __init pxa_dmac_module_init(void)
+{
+	return platform_driver_probe(&pxa_dmac_driver, pxa_dmac_probe);
+}
+subsys_initcall(pxa_dmac_module_init);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("PXA DMAC driver");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 04/13] dmaengine:mmp add peripheral dma driver
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1,add dmac driver for peripheral device

Change-Id: I65bee3b4b9e8c832f3c9ca607bc91f264fb4ed8e
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/plat-pxa/include/plat/dma.h |   33 +-
 drivers/dma/pxa_dmac.c               | 1077 ++++++++++++++++++++++++++++++++++
 2 files changed, 1109 insertions(+), 1 deletions(-)
 create mode 100644 drivers/dma/pxa_dmac.c

diff --git a/arch/arm/plat-pxa/include/plat/dma.h b/arch/arm/plat-pxa/include/plat/dma.h
index fb4c393..e30880e 100644
--- a/arch/arm/plat-pxa/include/plat/dma.h
+++ b/arch/arm/plat-pxa/include/plat/dma.h
@@ -1,6 +1,11 @@
 #ifndef __PLAT_DMA_H
 #define __PLAT_DMA_H
 
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#endif
+
 #define DMAC_REG(x)	(*((volatile u32 *)(DMAC_REGS_VIRT + (x))))
 
 #define DCSR(n)		DMAC_REG((n) << 2)
@@ -36,6 +41,7 @@
 #define DRCMR_CHLNUM	0x1f		/* mask for Channel Number (read / write) */
 
 #define DDADR_DESCADDR	0xfffffff0	/* Address of next descriptor (mask) */
+#define DDADR_BREN	(2 << 0)	/* Enable Descriptor Branch */
 #define DDADR_STOP	(1 << 0)	/* Stop (read / write) */
 
 #define DCMD_INCSRCADDR	(1 << 31)	/* Source Address Increment Setting. */
@@ -71,13 +77,38 @@ typedef enum {
 	DMA_PRIO_LOW = 2
 } pxa_dma_prio;
 
+struct pxa_dmac_data {
+	int priority;
+	int flow_ctrl;
+	int ch_map;
+};
+
+#if defined(CONFIG_CPU_MMP2) || defined(CONFIG_CPU_MMP3)
+static inline int pxa_dmac_is_this_type(struct dma_chan *chan)
+{
+	return !strcmp(dev_name(chan->device->dev), "pxa-dmac");
+}
+
 /*
  * DMA registration
  */
 
+struct pxa_dmac_platform_data {
+	unsigned int irq_base;
+	unsigned int nr_ch;
+};
+
+
+int __init pxa_init_dma(int second_irq_start, int num_ch);
+unsigned long pxa_dmac_chan_get_src_ptr(struct dma_chan *chan);
+unsigned long pxa_dmac_chan_get_dst_ptr(struct dma_chan *chan);
+pxa_dma_prio pxa_dmac_chan_get_prio(struct dma_chan *chan);
+#else
 int __init pxa_init_dma(int irq, int num_ch);
+#endif
+int __init pxa_init_dma_irq(int mux_irq, int second_irq_start, int num_ch);
 
-int pxa_request_dma (char *name,
+int pxa_request_dma(char *name,
 			 pxa_dma_prio prio,
 			 void (*irq_handler)(int, void *),
 			 void *data);
diff --git a/drivers/dma/pxa_dmac.c b/drivers/dma/pxa_dmac.c
new file mode 100644
index 0000000..16446d4
--- /dev/null
+++ b/drivers/dma/pxa_dmac.c
@@ -0,0 +1,1077 @@
+/*
+ * drivers/dma/pxa-dmac.c
+ *
+ * Driver for Xscale PXA DMAC engine and MMP peripheral DMA
+ *
+ * Copyright 2011 Leo Yan <leoy@marvell.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+
+#include <asm/irq.h>
+#include <mach/dma.h>
+#include <plat/dma.h>
+
+#define DMAC_DESC_SIZE		512
+#define DMAC_DESC_NUM		(int)(DMAC_DESC_SIZE / \
+				sizeof(struct pxa_dma_desc))
+
+#define DMAC_ALIGNMENT		3
+#define DMAC_MAX_XFER_BYTES	((SZ_8K - 1) & ~((1 << DMAC_ALIGNMENT) - 1))
+#define DMAC_CYCLIC_LOOP	(1 << 0)
+
+u32 dma_irq_base;
+
+struct pxa_dmac_chan {
+	struct pxa_dmac_engine		*pxa_dmac;
+	struct dma_chan			chan;
+	struct dma_async_tx_descriptor	desc;
+
+	struct pxa_dma_desc		*desc_arr;
+	phys_addr_t			desc_arr_phys;
+	enum dma_data_direction		dir;
+	dma_addr_t			dev_addr;
+	u32				burst_sz;
+	enum dma_slave_buswidth		width;
+	u32				dcmd;
+
+	dma_cookie_t			last_completed;
+	enum dma_status			status;
+	unsigned int			flags;
+
+	pxa_dma_prio			prio;
+	int				irq;
+	int				idx;
+	int				ch_map;
+};
+
+struct pxa_dmac_engine {
+	struct device			*dev;
+	void __iomem			*base;
+	struct dma_device		dmac_device;
+	unsigned int			dmac_nr;
+	struct pxa_dmac_chan		dmac[0];
+};
+
+/*
+ * Debug fs
+ */
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+
+#define DMA_DEBUG_NAME		"pxa_dmac"
+
+static struct dentry *dbgfs_root, *dbgfs_state, **dbgfs_chan;
+static int num_dma_channels;
+static spinlock_t dbgfs_lock;
+
+static int dbg_show_requester_chan(struct seq_file *s, void *p)
+{
+	int pos = 0;
+	struct pxa_dmac_chan *dmac = (struct pxa_dmac_chan *)s->private;
+	int chan = dmac->idx;
+	int i;
+	u32 drcmr;
+
+	pos += seq_printf(s, "DMA channel %d requesters list :\n", chan);
+	for (i = 0; i < (num_dma_channels << 1); i++) {
+		drcmr = DRCMR(i);
+		if (!(drcmr & DRCMR_MAPVLD))
+			continue;
+		if ((drcmr & DRCMR_CHLNUM) == chan)
+			pos += seq_printf(s, "\tRequester %d (MAPVLD=%d)\n", i,
+					  !!(drcmr & DRCMR_MAPVLD));
+	}
+	return pos;
+}
+
+static inline int dbg_burst_from_dcmd(u32 dcmd)
+{
+	int burst = (dcmd >> 16) & 0x3;
+
+	return burst ? 4 << burst : 0;
+}
+
+static int is_phys_valid(unsigned long addr)
+{
+	return pfn_valid(__phys_to_pfn(addr));
+}
+
+#define DCSR_STR(flag) (dcsr & DCSR_##flag ? #flag" " : "")
+#define DCMD_STR(flag) (dcmd & DCMD_##flag ? #flag" " : "")
+
+static int dbg_show_descriptors(struct seq_file *s, void *p)
+{
+	int pos = 0;
+	struct pxa_dmac_chan *dmac = (struct pxa_dmac_chan *)s->private;
+	int chan = dmac->idx;
+	int i, max_show = 20, burst, width;
+	u32 dcsr, dcmd;
+	unsigned long phys_desc;
+	struct pxa_dma_desc *desc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dbgfs_lock, flags);
+
+	dcsr       = DCSR(chan);
+	phys_desc  = DDADR(chan);
+	phys_desc &= ~(DDADR_BREN | DDADR_STOP);
+
+	if ((dcsr & DCSR_NODESC) || !phys_desc) {
+		pos += seq_printf(s, "Config to No-Descriptor Fetch\n");
+		goto out;
+	}
+
+	pos += seq_printf(s, "DMA channel %d descriptors :\n", chan);
+	pos += seq_printf(s, "[%03d] First descriptor unknown\n", 0);
+	for (i = 1; i < max_show && is_phys_valid(phys_desc); i++) {
+
+		/* means the last one */
+		if (!phys_desc)
+			break;
+
+		desc = phys_to_virt(phys_desc);
+		dcmd = desc->dcmd;
+		burst = dbg_burst_from_dcmd(dcmd);
+		width = (1 << ((dcmd >> 14) & 0x3)) >> 1;
+
+		pos += seq_printf(s, "[%03d] Desc at %08lx(virt %p)\n",
+				  i, phys_desc, desc);
+		pos += seq_printf(s, "\tDDADR = %08x\n", desc->ddadr);
+		pos += seq_printf(s, "\tDSADR = %08x\n", desc->dsadr);
+		pos += seq_printf(s, "\tDTADR = %08x\n", desc->dtadr);
+		pos += seq_printf(s, "\tDCMD  = %08x (%s%s%s%s%s%s%sburst=%d"
+				  " width=%d len=%d)\n",
+				  dcmd,
+				  DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR),
+				  DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG),
+				  DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN),
+				  DCMD_STR(ENDIAN), burst, width,
+				  dcmd & DCMD_LENGTH);
+
+		if (phys_desc & DDADR_BREN)
+			phys_desc += 32;
+		else
+			phys_desc = desc->ddadr;
+	}
+	if (i == max_show)
+		pos += seq_printf(s,
+			"[%03d] Desc at %08lx ... max display reached\n",
+				  i, phys_desc);
+	else
+		pos += seq_printf(s, "[%03d] Desc at %08lx is %s\n",
+				  i, phys_desc, phys_desc == DDADR_STOP ?
+				  "DDADR_STOP" : "invalid");
+
+out:
+	spin_unlock_irqrestore(&dbgfs_lock, flags);
+	return pos;
+}
+
+static int dbg_show_chan_state(struct seq_file *s, void *p)
+{
+	int pos = 0;
+	struct pxa_dmac_chan *dmac = (struct pxa_dmac_chan *)s->private;
+	int chan = dmac->idx;
+	u32 dcsr, dcmd;
+	int burst, width;
+	static char *str_prio[] = { "high", "normal", "low" };
+
+	dcsr = DCSR(chan);
+	dcmd = DCMD(chan);
+	burst = dbg_burst_from_dcmd(dcmd);
+	width = (1 << ((dcmd >> 14) & 0x3)) >> 1;
+
+	pos += seq_printf(s, "DMA channel %d\n", chan);
+	pos += seq_printf(s, "\tPriority : %s\n",
+			  str_prio[dmac->prio]);
+	pos += seq_printf(s, "\tUnaligned transfer bit: %s\n",
+			  DALGN & (1 << chan) ? "yes" : "no");
+	pos += seq_printf(s,
+		"\tDCSR  = %08x (%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s)\n",
+			  dcsr, DCSR_STR(RUN), DCSR_STR(NODESC),
+			  DCSR_STR(STOPIRQEN), DCSR_STR(EORIRQEN),
+			  DCSR_STR(EORJMPEN), DCSR_STR(EORSTOPEN),
+			  DCSR_STR(SETCMPST), DCSR_STR(CLRCMPST),
+			  DCSR_STR(CMPST), DCSR_STR(EORINTR), DCSR_STR(REQPEND),
+			  DCSR_STR(STOPSTATE), DCSR_STR(ENDINTR),
+			  DCSR_STR(STARTINTR), DCSR_STR(BUSERR));
+
+	pos += seq_printf(s, "\tDCMD  = %08x (%s%s%s%s%s%s%sburst=%d width=%d"
+			  " len=%d)\n",
+			  dcmd,
+			  DCMD_STR(INCSRCADDR), DCMD_STR(INCTRGADDR),
+			  DCMD_STR(FLOWSRC), DCMD_STR(FLOWTRG),
+			  DCMD_STR(STARTIRQEN), DCMD_STR(ENDIRQEN),
+			  DCMD_STR(ENDIAN), burst, width, dcmd & DCMD_LENGTH);
+	pos += seq_printf(s, "\tDSADR = %08x\n", DSADR(chan));
+	pos += seq_printf(s, "\tDTADR = %08x\n", DTADR(chan));
+	pos += seq_printf(s, "\tDDADR = %08x\n", DDADR(chan));
+	return pos;
+}
+
+static int dbg_show_state(struct seq_file *s, void *p)
+{
+	int pos = 0;
+
+	/* basic device status */
+	pos += seq_printf(s, "DMA engine status\n");
+	pos += seq_printf(s, "\tChannel number: %d\n", num_dma_channels);
+
+	return pos;
+}
+
+#define DBGFS_FUNC_DECL(name) \
+static int dbg_open_##name(struct inode *inode, struct file *file) \
+{ \
+	return single_open(file, dbg_show_##name, inode->i_private); \
+} \
+static const struct file_operations dbg_fops_##name = { \
+	.owner		= THIS_MODULE, \
+	.open		= dbg_open_##name, \
+	.llseek		= seq_lseek, \
+	.read		= seq_read, \
+	.release	= single_release, \
+}
+
+DBGFS_FUNC_DECL(state);
+DBGFS_FUNC_DECL(chan_state);
+DBGFS_FUNC_DECL(descriptors);
+DBGFS_FUNC_DECL(requester_chan);
+
+static struct dentry *pxa_dmac_dbg_alloc_chan(int ch,
+		struct dentry *chandir, void *dt)
+{
+	char chan_name[11];
+	struct dentry *chan, *chan_state = NULL, *chan_descr = NULL;
+	struct dentry *chan_reqs = NULL;
+
+	scnprintf(chan_name, sizeof(chan_name), "%d", ch);
+	chan = debugfs_create_dir(chan_name, chandir);
+
+	if (chan)
+		chan_state = debugfs_create_file("state", 0400, chan, dt,
+						 &dbg_fops_chan_state);
+	if (chan_state)
+		chan_descr = debugfs_create_file("descriptors", 0400, chan, dt,
+						 &dbg_fops_descriptors);
+	if (chan_descr)
+		chan_reqs = debugfs_create_file("requesters", 0400, chan, dt,
+						&dbg_fops_requester_chan);
+	if (!chan_reqs)
+		goto err_state;
+
+	return chan;
+
+err_state:
+	debugfs_remove_recursive(chan);
+	return NULL;
+}
+
+static void pxa_dmac_init_debugfs(int nr_ch, struct pxa_dmac_chan *dmac)
+{
+	int i;
+	struct dentry *chandir;
+
+	num_dma_channels = nr_ch;
+
+	dbgfs_root = debugfs_create_dir(DMA_DEBUG_NAME, NULL);
+	if (IS_ERR(dbgfs_root) || !dbgfs_root)
+		goto err_root;
+
+	dbgfs_state = debugfs_create_file("state", 0400, dbgfs_root, NULL,
+					  &dbg_fops_state);
+	if (!dbgfs_state)
+		goto err_state;
+
+	dbgfs_chan = kmalloc(sizeof(*dbgfs_state) * num_dma_channels,
+			     GFP_KERNEL);
+	if (!dbgfs_chan)
+		goto err_alloc;
+
+	chandir = debugfs_create_dir("channels", dbgfs_root);
+	if (!chandir)
+		goto err_chandir;
+
+	for (i = 0; i < num_dma_channels; i++) {
+		dbgfs_chan[i] = pxa_dmac_dbg_alloc_chan(i, chandir, &dmac[i]);
+		if (!dbgfs_chan[i])
+			goto err_chans;
+	}
+
+	spin_lock_init(&dbgfs_lock);
+	return;
+err_chans:
+err_chandir:
+	kfree(dbgfs_chan);
+err_alloc:
+err_state:
+	debugfs_remove_recursive(dbgfs_root);
+err_root:
+	dev_dbg(dmac->pxa_dmac->dev, "pxa_dma: debugfs is not available\n");
+}
+
+static void __exit pxa_dmac_cleanup_debugfs(void)
+{
+	debugfs_remove_recursive(dbgfs_root);
+}
+#else
+static inline void pxa_dmac_init_debugfs(nr_ch) {}
+static inline void pxa_dmac_cleanup_debugfs(void) {}
+#endif
+
+#ifdef DEBUG
+static void pxa_dmac_dump_dma_list(struct pxa_dmac_chan *dmac)
+{
+	struct pxa_dma_desc *desc = dmac->desc_arr;
+	unsigned long flags;
+
+	if (!desc) {
+		dev_dbg(dmac->pxa_dmac->dev,
+			"dma description list has no node!\n");
+		return;
+	}
+
+	local_irq_save(flags);
+
+	dev_dbg(dmac->pxa_dmac->dev, "dma description list nodes:\n");
+	do {
+		dev_dbg(dmac->pxa_dmac->dev, ("---------------------\n");
+		dev_dbg(dmac->pxa_dmac->dev, "ddadr = 0x%08x\n", desc->ddadr);
+		dev_dbg(dmac->pxa_dmac->dev, "dsadr = 0x%08x\n", desc->dsadr);
+		dev_dbg(dmac->pxa_dmac->dev, "dtadr = 0x%08x\n", desc->dtadr);
+		dev_dbg(dmac->pxa_dmac->dev, "dcmd  = 0x%08x\n", desc->dcmd);
+
+		if (desc->ddadr & DDADR_STOP)
+			break;
+
+		desc = (pxa_dma_desc *)((desc->ddadr & DDADR_DESCADDR) -
+				(int)dmac->desc_arr_phys +
+				(int)dmac->desc_arr);
+
+	} while (desc != dmac->desc_arr);
+
+	local_irq_restore(flags);
+	return;
+}
+#else
+#define pxa_dmac_dump_dma_list(dmac) do { } while (0)
+#endif
+
+static struct pxa_dmac_chan *to_pxa_dmac_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct pxa_dmac_chan, chan);
+}
+
+static void pxa_dmac_enable_chan(struct pxa_dmac_chan *dmac)
+{
+	DCSR(dmac->idx) = DCSR(dmac->idx) | DCSR_RUN;
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: [%x] DCSR %x\n",
+		__func__, dmac->idx, DCSR(dmac->idx));
+}
+
+static void pxa_dmac_disable_chan(struct pxa_dmac_chan *dmac)
+{
+	DCSR(dmac->idx) = DCSR(dmac->idx) & ~DCSR_RUN;
+
+	dmac->status = DMA_SUCCESS;
+}
+
+static void pxa_dmac_resume_chan(struct pxa_dmac_chan *dmac)
+{
+	DCSR(dmac->idx) = DCSR(dmac->idx) | DCSR_RUN;
+
+	dmac->status = DMA_IN_PROGRESS;
+}
+
+static void pxa_dmac_pause_chan(struct pxa_dmac_chan *dmac)
+{
+	DCSR(dmac->idx) = DCSR(dmac->idx) & ~DCSR_RUN;
+
+	dmac->status = DMA_PAUSED;
+}
+
+static dma_cookie_t pxa_dmac_assign_cookie(struct pxa_dmac_chan *dmac)
+{
+	dma_cookie_t cookie = dmac->chan.cookie;
+
+	if (++cookie < 0)
+		cookie = 1;
+
+	dmac->chan.cookie = cookie;
+	dmac->desc.cookie = cookie;
+
+	return cookie;
+}
+
+unsigned long pxa_dmac_chan_get_src_ptr(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+
+	return DSADR(dmac->idx);
+}
+EXPORT_SYMBOL(pxa_dmac_chan_get_src_ptr);
+
+unsigned long pxa_dmac_chan_get_dst_ptr(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+
+	return DTADR(dmac->idx);
+}
+EXPORT_SYMBOL(pxa_dmac_chan_get_dst_ptr);
+
+pxa_dma_prio pxa_dmac_chan_get_prio(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+
+	return dmac->prio;
+}
+EXPORT_SYMBOL(pxa_dmac_chan_get_prio);
+
+static dma_cookie_t pxa_dmac_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(tx->chan);
+
+	pxa_dmac_enable_chan(dmac);
+
+	return pxa_dmac_assign_cookie(dmac);
+}
+
+static irqreturn_t pxa_dmac_int_handler(int irq, void *dev_id)
+{
+	struct pxa_dmac_chan *dmac = dev_id;
+
+	if (DCSR(dmac->idx) & DCSR_BUSERR) {
+		dev_err(dmac->pxa_dmac->dev, "%s: error in channel %d\n",
+			__func__, dmac->idx);
+
+		dmac->status = DMA_ERROR;
+	} else {
+		if (dmac->flags & DMAC_CYCLIC_LOOP)
+			dmac->status = DMA_IN_PROGRESS;
+		else
+			dmac->status = DMA_SUCCESS;
+	}
+
+	if (dmac->status == DMA_SUCCESS)
+		dmac->last_completed = dmac->desc.cookie;
+
+	/* clear irq */
+	DCSR(dmac->idx) = DCSR(dmac->idx) | DCSR_STARTINTR |
+		DCSR_ENDINTR | DCSR_BUSERR;
+
+	if (dmac->desc.callback)
+		dmac->desc.callback(dmac->desc.callback_param);
+
+	return IRQ_HANDLED;
+}
+
+static int pxa_dmac_alloc_descriptor(struct pxa_dmac_chan *dmac)
+{
+	struct pxa_dmac_engine *pxa_dmac = dmac->pxa_dmac;
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: enter\n", __func__);
+
+	dmac->desc_arr = dma_alloc_coherent(pxa_dmac->dev,
+			DMAC_DESC_SIZE, &dmac->desc_arr_phys, GFP_KERNEL);
+	if (!dmac->desc_arr)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void pxa_dmac_free_descriptor(struct pxa_dmac_chan *dmac)
+{
+	struct pxa_dmac_engine *pxa_dmac = dmac->pxa_dmac;
+
+	dma_free_coherent(pxa_dmac->dev, DMAC_DESC_SIZE,
+			dmac->desc_arr, dmac->desc_arr_phys);
+}
+
+static int pxa_dmac_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+	int ret;
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: enter\n", __func__);
+
+	ret = pxa_dmac_alloc_descriptor(dmac);
+	if (ret < 0)
+		return ret;
+
+	dma_async_tx_descriptor_init(&dmac->desc, chan);
+	dmac->desc.tx_submit = pxa_dmac_tx_submit;
+
+	/* the descriptor is ready */
+	async_tx_ack(&dmac->desc);
+
+	ret = request_irq(dmac->irq, pxa_dmac_int_handler, IRQF_DISABLED,
+			  "dmac", dmac);
+	if (ret)
+		goto err_request_irq;
+
+	/*
+	 * init command register for default config,
+	 * later user can call dmaengine_slave_config
+	 * to config more settings.
+	 */
+	dmac->dcmd = DCMD_ENDIRQEN | DCMD_BURST8;
+	return 0;
+
+err_request_irq:
+	pxa_dmac_free_descriptor(dmac);
+	return ret;
+}
+
+static void pxa_dmac_free_chan_resources(struct dma_chan *chan)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: enter\n", __func__);
+
+	free_irq(dmac->irq, dmac);
+	pxa_dmac_disable_chan(dmac);
+	pxa_dmac_free_descriptor(dmac);
+}
+
+static int pxa_dmac_config_chan(struct pxa_dmac_chan *dmac)
+{
+	struct pxa_dmac_engine *pxa_dmac = dmac->pxa_dmac;
+	struct pxa_dmac_data *dmac_data = dmac->chan.private;
+
+	pxa_dmac_disable_chan(dmac);
+
+	if (dmac->dir == DMA_TO_DEVICE) {
+		dmac->dcmd |= DCMD_INCSRCADDR;
+		if (dmac_data->flow_ctrl)
+			dmac->dcmd |= DCMD_FLOWTRG;
+	} else if (dmac->dir == DMA_FROM_DEVICE) {
+		dmac->dcmd |= DCMD_INCTRGADDR;
+		if (dmac_data->flow_ctrl)
+			dmac->dcmd |= DCMD_FLOWSRC;
+	} else
+		dmac->dcmd |= DCMD_INCSRCADDR | DCMD_INCTRGADDR;
+
+	switch (dmac->burst_sz) {
+	case 8: /* 8 bytes */
+		dmac->dcmd |= DCMD_BURST8;
+		break;
+	case 16: /* 16 bytes */
+		dmac->dcmd |= DCMD_BURST16;
+		break;
+	case 32: /* 32 bytes */
+		dmac->dcmd |= DCMD_BURST32;
+		break;
+	default:
+		dev_err(pxa_dmac->dmac_device.dev, "invalid burst size\n");
+		return -EINVAL;
+	}
+
+	switch (dmac->width) {
+	case DMA_SLAVE_BUSWIDTH_UNDEFINED:
+		break;
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		dmac->dcmd |= DCMD_WIDTH1;
+		break;
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		dmac->dcmd |= DCMD_WIDTH2;
+		break;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		dmac->dcmd |= DCMD_WIDTH4;
+		break;
+	default:
+		dev_err(pxa_dmac->dmac_device.dev, "invalid width size\n");
+		return -EINVAL;
+	}
+
+	dmac->ch_map = dmac_data->ch_map;
+	if (dmac->ch_map != -1)
+		DRCMR(dmac->ch_map) = dmac->idx | DRCMR_MAPVLD;
+
+	return 0;
+}
+
+static struct dma_async_tx_descriptor *pxa_dmac_prep_memcpy(
+		struct dma_chan *chan,
+		dma_addr_t dma_dst, dma_addr_t dma_src,
+		size_t len, unsigned long flags)
+{
+	struct pxa_dmac_chan *dmac;
+	struct pxa_dma_desc *desc;
+	size_t copy;
+	int i = 0;
+
+	if (!chan || !len)
+		return NULL;
+
+	dmac = to_pxa_dmac_chan(chan);
+	dmac->status = DMA_IN_PROGRESS;
+
+	dmac->dcmd |= DCMD_INCSRCADDR | DCMD_INCTRGADDR;
+
+	dev_dbg(dmac->pxa_dmac->dev, "%s: desc_arr %p desc_arr_phys %x\n",
+		__func__, dmac->desc_arr, dmac->desc_arr_phys);
+
+	do {
+		dev_dbg(dmac->pxa_dmac->dev, "%s: dst %x src %x len %d\n",
+			__func__, dma_dst, dma_src, len);
+
+		desc = &dmac->desc_arr[i];
+		copy = min(len, (size_t)DMAC_MAX_XFER_BYTES);
+
+		desc->ddadr = dmac->desc_arr_phys + sizeof(*desc) * (i + 1);
+		desc->dsadr = dma_src;
+		desc->dtadr = dma_dst;
+		desc->dcmd  = dmac->dcmd | copy;
+
+		len -= copy;
+		dma_src += copy;
+		dma_dst += copy;
+
+		if (!len)
+			desc->ddadr |= DDADR_STOP;
+
+		i++;
+	} while (len);
+
+	DDADR(dmac->idx) = dmac->desc_arr_phys;
+
+	pxa_dmac_dump_dma_list(dmac);
+	return &dmac->desc;
+}
+
+static struct dma_async_tx_descriptor *pxa_dmac_prep_sg(struct dma_chan *chan,
+	struct scatterlist *dst_sg, unsigned int dst_nents,
+	struct scatterlist *src_sg, unsigned int src_nents,
+	unsigned long flags)
+{
+	struct pxa_dmac_chan *dmac;
+	struct pxa_dma_desc *desc = NULL;
+	size_t dst_avail, src_avail;
+	dma_addr_t dst, src;
+	size_t len;
+	int i = 0;
+
+	/* basic sanity checks */
+	if (dst_nents == 0 || src_nents == 0)
+		return NULL;
+
+	if (dst_sg == NULL || src_sg == NULL)
+		return NULL;
+
+	dmac = to_pxa_dmac_chan(chan);
+	dmac->status = DMA_IN_PROGRESS;
+
+	dmac->dcmd |= DCMD_INCSRCADDR | DCMD_INCTRGADDR;
+
+	/* get prepared for the loop */
+	dst_avail = sg_dma_len(dst_sg);
+	src_avail = sg_dma_len(src_sg);
+
+	/* run until we are out of scatterlist entries */
+	while (true) {
+
+		dev_dbg(dmac->pxa_dmac->dev, "%s: dst_avail %x src_avail %x\n",
+			__func__, dst_avail, src_avail);
+
+		/* create the largest transaction possible */
+		len = min_t(size_t, src_avail, dst_avail);
+		len = min_t(size_t, len, DMAC_MAX_XFER_BYTES);
+		if (len == 0)
+			goto fetch;
+
+		dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_avail;
+		src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_avail;
+
+		dev_dbg(dmac->pxa_dmac->dev, "%s: dst %x src %x len %x\n",
+			__func__, dst, src, len);
+
+		if (i >= DMAC_DESC_NUM)
+			goto fail;
+
+		desc = &dmac->desc_arr[i];
+		desc->ddadr = dmac->desc_arr_phys + sizeof(*desc) * (i + 1);
+		desc->dsadr = src;
+		desc->dtadr = dst;
+		desc->dcmd  = dmac->dcmd | len;
+
+		/* update metadata */
+		dst_avail -= len;
+		src_avail -= len;
+		i++;
+
+fetch:
+		/* fetch the next dst scatterlist entry */
+		if (dst_avail == 0) {
+
+			/* no more entries: we're done */
+			if (dst_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			dst_sg = sg_next(dst_sg);
+			if (dst_sg == NULL)
+				break;
+
+			dst_nents--;
+			dst_avail = sg_dma_len(dst_sg);
+		}
+
+		/* fetch the next src scatterlist entry */
+		if (src_avail == 0) {
+
+			/* no more entries: we're done */
+			if (src_nents == 0)
+				break;
+
+			/* fetch the next entry: if there are no more: done */
+			src_sg = sg_next(src_sg);
+			if (src_sg == NULL)
+				break;
+
+			src_nents--;
+			src_avail = sg_dma_len(src_sg);
+		}
+	}
+
+	if (desc)
+		desc->ddadr |= DDADR_STOP;
+
+	DDADR(dmac->idx) = dmac->desc_arr_phys;
+
+	pxa_dmac_dump_dma_list(dmac);
+	return &dmac->desc;
+
+fail:
+	return NULL;
+}
+
+static struct dma_async_tx_descriptor *pxa_dmac_prep_slave_sg(
+		struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_data_direction direction,
+		unsigned long append)
+{
+	struct pxa_dmac_chan *dmac;
+	struct pxa_dma_desc *desc = NULL;
+	struct scatterlist *sg;
+	size_t avail, len;
+	dma_addr_t slave_addr;
+	unsigned int i;
+
+	if (!sgl || !sg_len)
+		return NULL;
+
+	dmac = to_pxa_dmac_chan(chan);
+	dmac->status = DMA_IN_PROGRESS;
+
+	slave_addr = dmac->dev_addr;
+	for_each_sg(sgl, sg, sg_len, i) {
+		dma_addr_t sg_addr = sg_dma_address(sg);
+		avail = sg_dma_len(sg);
+		do {
+			len = min_t(size_t, avail, DMAC_MAX_XFER_BYTES);
+
+			dev_dbg(dmac->pxa_dmac->dev,
+				"Add SG #%d@%p[%d], dma %llx\n",
+				i, sg, len, (unsigned long long)sg_addr);
+
+			if (i >= DMAC_DESC_NUM)
+				goto fail;
+
+			desc = &dmac->desc_arr[i];
+			desc->ddadr = dmac->desc_arr_phys +
+				sizeof(*desc) * (i + 1);
+			if (direction == DMA_FROM_DEVICE) {
+				desc->dtadr = sg_addr;
+				desc->dsadr = slave_addr;
+				desc->dcmd  = dmac->dcmd | len;
+			} else {
+				desc->dtadr = slave_addr;
+				desc->dsadr = sg_addr;
+				desc->dcmd  = dmac->dcmd | len;
+			}
+
+			avail -= len;
+			sg_addr += len;
+			i++;
+
+		} while (avail);
+	}
+
+	if (desc)
+		desc->ddadr |= DDADR_STOP;
+
+	DDADR(dmac->idx) = dmac->desc_arr_phys;
+
+	pxa_dmac_dump_dma_list(dmac);
+	return &dmac->desc;
+
+fail:
+	return NULL;
+}
+
+static int pxa_dmac_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+		unsigned long arg)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+	struct dma_slave_config *dmaengine_cfg = (void *)arg;
+	int ret = 0;
+
+	switch (cmd) {
+	case DMA_TERMINATE_ALL:
+		pxa_dmac_disable_chan(dmac);
+		break;
+	case DMA_PAUSE:
+		pxa_dmac_pause_chan(dmac);
+		break;
+	case DMA_RESUME:
+		pxa_dmac_resume_chan(dmac);
+		break;
+	case DMA_SLAVE_CONFIG:
+		if (dmaengine_cfg->direction == DMA_FROM_DEVICE) {
+			dmac->dev_addr = dmaengine_cfg->src_addr;
+			dmac->burst_sz = dmaengine_cfg->src_maxburst;
+			dmac->width    = dmaengine_cfg->src_addr_width;
+		} else {
+			dmac->dev_addr = dmaengine_cfg->dst_addr;
+			dmac->burst_sz = dmaengine_cfg->dst_maxburst;
+			dmac->width    = dmaengine_cfg->dst_addr_width;
+		}
+		dmac->dir = dmaengine_cfg->direction;
+		return pxa_dmac_config_chan(dmac);
+	default:
+		ret = -ENOSYS;
+	}
+
+	return ret;
+}
+
+static enum dma_status pxa_dmac_tx_status(struct dma_chan *chan,
+			dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	struct pxa_dmac_chan *dmac = to_pxa_dmac_chan(chan);
+	dma_cookie_t last_used;
+
+	last_used = chan->cookie;
+	dma_set_tx_state(txstate, dmac->last_completed, last_used, 0);
+
+	return dmac->status;
+}
+
+static void pxa_dmac_issue_pending(struct dma_chan *chan)
+{
+	/*
+	 * Nothing to do. We only have a single descriptor.
+	 */
+}
+
+static void pxa_dmac_irq_ack(struct irq_data *d)
+{
+	DCSR(d->irq - dma_irq_base) = DCSR(d->irq - dma_irq_base) |
+		DCSR_STARTINTR | DCSR_ENDINTR | DCSR_BUSERR;
+}
+
+static void pxa_dmac_irq_mask(struct irq_data *d)
+{
+	DCSR(d->irq - dma_irq_base) = DCSR(d->irq - dma_irq_base) &
+		~DCSR_STOPIRQEN;
+}
+
+static void pxa_dmac_irq_unmask(struct irq_data *d)
+{
+	/*
+	 * As manual say, if the STOPIRQEN bit is set
+	 * before the channel is started, an interrupt
+	 * will be generated. So let driver set this bit
+	 * after the channel has began to run.
+	 */
+}
+
+static void pxa_dma_demux_handler(unsigned int irq, struct irq_desc *desc)
+{
+	int i, dint = DINT;
+
+	while (dint) {
+		i = __ffs(dint);
+		dint &= (dint - 1);
+		generic_handle_irq(dma_irq_base + i);
+	}
+}
+
+static struct irq_chip pxa_muxed_dma_chip = {
+	.name		= "DMAC",
+	.irq_ack	= pxa_dmac_irq_ack,
+	.irq_mask	= pxa_dmac_irq_mask,
+	.irq_unmask	= pxa_dmac_irq_unmask,
+};
+
+static int __init pxa_dmac_init_irq(int mux_irq, int second_irq_start,
+				    int num_ch)
+{
+	int i = 0, irq;
+	struct irq_data *d;
+	struct irq_chip *chip = &pxa_muxed_dma_chip;
+
+	/* init dma irq */
+	for (irq = second_irq_start; i < num_ch; irq++, i++) {
+		d = irq_get_irq_data(irq);
+
+		/* mask and clear the IRQ */
+		chip->irq_mask(d);
+		if (chip->irq_ack)
+			chip->irq_ack(d);
+
+		irq_set_chip(irq, chip);
+		set_irq_flags(irq, IRQF_VALID);
+		irq_set_handler(irq, handle_level_irq);
+	}
+	irq_set_chained_handler(mux_irq, pxa_dma_demux_handler);
+
+	return 0;
+}
+
+static int __init pxa_dmac_probe(struct platform_device *pdev)
+{
+	struct pxa_dmac_engine *pxa_dmac;
+	struct pxa_dmac_chan *dmac;
+	struct resource *iores;
+	struct pxa_dmac_platform_data *pdata = pdev->dev.platform_data;
+	int i, irq;
+	int ret;
+
+	if (!pdata)
+		return -ENODEV;
+
+	dma_irq_base = pdata->irq_base;
+
+	pxa_dmac = kzalloc(pdata->nr_ch * (sizeof(*dmac)) +
+			   sizeof(*pxa_dmac), GFP_KERNEL);
+	if (!pxa_dmac)
+		return -ENOMEM;
+
+	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iores) {
+		ret = -EINVAL;
+		goto err_irq;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		dev_info(&pdev->dev, "No interrupt specified\n");
+	else
+		pxa_dmac_init_irq(irq, pdata->irq_base, pdata->nr_ch);
+
+	if (!request_mem_region(iores->start, resource_size(iores),
+				pdev->name)) {
+		ret = -EBUSY;
+		goto err_request_region;
+	}
+
+	pxa_dmac->base = ioremap(iores->start, resource_size(iores));
+	if (!pxa_dmac->base) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	dma_cap_set(DMA_MEMCPY, pxa_dmac->dmac_device.cap_mask);
+	dma_cap_set(DMA_SLAVE, pxa_dmac->dmac_device.cap_mask);
+	dma_cap_set(DMA_SG, pxa_dmac->dmac_device.cap_mask);
+
+	INIT_LIST_HEAD(&pxa_dmac->dmac_device.channels);
+
+	/* initialize channel parameters */
+	for (i = 0; i < pdata->nr_ch; i++) {
+
+		dmac = &pxa_dmac->dmac[i];
+
+		/*
+		 * dma channel priorities on pxa2xx processors:
+		 * ch 0 - 3,  16 - 19  <--> (0) DMA_PRIO_HIGH
+		 * ch 4 - 7,  20 - 23  <--> (1) DMA_PRIO_MEDIUM
+		 * ch 8 - 15, 24 - 31  <--> (2) DMA_PRIO_LOW
+		 */
+		dmac->pxa_dmac	  = pxa_dmac;
+		dmac->chan.device = &pxa_dmac->dmac_device;
+		dmac->prio	  = min((i & 0xf) >> 2, DMA_PRIO_LOW);
+		dmac->irq	  = pdata->irq_base + i;
+		dmac->idx	  = i;
+
+		/* add the channel to tdma_chan list */
+		list_add_tail(&dmac->chan.device_node,
+			      &pxa_dmac->dmac_device.channels);
+	}
+
+	pxa_dmac->dev = &pdev->dev;
+	pxa_dmac->dmac_nr = pdata->nr_ch;
+	pxa_dmac->dmac_device.dev = &pdev->dev;
+	pxa_dmac->dmac_device.device_alloc_chan_resources =
+					pxa_dmac_alloc_chan_resources;
+	pxa_dmac->dmac_device.device_free_chan_resources =
+					pxa_dmac_free_chan_resources;
+	pxa_dmac->dmac_device.device_prep_dma_memcpy = pxa_dmac_prep_memcpy;
+	pxa_dmac->dmac_device.device_prep_slave_sg = pxa_dmac_prep_slave_sg;
+	pxa_dmac->dmac_device.device_prep_dma_sg = pxa_dmac_prep_sg;
+	pxa_dmac->dmac_device.device_tx_status = pxa_dmac_tx_status;
+	pxa_dmac->dmac_device.device_issue_pending = pxa_dmac_issue_pending;
+	pxa_dmac->dmac_device.device_control = pxa_dmac_control;
+	pxa_dmac->dmac_device.copy_align = DMAC_ALIGNMENT;
+
+	dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
+
+	ret = dma_async_device_register(&pxa_dmac->dmac_device);
+	if (ret) {
+		dev_err(pxa_dmac->dmac_device.dev, "unable to register\n");
+		goto err_init;
+	}
+
+	pxa_dmac_init_debugfs(pdata->nr_ch, pxa_dmac->dmac);
+
+	dev_info(pxa_dmac->dmac_device.dev, "initialized\n");
+	return 0;
+
+err_init:
+	iounmap(pxa_dmac->base);
+err_ioremap:
+	release_mem_region(iores->start, resource_size(iores));
+err_request_region:
+err_irq:
+	kfree(pxa_dmac);
+	return ret;
+}
+
+static struct platform_driver pxa_dmac_driver = {
+	.driver		= {
+		.name	= "pxa-dmac",
+	},
+};
+
+static int __init pxa_dmac_module_init(void)
+{
+	return platform_driver_probe(&pxa_dmac_driver, pxa_dmac_probe);
+}
+subsys_initcall(pxa_dmac_module_init);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("PXA DMAC driver");
+MODULE_LICENSE("GPL");
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 05/13] mmp2:register dma devices for mmp2 platform
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.register dma devices for mmp2 platform

Change-Id: I19a5bd38fbd65f2e465faec0aa9550cae7d5c592
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/mmp2.c |  122 +++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-mmp/mmp2.c b/arch/arm/mach-mmp/mmp2.c
index a0e083f..d816b47 100644
--- a/arch/arm/mach-mmp/mmp2.c
+++ b/arch/arm/mach-mmp/mmp2.c
@@ -1152,6 +1152,122 @@ static struct clk_lookup mmp2_clkregs[] = {
 	INIT_CLKREG(&clk_ccic2_gate, NULL, "CCIC2GATECLK"),
 };
 
+static struct resource mmp2_mdma_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xd42a0a00,
+		.end    = 0xd42a0a00 + SZ_512 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.name   = "mdma_irq",
+		.start  = IRQ_MMP2_DMA_SECONDARY(16),
+		.end    = IRQ_MMP2_DMA_SECONDARY(17),
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct mmp_tdma_chan_info mdma_chan_info[] = {
+	[0] = {
+		.type     = MDMA1_CH0,
+		.reg_base = 0xd42a0a00,
+	},
+	[1] = {
+		.type     = MDMA1_CH1,
+		.reg_base = 0xd42a0a04,
+	},
+};
+
+struct mmp_tdma_platform_data mdma_platform_data = {
+	.nr_ch = 2,
+	.info  = mdma_chan_info,
+};
+
+static struct platform_device mmp2_device_mdma = {
+	.name           = "mmp-mdma",
+	.id             = -1,
+	.num_resources  = ARRAY_SIZE(mmp2_mdma_resource),
+	.resource       = mmp2_mdma_resource,
+	.dev            = {
+		.platform_data = &mdma_platform_data,
+		.coherent_dma_mask = DMA_BIT_MASK(64),
+	},
+};
+
+static struct resource mmp2_adma_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xd42a0800,
+		.end    = 0xd42a0800 + SZ_512 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.name   = "adma_irq",
+		.start  = IRQ_MMP2_DMA_SECONDARY(18),
+		.end    = IRQ_MMP2_DMA_SECONDARY(21),
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct mmp_tdma_chan_info amda_chan_info[] = {
+	[0] = {
+		.type	  = ADMA1_CH0,
+		.reg_base = 0xd42a0800,
+	},
+	[1] = {
+		.type	  = ADMA1_CH1,
+		.reg_base = 0xd42a0804,
+	},
+	[2] = {
+		.type	  = ADMA2_CH0,
+		.reg_base = 0xd42a0900,
+	},
+	[3] = {
+		.type	  = ADMA2_CH1,
+		.reg_base = 0xd42a0904,
+	},
+};
+
+struct mmp_tdma_platform_data amda_platform_data = {
+	.nr_ch = 4,
+	.info  = amda_chan_info,
+};
+
+static struct platform_device mmp2_device_adma = {
+	.name		= "mmp-adma",
+	.id		= -1,
+	.num_resources  = ARRAY_SIZE(mmp2_adma_resource),
+	.resource       = mmp2_adma_resource,
+	.dev		= {
+		.platform_data = &amda_platform_data,
+	},
+};
+
+static struct resource mmp2_dmac_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xd4000000,
+		.end    = 0xd4000000 + SZ_1K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+struct pxa_dmac_platform_data dmac_platform_data = {
+	.irq_base = IRQ_MMP2_DMA_BASE,
+	.nr_ch = 16,
+};
+
+static struct platform_device mmp2_device_dmac = {
+	.name           = "pxa-dmac",
+	.id             = -1,
+	.num_resources  = ARRAY_SIZE(mmp2_dmac_resource),
+	.resource       = mmp2_dmac_resource,
+	.dev            = {
+		.platform_data = &dmac_platform_data,
+		.coherent_dma_mask = DMA_BIT_MASK(64),
+	},
+};
+
 #define MCB_SLFST_SEL		0xD0000570
 #define MCB_SLFST_CTRL2		0xD00005A0
 void __init mmp2_init_mcb(void)
@@ -1178,11 +1294,13 @@ static int __init mmp2_init(void)
 #endif
 		mfp_init_base(MFPR_VIRT_BASE);
 		mfp_init_addr(mmp2_addr_map);
-		pxa_init_dma(IRQ_MMP2_DMA_RIQ, 16);
-		mmp_init_dma(IRQ_MMP2_DMA_RIQ);
 		mmp2_init_mcb();
 		clkdev_add_table(ARRAY_AND_SIZE(mmp2_clkregs));
 		register_syscore_ops(&pxa_audio_syscore_ops);
+
+		platform_device_register(&mmp2_device_dmac);
+		platform_device_register(&mmp2_device_adma);
+		platform_device_register(&mmp2_device_mdma);
 	}
 
 	return 0;
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 05/13] mmp2:register dma devices for mmp2 platform
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.register dma devices for mmp2 platform

Change-Id: I19a5bd38fbd65f2e465faec0aa9550cae7d5c592
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/mmp2.c |  122 +++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-mmp/mmp2.c b/arch/arm/mach-mmp/mmp2.c
index a0e083f..d816b47 100644
--- a/arch/arm/mach-mmp/mmp2.c
+++ b/arch/arm/mach-mmp/mmp2.c
@@ -1152,6 +1152,122 @@ static struct clk_lookup mmp2_clkregs[] = {
 	INIT_CLKREG(&clk_ccic2_gate, NULL, "CCIC2GATECLK"),
 };
 
+static struct resource mmp2_mdma_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xd42a0a00,
+		.end    = 0xd42a0a00 + SZ_512 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.name   = "mdma_irq",
+		.start  = IRQ_MMP2_DMA_SECONDARY(16),
+		.end    = IRQ_MMP2_DMA_SECONDARY(17),
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct mmp_tdma_chan_info mdma_chan_info[] = {
+	[0] = {
+		.type     = MDMA1_CH0,
+		.reg_base = 0xd42a0a00,
+	},
+	[1] = {
+		.type     = MDMA1_CH1,
+		.reg_base = 0xd42a0a04,
+	},
+};
+
+struct mmp_tdma_platform_data mdma_platform_data = {
+	.nr_ch = 2,
+	.info  = mdma_chan_info,
+};
+
+static struct platform_device mmp2_device_mdma = {
+	.name           = "mmp-mdma",
+	.id             = -1,
+	.num_resources  = ARRAY_SIZE(mmp2_mdma_resource),
+	.resource       = mmp2_mdma_resource,
+	.dev            = {
+		.platform_data = &mdma_platform_data,
+		.coherent_dma_mask = DMA_BIT_MASK(64),
+	},
+};
+
+static struct resource mmp2_adma_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xd42a0800,
+		.end    = 0xd42a0800 + SZ_512 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.name   = "adma_irq",
+		.start  = IRQ_MMP2_DMA_SECONDARY(18),
+		.end    = IRQ_MMP2_DMA_SECONDARY(21),
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct mmp_tdma_chan_info amda_chan_info[] = {
+	[0] = {
+		.type	  = ADMA1_CH0,
+		.reg_base = 0xd42a0800,
+	},
+	[1] = {
+		.type	  = ADMA1_CH1,
+		.reg_base = 0xd42a0804,
+	},
+	[2] = {
+		.type	  = ADMA2_CH0,
+		.reg_base = 0xd42a0900,
+	},
+	[3] = {
+		.type	  = ADMA2_CH1,
+		.reg_base = 0xd42a0904,
+	},
+};
+
+struct mmp_tdma_platform_data amda_platform_data = {
+	.nr_ch = 4,
+	.info  = amda_chan_info,
+};
+
+static struct platform_device mmp2_device_adma = {
+	.name		= "mmp-adma",
+	.id		= -1,
+	.num_resources  = ARRAY_SIZE(mmp2_adma_resource),
+	.resource       = mmp2_adma_resource,
+	.dev		= {
+		.platform_data = &amda_platform_data,
+	},
+};
+
+static struct resource mmp2_dmac_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xd4000000,
+		.end    = 0xd4000000 + SZ_1K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+struct pxa_dmac_platform_data dmac_platform_data = {
+	.irq_base = IRQ_MMP2_DMA_BASE,
+	.nr_ch = 16,
+};
+
+static struct platform_device mmp2_device_dmac = {
+	.name           = "pxa-dmac",
+	.id             = -1,
+	.num_resources  = ARRAY_SIZE(mmp2_dmac_resource),
+	.resource       = mmp2_dmac_resource,
+	.dev            = {
+		.platform_data = &dmac_platform_data,
+		.coherent_dma_mask = DMA_BIT_MASK(64),
+	},
+};
+
 #define MCB_SLFST_SEL		0xD0000570
 #define MCB_SLFST_CTRL2		0xD00005A0
 void __init mmp2_init_mcb(void)
@@ -1178,11 +1294,13 @@ static int __init mmp2_init(void)
 #endif
 		mfp_init_base(MFPR_VIRT_BASE);
 		mfp_init_addr(mmp2_addr_map);
-		pxa_init_dma(IRQ_MMP2_DMA_RIQ, 16);
-		mmp_init_dma(IRQ_MMP2_DMA_RIQ);
 		mmp2_init_mcb();
 		clkdev_add_table(ARRAY_AND_SIZE(mmp2_clkregs));
 		register_syscore_ops(&pxa_audio_syscore_ops);
+
+		platform_device_register(&mmp2_device_dmac);
+		platform_device_register(&mmp2_device_adma);
+		platform_device_register(&mmp2_device_mdma);
 	}
 
 	return 0;
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 06/13] mmp3:register dma devices for mmp3 platform
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.register dma device for mmp3 platform

Change-Id: I93675ca4df66c17e08d6d64e3352d5bccf8afa9c
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/mmp3.c |   78 ++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-mmp/mmp3.c b/arch/arm/mach-mmp/mmp3.c
index 8ae7d1f..a98c611 100644
--- a/arch/arm/mach-mmp/mmp3.c
+++ b/arch/arm/mach-mmp/mmp3.c
@@ -249,6 +249,80 @@ struct platform_device mmp3_device_asoc_hdmi = {
 	.id		= -1,
 };
 
+static struct resource mmp3_adma_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xc0ffd800,
+		.end    = 0xc0ffd800 + SZ_512 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.name   = "adma_irq",
+		.start  = IRQ_MMP3_DMA_SECONDARY(18),
+		.end    = IRQ_MMP3_DMA_SECONDARY(21),
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct mmp_tdma_chan_info amda_chan_info[] = {
+	[0] = {
+		.type	  = ADMA1_CH0,
+		.reg_base = 0xc0ffd800,
+	},
+	[1] = {
+		.type	  = ADMA1_CH1,
+		.reg_base = 0xc0ffd804,
+	},
+	[2] = {
+		.type	  = ADMA2_CH0,
+		.reg_base = 0xc0ffd900,
+	},
+	[3] = {
+		.type	  = ADMA2_CH1,
+		.reg_base = 0xc0ffd904,
+	},
+};
+
+struct mmp_tdma_platform_data amda_platform_data = {
+	.nr_ch = 4,
+	.info  = amda_chan_info,
+};
+
+static struct platform_device mmp3_device_adma = {
+	.name		= "mmp-adma",
+	.id		= -1,
+	.num_resources  = ARRAY_SIZE(mmp3_adma_resource),
+	.resource       = mmp3_adma_resource,
+	.dev		= {
+		.platform_data = &amda_platform_data,
+	},
+};
+
+static struct resource mmp3_dmac_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xd4000000,
+		.end    = 0xd4000000 + SZ_1K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+struct pxa_dmac_platform_data dmac_platform_data = {
+	.irq_base = IRQ_MMP3_DMA_BASE,
+	.nr_ch = 16,
+};
+
+static struct platform_device mmp3_device_dmac = {
+	.name           = "pxa-dmac",
+	.id             = -1,
+	.num_resources  = ARRAY_SIZE(mmp3_dmac_resource),
+	.resource       = mmp3_dmac_resource,
+	.dev            = {
+		.platform_data = &dmac_platform_data,
+		.coherent_dma_mask = DMA_BIT_MASK(64),
+	},
+};
+
 static int __init mmp3_init(void)
 {
 	/*
@@ -263,8 +337,8 @@ static int __init mmp3_init(void)
 
 	mmp3_init_gpio();
 
-	pxa_init_dma(IRQ_MMP3_DMA_RIQ, 16);
-	mmp_init_dma(IRQ_MMP3_DMA_RIQ);
+	platform_device_register(&mmp3_device_dmac);
+	platform_device_register(&mmp3_device_adma);
 
 	platform_device_register(&mmp3_device_asoc_sspa1);
 	platform_device_register(&mmp3_device_asoc_platform);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 06/13] mmp3:register dma devices for mmp3 platform
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.register dma device for mmp3 platform

Change-Id: I93675ca4df66c17e08d6d64e3352d5bccf8afa9c
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/mmp3.c |   78 ++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-mmp/mmp3.c b/arch/arm/mach-mmp/mmp3.c
index 8ae7d1f..a98c611 100644
--- a/arch/arm/mach-mmp/mmp3.c
+++ b/arch/arm/mach-mmp/mmp3.c
@@ -249,6 +249,80 @@ struct platform_device mmp3_device_asoc_hdmi = {
 	.id		= -1,
 };
 
+static struct resource mmp3_adma_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xc0ffd800,
+		.end    = 0xc0ffd800 + SZ_512 - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+	[1] = {
+		.name   = "adma_irq",
+		.start  = IRQ_MMP3_DMA_SECONDARY(18),
+		.end    = IRQ_MMP3_DMA_SECONDARY(21),
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+struct mmp_tdma_chan_info amda_chan_info[] = {
+	[0] = {
+		.type	  = ADMA1_CH0,
+		.reg_base = 0xc0ffd800,
+	},
+	[1] = {
+		.type	  = ADMA1_CH1,
+		.reg_base = 0xc0ffd804,
+	},
+	[2] = {
+		.type	  = ADMA2_CH0,
+		.reg_base = 0xc0ffd900,
+	},
+	[3] = {
+		.type	  = ADMA2_CH1,
+		.reg_base = 0xc0ffd904,
+	},
+};
+
+struct mmp_tdma_platform_data amda_platform_data = {
+	.nr_ch = 4,
+	.info  = amda_chan_info,
+};
+
+static struct platform_device mmp3_device_adma = {
+	.name		= "mmp-adma",
+	.id		= -1,
+	.num_resources  = ARRAY_SIZE(mmp3_adma_resource),
+	.resource       = mmp3_adma_resource,
+	.dev		= {
+		.platform_data = &amda_platform_data,
+	},
+};
+
+static struct resource mmp3_dmac_resource[] = {
+	[0] = {
+		.name   = "reg_phys",
+		.start  = 0xd4000000,
+		.end    = 0xd4000000 + SZ_1K - 1,
+		.flags  = IORESOURCE_MEM,
+	},
+};
+
+struct pxa_dmac_platform_data dmac_platform_data = {
+	.irq_base = IRQ_MMP3_DMA_BASE,
+	.nr_ch = 16,
+};
+
+static struct platform_device mmp3_device_dmac = {
+	.name           = "pxa-dmac",
+	.id             = -1,
+	.num_resources  = ARRAY_SIZE(mmp3_dmac_resource),
+	.resource       = mmp3_dmac_resource,
+	.dev            = {
+		.platform_data = &dmac_platform_data,
+		.coherent_dma_mask = DMA_BIT_MASK(64),
+	},
+};
+
 static int __init mmp3_init(void)
 {
 	/*
@@ -263,8 +337,8 @@ static int __init mmp3_init(void)
 
 	mmp3_init_gpio();
 
-	pxa_init_dma(IRQ_MMP3_DMA_RIQ, 16);
-	mmp_init_dma(IRQ_MMP3_DMA_RIQ);
+	platform_device_register(&mmp3_device_dmac);
+	platform_device_register(&mmp3_device_adma);
 
 	platform_device_register(&mmp3_device_asoc_sspa1);
 	platform_device_register(&mmp3_device_asoc_platform);
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 07/13] DMA:mmp:remove redundant dma drivers
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.remove old dma drivers

Change-Id: I96ffed95012a4f3e855c2f6c184646a798a0ee7d
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/Makefile |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile
index a5d5ba7..d06adcb 100644
--- a/arch/arm/mach-mmp/Makefile
+++ b/arch/arm/mach-mmp/Makefile
@@ -9,9 +9,9 @@ obj-$(CONFIG_FB_PXA168)		+= onboard/
 obj-$(CONFIG_CPU_PXA168)	+= pxa168.o irq-pxa168.o
 obj-$(CONFIG_CPU_PXA910)	+= pxa910.o irq-pxa168.o sram.o pxa910-squ.o \
 clock.o
-obj-$(CONFIG_CPU_MMP2)		+= mmp2.o irq-mmp2.o clock.o mmp_dma.o \
+obj-$(CONFIG_CPU_MMP2)		+= mmp2.o irq-mmp2.o clock.o \
 sram.o mmp2_sspa.o mmp2_fuse.o clock-audio.o
-obj-$(CONFIG_CPU_MMP3)		+= mmp3.o irq-mmp3.o mmp_dma.o mmp2_sspa.o \
+obj-$(CONFIG_CPU_MMP3)		+= mmp3.o irq-mmp3.o mmp2_sspa.o \
 sram.o delay-mmp3.o clock-mmp3.o dvfs-mmp3.o
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 07/13] DMA:mmp:remove redundant dma drivers
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.remove old dma drivers

Change-Id: I96ffed95012a4f3e855c2f6c184646a798a0ee7d
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/mach-mmp/Makefile |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-mmp/Makefile b/arch/arm/mach-mmp/Makefile
index a5d5ba7..d06adcb 100644
--- a/arch/arm/mach-mmp/Makefile
+++ b/arch/arm/mach-mmp/Makefile
@@ -9,9 +9,9 @@ obj-$(CONFIG_FB_PXA168)		+= onboard/
 obj-$(CONFIG_CPU_PXA168)	+= pxa168.o irq-pxa168.o
 obj-$(CONFIG_CPU_PXA910)	+= pxa910.o irq-pxa168.o sram.o pxa910-squ.o \
 clock.o
-obj-$(CONFIG_CPU_MMP2)		+= mmp2.o irq-mmp2.o clock.o mmp_dma.o \
+obj-$(CONFIG_CPU_MMP2)		+= mmp2.o irq-mmp2.o clock.o \
 sram.o mmp2_sspa.o mmp2_fuse.o clock-audio.o
-obj-$(CONFIG_CPU_MMP3)		+= mmp3.o irq-mmp3.o mmp_dma.o mmp2_sspa.o \
+obj-$(CONFIG_CPU_MMP3)		+= mmp3.o irq-mmp3.o mmp2_sspa.o \
 sram.o delay-mmp3.o clock-mmp3.o dvfs-mmp3.o
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 08/13] DMAC:tty serial pxa use generic dma engine APIs
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.tty serial device use dma engine APIs

Change-Id: Ie425e6cb9321914e84a7e53b8a2d57d957dce42c
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/configs/mmp2_defconfig |    2 +-
 arch/arm/configs/mmp3_defconfig |    2 +-
 drivers/tty/serial/Kconfig      |   12 +-
 drivers/tty/serial/Makefile     |    1 +
 drivers/tty/serial/mmp_pxa.c    | 1698 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 1712 insertions(+), 3 deletions(-)
 create mode 100644 drivers/tty/serial/mmp_pxa.c

diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index 7000d6c..138c95a 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -967,7 +967,7 @@ CONFIG_DEVKMEM=y
 #
 # Non-8250 serial port support
 #
-CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_MMP_PXA=y
 CONFIG_SERIAL_PXA_CONSOLE=y
 CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
diff --git a/arch/arm/configs/mmp3_defconfig b/arch/arm/configs/mmp3_defconfig
index 53a9684..4f8b160 100644
--- a/arch/arm/configs/mmp3_defconfig
+++ b/arch/arm/configs/mmp3_defconfig
@@ -1032,7 +1032,7 @@ CONFIG_DEVKMEM=y
 #
 # CONFIG_SERIAL_MAX3100 is not set
 # CONFIG_SERIAL_MAX3107 is not set
-CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_MMP_PXA=y
 CONFIG_SERIAL_PXA_CONSOLE=y
 CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index b3692e6..70114c5 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -668,9 +668,19 @@ config SERIAL_PXA
 	  If you have a machine based on an Intel XScale PXA2xx CPU you
 	  can enable its onboard serial ports by enabling this option.
 
+config SERIAL_MMP_PXA
+	bool "MMP PXA serial port support"
+	depends on ARCH_PXA || ARCH_MMP
+	select SERIAL_CORE
+	help
+	  If you have a machine based on an Intel XScale PXA2xx CPU you
+	  can enable its onboard serial ports by enabling this option.
+	  mmp_pxa is a temporary config for mmp2 and mmp3,separate it from
+	  pxa.c for pxa910 and MG1.
+
 config SERIAL_PXA_CONSOLE
 	bool "Console on PXA serial port"
-	depends on SERIAL_PXA
+	depends on SERIAL_PXA || SERIAL_MMP_PXA
 	select SERIAL_CORE_CONSOLE
 	help
 	  If you have enabled the serial port on the Intel XScale PXA
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cb2628f..bc6c1d3 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
 obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
 obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
 obj-$(CONFIG_SERIAL_PXA) += pxa.o
+obj-$(CONFIG_SERIAL_MMP_PXA) += mmp_pxa.o
 obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
 obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
 obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o
diff --git a/drivers/tty/serial/mmp_pxa.c b/drivers/tty/serial/mmp_pxa.c
new file mode 100644
index 0000000..68e5b3b
--- /dev/null
+++ b/drivers/tty/serial/mmp_pxa.c
@@ -0,0 +1,1698 @@
+/*
+ *  Based on drivers/serial/8250.c by Russell King.
+ *
+ *  Author:	Nicolas Pitre
+ *  Created:	Feb 20, 2003
+ *  Copyright:	(C) 2003 Monta Vista Software, Inc.
+ *
+ * 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.
+ *
+ * Note 1: This driver is made separate from the already too overloaded
+ * 8250.c because it needs some kirks of its own and that'll make it
+ * easier to add DMA support.
+ *
+ * Note 2: I'm too sick of device allocation policies for serial ports.
+ * If someone else wants to request an "official" allocation of major/minor
+ * for this driver please be my guest.  And don't forget that new hardware
+ * to come from Intel might have more than 3 or 4 of those UARTs.  Let's
+ * hope for a better port registration and dynamic device allocation scheme
+ * with the serial core maintainer satisfaction to appear soon.
+ */
+
+
+#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/wakelock.h>
+#include <linux/dmaengine.h>
+#include <linux/scatterlist.h>
+#include <mach/dma.h>
+#include <plat/pm.h>
+
+#ifdef CONFIG_PXA95x
+#include <mach/dvfm.h>
+#include <mach/pxa95x_dvfm.h>
+#endif
+
+#define	DMA_BLOCK	UART_XMIT_SIZE
+
+#define PXA_UART_TX	0
+#define PXA_UART_RX	1
+
+struct uart_pxa_port {
+	struct uart_port        port;
+	unsigned int            ier;
+	unsigned char           lcr;
+	unsigned int            mcr;
+	unsigned int            lsr_break_flag;
+	struct clk		*clk;
+	char			*name;
+
+	struct timer_list	pxa_timer;
+	struct work_struct	uart_tx_lpm_work;
+#ifdef CONFIG_PXA95x
+	int			dvfm_dev_idx[2];
+	struct notifier_block	notifier_freq_block;
+	struct work_struct	uart_rx_lpm_work;
+#else
+	struct wake_lock idle_lock[2];
+	struct pm_qos_request_list qos_idle[2];
+#endif
+	struct dma_chan *txdma;
+	struct dma_chan *rxdma;
+	struct dma_async_tx_descriptor *rx_desc;
+	struct dma_async_tx_descriptor *tx_desc;
+
+	void			*txdma_addr;
+	void			*rxdma_addr;
+	dma_addr_t		txdma_addr_phys;
+	dma_addr_t		rxdma_addr_phys;
+	int			dma_enable;
+	int			tx_stop;
+	int			rx_stop;
+	int			data_len;
+	int			txdrcmr;
+	int			rxdrcmr;
+	struct	tasklet_struct	tklet;
+
+	int tx_cnt;
+	struct scatterlist rxsg;
+	struct scatterlist txsg;
+	dma_cookie_t rx_cookie;
+	dma_cookie_t tx_cookie;
+
+#ifdef	CONFIG_PM
+	/* We needn't save rx dma register because we
+	 * just restart the dma totallly after resume
+	 */
+	void			*buf_save;
+	unsigned long		dcsr_tx;
+	unsigned long		dsadr_tx;
+	unsigned long		dtadr_tx;
+	unsigned long		dcmd_tx;
+#endif
+};
+
+static int uart_dma;
+
+static int __init uart_dma_setup(char *__unused)
+{
+	uart_dma = 1;
+	return 1;
+}
+__setup("uart_dma", uart_dma_setup);
+
+static void pxa_uart_transmit_dma(void *data);
+static void pxa_uart_receive_dma(void *data);
+static void pxa_uart_receive_dma_err(struct uart_pxa_port *up, int *status);
+static void pxa_uart_transmit_dma_start(struct uart_pxa_port *up, int count);
+static void pxa_uart_receive_dma_start(struct uart_pxa_port *up);
+static inline void wait_for_xmitr(struct uart_pxa_port *up);
+static inline void serial_out(struct uart_pxa_port *up, int offset, int value);
+
+static unsigned int serial_pxa_tx_empty(struct uart_port *port);
+
+#define PXA_TIMER_TIMEOUT (3*HZ)
+
+static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
+{
+	offset <<= 2;
+	return readl(up->port.membase + offset);
+}
+
+static inline void serial_out(struct uart_pxa_port *up, int offset, int value)
+{
+	offset <<= 2;
+	writel(value, up->port.membase + offset);
+}
+
+static void serial_pxa_enable_ms(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->dma_enable)
+		return;
+
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void serial_pxa_stop_tx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->dma_enable) {
+		up->tx_stop = 1;
+
+		if (up->ier & UART_IER_DMAE) {
+			while (dma_async_is_tx_complete(up->txdma,
+				up->tx_cookie, NULL, NULL) != DMA_SUCCESS)
+				rmb();
+		}
+	} else {
+		if (up->ier & UART_IER_THRI) {
+			up->ier &= ~UART_IER_THRI;
+			serial_out(up, UART_IER, up->ier);
+		}
+	}
+}
+
+static void serial_pxa_stop_rx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->dma_enable) {
+		if (up->ier & UART_IER_DMAE) {
+			dmaengine_terminate_all(up->rxdma);
+
+			while (dma_async_is_tx_complete(up->rxdma,
+				up->rx_cookie, NULL, NULL) != DMA_SUCCESS)
+				rmb();
+		}
+		up->rx_stop = 1;
+	} else {
+		up->ier &= ~UART_IER_RLSI;
+		up->port.read_status_mask &= ~UART_LSR_DR;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static inline void receive_chars(struct uart_pxa_port *up, int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int ch, flag;
+	int max_count = 256;
+
+	do {
+		ch = serial_in(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (*status & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (*status & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (*status & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ignored.
+			 */
+			*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+			if (up->port.line == up->port.cons->index) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+#endif
+			if (*status & UART_LSR_BI)
+				flag = TTY_BREAK;
+			else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+
+ignore_char:
+		*status = serial_in(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+	tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct uart_pxa_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_out(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		serial_pxa_stop_tx(&up->port);
+		return;
+	}
+
+	count = up->port.fifosize / 2;
+	do {
+		serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+
+	if (uart_circ_empty(xmit))
+		serial_pxa_stop_tx(&up->port);
+}
+
+static inline void
+dma_receive_chars(struct uart_pxa_port *up, int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned char ch;
+	int max_count = 256;
+	int count = 0;
+	unsigned char *tmp;
+	unsigned int flag = TTY_NORMAL;
+
+	dmaengine_terminate_all(up->rxdma);
+	count = pxa_dmac_chan_get_dst_ptr(up->rxdma) - up->rxdma_addr_phys;
+	tmp = up->rxdma_addr;
+
+	while (count > 0) {
+		if (!uart_handle_sysrq_char(&up->port, *tmp))
+			uart_insert_char(&up->port, *status, 0, *tmp, flag);
+		tmp++;
+		count--;
+	}
+
+	*status = serial_in(up, UART_LSR);
+	if (!(*status & UART_LSR_DR)) {
+		ch = serial_in(up, UART_RX);
+		goto done;
+	}
+
+	do {
+		ch = serial_in(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+					UART_LSR_FE | UART_LSR_OE))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char2;
+			} else if (*status & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (*status & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (*status & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ignored.
+			 */
+			*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+			if (up->port.line == up->port.cons->index) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+#endif
+			if (*status & UART_LSR_BI)
+				flag = TTY_BREAK;
+			else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+		if (!uart_handle_sysrq_char(&up->port, ch))
+			uart_insert_char(&up->port, *status, UART_LSR_OE,
+					 ch, flag);
+ignore_char2:
+		*status = serial_in(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+
+done:
+	tty_schedule_flip(tty);
+	if (up->rx_stop)
+		return;
+	pxa_uart_receive_dma_start(up);
+}
+
+static void serial_pxa_start_tx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->dma_enable) {
+		up->tx_stop = 0;
+		tasklet_schedule(&up->tklet);
+	} else {
+		if (!(up->ier & UART_IER_THRI)) {
+			up->ier |= UART_IER_THRI;
+			serial_out(up, UART_IER, up->ier);
+		}
+	}
+}
+
+static inline void check_modem_status(struct uart_pxa_port *up)
+{
+	int status;
+
+	status = serial_in(up, UART_MSR);
+
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+		return;
+
+	if (status & UART_MSR_TERI)
+		up->port.icount.rng++;
+	if (status & UART_MSR_DDSR)
+		up->port.icount.dsr++;
+	if (status & UART_MSR_DDCD)
+		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+	if (status & UART_MSR_DCTS)
+		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+
+	wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
+{
+	struct uart_pxa_port *up = dev_id;
+	unsigned int iir, lsr;
+
+	iir = serial_in(up, UART_IIR);
+	if (iir & UART_IIR_NO_INT)
+		return IRQ_NONE;
+
+	/* timer is not active */
+	if (!mod_timer(&up->pxa_timer, jiffies + PXA_TIMER_TIMEOUT)) {
+#ifdef CONFIG_PXA95x
+		dvfm_disable_lowpower(up->dvfm_dev_idx[PXA_UART_RX]);
+#else
+		pm_qos_update_request(&up->qos_idle[PXA_UART_RX],
+						PM_QOS_CONSTRAINT);
+		wake_lock(&up->idle_lock[PXA_UART_RX]);
+#endif
+	}
+
+	lsr = serial_in(up, UART_LSR);
+	if (up->dma_enable) {
+		if (UART_LSR_FIFOE & lsr)
+			pxa_uart_receive_dma_err(up, &lsr);
+
+		if (iir & UART_IIR_TOD)
+			dma_receive_chars(up, &lsr);
+	} else {
+		if (lsr & UART_LSR_DR)
+			receive_chars(up, &lsr);
+
+		check_modem_status(up);
+		if (lsr & UART_LSR_THRE) {
+			transmit_chars(up);
+			/* wait Tx empty */
+			while (!serial_pxa_tx_empty(	\
+						(struct uart_port *)dev_id))
+				;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int serial_pxa_tx_empty(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	if (up->dma_enable) {
+		if (up->ier & UART_IER_DMAE) {
+			if (dma_async_is_tx_complete(up->txdma,
+				up->tx_cookie, NULL, NULL) != DMA_SUCCESS) {
+				spin_unlock_irqrestore(&up->port.lock, flags);
+				return 0;
+			}
+		}
+	}
+
+	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret;
+}
+
+static unsigned int serial_pxa_get_mctrl(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned char status;
+	unsigned int ret;
+
+	status = serial_in(up, UART_MSR);
+
+	ret = 0;
+	if (status & UART_MSR_DCD)
+		ret |= TIOCM_CAR;
+	if (status & UART_MSR_RI)
+		ret |= TIOCM_RNG;
+	if (status & UART_MSR_DSR)
+		ret |= TIOCM_DSR;
+	if (status & UART_MSR_CTS)
+		ret |= TIOCM_CTS;
+
+	return ret;
+}
+
+static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned int mcr = 0;
+
+	if (mctrl & TIOCM_RTS)
+		mcr |= UART_MCR_RTS;
+	if (mctrl & TIOCM_DTR)
+		mcr |= UART_MCR_DTR;
+	if (mctrl & TIOCM_OUT1)
+		mcr |= UART_MCR_OUT1;
+	if (mctrl & TIOCM_OUT2)
+		mcr |= UART_MCR_OUT2;
+	if (mctrl & TIOCM_LOOP)
+		mcr |= UART_MCR_LOOP;
+
+	mcr |= up->mcr;
+
+	serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (break_state == -1)
+		up->lcr |= UART_LCR_SBC;
+	else
+		up->lcr &= ~UART_LCR_SBC;
+	serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void pxa_uart_transmit_dma_start(struct uart_pxa_port *up, int count)
+{
+	unsigned long flags;
+	struct scatterlist *sg;
+	struct pxa_dmac_data dmac_data;
+	struct dma_slave_config slave_config;
+	int ret;
+
+	slave_config.direction      = DMA_TO_DEVICE;
+	slave_config.dst_addr       = up->port.mapbase;
+	slave_config.dst_maxburst   = 8;
+	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+	dmac_data.flow_ctrl = true;
+	dmac_data.ch_map    = up->txdrcmr;
+	up->txdma->private = &dmac_data;
+
+	ret = dmaengine_slave_config(up->txdma, &slave_config);
+	if (ret) {
+		printk(KERN_ERR "%s: dmaengine slave config err.\n", __func__);
+		return;
+	}
+
+	sg = &up->txsg;
+	sg_init_table(sg, 1); /* Initialize SG table */
+	sg_set_buf(sg, phys_to_virt(up->txdma_addr_phys), count);
+	sg_dma_address(sg) = up->txdma_addr_phys;
+
+	up->tx_desc = up->txdma->device->device_prep_slave_sg(up->txdma,
+			sg, 1, DMA_TO_DEVICE, 0);
+
+	up->tx_desc->callback = pxa_uart_transmit_dma;
+	up->tx_desc->callback_param = up;
+	up->tx_cnt = count;
+
+	local_irq_save(flags);
+
+#ifdef CONFIG_PXA95x
+	dvfm_disable_lowpower(up->dvfm_dev_idx[PXA_UART_TX]);
+#else
+	pm_qos_update_request(&up->qos_idle[PXA_UART_TX],
+					PM_QOS_CONSTRAINT);
+	wake_lock(&up->idle_lock[PXA_UART_TX]);
+#endif
+
+	up->tx_cookie = up->tx_desc->tx_submit(up->tx_desc);
+	dma_async_issue_pending(up->txdma);
+
+	local_irq_restore(flags);
+}
+
+static void pxa_uart_receive_dma_start(struct uart_pxa_port *up)
+{
+	struct scatterlist *sg;
+	struct pxa_dmac_data dmac_data;
+	struct dma_slave_config slave_config;
+	int ret;
+
+	slave_config.direction      = DMA_FROM_DEVICE;
+	slave_config.src_addr       = up->port.mapbase;
+	slave_config.src_maxburst   = 8;
+	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+	dmac_data.flow_ctrl = true;
+	dmac_data.ch_map    = up->rxdrcmr;
+	up->rxdma->private = &dmac_data;
+
+	ret = dmaengine_slave_config(up->rxdma, &slave_config);
+	if (ret) {
+		printk(KERN_ERR "%s: dmaengine slave config err.\n", __func__);
+		return;
+	}
+
+	sg = &up->rxsg;
+	sg_init_table(sg, 1); /* Initialize SG table */
+	sg_set_buf(sg, phys_to_virt(up->rxdma_addr_phys), DMA_BLOCK);
+	sg_dma_address(sg) = up->rxdma_addr_phys;
+
+	up->rx_desc = up->rxdma->device->device_prep_slave_sg(up->rxdma,
+			sg, 1, DMA_FROM_DEVICE, 0);
+
+	up->rx_desc->callback = pxa_uart_receive_dma;
+	up->rx_desc->callback_param = up;
+
+	up->rx_cookie = up->rx_desc->tx_submit(up->rx_desc);
+	dma_async_issue_pending(up->rxdma);
+}
+
+static void pxa_uart_receive_dma_err(struct uart_pxa_port *up, int *status)
+{
+	unsigned char ch;
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned char *tmp;
+	int count;
+	unsigned int flag = 0;
+
+	dmaengine_terminate_all(up->rxdma);
+	count = pxa_dmac_chan_get_dst_ptr(up->rxdma) - up->rxdma_addr_phys;
+
+	/* if have DMA reqeust, wait. */
+	while (dma_async_is_tx_complete(up->rxdma,
+		up->rx_cookie, NULL, NULL) != DMA_SUCCESS)
+		rmb();
+
+	tmp = up->rxdma_addr;
+
+	tty_insert_flip_string(tty, tmp, count);
+	up->port.icount.rx += count;
+
+	do {
+		ch = serial_in(up, UART_RX);
+		up->port.icount.rx++;
+
+		/*
+		 * For statistics only
+		 */
+		if (*status & UART_LSR_BI) {
+			*status &= ~(UART_LSR_FE | UART_LSR_PE);
+
+			up->port.icount.brk++;
+			/*
+			 * We do the SysRQ and SAK checking
+			 * here because otherwise the break
+			 * may get masked by ignore_status_mask
+			 * or read_status_mask.
+			 */
+			if (uart_handle_break(&up->port))
+				goto ignore_char;
+			flag = TTY_BREAK;
+		} else if (*status & UART_LSR_PE) {
+			up->port.icount.parity++;
+			flag = TTY_PARITY;
+		} else if (*status & UART_LSR_FE) {
+			up->port.icount.frame++;
+			flag = TTY_FRAME;
+		}
+
+		if (*status & UART_LSR_OE)
+			up->port.icount.overrun++;
+
+		/*
+		 * Mask off conditions which should be ignored.
+		 */
+		*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+		if (up->port.line == up->port.cons->index) {
+			/* Recover the break flag from console xmit */
+			*status |= up->lsr_break_flag;
+			up->lsr_break_flag = 0;
+		}
+#endif
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+
+ignore_char:
+		*status = serial_in(up, UART_LSR);
+	} while (*status & UART_LSR_DR);
+
+	tty_flip_buffer_push(tty);
+	if (up->rx_stop)
+		return;
+	pxa_uart_receive_dma_start(up);
+}
+
+static void pxa_uart_receive_dma(void *data)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int count;
+	unsigned char *tmp = up->rxdma_addr;
+
+	dmaengine_terminate_all(up->rxdma);
+
+	count = pxa_dmac_chan_get_dst_ptr(up->rxdma) - up->rxdma_addr_phys;
+	tty_insert_flip_string(tty, tmp, count);
+	up->port.icount.rx += count;
+	tty_flip_buffer_push(tty);
+	if (up->rx_stop)
+		return;
+	pxa_uart_receive_dma_start(up);
+	return;
+}
+
+static void pxa_uart_transmit_dma(void *data)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
+	struct circ_buf *xmit = &up->port.state->xmit;
+
+	dmaengine_terminate_all(up->txdma);
+
+	/* if tx stop, stop transmit DMA and return */
+	if (up->tx_stop)
+		return;
+
+	if (up->port.x_char) {
+		serial_out(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (!uart_circ_empty(xmit))
+		tasklet_schedule(&up->tklet);
+	return;
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	if (!pxa_dmac_is_this_type(chan))
+		return false;
+
+	if (pxa_dmac_chan_get_prio(chan) != (pxa_dma_prio)param)
+		return false;
+
+	return true;
+}
+
+static void uart_pxa_dma_init(struct uart_pxa_port *up)
+{
+	dma_cap_mask_t mask;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SG, mask);
+
+	if (0 == up->rxdma) {
+		up->rxdma = dma_request_channel(mask, filter,
+					(void *)DMA_PRIO_LOW);
+		if (up->rxdma < 0)
+			goto out;
+
+		printk(KERN_ERR "%s: get rx dma channel %p\n", __func__,
+					up->rxdma);
+	}
+
+	if (0 == up->txdma) {
+		up->txdma = dma_request_channel(mask, filter,
+					(void *)DMA_PRIO_LOW);
+		if (up->txdma < 0)
+			goto err_txdma;
+
+		printk(KERN_ERR "%s: get tx dma channel %p\n", __func__,
+					up->txdma);
+	}
+
+	if (NULL == up->txdma_addr) {
+		up->txdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK,
+				&up->txdma_addr_phys, GFP_KERNEL);
+		if (!up->txdma_addr)
+			goto txdma_err_alloc;
+	}
+
+	if (NULL == up->rxdma_addr) {
+		up->rxdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK,
+				&up->rxdma_addr_phys, GFP_KERNEL);
+		if (!up->rxdma_addr)
+			goto rxdma_err_alloc;
+	}
+
+#ifdef CONFIG_PM
+	up->buf_save = kmalloc(DMA_BLOCK, GFP_KERNEL);
+	if (!up->buf_save)
+		goto buf_err_alloc;
+#endif
+
+	return;
+
+#ifdef CONFIG_PM
+buf_err_alloc:
+	dma_free_coherent(NULL, DMA_BLOCK, up->rxdma_addr,
+			up->rxdma_addr_phys);
+	up->rxdma_addr = NULL;
+#endif
+rxdma_err_alloc:
+	dma_free_coherent(NULL, DMA_BLOCK, up->txdma_addr,
+			up->txdma_addr_phys);
+	up->txdma_addr = NULL;
+txdma_err_alloc:
+	dma_release_channel(up->rxdma);
+	dma_release_channel(up->txdma);
+err_txdma:
+	dma_release_channel(up->rxdma);
+out:
+	return;
+}
+
+static void uart_pxa_dma_uninit(struct uart_pxa_port *up)
+{
+#ifdef CONFIG_PM
+	kfree(up->buf_save);
+#endif
+	dmaengine_terminate_all(up->rxdma);
+	dmaengine_terminate_all(up->txdma);
+
+	if (up->txdma_addr != NULL) {
+		dma_free_coherent(NULL, DMA_BLOCK, up->txdma_addr,
+				up->txdma_addr_phys);
+		up->txdma_addr = NULL;
+	}
+	if (up->txdma != 0)
+		dma_release_channel(up->txdma);
+
+	if (up->rxdma_addr != NULL) {
+		dma_free_coherent(NULL, DMA_BLOCK, up->rxdma_addr,
+				up->rxdma_addr_phys);
+		up->rxdma_addr = NULL;
+	}
+
+	if (up->rxdma != 0)
+		dma_release_channel(up->rxdma);
+
+	return;
+}
+
+static void uart_task_action(unsigned long data)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
+	struct circ_buf *xmit = &up->port.state->xmit;
+	unsigned char *tmp = up->txdma_addr;
+	unsigned long flags;
+	int count = 0, c;
+
+	/* if the tx is stop, just return.*/
+	if (up->tx_stop)
+		return;
+
+	if (dma_async_is_tx_complete(up->txdma, up->tx_cookie, NULL, NULL) !=
+	    DMA_SUCCESS)
+		return;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	while (1) {
+		c = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+		if (c <= 0)
+			break;
+
+		memcpy(tmp, xmit->buf + xmit->tail, c);
+		xmit->tail = (xmit->tail + c) & (UART_XMIT_SIZE - 1);
+		tmp += c;
+		count += c;
+		up->port.icount.tx += c;
+	}
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	tmp = up->txdma_addr;
+	up->tx_stop = 0;
+
+	pxa_uart_transmit_dma_start(up, count);
+}
+
+static int serial_pxa_startup(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+	int retval;
+
+	if (port->line == 3) /* HWUART */
+		up->mcr |= UART_MCR_AFE;
+	else
+		up->mcr = 0;
+
+	up->port.uartclk = clk_get_rate(up->clk);
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
+	if (retval)
+		return retval;
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl |= TIOCM_OUT2;
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Finally, enable interrupts.  Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 */
+	if (up->dma_enable) {
+		uart_pxa_dma_init(up);
+		up->rx_stop = 0;
+		pxa_uart_receive_dma_start(up);
+		up->ier = UART_IER_DMAE | UART_IER_UUE | UART_IER_RTOIE;
+		tasklet_init(&up->tklet, uart_task_action, (unsigned long)up);
+	} else {
+		up->ier = UART_IER_RLSI | UART_IER_RDI |
+			UART_IER_RTOIE | UART_IER_UUE;
+	}
+	serial_out(up, UART_IER, up->ier);
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	return 0;
+}
+
+static void serial_pxa_shutdown(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+
+	flush_work(&up->uart_tx_lpm_work);
+
+	free_irq(up->port.irq, up);
+
+	if (up->dma_enable) {
+		tasklet_kill(&up->tklet);
+		uart_pxa_dma_uninit(up);
+	}
+
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	serial_out(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+				  UART_FCR_CLEAR_RCVR |
+				  UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+}
+
+static void
+serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot = 0;
+	unsigned int dll;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cval = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		cval = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cval |= UART_LCR_STOP;
+	if (termios->c_cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(termios->c_cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, 921600*16*4/16);
+	if (baud > 921600) {
+		port->uartclk = 921600*16*4; /* 58.9823MHz as the clk src */
+		up->ier |= UART_IER_HSE;
+		if (B1500000 == (termios->c_cflag & B1500000))
+			quot = 2;
+		if (B3500000 == (termios->c_cflag & B3500000))
+			quot = 1;
+		if (quot == 0)
+			quot = uart_get_divisor(port, baud);
+	} else {
+		quot = uart_get_divisor(port, baud);
+		up->ier &= ~UART_IER_HSE;
+	}
+
+	if (up->dma_enable) {
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
+		fcr &= ~UART_FCR_PXA_BUS32;
+	} else {
+		if ((up->port.uartclk / quot) < (2400 * 16))
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1;
+		else if ((up->port.uartclk / quot) < (230400 * 16))
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8;
+		else
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
+	}
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Ensure the port will be enabled.
+	 * This is required especially for serial console.
+	 */
+	up->ier |= UART_IER_UUE;
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= UART_LSR_BI;
+
+	/*
+	 * Characters to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= UART_LSR_BI;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			up->port.ignore_status_mask |= UART_LSR_OE;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= UART_LSR_DR;
+
+	/*
+	 * CTS flow control flag and modem status interrupts
+	 */
+	if (up->dma_enable) {
+		if (termios->c_cflag & CRTSCTS)
+			up->mcr |= UART_MCR_AFE;
+		else
+			up->mcr &= UART_MCR_AFE;
+	} else {
+		up->ier &= ~UART_IER_MSI;
+		if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+			up->ier |= UART_IER_MSI;
+	}
+
+	serial_out(up, UART_IER, up->ier);
+
+	if (termios->c_cflag & CRTSCTS)
+		up->mcr |= UART_MCR_AFE;
+	else
+		up->mcr &= ~UART_MCR_AFE;
+
+	serial_out(up, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
+	serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+
+	/*
+	 * work around Errata #75 according to Intel(R) PXA27x Processor Family
+	 * Specification Update (Nov 2005)
+	 */
+	dll = serial_in(up, UART_DLL);
+	WARN_ON(dll != (quot & 0xff));
+
+	serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_out(up, UART_LCR, cval);			/* reset DLAB */
+	up->lcr = cval;					/* Save LCR */
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	serial_out(up, UART_FCR, fcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_pxa_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (!state)
+		clk_enable(up->clk);
+	else
+		clk_disable(up->clk);
+}
+
+static void serial_pxa_release_port(struct uart_port *port)
+{
+}
+
+static int serial_pxa_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_pxa_config_port(struct uart_port *port, int flags)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	up->port.type = PORT_PXA;
+}
+
+static int
+serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* we don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+static const char *
+serial_pxa_type(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	return up->name;
+}
+
+static struct uart_pxa_port *serial_pxa_ports[4];
+static struct uart_driver serial_pxa_reg;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_pxa_port *up)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = serial_in(up, UART_LSR);
+
+		if (status & UART_LSR_BI)
+			up->lsr_break_flag = UART_LSR_BI;
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout &&
+		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+			udelay(1);
+	}
+}
+
+static void serial_pxa_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	wait_for_xmitr(up);
+	serial_out(up, UART_TX, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_pxa_port *up = serial_pxa_ports[co->index];
+	unsigned int ier;
+	unsigned long flags;
+	int locked = 1;
+
+	local_irq_save(flags);
+	if (up->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress)
+		locked = spin_trylock(&up->port.lock);
+	else
+		spin_lock(&up->port.lock);
+
+	clk_enable(up->clk);
+
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = serial_in(up, UART_IER);
+	serial_out(up, UART_IER, UART_IER_UUE);
+
+	uart_console_write(&up->port, s, count, serial_pxa_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up);
+	serial_out(up, UART_IER, ier);
+
+	clk_disable(up->clk);
+
+	if (locked)
+		spin_unlock(&up->port.lock);
+	local_irq_restore(flags);
+}
+
+static int __init
+serial_pxa_console_setup(struct console *co, char *options)
+{
+	struct uart_pxa_port *up;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index == -1 || co->index >= serial_pxa_reg.nr)
+		co->index = 0;
+	up = serial_pxa_ports[co->index];
+	if (!up)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static struct console serial_pxa_console = {
+	.name		= "ttyS",
+	.write		= serial_pxa_console_write,
+	.device		= uart_console_device,
+	.setup		= serial_pxa_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial_pxa_reg,
+};
+
+#define PXA_CONSOLE (&serial_pxa_console)
+#else
+#define PXA_CONSOLE NULL
+#endif
+
+struct uart_ops serial_pxa_pops = {
+	.tx_empty	= serial_pxa_tx_empty,
+	.set_mctrl	= serial_pxa_set_mctrl,
+	.get_mctrl	= serial_pxa_get_mctrl,
+	.stop_tx	= serial_pxa_stop_tx,
+	.start_tx	= serial_pxa_start_tx,
+	.stop_rx	= serial_pxa_stop_rx,
+	.enable_ms	= serial_pxa_enable_ms,
+	.break_ctl	= serial_pxa_break_ctl,
+	.startup	= serial_pxa_startup,
+	.shutdown	= serial_pxa_shutdown,
+	.set_termios	= serial_pxa_set_termios,
+	.pm		= serial_pxa_pm,
+	.type		= serial_pxa_type,
+	.release_port	= serial_pxa_release_port,
+	.request_port	= serial_pxa_request_port,
+	.config_port	= serial_pxa_config_port,
+	.verify_port	= serial_pxa_verify_port,
+};
+
+static struct uart_driver serial_pxa_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "PXA serial",
+	.dev_name	= "ttyS",
+	.major		= TTY_MAJOR,
+	.minor		= 64,
+	.nr		= 4,
+	.cons		= PXA_CONSOLE,
+};
+
+#ifdef CONFIG_PM
+static int serial_pxa_suspend(struct device *dev)
+{
+	struct uart_pxa_port *sport = dev_get_drvdata(dev);
+
+	if (sport && (sport->ier & UART_IER_DMAE)) {
+		int length = 0, sent = 0;
+		unsigned long flags;
+
+		local_irq_save(flags);
+		sport->tx_stop = 1;
+		sport->rx_stop = 1;
+		sport->data_len = 0;
+		if (dma_async_is_tx_complete(sport->txdma, sport->tx_cookie,
+					NULL, NULL) != DMA_SUCCESS) {
+			dmaengine_terminate_all(sport->txdma);
+			length = sport->tx_cnt;
+			sent = pxa_dmac_chan_get_src_ptr(sport->txdma) -
+							sport->txdma_addr_phys;
+			memcpy(sport->buf_save, sport->txdma_addr
+				 + sent, length);
+			sport->data_len = length;
+
+		}
+
+		if (dma_async_is_tx_complete(sport->rxdma, sport->rx_cookie,
+					NULL, NULL) != DMA_SUCCESS)
+			dmaengine_terminate_all(sport->rxdma);
+
+		pxa_uart_receive_dma(sport);
+
+		local_irq_restore(flags);
+	}
+
+	if (sport)
+		uart_suspend_port(&serial_pxa_reg, &sport->port);
+
+	return 0;
+}
+
+static int serial_pxa_resume(struct device *dev)
+{
+	struct uart_pxa_port *sport = dev_get_drvdata(dev);
+
+	if (sport)
+		uart_resume_port(&serial_pxa_reg, &sport->port);
+
+	if (sport && (sport->ier & UART_IER_DMAE)) {
+		if (sport->data_len > 0) {
+			memcpy(sport->txdma_addr, sport->buf_save,
+					sport->data_len);
+			pxa_uart_transmit_dma_start(sport,
+					sport->data_len);
+		} else
+			tasklet_schedule(&sport->tklet);
+
+		pxa_uart_receive_dma_start(sport);
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops serial_pxa_pm_ops = {
+	.suspend	= serial_pxa_suspend,
+	.resume		= serial_pxa_resume,
+};
+#endif
+
+static void pxa_timer_handler(unsigned long data)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
+#ifdef CONFIG_PXA95x
+	dvfm_enable_lowpower(up->dvfm_dev_idx[PXA_UART_RX]);
+#else
+	pm_qos_update_request(&up->qos_idle[PXA_UART_RX], PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&up->idle_lock[PXA_UART_RX]);
+#endif
+}
+
+static void uart_tx_lpm_handler(struct work_struct *work)
+{
+	struct uart_pxa_port *up =
+	    container_of(work, struct uart_pxa_port, uart_tx_lpm_work);
+
+	/* Polling until TX FIFO is empty */
+	while (!(serial_in(up, UART_LSR) & UART_LSR_TEMT))
+		msleep(1);
+#ifdef CONFIG_PXA95x
+	dvfm_enable_lowpower(up->dvfm_dev_idx[PXA_UART_TX]);
+#else
+	pm_qos_update_request(&up->qos_idle[PXA_UART_TX], PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&up->idle_lock[PXA_UART_TX]);
+#endif
+}
+
+#ifdef CONFIG_PXA95x
+
+extern void get_wakeup_source(pm_wakeup_src_t *);
+
+static void uart_rx_lpm_handler(struct work_struct *work)
+{
+	struct uart_pxa_port *up =
+	    container_of(work, struct uart_pxa_port, uart_rx_lpm_work);
+
+	mod_timer(&up->pxa_timer, jiffies + PXA_TIMER_TIMEOUT);
+	dvfm_disable_lowpower(up->dvfm_dev_idx[PXA_UART_RX]);
+}
+
+static int uart_notifier_freq(struct notifier_block *nb,
+				unsigned long val, void *data)
+{
+	struct dvfm_freqs *freqs = (struct dvfm_freqs *)data;
+	struct op_info *new = NULL;
+	struct dvfm_md_opt *opt;
+	pm_wakeup_src_t src;
+	struct uart_pxa_port *sport;
+
+	if (freqs)
+		new = &freqs->new_info;
+	else
+		return 0;
+
+	sport = container_of(nb, struct uart_pxa_port, notifier_freq_block);
+	if (val != DVFM_FREQ_POSTCHANGE)
+		return 0;
+
+	opt = new->op;
+	if ((opt->power_mode != POWER_MODE_D1) &&
+	    (opt->power_mode != POWER_MODE_D2) &&
+	    (opt->power_mode != POWER_MODE_CG))
+		return 0;
+
+	get_wakeup_source(&src);
+
+	if ((src.bits.uart1 && sport->port.irq == IRQ_FFUART) ||
+	    (src.bits.uart2 && sport->port.irq == IRQ_STUART)) {
+		schedule_work(&sport->uart_rx_lpm_work);
+	}
+
+	return 0;
+}
+#endif
+
+static int serial_pxa_probe(struct platform_device *dev)
+{
+	struct uart_pxa_port *sport;
+	struct resource *mmres, *irqres, *dmares;
+	int ret, i;
+	char dev_name[30];
+
+	mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+	if (!mmres || !irqres)
+		return -ENODEV;
+
+	sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
+	if (!sport)
+		return -ENOMEM;
+
+	sport->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(sport->clk)) {
+		ret = PTR_ERR(sport->clk);
+		goto err_free;
+	}
+
+	sport->port.type = PORT_PXA;
+	sport->port.iotype = UPIO_MEM;
+	sport->port.mapbase = mmres->start;
+	sport->port.irq = irqres->start;
+	sport->port.fifosize = 64;
+	sport->port.ops = &serial_pxa_pops;
+	sport->port.line = dev->id;
+	sport->port.dev = &dev->dev;
+	sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+	sport->port.uartclk = clk_get_rate(sport->clk);
+
+	switch (dev->id) {
+	case 0:
+		sport->name = "FFUART";
+		break;
+	case 1:
+		sport->name = "BTUART";
+		break;
+	case 2:
+		sport->name = "STUART";
+		break;
+	case 3:
+		sport->name = "HWUART";
+		break;
+	default:
+		sport->name = "???";
+		break;
+	}
+
+	sport->rxdrcmr = 0;
+	sport->txdrcmr = 0;
+	sport->txdma = 0;
+	sport->rxdma = 0;
+	sport->txdma_addr = NULL;
+	sport->rxdma_addr = NULL;
+	sport->dma_enable = 0;
+
+	if (uart_dma) {
+		/* Get Rx DMA mapping register */
+		dmares = platform_get_resource(dev, IORESOURCE_DMA, 0);
+		if (dmares)
+			sport->rxdrcmr = dmares->start;
+
+		/* Get Tx DMA mapping register */
+		dmares = platform_get_resource(dev, IORESOURCE_DMA, 1);
+		if (dmares)
+			sport->txdrcmr = dmares->start;
+
+		if (sport->rxdrcmr && sport->txdrcmr)
+			sport->dma_enable = 1;
+	}
+
+	for (i = 0; i < 2; i++) {
+		sprintf(dev_name, "%s.%s", sport->name,
+				(i == PXA_UART_RX) ? "rx" : "tx");
+
+#ifdef CONFIG_PXA95x
+		sport->dvfm_dev_idx[i] = -1;
+		dvfm_register(dev_name, &(sport->dvfm_dev_idx[i]));
+#else
+		pm_qos_add_request(&sport->qos_idle[i], PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+		wake_lock_init(&sport->idle_lock[i], WAKE_LOCK_IDLE,
+				(const char *)dev_name);
+#endif
+	}
+
+#ifdef CONFIG_PXA95x
+	sport->notifier_freq_block.notifier_call = uart_notifier_freq;
+	dvfm_register_notifier(&sport->notifier_freq_block,
+				DVFM_FREQUENCY_NOTIFIER);
+	INIT_WORK(&sport->uart_rx_lpm_work, uart_rx_lpm_handler);
+#endif
+	INIT_WORK(&sport->uart_tx_lpm_work, uart_tx_lpm_handler);
+
+	init_timer(&sport->pxa_timer);
+	sport->pxa_timer.function = pxa_timer_handler;
+	sport->pxa_timer.data = (long)sport;
+
+	sport->port.membase = ioremap(mmres->start,
+			mmres->end - mmres->start + 1);
+	if (!sport->port.membase) {
+		ret = -ENOMEM;
+		goto err_clk;
+	}
+
+	serial_pxa_ports[dev->id] = sport;
+
+	uart_add_one_port(&serial_pxa_reg, &sport->port);
+	platform_set_drvdata(dev, sport);
+
+	return 0;
+
+ err_clk:
+#ifdef CONFIG_PXA95x
+	dvfm_unregister(sport->name, &(sport->dvfm_dev_idx[PXA_UART_RX]));
+	dvfm_unregister(sport->name, &(sport->dvfm_dev_idx[PXA_UART_TX]));
+	dvfm_unregister_notifier(&sport->notifier_freq_block,
+			DVFM_FREQUENCY_NOTIFIER);
+#else
+	pm_qos_remove_request(&sport->qos_idle[PXA_UART_RX]);
+	pm_qos_remove_request(&sport->qos_idle[PXA_UART_TX]);
+	wake_lock_destroy(&sport->idle_lock[PXA_UART_RX]);
+	wake_lock_destroy(&sport->idle_lock[PXA_UART_TX]);
+#endif
+	clk_put(sport->clk);
+ err_free:
+	kfree(sport);
+	return ret;
+}
+
+static int serial_pxa_remove(struct platform_device *dev)
+{
+	struct uart_pxa_port *sport = platform_get_drvdata(dev);
+
+#ifdef CONFIG_PXA95x
+	dvfm_unregister(sport->name, &(sport->dvfm_dev_idx[PXA_UART_RX]));
+	dvfm_unregister(sport->name, &(sport->dvfm_dev_idx[PXA_UART_TX]));
+	dvfm_unregister_notifier(&sport->notifier_freq_block,
+			DVFM_FREQUENCY_NOTIFIER);
+#else
+	pm_qos_remove_request(&sport->qos_idle[PXA_UART_RX]);
+	pm_qos_remove_request(&sport->qos_idle[PXA_UART_TX]);
+	wake_lock_destroy(&sport->idle_lock[PXA_UART_RX]);
+	wake_lock_destroy(&sport->idle_lock[PXA_UART_TX]);
+#endif
+
+	platform_set_drvdata(dev, NULL);
+
+	uart_remove_one_port(&serial_pxa_reg, &sport->port);
+	clk_put(sport->clk);
+	kfree(sport);
+	serial_pxa_ports[dev->id] = NULL;
+
+	return 0;
+}
+
+static struct platform_driver serial_pxa_driver = {
+	.probe          = serial_pxa_probe,
+	.remove         = serial_pxa_remove,
+
+	.driver		= {
+		.name	= "pxa2xx-uart",
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &serial_pxa_pm_ops,
+#endif
+	},
+};
+
+int __init serial_pxa_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&serial_pxa_reg);
+	if (ret != 0)
+		return ret;
+
+	ret = platform_driver_register(&serial_pxa_driver);
+	if (ret != 0)
+		uart_unregister_driver(&serial_pxa_reg);
+
+	return ret;
+}
+
+void __exit serial_pxa_exit(void)
+{
+	platform_driver_unregister(&serial_pxa_driver);
+	uart_unregister_driver(&serial_pxa_reg);
+}
+
+module_init(serial_pxa_init);
+module_exit(serial_pxa_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-uart");
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 08/13] DMAC:tty serial pxa use generic dma engine APIs
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.tty serial device use dma engine APIs

Change-Id: Ie425e6cb9321914e84a7e53b8a2d57d957dce42c
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/configs/mmp2_defconfig |    2 +-
 arch/arm/configs/mmp3_defconfig |    2 +-
 drivers/tty/serial/Kconfig      |   12 +-
 drivers/tty/serial/Makefile     |    1 +
 drivers/tty/serial/mmp_pxa.c    | 1698 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 1712 insertions(+), 3 deletions(-)
 create mode 100644 drivers/tty/serial/mmp_pxa.c

diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index 7000d6c..138c95a 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -967,7 +967,7 @@ CONFIG_DEVKMEM=y
 #
 # Non-8250 serial port support
 #
-CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_MMP_PXA=y
 CONFIG_SERIAL_PXA_CONSOLE=y
 CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
diff --git a/arch/arm/configs/mmp3_defconfig b/arch/arm/configs/mmp3_defconfig
index 53a9684..4f8b160 100644
--- a/arch/arm/configs/mmp3_defconfig
+++ b/arch/arm/configs/mmp3_defconfig
@@ -1032,7 +1032,7 @@ CONFIG_DEVKMEM=y
 #
 # CONFIG_SERIAL_MAX3100 is not set
 # CONFIG_SERIAL_MAX3107 is not set
-CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_MMP_PXA=y
 CONFIG_SERIAL_PXA_CONSOLE=y
 CONFIG_SERIAL_CORE=y
 CONFIG_SERIAL_CORE_CONSOLE=y
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index b3692e6..70114c5 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -668,9 +668,19 @@ config SERIAL_PXA
 	  If you have a machine based on an Intel XScale PXA2xx CPU you
 	  can enable its onboard serial ports by enabling this option.
 
+config SERIAL_MMP_PXA
+	bool "MMP PXA serial port support"
+	depends on ARCH_PXA || ARCH_MMP
+	select SERIAL_CORE
+	help
+	  If you have a machine based on an Intel XScale PXA2xx CPU you
+	  can enable its onboard serial ports by enabling this option.
+	  mmp_pxa is a temporary config for mmp2 and mmp3,separate it from
+	  pxa.c for pxa910 and MG1.
+
 config SERIAL_PXA_CONSOLE
 	bool "Console on PXA serial port"
-	depends on SERIAL_PXA
+	depends on SERIAL_PXA || SERIAL_MMP_PXA
 	select SERIAL_CORE_CONSOLE
 	help
 	  If you have enabled the serial port on the Intel XScale PXA
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index cb2628f..bc6c1d3 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
 obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
 obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
 obj-$(CONFIG_SERIAL_PXA) += pxa.o
+obj-$(CONFIG_SERIAL_MMP_PXA) += mmp_pxa.o
 obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
 obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
 obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o
diff --git a/drivers/tty/serial/mmp_pxa.c b/drivers/tty/serial/mmp_pxa.c
new file mode 100644
index 0000000..68e5b3b
--- /dev/null
+++ b/drivers/tty/serial/mmp_pxa.c
@@ -0,0 +1,1698 @@
+/*
+ *  Based on drivers/serial/8250.c by Russell King.
+ *
+ *  Author:	Nicolas Pitre
+ *  Created:	Feb 20, 2003
+ *  Copyright:	(C) 2003 Monta Vista Software, Inc.
+ *
+ * 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.
+ *
+ * Note 1: This driver is made separate from the already too overloaded
+ * 8250.c because it needs some kirks of its own and that'll make it
+ * easier to add DMA support.
+ *
+ * Note 2: I'm too sick of device allocation policies for serial ports.
+ * If someone else wants to request an "official" allocation of major/minor
+ * for this driver please be my guest.  And don't forget that new hardware
+ * to come from Intel might have more than 3 or 4 of those UARTs.  Let's
+ * hope for a better port registration and dynamic device allocation scheme
+ * with the serial core maintainer satisfaction to appear soon.
+ */
+
+
+#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/wakelock.h>
+#include <linux/dmaengine.h>
+#include <linux/scatterlist.h>
+#include <mach/dma.h>
+#include <plat/pm.h>
+
+#ifdef CONFIG_PXA95x
+#include <mach/dvfm.h>
+#include <mach/pxa95x_dvfm.h>
+#endif
+
+#define	DMA_BLOCK	UART_XMIT_SIZE
+
+#define PXA_UART_TX	0
+#define PXA_UART_RX	1
+
+struct uart_pxa_port {
+	struct uart_port        port;
+	unsigned int            ier;
+	unsigned char           lcr;
+	unsigned int            mcr;
+	unsigned int            lsr_break_flag;
+	struct clk		*clk;
+	char			*name;
+
+	struct timer_list	pxa_timer;
+	struct work_struct	uart_tx_lpm_work;
+#ifdef CONFIG_PXA95x
+	int			dvfm_dev_idx[2];
+	struct notifier_block	notifier_freq_block;
+	struct work_struct	uart_rx_lpm_work;
+#else
+	struct wake_lock idle_lock[2];
+	struct pm_qos_request_list qos_idle[2];
+#endif
+	struct dma_chan *txdma;
+	struct dma_chan *rxdma;
+	struct dma_async_tx_descriptor *rx_desc;
+	struct dma_async_tx_descriptor *tx_desc;
+
+	void			*txdma_addr;
+	void			*rxdma_addr;
+	dma_addr_t		txdma_addr_phys;
+	dma_addr_t		rxdma_addr_phys;
+	int			dma_enable;
+	int			tx_stop;
+	int			rx_stop;
+	int			data_len;
+	int			txdrcmr;
+	int			rxdrcmr;
+	struct	tasklet_struct	tklet;
+
+	int tx_cnt;
+	struct scatterlist rxsg;
+	struct scatterlist txsg;
+	dma_cookie_t rx_cookie;
+	dma_cookie_t tx_cookie;
+
+#ifdef	CONFIG_PM
+	/* We needn't save rx dma register because we
+	 * just restart the dma totallly after resume
+	 */
+	void			*buf_save;
+	unsigned long		dcsr_tx;
+	unsigned long		dsadr_tx;
+	unsigned long		dtadr_tx;
+	unsigned long		dcmd_tx;
+#endif
+};
+
+static int uart_dma;
+
+static int __init uart_dma_setup(char *__unused)
+{
+	uart_dma = 1;
+	return 1;
+}
+__setup("uart_dma", uart_dma_setup);
+
+static void pxa_uart_transmit_dma(void *data);
+static void pxa_uart_receive_dma(void *data);
+static void pxa_uart_receive_dma_err(struct uart_pxa_port *up, int *status);
+static void pxa_uart_transmit_dma_start(struct uart_pxa_port *up, int count);
+static void pxa_uart_receive_dma_start(struct uart_pxa_port *up);
+static inline void wait_for_xmitr(struct uart_pxa_port *up);
+static inline void serial_out(struct uart_pxa_port *up, int offset, int value);
+
+static unsigned int serial_pxa_tx_empty(struct uart_port *port);
+
+#define PXA_TIMER_TIMEOUT (3*HZ)
+
+static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
+{
+	offset <<= 2;
+	return readl(up->port.membase + offset);
+}
+
+static inline void serial_out(struct uart_pxa_port *up, int offset, int value)
+{
+	offset <<= 2;
+	writel(value, up->port.membase + offset);
+}
+
+static void serial_pxa_enable_ms(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->dma_enable)
+		return;
+
+	up->ier |= UART_IER_MSI;
+	serial_out(up, UART_IER, up->ier);
+}
+
+static void serial_pxa_stop_tx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->dma_enable) {
+		up->tx_stop = 1;
+
+		if (up->ier & UART_IER_DMAE) {
+			while (dma_async_is_tx_complete(up->txdma,
+				up->tx_cookie, NULL, NULL) != DMA_SUCCESS)
+				rmb();
+		}
+	} else {
+		if (up->ier & UART_IER_THRI) {
+			up->ier &= ~UART_IER_THRI;
+			serial_out(up, UART_IER, up->ier);
+		}
+	}
+}
+
+static void serial_pxa_stop_rx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->dma_enable) {
+		if (up->ier & UART_IER_DMAE) {
+			dmaengine_terminate_all(up->rxdma);
+
+			while (dma_async_is_tx_complete(up->rxdma,
+				up->rx_cookie, NULL, NULL) != DMA_SUCCESS)
+				rmb();
+		}
+		up->rx_stop = 1;
+	} else {
+		up->ier &= ~UART_IER_RLSI;
+		up->port.read_status_mask &= ~UART_LSR_DR;
+		serial_out(up, UART_IER, up->ier);
+	}
+}
+
+static inline void receive_chars(struct uart_pxa_port *up, int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int ch, flag;
+	int max_count = 256;
+
+	do {
+		ch = serial_in(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+				       UART_LSR_FE | UART_LSR_OE))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char;
+			} else if (*status & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (*status & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (*status & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ignored.
+			 */
+			*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+			if (up->port.line == up->port.cons->index) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+#endif
+			if (*status & UART_LSR_BI)
+				flag = TTY_BREAK;
+			else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+
+ignore_char:
+		*status = serial_in(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+	tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct uart_pxa_port *up)
+{
+	struct circ_buf *xmit = &up->port.state->xmit;
+	int count;
+
+	if (up->port.x_char) {
+		serial_out(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+		return;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+		serial_pxa_stop_tx(&up->port);
+		return;
+	}
+
+	count = up->port.fifosize / 2;
+	do {
+		serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		up->port.icount.tx++;
+		if (uart_circ_empty(xmit))
+			break;
+	} while (--count > 0);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+
+	if (uart_circ_empty(xmit))
+		serial_pxa_stop_tx(&up->port);
+}
+
+static inline void
+dma_receive_chars(struct uart_pxa_port *up, int *status)
+{
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned char ch;
+	int max_count = 256;
+	int count = 0;
+	unsigned char *tmp;
+	unsigned int flag = TTY_NORMAL;
+
+	dmaengine_terminate_all(up->rxdma);
+	count = pxa_dmac_chan_get_dst_ptr(up->rxdma) - up->rxdma_addr_phys;
+	tmp = up->rxdma_addr;
+
+	while (count > 0) {
+		if (!uart_handle_sysrq_char(&up->port, *tmp))
+			uart_insert_char(&up->port, *status, 0, *tmp, flag);
+		tmp++;
+		count--;
+	}
+
+	*status = serial_in(up, UART_LSR);
+	if (!(*status & UART_LSR_DR)) {
+		ch = serial_in(up, UART_RX);
+		goto done;
+	}
+
+	do {
+		ch = serial_in(up, UART_RX);
+		flag = TTY_NORMAL;
+		up->port.icount.rx++;
+
+		if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+					UART_LSR_FE | UART_LSR_OE))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				up->port.icount.brk++;
+				/*
+				 * We do the SysRQ and SAK checking
+				 * here because otherwise the break
+				 * may get masked by ignore_status_mask
+				 * or read_status_mask.
+				 */
+				if (uart_handle_break(&up->port))
+					goto ignore_char2;
+			} else if (*status & UART_LSR_PE)
+				up->port.icount.parity++;
+			else if (*status & UART_LSR_FE)
+				up->port.icount.frame++;
+			if (*status & UART_LSR_OE)
+				up->port.icount.overrun++;
+
+			/*
+			 * Mask off conditions which should be ignored.
+			 */
+			*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+			if (up->port.line == up->port.cons->index) {
+				/* Recover the break flag from console xmit */
+				*status |= up->lsr_break_flag;
+				up->lsr_break_flag = 0;
+			}
+#endif
+			if (*status & UART_LSR_BI)
+				flag = TTY_BREAK;
+			else if (*status & UART_LSR_PE)
+				flag = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				flag = TTY_FRAME;
+		}
+		if (!uart_handle_sysrq_char(&up->port, ch))
+			uart_insert_char(&up->port, *status, UART_LSR_OE,
+					 ch, flag);
+ignore_char2:
+		*status = serial_in(up, UART_LSR);
+	} while ((*status & UART_LSR_DR) && (max_count-- > 0));
+
+done:
+	tty_schedule_flip(tty);
+	if (up->rx_stop)
+		return;
+	pxa_uart_receive_dma_start(up);
+}
+
+static void serial_pxa_start_tx(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (up->dma_enable) {
+		up->tx_stop = 0;
+		tasklet_schedule(&up->tklet);
+	} else {
+		if (!(up->ier & UART_IER_THRI)) {
+			up->ier |= UART_IER_THRI;
+			serial_out(up, UART_IER, up->ier);
+		}
+	}
+}
+
+static inline void check_modem_status(struct uart_pxa_port *up)
+{
+	int status;
+
+	status = serial_in(up, UART_MSR);
+
+	if ((status & UART_MSR_ANY_DELTA) == 0)
+		return;
+
+	if (status & UART_MSR_TERI)
+		up->port.icount.rng++;
+	if (status & UART_MSR_DDSR)
+		up->port.icount.dsr++;
+	if (status & UART_MSR_DDCD)
+		uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+	if (status & UART_MSR_DCTS)
+		uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+
+	wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline irqreturn_t serial_pxa_irq(int irq, void *dev_id)
+{
+	struct uart_pxa_port *up = dev_id;
+	unsigned int iir, lsr;
+
+	iir = serial_in(up, UART_IIR);
+	if (iir & UART_IIR_NO_INT)
+		return IRQ_NONE;
+
+	/* timer is not active */
+	if (!mod_timer(&up->pxa_timer, jiffies + PXA_TIMER_TIMEOUT)) {
+#ifdef CONFIG_PXA95x
+		dvfm_disable_lowpower(up->dvfm_dev_idx[PXA_UART_RX]);
+#else
+		pm_qos_update_request(&up->qos_idle[PXA_UART_RX],
+						PM_QOS_CONSTRAINT);
+		wake_lock(&up->idle_lock[PXA_UART_RX]);
+#endif
+	}
+
+	lsr = serial_in(up, UART_LSR);
+	if (up->dma_enable) {
+		if (UART_LSR_FIFOE & lsr)
+			pxa_uart_receive_dma_err(up, &lsr);
+
+		if (iir & UART_IIR_TOD)
+			dma_receive_chars(up, &lsr);
+	} else {
+		if (lsr & UART_LSR_DR)
+			receive_chars(up, &lsr);
+
+		check_modem_status(up);
+		if (lsr & UART_LSR_THRE) {
+			transmit_chars(up);
+			/* wait Tx empty */
+			while (!serial_pxa_tx_empty(	\
+						(struct uart_port *)dev_id))
+				;
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static unsigned int serial_pxa_tx_empty(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+	unsigned int ret;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	if (up->dma_enable) {
+		if (up->ier & UART_IER_DMAE) {
+			if (dma_async_is_tx_complete(up->txdma,
+				up->tx_cookie, NULL, NULL) != DMA_SUCCESS) {
+				spin_unlock_irqrestore(&up->port.lock, flags);
+				return 0;
+			}
+		}
+	}
+
+	ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	return ret;
+}
+
+static unsigned int serial_pxa_get_mctrl(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned char status;
+	unsigned int ret;
+
+	status = serial_in(up, UART_MSR);
+
+	ret = 0;
+	if (status & UART_MSR_DCD)
+		ret |= TIOCM_CAR;
+	if (status & UART_MSR_RI)
+		ret |= TIOCM_RNG;
+	if (status & UART_MSR_DSR)
+		ret |= TIOCM_DSR;
+	if (status & UART_MSR_CTS)
+		ret |= TIOCM_CTS;
+
+	return ret;
+}
+
+static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned int mcr = 0;
+
+	if (mctrl & TIOCM_RTS)
+		mcr |= UART_MCR_RTS;
+	if (mctrl & TIOCM_DTR)
+		mcr |= UART_MCR_DTR;
+	if (mctrl & TIOCM_OUT1)
+		mcr |= UART_MCR_OUT1;
+	if (mctrl & TIOCM_OUT2)
+		mcr |= UART_MCR_OUT2;
+	if (mctrl & TIOCM_LOOP)
+		mcr |= UART_MCR_LOOP;
+
+	mcr |= up->mcr;
+
+	serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	if (break_state == -1)
+		up->lcr |= UART_LCR_SBC;
+	else
+		up->lcr &= ~UART_LCR_SBC;
+	serial_out(up, UART_LCR, up->lcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void pxa_uart_transmit_dma_start(struct uart_pxa_port *up, int count)
+{
+	unsigned long flags;
+	struct scatterlist *sg;
+	struct pxa_dmac_data dmac_data;
+	struct dma_slave_config slave_config;
+	int ret;
+
+	slave_config.direction      = DMA_TO_DEVICE;
+	slave_config.dst_addr       = up->port.mapbase;
+	slave_config.dst_maxburst   = 8;
+	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+	dmac_data.flow_ctrl = true;
+	dmac_data.ch_map    = up->txdrcmr;
+	up->txdma->private = &dmac_data;
+
+	ret = dmaengine_slave_config(up->txdma, &slave_config);
+	if (ret) {
+		printk(KERN_ERR "%s: dmaengine slave config err.\n", __func__);
+		return;
+	}
+
+	sg = &up->txsg;
+	sg_init_table(sg, 1); /* Initialize SG table */
+	sg_set_buf(sg, phys_to_virt(up->txdma_addr_phys), count);
+	sg_dma_address(sg) = up->txdma_addr_phys;
+
+	up->tx_desc = up->txdma->device->device_prep_slave_sg(up->txdma,
+			sg, 1, DMA_TO_DEVICE, 0);
+
+	up->tx_desc->callback = pxa_uart_transmit_dma;
+	up->tx_desc->callback_param = up;
+	up->tx_cnt = count;
+
+	local_irq_save(flags);
+
+#ifdef CONFIG_PXA95x
+	dvfm_disable_lowpower(up->dvfm_dev_idx[PXA_UART_TX]);
+#else
+	pm_qos_update_request(&up->qos_idle[PXA_UART_TX],
+					PM_QOS_CONSTRAINT);
+	wake_lock(&up->idle_lock[PXA_UART_TX]);
+#endif
+
+	up->tx_cookie = up->tx_desc->tx_submit(up->tx_desc);
+	dma_async_issue_pending(up->txdma);
+
+	local_irq_restore(flags);
+}
+
+static void pxa_uart_receive_dma_start(struct uart_pxa_port *up)
+{
+	struct scatterlist *sg;
+	struct pxa_dmac_data dmac_data;
+	struct dma_slave_config slave_config;
+	int ret;
+
+	slave_config.direction      = DMA_FROM_DEVICE;
+	slave_config.src_addr       = up->port.mapbase;
+	slave_config.src_maxburst   = 8;
+	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+
+	dmac_data.flow_ctrl = true;
+	dmac_data.ch_map    = up->rxdrcmr;
+	up->rxdma->private = &dmac_data;
+
+	ret = dmaengine_slave_config(up->rxdma, &slave_config);
+	if (ret) {
+		printk(KERN_ERR "%s: dmaengine slave config err.\n", __func__);
+		return;
+	}
+
+	sg = &up->rxsg;
+	sg_init_table(sg, 1); /* Initialize SG table */
+	sg_set_buf(sg, phys_to_virt(up->rxdma_addr_phys), DMA_BLOCK);
+	sg_dma_address(sg) = up->rxdma_addr_phys;
+
+	up->rx_desc = up->rxdma->device->device_prep_slave_sg(up->rxdma,
+			sg, 1, DMA_FROM_DEVICE, 0);
+
+	up->rx_desc->callback = pxa_uart_receive_dma;
+	up->rx_desc->callback_param = up;
+
+	up->rx_cookie = up->rx_desc->tx_submit(up->rx_desc);
+	dma_async_issue_pending(up->rxdma);
+}
+
+static void pxa_uart_receive_dma_err(struct uart_pxa_port *up, int *status)
+{
+	unsigned char ch;
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned char *tmp;
+	int count;
+	unsigned int flag = 0;
+
+	dmaengine_terminate_all(up->rxdma);
+	count = pxa_dmac_chan_get_dst_ptr(up->rxdma) - up->rxdma_addr_phys;
+
+	/* if have DMA reqeust, wait. */
+	while (dma_async_is_tx_complete(up->rxdma,
+		up->rx_cookie, NULL, NULL) != DMA_SUCCESS)
+		rmb();
+
+	tmp = up->rxdma_addr;
+
+	tty_insert_flip_string(tty, tmp, count);
+	up->port.icount.rx += count;
+
+	do {
+		ch = serial_in(up, UART_RX);
+		up->port.icount.rx++;
+
+		/*
+		 * For statistics only
+		 */
+		if (*status & UART_LSR_BI) {
+			*status &= ~(UART_LSR_FE | UART_LSR_PE);
+
+			up->port.icount.brk++;
+			/*
+			 * We do the SysRQ and SAK checking
+			 * here because otherwise the break
+			 * may get masked by ignore_status_mask
+			 * or read_status_mask.
+			 */
+			if (uart_handle_break(&up->port))
+				goto ignore_char;
+			flag = TTY_BREAK;
+		} else if (*status & UART_LSR_PE) {
+			up->port.icount.parity++;
+			flag = TTY_PARITY;
+		} else if (*status & UART_LSR_FE) {
+			up->port.icount.frame++;
+			flag = TTY_FRAME;
+		}
+
+		if (*status & UART_LSR_OE)
+			up->port.icount.overrun++;
+
+		/*
+		 * Mask off conditions which should be ignored.
+		 */
+		*status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+		if (up->port.line == up->port.cons->index) {
+			/* Recover the break flag from console xmit */
+			*status |= up->lsr_break_flag;
+			up->lsr_break_flag = 0;
+		}
+#endif
+
+		if (uart_handle_sysrq_char(&up->port, ch))
+			goto ignore_char;
+
+		uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag);
+
+ignore_char:
+		*status = serial_in(up, UART_LSR);
+	} while (*status & UART_LSR_DR);
+
+	tty_flip_buffer_push(tty);
+	if (up->rx_stop)
+		return;
+	pxa_uart_receive_dma_start(up);
+}
+
+static void pxa_uart_receive_dma(void *data)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
+	struct tty_struct *tty = up->port.state->port.tty;
+	unsigned int count;
+	unsigned char *tmp = up->rxdma_addr;
+
+	dmaengine_terminate_all(up->rxdma);
+
+	count = pxa_dmac_chan_get_dst_ptr(up->rxdma) - up->rxdma_addr_phys;
+	tty_insert_flip_string(tty, tmp, count);
+	up->port.icount.rx += count;
+	tty_flip_buffer_push(tty);
+	if (up->rx_stop)
+		return;
+	pxa_uart_receive_dma_start(up);
+	return;
+}
+
+static void pxa_uart_transmit_dma(void *data)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
+	struct circ_buf *xmit = &up->port.state->xmit;
+
+	dmaengine_terminate_all(up->txdma);
+
+	/* if tx stop, stop transmit DMA and return */
+	if (up->tx_stop)
+		return;
+
+	if (up->port.x_char) {
+		serial_out(up, UART_TX, up->port.x_char);
+		up->port.icount.tx++;
+		up->port.x_char = 0;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(&up->port);
+
+	if (!uart_circ_empty(xmit))
+		tasklet_schedule(&up->tklet);
+	return;
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	if (!pxa_dmac_is_this_type(chan))
+		return false;
+
+	if (pxa_dmac_chan_get_prio(chan) != (pxa_dma_prio)param)
+		return false;
+
+	return true;
+}
+
+static void uart_pxa_dma_init(struct uart_pxa_port *up)
+{
+	dma_cap_mask_t mask;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SG, mask);
+
+	if (0 == up->rxdma) {
+		up->rxdma = dma_request_channel(mask, filter,
+					(void *)DMA_PRIO_LOW);
+		if (up->rxdma < 0)
+			goto out;
+
+		printk(KERN_ERR "%s: get rx dma channel %p\n", __func__,
+					up->rxdma);
+	}
+
+	if (0 == up->txdma) {
+		up->txdma = dma_request_channel(mask, filter,
+					(void *)DMA_PRIO_LOW);
+		if (up->txdma < 0)
+			goto err_txdma;
+
+		printk(KERN_ERR "%s: get tx dma channel %p\n", __func__,
+					up->txdma);
+	}
+
+	if (NULL == up->txdma_addr) {
+		up->txdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK,
+				&up->txdma_addr_phys, GFP_KERNEL);
+		if (!up->txdma_addr)
+			goto txdma_err_alloc;
+	}
+
+	if (NULL == up->rxdma_addr) {
+		up->rxdma_addr = dma_alloc_coherent(NULL, DMA_BLOCK,
+				&up->rxdma_addr_phys, GFP_KERNEL);
+		if (!up->rxdma_addr)
+			goto rxdma_err_alloc;
+	}
+
+#ifdef CONFIG_PM
+	up->buf_save = kmalloc(DMA_BLOCK, GFP_KERNEL);
+	if (!up->buf_save)
+		goto buf_err_alloc;
+#endif
+
+	return;
+
+#ifdef CONFIG_PM
+buf_err_alloc:
+	dma_free_coherent(NULL, DMA_BLOCK, up->rxdma_addr,
+			up->rxdma_addr_phys);
+	up->rxdma_addr = NULL;
+#endif
+rxdma_err_alloc:
+	dma_free_coherent(NULL, DMA_BLOCK, up->txdma_addr,
+			up->txdma_addr_phys);
+	up->txdma_addr = NULL;
+txdma_err_alloc:
+	dma_release_channel(up->rxdma);
+	dma_release_channel(up->txdma);
+err_txdma:
+	dma_release_channel(up->rxdma);
+out:
+	return;
+}
+
+static void uart_pxa_dma_uninit(struct uart_pxa_port *up)
+{
+#ifdef CONFIG_PM
+	kfree(up->buf_save);
+#endif
+	dmaengine_terminate_all(up->rxdma);
+	dmaengine_terminate_all(up->txdma);
+
+	if (up->txdma_addr != NULL) {
+		dma_free_coherent(NULL, DMA_BLOCK, up->txdma_addr,
+				up->txdma_addr_phys);
+		up->txdma_addr = NULL;
+	}
+	if (up->txdma != 0)
+		dma_release_channel(up->txdma);
+
+	if (up->rxdma_addr != NULL) {
+		dma_free_coherent(NULL, DMA_BLOCK, up->rxdma_addr,
+				up->rxdma_addr_phys);
+		up->rxdma_addr = NULL;
+	}
+
+	if (up->rxdma != 0)
+		dma_release_channel(up->rxdma);
+
+	return;
+}
+
+static void uart_task_action(unsigned long data)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
+	struct circ_buf *xmit = &up->port.state->xmit;
+	unsigned char *tmp = up->txdma_addr;
+	unsigned long flags;
+	int count = 0, c;
+
+	/* if the tx is stop, just return.*/
+	if (up->tx_stop)
+		return;
+
+	if (dma_async_is_tx_complete(up->txdma, up->tx_cookie, NULL, NULL) !=
+	    DMA_SUCCESS)
+		return;
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	while (1) {
+		c = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+		if (c <= 0)
+			break;
+
+		memcpy(tmp, xmit->buf + xmit->tail, c);
+		xmit->tail = (xmit->tail + c) & (UART_XMIT_SIZE - 1);
+		tmp += c;
+		count += c;
+		up->port.icount.tx += c;
+	}
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	tmp = up->txdma_addr;
+	up->tx_stop = 0;
+
+	pxa_uart_transmit_dma_start(up, count);
+}
+
+static int serial_pxa_startup(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+	int retval;
+
+	if (port->line == 3) /* HWUART */
+		up->mcr |= UART_MCR_AFE;
+	else
+		up->mcr = 0;
+
+	up->port.uartclk = clk_get_rate(up->clk);
+
+	/*
+	 * Allocate the IRQ
+	 */
+	retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
+	if (retval)
+		return retval;
+
+	/*
+	 * Clear the FIFO buffers and disable them.
+	 * (they will be reenabled in set_termios())
+	 */
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+			UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+
+	/*
+	 * Clear the interrupt registers.
+	 */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	/*
+	 * Now, initialize the UART
+	 */
+	serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl |= TIOCM_OUT2;
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Finally, enable interrupts.  Note: Modem status interrupts
+	 * are set via set_termios(), which will be occurring imminently
+	 * anyway, so we don't enable them here.
+	 */
+	if (up->dma_enable) {
+		uart_pxa_dma_init(up);
+		up->rx_stop = 0;
+		pxa_uart_receive_dma_start(up);
+		up->ier = UART_IER_DMAE | UART_IER_UUE | UART_IER_RTOIE;
+		tasklet_init(&up->tklet, uart_task_action, (unsigned long)up);
+	} else {
+		up->ier = UART_IER_RLSI | UART_IER_RDI |
+			UART_IER_RTOIE | UART_IER_UUE;
+	}
+	serial_out(up, UART_IER, up->ier);
+
+	/*
+	 * And clear the interrupt registers again for luck.
+	 */
+	(void) serial_in(up, UART_LSR);
+	(void) serial_in(up, UART_RX);
+	(void) serial_in(up, UART_IIR);
+	(void) serial_in(up, UART_MSR);
+
+	return 0;
+}
+
+static void serial_pxa_shutdown(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned long flags;
+
+	flush_work(&up->uart_tx_lpm_work);
+
+	free_irq(up->port.irq, up);
+
+	if (up->dma_enable) {
+		tasklet_kill(&up->tklet);
+		uart_pxa_dma_uninit(up);
+	}
+
+	/*
+	 * Disable interrupts from this port
+	 */
+	up->ier = 0;
+	serial_out(up, UART_IER, 0);
+
+	spin_lock_irqsave(&up->port.lock, flags);
+	up->port.mctrl &= ~TIOCM_OUT2;
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+
+	/*
+	 * Disable break condition and FIFOs
+	 */
+	serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+	serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+				  UART_FCR_CLEAR_RCVR |
+				  UART_FCR_CLEAR_XMIT);
+	serial_out(up, UART_FCR, 0);
+}
+
+static void
+serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
+		       struct ktermios *old)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	unsigned char cval, fcr = 0;
+	unsigned long flags;
+	unsigned int baud, quot = 0;
+	unsigned int dll;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cval = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		cval = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cval |= UART_LCR_STOP;
+	if (termios->c_cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(termios->c_cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, 921600*16*4/16);
+	if (baud > 921600) {
+		port->uartclk = 921600*16*4; /* 58.9823MHz as the clk src */
+		up->ier |= UART_IER_HSE;
+		if (B1500000 == (termios->c_cflag & B1500000))
+			quot = 2;
+		if (B3500000 == (termios->c_cflag & B3500000))
+			quot = 1;
+		if (quot == 0)
+			quot = uart_get_divisor(port, baud);
+	} else {
+		quot = uart_get_divisor(port, baud);
+		up->ier &= ~UART_IER_HSE;
+	}
+
+	if (up->dma_enable) {
+		fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
+		fcr &= ~UART_FCR_PXA_BUS32;
+	} else {
+		if ((up->port.uartclk / quot) < (2400 * 16))
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1;
+		else if ((up->port.uartclk / quot) < (230400 * 16))
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8;
+		else
+			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32;
+	}
+
+	/*
+	 * Ok, we're now changing the port state.  Do it with
+	 * interrupts disabled.
+	 */
+	spin_lock_irqsave(&up->port.lock, flags);
+
+	/*
+	 * Ensure the port will be enabled.
+	 * This is required especially for serial console.
+	 */
+	up->ier |= UART_IER_UUE;
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= UART_LSR_BI;
+
+	/*
+	 * Characters to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= UART_LSR_BI;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			up->port.ignore_status_mask |= UART_LSR_OE;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= UART_LSR_DR;
+
+	/*
+	 * CTS flow control flag and modem status interrupts
+	 */
+	if (up->dma_enable) {
+		if (termios->c_cflag & CRTSCTS)
+			up->mcr |= UART_MCR_AFE;
+		else
+			up->mcr &= UART_MCR_AFE;
+	} else {
+		up->ier &= ~UART_IER_MSI;
+		if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+			up->ier |= UART_IER_MSI;
+	}
+
+	serial_out(up, UART_IER, up->ier);
+
+	if (termios->c_cflag & CRTSCTS)
+		up->mcr |= UART_MCR_AFE;
+	else
+		up->mcr &= ~UART_MCR_AFE;
+
+	serial_out(up, UART_LCR, cval | UART_LCR_DLAB);	/* set DLAB */
+	serial_out(up, UART_DLL, quot & 0xff);		/* LS of divisor */
+
+	/*
+	 * work around Errata #75 according to Intel(R) PXA27x Processor Family
+	 * Specification Update (Nov 2005)
+	 */
+	dll = serial_in(up, UART_DLL);
+	WARN_ON(dll != (quot & 0xff));
+
+	serial_out(up, UART_DLM, quot >> 8);		/* MS of divisor */
+	serial_out(up, UART_LCR, cval);			/* reset DLAB */
+	up->lcr = cval;					/* Save LCR */
+	serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+	serial_out(up, UART_FCR, fcr);
+	spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_pxa_pm(struct uart_port *port, unsigned int state,
+	      unsigned int oldstate)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	if (!state)
+		clk_enable(up->clk);
+	else
+		clk_disable(up->clk);
+}
+
+static void serial_pxa_release_port(struct uart_port *port)
+{
+}
+
+static int serial_pxa_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+static void serial_pxa_config_port(struct uart_port *port, int flags)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	up->port.type = PORT_PXA;
+}
+
+static int
+serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	/* we don't want the core code to modify any port params */
+	return -EINVAL;
+}
+
+static const char *
+serial_pxa_type(struct uart_port *port)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+	return up->name;
+}
+
+static struct uart_pxa_port *serial_pxa_ports[4];
+static struct uart_driver serial_pxa_reg;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ *	Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_pxa_port *up)
+{
+	unsigned int status, tmout = 10000;
+
+	/* Wait up to 10ms for the character(s) to be sent. */
+	do {
+		status = serial_in(up, UART_LSR);
+
+		if (status & UART_LSR_BI)
+			up->lsr_break_flag = UART_LSR_BI;
+
+		if (--tmout == 0)
+			break;
+		udelay(1);
+	} while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+	/* Wait up to 1s for flow control if necessary */
+	if (up->port.flags & UPF_CONS_FLOW) {
+		tmout = 1000000;
+		while (--tmout &&
+		       ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+			udelay(1);
+	}
+}
+
+static void serial_pxa_console_putchar(struct uart_port *port, int ch)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+	wait_for_xmitr(up);
+	serial_out(up, UART_TX, ch);
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ *	The console_lock must be held when we get here.
+ */
+static void
+serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
+{
+	struct uart_pxa_port *up = serial_pxa_ports[co->index];
+	unsigned int ier;
+	unsigned long flags;
+	int locked = 1;
+
+	local_irq_save(flags);
+	if (up->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress)
+		locked = spin_trylock(&up->port.lock);
+	else
+		spin_lock(&up->port.lock);
+
+	clk_enable(up->clk);
+
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = serial_in(up, UART_IER);
+	serial_out(up, UART_IER, UART_IER_UUE);
+
+	uart_console_write(&up->port, s, count, serial_pxa_console_putchar);
+
+	/*
+	 *	Finally, wait for transmitter to become empty
+	 *	and restore the IER
+	 */
+	wait_for_xmitr(up);
+	serial_out(up, UART_IER, ier);
+
+	clk_disable(up->clk);
+
+	if (locked)
+		spin_unlock(&up->port.lock);
+	local_irq_restore(flags);
+}
+
+static int __init
+serial_pxa_console_setup(struct console *co, char *options)
+{
+	struct uart_pxa_port *up;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (co->index == -1 || co->index >= serial_pxa_reg.nr)
+		co->index = 0;
+	up = serial_pxa_ports[co->index];
+	if (!up)
+		return -ENODEV;
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static struct console serial_pxa_console = {
+	.name		= "ttyS",
+	.write		= serial_pxa_console_write,
+	.device		= uart_console_device,
+	.setup		= serial_pxa_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &serial_pxa_reg,
+};
+
+#define PXA_CONSOLE (&serial_pxa_console)
+#else
+#define PXA_CONSOLE NULL
+#endif
+
+struct uart_ops serial_pxa_pops = {
+	.tx_empty	= serial_pxa_tx_empty,
+	.set_mctrl	= serial_pxa_set_mctrl,
+	.get_mctrl	= serial_pxa_get_mctrl,
+	.stop_tx	= serial_pxa_stop_tx,
+	.start_tx	= serial_pxa_start_tx,
+	.stop_rx	= serial_pxa_stop_rx,
+	.enable_ms	= serial_pxa_enable_ms,
+	.break_ctl	= serial_pxa_break_ctl,
+	.startup	= serial_pxa_startup,
+	.shutdown	= serial_pxa_shutdown,
+	.set_termios	= serial_pxa_set_termios,
+	.pm		= serial_pxa_pm,
+	.type		= serial_pxa_type,
+	.release_port	= serial_pxa_release_port,
+	.request_port	= serial_pxa_request_port,
+	.config_port	= serial_pxa_config_port,
+	.verify_port	= serial_pxa_verify_port,
+};
+
+static struct uart_driver serial_pxa_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= "PXA serial",
+	.dev_name	= "ttyS",
+	.major		= TTY_MAJOR,
+	.minor		= 64,
+	.nr		= 4,
+	.cons		= PXA_CONSOLE,
+};
+
+#ifdef CONFIG_PM
+static int serial_pxa_suspend(struct device *dev)
+{
+	struct uart_pxa_port *sport = dev_get_drvdata(dev);
+
+	if (sport && (sport->ier & UART_IER_DMAE)) {
+		int length = 0, sent = 0;
+		unsigned long flags;
+
+		local_irq_save(flags);
+		sport->tx_stop = 1;
+		sport->rx_stop = 1;
+		sport->data_len = 0;
+		if (dma_async_is_tx_complete(sport->txdma, sport->tx_cookie,
+					NULL, NULL) != DMA_SUCCESS) {
+			dmaengine_terminate_all(sport->txdma);
+			length = sport->tx_cnt;
+			sent = pxa_dmac_chan_get_src_ptr(sport->txdma) -
+							sport->txdma_addr_phys;
+			memcpy(sport->buf_save, sport->txdma_addr
+				 + sent, length);
+			sport->data_len = length;
+
+		}
+
+		if (dma_async_is_tx_complete(sport->rxdma, sport->rx_cookie,
+					NULL, NULL) != DMA_SUCCESS)
+			dmaengine_terminate_all(sport->rxdma);
+
+		pxa_uart_receive_dma(sport);
+
+		local_irq_restore(flags);
+	}
+
+	if (sport)
+		uart_suspend_port(&serial_pxa_reg, &sport->port);
+
+	return 0;
+}
+
+static int serial_pxa_resume(struct device *dev)
+{
+	struct uart_pxa_port *sport = dev_get_drvdata(dev);
+
+	if (sport)
+		uart_resume_port(&serial_pxa_reg, &sport->port);
+
+	if (sport && (sport->ier & UART_IER_DMAE)) {
+		if (sport->data_len > 0) {
+			memcpy(sport->txdma_addr, sport->buf_save,
+					sport->data_len);
+			pxa_uart_transmit_dma_start(sport,
+					sport->data_len);
+		} else
+			tasklet_schedule(&sport->tklet);
+
+		pxa_uart_receive_dma_start(sport);
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops serial_pxa_pm_ops = {
+	.suspend	= serial_pxa_suspend,
+	.resume		= serial_pxa_resume,
+};
+#endif
+
+static void pxa_timer_handler(unsigned long data)
+{
+	struct uart_pxa_port *up = (struct uart_pxa_port *)data;
+#ifdef CONFIG_PXA95x
+	dvfm_enable_lowpower(up->dvfm_dev_idx[PXA_UART_RX]);
+#else
+	pm_qos_update_request(&up->qos_idle[PXA_UART_RX], PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&up->idle_lock[PXA_UART_RX]);
+#endif
+}
+
+static void uart_tx_lpm_handler(struct work_struct *work)
+{
+	struct uart_pxa_port *up =
+	    container_of(work, struct uart_pxa_port, uart_tx_lpm_work);
+
+	/* Polling until TX FIFO is empty */
+	while (!(serial_in(up, UART_LSR) & UART_LSR_TEMT))
+		msleep(1);
+#ifdef CONFIG_PXA95x
+	dvfm_enable_lowpower(up->dvfm_dev_idx[PXA_UART_TX]);
+#else
+	pm_qos_update_request(&up->qos_idle[PXA_UART_TX], PM_QOS_DEFAULT_VALUE);
+	wake_unlock(&up->idle_lock[PXA_UART_TX]);
+#endif
+}
+
+#ifdef CONFIG_PXA95x
+
+extern void get_wakeup_source(pm_wakeup_src_t *);
+
+static void uart_rx_lpm_handler(struct work_struct *work)
+{
+	struct uart_pxa_port *up =
+	    container_of(work, struct uart_pxa_port, uart_rx_lpm_work);
+
+	mod_timer(&up->pxa_timer, jiffies + PXA_TIMER_TIMEOUT);
+	dvfm_disable_lowpower(up->dvfm_dev_idx[PXA_UART_RX]);
+}
+
+static int uart_notifier_freq(struct notifier_block *nb,
+				unsigned long val, void *data)
+{
+	struct dvfm_freqs *freqs = (struct dvfm_freqs *)data;
+	struct op_info *new = NULL;
+	struct dvfm_md_opt *opt;
+	pm_wakeup_src_t src;
+	struct uart_pxa_port *sport;
+
+	if (freqs)
+		new = &freqs->new_info;
+	else
+		return 0;
+
+	sport = container_of(nb, struct uart_pxa_port, notifier_freq_block);
+	if (val != DVFM_FREQ_POSTCHANGE)
+		return 0;
+
+	opt = new->op;
+	if ((opt->power_mode != POWER_MODE_D1) &&
+	    (opt->power_mode != POWER_MODE_D2) &&
+	    (opt->power_mode != POWER_MODE_CG))
+		return 0;
+
+	get_wakeup_source(&src);
+
+	if ((src.bits.uart1 && sport->port.irq == IRQ_FFUART) ||
+	    (src.bits.uart2 && sport->port.irq == IRQ_STUART)) {
+		schedule_work(&sport->uart_rx_lpm_work);
+	}
+
+	return 0;
+}
+#endif
+
+static int serial_pxa_probe(struct platform_device *dev)
+{
+	struct uart_pxa_port *sport;
+	struct resource *mmres, *irqres, *dmares;
+	int ret, i;
+	char dev_name[30];
+
+	mmres = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	irqres = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+	if (!mmres || !irqres)
+		return -ENODEV;
+
+	sport = kzalloc(sizeof(struct uart_pxa_port), GFP_KERNEL);
+	if (!sport)
+		return -ENOMEM;
+
+	sport->clk = clk_get(&dev->dev, NULL);
+	if (IS_ERR(sport->clk)) {
+		ret = PTR_ERR(sport->clk);
+		goto err_free;
+	}
+
+	sport->port.type = PORT_PXA;
+	sport->port.iotype = UPIO_MEM;
+	sport->port.mapbase = mmres->start;
+	sport->port.irq = irqres->start;
+	sport->port.fifosize = 64;
+	sport->port.ops = &serial_pxa_pops;
+	sport->port.line = dev->id;
+	sport->port.dev = &dev->dev;
+	sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
+	sport->port.uartclk = clk_get_rate(sport->clk);
+
+	switch (dev->id) {
+	case 0:
+		sport->name = "FFUART";
+		break;
+	case 1:
+		sport->name = "BTUART";
+		break;
+	case 2:
+		sport->name = "STUART";
+		break;
+	case 3:
+		sport->name = "HWUART";
+		break;
+	default:
+		sport->name = "???";
+		break;
+	}
+
+	sport->rxdrcmr = 0;
+	sport->txdrcmr = 0;
+	sport->txdma = 0;
+	sport->rxdma = 0;
+	sport->txdma_addr = NULL;
+	sport->rxdma_addr = NULL;
+	sport->dma_enable = 0;
+
+	if (uart_dma) {
+		/* Get Rx DMA mapping register */
+		dmares = platform_get_resource(dev, IORESOURCE_DMA, 0);
+		if (dmares)
+			sport->rxdrcmr = dmares->start;
+
+		/* Get Tx DMA mapping register */
+		dmares = platform_get_resource(dev, IORESOURCE_DMA, 1);
+		if (dmares)
+			sport->txdrcmr = dmares->start;
+
+		if (sport->rxdrcmr && sport->txdrcmr)
+			sport->dma_enable = 1;
+	}
+
+	for (i = 0; i < 2; i++) {
+		sprintf(dev_name, "%s.%s", sport->name,
+				(i == PXA_UART_RX) ? "rx" : "tx");
+
+#ifdef CONFIG_PXA95x
+		sport->dvfm_dev_idx[i] = -1;
+		dvfm_register(dev_name, &(sport->dvfm_dev_idx[i]));
+#else
+		pm_qos_add_request(&sport->qos_idle[i], PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+		wake_lock_init(&sport->idle_lock[i], WAKE_LOCK_IDLE,
+				(const char *)dev_name);
+#endif
+	}
+
+#ifdef CONFIG_PXA95x
+	sport->notifier_freq_block.notifier_call = uart_notifier_freq;
+	dvfm_register_notifier(&sport->notifier_freq_block,
+				DVFM_FREQUENCY_NOTIFIER);
+	INIT_WORK(&sport->uart_rx_lpm_work, uart_rx_lpm_handler);
+#endif
+	INIT_WORK(&sport->uart_tx_lpm_work, uart_tx_lpm_handler);
+
+	init_timer(&sport->pxa_timer);
+	sport->pxa_timer.function = pxa_timer_handler;
+	sport->pxa_timer.data = (long)sport;
+
+	sport->port.membase = ioremap(mmres->start,
+			mmres->end - mmres->start + 1);
+	if (!sport->port.membase) {
+		ret = -ENOMEM;
+		goto err_clk;
+	}
+
+	serial_pxa_ports[dev->id] = sport;
+
+	uart_add_one_port(&serial_pxa_reg, &sport->port);
+	platform_set_drvdata(dev, sport);
+
+	return 0;
+
+ err_clk:
+#ifdef CONFIG_PXA95x
+	dvfm_unregister(sport->name, &(sport->dvfm_dev_idx[PXA_UART_RX]));
+	dvfm_unregister(sport->name, &(sport->dvfm_dev_idx[PXA_UART_TX]));
+	dvfm_unregister_notifier(&sport->notifier_freq_block,
+			DVFM_FREQUENCY_NOTIFIER);
+#else
+	pm_qos_remove_request(&sport->qos_idle[PXA_UART_RX]);
+	pm_qos_remove_request(&sport->qos_idle[PXA_UART_TX]);
+	wake_lock_destroy(&sport->idle_lock[PXA_UART_RX]);
+	wake_lock_destroy(&sport->idle_lock[PXA_UART_TX]);
+#endif
+	clk_put(sport->clk);
+ err_free:
+	kfree(sport);
+	return ret;
+}
+
+static int serial_pxa_remove(struct platform_device *dev)
+{
+	struct uart_pxa_port *sport = platform_get_drvdata(dev);
+
+#ifdef CONFIG_PXA95x
+	dvfm_unregister(sport->name, &(sport->dvfm_dev_idx[PXA_UART_RX]));
+	dvfm_unregister(sport->name, &(sport->dvfm_dev_idx[PXA_UART_TX]));
+	dvfm_unregister_notifier(&sport->notifier_freq_block,
+			DVFM_FREQUENCY_NOTIFIER);
+#else
+	pm_qos_remove_request(&sport->qos_idle[PXA_UART_RX]);
+	pm_qos_remove_request(&sport->qos_idle[PXA_UART_TX]);
+	wake_lock_destroy(&sport->idle_lock[PXA_UART_RX]);
+	wake_lock_destroy(&sport->idle_lock[PXA_UART_TX]);
+#endif
+
+	platform_set_drvdata(dev, NULL);
+
+	uart_remove_one_port(&serial_pxa_reg, &sport->port);
+	clk_put(sport->clk);
+	kfree(sport);
+	serial_pxa_ports[dev->id] = NULL;
+
+	return 0;
+}
+
+static struct platform_driver serial_pxa_driver = {
+	.probe          = serial_pxa_probe,
+	.remove         = serial_pxa_remove,
+
+	.driver		= {
+		.name	= "pxa2xx-uart",
+		.owner	= THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	= &serial_pxa_pm_ops,
+#endif
+	},
+};
+
+int __init serial_pxa_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&serial_pxa_reg);
+	if (ret != 0)
+		return ret;
+
+	ret = platform_driver_register(&serial_pxa_driver);
+	if (ret != 0)
+		uart_unregister_driver(&serial_pxa_reg);
+
+	return ret;
+}
+
+void __exit serial_pxa_exit(void)
+{
+	platform_driver_unregister(&serial_pxa_driver);
+	uart_unregister_driver(&serial_pxa_reg);
+}
+
+module_init(serial_pxa_init);
+module_exit(serial_pxa_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa2xx-uart");
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 09/13] DMA:remove unnecessary dma configurations
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.remove old unnecessary dma configurations

Change-Id: I3741d39ba643d7a6b95341e0ba695a689d18e147
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 sound/soc/pxa/mmp2-sspa.c |   28 ++++++++++++++--------------
 1 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/sound/soc/pxa/mmp2-sspa.c b/sound/soc/pxa/mmp2-sspa.c
index de3a45f..76e57c7 100644
--- a/sound/soc/pxa/mmp2-sspa.c
+++ b/sound/soc/pxa/mmp2-sspa.c
@@ -259,9 +259,6 @@ mmp2_sspa_get_dma_params(int id, struct ssp_device *sspa, int width, int out)
 		return NULL;
 
 	dma = &mmp2_pcm_adma_params[index];
-	dma->dcmd = (out ? (TDCR_DSTDIR_ADDR_HOLD | TDCR_SRCDIR_ADDR_INC) :
-			   (TDCR_SRCDIR_ADDR_HOLD | TDCR_DSTDIR_ADDR_INC)) |
-		    TDCR_PACKMOD | TDCR_BURSTSZ_4B | TDCR_FETCHND | width;
 	dma->dma_ch = out ? sspa->drcmr_tx : sspa->drcmr_rx;
 	dma->dev_addr = out ? (sspa->phys_base + SSPA_TXD) :
 			      (sspa->phys_base + SSPA_RXD);
@@ -629,20 +626,24 @@ static int mmp3_sspa_hw_params(struct snd_pcm_substream *substream,
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S8:
-		word_size = SSPA_CTL_8_BITS;
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS);
+		word_size = SSPA_CTL_16_BITS;
 		bits_per_frame = 16;
 		as_width = TDCR_SSZ_8_BITS;
 	case SNDRV_PCM_FORMAT_S16_LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS);
 		word_size = SSPA_CTL_32_BITS;
 		bits_per_frame = 32;
 		as_width = TDCR_SSZ_16_BITS;
 		break;
 	case SNDRV_PCM_FORMAT_S24_LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS);
 		word_size = SSPA_CTL_24_BITS;
 		bits_per_frame = 48;
 		as_width = TDCR_SSZ_24_BITS;
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS);
 		word_size = SSPA_CTL_32_BITS;
 		bits_per_frame = 64;
 		as_width = TDCR_SSZ_32_BITS;
@@ -655,8 +656,7 @@ static int mmp3_sspa_hw_params(struct snd_pcm_substream *substream,
 	 * that word_size bit can be sent out. the word_size - 1 length of
 	 * 0 would be ignored in codec. it should be also appiled to other
 	 * formats besides S16_LE */
-	sspa_ctrl |= SSPA_CTL_XSSZ1(word_size) |
-		     SSPA_CTL_XWDLEN1(word_size);
+	sspa_ctrl |= SSPA_CTL_XWDLEN1(word_size);
 	sspa_sp |= SSPA_SP_FPER((bits_per_frame << 1) - 1) |
 		     SSPA_SP_FWID(bits_per_frame - 1);
 
@@ -670,16 +670,16 @@ static int mmp3_sspa_hw_params(struct snd_pcm_substream *substream,
 		mmp2_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
 		mmp2_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0);
 		mmp2_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
-#ifdef CONFIG_CPU_MMP3
+
 		if (sspa_sp & SSPA_SP_MSL) {
-		/* FIXME: hw issue, for the rx port, can not config the
-		 * master mode in TXSP register. and must clear this bit
-		 * in TXSP register */
-		sspa_sp = mmp2_sspa_read_reg(sspa, SSPA_TXSP);
-		sspa_sp |= SSPA_SP_WEN;
-		mmp2_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp & ~SSPA_SP_MSL);
+			/* FIXME: hw issue, for the rx port, can not config the
+			 * master mode in TXSP register. and must clear this bit
+			 * in TXSP register */
+			sspa_sp = mmp2_sspa_read_reg(sspa, SSPA_TXSP);
+			sspa_sp |= SSPA_SP_WEN;
+			mmp2_sspa_write_reg(sspa, SSPA_TXSP,
+						sspa_sp & ~SSPA_SP_MSL);
 		}
-#endif
 	}
 
 	mmp2_sspa_dump_reg(sspa);
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 09/13] DMA:remove unnecessary dma configurations
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.remove old unnecessary dma configurations

Change-Id: I3741d39ba643d7a6b95341e0ba695a689d18e147
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 sound/soc/pxa/mmp2-sspa.c |   28 ++++++++++++++--------------
 1 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/sound/soc/pxa/mmp2-sspa.c b/sound/soc/pxa/mmp2-sspa.c
index de3a45f..76e57c7 100644
--- a/sound/soc/pxa/mmp2-sspa.c
+++ b/sound/soc/pxa/mmp2-sspa.c
@@ -259,9 +259,6 @@ mmp2_sspa_get_dma_params(int id, struct ssp_device *sspa, int width, int out)
 		return NULL;
 
 	dma = &mmp2_pcm_adma_params[index];
-	dma->dcmd = (out ? (TDCR_DSTDIR_ADDR_HOLD | TDCR_SRCDIR_ADDR_INC) :
-			   (TDCR_SRCDIR_ADDR_HOLD | TDCR_DSTDIR_ADDR_INC)) |
-		    TDCR_PACKMOD | TDCR_BURSTSZ_4B | TDCR_FETCHND | width;
 	dma->dma_ch = out ? sspa->drcmr_tx : sspa->drcmr_rx;
 	dma->dev_addr = out ? (sspa->phys_base + SSPA_TXD) :
 			      (sspa->phys_base + SSPA_RXD);
@@ -629,20 +626,24 @@ static int mmp3_sspa_hw_params(struct snd_pcm_substream *substream,
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S8:
-		word_size = SSPA_CTL_8_BITS;
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS);
+		word_size = SSPA_CTL_16_BITS;
 		bits_per_frame = 16;
 		as_width = TDCR_SSZ_8_BITS;
 	case SNDRV_PCM_FORMAT_S16_LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS);
 		word_size = SSPA_CTL_32_BITS;
 		bits_per_frame = 32;
 		as_width = TDCR_SSZ_16_BITS;
 		break;
 	case SNDRV_PCM_FORMAT_S24_LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS);
 		word_size = SSPA_CTL_24_BITS;
 		bits_per_frame = 48;
 		as_width = TDCR_SSZ_24_BITS;
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS);
 		word_size = SSPA_CTL_32_BITS;
 		bits_per_frame = 64;
 		as_width = TDCR_SSZ_32_BITS;
@@ -655,8 +656,7 @@ static int mmp3_sspa_hw_params(struct snd_pcm_substream *substream,
 	 * that word_size bit can be sent out. the word_size - 1 length of
 	 * 0 would be ignored in codec. it should be also appiled to other
 	 * formats besides S16_LE */
-	sspa_ctrl |= SSPA_CTL_XSSZ1(word_size) |
-		     SSPA_CTL_XWDLEN1(word_size);
+	sspa_ctrl |= SSPA_CTL_XWDLEN1(word_size);
 	sspa_sp |= SSPA_SP_FPER((bits_per_frame << 1) - 1) |
 		     SSPA_SP_FWID(bits_per_frame - 1);
 
@@ -670,16 +670,16 @@ static int mmp3_sspa_hw_params(struct snd_pcm_substream *substream,
 		mmp2_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
 		mmp2_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0);
 		mmp2_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
-#ifdef CONFIG_CPU_MMP3
+
 		if (sspa_sp & SSPA_SP_MSL) {
-		/* FIXME: hw issue, for the rx port, can not config the
-		 * master mode in TXSP register. and must clear this bit
-		 * in TXSP register */
-		sspa_sp = mmp2_sspa_read_reg(sspa, SSPA_TXSP);
-		sspa_sp |= SSPA_SP_WEN;
-		mmp2_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp & ~SSPA_SP_MSL);
+			/* FIXME: hw issue, for the rx port, can not config the
+			 * master mode in TXSP register. and must clear this bit
+			 * in TXSP register */
+			sspa_sp = mmp2_sspa_read_reg(sspa, SSPA_TXSP);
+			sspa_sp |= SSPA_SP_WEN;
+			mmp2_sspa_write_reg(sspa, SSPA_TXSP,
+						sspa_sp & ~SSPA_SP_MSL);
 		}
-#endif
 	}
 
 	mmp2_sspa_dump_reg(sspa);
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 10/13] dmaengine:audio:mmp audio use generic dma engine APIs
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.change mmp audio to use dma engine

Change-Id: I7be32501b87927a6d05ed34f1c73242e1c60517a
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 sound/soc/pxa/mmp-pcm.c  |  223 ++++++++++++++++++++++++----------------------
 sound/soc/pxa/mmp-pcm.h  |    4 +-
 sound/soc/pxa/mmp2-squ.c |  182 ++++++++++++++++++--------------------
 sound/soc/pxa/mmp2-squ.h |    8 ++-
 4 files changed, 210 insertions(+), 207 deletions(-)

diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 7469e19..0e322a2 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -137,7 +137,7 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 	char *src, *dst;
 	u32 point, rest;
 	int num, i;
-	u32 base;
+
 	pr_debug("%s: copy begin, sram_blk_idx = %d rbuf_blk_idx = %d "
 		 "sync_blk = %d", __func__,
 		 prtd->sram_blk_idx,  prtd->rbuf_blk_idx,
@@ -146,13 +146,12 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 	if (!prtd->sync_blk)
 		return;
 
-	base = mmp_get_dma_reg_base(prtd->adma_ch);
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		/*
 		 * adjust sram index if dma has run ahead
 		 * rather than memory copy speed
 		 */
-		point = TDSAR(base) - prtd->sram_phys;
+		point = mmp_tdma_chan_get_ptr(prtd->dma_chan) - prtd->sram_phys;
 		rest = mmp2_pcm_point_is_in_region(prtd->sram_size,
 				prtd->sram_blk_idx * prtd->blk_size,
 				prtd->sync_blk * prtd->blk_size,
@@ -173,7 +172,7 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 		 * by dma polluted, will copy these periods data
 		 * as possible.
 		 */
-		point = TDSAR(base) - prtd->sram_phys;
+		point = mmp_tdma_chan_get_ptr(prtd->dma_chan) - prtd->sram_phys;
 		rest = mmp2_pcm_point_is_in_region(prtd->sram_size,
 				prtd->sram_blk_idx * prtd->blk_size,
 				prtd->sync_blk * prtd->blk_size,
@@ -193,7 +192,6 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 
 	num = prtd->sync_blk;
 	for (i = 0; i < num; i++) {
-
 		mmp2_pcm_copy_data((int *)dst, (int *)src,
 			prtd->blk_size);
 
@@ -216,18 +214,24 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 	return;
 }
 
-static void mmp2_pcm_adma_irq(int dma_ch, void *data)
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct mmp2_runtime_data *prtd = param;
+
+	if (!mmp_adma_is_this_type(chan))
+		return false;
+
+	if (!mmp_tdma_is_specific_chan(chan, prtd->dma_ch))
+		return false;
+
+	chan->private = &prtd->tdma_data;
+	return true;
+}
+
+static void mmp2_pcm_adma_irq(void *data)
 {
 	struct snd_pcm_substream *substream = data;
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
-	u32 base = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (!base)
-		return;
-
-	if (!(TDISR(base) & 0x1))
-		return;
-	/* clear adma irq status */
-	TDISR(base) = 0;
 
 	/* sync sram with ring buf */
 	prtd->sync_blk = min(prtd->sync_blk + 1, prtd->sram_blk_num);
@@ -247,11 +251,12 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	size_t totsize = params_buffer_bytes(params);
 	size_t period = params_period_bytes(params);
 	struct mmp_tdma_desc *adma_desc;
-	dma_addr_t dma_buff_phys, next_desc_phys;
+	dma_addr_t adma_buff_phys, next_desc_phys;
 	int ret;
+	dma_cap_mask_t mask;
+
+	dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 
-	dma = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
-	    rtd->cpu_dai->playback_dma_data : rtd->cpu_dai->capture_dma_data;
 	/* return if this is a bufferless transfer e.g.
 	 * codec <--> BT codec or GSM modem -- lg FIXME */
 	if (!dma)
@@ -261,27 +266,66 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	 * this may get called several times by oss
 	 * emulation with different params
 	 */
-	if (prtd->params == NULL) {
-		prtd->params = dma;
-		ret = mmp_request_dma(prtd->params->name, dma->dma_ch,
-					 mmp2_pcm_adma_irq, substream);
-		if (ret < 0)
-			return ret;
-
-		prtd->adma_ch = ret;
-	} else if (prtd->params != dma) {
-		mmp_free_dma(prtd->adma_ch);
-		prtd->params = dma;
-		ret = mmp_request_dma(prtd->params->name, dma->dma_ch,
-					 mmp2_pcm_adma_irq, substream);
-		if (ret < 0)
-			return ret;
-
-		prtd->adma_ch = ret;
+	prtd->dma_ch = dma->dma_ch;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		prtd->tdma_data.bus_size = TDCR_SSZ_8_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_16_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_24_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_32_BITS;
+		break;
+	default:
+		return -EINVAL;
 	}
+	prtd->tdma_data.pack_mod = true;
+
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+	prtd->dma_chan = dma_request_channel(mask, filter, prtd);
+	if (!prtd->dma_chan)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		prtd->slave_config.direction    = DMA_TO_DEVICE;
+		prtd->slave_config.dst_addr     = dma->dev_addr;
+		prtd->slave_config.dst_maxburst = TDCR_BURSTSZ_4B;
+	} else {
+		prtd->slave_config.direction	  = DMA_FROM_DEVICE;
+		prtd->slave_config.src_addr	  = dma->dev_addr;
+		prtd->slave_config.src_maxburst = TDCR_BURSTSZ_4B;
+	}
+
+	ret = dmaengine_slave_config(prtd->dma_chan, &(prtd->slave_config));
+	if (ret) {
+		printk(KERN_ERR "%s: dmaengine slave config err.\n", __func__);
+		return ret;
+	}
+
 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-	runtime->dma_bytes = totsize;
 
+	/*
+	 * init prtd value.
+	 * the driver has alloc from sram for dma transfer,
+	 * size = MMP2_ADMA_BUF_SIZE (now is equal to PAGE_SIZE).
+	 * and driver has alloc ddr buffer for mmapping to userspace,
+	 * and the size = MMP2_DDR_BUF_SIZE, now is 64KB.
+	 *
+	 * but should note the used buffer size is specified
+	 * by userspace's app, which is calculated by the
+	 * periold_size and buffer_size. The buffer size
+	 * is even less than the buffer size which allocated
+	 * from the sram.
+	 * So at this point should adjust the parameters
+	 * according to user's setting.
+	 */
 	prtd->blk_size     = period;
 	prtd->rbuf_virt    = (u32)runtime->dma_area;
 	prtd->rbuf_phys    = (u32)runtime->dma_addr;
@@ -294,31 +338,22 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	prtd->sync_blk     = 0;
 
 	totsize        = prtd->sram_size;
-	dma_buff_phys  = prtd->sram_phys;
+	adma_buff_phys = prtd->sram_phys;
 	next_desc_phys = prtd->adma_desc_array_phys;
 	adma_desc      = prtd->adma_desc_array;
 
-	do {
-		next_desc_phys += sizeof(struct mmp_tdma_desc);
-
-		adma_desc->nxt_desc = next_desc_phys;
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			adma_desc->src_addr = dma_buff_phys;
-			adma_desc->dst_addr = prtd->params->dev_addr;
-		} else {
-			adma_desc->src_addr = prtd->params->dev_addr;
-			adma_desc->dst_addr = dma_buff_phys;
-		}
-		if (period > totsize)
-			period = totsize;
-		adma_desc->byte_cnt = period;
-		adma_desc++;
-		dma_buff_phys += period;
-
-	} while (totsize -= period);
-	adma_desc[-1].nxt_desc = prtd->adma_desc_array_phys;
+	prtd->desc = prtd->dma_chan->device->device_prep_dma_cyclic(
+			prtd->dma_chan, adma_buff_phys, totsize, period,
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			DMA_TO_DEVICE : DMA_FROM_DEVICE);
+	if (!prtd->desc) {
+		dev_err(&prtd->dma_chan->dev->device,
+			"cannot prepare slave dma\n");
+		return -EINVAL;
+	}
 
-	mmp2_pcm_dump_adma_list(prtd);
+	prtd->desc->callback = mmp2_pcm_adma_irq;
+	prtd->desc->callback_param = substream;
 
 	return 0;
 }
@@ -327,10 +362,9 @@ static int mmp2_pcm_hw_free(struct snd_pcm_substream *substream)
 {
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
 
-	if (prtd->adma_ch != -1) {
-		snd_pcm_set_runtime_buffer(substream, NULL);
-		mmp_free_dma(prtd->adma_ch);
-		prtd->adma_ch = -1;
+	if (prtd->dma_chan) {
+		dma_release_channel(prtd->dma_chan);
+		prtd->dma_chan = NULL;
 	}
 
 	return 0;
@@ -338,16 +372,6 @@ static int mmp2_pcm_hw_free(struct snd_pcm_substream *substream)
 
 static int mmp2_pcm_prepare(struct snd_pcm_substream *substream)
 {
-	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
-	u32 base;
-
-	base = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (!base)
-		return -EINVAL;
-
-	TDCR(base)  = (prtd->params->dcmd) & (~TDCR_CHANEN);
-	TDIMR(base) = TDIMR_COMP;
-
 	return 0;
 }
 
@@ -356,11 +380,6 @@ static int mmp2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
 	unsigned long flags;
 	int ret = 0;
-	u32 base_register;
-
-	base_register = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (!base_register)
-		return -EINVAL;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
@@ -378,24 +397,21 @@ static int mmp2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 		mmp2_pcm_sync_sram_with_ddr(substream);
 		spin_unlock_irqrestore(&prtd->lock, flags);
 
-		TDNDPR(base_register) = prtd->adma_desc_array_phys;
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		TDCR(base_register) = prtd->params->dcmd;
-		wake_up(&dma_wq);
+		dmaengine_terminate_all(prtd->dma_chan);
 		break;
 
 	case SNDRV_PCM_TRIGGER_RESUME:
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		TDNDPR(base_register) = prtd->adma_desc_array_phys;
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	default:
@@ -460,7 +476,7 @@ static int mmp2_pcm_open(struct snd_pcm_substream *substream)
 
 	prtd->substream = substream;
 	runtime->private_data = prtd;
-	prtd->adma_ch = -1;
+	prtd->dma_ch = -1;
 
 	/*
 	 * avoid sram fragment, allocate dma buffer and
@@ -471,6 +487,11 @@ static int mmp2_pcm_open(struct snd_pcm_substream *substream)
 			MMP2_ADMA_BUF_SIZE + MMP2_ADMA_DESC_SIZE,
 			(dma_addr_t *)&prtd->sram_phys);
 
+	if (!prtd->sram_virt) {
+		printk(KERN_ERR "failed to map audio sram\n");
+		goto alloc_sram_err;
+	}
+
 	prtd->adma_desc_array = (void *)(prtd->sram_virt + MMP2_ADMA_BUF_SIZE);
 	prtd->adma_desc_array_phys = (dma_addr_t)(prtd->sram_phys
 					+ MMP2_ADMA_BUF_SIZE);
@@ -492,7 +513,7 @@ static int mmp2_pcm_suspend(struct snd_soc_dai *dai)
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_runtime *runtime;
 	struct mmp2_runtime_data *prtd;
-	u32 base, ch;
+	u32 ch;
 	int stream;
 
 	for (stream = 0; stream < 2; stream++) {
@@ -504,22 +525,10 @@ static int mmp2_pcm_suspend(struct snd_soc_dai *dai)
 			continue;
 
 		prtd = runtime->private_data;
-		ch = prtd->adma_ch;
+		ch = prtd->dma_ch;
 
 		pr_debug("%s: dai name %s stream %d dma ch %d prtd %p\n",
-			__func__, dai->name, stream, prtd->adma_ch, prtd);
-
-		/* mmp2 only uses chain mode */
-		base = mmp_get_dma_reg_base(ch);
-
-		prtd->adma_saved.src_addr  = TDSAR(base);
-		prtd->adma_saved.dest_addr = TDDAR(base);
-		prtd->adma_saved.next_desc_ptr = TDNDPR(base);
-		prtd->adma_saved.ctrl = TDCR(base) & ~TDCR_CHANEN;
-		prtd->adma_saved.chan_pri = TDCP(base);
-		prtd->adma_saved.curr_desc_ptr = TDCDPR(base);
-		prtd->adma_saved.intr_mask = TDIMR(base);
-		prtd->adma_saved.intr_status = TDISR(base);
+			__func__, dai->name, stream, prtd->dma_ch, prtd);
 
 		memcpy(prtd->sram_saved, (void *)prtd->sram_virt,
 			MMP2_ADMA_BUF_SIZE + MMP2_ADMA_DESC_SIZE);
@@ -534,7 +543,7 @@ static int mmp2_pcm_resume(struct snd_soc_dai *dai)
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_runtime *runtime;
 	struct mmp2_runtime_data *prtd;
-	u32 base, ch;
+	u32 ch;
 	int stream;
 
 	for (stream = 0; stream < 2; stream++) {
@@ -545,18 +554,18 @@ static int mmp2_pcm_resume(struct snd_soc_dai *dai)
 			continue;
 
 		prtd = runtime->private_data;
-		ch = prtd->adma_ch;
+		ch = prtd->dma_ch;
 
 		pr_debug("%s: dai name %s stream %d dma ch %d prtd %p\n",
-			__func__, dai->name, stream, prtd->adma_ch, prtd);
+			__func__, dai->name, stream, prtd->dma_ch, prtd);
 
-		base = mmp_get_dma_reg_base(ch);
+		dmaengine_slave_config(prtd->dma_chan, &(prtd->slave_config));
 
-		/* MMP2 only uses chain mode */
-		TDNDPR(base) = prtd->adma_saved.next_desc_ptr;
-		TDCP(base)   = prtd->adma_saved.chan_pri;
-		TDIMR(base)  = prtd->adma_saved.intr_mask;
-		TDCR(base)   = prtd->adma_saved.ctrl;
+		prtd->dma_chan->device->device_prep_dma_cyclic(
+			prtd->dma_chan, prtd->sram_phys,
+			prtd->sram_size, prtd->blk_size,
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			DMA_TO_DEVICE : DMA_FROM_DEVICE);
 
 		memcpy((void *)prtd->sram_virt, prtd->sram_saved,
 			MMP2_ADMA_BUF_SIZE + MMP2_ADMA_DESC_SIZE);
diff --git a/sound/soc/pxa/mmp-pcm.h b/sound/soc/pxa/mmp-pcm.h
index ab03e9d..909d61a 100644
--- a/sound/soc/pxa/mmp-pcm.h
+++ b/sound/soc/pxa/mmp-pcm.h
@@ -44,7 +44,8 @@ struct mmp2_adma_registers {
 };
 
 struct mmp2_runtime_data {
-	int adma_ch;
+	int dma_ch;
+	struct mmp_tdma_data tdma_data;
 	struct mmp2_adma_params *params;
 	void *adma_desc_array;
 	dma_addr_t adma_desc_array_phys;
@@ -75,6 +76,7 @@ struct mmp2_runtime_data {
 
 	struct mmp2_adma_registers adma_saved;
 	char sram_saved[MMP2_ADMA_TOTAL_SIZE];
+	struct dma_slave_config slave_config;
 };
 
 extern struct snd_soc_platform_driver mmp2_soc_platform;
diff --git a/sound/soc/pxa/mmp2-squ.c b/sound/soc/pxa/mmp2-squ.c
index 6d3cc10..4b9bb3b 100644
--- a/sound/soc/pxa/mmp2-squ.c
+++ b/sound/soc/pxa/mmp2-squ.c
@@ -27,6 +27,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -131,21 +132,25 @@ static int mmp2_sram_mmap_noncached(struct vm_area_struct *vma,
 	return ret;
 }
 
-static void mmp2_adma_irq(int dma_ch, void *dev_id)
+static void mmp2_adma_irq(void *data)
 {
-	struct snd_pcm_substream *substream = dev_id;
-	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
+	struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
 
-	u32 base_register = mmp_get_dma_reg_base(dma_ch);
-	if (base_register) {
-		if (TDISR(base_register) & 0x1)
-			snd_pcm_period_elapsed(substream);
-		else {
-			printk(KERN_ERR "%s: SQU error on channel %d\n",
-			       prtd->params->name, dma_ch);
-		}
-		TDISR(base_register) = 0;
-	}
+	snd_pcm_period_elapsed(substream);
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct mmp2_runtime_data *prtd = param;
+
+	if (!mmp_adma_is_this_type(chan))
+		return false;
+
+	if (!mmp_tdma_is_specific_chan(chan, prtd->dma_ch))
+		return false;
+
+	chan->private = &prtd->tdma_data;
+	return true;
 }
 
 static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -159,6 +164,9 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	size_t period = params_period_bytes(params);
 	struct mmp_tdma_desc *adma_desc;
 	dma_addr_t dma_buff_phys, next_desc_phys;
+	struct dma_slave_config slave_config;
+	dma_cap_mask_t mask;
+	enum dma_slave_buswidth buswidth;
 	int ret;
 
 	dma = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
@@ -168,25 +176,47 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	if (!dma)
 		return 0;
 
-	/* this may get called several times by oss emulation
-	 * with different params */
-	if (prtd->params == NULL) {
-		prtd->params = dma;
-		ret = mmp_request_dma(prtd->params->name, dma->dma_ch,
-					 mmp2_adma_irq, substream);
-		if (ret < 0)
-			return ret;
-
-		prtd->adma_ch = ret;
-	} else if (prtd->params != dma) {
-		mmp_free_dma(prtd->adma_ch);
-		prtd->params = dma;
-		ret = mmp_request_dma(prtd->params->name, dma->dma_ch,
-					 mmp2_adma_irq, substream);
-		if (ret < 0)
-			return ret;
-
-		prtd->adma_ch = ret;
+	prtd->dma_ch = dma->dma_ch;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		prtd->tdma_data.bus_size = TDCR_SSZ_8_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_16_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_24_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_32_BITS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	prtd->tdma_data.pack_mod = true;
+
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+	prtd->dma_chan = dma_request_channel(mask, filter, prtd);
+	if (!prtd->dma_chan)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		slave_config.direction    = DMA_TO_DEVICE;
+		slave_config.dst_addr     = dma->dev_addr;
+		slave_config.dst_maxburst = TDCR_BURSTSZ_4B;
+	} else {
+		slave_config.direction	  = DMA_FROM_DEVICE;
+		slave_config.src_addr	  = dma->dev_addr;
+		slave_config.src_maxburst = TDCR_BURSTSZ_4B;
+	}
+
+	ret = dmaengine_slave_config(prtd->dma_chan, &slave_config);
+	if (ret) {
+		printk(KERN_ERR "%s: dmaengine slave config err.\n", __func__);
+		return ret;
 	}
 
 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
@@ -196,25 +226,19 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	dma_buff_phys = runtime->dma_addr;
 
 	adma_desc = prtd->adma_desc_array;
-	do {
-		next_desc_phys += sizeof(struct mmp_tdma_desc);
-
-		adma_desc->nxt_desc = next_desc_phys;
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			adma_desc->src_addr = dma_buff_phys;
-			adma_desc->dst_addr = prtd->params->dev_addr;
-		} else {
-			adma_desc->src_addr = prtd->params->dev_addr;
-			adma_desc->dst_addr = dma_buff_phys;
-		}
-		if (period > totsize)
-			period = totsize;
-		adma_desc->byte_cnt = period;
-		adma_desc++;
-		dma_buff_phys += period;
-
-	} while (totsize -= period);
-	adma_desc[-1].nxt_desc = prtd->adma_desc_array_phys;
+
+	prtd->desc = prtd->dma_chan->device->device_prep_dma_cyclic(
+			prtd->dma_chan, dma_buff_phys, totsize, period,
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			DMA_TO_DEVICE : DMA_FROM_DEVICE);
+	if (!prtd->desc) {
+		dev_err(&prtd->dma_chan->dev->device,
+				"cannot prepare slave dma\n");
+		return -EINVAL;
+	}
+
+	prtd->desc->callback = mmp2_adma_irq;
+	prtd->desc->callback_param = substream;
 
 	return 0;
 }
@@ -223,10 +247,9 @@ static int mmp2_pcm_hw_free(struct snd_pcm_substream *substream)
 {
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
 
-	if (prtd->adma_ch != -1) {
-		snd_pcm_set_runtime_buffer(substream, NULL);
-		mmp_free_dma(prtd->adma_ch);
-		prtd->adma_ch = -1;
+	if (prtd->dma_chan) {
+		dma_release_channel(prtd->dma_chan);
+		prtd->dma_chan = NULL;
 	}
 
 	return 0;
@@ -234,50 +257,31 @@ static int mmp2_pcm_hw_free(struct snd_pcm_substream *substream)
 
 static int mmp2_pcm_prepare(struct snd_pcm_substream *substream)
 {
-	int ret = 0;
-	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
-
-	u32 base_register = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (base_register) {
-		TDCR(base_register) = (prtd->params->dcmd)
-		    & (~TDCR_CHANEN);
-		TDIMR(base_register) = TDIMR_COMP;
-	} else
-		ret = -EINVAL;
-
-	return ret;
+	return 0;
 }
 
 static int mmp2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
 	int ret = 0;
-	u32 base_register;
-
-	base_register = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (!base_register)
-		return -EINVAL;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-		TDNDPR(base_register) = prtd->adma_desc_array_phys;
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		TDCR(base_register) = prtd->params->dcmd;
-		wake_up(&dma_wq);
+		dmaengine_terminate_all(prtd->dma_chan);
 		break;
 
 	case SNDRV_PCM_TRIGGER_RESUME:
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		TDNDPR(base_register) = prtd->adma_desc_array_phys;
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	default:
@@ -294,9 +298,7 @@ static snd_pcm_uframes_t mmp2_pcm_pointer(struct snd_pcm_substream *substream)
 	dma_addr_t ptr;
 	snd_pcm_uframes_t x;
 
-	u32 base_register = mmp_get_dma_reg_base(prtd->adma_ch);
-	ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-	    TDSAR(base_register) : TDDAR(base_register);
+	ptr = mmp_tdma_chan_get_ptr(prtd->dma_chan);
 
 	x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
 
@@ -344,23 +346,11 @@ static int mmp2_pcm_open(struct snd_pcm_substream *substream)
 		goto out;
 	}
 
-	/*
-	 * avoid sram fragment, allocate dma buffer and
-	 * dma desc list at the same time.
-	 */
-	prtd->adma_desc_array = sram_alloc("audio sram", PAGE_SIZE,
-				    (dma_addr_t *)&prtd->adma_desc_array_phys);
-	if (!prtd->adma_desc_array) {
-		ret = -ENOMEM;
-		goto err1;
-	}
-
 	runtime->private_data = prtd;
+	prtd->dma_ch = -1;
 
 	return 0;
 
-err1:
-	kfree(prtd);
 out:
 	return ret;
 }
@@ -370,8 +360,6 @@ static int mmp2_pcm_close(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct mmp2_runtime_data *prtd = runtime->private_data;
 
-	sram_free("audio sram", (void *)prtd->adma_desc_array, PAGE_SIZE);
-
 	kfree(prtd);
 	runtime->private_data = NULL;
 	return 0;
diff --git a/sound/soc/pxa/mmp2-squ.h b/sound/soc/pxa/mmp2-squ.h
index 4f03bb4..ff8a832 100644
--- a/sound/soc/pxa/mmp2-squ.h
+++ b/sound/soc/pxa/mmp2-squ.h
@@ -15,6 +15,8 @@
 #ifndef _MMP2_SQU_H
 #define _MMP2_SQU_H
 
+#include <mach/mmp_dma.h>
+
 #define MMP2_ADMA_BUF_SIZE		(PAGE_SIZE)
 #define MMP2_ADMA_DESC_SIZE		(1024)
 #define MMP2_DDR_BUF_SIZE		(MMP2_ADMA_BUF_SIZE << 4)
@@ -42,8 +44,8 @@ struct mmp2_adma_registers {
 };
 
 struct mmp2_runtime_data {
-	int adma_ch;
-	struct mmp2_adma_params *params;
+	int dma_ch;
+	struct mmp_tdma_data tdma_data;
 	void *adma_desc_array;
 	dma_addr_t adma_desc_array_phys;
 
@@ -69,6 +71,8 @@ struct mmp2_runtime_data {
 	u32 blk_size;
 
 	struct snd_pcm_substream *substream;
+	struct dma_chan *dma_chan;
+	struct dma_async_tx_descriptor *desc;
 
 	struct mmp2_adma_registers adma_saved;
 	char sram_saved[MMP2_ADMA_TOTAL_SIZE];
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 10/13] dmaengine:audio:mmp audio use generic dma engine APIs
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.change mmp audio to use dma engine

Change-Id: I7be32501b87927a6d05ed34f1c73242e1c60517a
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 sound/soc/pxa/mmp-pcm.c  |  223 ++++++++++++++++++++++++----------------------
 sound/soc/pxa/mmp-pcm.h  |    4 +-
 sound/soc/pxa/mmp2-squ.c |  182 ++++++++++++++++++--------------------
 sound/soc/pxa/mmp2-squ.h |    8 ++-
 4 files changed, 210 insertions(+), 207 deletions(-)

diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 7469e19..0e322a2 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -137,7 +137,7 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 	char *src, *dst;
 	u32 point, rest;
 	int num, i;
-	u32 base;
+
 	pr_debug("%s: copy begin, sram_blk_idx = %d rbuf_blk_idx = %d "
 		 "sync_blk = %d", __func__,
 		 prtd->sram_blk_idx,  prtd->rbuf_blk_idx,
@@ -146,13 +146,12 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 	if (!prtd->sync_blk)
 		return;
 
-	base = mmp_get_dma_reg_base(prtd->adma_ch);
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		/*
 		 * adjust sram index if dma has run ahead
 		 * rather than memory copy speed
 		 */
-		point = TDSAR(base) - prtd->sram_phys;
+		point = mmp_tdma_chan_get_ptr(prtd->dma_chan) - prtd->sram_phys;
 		rest = mmp2_pcm_point_is_in_region(prtd->sram_size,
 				prtd->sram_blk_idx * prtd->blk_size,
 				prtd->sync_blk * prtd->blk_size,
@@ -173,7 +172,7 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 		 * by dma polluted, will copy these periods data
 		 * as possible.
 		 */
-		point = TDSAR(base) - prtd->sram_phys;
+		point = mmp_tdma_chan_get_ptr(prtd->dma_chan) - prtd->sram_phys;
 		rest = mmp2_pcm_point_is_in_region(prtd->sram_size,
 				prtd->sram_blk_idx * prtd->blk_size,
 				prtd->sync_blk * prtd->blk_size,
@@ -193,7 +192,6 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 
 	num = prtd->sync_blk;
 	for (i = 0; i < num; i++) {
-
 		mmp2_pcm_copy_data((int *)dst, (int *)src,
 			prtd->blk_size);
 
@@ -216,18 +214,24 @@ static void mmp2_pcm_sync_sram_with_ddr(struct snd_pcm_substream *substream)
 	return;
 }
 
-static void mmp2_pcm_adma_irq(int dma_ch, void *data)
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct mmp2_runtime_data *prtd = param;
+
+	if (!mmp_adma_is_this_type(chan))
+		return false;
+
+	if (!mmp_tdma_is_specific_chan(chan, prtd->dma_ch))
+		return false;
+
+	chan->private = &prtd->tdma_data;
+	return true;
+}
+
+static void mmp2_pcm_adma_irq(void *data)
 {
 	struct snd_pcm_substream *substream = data;
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
-	u32 base = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (!base)
-		return;
-
-	if (!(TDISR(base) & 0x1))
-		return;
-	/* clear adma irq status */
-	TDISR(base) = 0;
 
 	/* sync sram with ring buf */
 	prtd->sync_blk = min(prtd->sync_blk + 1, prtd->sram_blk_num);
@@ -247,11 +251,12 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	size_t totsize = params_buffer_bytes(params);
 	size_t period = params_period_bytes(params);
 	struct mmp_tdma_desc *adma_desc;
-	dma_addr_t dma_buff_phys, next_desc_phys;
+	dma_addr_t adma_buff_phys, next_desc_phys;
 	int ret;
+	dma_cap_mask_t mask;
+
+	dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 
-	dma = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
-	    rtd->cpu_dai->playback_dma_data : rtd->cpu_dai->capture_dma_data;
 	/* return if this is a bufferless transfer e.g.
 	 * codec <--> BT codec or GSM modem -- lg FIXME */
 	if (!dma)
@@ -261,27 +266,66 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	 * this may get called several times by oss
 	 * emulation with different params
 	 */
-	if (prtd->params == NULL) {
-		prtd->params = dma;
-		ret = mmp_request_dma(prtd->params->name, dma->dma_ch,
-					 mmp2_pcm_adma_irq, substream);
-		if (ret < 0)
-			return ret;
-
-		prtd->adma_ch = ret;
-	} else if (prtd->params != dma) {
-		mmp_free_dma(prtd->adma_ch);
-		prtd->params = dma;
-		ret = mmp_request_dma(prtd->params->name, dma->dma_ch,
-					 mmp2_pcm_adma_irq, substream);
-		if (ret < 0)
-			return ret;
-
-		prtd->adma_ch = ret;
+	prtd->dma_ch = dma->dma_ch;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		prtd->tdma_data.bus_size = TDCR_SSZ_8_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_16_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_24_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_32_BITS;
+		break;
+	default:
+		return -EINVAL;
 	}
+	prtd->tdma_data.pack_mod = true;
+
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+	prtd->dma_chan = dma_request_channel(mask, filter, prtd);
+	if (!prtd->dma_chan)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		prtd->slave_config.direction    = DMA_TO_DEVICE;
+		prtd->slave_config.dst_addr     = dma->dev_addr;
+		prtd->slave_config.dst_maxburst = TDCR_BURSTSZ_4B;
+	} else {
+		prtd->slave_config.direction	  = DMA_FROM_DEVICE;
+		prtd->slave_config.src_addr	  = dma->dev_addr;
+		prtd->slave_config.src_maxburst = TDCR_BURSTSZ_4B;
+	}
+
+	ret = dmaengine_slave_config(prtd->dma_chan, &(prtd->slave_config));
+	if (ret) {
+		printk(KERN_ERR "%s: dmaengine slave config err.\n", __func__);
+		return ret;
+	}
+
 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-	runtime->dma_bytes = totsize;
 
+	/*
+	 * init prtd value.
+	 * the driver has alloc from sram for dma transfer,
+	 * size = MMP2_ADMA_BUF_SIZE (now is equal to PAGE_SIZE).
+	 * and driver has alloc ddr buffer for mmapping to userspace,
+	 * and the size = MMP2_DDR_BUF_SIZE, now is 64KB.
+	 *
+	 * but should note the used buffer size is specified
+	 * by userspace's app, which is calculated by the
+	 * periold_size and buffer_size. The buffer size
+	 * is even less than the buffer size which allocated
+	 * from the sram.
+	 * So at this point should adjust the parameters
+	 * according to user's setting.
+	 */
 	prtd->blk_size     = period;
 	prtd->rbuf_virt    = (u32)runtime->dma_area;
 	prtd->rbuf_phys    = (u32)runtime->dma_addr;
@@ -294,31 +338,22 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	prtd->sync_blk     = 0;
 
 	totsize        = prtd->sram_size;
-	dma_buff_phys  = prtd->sram_phys;
+	adma_buff_phys = prtd->sram_phys;
 	next_desc_phys = prtd->adma_desc_array_phys;
 	adma_desc      = prtd->adma_desc_array;
 
-	do {
-		next_desc_phys += sizeof(struct mmp_tdma_desc);
-
-		adma_desc->nxt_desc = next_desc_phys;
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			adma_desc->src_addr = dma_buff_phys;
-			adma_desc->dst_addr = prtd->params->dev_addr;
-		} else {
-			adma_desc->src_addr = prtd->params->dev_addr;
-			adma_desc->dst_addr = dma_buff_phys;
-		}
-		if (period > totsize)
-			period = totsize;
-		adma_desc->byte_cnt = period;
-		adma_desc++;
-		dma_buff_phys += period;
-
-	} while (totsize -= period);
-	adma_desc[-1].nxt_desc = prtd->adma_desc_array_phys;
+	prtd->desc = prtd->dma_chan->device->device_prep_dma_cyclic(
+			prtd->dma_chan, adma_buff_phys, totsize, period,
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			DMA_TO_DEVICE : DMA_FROM_DEVICE);
+	if (!prtd->desc) {
+		dev_err(&prtd->dma_chan->dev->device,
+			"cannot prepare slave dma\n");
+		return -EINVAL;
+	}
 
-	mmp2_pcm_dump_adma_list(prtd);
+	prtd->desc->callback = mmp2_pcm_adma_irq;
+	prtd->desc->callback_param = substream;
 
 	return 0;
 }
@@ -327,10 +362,9 @@ static int mmp2_pcm_hw_free(struct snd_pcm_substream *substream)
 {
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
 
-	if (prtd->adma_ch != -1) {
-		snd_pcm_set_runtime_buffer(substream, NULL);
-		mmp_free_dma(prtd->adma_ch);
-		prtd->adma_ch = -1;
+	if (prtd->dma_chan) {
+		dma_release_channel(prtd->dma_chan);
+		prtd->dma_chan = NULL;
 	}
 
 	return 0;
@@ -338,16 +372,6 @@ static int mmp2_pcm_hw_free(struct snd_pcm_substream *substream)
 
 static int mmp2_pcm_prepare(struct snd_pcm_substream *substream)
 {
-	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
-	u32 base;
-
-	base = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (!base)
-		return -EINVAL;
-
-	TDCR(base)  = (prtd->params->dcmd) & (~TDCR_CHANEN);
-	TDIMR(base) = TDIMR_COMP;
-
 	return 0;
 }
 
@@ -356,11 +380,6 @@ static int mmp2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
 	unsigned long flags;
 	int ret = 0;
-	u32 base_register;
-
-	base_register = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (!base_register)
-		return -EINVAL;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
@@ -378,24 +397,21 @@ static int mmp2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 		mmp2_pcm_sync_sram_with_ddr(substream);
 		spin_unlock_irqrestore(&prtd->lock, flags);
 
-		TDNDPR(base_register) = prtd->adma_desc_array_phys;
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		TDCR(base_register) = prtd->params->dcmd;
-		wake_up(&dma_wq);
+		dmaengine_terminate_all(prtd->dma_chan);
 		break;
 
 	case SNDRV_PCM_TRIGGER_RESUME:
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		TDNDPR(base_register) = prtd->adma_desc_array_phys;
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	default:
@@ -460,7 +476,7 @@ static int mmp2_pcm_open(struct snd_pcm_substream *substream)
 
 	prtd->substream = substream;
 	runtime->private_data = prtd;
-	prtd->adma_ch = -1;
+	prtd->dma_ch = -1;
 
 	/*
 	 * avoid sram fragment, allocate dma buffer and
@@ -471,6 +487,11 @@ static int mmp2_pcm_open(struct snd_pcm_substream *substream)
 			MMP2_ADMA_BUF_SIZE + MMP2_ADMA_DESC_SIZE,
 			(dma_addr_t *)&prtd->sram_phys);
 
+	if (!prtd->sram_virt) {
+		printk(KERN_ERR "failed to map audio sram\n");
+		goto alloc_sram_err;
+	}
+
 	prtd->adma_desc_array = (void *)(prtd->sram_virt + MMP2_ADMA_BUF_SIZE);
 	prtd->adma_desc_array_phys = (dma_addr_t)(prtd->sram_phys
 					+ MMP2_ADMA_BUF_SIZE);
@@ -492,7 +513,7 @@ static int mmp2_pcm_suspend(struct snd_soc_dai *dai)
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_runtime *runtime;
 	struct mmp2_runtime_data *prtd;
-	u32 base, ch;
+	u32 ch;
 	int stream;
 
 	for (stream = 0; stream < 2; stream++) {
@@ -504,22 +525,10 @@ static int mmp2_pcm_suspend(struct snd_soc_dai *dai)
 			continue;
 
 		prtd = runtime->private_data;
-		ch = prtd->adma_ch;
+		ch = prtd->dma_ch;
 
 		pr_debug("%s: dai name %s stream %d dma ch %d prtd %p\n",
-			__func__, dai->name, stream, prtd->adma_ch, prtd);
-
-		/* mmp2 only uses chain mode */
-		base = mmp_get_dma_reg_base(ch);
-
-		prtd->adma_saved.src_addr  = TDSAR(base);
-		prtd->adma_saved.dest_addr = TDDAR(base);
-		prtd->adma_saved.next_desc_ptr = TDNDPR(base);
-		prtd->adma_saved.ctrl = TDCR(base) & ~TDCR_CHANEN;
-		prtd->adma_saved.chan_pri = TDCP(base);
-		prtd->adma_saved.curr_desc_ptr = TDCDPR(base);
-		prtd->adma_saved.intr_mask = TDIMR(base);
-		prtd->adma_saved.intr_status = TDISR(base);
+			__func__, dai->name, stream, prtd->dma_ch, prtd);
 
 		memcpy(prtd->sram_saved, (void *)prtd->sram_virt,
 			MMP2_ADMA_BUF_SIZE + MMP2_ADMA_DESC_SIZE);
@@ -534,7 +543,7 @@ static int mmp2_pcm_resume(struct snd_soc_dai *dai)
 	struct snd_pcm_substream *substream;
 	struct snd_pcm_runtime *runtime;
 	struct mmp2_runtime_data *prtd;
-	u32 base, ch;
+	u32 ch;
 	int stream;
 
 	for (stream = 0; stream < 2; stream++) {
@@ -545,18 +554,18 @@ static int mmp2_pcm_resume(struct snd_soc_dai *dai)
 			continue;
 
 		prtd = runtime->private_data;
-		ch = prtd->adma_ch;
+		ch = prtd->dma_ch;
 
 		pr_debug("%s: dai name %s stream %d dma ch %d prtd %p\n",
-			__func__, dai->name, stream, prtd->adma_ch, prtd);
+			__func__, dai->name, stream, prtd->dma_ch, prtd);
 
-		base = mmp_get_dma_reg_base(ch);
+		dmaengine_slave_config(prtd->dma_chan, &(prtd->slave_config));
 
-		/* MMP2 only uses chain mode */
-		TDNDPR(base) = prtd->adma_saved.next_desc_ptr;
-		TDCP(base)   = prtd->adma_saved.chan_pri;
-		TDIMR(base)  = prtd->adma_saved.intr_mask;
-		TDCR(base)   = prtd->adma_saved.ctrl;
+		prtd->dma_chan->device->device_prep_dma_cyclic(
+			prtd->dma_chan, prtd->sram_phys,
+			prtd->sram_size, prtd->blk_size,
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			DMA_TO_DEVICE : DMA_FROM_DEVICE);
 
 		memcpy((void *)prtd->sram_virt, prtd->sram_saved,
 			MMP2_ADMA_BUF_SIZE + MMP2_ADMA_DESC_SIZE);
diff --git a/sound/soc/pxa/mmp-pcm.h b/sound/soc/pxa/mmp-pcm.h
index ab03e9d..909d61a 100644
--- a/sound/soc/pxa/mmp-pcm.h
+++ b/sound/soc/pxa/mmp-pcm.h
@@ -44,7 +44,8 @@ struct mmp2_adma_registers {
 };
 
 struct mmp2_runtime_data {
-	int adma_ch;
+	int dma_ch;
+	struct mmp_tdma_data tdma_data;
 	struct mmp2_adma_params *params;
 	void *adma_desc_array;
 	dma_addr_t adma_desc_array_phys;
@@ -75,6 +76,7 @@ struct mmp2_runtime_data {
 
 	struct mmp2_adma_registers adma_saved;
 	char sram_saved[MMP2_ADMA_TOTAL_SIZE];
+	struct dma_slave_config slave_config;
 };
 
 extern struct snd_soc_platform_driver mmp2_soc_platform;
diff --git a/sound/soc/pxa/mmp2-squ.c b/sound/soc/pxa/mmp2-squ.c
index 6d3cc10..4b9bb3b 100644
--- a/sound/soc/pxa/mmp2-squ.c
+++ b/sound/soc/pxa/mmp2-squ.c
@@ -27,6 +27,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -131,21 +132,25 @@ static int mmp2_sram_mmap_noncached(struct vm_area_struct *vma,
 	return ret;
 }
 
-static void mmp2_adma_irq(int dma_ch, void *dev_id)
+static void mmp2_adma_irq(void *data)
 {
-	struct snd_pcm_substream *substream = dev_id;
-	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
+	struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
 
-	u32 base_register = mmp_get_dma_reg_base(dma_ch);
-	if (base_register) {
-		if (TDISR(base_register) & 0x1)
-			snd_pcm_period_elapsed(substream);
-		else {
-			printk(KERN_ERR "%s: SQU error on channel %d\n",
-			       prtd->params->name, dma_ch);
-		}
-		TDISR(base_register) = 0;
-	}
+	snd_pcm_period_elapsed(substream);
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct mmp2_runtime_data *prtd = param;
+
+	if (!mmp_adma_is_this_type(chan))
+		return false;
+
+	if (!mmp_tdma_is_specific_chan(chan, prtd->dma_ch))
+		return false;
+
+	chan->private = &prtd->tdma_data;
+	return true;
 }
 
 static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -159,6 +164,9 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	size_t period = params_period_bytes(params);
 	struct mmp_tdma_desc *adma_desc;
 	dma_addr_t dma_buff_phys, next_desc_phys;
+	struct dma_slave_config slave_config;
+	dma_cap_mask_t mask;
+	enum dma_slave_buswidth buswidth;
 	int ret;
 
 	dma = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
@@ -168,25 +176,47 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	if (!dma)
 		return 0;
 
-	/* this may get called several times by oss emulation
-	 * with different params */
-	if (prtd->params == NULL) {
-		prtd->params = dma;
-		ret = mmp_request_dma(prtd->params->name, dma->dma_ch,
-					 mmp2_adma_irq, substream);
-		if (ret < 0)
-			return ret;
-
-		prtd->adma_ch = ret;
-	} else if (prtd->params != dma) {
-		mmp_free_dma(prtd->adma_ch);
-		prtd->params = dma;
-		ret = mmp_request_dma(prtd->params->name, dma->dma_ch,
-					 mmp2_adma_irq, substream);
-		if (ret < 0)
-			return ret;
-
-		prtd->adma_ch = ret;
+	prtd->dma_ch = dma->dma_ch;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		prtd->tdma_data.bus_size = TDCR_SSZ_8_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_16_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_24_BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		prtd->tdma_data.bus_size = TDCR_SSZ_32_BITS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	prtd->tdma_data.pack_mod = true;
+
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_CYCLIC, mask);
+	prtd->dma_chan = dma_request_channel(mask, filter, prtd);
+	if (!prtd->dma_chan)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		slave_config.direction    = DMA_TO_DEVICE;
+		slave_config.dst_addr     = dma->dev_addr;
+		slave_config.dst_maxburst = TDCR_BURSTSZ_4B;
+	} else {
+		slave_config.direction	  = DMA_FROM_DEVICE;
+		slave_config.src_addr	  = dma->dev_addr;
+		slave_config.src_maxburst = TDCR_BURSTSZ_4B;
+	}
+
+	ret = dmaengine_slave_config(prtd->dma_chan, &slave_config);
+	if (ret) {
+		printk(KERN_ERR "%s: dmaengine slave config err.\n", __func__);
+		return ret;
 	}
 
 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
@@ -196,25 +226,19 @@ static int mmp2_pcm_hw_params(struct snd_pcm_substream *substream,
 	dma_buff_phys = runtime->dma_addr;
 
 	adma_desc = prtd->adma_desc_array;
-	do {
-		next_desc_phys += sizeof(struct mmp_tdma_desc);
-
-		adma_desc->nxt_desc = next_desc_phys;
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-			adma_desc->src_addr = dma_buff_phys;
-			adma_desc->dst_addr = prtd->params->dev_addr;
-		} else {
-			adma_desc->src_addr = prtd->params->dev_addr;
-			adma_desc->dst_addr = dma_buff_phys;
-		}
-		if (period > totsize)
-			period = totsize;
-		adma_desc->byte_cnt = period;
-		adma_desc++;
-		dma_buff_phys += period;
-
-	} while (totsize -= period);
-	adma_desc[-1].nxt_desc = prtd->adma_desc_array_phys;
+
+	prtd->desc = prtd->dma_chan->device->device_prep_dma_cyclic(
+			prtd->dma_chan, dma_buff_phys, totsize, period,
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			DMA_TO_DEVICE : DMA_FROM_DEVICE);
+	if (!prtd->desc) {
+		dev_err(&prtd->dma_chan->dev->device,
+				"cannot prepare slave dma\n");
+		return -EINVAL;
+	}
+
+	prtd->desc->callback = mmp2_adma_irq;
+	prtd->desc->callback_param = substream;
 
 	return 0;
 }
@@ -223,10 +247,9 @@ static int mmp2_pcm_hw_free(struct snd_pcm_substream *substream)
 {
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
 
-	if (prtd->adma_ch != -1) {
-		snd_pcm_set_runtime_buffer(substream, NULL);
-		mmp_free_dma(prtd->adma_ch);
-		prtd->adma_ch = -1;
+	if (prtd->dma_chan) {
+		dma_release_channel(prtd->dma_chan);
+		prtd->dma_chan = NULL;
 	}
 
 	return 0;
@@ -234,50 +257,31 @@ static int mmp2_pcm_hw_free(struct snd_pcm_substream *substream)
 
 static int mmp2_pcm_prepare(struct snd_pcm_substream *substream)
 {
-	int ret = 0;
-	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
-
-	u32 base_register = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (base_register) {
-		TDCR(base_register) = (prtd->params->dcmd)
-		    & (~TDCR_CHANEN);
-		TDIMR(base_register) = TDIMR_COMP;
-	} else
-		ret = -EINVAL;
-
-	return ret;
+	return 0;
 }
 
 static int mmp2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct mmp2_runtime_data *prtd = substream->runtime->private_data;
 	int ret = 0;
-	u32 base_register;
-
-	base_register = mmp_get_dma_reg_base(prtd->adma_ch);
-	if (!base_register)
-		return -EINVAL;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
-		TDNDPR(base_register) = prtd->adma_desc_array_phys;
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		TDCR(base_register) = prtd->params->dcmd;
-		wake_up(&dma_wq);
+		dmaengine_terminate_all(prtd->dma_chan);
 		break;
 
 	case SNDRV_PCM_TRIGGER_RESUME:
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		TDNDPR(base_register) = prtd->adma_desc_array_phys;
-		TDCR(base_register) = prtd->params->dcmd | TDCR_CHANEN;
+		dmaengine_submit(prtd->desc);
 		break;
 
 	default:
@@ -294,9 +298,7 @@ static snd_pcm_uframes_t mmp2_pcm_pointer(struct snd_pcm_substream *substream)
 	dma_addr_t ptr;
 	snd_pcm_uframes_t x;
 
-	u32 base_register = mmp_get_dma_reg_base(prtd->adma_ch);
-	ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-	    TDSAR(base_register) : TDDAR(base_register);
+	ptr = mmp_tdma_chan_get_ptr(prtd->dma_chan);
 
 	x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
 
@@ -344,23 +346,11 @@ static int mmp2_pcm_open(struct snd_pcm_substream *substream)
 		goto out;
 	}
 
-	/*
-	 * avoid sram fragment, allocate dma buffer and
-	 * dma desc list at the same time.
-	 */
-	prtd->adma_desc_array = sram_alloc("audio sram", PAGE_SIZE,
-				    (dma_addr_t *)&prtd->adma_desc_array_phys);
-	if (!prtd->adma_desc_array) {
-		ret = -ENOMEM;
-		goto err1;
-	}
-
 	runtime->private_data = prtd;
+	prtd->dma_ch = -1;
 
 	return 0;
 
-err1:
-	kfree(prtd);
 out:
 	return ret;
 }
@@ -370,8 +360,6 @@ static int mmp2_pcm_close(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct mmp2_runtime_data *prtd = runtime->private_data;
 
-	sram_free("audio sram", (void *)prtd->adma_desc_array, PAGE_SIZE);
-
 	kfree(prtd);
 	runtime->private_data = NULL;
 	return 0;
diff --git a/sound/soc/pxa/mmp2-squ.h b/sound/soc/pxa/mmp2-squ.h
index 4f03bb4..ff8a832 100644
--- a/sound/soc/pxa/mmp2-squ.h
+++ b/sound/soc/pxa/mmp2-squ.h
@@ -15,6 +15,8 @@
 #ifndef _MMP2_SQU_H
 #define _MMP2_SQU_H
 
+#include <mach/mmp_dma.h>
+
 #define MMP2_ADMA_BUF_SIZE		(PAGE_SIZE)
 #define MMP2_ADMA_DESC_SIZE		(1024)
 #define MMP2_DDR_BUF_SIZE		(MMP2_ADMA_BUF_SIZE << 4)
@@ -42,8 +44,8 @@ struct mmp2_adma_registers {
 };
 
 struct mmp2_runtime_data {
-	int adma_ch;
-	struct mmp2_adma_params *params;
+	int dma_ch;
+	struct mmp_tdma_data tdma_data;
 	void *adma_desc_array;
 	dma_addr_t adma_desc_array_phys;
 
@@ -69,6 +71,8 @@ struct mmp2_runtime_data {
 	u32 blk_size;
 
 	struct snd_pcm_substream *substream;
+	struct dma_chan *dma_chan;
+	struct dma_async_tx_descriptor *desc;
 
 	struct mmp2_adma_registers adma_saved;
 	char sram_saved[MMP2_ADMA_TOTAL_SIZE];
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 11/13] DMA:update defconfig for mmp3
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.open DMAC and TDMA config in defconfig

Change-Id: I78876368e43472fe6e86314c438b98e518cfcb87
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/configs/mmp3_defconfig |   19 ++++++++++++++++++-
 1 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/arch/arm/configs/mmp3_defconfig b/arch/arm/configs/mmp3_defconfig
index 4f8b160..3a363f4 100644
--- a/arch/arm/configs/mmp3_defconfig
+++ b/arch/arm/configs/mmp3_defconfig
@@ -2038,7 +2038,24 @@ CONFIG_RTC_INTF_ALARM_DEV=y
 # on-CPU RTC drivers
 #
 CONFIG_RTC_DRV_MMP=y
-# CONFIG_DMADEVICES is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_PXA_DMAC=y
+CONFIG_MMP_TDMA=y
+CONFIG_DMA_ENGINE=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
 # CONFIG_AUXDISPLAY is not set
 CONFIG_UIO=y
 # CONFIG_UIO_PDRV is not set
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 11/13] DMA:update defconfig for mmp3
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.open DMAC and TDMA config in defconfig

Change-Id: I78876368e43472fe6e86314c438b98e518cfcb87
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/configs/mmp3_defconfig |   19 ++++++++++++++++++-
 1 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/arch/arm/configs/mmp3_defconfig b/arch/arm/configs/mmp3_defconfig
index 4f8b160..3a363f4 100644
--- a/arch/arm/configs/mmp3_defconfig
+++ b/arch/arm/configs/mmp3_defconfig
@@ -2038,7 +2038,24 @@ CONFIG_RTC_INTF_ALARM_DEV=y
 # on-CPU RTC drivers
 #
 CONFIG_RTC_DRV_MMP=y
-# CONFIG_DMADEVICES is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_PXA_DMAC=y
+CONFIG_MMP_TDMA=y
+CONFIG_DMA_ENGINE=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
 # CONFIG_AUXDISPLAY is not set
 CONFIG_UIO=y
 # CONFIG_UIO_PDRV is not set
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 12/13] mdma:dmaengine:update mdma for tdma
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1, do some update to add mdma to the
	dma engine as adma

Change-Id: I514b247e0c2301fc48b1f1980562f3c50d6a6455
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 drivers/char/mmp2_mdma.c |  273 ++++++++++++++--------------------------------
 1 files changed, 84 insertions(+), 189 deletions(-)

diff --git a/drivers/char/mmp2_mdma.c b/drivers/char/mmp2_mdma.c
index c16b94a..72fee88 100644
--- a/drivers/char/mmp2_mdma.c
+++ b/drivers/char/mmp2_mdma.c
@@ -37,8 +37,8 @@ static void *dma_desc_array_ch1 = NULL;
 static void *dma_desc_array_ch2 = NULL;
 static dma_addr_t dma_desc_array_phys_ch1;
 static dma_addr_t dma_desc_array_phys_ch2;
-static struct completion complete_ch1;
-static struct completion complete_ch2;
+static struct completion complete_memcpy;
+static struct completion complete_memset;
 
 static unsigned long uva_to_pa(struct mm_struct *mm, unsigned long addr)
 {
@@ -68,43 +68,41 @@ static unsigned long uva_to_pa(struct mm_struct *mm, unsigned long addr)
 	return ret;
 }
 
-static void mdma_irq(int channel, void *data)
+static void mdma_irq(void *data)
 {
-	unsigned int base_register = mmp_get_dma_reg_base(channel);
-
-	if(base_register){
-		if (TDISR(base_register) & 0x1) {
-			if(channel == MDMA1_CH0)
-				complete(&complete_ch1);
-			else
-				complete(&complete_ch2);
-			if(TDISR(base_register) & 0xc)
-				printk(KERN_ERR "MDMA error 0x%x on channel %d \n",
-					TDISR(base_register), channel);
-		} else {
-			printk(KERN_ERR "MDMA error on channel %d \n", channel);
-		}
-		TDISR(base_register) = 0;
-	}
+	struct completion *mdma_complete = data;
+
+	complete(mdma_complete);
 
 	return;
 }
 
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct mmp_tdma_data *tdma_data = param;
+
+	if (!mmp_mdma_is_this_type(chan))
+		return false;
+
+	chan->private = tdma_data;
+
+	return true;
+}
 /*
  * len requires at least 8bytes alignment. 128bytes alignment will get better performance.
  * Alignment: 8, 16, 32, 64, 128. The bigger, the better.
  * source and destination address aslo need alignment with burst size.
  */
+struct mmp_tdma_data tdma_data_pmemcpy;
 unsigned long mdma_pmemcpy(unsigned long pdst, unsigned long psrc, unsigned int len)
 {
-	int ret, dma_ch;
-	unsigned int base_register;
-	unsigned int dcmd, mdma_dcr;
+	int ret;
 	unsigned long len_tmp, len_total;
-	struct pxa910_squ_desc *dma_desc_tmp;
-	dma_addr_t dma_desc_p_tmp;
 	unsigned long srcphyaddr = psrc;
 	unsigned long dstphyaddr = pdst;
+	struct dma_chan *dma_chan;
+	dma_cap_mask_t mask;
+	struct dma_async_tx_descriptor *desc;
 
 	if (srcphyaddr == 0 || dstphyaddr == 0)
 		return -1;
@@ -124,95 +122,44 @@ unsigned long mdma_pmemcpy(unsigned long pdst, unsigned long psrc, unsigned int
 		return -1;
 	}
 
-	ret = mmp_request_dma("mdma_memcpy", MDMA1_CH0,
-					mdma_irq, NULL);
-	if (ret < 0){
-		ret = mmp_request_dma("mdma_memcpy", MDMA1_CH1,
-						mdma_irq, NULL);
-		if (ret < 0){
-			printk(KERN_ERR	"Can't request MDMA for memcpy\n");
-			return -ENODEV;
-		}else{
-			dma_ch = MDMA1_CH1;
-		}
-	}else{
-		dma_ch = MDMA1_CH0;
-	}
-
-	if(dma_ch == MDMA1_CH0){
-		dma_desc_tmp = dma_desc_array_ch1;
-		dma_desc_p_tmp = dma_desc_array_phys_ch1;
-	}else{
-		dma_desc_tmp = dma_desc_array_ch2;
-		dma_desc_p_tmp = dma_desc_array_phys_ch2;
-	}
-
-	len_total = len;
-	while(len_total) {
-		len_tmp = len_total>SINGLE_DESC_TRANS_MAX ?
-				SINGLE_DESC_TRANS_MAX : len_total;
-		dma_desc_tmp->nxt_desc = dma_desc_p_tmp + sizeof(struct pxa910_squ_desc);
-		dma_desc_tmp->src_addr = srcphyaddr;
-		dma_desc_tmp->dst_addr = dstphyaddr;
-		dma_desc_tmp->byte_cnt = len_tmp;
-		if (len_total <= SINGLE_DESC_TRANS_MAX) {
-			dma_desc_tmp->nxt_desc = 0;
-			break;
-		}
-		len_total -= len_tmp;
-		dma_desc_tmp ++;
-		dma_desc_p_tmp += sizeof(struct pxa910_squ_desc);
-		srcphyaddr += len_tmp;
-		dstphyaddr += len_tmp;
-	}
-
 	if (!((len & 0x7f) || (srcphyaddr & 0x7f) || (dstphyaddr & 0x7f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_128B | SDCR_FETCHND;
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_128B;
 	else if (!((len & 0x3f) || (srcphyaddr & 0x3f) || (dstphyaddr & 0x3f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_64B | SDCR_FETCHND;
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_64B;
 	else if (!((len & 0x1f) || (srcphyaddr & 0x1f) || (dstphyaddr & 0x1f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_32B | SDCR_FETCHND;
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_32B;
 	else if (!((len & 0xf) || (srcphyaddr & 0xf) || (dstphyaddr & 0xf)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_16B | SDCR_FETCHND;
-	else
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_8B | SDCR_FETCHND;
-
-	base_register = mmp_get_dma_reg_base(dma_ch);
-	TDCR(base_register)= (dcmd) & (~MDCR_CHANEN);
-	TDIMR(base_register) = MDIMR_COMP;
-	if(dma_ch == MDMA1_CH0)
-		TDNDPR(base_register) = dma_desc_array_phys_ch1;
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_16B;
 	else
-		TDNDPR(base_register) = dma_desc_array_phys_ch2;
-	if(dma_ch == MDMA1_CH0){
-		init_completion(&complete_ch1);
-	}else{
-		init_completion(&complete_ch2);
-	}
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_8B;
 
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_MEMCPY, mask);
+	dma_chan = dma_request_channel(mask, filter, &tdma_data_pmemcpy);
 
-	TDCR(base_register)= dcmd | MDCR_CHANEN;
+	len_total = len;
+	len_tmp = len_total > SINGLE_DESC_TRANS_MAX ?
+				SINGLE_DESC_TRANS_MAX : len_total;
 
-	if(dma_ch == MDMA1_CH0){
-		ret = wait_for_completion_timeout(&complete_ch1, 10*HZ);
-	}else{
-		ret = wait_for_completion_timeout(&complete_ch2, 10*HZ);
-	}
+	desc = dma_chan->device->device_prep_dma_memcpy(
+			dma_chan, dstphyaddr, srcphyaddr, len_total,
+			DMA_BIDIRECTIONAL);
 
-	TDIMR(base_register) = 0;
-	mdma_dcr = TDCR(base_register);
-	if (mdma_dcr & TDCR_CHANACT) {
-		TDCR(base_register) = mdma_dcr | TDCR_ABR;
-	}
-	while (mdma_dcr & TDCR_CHANACT) {
-		mdma_dcr = TDCR(base_register);
-	}
-	mmp_free_dma(dma_ch);
+	/* this completion will be finished after mdma irq */
+	init_completion(&complete_memcpy);
+
+	desc->callback = mdma_irq;
+	desc->callback_param = &complete_memcpy;
+
+	/* start dma transfer */
+	dmaengine_submit(desc);
+
+	ret = wait_for_completion_timeout(&complete_memcpy, 10*HZ);
+
+	/* release dma channel */
+	dma_release_channel(dma_chan);
+	dma_chan = NULL;
 
 	if (ret)
 		return len;
@@ -227,15 +174,15 @@ EXPORT_SYMBOL(mdma_pmemcpy);
  * destination address aslo need alignment with burst size.
  * data requires 32bits.
  */
+struct mmp_tdma_data tdma_data_pmemset;
 unsigned long mdma_pmemset(unsigned long paddr, unsigned long c, unsigned int len)
 {
-	int ret, dma_ch;
-	unsigned int base_register;
-	unsigned int dcmd, mdma_dcr;
+	int ret;
 	unsigned long len_tmp, len_total;
-	struct pxa910_squ_desc *dma_desc_tmp;
-	dma_addr_t dma_desc_p_tmp;
 	unsigned long dstphyaddr = paddr;
+	struct dma_chan *dma_chan;
+	dma_cap_mask_t mask;
+	struct dma_async_tx_descriptor *desc;
 
 	if (dstphyaddr == 0)
 		return -1;
@@ -250,95 +197,43 @@ unsigned long mdma_pmemset(unsigned long paddr, unsigned long c, unsigned int le
 		return -1;
 	}
 
-	ret = mmp_request_dma("mdma_memcpy", MDMA1_CH0,
-					mdma_irq, NULL);
-	if (ret < 0){
-		ret = mmp_request_dma("mdma_memcpy", MDMA1_CH1,
-						mdma_irq, NULL);
-		if (ret < 0){
-			printk(KERN_ERR	"Can't request MDMA for memcpy\n");
-			return -ENODEV;
-		}else{
-			dma_ch = MDMA1_CH1;
-		}
-	}else{
-		dma_ch = MDMA1_CH0;
-	}
-
-	if(dma_ch == MDMA1_CH0){
-		dma_desc_tmp = dma_desc_array_ch1;
-		dma_desc_p_tmp = dma_desc_array_phys_ch1;
-	}else{
-		dma_desc_tmp = dma_desc_array_ch2;
-		dma_desc_p_tmp = dma_desc_array_phys_ch2;
-	}
-
-	len_total = len;
-	while(len_total) {
-		len_tmp = len_total>SINGLE_DESC_TRANS_MAX ?
-				SINGLE_DESC_TRANS_MAX : len_total;
-		dma_desc_tmp->nxt_desc = dma_desc_p_tmp + sizeof(struct pxa910_squ_desc);
-		dma_desc_tmp->src_addr = 0;
-		dma_desc_tmp->dst_addr = dstphyaddr;
-		dma_desc_tmp->byte_cnt = len_tmp;
-		if (len_total <= SINGLE_DESC_TRANS_MAX) {
-			dma_desc_tmp->nxt_desc = 0;
-			break;
-		}
-		len_total -= len_tmp;
-		dma_desc_tmp ++;
-		dma_desc_p_tmp += sizeof(struct pxa910_squ_desc);
-		dstphyaddr += len_tmp;
-	}
-
 	if (!((len & 0x7f) || (dstphyaddr & 0x7f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_128B | SDCR_FETCHND;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_128B;
 	else if (!((len & 0x3f) || (dstphyaddr & 0x3f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_64B | SDCR_FETCHND;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_64B;
 	else if (!((len & 0x1f) || (dstphyaddr & 0x1f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_32B | SDCR_FETCHND;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_32B;
 	else if (!((len & 0xf) || (dstphyaddr & 0xf)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_16B | SDCR_FETCHND;
-	else
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_8B | SDCR_FETCHND;
-
-	base_register = mmp_get_dma_reg_base(dma_ch);
-	TDCR(base_register)= (dcmd) & (~MDCR_CHANEN);
-	TDIMR(base_register) = MDIMR_COMP;
-	if(dma_ch == MDMA1_CH0)
-		TDNDPR(base_register) = dma_desc_array_phys_ch1;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_16B;
 	else
-		TDNDPR(base_register) = dma_desc_array_phys_ch2;
-	MMP2_FILL_DATA(base_register) = c;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_8B;
 
-	if(dma_ch == MDMA1_CH0){
-		init_completion(&complete_ch1);
-	}else{
-		init_completion(&complete_ch2);
-	}
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_MEMSET, mask);
+	dma_chan = dma_request_channel(mask, filter, &tdma_data_pmemset);
 
-	TDCR(base_register)= dcmd | MDCR_CHANEN;
+	len_total = len;
+	len_tmp = len_total > SINGLE_DESC_TRANS_MAX ?
+				SINGLE_DESC_TRANS_MAX : len_total;
+	desc = dma_chan->device->device_prep_dma_memset(
+			dma_chan, dstphyaddr, c, len_total,
+			DMA_BIDIRECTIONAL);
 
-	if(dma_ch == MDMA1_CH0){
-		ret = wait_for_completion_timeout(&complete_ch1, 10*HZ);
-	}else{
-		ret = wait_for_completion_timeout(&complete_ch2, 10*HZ);
-	}
+	/* this completion will be finished after mdma irq */
+	init_completion(&complete_memset);
 
-	TDIMR(base_register) = 0;
-	mdma_dcr = TDCR(base_register);
-	if (mdma_dcr & TDCR_CHANACT) {
-		TDCR(base_register) = mdma_dcr | TDCR_ABR;
-	}
-	while (mdma_dcr & TDCR_CHANACT) {
-		mdma_dcr = TDCR(base_register);
-	}
-	mmp_free_dma(dma_ch);
+	desc->callback = mdma_irq;
+	desc->callback_param = &complete_memset;
+
+	/* start dma transfer */
+	dmaengine_submit(desc);
+
+	ret = wait_for_completion_timeout(&complete_memset, 10*HZ);
+
+	/* release dma channel */
+	dma_release_channel(dma_chan);
+	dma_chan = NULL;
 
 	if (ret)
 		return len;
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 12/13] mdma:dmaengine:update mdma for tdma
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1, do some update to add mdma to the
	dma engine as adma

Change-Id: I514b247e0c2301fc48b1f1980562f3c50d6a6455
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 drivers/char/mmp2_mdma.c |  273 ++++++++++++++--------------------------------
 1 files changed, 84 insertions(+), 189 deletions(-)

diff --git a/drivers/char/mmp2_mdma.c b/drivers/char/mmp2_mdma.c
index c16b94a..72fee88 100644
--- a/drivers/char/mmp2_mdma.c
+++ b/drivers/char/mmp2_mdma.c
@@ -37,8 +37,8 @@ static void *dma_desc_array_ch1 = NULL;
 static void *dma_desc_array_ch2 = NULL;
 static dma_addr_t dma_desc_array_phys_ch1;
 static dma_addr_t dma_desc_array_phys_ch2;
-static struct completion complete_ch1;
-static struct completion complete_ch2;
+static struct completion complete_memcpy;
+static struct completion complete_memset;
 
 static unsigned long uva_to_pa(struct mm_struct *mm, unsigned long addr)
 {
@@ -68,43 +68,41 @@ static unsigned long uva_to_pa(struct mm_struct *mm, unsigned long addr)
 	return ret;
 }
 
-static void mdma_irq(int channel, void *data)
+static void mdma_irq(void *data)
 {
-	unsigned int base_register = mmp_get_dma_reg_base(channel);
-
-	if(base_register){
-		if (TDISR(base_register) & 0x1) {
-			if(channel == MDMA1_CH0)
-				complete(&complete_ch1);
-			else
-				complete(&complete_ch2);
-			if(TDISR(base_register) & 0xc)
-				printk(KERN_ERR "MDMA error 0x%x on channel %d \n",
-					TDISR(base_register), channel);
-		} else {
-			printk(KERN_ERR "MDMA error on channel %d \n", channel);
-		}
-		TDISR(base_register) = 0;
-	}
+	struct completion *mdma_complete = data;
+
+	complete(mdma_complete);
 
 	return;
 }
 
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct mmp_tdma_data *tdma_data = param;
+
+	if (!mmp_mdma_is_this_type(chan))
+		return false;
+
+	chan->private = tdma_data;
+
+	return true;
+}
 /*
  * len requires at least 8bytes alignment. 128bytes alignment will get better performance.
  * Alignment: 8, 16, 32, 64, 128. The bigger, the better.
  * source and destination address aslo need alignment with burst size.
  */
+struct mmp_tdma_data tdma_data_pmemcpy;
 unsigned long mdma_pmemcpy(unsigned long pdst, unsigned long psrc, unsigned int len)
 {
-	int ret, dma_ch;
-	unsigned int base_register;
-	unsigned int dcmd, mdma_dcr;
+	int ret;
 	unsigned long len_tmp, len_total;
-	struct pxa910_squ_desc *dma_desc_tmp;
-	dma_addr_t dma_desc_p_tmp;
 	unsigned long srcphyaddr = psrc;
 	unsigned long dstphyaddr = pdst;
+	struct dma_chan *dma_chan;
+	dma_cap_mask_t mask;
+	struct dma_async_tx_descriptor *desc;
 
 	if (srcphyaddr == 0 || dstphyaddr == 0)
 		return -1;
@@ -124,95 +122,44 @@ unsigned long mdma_pmemcpy(unsigned long pdst, unsigned long psrc, unsigned int
 		return -1;
 	}
 
-	ret = mmp_request_dma("mdma_memcpy", MDMA1_CH0,
-					mdma_irq, NULL);
-	if (ret < 0){
-		ret = mmp_request_dma("mdma_memcpy", MDMA1_CH1,
-						mdma_irq, NULL);
-		if (ret < 0){
-			printk(KERN_ERR	"Can't request MDMA for memcpy\n");
-			return -ENODEV;
-		}else{
-			dma_ch = MDMA1_CH1;
-		}
-	}else{
-		dma_ch = MDMA1_CH0;
-	}
-
-	if(dma_ch == MDMA1_CH0){
-		dma_desc_tmp = dma_desc_array_ch1;
-		dma_desc_p_tmp = dma_desc_array_phys_ch1;
-	}else{
-		dma_desc_tmp = dma_desc_array_ch2;
-		dma_desc_p_tmp = dma_desc_array_phys_ch2;
-	}
-
-	len_total = len;
-	while(len_total) {
-		len_tmp = len_total>SINGLE_DESC_TRANS_MAX ?
-				SINGLE_DESC_TRANS_MAX : len_total;
-		dma_desc_tmp->nxt_desc = dma_desc_p_tmp + sizeof(struct pxa910_squ_desc);
-		dma_desc_tmp->src_addr = srcphyaddr;
-		dma_desc_tmp->dst_addr = dstphyaddr;
-		dma_desc_tmp->byte_cnt = len_tmp;
-		if (len_total <= SINGLE_DESC_TRANS_MAX) {
-			dma_desc_tmp->nxt_desc = 0;
-			break;
-		}
-		len_total -= len_tmp;
-		dma_desc_tmp ++;
-		dma_desc_p_tmp += sizeof(struct pxa910_squ_desc);
-		srcphyaddr += len_tmp;
-		dstphyaddr += len_tmp;
-	}
-
 	if (!((len & 0x7f) || (srcphyaddr & 0x7f) || (dstphyaddr & 0x7f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_128B | SDCR_FETCHND;
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_128B;
 	else if (!((len & 0x3f) || (srcphyaddr & 0x3f) || (dstphyaddr & 0x3f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_64B | SDCR_FETCHND;
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_64B;
 	else if (!((len & 0x1f) || (srcphyaddr & 0x1f) || (dstphyaddr & 0x1f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_32B | SDCR_FETCHND;
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_32B;
 	else if (!((len & 0xf) || (srcphyaddr & 0xf) || (dstphyaddr & 0xf)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_16B | SDCR_FETCHND;
-	else
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_INC | MDCR_TRANSMOD |
-			MDCR_DMA_BURST_8B | SDCR_FETCHND;
-
-	base_register = mmp_get_dma_reg_base(dma_ch);
-	TDCR(base_register)= (dcmd) & (~MDCR_CHANEN);
-	TDIMR(base_register) = MDIMR_COMP;
-	if(dma_ch == MDMA1_CH0)
-		TDNDPR(base_register) = dma_desc_array_phys_ch1;
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_16B;
 	else
-		TDNDPR(base_register) = dma_desc_array_phys_ch2;
-	if(dma_ch == MDMA1_CH0){
-		init_completion(&complete_ch1);
-	}else{
-		init_completion(&complete_ch2);
-	}
+		tdma_data_pmemcpy.bus_size = MDCR_DMA_BURST_8B;
 
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_MEMCPY, mask);
+	dma_chan = dma_request_channel(mask, filter, &tdma_data_pmemcpy);
 
-	TDCR(base_register)= dcmd | MDCR_CHANEN;
+	len_total = len;
+	len_tmp = len_total > SINGLE_DESC_TRANS_MAX ?
+				SINGLE_DESC_TRANS_MAX : len_total;
 
-	if(dma_ch == MDMA1_CH0){
-		ret = wait_for_completion_timeout(&complete_ch1, 10*HZ);
-	}else{
-		ret = wait_for_completion_timeout(&complete_ch2, 10*HZ);
-	}
+	desc = dma_chan->device->device_prep_dma_memcpy(
+			dma_chan, dstphyaddr, srcphyaddr, len_total,
+			DMA_BIDIRECTIONAL);
 
-	TDIMR(base_register) = 0;
-	mdma_dcr = TDCR(base_register);
-	if (mdma_dcr & TDCR_CHANACT) {
-		TDCR(base_register) = mdma_dcr | TDCR_ABR;
-	}
-	while (mdma_dcr & TDCR_CHANACT) {
-		mdma_dcr = TDCR(base_register);
-	}
-	mmp_free_dma(dma_ch);
+	/* this completion will be finished after mdma irq */
+	init_completion(&complete_memcpy);
+
+	desc->callback = mdma_irq;
+	desc->callback_param = &complete_memcpy;
+
+	/* start dma transfer */
+	dmaengine_submit(desc);
+
+	ret = wait_for_completion_timeout(&complete_memcpy, 10*HZ);
+
+	/* release dma channel */
+	dma_release_channel(dma_chan);
+	dma_chan = NULL;
 
 	if (ret)
 		return len;
@@ -227,15 +174,15 @@ EXPORT_SYMBOL(mdma_pmemcpy);
  * destination address aslo need alignment with burst size.
  * data requires 32bits.
  */
+struct mmp_tdma_data tdma_data_pmemset;
 unsigned long mdma_pmemset(unsigned long paddr, unsigned long c, unsigned int len)
 {
-	int ret, dma_ch;
-	unsigned int base_register;
-	unsigned int dcmd, mdma_dcr;
+	int ret;
 	unsigned long len_tmp, len_total;
-	struct pxa910_squ_desc *dma_desc_tmp;
-	dma_addr_t dma_desc_p_tmp;
 	unsigned long dstphyaddr = paddr;
+	struct dma_chan *dma_chan;
+	dma_cap_mask_t mask;
+	struct dma_async_tx_descriptor *desc;
 
 	if (dstphyaddr == 0)
 		return -1;
@@ -250,95 +197,43 @@ unsigned long mdma_pmemset(unsigned long paddr, unsigned long c, unsigned int le
 		return -1;
 	}
 
-	ret = mmp_request_dma("mdma_memcpy", MDMA1_CH0,
-					mdma_irq, NULL);
-	if (ret < 0){
-		ret = mmp_request_dma("mdma_memcpy", MDMA1_CH1,
-						mdma_irq, NULL);
-		if (ret < 0){
-			printk(KERN_ERR	"Can't request MDMA for memcpy\n");
-			return -ENODEV;
-		}else{
-			dma_ch = MDMA1_CH1;
-		}
-	}else{
-		dma_ch = MDMA1_CH0;
-	}
-
-	if(dma_ch == MDMA1_CH0){
-		dma_desc_tmp = dma_desc_array_ch1;
-		dma_desc_p_tmp = dma_desc_array_phys_ch1;
-	}else{
-		dma_desc_tmp = dma_desc_array_ch2;
-		dma_desc_p_tmp = dma_desc_array_phys_ch2;
-	}
-
-	len_total = len;
-	while(len_total) {
-		len_tmp = len_total>SINGLE_DESC_TRANS_MAX ?
-				SINGLE_DESC_TRANS_MAX : len_total;
-		dma_desc_tmp->nxt_desc = dma_desc_p_tmp + sizeof(struct pxa910_squ_desc);
-		dma_desc_tmp->src_addr = 0;
-		dma_desc_tmp->dst_addr = dstphyaddr;
-		dma_desc_tmp->byte_cnt = len_tmp;
-		if (len_total <= SINGLE_DESC_TRANS_MAX) {
-			dma_desc_tmp->nxt_desc = 0;
-			break;
-		}
-		len_total -= len_tmp;
-		dma_desc_tmp ++;
-		dma_desc_p_tmp += sizeof(struct pxa910_squ_desc);
-		dstphyaddr += len_tmp;
-	}
-
 	if (!((len & 0x7f) || (dstphyaddr & 0x7f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_128B | SDCR_FETCHND;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_128B;
 	else if (!((len & 0x3f) || (dstphyaddr & 0x3f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_64B | SDCR_FETCHND;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_64B;
 	else if (!((len & 0x1f) || (dstphyaddr & 0x1f)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_32B | SDCR_FETCHND;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_32B;
 	else if (!((len & 0xf) || (dstphyaddr & 0xf)))
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_16B | SDCR_FETCHND;
-	else
-		dcmd = MDCR_DST_ADDR_INC | MDCR_SRC_ADDR_HOLD | MDCR_TRANSMOD |
-			MDCR_FILLMOD | MDCR_DMA_BURST_8B | SDCR_FETCHND;
-
-	base_register = mmp_get_dma_reg_base(dma_ch);
-	TDCR(base_register)= (dcmd) & (~MDCR_CHANEN);
-	TDIMR(base_register) = MDIMR_COMP;
-	if(dma_ch == MDMA1_CH0)
-		TDNDPR(base_register) = dma_desc_array_phys_ch1;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_16B;
 	else
-		TDNDPR(base_register) = dma_desc_array_phys_ch2;
-	MMP2_FILL_DATA(base_register) = c;
+		tdma_data_pmemset.bus_size = MDCR_FILLMOD | MDCR_DMA_BURST_8B;
 
-	if(dma_ch == MDMA1_CH0){
-		init_completion(&complete_ch1);
-	}else{
-		init_completion(&complete_ch2);
-	}
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_MEMSET, mask);
+	dma_chan = dma_request_channel(mask, filter, &tdma_data_pmemset);
 
-	TDCR(base_register)= dcmd | MDCR_CHANEN;
+	len_total = len;
+	len_tmp = len_total > SINGLE_DESC_TRANS_MAX ?
+				SINGLE_DESC_TRANS_MAX : len_total;
+	desc = dma_chan->device->device_prep_dma_memset(
+			dma_chan, dstphyaddr, c, len_total,
+			DMA_BIDIRECTIONAL);
 
-	if(dma_ch == MDMA1_CH0){
-		ret = wait_for_completion_timeout(&complete_ch1, 10*HZ);
-	}else{
-		ret = wait_for_completion_timeout(&complete_ch2, 10*HZ);
-	}
+	/* this completion will be finished after mdma irq */
+	init_completion(&complete_memset);
 
-	TDIMR(base_register) = 0;
-	mdma_dcr = TDCR(base_register);
-	if (mdma_dcr & TDCR_CHANACT) {
-		TDCR(base_register) = mdma_dcr | TDCR_ABR;
-	}
-	while (mdma_dcr & TDCR_CHANACT) {
-		mdma_dcr = TDCR(base_register);
-	}
-	mmp_free_dma(dma_ch);
+	desc->callback = mdma_irq;
+	desc->callback_param = &complete_memset;
+
+	/* start dma transfer */
+	dmaengine_submit(desc);
+
+	ret = wait_for_completion_timeout(&complete_memset, 10*HZ);
+
+	/* release dma channel */
+	dma_release_channel(dma_chan);
+	dma_chan = NULL;
 
 	if (ret)
 		return len;
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 13/13] DMA:update defconfig for mmp2
       [not found] <Y>
@ 2012-02-28  7:27   ` zhaoy
  2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
                     ` (19 subsequent siblings)
  20 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy
  Cc: zhaoy

	1.open DMAC and TDMA config in mmp2 defconfig

Change-Id: Ie253d527e481f376edf4cc5e56fbad7a7da4fefa
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/configs/mmp2_defconfig |   21 +++++++++++++++++++--
 1 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index 138c95a..b975779 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -1919,8 +1919,25 @@ CONFIG_RTC_DRV_MAX8925=y
 #
 # on-CPU RTC drivers
 #
-CONFIG_RTC_DRV_MMP=y
-# CONFIG_DMADEVICES is not set
+# CONFIG_RTC_DRV_MMP is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_PXA_DMAC=y
+CONFIG_MMP_TDMA=y
+CONFIG_DMA_ENGINE=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
 # CONFIG_AUXDISPLAY is not set
 CONFIG_UIO=y
 # CONFIG_UIO_PDRV is not set
-- 
1.7.0.4


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 13/13] DMA:update defconfig for mmp2
@ 2012-02-28  7:27   ` zhaoy
  0 siblings, 0 replies; 45+ messages in thread
From: zhaoy @ 2012-02-28  7:27 UTC (permalink / raw)
  To: linux-arm-kernel

	1.open DMAC and TDMA config in mmp2 defconfig

Change-Id: Ie253d527e481f376edf4cc5e56fbad7a7da4fefa
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/configs/mmp2_defconfig |   21 +++++++++++++++++++--
 1 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index 138c95a..b975779 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -1919,8 +1919,25 @@ CONFIG_RTC_DRV_MAX8925=y
 #
 # on-CPU RTC drivers
 #
-CONFIG_RTC_DRV_MMP=y
-# CONFIG_DMADEVICES is not set
+# CONFIG_RTC_DRV_MMP is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_PXA_DMAC=y
+CONFIG_MMP_TDMA=y
+CONFIG_DMA_ENGINE=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
 # CONFIG_AUXDISPLAY is not set
 CONFIG_UIO=y
 # CONFIG_UIO_PDRV is not set
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* RE: [PATCH 13/13] DMA:update defconfig for mmp2
  2012-02-28  7:27   ` zhaoy
@ 2012-02-28  7:42     ` Zhao Ye
  -1 siblings, 0 replies; 45+ messages in thread
From: Zhao Ye @ 2012-02-28  7:42 UTC (permalink / raw)
  To: Zhao Ye, linux-arm-kernel, linux-kernel, nicolas.pitre, linux,
	Haojian Zhuang, Chao Xie, Leo Yan

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb2312", Size: 1609 bytes --]

Sorry! Please ignore these patches. I will send a new version.

Best Regard
Zhao Ye
+61942433
Marvell Tech (SH) Co. Ltd.

-----Original Message-----
From: zhaoy [mailto:zhaoy@marvell.com] 
Sent: 2012Äê2ÔÂ28ÈÕ 15:28
To: linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; nicolas.pitre@linaro.org; linux@arm.linux.org.uk; Haojian Zhuang; Chao Xie; Leo Yan
Cc: Zhao Ye
Subject: [PATCH 13/13] DMA:update defconfig for mmp2

	1.open DMAC and TDMA config in mmp2 defconfig

Change-Id: Ie253d527e481f376edf4cc5e56fbad7a7da4fefa
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/configs/mmp2_defconfig |   21 +++++++++++++++++++--
 1 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index 138c95a..b975779 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -1919,8 +1919,25 @@ CONFIG_RTC_DRV_MAX8925=y
 #
 # on-CPU RTC drivers
 #
-CONFIG_RTC_DRV_MMP=y
-# CONFIG_DMADEVICES is not set
+# CONFIG_RTC_DRV_MMP is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_PXA_DMAC=y
+CONFIG_MMP_TDMA=y
+CONFIG_DMA_ENGINE=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
 # CONFIG_AUXDISPLAY is not set
 CONFIG_UIO=y
 # CONFIG_UIO_PDRV is not set
-- 
1.7.0.4

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 13/13] DMA:update defconfig for mmp2
@ 2012-02-28  7:42     ` Zhao Ye
  0 siblings, 0 replies; 45+ messages in thread
From: Zhao Ye @ 2012-02-28  7:42 UTC (permalink / raw)
  To: linux-arm-kernel

Sorry! Please ignore these patches. I will send a new version.

Best Regard
Zhao Ye
+61942433
Marvell Tech (SH) Co. Ltd.

-----Original Message-----
From: zhaoy [mailto:zhaoy at marvell.com] 
Sent: 2012?2?28? 15:28
To: linux-arm-kernel at lists.infradead.org; linux-kernel at vger.kernel.org; nicolas.pitre at linaro.org; linux at arm.linux.org.uk; Haojian Zhuang; Chao Xie; Leo Yan
Cc: Zhao Ye
Subject: [PATCH 13/13] DMA:update defconfig for mmp2

	1.open DMAC and TDMA config in mmp2 defconfig

Change-Id: Ie253d527e481f376edf4cc5e56fbad7a7da4fefa
Signed-off-by: zhaoy <zhaoy@marvell.com>
---
 arch/arm/configs/mmp2_defconfig |   21 +++++++++++++++++++--
 1 files changed, 19 insertions(+), 2 deletions(-)

diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index 138c95a..b975779 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -1919,8 +1919,25 @@ CONFIG_RTC_DRV_MAX8925=y
 #
 # on-CPU RTC drivers
 #
-CONFIG_RTC_DRV_MMP=y
-# CONFIG_DMADEVICES is not set
+# CONFIG_RTC_DRV_MMP is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_PXA_DMAC=y
+CONFIG_MMP_TDMA=y
+CONFIG_DMA_ENGINE=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
 # CONFIG_AUXDISPLAY is not set
 CONFIG_UIO=y
 # CONFIG_UIO_PDRV is not set
-- 
1.7.0.4

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* RE: [PATCH 00/13] Enable DMA engine for mmp2 and mmp3
  2012-02-28  7:27   ` zhaoy
@ 2012-02-28  8:41     ` Haojian Zhuang
  -1 siblings, 0 replies; 45+ messages in thread
From: Haojian Zhuang @ 2012-02-28  8:41 UTC (permalink / raw)
  To: Zhao Ye, linux-arm-kernel, linux-kernel, nicolas.pitre, linux,
	Chao Xie, Leo Yan
  Cc: Zhao Ye

Hi Zhao,

Which code base are you using? Now it's kernel v3.3-rc5.

Thanks
Haojian
________________________________________
From: zhaoy [zhaoy@marvell.com]
Sent: Tuesday, February 28, 2012 3:27 PM
To: linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; nicolas.pitre@linaro.org; linux@arm.linux.org.uk; Haojian Zhuang; Chao Xie; Leo Yan
Cc: Zhao Ye
Subject: [PATCH 00/13] Enable DMA engine for mmp2 and mmp3

These patch is for marvell mmp2 and mmp3 platform to enable
dmaengine, use dmaengine to manage the audio dma, video dma
and peripheral device dma together.

zhaoy (13):
  DMA:define second level irq definition
  dmaengine:open dmac and tdma support
  dmaengine: mmp: add two-channel dma driver
  dmaengine:mmp add peripheral dma driver
  mmp2:register dma devices for mmp2 platform
  mmp3:register dma devices for mmp3 platform
  DMA:mmp:remove redundant dma drivers
  DMAC:tty serial pxa use generic dma engine APIs
  DMA:remove unnecessary dma configurations
  dmaengine:audio:mmp audio use generic dma engine APIs
  DMA:update defconfig for mmp3
  mdma:dmaengine:update mdma for tdma
  DMA:update defconfig for mmp2

 arch/arm/configs/mmp2_defconfig           |   23 +-
 arch/arm/configs/mmp3_defconfig           |   21 +-
 arch/arm/mach-mmp/Makefile                |    4 +-
 arch/arm/mach-mmp/include/mach/irqs.h     |   18 +-
 arch/arm/mach-mmp/include/mach/mmp_dma.h  |   89 ++-
 arch/arm/mach-mmp/include/mach/regs-icu.h |   12 +-
 arch/arm/mach-mmp/irq-mmp2.c              |    4 +
 arch/arm/mach-mmp/irq-mmp3.c              |    3 +
 arch/arm/mach-mmp/mmp2.c                  |  122 ++-
 arch/arm/mach-mmp/mmp3.c                  |   78 ++-
 arch/arm/plat-pxa/include/plat/dma.h      |   33 +-
 drivers/char/mmp2_mdma.c                  |  273 ++----
 drivers/dma/Kconfig                       |   19 +
 drivers/dma/Makefile                      |    2 +
 drivers/dma/mmp_tdma.c                    |  858 +++++++++++++++
 drivers/dma/pxa_dmac.c                    | 1077 ++++++++++++++++++
 drivers/tty/serial/Kconfig                |   12 +-
 drivers/tty/serial/Makefile               |    1 +
 drivers/tty/serial/mmp_pxa.c              | 1698 +++++++++++++++++++++++++++++
 sound/soc/pxa/mmp-pcm.c                   |  232 ++--
 sound/soc/pxa/mmp-pcm.h                   |    4 +-
 sound/soc/pxa/mmp2-squ.c                  |  192 ++--
 sound/soc/pxa/mmp2-squ.h                  |    8 +-
 sound/soc/pxa/mmp2-sspa.c                 |   28 +-
 24 files changed, 4361 insertions(+), 450 deletions(-)
 create mode 100644 drivers/dma/mmp_tdma.c
 create mode 100644 drivers/dma/pxa_dmac.c
 create mode 100644 drivers/tty/serial/mmp_pxa.c


^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 00/13] Enable DMA engine for mmp2 and mmp3
@ 2012-02-28  8:41     ` Haojian Zhuang
  0 siblings, 0 replies; 45+ messages in thread
From: Haojian Zhuang @ 2012-02-28  8:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Zhao,

Which code base are you using? Now it's kernel v3.3-rc5.

Thanks
Haojian
________________________________________
From: zhaoy [zhaoy at marvell.com]
Sent: Tuesday, February 28, 2012 3:27 PM
To: linux-arm-kernel at lists.infradead.org; linux-kernel at vger.kernel.org; nicolas.pitre at linaro.org; linux at arm.linux.org.uk; Haojian Zhuang; Chao Xie; Leo Yan
Cc: Zhao Ye
Subject: [PATCH 00/13] Enable DMA engine for mmp2 and mmp3

These patch is for marvell mmp2 and mmp3 platform to enable
dmaengine, use dmaengine to manage the audio dma, video dma
and peripheral device dma together.

zhaoy (13):
  DMA:define second level irq definition
  dmaengine:open dmac and tdma support
  dmaengine: mmp: add two-channel dma driver
  dmaengine:mmp add peripheral dma driver
  mmp2:register dma devices for mmp2 platform
  mmp3:register dma devices for mmp3 platform
  DMA:mmp:remove redundant dma drivers
  DMAC:tty serial pxa use generic dma engine APIs
  DMA:remove unnecessary dma configurations
  dmaengine:audio:mmp audio use generic dma engine APIs
  DMA:update defconfig for mmp3
  mdma:dmaengine:update mdma for tdma
  DMA:update defconfig for mmp2

 arch/arm/configs/mmp2_defconfig           |   23 +-
 arch/arm/configs/mmp3_defconfig           |   21 +-
 arch/arm/mach-mmp/Makefile                |    4 +-
 arch/arm/mach-mmp/include/mach/irqs.h     |   18 +-
 arch/arm/mach-mmp/include/mach/mmp_dma.h  |   89 ++-
 arch/arm/mach-mmp/include/mach/regs-icu.h |   12 +-
 arch/arm/mach-mmp/irq-mmp2.c              |    4 +
 arch/arm/mach-mmp/irq-mmp3.c              |    3 +
 arch/arm/mach-mmp/mmp2.c                  |  122 ++-
 arch/arm/mach-mmp/mmp3.c                  |   78 ++-
 arch/arm/plat-pxa/include/plat/dma.h      |   33 +-
 drivers/char/mmp2_mdma.c                  |  273 ++----
 drivers/dma/Kconfig                       |   19 +
 drivers/dma/Makefile                      |    2 +
 drivers/dma/mmp_tdma.c                    |  858 +++++++++++++++
 drivers/dma/pxa_dmac.c                    | 1077 ++++++++++++++++++
 drivers/tty/serial/Kconfig                |   12 +-
 drivers/tty/serial/Makefile               |    1 +
 drivers/tty/serial/mmp_pxa.c              | 1698 +++++++++++++++++++++++++++++
 sound/soc/pxa/mmp-pcm.c                   |  232 ++--
 sound/soc/pxa/mmp-pcm.h                   |    4 +-
 sound/soc/pxa/mmp2-squ.c                  |  192 ++--
 sound/soc/pxa/mmp2-squ.h                  |    8 +-
 sound/soc/pxa/mmp2-sspa.c                 |   28 +-
 24 files changed, 4361 insertions(+), 450 deletions(-)
 create mode 100644 drivers/dma/mmp_tdma.c
 create mode 100644 drivers/dma/pxa_dmac.c
 create mode 100644 drivers/tty/serial/mmp_pxa.c

^ permalink raw reply	[flat|nested] 45+ messages in thread

* RE: [PATCH 00/13] Enable DMA engine for mmp2 and mmp3
  2012-02-28  8:41     ` Haojian Zhuang
@ 2012-02-28  8:49       ` Zhao Ye
  -1 siblings, 0 replies; 45+ messages in thread
From: Zhao Ye @ 2012-02-28  8:49 UTC (permalink / raw)
  To: Haojian Zhuang, linux-arm-kernel, linux-kernel, nicolas.pitre,
	linux, Chao Xie, Leo Yan

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="gb2312", Size: 3380 bytes --]

I am sorry these patches is based on kernel 3.0, and based on our company's
Branch. 

Please ignore these patches, I will move them to kernel v3.3-rc5.
And later I will send a new version out.

Sorry!

Zhao Ye
+61942433
Marvell Tech (SH) Co. Ltd.


-----Original Message-----
From: Haojian Zhuang 
Sent: 2012Äê2ÔÂ28ÈÕ 16:42
To: Zhao Ye; linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; nicolas.pitre@linaro.org; linux@arm.linux.org.uk; Chao Xie; Leo Yan
Cc: Zhao Ye
Subject: RE: [PATCH 00/13] Enable DMA engine for mmp2 and mmp3

Hi Zhao,

Which code base are you using? Now it's kernel v3.3-rc5.

Thanks
Haojian
________________________________________
From: zhaoy [zhaoy@marvell.com]
Sent: Tuesday, February 28, 2012 3:27 PM
To: linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org; nicolas.pitre@linaro.org; linux@arm.linux.org.uk; Haojian Zhuang; Chao Xie; Leo Yan
Cc: Zhao Ye
Subject: [PATCH 00/13] Enable DMA engine for mmp2 and mmp3

These patch is for marvell mmp2 and mmp3 platform to enable
dmaengine, use dmaengine to manage the audio dma, video dma
and peripheral device dma together.

zhaoy (13):
  DMA:define second level irq definition
  dmaengine:open dmac and tdma support
  dmaengine: mmp: add two-channel dma driver
  dmaengine:mmp add peripheral dma driver
  mmp2:register dma devices for mmp2 platform
  mmp3:register dma devices for mmp3 platform
  DMA:mmp:remove redundant dma drivers
  DMAC:tty serial pxa use generic dma engine APIs
  DMA:remove unnecessary dma configurations
  dmaengine:audio:mmp audio use generic dma engine APIs
  DMA:update defconfig for mmp3
  mdma:dmaengine:update mdma for tdma
  DMA:update defconfig for mmp2

 arch/arm/configs/mmp2_defconfig           |   23 +-
 arch/arm/configs/mmp3_defconfig           |   21 +-
 arch/arm/mach-mmp/Makefile                |    4 +-
 arch/arm/mach-mmp/include/mach/irqs.h     |   18 +-
 arch/arm/mach-mmp/include/mach/mmp_dma.h  |   89 ++-
 arch/arm/mach-mmp/include/mach/regs-icu.h |   12 +-
 arch/arm/mach-mmp/irq-mmp2.c              |    4 +
 arch/arm/mach-mmp/irq-mmp3.c              |    3 +
 arch/arm/mach-mmp/mmp2.c                  |  122 ++-
 arch/arm/mach-mmp/mmp3.c                  |   78 ++-
 arch/arm/plat-pxa/include/plat/dma.h      |   33 +-
 drivers/char/mmp2_mdma.c                  |  273 ++----
 drivers/dma/Kconfig                       |   19 +
 drivers/dma/Makefile                      |    2 +
 drivers/dma/mmp_tdma.c                    |  858 +++++++++++++++
 drivers/dma/pxa_dmac.c                    | 1077 ++++++++++++++++++
 drivers/tty/serial/Kconfig                |   12 +-
 drivers/tty/serial/Makefile               |    1 +
 drivers/tty/serial/mmp_pxa.c              | 1698 +++++++++++++++++++++++++++++
 sound/soc/pxa/mmp-pcm.c                   |  232 ++--
 sound/soc/pxa/mmp-pcm.h                   |    4 +-
 sound/soc/pxa/mmp2-squ.c                  |  192 ++--
 sound/soc/pxa/mmp2-squ.h                  |    8 +-
 sound/soc/pxa/mmp2-sspa.c                 |   28 +-
 24 files changed, 4361 insertions(+), 450 deletions(-)
 create mode 100644 drivers/dma/mmp_tdma.c
 create mode 100644 drivers/dma/pxa_dmac.c
 create mode 100644 drivers/tty/serial/mmp_pxa.c

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 00/13] Enable DMA engine for mmp2 and mmp3
@ 2012-02-28  8:49       ` Zhao Ye
  0 siblings, 0 replies; 45+ messages in thread
From: Zhao Ye @ 2012-02-28  8:49 UTC (permalink / raw)
  To: linux-arm-kernel

I am sorry these patches is based on kernel 3.0, and based on our company's
Branch. 

Please ignore these patches, I will move them to kernel v3.3-rc5.
And later I will send a new version out.

Sorry!

Zhao Ye
+61942433
Marvell Tech (SH) Co. Ltd.


-----Original Message-----
From: Haojian Zhuang 
Sent: 2012?2?28? 16:42
To: Zhao Ye; linux-arm-kernel at lists.infradead.org; linux-kernel at vger.kernel.org; nicolas.pitre at linaro.org; linux at arm.linux.org.uk; Chao Xie; Leo Yan
Cc: Zhao Ye
Subject: RE: [PATCH 00/13] Enable DMA engine for mmp2 and mmp3

Hi Zhao,

Which code base are you using? Now it's kernel v3.3-rc5.

Thanks
Haojian
________________________________________
From: zhaoy [zhaoy at marvell.com]
Sent: Tuesday, February 28, 2012 3:27 PM
To: linux-arm-kernel at lists.infradead.org; linux-kernel at vger.kernel.org; nicolas.pitre at linaro.org; linux at arm.linux.org.uk; Haojian Zhuang; Chao Xie; Leo Yan
Cc: Zhao Ye
Subject: [PATCH 00/13] Enable DMA engine for mmp2 and mmp3

These patch is for marvell mmp2 and mmp3 platform to enable
dmaengine, use dmaengine to manage the audio dma, video dma
and peripheral device dma together.

zhaoy (13):
  DMA:define second level irq definition
  dmaengine:open dmac and tdma support
  dmaengine: mmp: add two-channel dma driver
  dmaengine:mmp add peripheral dma driver
  mmp2:register dma devices for mmp2 platform
  mmp3:register dma devices for mmp3 platform
  DMA:mmp:remove redundant dma drivers
  DMAC:tty serial pxa use generic dma engine APIs
  DMA:remove unnecessary dma configurations
  dmaengine:audio:mmp audio use generic dma engine APIs
  DMA:update defconfig for mmp3
  mdma:dmaengine:update mdma for tdma
  DMA:update defconfig for mmp2

 arch/arm/configs/mmp2_defconfig           |   23 +-
 arch/arm/configs/mmp3_defconfig           |   21 +-
 arch/arm/mach-mmp/Makefile                |    4 +-
 arch/arm/mach-mmp/include/mach/irqs.h     |   18 +-
 arch/arm/mach-mmp/include/mach/mmp_dma.h  |   89 ++-
 arch/arm/mach-mmp/include/mach/regs-icu.h |   12 +-
 arch/arm/mach-mmp/irq-mmp2.c              |    4 +
 arch/arm/mach-mmp/irq-mmp3.c              |    3 +
 arch/arm/mach-mmp/mmp2.c                  |  122 ++-
 arch/arm/mach-mmp/mmp3.c                  |   78 ++-
 arch/arm/plat-pxa/include/plat/dma.h      |   33 +-
 drivers/char/mmp2_mdma.c                  |  273 ++----
 drivers/dma/Kconfig                       |   19 +
 drivers/dma/Makefile                      |    2 +
 drivers/dma/mmp_tdma.c                    |  858 +++++++++++++++
 drivers/dma/pxa_dmac.c                    | 1077 ++++++++++++++++++
 drivers/tty/serial/Kconfig                |   12 +-
 drivers/tty/serial/Makefile               |    1 +
 drivers/tty/serial/mmp_pxa.c              | 1698 +++++++++++++++++++++++++++++
 sound/soc/pxa/mmp-pcm.c                   |  232 ++--
 sound/soc/pxa/mmp-pcm.h                   |    4 +-
 sound/soc/pxa/mmp2-squ.c                  |  192 ++--
 sound/soc/pxa/mmp2-squ.h                  |    8 +-
 sound/soc/pxa/mmp2-sspa.c                 |   28 +-
 24 files changed, 4361 insertions(+), 450 deletions(-)
 create mode 100644 drivers/dma/mmp_tdma.c
 create mode 100644 drivers/dma/pxa_dmac.c
 create mode 100644 drivers/tty/serial/mmp_pxa.c

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 07/13] DMA:mmp:remove redundant dma drivers
  2012-02-28  7:27   ` zhaoy
@ 2012-02-28 12:30     ` Sergei Shtylyov
  -1 siblings, 0 replies; 45+ messages in thread
From: Sergei Shtylyov @ 2012-02-28 12:30 UTC (permalink / raw)
  To: zhaoy
  Cc: linux-arm-kernel, linux-kernel, nicolas.pitre, linux, hzhuang1,
	cxie4, leoy

Hello.

On 28-02-2012 11:27, zhaoy wrote:

> 	1.remove old dma drivers

> Change-Id: I96ffed95012a4f3e855c2f6c184646a798a0ee7d

    This line shouldn't be in the upstream patch.

> Signed-off-by: zhaoy <zhaoy@marvell.com>

    Real name required here.

WBR, Sergei

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 07/13] DMA:mmp:remove redundant dma drivers
@ 2012-02-28 12:30     ` Sergei Shtylyov
  0 siblings, 0 replies; 45+ messages in thread
From: Sergei Shtylyov @ 2012-02-28 12:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hello.

On 28-02-2012 11:27, zhaoy wrote:

> 	1.remove old dma drivers

> Change-Id: I96ffed95012a4f3e855c2f6c184646a798a0ee7d

    This line shouldn't be in the upstream patch.

> Signed-off-by: zhaoy <zhaoy@marvell.com>

    Real name required here.

WBR, Sergei

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH v2] mtd: mtd_torturetest can cause stack overflows
       [not found] <Y>
                   ` (19 preceding siblings ...)
  2012-02-28  7:27   ` zhaoy
@ 2013-02-05 14:08 ` Al Cooper
  2013-02-13 12:49   ` Artem Bityutskiy
  20 siblings, 1 reply; 45+ messages in thread
From: Al Cooper @ 2013-02-05 14:08 UTC (permalink / raw)
  To: dwmw2, linux-mtd; +Cc: Al Cooper

mtd_torturetest uses the module parm "ebcnt" to control the size of a
stack based array of int's. When "ebcnt" is large, Ex: 1000, it
causes stack overflows on systems with small kernel stacks. The fix
is to move the array from the stack to kmalloc memory.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
---
 drivers/mtd/tests/mtd_torturetest.c |   25 +++++++++++--------------
 1 files changed, 11 insertions(+), 14 deletions(-)

diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/mtd_torturetest.c
index c4cde1e..3a9f6a6 100644
--- a/drivers/mtd/tests/mtd_torturetest.c
+++ b/drivers/mtd/tests/mtd_torturetest.c
@@ -208,7 +208,7 @@ static inline int write_pattern(int ebnum, void *buf)
 static int __init tort_init(void)
 {
 	int err = 0, i, infinite = !cycles_count;
-	int bad_ebs[ebcnt];
+	int *bad_ebs;
 
 	printk(KERN_INFO "\n");
 	printk(KERN_INFO "=================================================\n");
@@ -250,28 +250,24 @@ static int __init tort_init(void)
 
 	err = -ENOMEM;
 	patt_5A5 = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!patt_5A5) {
-		pr_err("error: cannot allocate memory\n");
+	if (!patt_5A5)
 		goto out_mtd;
-	}
 
 	patt_A5A = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!patt_A5A) {
-		pr_err("error: cannot allocate memory\n");
+	if (!patt_A5A)
 		goto out_patt_5A5;
-	}
 
 	patt_FF = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!patt_FF) {
-		pr_err("error: cannot allocate memory\n");
+	if (!patt_FF)
 		goto out_patt_A5A;
-	}
 
 	check_buf = kmalloc(mtd->erasesize, GFP_KERNEL);
-	if (!check_buf) {
-		pr_err("error: cannot allocate memory\n");
+	if (!check_buf)
 		goto out_patt_FF;
-	}
+
+	bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL);
+	if (!bad_ebs)
+		goto out_check_buf;
 
 	err = 0;
 
@@ -290,7 +286,6 @@ static int __init tort_init(void)
 	/*
 	 * Check if there is a bad eraseblock among those we are going to test.
 	 */
-	memset(&bad_ebs[0], 0, sizeof(int) * ebcnt);
 	if (mtd_can_have_bb(mtd)) {
 		for (i = eb; i < eb + ebcnt; i++) {
 			err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize);
@@ -394,6 +389,8 @@ out:
 
 	pr_info("finished after %u erase cycles\n",
 	       erase_cycles);
+	kfree(bad_ebs);
+out_check_buf:
 	kfree(check_buf);
 out_patt_FF:
 	kfree(patt_FF);
-- 
1.7.6

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* Re: [PATCH v2] mtd: mtd_torturetest can cause stack overflows
  2013-02-05 14:08 ` [PATCH v2] mtd: mtd_torturetest can cause stack overflows Al Cooper
@ 2013-02-13 12:49   ` Artem Bityutskiy
  0 siblings, 0 replies; 45+ messages in thread
From: Artem Bityutskiy @ 2013-02-13 12:49 UTC (permalink / raw)
  To: Al Cooper; +Cc: linux-mtd, dwmw2

On Tue, 2013-02-05 at 09:08 -0500, Al Cooper wrote:
> mtd_torturetest uses the module parm "ebcnt" to control the size of a
> stack based array of int's. When "ebcnt" is large, Ex: 1000, it
> causes stack overflows on systems with small kernel stacks. The fix
> is to move the array from the stack to kmalloc memory.
> 
> Signed-off-by: Al Cooper <alcooperx@gmail.com>

Pushed to l2-mtd.git, thanks!

-- 
Best Regards,
Artem Bityutskiy

^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2013-02-13 12:49 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <Y>
2011-01-18 22:54 ` [PATCH v4, 3/7] cdma-sms: Add CDMA SMS Support Lei Yu
2011-01-18 22:54 ` [PATCH v4, 4/7] " Lei Yu
2011-01-18 22:54 ` [PATCH v4, 5/7] cdmamodem: Add CDMA SMS driver support Lei Yu
2011-01-18 22:54 ` [PATCH v4, 6/7] test: Add CDMA SMS Support Lei Yu
2011-01-18 22:54 ` [PATCH v4, 7/7] cdmaphonesim: " Lei Yu
2011-10-07  0:51 ` [PATCH 1/1] ath6kl: Add WSC IE on the associate message Kevin Fang
2011-10-11 11:24   ` Kalle Valo
2012-02-28  7:27 ` [PATCH 00/13] Enable DMA engine for mmp2 and mmp3 zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  8:41   ` Haojian Zhuang
2012-02-28  8:41     ` Haojian Zhuang
2012-02-28  8:49     ` Zhao Ye
2012-02-28  8:49       ` Zhao Ye
2012-02-28  7:27 ` [PATCH 01/13] DMA:define second level irq definition zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 02/13] dmaengine:open dmac and tdma support zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 03/13] dmaengine: mmp: add two-channel dma driver zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 04/13] dmaengine:mmp add peripheral " zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 05/13] mmp2:register dma devices for mmp2 platform zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 06/13] mmp3:register dma devices for mmp3 platform zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 07/13] DMA:mmp:remove redundant dma drivers zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28 12:30   ` Sergei Shtylyov
2012-02-28 12:30     ` Sergei Shtylyov
2012-02-28  7:27 ` [PATCH 08/13] DMAC:tty serial pxa use generic dma engine APIs zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 09/13] DMA:remove unnecessary dma configurations zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 10/13] dmaengine:audio:mmp audio use generic dma engine APIs zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 11/13] DMA:update defconfig for mmp3 zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 12/13] mdma:dmaengine:update mdma for tdma zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:27 ` [PATCH 13/13] DMA:update defconfig for mmp2 zhaoy
2012-02-28  7:27   ` zhaoy
2012-02-28  7:42   ` Zhao Ye
2012-02-28  7:42     ` Zhao Ye
2013-02-05 14:08 ` [PATCH v2] mtd: mtd_torturetest can cause stack overflows Al Cooper
2013-02-13 12:49   ` Artem Bityutskiy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.