summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorShyam Krishnan M <shyam.moni@stericsson.com>2011-05-27 19:16:19 +0530
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 15:15:34 +0200
commite727d0bf3262328711d6ba2317e274bcef1431ea (patch)
tree5b0f7e2e263b62dcdf7593a0f0b3d53d97298eef /sound
parent12c4771391be424b84d2012497ef4b3aa6c39217 (diff)
Ux500 ASoC: Add codec-driver for AB5500
ST-Ericsson Linux next: Not tested ST-Ericsson ID: 332112 ST-Ericsson FOSS-OUT ID: STETL-FOSS-OUT-10006 Change-Id:Ic35d9290ab3d938f19b8581c8e82cbd878952f54 Signed-off-by: Shyam Krishnan M <shyam.moni@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/24090 Tested-by: Hemanth P SETHURAM <hemanth.sethuram@stericsson.com> Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/Makefile1
-rw-r--r--sound/soc/codecs/ab5500.c116
-rw-r--r--sound/soc/codecs/ab5500.h11
-rw-r--r--sound/soc/ux500/u5500.c29
-rw-r--r--sound/soc/ux500/ux500_ab5500.c32
-rw-r--r--sound/soc/ux500/ux500_ab5500.h2
6 files changed, 111 insertions, 80 deletions
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a3bfc31c640..a8e4d5c6cde 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -98,7 +98,6 @@ obj-$(CONFIG_SND_SOC_AB3550) += snd-soc-ab3550.o
obj-$(CONFIG_SND_SOC_AB5500) += snd-soc-ab5500.o
obj-$(CONFIG_SND_SOC_AB8500) += snd-soc-ab8500_audio.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
-obj-$(CONFIG_SND_SOC_AB3550) += snd-soc-ab3550.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1836) += snd-soc-ad1836.o
obj-$(CONFIG_SND_SOC_AD193X) += snd-soc-ad193x.o
diff --git a/sound/soc/codecs/ab5500.c b/sound/soc/codecs/ab5500.c
index 8f8fa6a4ea2..52c56c13631 100644
--- a/sound/soc/codecs/ab5500.c
+++ b/sound/soc/codecs/ab5500.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST-Ericsson SA 2011
*
* Author: Xie Xiaolei <xie.xiaolei@etericsson.com>,
* Ola Lilja <ola.o.lilja@stericsson.com>,
@@ -19,21 +19,21 @@
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/mfd/abx500.h>
-#include <linux/bitmap.h>
-#include <linux/bitops.h>
-#include <linux/mutex.h>
-#include <asm/atomic.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <linux/mfd/abx500.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <asm/atomic.h>
+#include <linux/rwsem.h>
+#include <linux/mutex.h>
+#include <stdarg.h>
#include "ab5500.h"
-#define AB5500_CODEC_DEBUG 0
-
/* codec private data */
struct ab5500_codec_dai_data {
};
@@ -56,6 +56,9 @@ static void mask_set_reg(u8 reg, u8 mask, u8 val)
__func__);
return;
}
+ /* Check if the reg value falls within the
+ * range of AB5500 real registers. If
+ * so, set the mask */
if (reg < AB5500_FIRST_REG)
return;
if (reg <= AB5500_LAST_REG) {
@@ -68,6 +71,11 @@ static void mask_set_reg(u8 reg, u8 mask, u8 val)
return;
/* treatment of virtual registers follows */
+ /*Compute the difference between the new value and the old value.
+ *1.If there is no difference, do nothing.
+ *2.If the difference is in the PWR_SHIFT,
+ *set the PWR masks appropriately.
+ */
oldval = virtual_regs[reg - AB5500_LAST_REG - 1];
diff = (val ^ oldval) & mask;
if (!diff)
@@ -76,9 +84,13 @@ static void mask_set_reg(u8 reg, u8 mask, u8 val)
switch (reg) {
case AB5500_VIRTUAL_REG3:
if ((diff & (1 << SPKR1_PWR_SHIFT))) {
- if ((val & (1 << SPKR1_PWR_SHIFT)) == 0)
+ if ((val & (1 << SPKR1_PWR_SHIFT)) == 0) {
+ /* If the new value has PWR_SHIFT disabled, set the
+ * PWR_MASK to 0 */
mask_set_reg(SPKR1, SPKRx_PWR_MASK, 0);
- else
+ }
+ else {
+ /* Else, set the PWR_MASK values based on the old value. */
switch (oldval & SPKR1_MODE_MASK) {
case 0:
mask_set_reg(SPKR1, SPKRx_PWR_MASK,
@@ -94,10 +106,15 @@ static void mask_set_reg(u8 reg, u8 mask, u8 val)
break;
}
}
+ }
if ((diff & (1 << SPKR2_PWR_SHIFT))) {
- if ((val & (1 << SPKR2_PWR_SHIFT)) == 0)
+ if ((val & (1 << SPKR2_PWR_SHIFT)) == 0) {
+ /* If the new value has PWR_SHIFT disabled, set the
+ * PWR_MASK to 0 */
mask_set_reg(SPKR2, SPKRx_PWR_MASK, 0);
- else
+ }
+ else {
+ /* Else, set the PWR_MASK values based on the old value. */
switch (oldval & SPKR2_MODE_MASK) {
case 0:
mask_set_reg(SPKR2, SPKRx_PWR_MASK,
@@ -108,6 +125,7 @@ static void mask_set_reg(u8 reg, u8 mask, u8 val)
SPKRx_PWR_CLS_D_VALUE);
break;
}
+ }
}
break;
@@ -126,6 +144,8 @@ static u8 read_reg(u8 reg)
__func__);
return 0;
}
+ /* Check if the reg value falls within the range of AB5500 real
+ * registers.If so, set the mask */
if (reg < AB5500_FIRST_REG)
return 0;
else if (reg <= AB5500_LAST_REG) {
@@ -143,51 +163,38 @@ static u8 read_reg(u8 reg)
/* Components that can be powered up/down */
enum enum_widget {
widget_ear = 0,
-
widget_auxo1,
widget_auxo2,
widget_auxo3,
widget_auxo4,
-
widget_spkr1,
widget_spkr2,
widget_spkr1_adder,
widget_spkr2_adder,
-
widget_pwm_spkr1,
widget_pwm_spkr2,
-
widget_pwm_spkr1n,
widget_pwm_spkr1p,
widget_pwm_spkr2n,
widget_pwm_spkr2p,
-
widget_line1,
widget_line2,
-
widget_dac1,
widget_dac2,
widget_dac3,
-
widget_rx1,
widget_rx2,
widget_rx3,
-
widget_mic1,
widget_mic2,
-
widget_micbias1,
widget_micbias2,
-
widget_apga1,
widget_apga2,
-
widget_tx1,
widget_tx2,
-
widget_adc1,
widget_adc2,
-
widget_if0_dld_l,
widget_if0_dld_r,
widget_if0_uld_l,
@@ -196,23 +203,19 @@ enum enum_widget {
widget_if1_dld_r,
widget_if1_uld_l,
widget_if1_uld_r,
-
widget_mic1p1,
widget_mic1n1,
widget_mic1p2,
widget_mic1n2,
-
widget_mic2p1,
widget_mic2n1,
widget_mic2p2,
widget_mic2n2,
-
widget_clock,
-
number_of_widgets
};
-#if AB5500_CODEC_DEBUG
+/* This is only meant for debugging */
static const char *widget_names[] = {
"EAR", "AUXO1", "AUXO2", "AUXO3", "AUXO4",
"SPKR1", "SPKR2", "SPKR1_ADDER", "SPKR2_ADDER",
@@ -233,7 +236,6 @@ static const char *widget_names[] = {
"MIC2P1", "MIC2N1", "MIC2P2", "MIC2N2",
"CLOCK"
};
-#endif
struct widget_pm {
enum enum_widget widget;
@@ -806,6 +808,15 @@ static struct snd_kcontrol_new ab5500_snd_controls[] = {
SOC_SINGLE("Negative Charge Pump", NEG_CHARGE_PUMP, 0, 0x03, 0)
};
+/* count the number of 1 */
+#define count_ones(x) ({ \
+ int num; \
+ typeof(x) y = x; \
+ for (num = 0; y; y &= y - 1, num++) \
+ ; \
+ num; \
+ })
+
enum enum_power {
POWER_OFF = 0,
POWER_ON = 1
@@ -857,8 +868,7 @@ static int has_stacked_neighbors(const unsigned long *neighbors)
return bitmap_intersects(stack_map, neighbors, number_of_widgets);
}
-static void power_widget_unlocked(enum enum_power onoff,
- enum enum_widget widget)
+static void power_widget_unlocked(enum enum_power onoff, enum enum_widget widget)
{
enum enum_widget w;
int done;
@@ -867,10 +877,14 @@ static void power_widget_unlocked(enum enum_power onoff,
return;
if (get_widget_power_status(widget) == onoff)
return;
+
for (w = widget, done = 0; !done;) {
unsigned long i;
unsigned long *srcs = widget_pm_array[w].source_list;
unsigned long *sinks = widget_pm_array[w].sink_list;
+
+ dev_info(ab5500_dev, "%s: processing widget %s.\n",
+ __func__, widget_names[w]);
if (onoff == POWER_ON &&
!bitmap_empty(srcs, number_of_widgets) &&
!has_powered_neighbors(srcs)) {
@@ -902,6 +916,10 @@ static void power_widget_unlocked(enum enum_power onoff,
mask_set_reg(widget_pm_array[w].reg,
1 << widget_pm_array[w].shift,
onoff == POWER_ON ? 0xff : 0);
+ dev_info(ab5500_dev, "%s: widget %s powered %s.\n",
+ __func__, widget_names[w],
+ onoff == POWER_ON ? "on" : "off");
+
if (onoff == POWER_ON &&
!bitmap_empty(sinks, number_of_widgets) &&
!has_powered_neighbors(sinks) &&
@@ -941,7 +959,7 @@ static void power_widget_locked(enum enum_power onoff,
mutex_unlock(&ab5500_pm_mutex);
}
-#if AB5500_CODEC_DEBUG
+
static void dump_registers(const char *where, ...)
{
va_list ap;
@@ -955,7 +973,6 @@ static void dump_registers(const char *where, ...)
} while (1);
va_end(ap);
}
-#endif
/**
* update the link two widgets.
@@ -1063,6 +1080,8 @@ static int ab5500_add_widgets(struct snd_soc_codec *codec)
static void power_for_playback(enum enum_power onoff, int ifsel)
{
+ dev_info(ab5500_dev, "%s: interface %d power %s.\n", __func__,
+ ifsel, onoff == POWER_ON ? "on" : "off");
if (mutex_lock_interruptible(&ab5500_pm_mutex)) {
dev_warn(ab5500_dev,
"%s: Signal received while waiting on the PM mutex.\n",
@@ -1078,6 +1097,8 @@ static void power_for_playback(enum enum_power onoff, int ifsel)
static void power_for_capture(enum enum_power onoff, int ifsel)
{
+ dev_info(ab5500_dev, "%s: interface %d power %s", __func__,
+ ifsel, onoff == POWER_ON ? "on" : "off");
if (mutex_lock_interruptible(&ab5500_pm_mutex)) {
dev_warn(ab5500_dev,
"%s: Signal received while waiting on the PM mutex.\n",
@@ -1095,6 +1116,7 @@ static int ab5500_add_controls(struct snd_soc_codec *codec)
{
int err = 0, i, n = ARRAY_SIZE(ab5500_snd_controls);
+ pr_info("%s: %s called.\n", __FILE__, __func__);
for (i = 0; i < n; i++) {
err = snd_ctl_add(codec->card->snd_card, snd_ctl_new1(
&ab5500_snd_controls[i], codec));
@@ -1132,6 +1154,7 @@ static int ab5500_pcm_hw_params(struct snd_pcm_substream *substream,
__func__);
return -EAGAIN;
}
+ dev_info(ab5500_dev, "%s called.\n", __func__);
switch (params_rate(hw_params)) {
case 8000:
val = I2Sx_SR_8000Hz;
@@ -1163,11 +1186,14 @@ static int ab5500_pcm_hw_params(struct snd_pcm_substream *substream,
static int ab5500_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ dev_info(ab5500_dev, "%s called.\n", __func__);
+
/* Configure registers for either playback or capture */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
power_for_playback(POWER_ON, dai->id);
else
power_for_capture(POWER_ON, dai->id);
+ dump_registers(__func__, RX1, AUXO1_ADDER, RX2, AUXO2_ADDER, RX1_DPGA, RX2_DPGA, AUXO1, AUXO2, -1);
return 0;
}
@@ -1175,10 +1201,12 @@ static void ab5500_pcm_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
u8 iface = dai->id == 0 ? INTERFACE0 : INTERFACE1;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dev_info(ab5500_dev, "%s called.\n", __func__);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
power_for_playback(POWER_OFF, dai->id);
- else
+ } else {
power_for_capture(POWER_OFF, dai->id);
+ }
if (!dai->playback_active && !dai->capture_active &&
(read_reg(iface) & I2Sx_MODE_MASK) == 0)
mask_set_reg(iface, MASTER_GENx_PWR_MASK, 0);
@@ -1194,6 +1222,7 @@ static int ab5500_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
u8 iface = (codec_dai->id == 0) ? INTERFACE0 : INTERFACE1;
u8 val = 0;
+ dev_info(ab5500_dev, "%s called.\n", __func__);
switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK |
SND_SOC_DAIFMT_MASTER_MASK)) {
@@ -1341,7 +1370,7 @@ static int ab5500_codec_write_reg(struct snd_soc_codec *codec,
* The APGA is to be turned on/off. The power bit and the
* other bits in the same register won't be changed at the
* same time since they belong to different controls.
- */
+ */
if (diff & (1 << APGAx_PWR_SHIFT)) {
power_widget_locked(value >> APGAx_PWR_SHIFT & 1,
apga);
@@ -1455,7 +1484,8 @@ static struct snd_soc_codec_driver ab5500_codec_drv = {
.resume = ab5500_codec_resume,
.read = ab5500_codec_read_reg,
.write = ab5500_codec_write_reg,
-}; EXPORT_SYMBOL_GPL(ab5500_codec_drv);
+};
+EXPORT_SYMBOL_GPL(ab5500_codec_drv);
static inline void init_playback_route(void)
{
@@ -1542,10 +1572,11 @@ static inline void init_capture_gain(void)
static int __devinit ab5500_platform_probe(struct platform_device *pdev)
{
- int ret;
+ int ret = 0;
u8 reg;
struct ab5500_codec_dai_data *codec_drvdata;
+ pr_info("%s invoked with pdev = %p.\n", __func__, pdev);
ab5500_dev = &pdev->dev;
codec_drvdata = kzalloc(sizeof(struct ab5500_codec_dai_data),
GFP_KERNEL);
@@ -1567,6 +1598,7 @@ static int __devinit ab5500_platform_probe(struct platform_device *pdev)
mask_set_reg(CLOCK, CLOCK_REF_SELECT_MASK | CLOCK_ENABLE_MASK,
1 << CLOCK_REF_SELECT_SHIFT | 1 << CLOCK_ENABLE_SHIFT);
+ printk(KERN_ERR "Clock Setting ab5500\n");
init_playback_route();
init_playback_gain();
init_capture_route();
@@ -1577,6 +1609,7 @@ static int __devinit ab5500_platform_probe(struct platform_device *pdev)
static int __devexit ab5500_platform_remove(struct platform_device *pdev)
{
+ pr_info("%s called.\n", __func__);
mask_set_reg(CLOCK, CLOCK_ENABLE_MASK, 0);
snd_soc_unregister_codec(ab5500_dev);
kfree(platform_get_drvdata(pdev));
@@ -1610,6 +1643,8 @@ static int __devinit ab5500_init(void)
{
int ret;
+ pr_info("%s called.\n", __func__);
+
/* Register codec platform driver. */
ret = platform_driver_register(&ab5500_platform_driver);
if (ret) {
@@ -1621,6 +1656,7 @@ static int __devinit ab5500_init(void)
static void __devexit ab5500_exit(void)
{
+ pr_info("%s called.\n", __func__);
platform_driver_unregister(&ab5500_platform_driver);
}
diff --git a/sound/soc/codecs/ab5500.h b/sound/soc/codecs/ab5500.h
index a401280f3cc..c410259d73a 100644
--- a/sound/soc/codecs/ab5500.h
+++ b/sound/soc/codecs/ab5500.h
@@ -1,6 +1,7 @@
/*
- * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST-Ericsson SA 2011
*
+ * Register definitions for AB5500 codec
* Author: Xie Xiaolei <xie.xiaolei@etericsson.com>
* for ST-Ericsson.
*
@@ -396,4 +397,12 @@
#define PWM_SPKR2P_SEL_SHIFT 3
#define DUMMY_REG 0xff
+
+/* #define SPKR1_PWR_VBR_SHIFT 0 */
+/* #define SPKR1_PWR_CLS_D_SHIFT 1 */
+/* #define SPKR1_PWR_CLS_AB_SHIFT 2 */
+/* #define SPKR2_PWR_VBR_SHIFT 3 */
+/* #define SPKR2_PWR_CLS_D_SHIFT 4 */
+/* #define SPKR2_PWR_CLS_AB_SHIFT 5 */
+
#endif
diff --git a/sound/soc/ux500/u5500.c b/sound/soc/ux500/u5500.c
index 23355baa257..fda8db3472b 100644
--- a/sound/soc/ux500/u5500.c
+++ b/sound/soc/ux500/u5500.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST-Ericsson SA 2011
*
* Author: Xie Xiaolei (xie.xiaolei@stericsson.com)
* for ST-Ericsson.
@@ -12,20 +12,20 @@
*/
#include <linux/io.h>
-#include <linux/spi/spi.h>
#include <sound/soc.h>
-#include <sound/initval.h>
#include <asm/mach-types.h>
#include "ux500_pcm.h"
#include "ux500_msp_dai.h"
+#include <linux/spi/spi.h>
+#include <sound/initval.h>
+
#ifdef CONFIG_SND_SOC_UX500_AB5500
#include "ux500_ab5500.h"
#endif
static struct platform_device *u5500_platform_dev;
-
/* Create dummy devices for platform drivers */
static struct platform_device ux500_pcm = {
@@ -36,6 +36,7 @@ static struct platform_device ux500_pcm = {
},
};
+
/* Define the whole U5500 soundcard, linking platform to the codec-drivers */
struct snd_soc_dai_link u5500_dai_links[] = {
#ifdef CONFIG_SND_SOC_UX500_AB5500
@@ -54,24 +55,8 @@ struct snd_soc_dai_link u5500_dai_links[] = {
.hw_params = ux500_ab5500_hw_params,
}
}
- },
- {
- .name = "ab5500_1",
- .stream_name = "ab5500_1",
- .cpu_dai_name = "i2s.1",
- .codec_dai_name = "ab5500-codec-dai.1",
- .platform_name = "ux500-pcm.0",
- .codec_name = "ab5500-codec.9",
- .init = NULL,
- .ops = (struct snd_soc_ops[]) {
- {
- .startup = ux500_ab5500_startup,
- .shutdown = ux500_ab5500_shutdown,
- .hw_params = ux500_ab5500_hw_params,
- }
- }
- },
#endif
+ }
};
static struct snd_soc_card u5500_drvdata = {
@@ -83,7 +68,7 @@ static struct snd_soc_card u5500_drvdata = {
static int __init u5500_soc_init(void)
{
- int ret;
+ int ret = 0;
pr_debug("%s: Enter.\n", __func__);
diff --git a/sound/soc/ux500/ux500_ab5500.c b/sound/soc/ux500/ux500_ab5500.c
index 89ecc19dfdc..711351e2f55 100644
--- a/sound/soc/ux500/ux500_ab5500.c
+++ b/sound/soc/ux500/ux500_ab5500.c
@@ -1,8 +1,8 @@
/*
- * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST-Ericsson SA 2011
*
- * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
- * Roger Nilsson <roger.xr.nilsson@stericsson.com>
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ * Roger Nilsson roger.xr.nilsson@stericsson.com
* for ST-Ericsson.
*
* License terms:
@@ -14,7 +14,6 @@
#include <sound/soc.h>
#include "../codecs/ab5500.h"
-
int ux500_ab5500_startup(struct snd_pcm_substream *substream)
{
printk(KERN_DEBUG "%s: Enter.\n", __func__);
@@ -35,23 +34,26 @@ int ux500_ab5500_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int ret = 0;
+ int channels = params_channels(params);
+
+ printk(KERN_DEBUG "%s: Enter.\n", __func__);
+ printk(KERN_DEBUG "%s: substream->pcm->name = %s.\n", __func__, substream->pcm->name);
+ printk(KERN_DEBUG "%s: substream->pcm->id = %s.\n", __func__, substream->pcm->id);
+ printk(KERN_DEBUG "%s: substream->name = %s.\n", __func__, substream->name);
+ printk(KERN_DEBUG "%s: substream->number = %d.\n", __func__, substream->number);
+ printk(KERN_DEBUG "%s: channels = %d.\n", __func__, channels);
+ printk(KERN_DEBUG "%s: DAI-index (Codec): %d\n", __func__, codec_dai->id);
+ printk(KERN_DEBUG "%s: DAI-index (Platform): %d\n", __func__, cpu_dai->id);
+
ret = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0) {
- printk(KERN_DEBUG "%s: snd_soc_dai_set_fmt failed with %d.\n",
- __func__,
- ret);
+ SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
+ if (ret < 0)
return ret;
- }
ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
- if (ret < 0) {
- printk(KERN_DEBUG "%s: snd_soc_dai_set_fmt failed with %d.\n",
- __func__,
- ret);
+ if (ret < 0)
return ret;
- }
return ret;
}
diff --git a/sound/soc/ux500/ux500_ab5500.h b/sound/soc/ux500/ux500_ab5500.h
index 977851bbce6..ea69f1a048c 100644
--- a/sound/soc/ux500/ux500_ab5500.h
+++ b/sound/soc/ux500/ux500_ab5500.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) ST-Ericsson SA 2011
*
* Author: Xie Xiaolei (xie.xiaolei@stericsson.com)
* for ST-Ericsson.