blob: 75f3bbb5062be5e5704b7b54dd1062eec4e0df24 [file] [log] [blame]
/* Copyright (c) 2016-2017, 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/module.h>
#include <linux/slab.h>
#include <soc/qcom/service-locator.h>
#include <soc/qcom/service-notifier.h>
#include "audio_pdr.h"
static struct pd_qmi_client_data audio_pdr_services[AUDIO_PDR_DOMAIN_MAX] = {
{ /* AUDIO_PDR_DOMAIN_ADSP */
.client_name = "audio_pdr_adsp",
.service_name = "avs/audio"
}
};
struct srcu_notifier_head audio_pdr_cb_list;
static int audio_pdr_locator_callback(struct notifier_block *this,
unsigned long opcode, void *data)
{
unsigned long pdr_state = AUDIO_PDR_FRAMEWORK_DOWN;
if (opcode == LOCATOR_DOWN) {
pr_debug("%s: Service %s is down!", __func__,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
service_name);
goto done;
}
memcpy(&audio_pdr_services, data,
sizeof(audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]));
if (audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].total_domains == 1) {
pr_debug("%s: Service %s, returned total domains %d, ",
__func__,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
total_domains);
pdr_state = AUDIO_PDR_FRAMEWORK_UP;
goto done;
} else
pr_err("%s: Service %s returned invalid total domains %d",
__func__,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
total_domains);
done:
srcu_notifier_call_chain(&audio_pdr_cb_list, pdr_state, NULL);
return NOTIFY_OK;
}
static struct notifier_block audio_pdr_locator_nb = {
.notifier_call = audio_pdr_locator_callback,
.priority = 0,
};
/**
* audio_pdr_register -
* register to PDR framework
*
* @nb: notifier block
*
* Returns 0 on success or error on failure
*/
int audio_pdr_register(struct notifier_block *nb)
{
if (nb == NULL) {
pr_err("%s: Notifier block is NULL\n", __func__);
return -EINVAL;
}
return srcu_notifier_chain_register(&audio_pdr_cb_list, nb);
}
EXPORT_SYMBOL(audio_pdr_register);
/**
* audio_pdr_deregister -
* Deregister from PDR framework
*
* @nb: notifier block
*
* Returns 0 on success or error on failure
*/
int audio_pdr_deregister(struct notifier_block *nb)
{
if (nb == NULL) {
pr_err("%s: Notifier block is NULL\n", __func__);
return -EINVAL;
}
return srcu_notifier_chain_unregister(&audio_pdr_cb_list, nb);
}
EXPORT_SYMBOL(audio_pdr_deregister);
void *audio_pdr_service_register(int domain_id,
struct notifier_block *nb, int *curr_state)
{
void *handle;
if ((domain_id < 0) ||
(domain_id >= AUDIO_PDR_DOMAIN_MAX)) {
pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
return ERR_PTR(-EINVAL);
}
handle = service_notif_register_notifier(
audio_pdr_services[domain_id].domain_list[0].name,
audio_pdr_services[domain_id].domain_list[0].instance_id,
nb, curr_state);
if (IS_ERR_OR_NULL(handle)) {
pr_err("%s: Failed to register for service %s, instance %d\n",
__func__,
audio_pdr_services[domain_id].domain_list[0].name,
audio_pdr_services[domain_id].domain_list[0].
instance_id);
}
return handle;
}
EXPORT_SYMBOL(audio_pdr_service_register);
int audio_pdr_service_deregister(void *service_handle,
struct notifier_block *nb)
{
int ret;
if (service_handle == NULL) {
pr_err("%s: service handle is NULL\n", __func__);
ret = -EINVAL;
goto done;
}
ret = service_notif_unregister_notifier(
service_handle, nb);
if (ret < 0)
pr_err("%s: Failed to deregister service ret %d\n",
__func__, ret);
done:
return ret;
}
EXPORT_SYMBOL(audio_pdr_service_deregister);
static int __init audio_pdr_subsys_init(void)
{
srcu_init_notifier_head(&audio_pdr_cb_list);
return 0;
}
static int __init audio_pdr_late_init(void)
{
int ret;
audio_pdr_subsys_init();
ret = get_service_location(
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].client_name,
audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
&audio_pdr_locator_nb);
if (ret < 0) {
pr_err("%s get_service_location failed ret %d\n",
__func__, ret);
srcu_notifier_call_chain(&audio_pdr_cb_list,
AUDIO_PDR_FRAMEWORK_DOWN, NULL);
}
return ret;
}
module_init(audio_pdr_late_init);
static void __exit audio_pdr_late_exit(void)
{
}
module_exit(audio_pdr_late_exit);
MODULE_DESCRIPTION("PDR framework driver");
MODULE_LICENSE("GPL v2");