Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/arch/arm/mach-msm/qdsp5v2/lpa.c b/arch/arm/mach-msm/qdsp5v2/lpa.c
new file mode 100644
index 0000000..c4e0fee
--- /dev/null
+++ b/arch/arm/mach-msm/qdsp5v2/lpa.c
@@ -0,0 +1,608 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <mach/qdsp5v2/lpa.h>
+#include <mach/qdsp5v2/lpa_hw.h>
+#include <mach/qdsp5v2/msm_lpa.h>
+#include <mach/debug_mm.h>
+
+#define LPA_REG_WRITEL(drv, val, reg)  writel(val, drv->baseaddr + reg)
+#define LPA_REG_READL(drv, reg)  readl(drv->baseaddr + reg)
+
+/* bit 2:0 is reserved because watermarks have to be 64-bit aligned */
+#define LLB_WATERMARK_VAL_MASK 0x00000003
+
+#define LPA_STATUS_SBUF_EN 0x01
+
+struct lpa_drv {
+	void __iomem *baseaddr;
+	u32 obuf_hlb_size;
+	u32 dsp_proc_id;
+	u32 app_proc_id;
+	struct lpa_mem_config nosb_config;
+	struct lpa_mem_config sb_config;
+	u32 status;
+	u32 watermark_bytes;
+	u32 watermark_aheadtime;
+	u32 sample_boundary;
+};
+
+struct lpa_state {
+	struct lpa_drv lpa_drv; /* One instance for now */
+	u32 assigned;
+	struct mutex lpa_lock;
+};
+
+struct lpa_state the_lpa_state;
+
+static void lpa_enable_codec(struct lpa_drv *lpa, bool enable)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CODEC);
+	val = enable ? (val | LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) :
+		(val & ~LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK);
+	val |= LPA_OBUF_CODEC_LOAD_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CODEC);
+	mb();
+}
+
+static void lpa_reset(struct lpa_drv *lpa)
+{
+	u32 status;
+	struct clk *adsp_clk;
+	/* Need to make sure not disable clock while other device is enabled */
+	adsp_clk = clk_get(NULL, "adsp_clk");
+	if (!adsp_clk) {
+		MM_ERR("failed to get adsp clk\n");
+		goto error;
+	}
+	clk_enable(adsp_clk);
+	lpa_enable_codec(lpa, 0);
+	LPA_REG_WRITEL(lpa, (LPA_OBUF_RESETS_MISR_RESET |
+		LPA_OBUF_RESETS_OVERALL_RESET), LPA_OBUF_RESETS);
+	do {
+		status = LPA_REG_READL(lpa, LPA_OBUF_STATUS);
+	} while (!(status & LPA_OBUF_STATUS_RESET_DONE));
+
+	LPA_REG_WRITEL(lpa, LPA_OBUF_ACK_RESET_DONE_BMSK, LPA_OBUF_ACK);
+	mb();
+	clk_disable(adsp_clk);
+	clk_put(adsp_clk);
+error:
+	return;
+}
+
+static void lpa_config_hlb_addr(struct lpa_drv *lpa)
+{
+	u32 val, min_addr = 0, max_addr = min_addr + lpa->obuf_hlb_size;
+
+	val = (min_addr & LPA_OBUF_HLB_MIN_ADDR_SEG_BMSK) |
+	LPA_OBUF_HLB_MIN_ADDR_LOAD_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_HLB_MIN_ADDR);
+	val = max_addr & LPA_OBUF_HLB_MAX_ADDR_SEG_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_HLB_MAX_ADDR);
+}
+
+static void lpa_powerup_mem_bank(struct lpa_drv *lpa,
+	struct lpa_mem_bank_select *bank)
+{
+	u32 status, val;
+
+	status = LPA_REG_READL(lpa, LPA_OBUF_MEMORY_CONTROL);
+	val = ((*((u32 *) bank)) << LPA_OBUF_MEM_CTL_PWRUP_SHFT) &
+	LPA_OBUF_MEM_CTL_PWRUP_BMSK;
+	val |= status;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_MEMORY_CONTROL);
+}
+
+static void lpa_enable_interrupt(struct lpa_drv *lpa, u32 proc_id)
+{
+	u32 val;
+
+	proc_id &= LPA_OBUF_INTR_EN_BMSK;
+	val = 0x1 << proc_id;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_INTR_ENABLE);
+}
+
+static void lpa_config_llb_addr(struct lpa_drv *lpa, u32 min_addr, u32 max_addr)
+{
+	u32 val;
+
+	val = (min_addr & LPA_OBUF_LLB_MIN_ADDR_SEG_BMSK) |
+	LPA_OBUF_LLB_MIN_ADDR_LOAD_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_LLB_MIN_ADDR);
+	val = max_addr & LPA_OBUF_LLB_MAX_ADDR_SEG_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_LLB_MAX_ADDR);
+}
+
+static void lpa_config_sb_addr(struct lpa_drv *lpa, u32 min_addr, u32 max_addr)
+{
+	u32 val;
+
+	val = (min_addr & LPA_OBUF_SB_MIN_ADDR_SEG_BMSK) |
+	LPA_OBUF_SB_MIN_ADDR_LOAD_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_SB_MIN_ADDR);
+	val = max_addr & LPA_OBUF_SB_MAX_ADDR_SEG_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_SB_MAX_ADDR);
+}
+
+static void lpa_switch_sb(struct lpa_drv *lpa)
+{
+	if (lpa->status & LPA_STATUS_SBUF_EN) {
+		lpa_config_llb_addr(lpa, lpa->sb_config.llb_min_addr,
+		lpa->sb_config.llb_max_addr);
+		lpa_config_sb_addr(lpa, lpa->sb_config.sb_min_addr,
+		lpa->sb_config.sb_max_addr);
+	} else {
+		lpa_config_llb_addr(lpa, lpa->nosb_config.llb_min_addr,
+		lpa->nosb_config.llb_max_addr);
+		lpa_config_sb_addr(lpa, lpa->nosb_config.sb_min_addr,
+		lpa->nosb_config.sb_max_addr);
+	}
+}
+
+static u8 lpa_req_wmark_id(struct lpa_drv *lpa)
+{
+  return (u8) (LPA_REG_READL(lpa, LPA_OBUF_WMARK_ASSIGN) &
+	LPA_OBUF_WMARK_ASSIGN_BMSK);
+}
+
+static void lpa_enable_llb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+	u32 wmark_id, u32 cpu_id)
+{
+	u32 val;
+
+	wmark_id = (wmark_id > 3) ? 0 : wmark_id;
+	val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_n_LLB_ADDR(wmark_id));
+	val &= ~LPA_OBUF_LLB_WMARK_CTRL_BMSK;
+	val &= ~LPA_OBUF_LLB_WMARK_MAP_BMSK;
+	val |= (wmark_ctrl << LPA_OBUF_LLB_WMARK_CTRL_SHFT) &
+	LPA_OBUF_LLB_WMARK_CTRL_BMSK;
+	val |= (cpu_id << LPA_OBUF_LLB_WMARK_MAP_SHFT) &
+	LPA_OBUF_LLB_WMARK_MAP_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_n_LLB_ADDR(wmark_id));
+}
+
+static void lpa_enable_sb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+	u32 cpu_id)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_SB);
+	val &= ~LPA_OBUF_SB_WMARK_CTRL_BMSK;
+	val &= ~LPA_OBUF_SB_WMARK_MAP_BMSK;
+	val |= (wmark_ctrl << LPA_OBUF_SB_WMARK_CTRL_SHFT) &
+	LPA_OBUF_SB_WMARK_CTRL_BMSK;
+	val |= (cpu_id << LPA_OBUF_SB_WMARK_MAP_SHFT) &
+	LPA_OBUF_SB_WMARK_MAP_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_SB);
+}
+static void lpa_enable_hlb_wmark(struct lpa_drv *lpa, u32 wmark_ctrl,
+	u32 cpu_id)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_WMARK_HLB);
+	val &= ~LPA_OBUF_HLB_WMARK_CTRL_BMSK;
+	val &= ~LPA_OBUF_HLB_WMARK_MAP_BMSK;
+	val |= (wmark_ctrl << LPA_OBUF_HLB_WMARK_CTRL_SHFT) &
+	LPA_OBUF_HLB_WMARK_CTRL_BMSK;
+	val |= (cpu_id << LPA_OBUF_HLB_WMARK_MAP_SHFT) &
+	LPA_OBUF_HLB_WMARK_MAP_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_WMARK_HLB);
+}
+
+static void lpa_enable_utc(struct lpa_drv *lpa, bool enable, u32 cpu_id)
+{
+	u32 val;
+
+	val = (cpu_id << LPA_OBUF_UTC_CONFIG_MAP_SHFT) &
+	LPA_OBUF_UTC_CONFIG_MAP_BMSK;
+	enable = (enable ? 1 : 0);
+	val = (enable << LPA_OBUF_UTC_CONFIG_EN_SHFT) &
+	LPA_OBUF_UTC_CONFIG_EN_BMSK;
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_UTC_CONFIG);
+}
+
+static void lpa_enable_mixing(struct lpa_drv *lpa, bool enable)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+	val = (enable ? val | LPA_OBUF_CONTROL_LLB_EN_BMSK :
+		val & ~LPA_OBUF_CONTROL_LLB_EN_BMSK);
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+static void lpa_enable_mixer_saturation(struct lpa_drv *lpa, u32 buf_id,
+	bool enable)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+
+	switch (buf_id) {
+	case LPA_BUF_ID_LLB:
+		val = enable ? (val | LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK) :
+		(val & ~LPA_OBUF_CONTROL_LLB_SAT_EN_BMSK);
+		break;
+
+	case LPA_BUF_ID_SB:
+		val = enable ? (val | LPA_OBUF_CONTROL_SB_SAT_EN_BMSK) :
+		(val & ~LPA_OBUF_CONTROL_SB_SAT_EN_BMSK);
+		break;
+	}
+
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+static void lpa_enable_obuf(struct lpa_drv *lpa, u32 buf_id, bool enable)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+
+	switch (buf_id) {
+	case LPA_BUF_ID_HLB:
+		val = enable ? (val | LPA_OBUF_CONTROL_HLB_EN_BMSK) :
+			(val & ~LPA_OBUF_CONTROL_HLB_EN_BMSK);
+		break;
+
+	case LPA_BUF_ID_LLB:
+		val = enable ? (val | LPA_OBUF_CONTROL_LLB_EN_BMSK) :
+		(val & ~LPA_OBUF_CONTROL_LLB_EN_BMSK);
+		break;
+
+	case LPA_BUF_ID_SB:
+		val = enable ? (val | LPA_OBUF_CONTROL_SB_EN_BMSK) :
+			(val & ~LPA_OBUF_CONTROL_SB_EN_BMSK);
+		break;
+	}
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+struct lpa_drv *lpa_get(void)
+{
+	struct lpa_mem_bank_select mem_bank;
+	struct lpa_drv *ret_lpa = &the_lpa_state.lpa_drv;
+
+	mutex_lock(&the_lpa_state.lpa_lock);
+	if (the_lpa_state.assigned) {
+		MM_ERR("LPA HW accupied\n");
+		ret_lpa = ERR_PTR(-EBUSY);
+		goto error;
+	}
+	/* perform initialization */
+	lpa_reset(ret_lpa);
+	/* Config adec param */
+	/* Initialize LLB/SB min/max address */
+	lpa_switch_sb(ret_lpa);
+	/* Config HLB minx/max address */
+	lpa_config_hlb_addr(ret_lpa);
+
+	/* Power up all memory bank for now */
+	mem_bank.b0 = 1;
+	mem_bank.b1 = 1;
+	mem_bank.b2 = 1;
+	mem_bank.b3 = 1;
+	mem_bank.b4 = 1;
+	mem_bank.b5 = 1;
+	mem_bank.b6 = 1;
+	mem_bank.b7 = 1;
+	mem_bank.b8 = 1;
+	mem_bank.b9 = 1;
+	mem_bank.b10 = 1;
+	mem_bank.llb = 1;
+	lpa_powerup_mem_bank(ret_lpa, &mem_bank);
+
+	while
+	(lpa_req_wmark_id(ret_lpa) != LPA_OBUF_WMARK_ASSIGN_DONE);
+
+	lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 0,
+	ret_lpa->dsp_proc_id);
+	lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 1,
+	ret_lpa->dsp_proc_id);
+	lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 2,
+	ret_lpa->app_proc_id);
+	lpa_enable_llb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED, 3,
+	ret_lpa->app_proc_id);
+	lpa_enable_hlb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED,
+	ret_lpa->dsp_proc_id);
+	lpa_enable_sb_wmark(ret_lpa, LPA_WMARK_CTL_DISABLED,
+	ret_lpa->dsp_proc_id);
+	lpa_enable_utc(ret_lpa, 0, LPA_OBUF_UTC_CONFIG_NO_INTR);
+
+	lpa_enable_mixing(ret_lpa, 1);
+	lpa_enable_mixer_saturation(ret_lpa, LPA_BUF_ID_LLB, 1);
+
+	lpa_enable_obuf(ret_lpa, LPA_BUF_ID_HLB, 0);
+	lpa_enable_obuf(ret_lpa, LPA_BUF_ID_LLB, 1);
+	if (ret_lpa->status & LPA_STATUS_SBUF_EN) {
+		lpa_enable_mixer_saturation(ret_lpa, LPA_BUF_ID_SB, 1);
+		lpa_enable_obuf(ret_lpa, LPA_BUF_ID_SB, 1);
+	}
+
+	lpa_enable_interrupt(ret_lpa, ret_lpa->dsp_proc_id);
+	mb();
+	the_lpa_state.assigned++;
+error:
+	mutex_unlock(&the_lpa_state.lpa_lock);
+	return ret_lpa;
+}
+EXPORT_SYMBOL(lpa_get);
+
+void lpa_put(struct lpa_drv *lpa)
+{
+
+	mutex_lock(&the_lpa_state.lpa_lock);
+	if (!lpa || &the_lpa_state.lpa_drv != lpa) {
+		MM_ERR("invalid arg\n");
+		goto error;
+	}
+	/* Deinitialize */
+	the_lpa_state.assigned--;
+error:
+	mutex_unlock(&the_lpa_state.lpa_lock);
+}
+EXPORT_SYMBOL(lpa_put);
+
+int lpa_cmd_codec_config(struct lpa_drv *lpa,
+	struct lpa_codec_config *config_ptr)
+{
+	u32 sample_rate;
+	u32 num_channels;
+	u32 width;
+	u32 val = 0;
+
+	if (!lpa || !config_ptr) {
+		MM_ERR("invalid parameters\n");
+		return -EINVAL;
+	}
+
+	switch (config_ptr->num_channels) {
+	case 8:
+		num_channels = LPA_NUM_CHAN_7P1;
+		break;
+	case 6:
+		num_channels = LPA_NUM_CHAN_5P1;
+		break;
+	case 4:
+		num_channels = LPA_NUM_CHAN_4_CHANNEL;
+		break;
+	case 2:
+		num_channels = LPA_NUM_CHAN_STEREO;
+		break;
+	case 1:
+		num_channels = LPA_NUM_CHAN_MONO;
+		break;
+	default:
+		MM_ERR("unsupported number of channel\n");
+		goto error;
+	}
+	val |= (num_channels << LPA_OBUF_CODEC_NUM_CHAN_SHFT) &
+	LPA_OBUF_CODEC_NUM_CHAN_BMSK;
+
+	switch (config_ptr->sample_rate) {
+	case 96000:
+		sample_rate = LPA_SAMPLE_RATE_96KHZ;
+		break;
+	case 64000:
+		sample_rate = LPA_SAMPLE_RATE_64KHZ;
+		break;
+	case 48000:
+		sample_rate = LPA_SAMPLE_RATE_48KHZ;
+		break;
+	case 44100:
+		sample_rate = LPA_SAMPLE_RATE_44P1KHZ;
+		break;
+	case 32000:
+		sample_rate = LPA_SAMPLE_RATE_32KHZ;
+		break;
+	case 22050:
+		sample_rate = LPA_SAMPLE_RATE_22P05KHZ;
+		break;
+	case 16000:
+		sample_rate = LPA_SAMPLE_RATE_16KHZ;
+		break;
+	case 11025:
+		sample_rate = LPA_SAMPLE_RATE_11P025KHZ;
+		break;
+	case 8000:
+		sample_rate = LPA_SAMPLE_RATE_8KHZ;
+		break;
+	default:
+		MM_ERR("unsupported sample rate \n");
+		goto error;
+	}
+	val |= (sample_rate << LPA_OBUF_CODEC_SAMP_SHFT) &
+		LPA_OBUF_CODEC_SAMP_BMSK;
+	switch (config_ptr->sample_width) {
+	case 32:
+		width = LPA_BITS_PER_CHAN_32BITS;
+		break;
+	case 24:
+		width = LPA_BITS_PER_CHAN_24BITS;
+		break;
+	case 16:
+		width = LPA_BITS_PER_CHAN_16BITS;
+		break;
+	default:
+		MM_ERR("unsupported sample width \n");
+		goto error;
+	}
+	val |= (width << LPA_OBUF_CODEC_BITS_PER_CHAN_SHFT) &
+		LPA_OBUF_CODEC_BITS_PER_CHAN_BMSK;
+
+	val |= LPA_OBUF_CODEC_LOAD_BMSK;
+	val |= (config_ptr->output_interface << LPA_OBUF_CODEC_INTF_SHFT) &
+	LPA_OBUF_CODEC_INTF_BMSK;
+
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CODEC);
+	mb();
+
+	return 0;
+error:
+	return -EINVAL;
+}
+EXPORT_SYMBOL(lpa_cmd_codec_config);
+
+static int lpa_check_llb_clear(struct lpa_drv *lpa)
+{
+	u32 val;
+	val = LPA_REG_READL(lpa, LPA_OBUF_STATUS);
+
+	return !(val & LPA_OBUF_STATUS_LLB_CLR_BMSK);
+}
+
+static void lpa_clear_llb(struct lpa_drv *lpa)
+{
+	u32 val;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CONTROL);
+	LPA_REG_WRITEL(lpa, (val | LPA_OBUF_CONTROL_LLB_CLR_CMD_BMSK),
+	LPA_OBUF_CONTROL);
+	lpa_enable_obuf(lpa, LPA_BUF_ID_LLB, 0);
+
+	while (!lpa_check_llb_clear(lpa))
+		udelay(100);
+	LPA_REG_WRITEL(lpa, val, LPA_OBUF_CONTROL);
+}
+
+int lpa_cmd_enable_codec(struct lpa_drv *lpa, bool enable)
+{
+	u32 val;
+	struct lpa_mem_bank_select mem_bank;
+
+	MM_DBG(" %s\n", (enable ? "enable" : "disable"));
+
+	if (!lpa)
+		return -EINVAL;
+
+	val = LPA_REG_READL(lpa, LPA_OBUF_CODEC);
+
+	if (enable) {
+		if (val & LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK)
+			return -EBUSY;
+		/* Power up all memory bank for now */
+		mem_bank.b0 = 1;
+		mem_bank.b1 = 1;
+		mem_bank.b2 = 1;
+		mem_bank.b3 = 1;
+		mem_bank.b4 = 1;
+		mem_bank.b5 = 1;
+		mem_bank.b6 = 1;
+		mem_bank.b7 = 1;
+		mem_bank.b8 = 1;
+		mem_bank.b9 = 1;
+		mem_bank.b10 = 1;
+		mem_bank.llb = 1;
+		lpa_powerup_mem_bank(lpa, &mem_bank);
+
+		/*clear LLB*/
+		lpa_clear_llb(lpa);
+
+		lpa_enable_codec(lpa, 1);
+		MM_DBG("LPA codec is enabled\n");
+	} else {
+		if (val & LPA_OBUF_CODEC_CODEC_INTF_EN_BMSK) {
+			lpa_enable_codec(lpa, 0);
+			MM_DBG("LPA codec is disabled\n");
+		} else
+			MM_ERR("LPA codec is already disable\n");
+	}
+	mb();
+	return 0;
+}
+EXPORT_SYMBOL(lpa_cmd_enable_codec);
+
+static int lpa_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct resource *mem_src;
+	struct msm_lpa_platform_data *pdata;
+
+	MM_INFO("lpa probe\n");
+
+	if (!pdev || !pdev->dev.platform_data) {
+		MM_ERR("no plaform data\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	mem_src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpa");
+	if (!mem_src) {
+		MM_ERR("LPA base address undefined\n");
+		rc = -ENODEV;
+		goto error;
+	}
+
+	pdata = pdev->dev.platform_data;
+	the_lpa_state.lpa_drv.baseaddr = ioremap(mem_src->start,
+	(mem_src->end - mem_src->start) + 1);
+	if (!the_lpa_state.lpa_drv.baseaddr) {
+		rc = -ENOMEM;
+		goto error;
+	}
+
+	the_lpa_state.lpa_drv.obuf_hlb_size = pdata->obuf_hlb_size;
+	the_lpa_state.lpa_drv.dsp_proc_id = pdata->dsp_proc_id;
+	the_lpa_state.lpa_drv.app_proc_id = pdata->app_proc_id;
+	the_lpa_state.lpa_drv.nosb_config = pdata->nosb_config;
+	the_lpa_state.lpa_drv.sb_config = pdata->sb_config;
+	/* default to enable summing buffer */
+	the_lpa_state.lpa_drv.status = LPA_STATUS_SBUF_EN;
+
+error:
+	return rc;
+
+}
+
+static int lpa_remove(struct platform_device *pdev)
+{
+	iounmap(the_lpa_state.lpa_drv.baseaddr);
+	return 0;
+}
+
+static struct platform_driver lpa_driver = {
+	.probe = lpa_probe,
+	.remove = lpa_remove,
+	.driver = {
+		.name = "lpa",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init lpa_init(void)
+{
+	the_lpa_state.assigned = 0;
+	mutex_init(&the_lpa_state.lpa_lock);
+	return platform_driver_register(&lpa_driver);
+}
+
+static void __exit lpa_exit(void)
+{
+	platform_driver_unregister(&lpa_driver);
+}
+
+module_init(lpa_init);
+module_exit(lpa_exit);
+
+MODULE_DESCRIPTION("MSM LPA driver");
+MODULE_LICENSE("GPL v2");