1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036 |
- /* Copyright (c) 2012-2013, 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/init.h>
- #include <linux/module.h>
- #include <linux/types.h>
- #include <linux/spinlock.h>
- #include <linux/mutex.h>
- #include <linux/sched.h>
- #include <linux/platform_device.h>
- #include <linux/device.h>
- #include <linux/slab.h>
- #include <linux/io.h>
- #include <linux/of_device.h>
- #include <linux/memory_alloc.h>
- #include <asm/mach-types.h>
- #include <mach/msm_bus.h>
- #include <mach/msm_bus_board.h>
- #include <mach/ocmem.h>
- #include <mach/subsystem_notif.h>
- #include <mach/subsystem_restart.h>
- #include <mach/msm_memtypes.h>
- #include <mach/ramdump.h>
- #include <sound/q6core.h>
- #include "audio_ocmem.h"
- #define AUDIO_OCMEM_BUF_SIZE (512 * SZ_1K)
- /**
- * Exercise OCMEM Dump if audio OCMEM state is
- * one of the following. All other states indicate
- * audio data is not mapped from DDR to OCMEM and
- * therefore no need of dump.
- */
- #define _DO_OCMEM_DUMP_BIT_MASK_\
- ((1 << OCMEM_STATE_MAP_COMPL) |\
- (1 << OCMEM_STATE_MAP_TRANSITION) |\
- (1 << OCMEM_STATE_UNMAP_TRANSITION) |\
- (1 << OCMEM_STATE_SHRINK) |\
- (1 << OCMEM_STATE_GROW))
- /**
- * Wait for OCMEM driver to process and respond for
- * ongoing map/unmap request before calling OCMEM dump.
- */
- #define _WAIT_BFR_DUMP_BIT_MASK_\
- ((1 << OCMEM_STATE_MAP_COMPL) |\
- (1 << OCMEM_STATE_UNMAP_COMPL) |\
- (1 << OCMEM_STATE_MAP_FAIL) |\
- (1 << OCMEM_STATE_UNMAP_FAIL))
- #define _MAP_RESPONSE_BIT_MASK_\
- ((1 << OCMEM_STATE_MAP_COMPL) |\
- (1 << OCMEM_STATE_MAP_FAIL))
- #define _UNMAP_RESPONSE_BIT_MASK_\
- ((1 << OCMEM_STATE_UNMAP_COMPL) |\
- (1 << OCMEM_STATE_UNMAP_FAIL))
- #define _BIT_MASK_\
- ((1 << OCMEM_STATE_SSR) |\
- (1 << OCMEM_STATE_EXIT) |\
- (1 << OCMEM_STATE_GROW) |\
- (1 << OCMEM_STATE_SHRINK))
- #define set_bit_pos(x, y) (atomic_set(&x, (atomic_read(&x) | (1 << y))))
- #define clear_bit_pos(x, y) (atomic_set(&x, (atomic_read(&x) & (~(1 << y)))))
- #define test_bit_pos(x, y) ((atomic_read(&x)) & (1 << y))
- static int enable_ocmem_audio_voice;
- module_param(enable_ocmem_audio_voice, int,
- S_IRUGO | S_IWUSR | S_IWGRP);
- MODULE_PARM_DESC(enable_ocmem_audio_voice, "control OCMEM usage for audio/voice");
- enum {
- OCMEM_STATE_DEFAULT = 0,
- OCMEM_STATE_ALLOC = 1,
- OCMEM_STATE_MAP_TRANSITION,
- OCMEM_STATE_MAP_COMPL,
- OCMEM_STATE_UNMAP_TRANSITION,
- OCMEM_STATE_UNMAP_COMPL,
- OCMEM_STATE_SHRINK,
- OCMEM_STATE_GROW,
- OCMEM_STATE_FREE,
- OCMEM_STATE_MAP_FAIL,
- OCMEM_STATE_UNMAP_FAIL,
- OCMEM_STATE_EXIT,
- OCMEM_STATE_SSR,
- OCMEM_STATE_DISABLE,
- };
- static void audio_ocmem_process_workdata(struct work_struct *work);
- struct audio_ocmem_workdata {
- int id;
- bool en;
- struct work_struct work;
- };
- struct voice_ocmem_workdata {
- int id;
- bool en;
- struct work_struct work;
- };
- struct audio_ocmem_prv {
- atomic_t audio_state;
- struct ocmem_notifier *audio_hdl;
- struct ocmem_buf *buf;
- uint32_t audio_ocmem_bus_client;
- struct ocmem_map_list mlist;
- struct avcs_cmd_rsp_get_low_power_segments_info_t *lp_memseg_ptr;
- wait_queue_head_t audio_wait;
- atomic_t audio_cond;
- atomic_t audio_exit;
- spinlock_t audio_lock;
- struct mutex state_process_lock;
- struct workqueue_struct *audio_ocmem_workqueue;
- struct workqueue_struct *voice_ocmem_workqueue;
- bool ocmem_en;
- bool audio_ocmem_running;
- void *ocmem_ramdump_dev;
- struct ramdump_segment ocmem_ramdump_segment;
- unsigned long ocmem_dump_addr;
- };
- static struct audio_ocmem_prv audio_ocmem_lcl;
- static int audio_ocmem_client_cb(struct notifier_block *this,
- unsigned long event1, void *data)
- {
- int rc = NOTIFY_DONE;
- unsigned long flags;
- struct ocmem_buf *rbuf;
- int vwait = 0;
- pr_debug("%s: event[%ld] cur state[%x]\n", __func__,
- event1, atomic_read(&audio_ocmem_lcl.audio_state));
- spin_lock_irqsave(&audio_ocmem_lcl.audio_lock, flags);
- switch (event1) {
- case OCMEM_MAP_DONE:
- pr_debug("%s: map done\n", __func__);
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_TRANSITION);
- set_bit_pos(audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_COMPL);
- break;
- case OCMEM_MAP_FAIL:
- pr_debug("%s: map fail\n", __func__);
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_TRANSITION);
- set_bit_pos(audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_FAIL);
- break;
- case OCMEM_UNMAP_DONE:
- pr_debug("%s: unmap done\n", __func__);
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_TRANSITION);
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_COMPL);
- break;
- case OCMEM_UNMAP_FAIL:
- pr_debug("%s: unmap fail\n", __func__);
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_TRANSITION);
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_FAIL);
- break;
- case OCMEM_ALLOC_GROW:
- rbuf = data;
- if ((rbuf->len == AUDIO_OCMEM_BUF_SIZE)) {
- audio_ocmem_lcl.buf = data;
- pr_debug("%s: Alloc grow request received buf->addr: 0x%08lx\n",
- __func__,
- (audio_ocmem_lcl.buf)->addr);
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_GROW);
- } else {
- pr_debug("%s: Alloc grow request with size: %ld",
- __func__,
- rbuf->len);
- vwait = 1;
- }
- break;
- case OCMEM_ALLOC_SHRINK:
- pr_debug("%s: Alloc shrink request received\n", __func__);
- set_bit_pos(audio_ocmem_lcl.audio_state, OCMEM_STATE_SHRINK);
- break;
- default:
- pr_err("%s: Invalid event[%ld]\n", __func__, event1);
- break;
- }
- spin_unlock_irqrestore(&audio_ocmem_lcl.audio_lock, flags);
- atomic_set(&audio_ocmem_lcl.audio_cond, 0);
- wake_up(&audio_ocmem_lcl.audio_wait);
- return rc;
- }
- int get_state_to_process(atomic_t *state)
- {
- if (test_bit_pos((*state), OCMEM_STATE_SHRINK)) {
- pr_debug("%s: returning shrink state\n", __func__);
- return OCMEM_STATE_SHRINK;
- } else if (test_bit_pos((*state), OCMEM_STATE_GROW)) {
- pr_debug("%s: returning grow state\n", __func__);
- return OCMEM_STATE_GROW;
- } else if (test_bit_pos((*state), OCMEM_STATE_EXIT)) {
- pr_debug("%s: returning exit state\n", __func__);
- return OCMEM_STATE_EXIT;
- } else if (test_bit_pos((*state), OCMEM_STATE_SSR)) {
- pr_debug("%s: returning ssr state\n", __func__);
- return OCMEM_STATE_SSR;
- } else
- return -EINVAL;
- }
- /**
- * audio_ocmem_enable() - Exercise OCMEM for audio
- * @cid: client id - OCMEM_LP_AUDIO
- *
- * OCMEM gets allocated for audio usecase and the low power
- * segments obtained from the DSP will be moved from/to main
- * memory to OCMEM. Shrink and grow requests will be received
- * and processed accordingly based on the current audio state.
- */
- int audio_ocmem_enable(int cid)
- {
- int ret;
- int i, j;
- int state_bit;
- struct ocmem_buf *buf = NULL;
- struct avcs_cmd_rsp_get_low_power_segments_info_t *lp_segptr;
- pr_debug("%s, %p\n", __func__, &audio_ocmem_lcl);
- atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_DEFAULT);
- if (audio_ocmem_lcl.lp_memseg_ptr == NULL) {
- /* Retrieve low power segments */
- ret = core_get_low_power_segments(
- &audio_ocmem_lcl.lp_memseg_ptr);
- if (ret != 0) {
- pr_err("%s: get low power segments from DSP failed, rc=%d\n",
- __func__, ret);
- mutex_unlock(&audio_ocmem_lcl.state_process_lock);
- goto fail_cmd;
- }
- }
- lp_segptr = audio_ocmem_lcl.lp_memseg_ptr;
- audio_ocmem_lcl.mlist.num_chunks = lp_segptr->num_segments;
- for (i = 0, j = 0; j < audio_ocmem_lcl.mlist.num_chunks; j++, i++) {
- audio_ocmem_lcl.mlist.chunks[j].ro =
- (lp_segptr->mem_segment[i].type == READ_ONLY_SEGMENT);
- audio_ocmem_lcl.mlist.chunks[j].ddr_paddr =
- lp_segptr->mem_segment[i].start_address_lsw;
- audio_ocmem_lcl.mlist.chunks[j].size =
- lp_segptr->mem_segment[i].size;
- pr_debug("%s: ro:%d, ddr_paddr[0x%08x], size[0x%x]\n", __func__,
- audio_ocmem_lcl.mlist.chunks[j].ro,
- (uint32_t)audio_ocmem_lcl.mlist.chunks[j].ddr_paddr,
- (uint32_t)audio_ocmem_lcl.mlist.chunks[j].size);
- }
- /* Non-blocking ocmem allocate (asynchronous) */
- buf = ocmem_allocate_nb(cid, AUDIO_OCMEM_BUF_SIZE);
- if (IS_ERR_OR_NULL(buf)) {
- pr_err("%s: failed: %d\n", __func__, cid);
- ret = -ENOMEM;
- mutex_unlock(&audio_ocmem_lcl.state_process_lock);
- goto fail_cmd;
- }
- set_bit_pos(audio_ocmem_lcl.audio_state, OCMEM_STATE_ALLOC);
- audio_ocmem_lcl.buf = buf;
- atomic_set(&audio_ocmem_lcl.audio_exit, 0);
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- pr_debug("%s: buf->len: %ld\n", __func__, buf->len);
- if (!buf->len) {
- pr_debug("%s: buf.len is 0, waiting for ocmem region\n",
- __func__);
- mutex_unlock(&audio_ocmem_lcl.state_process_lock);
- wait_event_interruptible(audio_ocmem_lcl.audio_wait,
- (atomic_read(&audio_ocmem_lcl.audio_cond) == 0) ||
- (atomic_read(&audio_ocmem_lcl.audio_exit) == 1));
- if (atomic_read(&audio_ocmem_lcl.audio_exit)) {
- ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
- if (ret) {
- pr_err("%s: ocmem_free failed, state[%d]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- }
- pr_info("%s: audio playback ended while waiting for ocmem\n",
- __func__);
- ret = 0;
- goto fail_cmd;
- }
- clear_bit_pos(audio_ocmem_lcl.audio_state, OCMEM_STATE_GROW);
- mutex_trylock(&audio_ocmem_lcl.state_process_lock);
- }
- pr_debug("%s: buf->len: %ld\n", __func__, (audio_ocmem_lcl.buf)->len);
- /* vote for ocmem bus bandwidth */
- ret = msm_bus_scale_client_update_request(
- audio_ocmem_lcl.audio_ocmem_bus_client,
- 1);
- if (ret)
- pr_err("%s: failed to vote for bus bandwidth\n", __func__);
- set_bit_pos(audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_TRANSITION);
- pr_debug("%s: buf->addr: 0x%08lx, len: %ld, audio_state[0x%x]\n",
- __func__,
- audio_ocmem_lcl.buf->addr,
- audio_ocmem_lcl.buf->len,
- atomic_read(&audio_ocmem_lcl.audio_state));
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- ret = ocmem_map(cid, audio_ocmem_lcl.buf, &audio_ocmem_lcl.mlist);
- if (ret) {
- pr_err("%s: ocmem_map failed\n", __func__);
- atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_MAP_FAIL);
- goto fail_cmd1;
- }
- wait_event_interruptible(audio_ocmem_lcl.audio_wait,
- (atomic_read(&audio_ocmem_lcl.audio_state) &
- _MAP_RESPONSE_BIT_MASK_) != 0);
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- mutex_unlock(&audio_ocmem_lcl.state_process_lock);
- pr_debug("%s: audio_cond[%d] audio_state[0x%x]\n", __func__,
- atomic_read(&audio_ocmem_lcl.audio_cond),
- atomic_read(&audio_ocmem_lcl.audio_state));
- while ((test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_DISABLE)) == 0) {
- wait_event_interruptible(audio_ocmem_lcl.audio_wait,
- (atomic_read(&audio_ocmem_lcl.audio_state) &
- _BIT_MASK_) != 0);
- mutex_lock(&audio_ocmem_lcl.state_process_lock);
- state_bit = get_state_to_process(&audio_ocmem_lcl.audio_state);
- switch (state_bit) {
- case OCMEM_STATE_MAP_COMPL:
- pr_debug("%s: audio_cond[0x%x], audio_state[0x%x]\n",
- __func__, atomic_read(&audio_ocmem_lcl.audio_cond),
- atomic_read(&audio_ocmem_lcl.audio_state));
- atomic_set(&audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_COMPL);
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- break;
- case OCMEM_STATE_SHRINK:
- pr_debug("%s: ocmem shrink request process\n",
- __func__);
- if (test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_COMPL)) {
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_COMPL);
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_TRANSITION);
- ret = ocmem_unmap(cid, audio_ocmem_lcl.buf,
- &audio_ocmem_lcl.mlist);
- if (ret) {
- pr_err("%s: ocmem_unmap failed, state[%d]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd1;
- }
- wait_event_interruptible(audio_ocmem_lcl.audio_wait,
- (atomic_read(&audio_ocmem_lcl.audio_state) &
- _UNMAP_RESPONSE_BIT_MASK_)
- != 0);
- ret = ocmem_shrink(cid, audio_ocmem_lcl.buf, 0);
- if (ret) {
- pr_err("%s: ocmem_shrink failed, state[%d]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd1;
- }
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_SHRINK);
- }
- pr_debug("%s:shrink process complete\n", __func__);
- break;
- case OCMEM_STATE_GROW:
- pr_debug("%s: ocmem grow request process\n",
- __func__);
- if (test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_COMPL)) {
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_COMPL);
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_TRANSITION);
- ret = ocmem_map(cid, audio_ocmem_lcl.buf,
- &audio_ocmem_lcl.mlist);
- if (ret) {
- pr_err("%s: ocmem_map failed, state[%d]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd1;
- }
- wait_event_interruptible(audio_ocmem_lcl.audio_wait,
- (atomic_read(&audio_ocmem_lcl.audio_state) &
- _MAP_RESPONSE_BIT_MASK_) != 0);
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_GROW);
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- }
- break;
- case OCMEM_STATE_EXIT:
- if (test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_COMPL)) {
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_COMPL);
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_TRANSITION);
- ret = ocmem_unmap(cid, audio_ocmem_lcl.buf,
- &audio_ocmem_lcl.mlist);
- if (ret) {
- pr_err("%s: ocmem_unmap failed, state[0x%x]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd1;
- }
- wait_event_interruptible(
- audio_ocmem_lcl.audio_wait,
- (atomic_read(&audio_ocmem_lcl.audio_state) &
- _UNMAP_RESPONSE_BIT_MASK_) != 0);
- }
- if (test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_SHRINK)) {
- pr_debug("%s: SHRINK while exiting\n",
- __func__);
- ret = ocmem_shrink(cid, audio_ocmem_lcl.buf,
- 0);
- if (ret) {
- pr_err("%s: ocmem_shrink failed, state[0x%x]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd1;
- }
- clear_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_SHRINK);
- }
- if (test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_DISABLE) ||
- test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_FREE)) {
- pr_info("%s: audio already freed from ocmem, state[0x%x]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd2;
- }
- pr_debug("%s: calling ocmem free, state:0x%x\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
- if (ret == -EAGAIN) {
- pr_debug("%s: received EAGAIN\n", __func__);
- if (test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_SHRINK)) {
- ret = ocmem_shrink(cid,
- audio_ocmem_lcl.buf,
- 0);
- if (ret) {
- pr_err("%s: ocmem_shrink failed, state[0x%x]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- goto fail_cmd1;
- }
- pr_debug("calling free after EAGAIN");
- ret = ocmem_free(OCMEM_LP_AUDIO,
- audio_ocmem_lcl.buf);
- if (ret) {
- pr_err("%s: ocmem_free failed\n",
- __func__);
- goto fail_cmd2;
- }
- } else {
- pr_debug("%s: shrink callback already processed\n",
- __func__);
- goto fail_cmd1;
- }
- } else if (ret) {
- pr_err("%s: ocmem_free failed, state[0x%x], ret:%d\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state),
- ret);
- goto fail_cmd2;
- }
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_FREE);
- pr_debug("%s: ocmem_free success, state[0x%x]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_state));
- /* Fall through */
- case OCMEM_STATE_SSR:
- msm_bus_scale_client_update_request(
- audio_ocmem_lcl.audio_ocmem_bus_client,
- 0);
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_DISABLE);
- break;
- case -EINVAL:
- pr_info("%s: audio_cond[%d] audio_state[0x%x]\n",
- __func__,
- atomic_read(&audio_ocmem_lcl.audio_cond),
- atomic_read(&audio_ocmem_lcl.audio_state));
- break;
- }
- mutex_unlock(&audio_ocmem_lcl.state_process_lock);
- }
- ret = 0;
- goto fail_cmd;
- fail_cmd1:
- ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
- if (ret)
- pr_err("%s: ocmem_free failed\n", __func__);
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_FREE);
- fail_cmd2:
- mutex_unlock(&audio_ocmem_lcl.state_process_lock);
- fail_cmd:
- pr_debug("%s: exit\n", __func__);
- audio_ocmem_lcl.buf = NULL;
- audio_ocmem_lcl.audio_ocmem_running = false;
- return ret;
- }
- /**
- * audio_ocmem_disable() - Disable OCMEM for audio
- * @cid: client id - OCMEM_LP_AUDIO
- *
- * OCMEM gets deallocated for audio usecase. Depending on
- * current audio state, OCMEM will be freed from using audio
- * segments.
- */
- int audio_ocmem_disable(int cid)
- {
- pr_debug("%s: audio_cond[0x%x], audio_state[0x%x]\n", __func__,
- atomic_read(&audio_ocmem_lcl.audio_cond),
- atomic_read(&audio_ocmem_lcl.audio_state));
- if (!test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_SSR))
- set_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_EXIT);
- wake_up(&audio_ocmem_lcl.audio_wait);
- mutex_unlock(&audio_ocmem_lcl.state_process_lock);
- pr_debug("%s: exit\n", __func__);
- return 0;
- }
- static void voice_ocmem_process_workdata(struct work_struct *work)
- {
- int cid;
- bool en;
- int rc = 0;
- struct voice_ocmem_workdata *voice_ocm_work =
- container_of(work, struct voice_ocmem_workdata, work);
- en = voice_ocm_work->en;
- switch (voice_ocm_work->id) {
- case VOICE:
- cid = OCMEM_VOICE;
- if (en)
- disable_ocmem_for_voice(cid);
- else
- enable_ocmem_after_voice(cid);
- break;
- default:
- pr_err("%s: Invalid client id[%d]\n", __func__,
- voice_ocm_work->id);
- rc = -EINVAL;
- }
- kfree(voice_ocm_work);
- return;
- }
- /**
- * voice_ocmem_process_req() - disable/enable OCMEM during voice call
- * @cid: client id - VOICE
- * @enable: 1 - enable
- * 0 - disable
- *
- * This configures OCMEM during start of voice call. If any
- * audio clients are already using OCMEM, they will be evicted
- * out of OCMEM during voice call and get restored after voice
- * call.
- */
- int voice_ocmem_process_req(int cid, bool enable)
- {
- struct voice_ocmem_workdata *workdata = NULL;
- if (enable) {
- if (!enable_ocmem_audio_voice)
- audio_ocmem_lcl.ocmem_en = false;
- else
- audio_ocmem_lcl.ocmem_en = true;
- }
- if (audio_ocmem_lcl.ocmem_en) {
- if (audio_ocmem_lcl.voice_ocmem_workqueue == NULL) {
- pr_err("%s: voice ocmem workqueue is NULL\n",
- __func__);
- return -EINVAL;
- }
- workdata = kzalloc(sizeof(struct voice_ocmem_workdata),
- GFP_ATOMIC);
- if (workdata == NULL) {
- pr_err("%s: mem failure\n", __func__);
- return -ENOMEM;
- }
- workdata->id = cid;
- workdata->en = enable;
- INIT_WORK(&workdata->work, voice_ocmem_process_workdata);
- queue_work(audio_ocmem_lcl.voice_ocmem_workqueue,
- &workdata->work);
- }
- return 0;
- }
- /**
- * disable_ocmem_for_voice() - disable OCMEM during voice call
- * @cid: client id - OCMEM_VOICE
- *
- * This configures OCMEM during start of voice call. If any
- * audio clients are already using OCMEM, they will be evicted
- */
- int disable_ocmem_for_voice(int cid)
- {
- int ret;
- ret = ocmem_evict(cid);
- if (ret)
- pr_err("%s: ocmem_evict is not successful\n", __func__);
- return ret;
- }
- /**
- * enable_ocmem_for_voice() - To enable OCMEM after voice call
- * @cid: client id - OCMEM_VOICE
- *
- * OCMEM gets re-enabled after OCMEM voice call. If other client
- * is evicted out of OCMEM, that gets restored and remapped in
- * OCMEM after the voice call.
- */
- int enable_ocmem_after_voice(int cid)
- {
- int ret;
- ret = ocmem_restore(cid);
- if (ret)
- pr_err("%s: ocmem_restore is not successful\n", __func__);
- return ret;
- }
- static void audio_ocmem_process_workdata(struct work_struct *work)
- {
- int cid;
- bool en;
- int rc = 0;
- struct audio_ocmem_workdata *audio_ocm_work =
- container_of(work, struct audio_ocmem_workdata, work);
- en = audio_ocm_work->en;
- mutex_lock(&audio_ocmem_lcl.state_process_lock);
- /* if previous work waiting for ocmem - signal it to exit */
- atomic_set(&audio_ocmem_lcl.audio_exit, 1);
- pr_debug("%s: acquired mutex for %d\n", __func__, en);
- switch (audio_ocm_work->id) {
- case AUDIO:
- cid = OCMEM_LP_AUDIO;
- if (en)
- audio_ocmem_enable(cid);
- else
- audio_ocmem_disable(cid);
- break;
- default:
- pr_err("%s: Invalid client id[%d]\n", __func__,
- audio_ocm_work->id);
- rc = -EINVAL;
- }
- kfree(audio_ocm_work);
- return;
- }
- /**
- * audio_ocmem_process_req() - process audio request to use OCMEM
- * @id: client id - OCMEM_LP_AUDIO
- * @enable: enable or disable OCMEM
- *
- * A workqueue gets created and initialized to use OCMEM for
- * audio clients.
- */
- int audio_ocmem_process_req(int id, bool enable)
- {
- struct audio_ocmem_workdata *workdata = NULL;
- if (enable) {
- if (!enable_ocmem_audio_voice)
- audio_ocmem_lcl.ocmem_en = false;
- else
- audio_ocmem_lcl.ocmem_en = true;
- }
- if (audio_ocmem_lcl.ocmem_en &&
- (!enable || !audio_ocmem_lcl.audio_ocmem_running)) {
- if (audio_ocmem_lcl.audio_ocmem_workqueue == NULL) {
- pr_err("%s: audio ocmem workqueue is NULL\n",
- __func__);
- return -EINVAL;
- }
- workdata = kzalloc(sizeof(struct audio_ocmem_workdata),
- GFP_ATOMIC);
- if (workdata == NULL) {
- pr_err("%s: mem failure\n", __func__);
- return -ENOMEM;
- }
- workdata->id = id;
- workdata->en = enable;
- audio_ocmem_lcl.audio_ocmem_running = true;
- INIT_WORK(&workdata->work, audio_ocmem_process_workdata);
- queue_work(audio_ocmem_lcl.audio_ocmem_workqueue,
- &workdata->work);
- }
- return 0;
- }
- static struct notifier_block audio_ocmem_client_nb = {
- .notifier_call = audio_ocmem_client_cb,
- };
- static int audio_ocmem_platform_data_populate(struct platform_device *pdev)
- {
- struct msm_bus_scale_pdata *audio_ocmem_adata = NULL;
- if (!pdev->dev.of_node) {
- pr_err("%s: device tree information missing\n", __func__);
- return -ENODEV;
- }
- audio_ocmem_adata = msm_bus_cl_get_pdata(pdev);
- if (!audio_ocmem_adata) {
- pr_err("%s: bus device tree allocation failed\n", __func__);
- return -EINVAL;
- }
- dev_set_drvdata(&pdev->dev, audio_ocmem_adata);
- return 0;
- }
- static void do_ocmem_ramdump(void)
- {
- int ret = 0;
- void *virt = NULL;
- virt = ioremap(audio_ocmem_lcl.ocmem_dump_addr, AUDIO_OCMEM_BUF_SIZE);
- ret = ocmem_dump(OCMEM_LP_AUDIO,
- audio_ocmem_lcl.buf,
- (unsigned long)virt);
- iounmap(virt);
- if (ret)
- pr_err("%s: ocmem_dump failed\n", __func__);
- audio_ocmem_lcl.ocmem_ramdump_segment.address
- = (unsigned long)audio_ocmem_lcl.ocmem_dump_addr;
- audio_ocmem_lcl.ocmem_ramdump_segment.size
- = AUDIO_OCMEM_BUF_SIZE;
- ret = do_ramdump(audio_ocmem_lcl.ocmem_ramdump_dev,
- &audio_ocmem_lcl.ocmem_ramdump_segment,
- 1);
- if (ret < 0)
- pr_err("%s: do_ramdump failed\n", __func__);
- }
- static void process_ocmem_dump(void)
- {
- int ret = 0;
- set_bit_pos(audio_ocmem_lcl.audio_state, OCMEM_STATE_SSR);
- if (atomic_read(&audio_ocmem_lcl.audio_state) &
- _DO_OCMEM_DUMP_BIT_MASK_) {
- wait_event_interruptible(audio_ocmem_lcl.audio_wait,
- (atomic_read(&audio_ocmem_lcl.audio_state) &
- _WAIT_BFR_DUMP_BIT_MASK_) != 0);
- if (test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_MAP_COMPL) ||
- test_bit_pos(audio_ocmem_lcl.audio_state,
- OCMEM_STATE_UNMAP_FAIL)) {
- if (audio_ocmem_lcl.ocmem_dump_addr &&
- audio_ocmem_lcl.ocmem_ramdump_dev)
- do_ocmem_ramdump();
- else
- pr_err("%s: Error calling ocmem ramdump\n",
- __func__);
- ret = ocmem_drop(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf,
- &audio_ocmem_lcl.mlist);
- if (ret)
- pr_err("%s: ocmem_drop failed\n", __func__);
- }
- }
- ret = ocmem_free(OCMEM_LP_AUDIO, audio_ocmem_lcl.buf);
- if (ret)
- pr_err("%s: ocmem_free failed\n", __func__);
- audio_ocmem_lcl.buf = NULL;
- }
- static int lpass_notifier_cb(struct notifier_block *this, unsigned long code,
- void *_cmd)
- {
- int ret = NOTIFY_DONE;
- switch (code) {
- case SUBSYS_BEFORE_SHUTDOWN:
- pr_debug("AO-Notify: Shutdown started\n");
- break;
- case SUBSYS_AFTER_SHUTDOWN:
- pr_debug("AO-Notify: Shutdown Completed\n");
- break;
- case SUBSYS_RAMDUMP_NOTIFICATION:
- pr_debug("AO-Notify: OCMEM dump\n");
- if (audio_ocmem_lcl.ocmem_en &&
- audio_ocmem_lcl.audio_ocmem_running)
- process_ocmem_dump();
- pr_debug("AO-Notify: OCMEM dump done\n");
- break;
- case SUBSYS_BEFORE_POWERUP:
- pr_debug("AO-Notify: Powerup started\n");
- break;
- case SUBSYS_AFTER_POWERUP:
- pr_debug("AO-Notify: Powerup completed\n");
- break;
- default:
- pr_err("AO-Notify: Generel: %lu\n", code);
- break;
- }
- return ret;
- }
- static struct notifier_block anb = {
- .notifier_call = lpass_notifier_cb,
- };
- static int ocmem_audio_client_probe(struct platform_device *pdev)
- {
- int ret;
- struct msm_bus_scale_pdata *audio_ocmem_bus_scale_pdata = NULL;
- pr_debug("%s\n", __func__);
- audio_ocmem_lcl.audio_hdl = ocmem_notifier_register(OCMEM_LP_AUDIO,
- &audio_ocmem_client_nb);
- if (PTR_RET(audio_ocmem_lcl.audio_hdl) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- else if (!audio_ocmem_lcl.audio_hdl) {
- pr_err("%s: Failed to get ocmem handle %d\n", __func__,
- OCMEM_LP_AUDIO);
- return -ENODEV;
- }
- subsys_notif_register_notifier("adsp", &anb);
- audio_ocmem_lcl.ocmem_dump_addr =
- allocate_contiguous_memory_nomap(AUDIO_OCMEM_BUF_SIZE,
- MEMTYPE_EBI1,
- AUDIO_OCMEM_BUF_SIZE);
- if (audio_ocmem_lcl.ocmem_dump_addr) {
- audio_ocmem_lcl.ocmem_ramdump_dev =
- create_ramdump_device("audio-ocmem", &pdev->dev);
- if (!audio_ocmem_lcl.ocmem_ramdump_dev)
- pr_info("%s: audio-ocmem ramdump device failed\n",
- __func__);
- } else {
- pr_err("%s: ocmem dump memory alloc failed\n", __func__);
- }
- audio_ocmem_lcl.audio_ocmem_workqueue =
- alloc_workqueue("ocmem_audio_client_driver_audio",
- WQ_NON_REENTRANT | WQ_UNBOUND, 0);
- if (!audio_ocmem_lcl.audio_ocmem_workqueue) {
- pr_err("%s: Failed to create ocmem audio work queue\n",
- __func__);
- ret = -ENOMEM;
- goto destroy_ramdump;
- }
- audio_ocmem_lcl.voice_ocmem_workqueue =
- alloc_workqueue("ocmem_audio_client_driver_voice",
- WQ_NON_REENTRANT, 0);
- if (!audio_ocmem_lcl.voice_ocmem_workqueue) {
- pr_info("%s: Failed to create ocmem voice work queue\n",
- __func__);
- ret = -ENOMEM;
- goto destroy_audio_wq;
- }
- init_waitqueue_head(&audio_ocmem_lcl.audio_wait);
- atomic_set(&audio_ocmem_lcl.audio_cond, 1);
- atomic_set(&audio_ocmem_lcl.audio_state, OCMEM_STATE_DEFAULT);
- atomic_set(&audio_ocmem_lcl.audio_exit, 0);
- spin_lock_init(&audio_ocmem_lcl.audio_lock);
- mutex_init(&audio_ocmem_lcl.state_process_lock);
- audio_ocmem_lcl.ocmem_en = true;
- audio_ocmem_lcl.audio_ocmem_running = false;
- /* populate platform data */
- ret = audio_ocmem_platform_data_populate(pdev);
- if (ret) {
- dev_err(&pdev->dev, "%s: failed to populate platform data, rc = %d\n",
- __func__, ret);
- goto destroy_voice_wq;
- }
- audio_ocmem_bus_scale_pdata = dev_get_drvdata(&pdev->dev);
- audio_ocmem_lcl.audio_ocmem_bus_client =
- msm_bus_scale_register_client(audio_ocmem_bus_scale_pdata);
- if (!audio_ocmem_lcl.audio_ocmem_bus_client) {
- pr_err("%s: msm_bus_scale_register_client() failed\n",
- __func__);
- ret = -EFAULT;
- goto destroy_voice_wq;
- }
- audio_ocmem_lcl.lp_memseg_ptr = NULL;
- return 0;
- destroy_voice_wq:
- if (audio_ocmem_lcl.voice_ocmem_workqueue) {
- destroy_workqueue(audio_ocmem_lcl.voice_ocmem_workqueue);
- audio_ocmem_lcl.voice_ocmem_workqueue = NULL;
- }
- destroy_audio_wq:
- if (audio_ocmem_lcl.audio_ocmem_workqueue) {
- destroy_workqueue(audio_ocmem_lcl.audio_ocmem_workqueue);
- audio_ocmem_lcl.audio_ocmem_workqueue = NULL;
- }
- destroy_ramdump:
- if (audio_ocmem_lcl.ocmem_ramdump_dev)
- destroy_ramdump_device(audio_ocmem_lcl.ocmem_ramdump_dev);
- if (audio_ocmem_lcl.ocmem_dump_addr)
- free_contiguous_memory_by_paddr(
- audio_ocmem_lcl.ocmem_dump_addr);
- return ret;
- }
- static int ocmem_audio_client_remove(struct platform_device *pdev)
- {
- struct msm_bus_scale_pdata *audio_ocmem_bus_scale_pdata = NULL;
- audio_ocmem_bus_scale_pdata = (struct msm_bus_scale_pdata *)
- dev_get_drvdata(&pdev->dev);
- msm_bus_cl_clear_pdata(audio_ocmem_bus_scale_pdata);
- ocmem_notifier_unregister(audio_ocmem_lcl.audio_hdl,
- &audio_ocmem_client_nb);
- if (audio_ocmem_lcl.ocmem_ramdump_dev)
- destroy_ramdump_device(audio_ocmem_lcl.ocmem_ramdump_dev);
- if (audio_ocmem_lcl.ocmem_dump_addr)
- free_contiguous_memory_by_paddr(
- audio_ocmem_lcl.ocmem_dump_addr);
- return 0;
- }
- static const struct of_device_id msm_ocmem_audio_dt_match[] = {
- {.compatible = "qcom,msm-ocmem-audio"},
- {}
- };
- MODULE_DEVICE_TABLE(of, msm_ocmem_audio_dt_match);
- static struct platform_driver audio_ocmem_driver = {
- .driver = {
- .name = "audio-ocmem",
- .owner = THIS_MODULE,
- .of_match_table = msm_ocmem_audio_dt_match,
- },
- .probe = ocmem_audio_client_probe,
- .remove = ocmem_audio_client_remove,
- };
- static int __init ocmem_audio_client_init(void)
- {
- int rc;
- rc = platform_driver_register(&audio_ocmem_driver);
- if (rc)
- pr_err("%s: Failed to register audio ocmem driver\n", __func__);
- return rc;
- }
- module_init(ocmem_audio_client_init);
- static void __exit ocmem_audio_client_exit(void)
- {
- platform_driver_unregister(&audio_ocmem_driver);
- }
- module_exit(ocmem_audio_client_exit);
|