Files
kernel_xiaomi_sm8250/techpack/audio/dsp/audio_notifier.c
Michael Bestas 5850f8798e Merge tag 'LA.UM.9.15.r1-07300-KAMORTA.QSSI13.0' of https://git.codelinaro.org/clo/la/platform/vendor/opensource/audio-kernel into android13-4.19-kona
"LA.UM.9.15.r1-07300-KAMORTA.QSSI13.0"

* tag 'LA.UM.9.15.r1-07300-KAMORTA.QSSI13.0' of https://git.codelinaro.org/clo/la/platform/vendor/opensource/audio-kernel:
  ASoC: msm-pcm-q6-v2: Add dsp buf check
  ASoC: rouleur-mbhc: Enhance impedance calculation logic
  ASoC: rouleur-mbhc: Fix pop noise heard when headset insertion
  asoc: wsa881x: Fix to set the correct volume level
  soc: swr-mstr-ctrl: remove FIFO_Flush for overflow
  asoc: Add routing control for qrb5165 BT HFP usecase
  soc: swr-mstr: Store and compare dev_addr along with dev_num
  Asoc: fix integer overflow for long duration offload playback
  asoc: Correct the range of dev_token for usb headset.
  asoc: pcm-noirq: set substream state to disconnect when ssr happens
  soc: pinctrl-lpi-legacy : Add nullptr check
  asoc: msm-pcm-routing: Assign default topology when app_type is zero
  asoc: notify dp driver after disp codec probe
  asoc: codecs: fix race condition of core vote and reg access
  ASoC: wcd937x: Add check for ULP irrespective of the Class
  asoc: codecs: Add nullptr check
  ASoC: wcd937x: Update QCRG sequence for wcd937x
  asoc: changes to fix KW errors.
  asoc: changes to fix KW errors.
  ASoC: pcm-routing: Add WSA VI sense macro to support capture
  ASoC: wcd937x: Update EAR PA CnP FSM sequence
  soc: swr-mstr: update component and interrupt enable sequence
  ASoC: wcd937x: Update EAR PA CnP FSM sequence
  soc: add check condition before enabling irq
  ASoC: wcd937x: Add change for LDOL VOUT accuracy calibration
  ASoC: dsp: Synchronise adm commands
  ASoC: wcd937x: Add change to micbias reg for new tanggu fab id
  ASoC: wcd937x: Update register value for new fab id for tanggu
  dsp:q6afe: Resolve crash in q6afe_get_avcs_modules
  ASoC: wcd937x: Change resistor divider output for hph if fab id is enabled
  dsp: adm: add to check output/input channel value
  asoc: Parse wcd-datalane-mismatch property to update rx frame config for khaje
  asoc: wcd-cpe: Set the dma_mask for cpe_device
  asoc: Parse wcd-datalane-mismatch property to update rx frame config for khaje
  ASoC: msm8952: Defer until pcm voice probe done
  q6afe: Fix out of bound access of clkinfo_per_port
  asoc: Register CDC_DMA interface only if enabled
  config: sdm450: Disable CDC_DMA interfaces
  asoc: codecs: add null check before access
  asoc: fix KW issue of using uninitialized variable
  audio-kernel: enable wsa883x compilation on qrb5165
  ASoC: Enable wsa881x-temp-sensor for bengal
  ASoC: msm8952: avoid static route between cpu and codec dai
  asoc: codecs: Fix LPASS register access during bootup
  ASoC: wcd: Add check to stop invalid memory access
  afe: release codec port when afe starts failed
  ASoC: Changes to enable codec clk for wcd9335
  Asoc: check for invalid voice session id
  SoC: codecs: Fix slim_tx port configuration for wcd9335
  ASoC: Get the component handle for cpe and tasha
  ASoC: wcd937x: Change vbg fine voltage to 0.5V for wcd937x second source
  audio-kernel: enable wsa883x compilation on sxr2130
  dsp: fix out of bound access for cal_data size
  dsp: update size check for set/get VI param function
  config: sdm450: Disable TDM and AUXPCM interfaces
  asoc: msm_sdw: Revert adsp ready check for read/write
  ASoC: SDM660: Avoid static route between cpu and codec dai
  asoc: bolero: Update PCM_RATE based delay for amic
  ASoC: SDM660: Avoid static route between cpu and codec dai
  ASoC: Fix for SSR issue on msm8953
  ASoC: error check for backend index and MCLK src
  Asoc: sdm450: Add capture support for Multimedia10 FE
  asoc: add proxy ports for call screening in machine driver
  asoc: msm: Fix compilation error when enable TDM and disable MI2S
  Makefile: Correct TARGET_SUPPORT entry for qrb5165
  dsp: update size check for get VI param function
  asoc: codecs: add child devices after completing initialization
  asoc: bolero: Add core_vote before gfmux access
  dsp: Disable q6_pdr build for msm8937 and msm8953
  asoc: Fixed wsa881x codec machine driver callback
  asoc: codec: enable the cpe on tasha
  Use proper TARGET_SUPPORT entry for sxr2130
  asoc: wsa881x: Fix to set the correct volume level
  ASoC: ep92: Add external mclk support
  ASoC: msm: Add WCD interrupt config for SDA660
  dsp: Unload the BT modules when APR handle is NULL
  asoc: Update copy_to_user to requested buffer size
  ASoC: Audio bringup changes for msm8937_32go.
  asoc: Add check to handle negative value passed for num_app_cfg_type
  ASoC: QCS405: Additional TDM samplerates and channels
  asoc: qcs405: Add support for TDM interface
  asoc: Add support for QUAT and SEN TDM interface
  asoc: qcs405: Update clk src string from dts
  dsp: Add support to update and store clk src values
  dsp: afe topology deregister
  ASoC: qcs405: Add external mclk support
  ASoC: Add external mclk mixer support
  dsp: Fix improper mutex unlock in afe close
  ASoC: wsa883x: Handle PA_ERR interrupt on WSA speaker
  ASoC: Add support to send voice UI port id to afe
  Revert "dsp: avtimer: Add adsp ready check before accessing avtimer registers"
  asoc: wsa881x: Fix crash while capturing swr_slv codec dump
  dsp: add change to enable preemption at cal_utils_dealloc_cal
  asoc: msm_sdw: Handle locking for mclk enable flag
  asoc: codecs: sdm660_cdc: Fix LPASS register access during PDR
  ASoC: lagoon: add support for CPS speaker protection
  dsp: add change to enable preemption at cal_utils_dealloc_cal
  ASoC: tx-macro: Allow regcache sync during clock enablement
  ASoC: wsa883x: add function to get swr device number
  dsp: add change to handle use-after-free in cal_utils_is_cal_stale
  soc: swr-mstr: Check if fifo available before bulk write
  asoc: bolero: control wakeup of swr_tx during clock setup
  soc: swr-mstr: Resolve swr overflow, underflow errors for wsa
  soc: swr-mstr: Update soundwire state to SSR during UP
  dsp: add change to fix use-after-free issue
  asoc: msm-pcm-q6-v2: Update memset for period size
  asoc: va-macro: Allow swr switch at powerup/shutdown
  ASoC: SDM660: Avoid static route between cpu and codec dai
  soc: Synchronize RESET event and lpi ops in legacy driver
  ASoC: codec: Enable wcd-spi driver compilation
  asoc: wcd-spi: Set the dma_mask for spi_device
  ASoC: audio-ext-clk: Add pmi clk support for tasha
  asoc: add null check for pcm pointer of snd_pcm_volume
  dsp: add afe function to send cps configuration
  asoc: Add 32bit support in meta i2s
  asoc: va-macro: Update widget order for VA_MCLK and VA_SWR_PWR
  dsp: Add param header size based on the instance id support
  dsp: q6core: Check q6core avs state to be modules ready
  ASoC: dsp: Synchronise adm and rtac commands
  asoc: codecs: Add support to dump rouleur swr slave regs
  asoc: sdm660: Correct the dev_id for hdmi backend.
  Revert "ASoC: codecs: handle device disconnect during SSR/PDR"
  ASoC: wsa883x: Update register sequence to reduce CnP
  asoc: sdm660: add proxy ports for call screening in machine driver
  soc: swr-mstr: Fix headset detect issue due to master.num_dev not updated
  soc: swr-mstr: Fix random headset detection issue on scuba
  dsp: Feedback path cfg to support 3rd party spkr protection algorithms.
  ASOC: SPV4 set correct number of speakers.
  ASoC: wsa883x: enable vbat adc filter
  config: kona: Disable MI2S, TDM and AUXPCM interfaces
  dsp: fix compilation issue in q6lsm_callback function
  asoc: Register widgets and intercons only when supported
  soc: swr-mstr: Fix random headset detection issue on scuba
  ASoC: codecs: handle device disconnect during SSR
  asoc: dsp: Fix possible invalid memory access due to race condition
  ASoC: rouleur-mbhc: Fix impedance detection issue on rouleur
  dsp: Add support for GET_PARAMS

 Conflicts:
	techpack/audio/asoc/kona.c
	techpack/audio/asoc/msm-pcm-routing-v2.c
	techpack/audio/dsp/q6core.c
	techpack/audio/dsp/q6lsm.c
	techpack/audio/include/dsp/audio_cal_utils.h
	techpack/audio/soc/swr-mstr-ctrl.c
	techpack/audio/soc/swr-mstr-ctrl.h

Change-Id: Id77d1c17f36618427f929d7830200733a0e8b460
2023-04-01 01:07:23 +03:00

649 lines
16 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2017, 2021, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/service-notifier.h>
#include <dsp/audio_notifier.h>
#include "audio_ssr.h"
#include "audio_pdr.h"
/* Audio states internal to notifier. Client */
/* used states defined in audio_notifier.h */
/* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */
#define NO_SERVICE -2
#define UNINIT_SERVICE -1
/*
* Used for each client registered with audio notifier
*/
struct client_data {
struct list_head list;
/* Notifier block given by client */
struct notifier_block *nb;
char client_name[20];
int service;
int domain;
};
/*
* Used for each service and domain combination
* Tracks information specific to the underlying
* service.
*/
struct service_info {
const char name[20];
int domain_id;
int state;
void *handle;
/* Notifier block registered to service */
struct notifier_block *nb;
/* Used to determine when to register and deregister service */
int num_of_clients;
/* List of all clients registered to the service and domain */
struct srcu_notifier_head client_nb_list;
};
static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
unsigned long opcode, void *data);
static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
unsigned long opcode, void *data);
static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
unsigned long opcode, void *data);
static struct notifier_block notifier_ssr_adsp_nb = {
.notifier_call = audio_notifer_ssr_adsp_cb,
.priority = 0,
};
static struct notifier_block notifier_ssr_modem_nb = {
.notifier_call = audio_notifer_ssr_modem_cb,
.priority = 0,
};
static struct notifier_block notifier_pdr_adsp_nb = {
.notifier_call = audio_notifer_pdr_adsp_cb,
.priority = 0,
};
static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES]
[AUDIO_NOTIFIER_MAX_DOMAINS] = {
{{
.name = "SSR_ADSP",
.domain_id = AUDIO_SSR_DOMAIN_ADSP,
.state = AUDIO_NOTIFIER_SERVICE_DOWN,
.nb = &notifier_ssr_adsp_nb
},
{
.name = "SSR_MODEM",
.domain_id = AUDIO_SSR_DOMAIN_MODEM,
.state = AUDIO_NOTIFIER_SERVICE_DOWN,
.nb = &notifier_ssr_modem_nb
} },
{{
.name = "PDR_ADSP",
.domain_id = AUDIO_PDR_DOMAIN_ADSP,
.state = UNINIT_SERVICE,
.nb = &notifier_pdr_adsp_nb
},
{ /* PDR MODEM service not enabled */
.name = "INVALID",
.state = NO_SERVICE,
.nb = NULL
} }
};
/* Master list of all audio notifier clients */
struct list_head client_list;
struct mutex notifier_mutex;
static int audio_notifer_get_default_service(int domain)
{
int service = NO_SERVICE;
/* initial service to connect per domain */
switch (domain) {
case AUDIO_NOTIFIER_ADSP_DOMAIN:
service = AUDIO_NOTIFIER_PDR_SERVICE;
break;
case AUDIO_NOTIFIER_MODEM_DOMAIN:
service = AUDIO_NOTIFIER_SSR_SERVICE;
break;
}
return service;
}
static void audio_notifer_disable_service(int service)
{
int i;
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
service_data[service][i].state = NO_SERVICE;
}
static bool audio_notifer_is_service_enabled(int service)
{
int i;
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
if (service_data[service][i].state != NO_SERVICE)
return true;
return false;
}
static void audio_notifer_init_service(int service)
{
int i;
for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) {
if (service_data[service][i].state == UNINIT_SERVICE)
service_data[service][i].state =
AUDIO_NOTIFIER_SERVICE_DOWN;
}
}
static int audio_notifer_reg_service(int service, int domain)
{
void *handle;
int ret = 0;
int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
switch (service) {
case AUDIO_NOTIFIER_SSR_SERVICE:
handle = audio_ssr_register(
service_data[service][domain].domain_id,
service_data[service][domain].nb);
break;
case AUDIO_NOTIFIER_PDR_SERVICE:
handle = audio_pdr_service_register(
service_data[service][domain].domain_id,
service_data[service][domain].nb, &curr_state);
if (curr_state == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
curr_state = AUDIO_NOTIFIER_SERVICE_UP;
else
curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
break;
default:
pr_err("%s: Invalid service %d\n",
__func__, service);
ret = -EINVAL;
goto done;
}
if (IS_ERR_OR_NULL(handle)) {
pr_err("%s: handle is incorrect for service %s\n",
__func__, service_data[service][domain].name);
ret = -EINVAL;
goto done;
}
service_data[service][domain].state = curr_state;
service_data[service][domain].handle = handle;
pr_info("%s: service %s is in use\n",
__func__, service_data[service][domain].name);
pr_debug("%s: service %s has current state %d, handle 0x%pK\n",
__func__, service_data[service][domain].name,
service_data[service][domain].state,
service_data[service][domain].handle);
done:
return ret;
}
static int audio_notifer_dereg_service(int service, int domain)
{
int ret;
switch (service) {
case AUDIO_NOTIFIER_SSR_SERVICE:
ret = audio_ssr_deregister(
service_data[service][domain].handle,
service_data[service][domain].nb);
break;
case AUDIO_NOTIFIER_PDR_SERVICE:
ret = audio_pdr_service_deregister(
service_data[service][domain].handle,
service_data[service][domain].nb);
break;
default:
pr_err("%s: Invalid service %d\n",
__func__, service);
ret = -EINVAL;
goto done;
}
if (ret < 0) {
pr_err("%s: deregister failed for service %s, ret %d\n",
__func__, service_data[service][domain].name, ret);
goto done;
}
pr_debug("%s: service %s with handle 0x%pK deregistered\n",
__func__, service_data[service][domain].name,
service_data[service][domain].handle);
service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN;
service_data[service][domain].handle = NULL;
done:
return ret;
}
static int audio_notifer_reg_client_service(struct client_data *client_data,
int service)
{
int ret = 0;
int domain = client_data->domain;
struct audio_notifier_cb_data data;
switch (service) {
case AUDIO_NOTIFIER_SSR_SERVICE:
case AUDIO_NOTIFIER_PDR_SERVICE:
if (service_data[service][domain].num_of_clients == 0)
ret = audio_notifer_reg_service(service, domain);
break;
default:
pr_err("%s: Invalid service for client %s, service %d, domain %d\n",
__func__, client_data->client_name, service, domain);
ret = -EINVAL;
goto done;
}
if (ret < 0) {
pr_err("%s: service registration failed on service %s for client %s\n",
__func__, service_data[service][domain].name,
client_data->client_name);
goto done;
}
client_data->service = service;
srcu_notifier_chain_register(
&service_data[service][domain].client_nb_list,
client_data->nb);
service_data[service][domain].num_of_clients++;
pr_debug("%s: registered client %s on service %s, current state 0x%x\n",
__func__, client_data->client_name,
service_data[service][domain].name,
service_data[service][domain].state);
/*
* PDR registration returns current state
* Force callback of client with current state for PDR
*/
if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) {
data.service = service;
data.domain = domain;
(void)client_data->nb->notifier_call(client_data->nb,
service_data[service][domain].state, &data);
}
done:
return ret;
}
static int audio_notifer_reg_client(struct client_data *client_data)
{
int ret = 0;
int service;
int domain = client_data->domain;
service = audio_notifer_get_default_service(domain);
if (service < 0) {
pr_err("%s: service %d is incorrect\n", __func__, service);
ret = -EINVAL;
goto done;
}
/* Search through services to find a valid one to register client on. */
for (; service >= 0; service--) {
/* If a service is not initialized, wait for it to come up. */
if (service_data[service][domain].state == UNINIT_SERVICE)
goto done;
/* Skip unsupported service and domain combinations. */
if (service_data[service][domain].state < 0)
continue;
/* Only register clients who have not acquired a service. */
if (client_data->service != NO_SERVICE)
continue;
/*
* Only register clients, who have not acquired a service, on
* the best available service for their domain. Uninitialized
* services will try to register all of their clients after
* they initialize correctly or will disable their service and
* register clients on the next best avaialable service.
*/
pr_debug("%s: register client %s on service %s",
__func__, client_data->client_name,
service_data[service][domain].name);
ret = audio_notifer_reg_client_service(client_data, service);
if (ret < 0)
pr_err("%s: client %s failed to register on service %s",
__func__, client_data->client_name,
service_data[service][domain].name);
}
done:
return ret;
}
static int audio_notifer_dereg_client(struct client_data *client_data)
{
int ret = 0;
int service = client_data->service;
int domain = client_data->domain;
switch (client_data->service) {
case AUDIO_NOTIFIER_SSR_SERVICE:
case AUDIO_NOTIFIER_PDR_SERVICE:
if (service_data[service][domain].num_of_clients == 1)
ret = audio_notifer_dereg_service(service, domain);
break;
case NO_SERVICE:
goto done;
default:
pr_err("%s: Invalid service for client %s, service %d\n",
__func__, client_data->client_name,
client_data->service);
ret = -EINVAL;
goto done;
}
if (ret < 0) {
pr_err("%s: deregister failed for client %s on service %s, ret %d\n",
__func__, client_data->client_name,
service_data[service][domain].name, ret);
goto done;
}
ret = srcu_notifier_chain_unregister(&service_data[service][domain].
client_nb_list, client_data->nb);
if (ret < 0) {
pr_err("%s: srcu_notifier_chain_unregister failed, ret %d\n",
__func__, ret);
goto done;
}
pr_debug("%s: deregistered client %s on service %s\n",
__func__, client_data->client_name,
service_data[service][domain].name);
client_data->service = NO_SERVICE;
if (service_data[service][domain].num_of_clients > 0)
service_data[service][domain].num_of_clients--;
done:
return ret;
}
static void audio_notifer_reg_all_clients(void)
{
struct list_head *ptr, *next;
struct client_data *client_data;
int ret;
list_for_each_safe(ptr, next, &client_list) {
client_data = list_entry(ptr, struct client_data, list);
ret = audio_notifer_reg_client(client_data);
if (ret < 0)
pr_err("%s: audio_notifer_reg_client failed for client %s, ret %d\n",
__func__, client_data->client_name,
ret);
}
}
static int audio_notifer_pdr_callback(struct notifier_block *this,
unsigned long opcode, void *data)
{
pr_debug("%s: Audio PDR framework state 0x%lx\n",
__func__, opcode);
mutex_lock(&notifier_mutex);
if (opcode == AUDIO_PDR_FRAMEWORK_DOWN)
audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
else
audio_notifer_init_service(AUDIO_NOTIFIER_PDR_SERVICE);
audio_notifer_reg_all_clients();
mutex_unlock(&notifier_mutex);
return 0;
}
static struct notifier_block pdr_nb = {
.notifier_call = audio_notifer_pdr_callback,
.priority = 0,
};
static int audio_notifer_convert_opcode(unsigned long opcode,
unsigned long *notifier_opcode)
{
int ret = 0;
switch (opcode) {
case SUBSYS_BEFORE_SHUTDOWN:
case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
*notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN;
break;
case SUBSYS_AFTER_POWERUP:
case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
*notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP;
break;
default:
pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode);
ret = -EINVAL;
}
return ret;
}
static int audio_notifer_service_cb(unsigned long opcode,
int service, int domain)
{
int ret = 0;
unsigned long notifier_opcode;
struct audio_notifier_cb_data data;
if (audio_notifer_convert_opcode(opcode, &notifier_opcode) < 0)
goto done;
data.service = service;
data.domain = domain;
pr_debug("%s: service %s, opcode 0x%lx\n",
__func__, service_data[service][domain].name, notifier_opcode);
mutex_lock(&notifier_mutex);
service_data[service][domain].state = notifier_opcode;
ret = srcu_notifier_call_chain(&service_data[service][domain].
client_nb_list, notifier_opcode, &data);
if (ret < 0)
pr_err("%s: srcu_notifier_call_chain returned %d, service %s, opcode 0x%lx\n",
__func__, ret, service_data[service][domain].name,
notifier_opcode);
mutex_unlock(&notifier_mutex);
done:
return NOTIFY_OK;
}
static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
return audio_notifer_service_cb(opcode,
AUDIO_NOTIFIER_PDR_SERVICE,
AUDIO_NOTIFIER_ADSP_DOMAIN);
}
static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
return audio_notifer_service_cb(opcode,
AUDIO_NOTIFIER_SSR_SERVICE,
AUDIO_NOTIFIER_ADSP_DOMAIN);
}
static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
unsigned long opcode, void *data)
{
return audio_notifer_service_cb(opcode,
AUDIO_NOTIFIER_SSR_SERVICE,
AUDIO_NOTIFIER_MODEM_DOMAIN);
}
int audio_notifier_deregister(char *client_name)
{
int ret = 0;
int ret2;
struct list_head *ptr, *next;
struct client_data *client_data = NULL;
if (client_name == NULL) {
pr_err("%s: client_name is NULL\n", __func__);
ret = -EINVAL;
goto done;
}
mutex_lock(&notifier_mutex);
list_for_each_safe(ptr, next, &client_list) {
client_data = list_entry(ptr, struct client_data, list);
if (!strcmp(client_name, client_data->client_name)) {
ret2 = audio_notifer_dereg_client(client_data);
if (ret2 < 0) {
pr_err("%s: audio_notifer_dereg_client failed, ret %d\n, service %d, domain %d",
__func__, ret2, client_data->service,
client_data->domain);
ret = ret2;
continue;
}
list_del(&client_data->list);
kfree(client_data);
}
}
mutex_unlock(&notifier_mutex);
done:
return ret;
}
EXPORT_SYMBOL(audio_notifier_deregister);
int audio_notifier_register(char *client_name, int domain,
struct notifier_block *nb)
{
int ret;
struct client_data *client_data;
if (client_name == NULL) {
pr_err("%s: client_name is NULL\n", __func__);
ret = -EINVAL;
goto done;
} else if (nb == NULL) {
pr_err("%s: Notifier block is NULL\n", __func__);
ret = -EINVAL;
goto done;
}
client_data = kmalloc(sizeof(*client_data), GFP_KERNEL);
if (client_data == NULL) {
ret = -ENOMEM;
goto done;
}
INIT_LIST_HEAD(&client_data->list);
client_data->nb = nb;
strlcpy(client_data->client_name, client_name,
sizeof(client_data->client_name));
client_data->service = NO_SERVICE;
client_data->domain = domain;
mutex_lock(&notifier_mutex);
ret = audio_notifer_reg_client(client_data);
if (ret < 0) {
mutex_unlock(&notifier_mutex);
pr_err("%s: audio_notifer_reg_client for client %s failed ret = %d\n",
__func__, client_data->client_name,
ret);
kfree(client_data);
goto done;
}
list_add_tail(&client_data->list, &client_list);
mutex_unlock(&notifier_mutex);
done:
return ret;
}
EXPORT_SYMBOL(audio_notifier_register);
static int __init audio_notifier_subsys_init(void)
{
int i, j;
mutex_init(&notifier_mutex);
INIT_LIST_HEAD(&client_list);
for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) {
for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) {
if (service_data[i][j].state <= NO_SERVICE)
continue;
srcu_init_notifier_head(
&service_data[i][j].client_nb_list);
}
}
return 0;
}
static int __init audio_notifier_late_init(void)
{
/*
* If pdr registration failed, register clients on next service
* Do in late init to ensure that SSR subsystem is initialized
*/
mutex_lock(&notifier_mutex);
if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE))
audio_notifer_reg_all_clients();
mutex_unlock(&notifier_mutex);
return 0;
}
#ifdef CONFIG_MSM_QDSP6_PDR
static int __init audio_notifier_init(void)
{
int ret;
audio_notifier_subsys_init();
ret = audio_pdr_register(&pdr_nb);
if (ret < 0) {
pr_err("%s: PDR register failed, ret = %d, disable service\n",
__func__, ret);
audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
}
/* Do not return error since PDR enablement is not critical */
audio_notifier_late_init();
return 0;
}
#else
static int __init audio_notifier_init(void)
{
audio_notifier_subsys_init();
audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
audio_notifier_late_init();
return 0;
}
#endif
module_init(audio_notifier_init);
static void __exit audio_notifier_exit(void)
{
audio_pdr_deregister(&pdr_nb);
}
module_exit(audio_notifier_exit);
MODULE_DESCRIPTION("Audio notifier driver");
MODULE_LICENSE("GPL v2");