msm: APRv2: Add APRv2 support

The MSM platforms that turn off LPASS core by default have to bring up
LPASS core prior to talk with it.
Current implementation is during bootup, before the slimbus slave device
is up, the slimbus master on apps would try to configure the slimbus
hardware.
Add APRv2 driver to let adsp loader to bring up LPASS core on and make
slimbus driver to configure hardware after bringing up LPASS core.

Change-Id: I9c45e229e6e4c5c142d8a327509d66d9662b52d4
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
This commit is contained in:
Joonwoo Park 2012-08-02 10:55:54 -07:00 committed by Stephen Boyd
parent f2b1901295
commit b1aa9c2941
7 changed files with 543 additions and 238 deletions

View File

@ -256,7 +256,7 @@ config ARCH_MSM8974
select MSM_RPM_SMD
select REGULATOR
select HAVE_ARM_ARCH_TIMER
select MSM_QDSP6_APR
select MSM_QDSP6_APRV2
select MSM_QDSP6V2_CODECS
select MSM_AUDIO_QDSP6V2 if SND_SOC
select MSM_RPM_REGULATOR_SMD
@ -2211,6 +2211,16 @@ config MSM_QDSP6_APR
used by audio driver to configure QDSP6's
ASM, ADM and AFE.
config MSM_QDSP6_APRV2
bool "Audio QDSP6 APRv2 support"
depends on MSM_SMD
default n
help
Enable APRv2 IPC protocol support between
application processor and QDSP6. APR is
used by audio driver to configure QDSP6's
ASM, ADM and AFE.
config MSM_QDSP6_CODECS
bool "Audio Codecs on QDSP6 APR "
depends on MSM_SMD

View File

@ -13,13 +13,18 @@
#ifndef __APR_H_
#define __APR_H_
#define APR_Q6_NOIMG 0
#define APR_Q6_LOADING 1
#define APR_Q6_LOADED 2
#include <linux/mutex.h>
enum apr_subsys_state {
APR_SUBSYS_DOWN,
APR_SUBSYS_UP,
APR_SUBSYS_LOADED,
};
struct apr_q6 {
void *pil;
uint32_t state;
atomic_t q6_state;
atomic_t modem_state;
struct mutex lock;
};
@ -138,6 +143,12 @@ struct apr_client {
struct apr_svc svc[APR_SVC_MAX];
};
int apr_load_adsp_image(void);
struct apr_client *apr_get_client(int dest_id, int client_id);
int apr_wait_for_device_up(int dest_id);
int apr_get_svc(const char *svc_name, int dest_id, int *client_id,
int *svc_idx, int *svc_id);
void apr_cb_func(void *buf, int len, void *priv);
struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
uint32_t src_port, void *priv);
inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port,
@ -149,4 +160,9 @@ int apr_deregister(void *handle);
void change_q6_state(int state);
void q6audio_dsp_not_responding(void);
void apr_reset(void *handle);
enum apr_subsys_state apr_get_modem_state(void);
void apr_set_modem_state(enum apr_subsys_state state);
enum apr_subsys_state apr_get_q6_state(void);
int apr_set_q6_state(enum apr_subsys_state state);
void apr_set_subsys_state(void);
#endif

View File

@ -10,7 +10,8 @@ obj-y += snddev_hdmi.o
obj-y += audio_mvs.o
obj-$(CONFIG_FB_MSM_HDMI_MSM_PANEL) += lpa_if_hdmi.o
endif
obj-$(CONFIG_MSM_QDSP6_APR) += apr.o apr_tal.o q6core.o dsp_debug.o
obj-$(CONFIG_MSM_QDSP6_APR) += apr.o apr_v1.o apr_tal.o q6core.o dsp_debug.o
obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o q6core.o dsp_debug.o
obj-y += audio_acdb.o
ifdef CONFIG_ARCH_MSM9615
obj-y += rtac.o
@ -23,4 +24,5 @@ obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o aud
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += rtac_v2.o q6audio_v2.o q6audio_v2_aio.o
obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_evrc.o audio_qcelp.o amrwb_in.o
obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/

View File

@ -15,7 +15,6 @@
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/wait.h>
@ -36,13 +35,11 @@
#include <mach/subsystem_notif.h>
#include <mach/subsystem_restart.h>
struct apr_q6 q6;
struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
static atomic_t dsp_state;
static atomic_t modem_state;
static struct apr_q6 q6;
static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
static wait_queue_head_t dsp_wait;
static wait_queue_head_t modem_wait;
static wait_queue_head_t dsp_wait;
static wait_queue_head_t modem_wait;
/* Subsystem restart: QDSP6 data, functions */
static struct workqueue_struct *apr_reset_workqueue;
static void apr_reset_deregister(struct work_struct *work);
@ -51,6 +48,199 @@ struct apr_reset_work {
struct work_struct work;
};
struct apr_svc_table {
char name[64];
int idx;
int id;
int client_id;
};
static const struct apr_svc_table svc_tbl_audio[] = {
{
.name = "AFE",
.idx = 0,
.id = APR_SVC_AFE,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "ASM",
.idx = 1,
.id = APR_SVC_ASM,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "ADM",
.idx = 2,
.id = APR_SVC_ADM,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "CORE",
.idx = 3,
.id = APR_SVC_ADSP_CORE,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "TEST",
.idx = 4,
.id = APR_SVC_TEST_CLIENT,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "MVM",
.idx = 5,
.id = APR_SVC_ADSP_MVM,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "CVS",
.idx = 6,
.id = APR_SVC_ADSP_CVS,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "CVP",
.idx = 7,
.id = APR_SVC_ADSP_CVP,
.client_id = APR_CLIENT_AUDIO,
},
{
.name = "USM",
.idx = 8,
.id = APR_SVC_USM,
.client_id = APR_CLIENT_AUDIO,
},
};
static struct apr_svc_table svc_tbl_voice[] = {
{
.name = "VSM",
.idx = 0,
.id = APR_SVC_VSM,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "VPM",
.idx = 1,
.id = APR_SVC_VPM,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "MVS",
.idx = 2,
.id = APR_SVC_MVS,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "MVM",
.idx = 3,
.id = APR_SVC_MVM,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "CVS",
.idx = 4,
.id = APR_SVC_CVS,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "CVP",
.idx = 5,
.id = APR_SVC_CVP,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "SRD",
.idx = 6,
.id = APR_SVC_SRD,
.client_id = APR_CLIENT_VOICE,
},
{
.name = "TEST",
.idx = 7,
.id = APR_SVC_TEST_CLIENT,
.client_id = APR_CLIENT_VOICE,
},
};
enum apr_subsys_state apr_get_modem_state(void)
{
return atomic_read(&q6.modem_state);
}
void apr_set_modem_state(enum apr_subsys_state state)
{
atomic_set(&q6.modem_state, state);
}
enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev,
enum apr_subsys_state new)
{
return atomic_cmpxchg(&q6.modem_state, prev, new);
}
enum apr_subsys_state apr_get_q6_state(void)
{
return atomic_read(&q6.q6_state);
}
EXPORT_SYMBOL_GPL(apr_get_q6_state);
int apr_set_q6_state(enum apr_subsys_state state)
{
pr_debug("%s: setting adsp state %d\n", __func__, state);
if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED)
return -EINVAL;
atomic_set(&q6.q6_state, state);
return 0;
}
EXPORT_SYMBOL_GPL(apr_set_q6_state);
enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
enum apr_subsys_state new)
{
return atomic_cmpxchg(&q6.q6_state, prev, new);
}
int apr_wait_for_device_up(int dest_id)
{
int rc = -1;
if (dest_id == APR_DEST_MODEM)
rc = wait_event_interruptible_timeout(modem_wait,
(apr_get_modem_state() == APR_SUBSYS_UP),
(1 * HZ));
else if (dest_id == APR_DEST_QDSP6)
rc = wait_event_interruptible_timeout(dsp_wait,
(apr_get_q6_state() == APR_SUBSYS_UP),
(1 * HZ));
else
pr_err("%s: unknown dest_id %d\n", __func__, dest_id);
/* returns left time */
return rc;
}
int apr_load_adsp_image(void)
{
int rc = 0;
mutex_lock(&q6.lock);
if (apr_get_q6_state() == APR_SUBSYS_UP) {
q6.pil = pil_get("q6");
if (IS_ERR(q6.pil)) {
rc = PTR_ERR(q6.pil);
pr_err("APR: Unable to load q6 image, error:%d\n", rc);
} else {
apr_set_q6_state(APR_SUBSYS_LOADED);
pr_debug("APR: Image is loaded, stated\n");
}
} else
pr_debug("APR: cannot load state %d\n", apr_get_q6_state());
mutex_unlock(&q6.lock);
return rc;
}
struct apr_client *apr_get_client(int dest_id, int client_id)
{
return &client[dest_id][client_id];
}
int apr_send_pkt(void *handle, uint32_t *buf)
{
@ -72,11 +262,11 @@ int apr_send_pkt(void *handle, uint32_t *buf)
}
if ((svc->dest_id == APR_DEST_QDSP6) &&
(atomic_read(&dsp_state) == 0)) {
pr_err("apr: Still dsp is not Up\n");
(apr_get_q6_state() != APR_SUBSYS_LOADED)) {
pr_err("%s: Still dsp is not Up\n", __func__);
return -ENETRESET;
} else if ((svc->dest_id == APR_DEST_MODEM) &&
(atomic_read(&modem_state) == 0)) {
(apr_get_modem_state() == APR_SUBSYS_DOWN)) {
pr_err("apr: Still Modem is not Up\n");
return -ENETRESET;
}
@ -111,7 +301,7 @@ int apr_send_pkt(void *handle, uint32_t *buf)
return w_len;
}
static void apr_cb_func(void *buf, int len, void *priv)
void apr_cb_func(void *buf, int len, void *priv)
{
struct apr_client_data data;
struct apr_client *apr_client;
@ -136,8 +326,7 @@ static void apr_cb_func(void *buf, int len, void *priv)
pr_debug("\n*****************\n");
if (!buf || len <= APR_HDR_SIZE) {
pr_err("APR: Improper apr pkt received:%p %d\n",
buf, len);
pr_err("APR: Improper apr pkt received:%p %d\n", buf, len);
return;
}
hdr = buf;
@ -162,8 +351,7 @@ static void apr_cb_func(void *buf, int len, void *priv)
}
msg_type = hdr->hdr_field;
msg_type = (msg_type >> 0x08) & 0x0003;
if (msg_type >= APR_MSG_TYPE_MAX &&
msg_type != APR_BASIC_RSP_RESULT) {
if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) {
pr_err("APR: Wrong message type: %d\n", msg_type);
return;
}
@ -180,8 +368,8 @@ static void apr_cb_func(void *buf, int len, void *priv)
if (hdr->src_domain == APR_DOMAIN_MODEM) {
src = APR_DEST_MODEM;
if (svc == APR_SVC_MVS || svc == APR_SVC_MVM ||
svc == APR_SVC_CVS || svc == APR_SVC_CVP ||
svc == APR_SVC_TEST_CLIENT)
svc == APR_SVC_CVS || svc == APR_SVC_CVP ||
svc == APR_SVC_TEST_CLIENT)
clnt = APR_CLIENT_VOICE;
else {
pr_err("APR: Wrong svc :%d\n", svc);
@ -190,11 +378,11 @@ static void apr_cb_func(void *buf, int len, void *priv)
} else if (hdr->src_domain == APR_DOMAIN_ADSP) {
src = APR_DEST_QDSP6;
if (svc == APR_SVC_AFE || svc == APR_SVC_ASM ||
svc == APR_SVC_VSM || svc == APR_SVC_VPM ||
svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
svc == APR_SVC_USM ||
svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP)
svc == APR_SVC_VSM || svc == APR_SVC_VPM ||
svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
svc == APR_SVC_USM ||
svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP)
clnt = APR_CLIENT_AUDIO;
else {
pr_err("APR: Wrong svc :%d\n", svc);
@ -220,7 +408,7 @@ static void apr_cb_func(void *buf, int len, void *priv)
}
pr_debug("svc_idx = %d\n", i);
pr_debug("%x %x %x %p %p\n", c_svc->id, c_svc->dest_id,
c_svc->client_id, c_svc->fn, c_svc->priv);
c_svc->client_id, c_svc->fn, c_svc->priv);
data.payload_size = hdr->pkt_size - hdr_size;
data.opcode = hdr->opcode;
data.src = src;
@ -241,199 +429,39 @@ static void apr_cb_func(void *buf, int len, void *priv)
pr_err("APR: Rxed a packet for NULL callback\n");
}
struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
uint32_t src_port, void *priv)
int apr_get_svc(const char *svc_name, int dest_id, int *client_id,
int *svc_idx, int *svc_id)
{
int client_id = 0;
int svc_idx = 0;
int svc_id = 0;
int dest_id = 0;
int temp_port = 0;
struct apr_svc *svc = NULL;
int rc = 0;
int i;
int size;
struct apr_svc_table *tbl;
int ret = 0;
if (!dest || !svc_name || !svc_fn)
return NULL;
if (!strncmp(dest, "ADSP", 4))
dest_id = APR_DEST_QDSP6;
else if (!strncmp(dest, "MODEM", 5)) {
dest_id = APR_DEST_MODEM;
if (dest_id == APR_DEST_QDSP6) {
tbl = (struct apr_svc_table *)&svc_tbl_audio;
size = ARRAY_SIZE(svc_tbl_audio);
} else {
pr_err("APR: wrong destination\n");
goto done;
tbl = (struct apr_svc_table *)&svc_tbl_voice;
size = ARRAY_SIZE(svc_tbl_voice);
}
if ((dest_id == APR_DEST_QDSP6) &&
(atomic_read(&dsp_state) == 0)) {
pr_info("%s: Wait for Lpass to bootup\n", __func__);
rc = wait_event_interruptible_timeout(dsp_wait,
(atomic_read(&dsp_state) == 1), (1 * HZ));
if (rc == 0) {
pr_err("%s: DSP is not Up\n", __func__);
return NULL;
}
pr_info("%s: Lpass Up\n", __func__);
} else if ((dest_id == APR_DEST_MODEM) &&
(atomic_read(&modem_state) == 0)) {
pr_info("%s: Wait for modem to bootup\n", __func__);
rc = wait_event_interruptible_timeout(modem_wait,
(atomic_read(&modem_state) == 1), (1 * HZ));
if (rc == 0) {
pr_err("%s: Modem is not Up\n", __func__);
return NULL;
}
pr_info("%s: modem Up\n", __func__);
}
if (!strncmp(svc_name, "AFE", 3)) {
client_id = APR_CLIENT_AUDIO;
svc_idx = 0;
svc_id = APR_SVC_AFE;
} else if (!strncmp(svc_name, "ASM", 3)) {
client_id = APR_CLIENT_AUDIO;
svc_idx = 1;
svc_id = APR_SVC_ASM;
} else if (!strncmp(svc_name, "ADM", 3)) {
client_id = APR_CLIENT_AUDIO;
svc_idx = 2;
svc_id = APR_SVC_ADM;
} else if (!strncmp(svc_name, "CORE", 4)) {
client_id = APR_CLIENT_AUDIO;
svc_idx = 3;
svc_id = APR_SVC_ADSP_CORE;
} else if (!strncmp(svc_name, "TEST", 4)) {
if (dest_id == APR_DEST_QDSP6) {
client_id = APR_CLIENT_AUDIO;
svc_idx = 4;
} else {
client_id = APR_CLIENT_VOICE;
svc_idx = 7;
}
svc_id = APR_SVC_TEST_CLIENT;
} else if (!strncmp(svc_name, "VSM", 3)) {
client_id = APR_CLIENT_VOICE;
svc_idx = 0;
svc_id = APR_SVC_VSM;
} else if (!strncmp(svc_name, "VPM", 3)) {
client_id = APR_CLIENT_VOICE;
svc_idx = 1;
svc_id = APR_SVC_VPM;
} else if (!strncmp(svc_name, "MVS", 3)) {
client_id = APR_CLIENT_VOICE;
svc_idx = 2;
svc_id = APR_SVC_MVS;
} else if (!strncmp(svc_name, "MVM", 3)) {
if (dest_id == APR_DEST_MODEM) {
client_id = APR_CLIENT_VOICE;
svc_idx = 3;
svc_id = APR_SVC_MVM;
} else {
client_id = APR_CLIENT_AUDIO;
svc_idx = 5;
svc_id = APR_SVC_ADSP_MVM;
}
} else if (!strncmp(svc_name, "CVS", 3)) {
if (dest_id == APR_DEST_MODEM) {
client_id = APR_CLIENT_VOICE;
svc_idx = 4;
svc_id = APR_SVC_CVS;
} else {
client_id = APR_CLIENT_AUDIO;
svc_idx = 6;
svc_id = APR_SVC_ADSP_CVS;
}
} else if (!strncmp(svc_name, "CVP", 3)) {
if (dest_id == APR_DEST_MODEM) {
client_id = APR_CLIENT_VOICE;
svc_idx = 5;
svc_id = APR_SVC_CVP;
} else {
client_id = APR_CLIENT_AUDIO;
svc_idx = 7;
svc_id = APR_SVC_ADSP_CVP;
}
} else if (!strncmp(svc_name, "SRD", 3)) {
client_id = APR_CLIENT_VOICE;
svc_idx = 6;
svc_id = APR_SVC_SRD;
} else if (!strncmp(svc_name, "USM", 3)) {
client_id = APR_CLIENT_AUDIO;
svc_idx = 8;
svc_id = APR_SVC_USM;
} else {
pr_err("APR: Wrong svc name\n");
goto done;
}
pr_debug("svc name = %s c_id = %d dest_id = %d\n",
svc_name, client_id, dest_id);
mutex_lock(&q6.lock);
if (q6.state == APR_Q6_NOIMG) {
q6.pil = pil_get("q6");
if (IS_ERR(q6.pil)) {
q6.pil = pil_get("adsp");
if (IS_ERR(q6.pil)) {
rc = PTR_ERR(q6.pil);
pr_err("APR: Unable to load q6 image, error:%d\n",
rc);
mutex_unlock(&q6.lock);
return svc;
}
}
q6.state = APR_Q6_LOADED;
}
mutex_unlock(&q6.lock);
mutex_lock(&client[dest_id][client_id].m_lock);
if (!client[dest_id][client_id].handle) {
client[dest_id][client_id].handle = apr_tal_open(client_id,
dest_id, APR_DL_SMD, apr_cb_func, NULL);
if (!client[dest_id][client_id].handle) {
svc = NULL;
pr_err("APR: Unable to open handle\n");
mutex_unlock(&client[dest_id][client_id].m_lock);
goto done;
}
}
mutex_unlock(&client[dest_id][client_id].m_lock);
svc = &client[dest_id][client_id].svc[svc_idx];
mutex_lock(&svc->m_lock);
client[dest_id][client_id].id = client_id;
if (svc->need_reset) {
mutex_unlock(&svc->m_lock);
pr_err("APR: Service needs reset\n");
goto done;
}
svc->priv = priv;
svc->id = svc_id;
svc->dest_id = dest_id;
svc->client_id = client_id;
if (src_port != 0xFFFFFFFF) {
temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF);
pr_debug("port = %d t_port = %d\n", src_port, temp_port);
if (temp_port >= APR_MAX_PORTS || temp_port < 0) {
pr_err("APR: temp_port out of bounds\n");
mutex_unlock(&svc->m_lock);
return NULL;
}
if (!svc->port_cnt && !svc->svc_cnt)
client[dest_id][client_id].svc_cnt++;
svc->port_cnt++;
svc->port_fn[temp_port] = svc_fn;
svc->port_priv[temp_port] = priv;
} else {
if (!svc->fn) {
if (!svc->port_cnt && !svc->svc_cnt)
client[dest_id][client_id].svc_cnt++;
svc->fn = svc_fn;
if (svc->port_cnt)
svc->svc_cnt++;
for (i = 0; i < size; i++) {
if (!strncmp(svc_name, tbl[i].name, strlen(tbl[i].name))) {
*client_id = tbl[i].client_id;
*svc_idx = tbl[i].idx;
*svc_id = tbl[i].id;
break;
}
}
mutex_unlock(&svc->m_lock);
done:
return svc;
pr_debug("%s: svc_name = %s c_id = %d dest_id = %d\n",
__func__, svc_name, *client_id, dest_id);
if (i == size) {
pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name);
ret = -EINVAL;
}
return ret;
}
static void apr_reset_deregister(struct work_struct *work)
@ -489,7 +517,7 @@ int apr_deregister(void *handle)
svc->need_reset = 0x0;
}
if (client[dest_id][client_id].handle &&
!client[dest_id][client_id].svc_cnt) {
!client[dest_id][client_id].svc_cnt) {
apr_tal_close(client[dest_id][client_id].handle);
client[dest_id][client_id].handle = NULL;
}
@ -524,14 +552,7 @@ void apr_reset(void *handle)
queue_work(apr_reset_workqueue, &apr_reset_worker->work);
}
void change_q6_state(int state)
{
mutex_lock(&q6.lock);
q6.state = state;
mutex_unlock(&q6.lock);
}
int adsp_state(int state)
static int adsp_state(int state)
{
pr_info("dsp state = %d\n", state);
return 0;
@ -590,12 +611,12 @@ void dispatch_event(unsigned long code, unsigned short proc)
}
static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
void *_cmd)
void *_cmd)
{
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
pr_debug("M-Notify: Shutdown started\n");
atomic_set(&modem_state, 0);
apr_set_modem_state(APR_SUBSYS_DOWN);
dispatch_event(code, APR_DEST_MODEM);
break;
case SUBSYS_AFTER_SHUTDOWN:
@ -605,10 +626,9 @@ static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
pr_debug("M-notify: Bootup started\n");
break;
case SUBSYS_AFTER_POWERUP:
if (atomic_read(&modem_state) == 0) {
atomic_set(&modem_state, 1);
if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
APR_SUBSYS_DOWN)
wake_up(&modem_wait);
}
pr_debug("M-Notify: Bootup Completed\n");
break;
default:
@ -623,12 +643,12 @@ static struct notifier_block mnb = {
};
static int lpass_notifier_cb(struct notifier_block *this, unsigned long code,
void *_cmd)
void *_cmd)
{
switch (code) {
case SUBSYS_BEFORE_SHUTDOWN:
pr_debug("L-Notify: Shutdown started\n");
atomic_set(&dsp_state, 0);
apr_set_q6_state(APR_SUBSYS_DOWN);
dispatch_event(code, APR_DEST_QDSP6);
break;
case SUBSYS_AFTER_SHUTDOWN:
@ -638,10 +658,9 @@ static int lpass_notifier_cb(struct notifier_block *this, unsigned long code,
pr_debug("L-notify: Bootup started\n");
break;
case SUBSYS_AFTER_POWERUP:
if (atomic_read(&dsp_state) == 0) {
atomic_set(&dsp_state, 1);
if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
APR_SUBSYS_DOWN)
wake_up(&dsp_wait);
}
pr_debug("L-Notify: Bootup Completed\n");
break;
default:
@ -670,8 +689,7 @@ static int __init apr_init(void)
}
mutex_init(&q6.lock);
dsp_debug_register(adsp_state);
apr_reset_workqueue =
create_singlethread_workqueue("apr_driver");
apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
if (!apr_reset_workqueue)
return -ENOMEM;
return 0;
@ -683,10 +701,9 @@ static int __init apr_late_init(void)
int ret = 0;
init_waitqueue_head(&dsp_wait);
init_waitqueue_head(&modem_wait);
atomic_set(&dsp_state, 1);
atomic_set(&modem_state, 1);
subsys_notif_register_notifier("modem", &mnb);
subsys_notif_register_notifier("lpass", &lnb);
apr_set_subsys_state();
return ret;
}
late_initcall(apr_late_init);

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2012, The Linux Foundation. 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/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/err.h>
#include <mach/qdsp6v2/apr.h>
#include <mach/qdsp6v2/apr_tal.h>
#include <mach/qdsp6v2/dsp_debug.h>
#include <mach/peripheral-loader.h>
struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
uint32_t src_port, void *priv)
{
struct apr_client *client;
int client_id = 0;
int svc_idx = 0;
int svc_id = 0;
int dest_id = 0;
int temp_port = 0;
struct apr_svc *svc = NULL;
int rc = 0;
if (!dest || !svc_name || !svc_fn)
return NULL;
if (!strncmp(dest, "ADSP", 4))
dest_id = APR_DEST_QDSP6;
else if (!strncmp(dest, "MODEM", 5)) {
dest_id = APR_DEST_MODEM;
} else {
pr_err("APR: wrong destination\n");
goto done;
}
if (dest_id == APR_DEST_QDSP6 &&
apr_get_q6_state() == APR_SUBSYS_DOWN) {
pr_info("%s: Wait for Lpass to bootup\n", __func__);
rc = apr_wait_for_device_up(dest_id);
if (rc == 0) {
pr_err("%s: DSP is not Up\n", __func__);
return NULL;
}
pr_info("%s: Lpass Up\n", __func__);
} else if (dest_id == APR_DEST_MODEM &&
(apr_get_modem_state() == APR_SUBSYS_DOWN)) {
pr_info("%s: Wait for modem to bootup\n", __func__);
rc = apr_wait_for_device_up(dest_id);
if (rc == 0) {
pr_err("%s: Modem is not Up\n", __func__);
return NULL;
}
pr_info("%s: modem Up\n", __func__);
}
if (apr_get_svc(svc_name, dest_id, &client_id, &svc_idx, &svc_id)) {
pr_err("%s: apr_get_svc failed\n", __func__);
goto done;
}
/* APRv1 loads ADSP image automatically */
apr_load_adsp_image();
client = apr_get_client(dest_id, client_id);
mutex_lock(&client->m_lock);
if (!client->handle) {
client->handle = apr_tal_open(client_id, dest_id, APR_DL_SMD,
apr_cb_func, NULL);
if (!client->handle) {
svc = NULL;
pr_err("APR: Unable to open handle\n");
mutex_unlock(&client->m_lock);
goto done;
}
}
mutex_unlock(&client->m_lock);
svc = &client->svc[svc_idx];
mutex_lock(&svc->m_lock);
client->id = client_id;
if (svc->need_reset) {
mutex_unlock(&svc->m_lock);
pr_err("APR: Service needs reset\n");
goto done;
}
svc->priv = priv;
svc->id = svc_id;
svc->dest_id = dest_id;
svc->client_id = client_id;
if (src_port != 0xFFFFFFFF) {
temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF);
pr_debug("port = %d t_port = %d\n", src_port, temp_port);
if (temp_port >= APR_MAX_PORTS || temp_port < 0) {
pr_err("APR: temp_port out of bounds\n");
mutex_unlock(&svc->m_lock);
return NULL;
}
if (!svc->port_cnt && !svc->svc_cnt)
client->svc_cnt++;
svc->port_cnt++;
svc->port_fn[temp_port] = svc_fn;
svc->port_priv[temp_port] = priv;
} else {
if (!svc->fn) {
if (!svc->port_cnt && !svc->svc_cnt)
client->svc_cnt++;
svc->fn = svc_fn;
if (svc->port_cnt)
svc->svc_cnt++;
}
}
mutex_unlock(&svc->m_lock);
done:
return svc;
}
void apr_set_subsys_state(void)
{
apr_set_q6_state(APR_SUBSYS_UP);
apr_set_modem_state(APR_SUBSYS_UP);
}

View File

@ -0,0 +1,127 @@
/*
* Copyright (c) 2012, The Linux Foundation. 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/types.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <mach/qdsp6v2/apr.h>
#include <mach/qdsp6v2/apr_tal.h>
#include <mach/qdsp6v2/dsp_debug.h>
struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
uint32_t src_port, void *priv)
{
struct apr_client *client;
int client_id = 0;
int svc_idx = 0;
int svc_id = 0;
int dest_id = 0;
int temp_port = 0;
struct apr_svc *svc = NULL;
int rc = 0;
if (!dest || !svc_name || !svc_fn)
return NULL;
if (!strncmp(dest, "ADSP", 4))
dest_id = APR_DEST_QDSP6;
else if (!strncmp(dest, "MODEM", 5)) {
dest_id = APR_DEST_MODEM;
} else {
pr_err("APR: wrong destination\n");
goto done;
}
if ((dest_id == APR_DEST_QDSP6)) {
if (apr_get_q6_state() != APR_SUBSYS_LOADED) {
pr_err("%s: adsp not up\n", __func__);
return NULL;
}
pr_info("%s: Lpass Up\n", __func__);
} else if ((dest_id == APR_DEST_MODEM) &&
(apr_get_modem_state() == APR_SUBSYS_DOWN)) {
pr_info("%s: Wait for modem to bootup\n", __func__);
rc = apr_wait_for_device_up(dest_id);
if (rc == 0) {
pr_err("%s: Modem is not Up\n", __func__);
return NULL;
}
pr_info("%s: modem Up\n", __func__);
}
if (apr_get_svc(svc_name, dest_id, &client_id, &svc_idx, &svc_id)) {
pr_err("%s: apr_get_svc failed\n", __func__);
goto done;
}
/* APRv2 doen't load ADSP image automatically */
client = apr_get_client(dest_id, client_id);
mutex_lock(&client->m_lock);
if (!client->handle) {
client->handle = apr_tal_open(client_id, dest_id, APR_DL_SMD,
apr_cb_func, NULL);
if (!client->handle) {
svc = NULL;
pr_err("APR: Unable to open handle\n");
mutex_unlock(&client->m_lock);
goto done;
}
}
mutex_unlock(&client->m_lock);
svc = &client->svc[svc_idx];
mutex_lock(&svc->m_lock);
client->id = client_id;
if (svc->need_reset) {
mutex_unlock(&svc->m_lock);
pr_err("APR: Service needs reset\n");
goto done;
}
svc->priv = priv;
svc->id = svc_id;
svc->dest_id = dest_id;
svc->client_id = client_id;
if (src_port != 0xFFFFFFFF) {
temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF);
pr_debug("port = %d t_port = %d\n", src_port, temp_port);
if (temp_port >= APR_MAX_PORTS || temp_port < 0) {
pr_err("APR: temp_port out of bounds\n");
mutex_unlock(&svc->m_lock);
return NULL;
}
if (!svc->port_cnt && !svc->svc_cnt)
client->svc_cnt++;
svc->port_cnt++;
svc->port_fn[temp_port] = svc_fn;
svc->port_priv[temp_port] = priv;
} else {
if (!svc->fn) {
if (!svc->port_cnt && !svc->svc_cnt)
client->svc_cnt++;
svc->fn = svc_fn;
if (svc->port_cnt)
svc->svc_cnt++;
}
}
mutex_unlock(&svc->m_lock);
done:
return svc;
}
void apr_set_subsys_state(void)
{
apr_set_q6_state(APR_SUBSYS_DOWN);
apr_set_modem_state(APR_SUBSYS_UP);
}

View File

@ -337,7 +337,7 @@ static ssize_t apr_debug_write(struct file *file, const char __user *buf,
if (apr_handle_q)
apr_deregister(apr_handle_q);
} else if (!strncmp(l_buf + 20, "loaded", 64)) {
change_q6_state(APR_Q6_LOADED);
apr_set_q6_state(APR_SUBSYS_LOADED);
} else if (!strncmp(l_buf + 20, "boom", 64)) {
q6audio_dsp_not_responding();
} else if (!strncmp(l_buf + 20, "dsp_ver", 64)) {