/* arch/arm/mach-msm/qdsp6_10/q6audio.c * * Copyright (C) 2010 Cotulla * Copyright (C) 2009 Google, Inc. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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 #include #include #include #include #include #include #include #include #include "dal.h" #include "dal_audio.h" #include "dal_audio_format.h" #include "dal_acdb.h" #include "dal_adie.h" #include #include #include #include #include #include "q6audio_devices.h" #include "../dex_comm.h" #if 0 #define TRACE(x...) pr_info("Q6: "x) #else #define TRACE(x...) do{}while(0) #endif #if 0 #define AUDIO_INFO(x...) pr_info("Audio: "x) #else #define AUDIO_INFO(x...) do{}while(0) #endif #define CB_EVENT_COOKIE 0xC00CE13E #if 1 // LEO static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { [Q6_HW_HANDSET] = { .min_gain = -2000, .max_gain = 0, }, [Q6_HW_HEADSET] = { .min_gain = -2000, .max_gain = 0, }, [Q6_HW_SPEAKER] = { .min_gain = -1000, .max_gain = 500, }, [Q6_HW_TTY] = { .min_gain = 0, .max_gain = 0, }, [Q6_HW_BT_SCO] = { .min_gain = -1100, .max_gain = 400, }, [Q6_HW_BT_A2DP] = { .min_gain = -1100, .max_gain = 400, }, }; #else // Old one (from Desire) static struct q6_hw_info q6_audio_hw[Q6_HW_COUNT] = { [Q6_HW_HANDSET] = { .min_gain = -2000, .max_gain = 0, }, [Q6_HW_HEADSET] = { .min_gain = -2000, .max_gain = 0, }, [Q6_HW_SPEAKER] = { .min_gain = -1500, .max_gain = 0, }, [Q6_HW_TTY] = { .min_gain = -2000, .max_gain = 0, }, [Q6_HW_BT_SCO] = { .min_gain = -2000, .max_gain = 0, }, [Q6_HW_BT_A2DP] = { .min_gain = -2000, .max_gain = 0, }, }; #endif extern int global_now_phone_call; static struct audio_client * audio_test(void); static void callback(void *data, int len, void *cookie); static int audio_init(struct audio_client *ac); static int audio_info(struct audio_client *ac); static int q6audio_init_rx_volumes(); static struct wake_lock wakelock; static struct wake_lock idlelock; static int idlecount; static DEFINE_MUTEX(idlecount_lock); void audio_prevent_sleep(void) { mutex_lock(&idlecount_lock); if (++idlecount == 1) { wake_lock(&wakelock); wake_lock(&idlelock); } mutex_unlock(&idlecount_lock); } void audio_allow_sleep(void) { mutex_lock(&idlecount_lock); if (--idlecount == 0) { wake_unlock(&idlelock); wake_unlock(&wakelock); } mutex_unlock(&idlecount_lock); } static struct clk *icodec_rx_clk; static struct clk *icodec_tx_clk; static struct clk *ecodec_clk; static struct clk *sdac_clk; static struct q6audio_analog_ops default_analog_ops; static struct q6audio_analog_ops *analog_ops = &default_analog_ops; static uint32_t tx_clk_freq = 8000; static int tx_mute_status = 0; static int rx_vol_level = 100; static char acdb_file[64] = "default.acdb"; static uint32_t tx_acdb = 0; static uint32_t rx_acdb = 0; static int acdb_use_rpc = 0; ///////////////////////////////////////////////////////////////////////////////// // helper functions for device parameters ///////////////////////////////////////////////////////////////////////////////// void q6audio_register_analog_ops(struct q6audio_analog_ops *ops) { pr_info("register analog ops = %p\n", ops); analog_ops = ops; } void q6audio_set_acdb_file(char* filename) { if (filename) { pr_info("audio: set acdb file as %s\n", filename); strncpy(acdb_file, filename, sizeof(acdb_file)-1); } } static struct q6_device_info *q6_lookup_device(uint32_t device_id) { struct q6_device_info *di = q6_audio_devices; for (;;) { if (di->id == device_id) return di; if (di->id == 0) { pr_err("q6_lookup_device: bogus id 0x%08x\n", device_id); return di; } di++; } } static uint32_t q6_device_to_codec(uint32_t device_id) { struct q6_device_info *di = q6_lookup_device(device_id); return di->codec; } static uint32_t q6_device_to_dir(uint32_t device_id) { struct q6_device_info *di = q6_lookup_device(device_id); return di->dir; } static uint32_t q6_device_to_cad_id(uint32_t device_id) { struct q6_device_info *di = q6_lookup_device(device_id); return di->cad_id; } static uint32_t q6_device_to_path(uint32_t device_id) { struct q6_device_info *di = q6_lookup_device(device_id); return di->path; } static uint32_t q6_device_to_rate(uint32_t device_id) { struct q6_device_info *di = q6_lookup_device(device_id); return di->rate; } int q6_device_volume(uint32_t device_id, int level) { struct q6_device_info *di = q6_lookup_device(device_id); if (analog_ops->get_rx_vol) return analog_ops->get_rx_vol(di->hw, level); else { struct q6_hw_info *hw; hw = &q6_audio_hw[di->hw]; return hw->min_gain + ((hw->max_gain - hw->min_gain) * level) / 100; } } ///////////////////////////////////////////////////////////////////////////////// // ADIE functions ///////////////////////////////////////////////////////////////////////////////// static inline int adie_open(struct dal_client *client) { return dal_call_f0(client, DAL_OP_OPEN, 0); } static inline int adie_close(struct dal_client *client) { return dal_call_f0(client, DAL_OP_CLOSE, 0); } static inline int adie_set_path(struct dal_client *client, uint32_t id, uint32_t path_type) { return dal_call_f1(client, ADIE_OP_SET_PATH, id, path_type); } static inline int adie_set_path_freq_plan(struct dal_client *client, uint32_t path_type, uint32_t plan) { return dal_call_f1(client, ADIE_OP_SET_PATH_FREQUENCY_PLAN, path_type, plan); } static inline int adie_proceed_to_stage(struct dal_client *client, uint32_t path_type, uint32_t stage) { return dal_call_f1(client, ADIE_OP_PROCEED_TO_STAGE, path_type, stage); } static inline int adie_mute_path(struct dal_client *client, uint32_t path_type, uint32_t mute_state) { return dal_call_f1(client, ADIE_OP_MUTE_PATH, path_type, mute_state); } static int adie_refcount; static struct dal_client *adie; static struct dal_client *adsp; static struct dal_client *acdb; static int adie_enable(void) { adie_refcount++; if (adie_refcount == 1) adie_open(adie); return 0; } static int adie_disable(void) { adie_refcount--; if (adie_refcount == 0) adie_close(adie); return 0; } /* 4k DMA scratch page used for exchanging acdb device config tables * and stream format descriptions with the DSP. */ static void *audio_data; static dma_addr_t audio_phys; // this memory used to pass open params into DSP static void *params_data; static dma_addr_t params_phys; #define SESSION_MIN 0 #define SESSION_MAX 64 static DEFINE_MUTEX(session_lock); static DEFINE_MUTEX(audio_lock); static DEFINE_MUTEX(open_mem_lock); static struct audio_client *session[SESSION_MAX]; static int session_alloc(struct audio_client *ac) { int n; mutex_lock(&session_lock); for (n = SESSION_MIN; n < SESSION_MAX; n++) { if (!session[n]) { session[n] = ac; mutex_unlock(&session_lock); return n; } } mutex_unlock(&session_lock); return -ENOMEM; } static void session_free(int n, struct audio_client *ac) { mutex_lock(&session_lock); if (session[n] == ac) session[n] = 0; mutex_unlock(&session_lock); } void audio_client_dump(struct audio_client *ac) { dal_trace_dump(ac->client); } static void audio_client_free(struct audio_client *ac) { AUDIO_INFO("%s: session %d\n", __func__, ac->session); session_free(ac->session, ac); if (ac->buf[0].data) dma_free_coherent(NULL, ac->buf[0].size, ac->buf[0].data, ac->buf[0].phys); if (ac->buf[1].data) dma_free_coherent(NULL, ac->buf[1].size, ac->buf[1].data, ac->buf[1].phys); if (ac->client) dal_detach(ac->client); kfree(ac); } static struct audio_client *audio_client_alloc(unsigned bufsz) { struct audio_client *ac; struct dal_client *dsp; int n; AUDIO_INFO("%s\n", __func__); ac = kzalloc(sizeof(*ac), GFP_KERNEL); if (!ac) { AUDIO_INFO("%s: alloc error\n", __func__); return 0; } n = session_alloc(ac); if (n < 0) { AUDIO_INFO("%s: session alloc error\n", __func__); goto fail_session; } ac->session = n; AUDIO_INFO("%s: session %d\n", __func__, ac->session); if (bufsz > 0) { ac->buf[0].data = dma_alloc_coherent(NULL, bufsz, &ac->buf[0].phys, GFP_KERNEL); if (!ac->buf[0].data) { goto fail; } ac->buf[1].data = dma_alloc_coherent(NULL, bufsz, &ac->buf[1].phys, GFP_KERNEL); if (!ac->buf[1].data) { goto fail; } ac->buf[0].size = bufsz; ac->buf[1].size = bufsz; } init_waitqueue_head(&ac->wait); // dsp = dal_attach(AUDIO_DAL_DEVICE, AUDIO_DAL_PORT, callback, 0); dsp = dal_attach_ex(AUDIO_DAL_DEVICE, "AudioQdsp", AUDIO_DAL_PORT, callback, 0); if (!dsp) { pr_err("audio_client_alloc: cannot attach to adsp\n"); goto fail; } ac->client = dsp; audio_init(ac); audio_info(ac); audio_client_dump(ac); return ac; fail: if (ac->buf[0].data) dma_free_coherent(NULL, ac->buf[0].size, ac->buf[0].data, ac->buf[0].phys); if (ac->buf[1].data) dma_free_coherent(NULL, ac->buf[1].size, ac->buf[1].data, ac->buf[1].phys); session_free(n, ac); fail_session: audio_client_free(ac); return 0; } static int audio_ioctl(struct audio_client *ac, uint32_t cmd, void *ptr, uint32_t len) { int r; r = dal_call_f6(ac->client, AUDIO_OP_IOCTL, cmd, ptr, len); return r; } static int audio_command(struct audio_client *ac, uint32_t cmd) { struct adsp_command_hdr rpc; memset(&rpc, 0, sizeof(rpc)); return audio_ioctl(ac, cmd, NULL, 0); } static int audio_open_control(struct audio_client *ac) { int r; struct adsp_open_command rpc; memset(&rpc, 0, sizeof(rpc)); rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_DEV; // reset flag before call, callback() will set it later ac->open_done = 0; r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); if (r != 0) return r; // wait for async event if (!wait_event_timeout(ac->wait, (ac->open_done == 1), 3 * HZ)) { pr_err("wait for open async event failed!\n"); return -1; } return ac->open_status; } static int audio_out_open(struct audio_client *ac, uint32_t bufsz, uint32_t rate, uint32_t channels) { int r; struct adsp_open_command rpc; union adsp_audio_format *ptr; TRACE("audio_out_open: %x %d %d\n", bufsz, rate, channels); mutex_lock(&open_mem_lock); memset(&rpc, 0, sizeof(rpc)); ptr = (union adsp_audio_format*)params_data; memset(ptr, 0, sizeof(union adsp_audio_format)); rpc.numdev = 1; // rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO; rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; rpc.buf_max_size = bufsz; rpc.format = ADSP_AUDIO_FORMAT_PCM; rpc.pblock = params_phys; rpc.blocklen = sizeof(struct adsp_audio_standard_format); // rpc.pblock = 0; // rpc.blocklen = 0; rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_WRITE; ptr->standard.channels = channels; ptr->standard.bits_per_sample = 16; ptr->standard.sampling_rate = rate; ptr->standard.is_signed = 1; ptr->standard.is_interleaved = 1; AUDIO_INFO("open out %p\n", ac); ac->open_done = 0; r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); // wait for async event if (!wait_event_timeout(ac->wait, (ac->open_done == 1), 3 * HZ)) { mutex_unlock(&open_mem_lock); pr_err("wait for open async event failed!\n"); return -1; } mutex_unlock(&open_mem_lock); // return r; return ac->open_status; } static int audio_in_open(struct audio_client *ac, uint32_t bufsz, uint32_t rate, uint32_t channels) { int r; struct adsp_open_command rpc; union adsp_audio_format *ptr; TRACE("audio_in_open: %x %d %d\n", bufsz, rate, channels); mutex_lock(&open_mem_lock); memset(&rpc, 0, sizeof(rpc)); ptr = (union adsp_audio_format*)params_data; memset(ptr, 0, sizeof(union adsp_audio_format)); rpc.numdev = 1; rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; rpc.buf_max_size = bufsz; rpc.format = ADSP_AUDIO_FORMAT_PCM; rpc.pblock = params_phys; rpc.blocklen = sizeof(struct adsp_audio_standard_format); rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_READ; ptr->standard.channels = channels; ptr->standard.bits_per_sample = 16; ptr->standard.sampling_rate = rate; ptr->standard.is_signed = 1; ptr->standard.is_interleaved = 1; AUDIO_INFO("%p: open in\n", ac); ac->open_done = 0; r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); // wait for async event if (!wait_event_timeout(ac->wait, (ac->open_done == 1), 3 * HZ)) { mutex_unlock(&open_mem_lock); pr_err("wait for open async event failed (IN)!\n"); return -1; } mutex_unlock(&open_mem_lock); // return r; return ac->open_status; } static int audio_mp3_open(struct audio_client *ac, uint32_t bufsz, uint32_t rate, uint32_t channels) { int r; struct adsp_open_command rpc; union adsp_audio_format *ptr; TRACE("audio_mp3_open: %x %d %d\n", bufsz, rate, channels); mutex_lock(&open_mem_lock); memset(&rpc, 0, sizeof(rpc)); ptr = (union adsp_audio_format*)params_data; memset(ptr, 0, sizeof(union adsp_audio_format)); rpc.numdev = 1; rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; rpc.buf_max_size = bufsz; rpc.format = ADSP_AUDIO_FORMAT_MP3; rpc.pblock = params_phys; rpc.blocklen = sizeof(struct adsp_audio_standard_format); rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_WRITE; ptr->standard.channels = channels; ptr->standard.bits_per_sample = 16; ptr->standard.sampling_rate = rate; ptr->standard.is_signed = 1; ptr->standard.is_interleaved = 1; r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); // wait for async event if (!wait_event_timeout(ac->wait, (ac->open_done == 1), 3 * HZ)) { mutex_unlock(&open_mem_lock); pr_err("wait for open async event failed (MP3)!\n"); return -1; } mutex_unlock(&open_mem_lock); // return r; return ac->open_status; } static int audio_aac_open(struct audio_client *ac, uint32_t bufsz, void *data) { int r; struct aac_format *af = data; struct adsp_open_command rpc; uint32_t *aac_type; int idx = 0; // sizeof(uint32_t); struct adsp_audio_binary_format *fmt = (struct adsp_audio_binary_format *)params_data; union adsp_audio_format *ptr; mutex_lock(&open_mem_lock); memset(&rpc, 0, sizeof(rpc)); ptr = (union adsp_audio_format*)params_data; memset(ptr, 0, sizeof(union adsp_audio_format)); rpc.numdev = 1; rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; rpc.format = ADSP_AUDIO_FORMAT_MPEG4_AAC; rpc.pblock = params_phys; aac_type = (uint32_t *)(fmt->data); switch (af->block_formats) { case 0xffff: if (ac->flags & AUDIO_FLAG_WRITE) *aac_type = ADSP_AUDIO_AAC_ADTS; else *aac_type = ADSP_AUDIO_AAC_MPEG4_ADTS; break; case 0: if (ac->flags & AUDIO_FLAG_WRITE) *aac_type = ADSP_AUDIO_AAC_ADIF; else *aac_type = ADSP_AUDIO_AAC_RAW; break; case 1: *aac_type = ADSP_AUDIO_AAC_RAW; break; case 2: *aac_type = ADSP_AUDIO_AAC_LOAS; break; case 3: *aac_type = ADSP_AUDIO_AAC_FRAMED_RAW; break; case 4: *aac_type = ADSP_AUDIO_AAC_RAW; break; default: pr_err("unsupported AAC type %d\n", af->block_formats); mutex_unlock(&open_mem_lock); return -EINVAL; } AUDIO_INFO("aac_open: type %x, obj %d, idx %d\n", *aac_type, af->audio_object_type, idx); fmt->data[idx++] = (u8)(((af->audio_object_type & 0x1F) << 3) | ((af->sample_rate >> 1) & 0x7)); fmt->data[idx] = (u8)(((af->sample_rate & 0x1) << 7) | ((af->channel_config & 0x7) << 3)); switch (af->audio_object_type) { case AAC_OBJECT_ER_LC: case AAC_OBJECT_ER_LTP: case AAC_OBJECT_ER_LD: /* extension flag */ fmt->data[idx++] |= 0x1; fmt->data[idx] = (u8)( ((af->aac_section_data_resilience_flag & 0x1) << 7) | ((af->aac_scalefactor_data_resilience_flag & 0x1) << 6) | ((af->aac_spectral_data_resilience_flag & 0x1) << 5) | ((af->ep_config & 0x3) << 2)); break; case AAC_OBJECT_ER_SCALABLE: fmt->data[idx++] |= 0x1; /* extension flag */ fmt->data[idx++] = (u8)( ((af->aac_section_data_resilience_flag & 0x1) << 4) | ((af->aac_scalefactor_data_resilience_flag & 0x1) << 3) | ((af->aac_spectral_data_resilience_flag & 0x1) << 2) | ((af->ep_config >> 1) & 0x1)); fmt->data[idx] = (u8)((af->ep_config & 0x1) << 7); break; case AAC_OBJECT_BSAC: fmt->data[++idx] = (u8)((af->ep_config & 0x3) << 6); break; default: pr_err("dbg unknown object type \n"); break; } // fmt->num_bytes = idx + 1; rpc.blocklen = idx + 1; TRACE("aac_open: format %x%x %x%x%x%x %x%x, \n", fmt->data[0], fmt->data[1], fmt->data[2], fmt->data[3], fmt->data[4], fmt->data[5], fmt->data[6], fmt->data[7]); rpc.config.aac.bit_rate = af->bit_rate; if (ac->flags & AUDIO_FLAG_WRITE) { rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_WRITE; rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_PLAYBACK; } else { rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_READ; rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; } if ((af->sbr_on_flag == 0) && (af->sbr_ps_on_flag == 0)) { rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_AAC_LC_ONLY_MODE; } else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 0)) { rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_AAC_PLUS_MODE; } else if ((af->sbr_on_flag == 1) && (af->sbr_ps_on_flag == 1)) { rpc.config.aac.encoder_mode = ADSP_AUDIO_ENC_ENHANCED_AAC_PLUS_MODE; } else { pr_err("unsupported SBR flag\n"); mutex_unlock(&open_mem_lock); return -EINVAL; } rpc.buf_max_size = bufsz; /* XXX ??? */ TRACE("aac_open: opcode %x, stream_context 0x%x, " "mode %d, bytes %d, bbuffer size %d\n", rpc.opcode, rpc.stream_context, rpc.config.aac.encoder_mode, rpc.blocklen, bufsz); r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); mutex_unlock(&open_mem_lock); return r; } static int audio_qcelp_open(struct audio_client *ac, uint32_t bufsz, void *data) { int r; struct msm_audio_qcelp_config *qf = data; struct adsp_open_command rpc; struct adsp_audio_standard_format *fmt; union adsp_audio_format *ptr; mutex_lock(&open_mem_lock); memset(&rpc, 0, sizeof(rpc)); fmt = (struct adsp_audio_standard_format *)params_data; memset(ptr, 0, sizeof(struct adsp_audio_standard_format)); rpc.numdev = 1; rpc.dev[0] = ADSP_AUDIO_DEVICE_ID_DEFAULT; rpc.stream_context = ADSP_AUDIO_DEVICE_CONTEXT_RECORD; rpc.format = ADSP_AUDIO_FORMAT_V13K_FS; rpc.pblock = params_phys; rpc.blocklen = sizeof(struct adsp_audio_standard_format); rpc.opcode = ADSP_AUDIO_OPCODE_OPEN_READ; fmt->sampling_rate = 8000; fmt->channels = 1; fmt->bits_per_sample = 16; fmt->is_signed = 1; fmt->is_interleaved = 0; rpc.config.qcelp13k.min_rate = (uint16_t) qf->min_bit_rate; rpc.config.qcelp13k.max_rate = (uint16_t) qf->max_bit_rate; rpc.buf_max_size = bufsz; /* XXX ??? */ r = dal_call_f5(ac->client, AUDIO_OP_OPEN, &rpc, sizeof(rpc)); mutex_unlock(&open_mem_lock); return r; } static int audio_close(struct audio_client *ac) { AUDIO_INFO("%p: close\n", ac); audio_command(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_STOP); // audio_command(ac, ADSP_AUDIO_IOCTL_CMD_CLOSE); dal_call_f0(ac->client, AUDIO_OP_CLOSE, ac->session); return 0; } static int audio_set_table(struct audio_client *ac,int32_t device_id, int size) { struct adsp_set_dev_cfg_table_command rpc; AUDIO_INFO("%s: %x %d\n", __func__, device_id, size); // print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, audio_data, size); memset(&rpc, 0, sizeof(rpc)); if (q6_device_to_dir(device_id) == Q6_TX) rpc.hdr.data = tx_clk_freq; rpc.device_id = device_id; rpc.phys_addr = audio_phys; rpc.phys_size = size; rpc.phys_used = size; TRACE("control: set table %x\n", device_id); return audio_ioctl(ac, ADSP_AUDIO_IOCTL_SET_DEVICE_CONFIG_TABLE, &rpc, sizeof(rpc)); } int q6audio_read(struct audio_client *ac, struct audio_buffer *ab) { struct adsp_buffer_command rpc; int r; AUDIO_INFO("%s: %X %X %X\n", __func__, ab->phys , ab->size, ab->used); memset(&rpc, 0, sizeof(rpc)); rpc.hdr.context = ac->session; rpc.hdr.data = (uint32_t)ab->data; rpc.buffer.addr = ab->phys; rpc.buffer.max_size = ab->size; rpc.buffer.actual_size = ab->used; rpc.buffer.flags = ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR | ADSP_AUDIO_BUFFER_FLAG_START_SET; TRACE("%p: read\n", ac); // r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), &res, sizeof(res)); r = dal_call_f5(ac->client, AUDIO_OP_READ, &rpc, sizeof(rpc)); return 0; } int q6audio_write(struct audio_client *ac, struct audio_buffer *ab) { struct adsp_buffer_command rpc; int r; AUDIO_INFO("%s: %X %X %X\n", __func__, ab->phys , ab->size, ab->used); memset(&rpc, 0, sizeof(rpc)); rpc.hdr.context = ac->session; rpc.hdr.data = (uint32_t)ab->data; rpc.buffer.addr = ab->phys; rpc.buffer.max_size = ab->size; rpc.buffer.actual_size = ab->used; rpc.buffer.start = (~0x7fffffffffffffffLL); rpc.buffer.flags = ADSP_AUDIO_BUFFER_FLAG_PHYS_ADDR | ADSP_AUDIO_BUFFER_FLAG_START_SET; TRACE("%p: write\n", ac); // r = dal_call(ac->client, AUDIO_OP_DATA, 5, &rpc, sizeof(rpc), &res, sizeof(res)); r = dal_call_f5(ac->client, AUDIO_OP_WRITE, &rpc, sizeof(rpc)); return 0; } static int audio_rx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume) { struct adsp_set_dev_volume_command rpc; AUDIO_INFO("%s: dev_id 0x%08x, volume = %d\n", __func__, dev_id , volume); memset(&rpc, 0, sizeof(rpc)); rpc.device_id = dev_id; rpc.path = ADSP_PATH_RX; rpc.volume = volume; return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL, &rpc, sizeof(rpc)); } static int audio_tx_volume(struct audio_client *ac, uint32_t dev_id, int32_t volume) { struct adsp_set_dev_volume_command rpc; AUDIO_INFO("%s: dev_id 0x%08x, volume = %d\n", __func__, dev_id , volume); memset(&rpc, 0, sizeof(rpc)); rpc.device_id = dev_id; rpc.path = ADSP_PATH_TX; rpc.volume = volume; return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_VOL, &rpc, sizeof(rpc)); } static int audio_rx_mute(struct audio_client *ac, uint32_t dev_id, int mute) { struct adsp_set_dev_mute_command rpc; AUDIO_INFO("%s: dev_id 0x%08x, mute %d\n", __func__, dev_id , mute); memset(&rpc, 0, sizeof(rpc)); rpc.device_id = dev_id; rpc.path = ADSP_PATH_RX; rpc.mute = !!mute; return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE, &rpc, sizeof(rpc)); } static int audio_tx_mute(struct audio_client *ac, uint32_t dev_id, int mute) { struct adsp_set_dev_mute_command rpc; AUDIO_INFO("%s: dev_id 0x%08x, mute %d\n", __func__, dev_id , mute); memset(&rpc, 0, sizeof(rpc)); rpc.device_id = dev_id; rpc.path = ADSP_PATH_TX; rpc.mute = !!mute; return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_DEVICE_MUTE, &rpc, sizeof(rpc)); } static int audio_stream_volume(struct audio_client *ac, int volume) { struct adsp_set_volume_command rpc; int rc; AUDIO_INFO("%s: volume %d\n", __func__, volume); // // CotullaHACK: this function called from libaudio-qsd8k / AudioHardware.cpp // status = ioctl(mFd, AUDIO_SET_VOLUME, &stream_volume); // stream_volume have fixed -300 value, replace it to zero // // printk("$$$ audio_stream_volume(%d)\n", volume); volume = 0; memset(&rpc, 0, sizeof(rpc)); rpc.volume = volume; rc = audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_STREAM_VOL, &rpc, sizeof(rpc)); return rc; } static int audio_stream_mute(struct audio_client *ac, int mute) { struct adsp_set_mute_command rpc; int rc; AUDIO_INFO("%s: mute %d\n", __func__, mute); memset(&rpc, 0, sizeof(rpc)); rpc.mute = mute; rc = audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_SET_STREAM_MUTE, &rpc, sizeof(rpc)); return rc; } static int audio_stream_eq(struct audio_client *ac, struct cad_audio_eq_cfg *eq_cfg) { struct adsp_audio_set_equalizer_command rpc; int rc; uint32_t i = 0; AUDIO_INFO("%s\n", __func__); memset(&rpc, 0, sizeof(rpc)); rpc.enable = eq_cfg->enable; rpc.num_bands = eq_cfg->num_bands; for (i = 0; i < rpc.num_bands; i++) { rpc.eq_bands[i].band_idx = eq_cfg->eq_bands[i].band_idx; rpc.eq_bands[i].filter_type = eq_cfg->eq_bands[i].filter_type; rpc.eq_bands[i].center_freq_hz = eq_cfg->eq_bands[i].center_freq_hz; rpc.eq_bands[i].filter_gain = eq_cfg->eq_bands[i].filter_gain; rpc.eq_bands[i].q_factor = eq_cfg->eq_bands[i].q_factor; #if 1 pr_info(">>>>>> band_idx = %d\n", rpc.eq_bands[i].band_idx); pr_info(">>>>>> filter_type = %d\n", rpc.eq_bands[i].filter_type); pr_info(">>>>>> center_freq_hz = %d\n", rpc.eq_bands[i].center_freq_hz); pr_info(">>>>>> filter_gain = %d\n", rpc.eq_bands[i].filter_gain); pr_info(">>>>>> q_factor = %d\n\n", rpc.eq_bands[i].q_factor); #endif } rc = audio_ioctl(ac, ADSP_AUDIO_IOCTL_SET_SESSION_EQ_CONFIG, &rpc, sizeof(rpc)); return rc; } static void callback(void *data, int len, void *cookie) { // struct adsp_event_hdr *e = data; struct adsp_audio_dal_event *e = data; struct audio_client *ac; struct adsp_audio_event* ae; // printk("dal event\n"); TRACE("audio callback: CB: %X LOC: %X SIZE=%X\n", e->cb_evt, e->loc_evt, e->size); if (e->cb_evt != CB_EVENT_COOKIE) { pr_err("audio callback: bad cb_event %X\n", e->cb_evt); return; } ae = &e->ae; if (ae->session >= SESSION_MAX) { pr_err("audio callback: bogus session %d\n", ae->session); return; } ac = session[ae->session]; if (!ac) { pr_err("audio callback: unknown session %d\n", ae->session); return; } if (ae->event_id == ADSP_AUDIO_EVT_STATUS_OPEN) { pr_info("open done!\n"); ac->open_status = ae->status; ac->open_done = 1; wake_up(&ac->wait); return; } if (ae->event_id == ADSP_AUDIO_IOCTL_CMD_STREAM_EOS) { TRACE("%p: CB stream eos\n", ac); if (ae->status) { pr_err("playback status %d\n", ae->status); } if (ac->cb_status == -EBUSY) { ac->cb_status = ae->status; wake_up(&ac->wait); } return; } if (ae->event_id == ADSP_AUDIO_EVT_STATUS_BUF_DONE) { TRACE("%p: CB done (%d)\n", ac, ae->status); // TRACE("%p: actual_size %d, buffer_size %d\n", // ac, abe->buffer.actual_size, ac->buf[ac->dsp_buf].size); if (ae->status) { pr_err("buffer status %d\n", ae->status); } ac->buf[ac->dsp_buf].used = 0; ac->dsp_buf ^= 1; wake_up(&ac->wait); return; } TRACE("%p: CB %08x status %d\n", ac, ae->event_id, ae->status); if (ae->status) { pr_warning("audio_cb: s=%d e=%08x status=%d\n", ae->session, ae->event_id, ae->status); } if (ac->cb_status == -EBUSY) { ac->cb_status = ae->status; wake_up(&ac->wait); } } struct rpc_config_info { u32 cb_evt; u32 loc_evt; u32 session_id; u32 processor_id; }; struct rpc_info { uint32_t size; uint32_t version; char name[32]; }; static int audio_init(struct audio_client *ac) { int r; struct rpc_config_info info; AUDIO_INFO("%s\n", __func__); info.session_id = ac->session; info.processor_id = 1; // AARM info.cb_evt = CB_EVENT_COOKIE; info.loc_evt = (u32)(((u32)ac->session + 1) << 16); r = dal_call_f5(ac->client, AUDIO_OP_INIT, &info, sizeof(info)); return r; } static int audio_info(struct audio_client *ac) { int r; struct rpc_info info; AUDIO_INFO("%s\n", __func__); r = dal_call_f9(ac->client, DAL_OP_INFO, &info, sizeof(info)); return r; } static struct audio_client *ac_control; static int zero_already_inited; struct audio_config_data { uint32_t device_id; uint32_t sample_rate; uint32_t offset; uint32_t length; }; struct audio_config_database { uint8_t magic[8]; uint32_t entry_count; uint32_t unused; struct audio_config_data entry[0]; }; void *acdb_data; const struct firmware *acdb_fw; extern struct miscdevice q6_control_device; static int acdb_init(char *filename) { const struct audio_config_database *db; const struct firmware *fw; int n; // return -ENODEV; pr_info("acdb: load '%s'\n", filename); if (request_firmware(&fw, filename, q6_control_device.this_device) < 0) { pr_err("acdb: load 'default.acdb' failed...\n"); return -ENODEV; } db = (void*) fw->data; if (fw->size < sizeof(struct audio_config_database)) { pr_err("acdb: undersized database\n"); goto fail; } if (strcmp(db->magic, "ACDB1.0")) { pr_err("acdb: invalid magic\n"); goto fail; } if (db->entry_count > 1024) { pr_err("acdb: too many entries\n"); goto fail; } if (fw->size < (sizeof(struct audio_config_database) + db->entry_count * sizeof(struct audio_config_data))) { pr_err("acdb: undersized TOC\n"); goto fail; } for (n = 0; n < db->entry_count; n++) { if (db->entry[n].length > 4096) { pr_err("acdb: entry %d too large (%d)\n", n, db->entry[n].length); goto fail; } if ((db->entry[n].offset + db->entry[n].length) > fw->size) { pr_err("acdb: entry %d outside of data\n", n); goto fail; } } if (acdb_data) release_firmware(acdb_fw); acdb_data = (void*) fw->data; acdb_fw = fw; return 0; fail: release_firmware(fw); return -ENODEV; } static int q6audio_init(void) { struct audio_client *ac = 0; int res; AUDIO_INFO("%s\n", __func__); mutex_lock(&audio_lock); if (ac_control) { res = 0; goto done; } pr_info("q6audio_init\n"); icodec_rx_clk = clk_get(0, "icodec_rx_clk"); icodec_tx_clk = clk_get(0, "icodec_tx_clk"); ecodec_clk = clk_get(0, "ecodec_clk"); sdac_clk = clk_get(0, "sdac_clk"); audio_data = dma_alloc_coherent(NULL, 4096, &audio_phys, GFP_KERNEL); params_data = dma_alloc_coherent(NULL, 4096, ¶ms_phys, GFP_KERNEL); // printk("allocated: %p %x\n", params_data, params_phys); // pr_info("audio: init: INIT\n"); if (!zero_already_inited) { zero_already_inited = 1; pr_info("audio: init: first run, init dummy control\n"); audio_client_alloc(0); } ac = audio_client_alloc(0); if (!ac) { pr_err("audio_init: cannot allocate client\n"); res = -ENOMEM; goto done; } // pr_info("audio: init: OPEN control\n"); if (audio_open_control(ac)) { pr_err("audio_init: cannot open control channel\n"); res = -ENODEV; goto done; } // pr_info("audio: init: attach ACDB\n"); acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0); if (!acdb) { pr_err("audio_init: cannot attach to acdb channel\n"); res = -ENODEV; goto done; } // pr_info("audio: init: attach ADIE\n"); adie = dal_attach(ADIE_DAL_DEVICE, ADIE_DAL_PORT, 0, 0); if (!adie) { pr_err("audio_init: cannot attach to adie\n"); res = -ENODEV; goto done; } if (analog_ops->init) analog_ops->init(); if (!acdb_data && !acdb_use_rpc) { if (acdb_init(acdb_file)) { pr_info("use RPC to query ACDB.\n"); acdb_use_rpc = 1; } } res = 0; ac_control = ac; wake_lock_init(&idlelock, WAKE_LOCK_IDLE, "audio_pcm_idle"); wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "audio_pcm_suspend"); q6audio_init_rx_volumes(); // TEST HERE // pr_info("START TEST\n"); // audio_tx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_DEFAULT, 1); // pr_info("END TEST\n"); done: if ((res < 0) && ac) { audio_client_free(ac); pr_err(" init failed!\n"); } mutex_unlock(&audio_lock); return res; } static int map_cad_dev_to_virtual(int cd) { if (global_now_phone_call) { return cd; } switch (cd) { case 1: return 507; case 2: return 208; case 3: return 307; case 5: return 407; case 6: return 507; case 7: return 607; case 17: return 408; default: return cd; } return 0; } static int acdb_get_config_table(uint32_t device_id, uint32_t sample_rate) { AUDIO_INFO("%s\n", __func__); if (q6audio_init()) return 0; if (acdb_use_rpc) { struct acdb_cmd_device_table rpc; struct acdb_result res; int r; uint32_t new_device_id; memset(audio_data, 0, 4096); memset(&rpc, 0, sizeof(rpc)); new_device_id = map_cad_dev_to_virtual(device_id); printk(" ACDB MAP: %d -> %d\n", device_id, new_device_id); rpc.size = sizeof(rpc) - (2 * sizeof(uint32_t)); rpc.command_id = ACDB_GET_DEVICE_TABLE; rpc.device_id = new_device_id; //0x25F; //device_id; rpc.sample_rate_id = sample_rate; rpc.total_bytes = 4096; rpc.unmapped_buf = audio_phys; rpc.res_size = sizeof(res) - (2 * sizeof(uint32_t)); // printk("ACDB dal call\n"); r = dal_call(acdb, ACDB_OP_IOCTL, 8, &rpc, sizeof(rpc), &res, sizeof(res)); // r = dal_call_f8(acdb, ACDB_OP_IOCTL, &rpc, sizeof(rpc), &res, sizeof(res)); pr_info("acdb ret: %d %X %X %X %d\n", res. dal_status, res.size, res.unmapped_buf, res.used_bytes, res.result); if ((r == sizeof(res)) && (res.result == 0)) { pr_info("acdb: %d bytes for device %d, rate %d.\n", res.used_bytes, device_id, sample_rate); return res.used_bytes; } return 0; } else { struct audio_config_database *db; int n; printk("table\n"); db = acdb_data; for (n = 0; n < db->entry_count; n++) { if (db->entry[n].device_id != device_id) continue; if (db->entry[n].sample_rate != sample_rate) continue; break; } if (n == db->entry_count) { pr_err("acdb: no entry for device %d, rate %d.\n", device_id, sample_rate); return 0; } pr_info("acdb: %d bytes for device %d, rate %d.\n", db->entry[n].length, device_id, sample_rate); memcpy(audio_data, acdb_data + db->entry[n].offset, db->entry[n].length); return db->entry[n].length; } } //static uint32_t audio_rx_path_id = ADIE_PATH_HANDSET_RX; //static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR; static uint32_t audio_rx_path_id = ADIE_PATH_SPEAKER_RX; static uint32_t audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO; static uint32_t audio_rx_device_group = -1; static uint32_t audio_tx_path_id = ADIE_PATH_HANDSET_TX; static uint32_t audio_tx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC; static uint32_t audio_tx_device_group = -1; static int qdsp6_devchg_notify(struct audio_client *ac, uint32_t dev_type, uint32_t dev_id) { struct adsp_device_switch_command rpc; AUDIO_INFO("%s\n", __func__); if (dev_type != ADSP_AUDIO_RX_DEVICE && dev_type != ADSP_AUDIO_TX_DEVICE) return -EINVAL; memset(&rpc, 0, sizeof(rpc)); if (dev_type == ADSP_AUDIO_RX_DEVICE) { rpc.old_device = audio_rx_device_id; rpc.new_device = dev_id; } else { rpc.old_device = audio_tx_device_id; rpc.new_device = dev_id; } rpc.device_class = 0; rpc.device_type = dev_type; return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_PREPARE, &rpc, sizeof(rpc)); } static int qdsp6_standby(struct audio_client *ac) { AUDIO_INFO("%s\n", __func__); return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_STANDBY); } static int qdsp6_start(struct audio_client *ac) { AUDIO_INFO("%s\n", __func__); return audio_command(ac, ADSP_AUDIO_IOCTL_CMD_DEVICE_SWITCH_COMMIT); } static void audio_rx_analog_enable(int en) { AUDIO_INFO("%s\n", __func__); switch (audio_rx_device_id) { case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_MONO: case ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO: case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_SPKR: if (analog_ops->headset_enable) analog_ops->headset_enable(en); break; case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_MONO_HEADSET: case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO_W_STEREO_HEADSET: case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_MONO_HEADSET: case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO_W_STEREO_HEADSET: if (analog_ops->headset_enable) analog_ops->headset_enable(en); if (analog_ops->speaker_enable) analog_ops->speaker_enable(en); break; case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO: case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_STEREO: if (analog_ops->speaker_enable) analog_ops->speaker_enable(en); break; case ADSP_AUDIO_DEVICE_ID_BT_SCO_SPKR: if (analog_ops->bt_sco_enable) analog_ops->bt_sco_enable(en); break; case ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR: if (analog_ops->receiver_enable) analog_ops->receiver_enable(en); break; /* TODO: we need this? case ADSP_AUDIO_DEVICE_ID_I2S_SPKR: if (analog_ops->i2s_enable) analog_ops->i2s_enable(en); break; */ } } static void audio_tx_analog_enable(int en) { AUDIO_INFO("%s\n", __func__); switch (audio_tx_device_id) { case ADSP_AUDIO_DEVICE_ID_HANDSET_MIC: case ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MIC: if (analog_ops->int_mic_enable) analog_ops->int_mic_enable(en); break; case ADSP_AUDIO_DEVICE_ID_HEADSET_MIC: case ADSP_AUDIO_DEVICE_ID_TTY_HEADSET_MIC: if (analog_ops->ext_mic_enable) analog_ops->ext_mic_enable(en); break; case ADSP_AUDIO_DEVICE_ID_BT_SCO_MIC: if (analog_ops->bt_sco_enable) analog_ops->bt_sco_enable(en); break; } } static int audio_update_acdb(uint32_t adev, uint32_t acdb_id) { uint32_t sample_rate; int sz = -1; printk("%s (%d, %d)\n", __func__, adev, acdb_id); AUDIO_INFO("%s (%d, %d)\n", __func__, adev, acdb_id); // dex_comm(PCOM_UPDATE_ACDB, 0, 0); sample_rate = q6_device_to_rate(adev); if (q6_device_to_dir(adev) == Q6_RX) rx_acdb = acdb_id; else tx_acdb = acdb_id; if (acdb_id != 0) { sz = acdb_get_config_table(acdb_id, sample_rate); } TRACE("res1 = %d\n", sz); if (sz <= 0) { acdb_id = q6_device_to_cad_id(adev); printk(" new acdb = %d\n", acdb_id); sz = acdb_get_config_table(acdb_id, sample_rate); } TRACE("res2 = %d\n", sz); if (sz > 0) { TRACE("call audio_set_table\n"); audio_set_table(ac_control, adev, sz); } return 0; } static void _audio_rx_path_enable(int reconf, uint32_t acdb_id) { AUDIO_INFO("%s\n", __func__); adie_enable(); adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); audio_update_acdb(audio_rx_device_id, acdb_id); if (!reconf) qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); audio_update_acdb(audio_rx_device_id, acdb_id); audio_rx_analog_enable(1); } static void _audio_tx_path_enable(int reconf, uint32_t acdb_id) { AUDIO_INFO("%s\n", __func__); audio_tx_analog_enable(1); adie_enable(); adie_set_path(adie, audio_tx_path_id, ADIE_PATH_TX); if (tx_clk_freq > 8000) adie_set_path_freq_plan(adie, ADIE_PATH_TX, 48000); else adie_set_path_freq_plan(adie, ADIE_PATH_TX, 8000); adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_READY); adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_ANALOG_READY); audio_update_acdb(audio_tx_device_id, acdb_id); if (!reconf) qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, audio_tx_device_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); audio_tx_mute(ac_control, audio_tx_device_id, tx_mute_status); } static void _audio_rx_path_disable(void) { AUDIO_INFO("%s\n", __func__); audio_rx_analog_enable(0); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_ANALOG_OFF); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_OFF); adie_disable(); } static void _audio_tx_path_disable(void) { AUDIO_INFO("%s\n", __func__); audio_tx_analog_enable(0); adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_ANALOG_OFF); adie_proceed_to_stage(adie, ADIE_PATH_TX, ADIE_STAGE_DIGITAL_OFF); adie_disable(); } static int icodec_rx_clk_refcount; static int icodec_tx_clk_refcount; static int ecodec_clk_refcount; static int sdac_clk_refcount; static void _audio_rx_clk_enable(void) { uint32_t device_group = q6_device_to_codec(audio_rx_device_id); AUDIO_INFO("%s: device 0x%08x, group %d\n", __func__, audio_rx_device_id, device_group); switch(device_group) { case Q6_ICODEC_RX: icodec_rx_clk_refcount++; AUDIO_INFO("%s: icodec_rx_clk_refcount = %d\n", __func__, icodec_rx_clk_refcount); if (icodec_rx_clk_refcount == 1) { clk_set_rate(icodec_rx_clk, 12288000); clk_enable(icodec_rx_clk); AUDIO_INFO("%s: icodec_rx_clk enabled\n", __func__); } break; case Q6_ECODEC_RX: ecodec_clk_refcount++; AUDIO_INFO("%s: ecodec_clk_refcount = %d\n", __func__, ecodec_clk_refcount); if (ecodec_clk_refcount == 1) { clk_set_rate(ecodec_clk, 2048000); clk_enable(ecodec_clk); AUDIO_INFO("%s: ecodec_clk enabled\n", __func__); } break; case Q6_SDAC_RX: sdac_clk_refcount++; AUDIO_INFO("%s: sdac_clk_refcount = %d\n", __func__, sdac_clk_refcount); if (sdac_clk_refcount == 1) { clk_set_rate(sdac_clk, 12288000); clk_enable(sdac_clk); AUDIO_INFO("%s: sdac_clk enabled\n", __func__); } break; default: return; } audio_rx_device_group = device_group; } static void _audio_tx_clk_enable(void) { uint32_t device_group = q6_device_to_codec(audio_tx_device_id); AUDIO_INFO("%s: device 0x%08x, group %d\n", __func__, audio_tx_device_id, device_group); switch (device_group) { case Q6_ICODEC_TX: icodec_tx_clk_refcount++; AUDIO_INFO("%s: icodec_tx_clk_refcount = %d\n", __func__, icodec_tx_clk_refcount); if (icodec_tx_clk_refcount == 1) { clk_set_rate(icodec_tx_clk, tx_clk_freq * 256); clk_enable(icodec_tx_clk); AUDIO_INFO("%s: icodec_tx_clk enabled\n", __func__); } break; case Q6_ECODEC_TX: ecodec_clk_refcount++; AUDIO_INFO("%s: ecodec_clk_refcount = %d\n", __func__, ecodec_clk_refcount); if (ecodec_clk_refcount == 1) { clk_set_rate(ecodec_clk, 2048000); clk_enable(ecodec_clk); AUDIO_INFO("%s: ecodec_clk enabled\n", __func__); } break; case Q6_SDAC_TX: /* TODO: In QCT BSP, clk rate was set to 20480000 */ sdac_clk_refcount++; AUDIO_INFO("%s: sdac_clk_refcount = %d\n", __func__, sdac_clk_refcount); if (sdac_clk_refcount == 1) { clk_set_rate(sdac_clk, 12288000); clk_enable(sdac_clk); AUDIO_INFO("%s: sdac_clk enabled\n", __func__); } break; default: return; } audio_tx_device_group = device_group; } static void _audio_rx_clk_disable(void) { AUDIO_INFO("%s: group %d\n", __func__, audio_rx_device_group); switch (audio_rx_device_group) { case Q6_ICODEC_RX: icodec_rx_clk_refcount--; AUDIO_INFO("%s: icodec_rx_clk_refcount = %d\n", __func__, icodec_rx_clk_refcount); if (icodec_rx_clk_refcount == 0) { clk_disable(icodec_rx_clk); audio_rx_device_group = -1; AUDIO_INFO("%s: icodec_rx_clk disabled\n", __func__); } break; case Q6_ECODEC_RX: ecodec_clk_refcount--; AUDIO_INFO("%s: ecodec_clk_refcount = %d\n", __func__, ecodec_clk_refcount); if (ecodec_clk_refcount == 0) { clk_disable(ecodec_clk); audio_rx_device_group = -1; AUDIO_INFO("%s: ecodec_clk disabled\n", __func__); } break; case Q6_SDAC_RX: sdac_clk_refcount--; AUDIO_INFO("%s: sdac_clk_refcount = %d\n", __func__, sdac_clk_refcount); if (sdac_clk_refcount == 0) { clk_disable(sdac_clk); audio_rx_device_group = -1; AUDIO_INFO("%s: sdac_clk disabled\n", __func__); } break; default: pr_err("audiolib: invalid rx device group %d\n", audio_rx_device_group); break; } } static void _audio_tx_clk_disable(void) { AUDIO_INFO("%s: group %d\n", __func__, audio_tx_device_group); switch (audio_tx_device_group) { case Q6_ICODEC_TX: icodec_tx_clk_refcount--; AUDIO_INFO("%s: icodec_tx_clk_refcount = %d\n", __func__, icodec_tx_clk_refcount); if (icodec_tx_clk_refcount == 0) { clk_disable(icodec_tx_clk); audio_tx_device_group = -1; AUDIO_INFO("%s: icodec_tx_clk disabled\n", __func__); } break; case Q6_ECODEC_TX: ecodec_clk_refcount--; AUDIO_INFO("%s: ecodec_clk_refcount = %d\n", __func__, ecodec_clk_refcount); if (ecodec_clk_refcount == 0) { clk_disable(ecodec_clk); audio_tx_device_group = -1; AUDIO_INFO("%s: ecodec_clk disabled\n", __func__); } break; case Q6_SDAC_TX: sdac_clk_refcount--; AUDIO_INFO("%s: sdac_clk_refcount = %d\n", __func__, sdac_clk_refcount); if (sdac_clk_refcount == 0) { clk_disable(sdac_clk); audio_tx_device_group = -1; AUDIO_INFO("%s: sdac_clk disabled\n", __func__); } break; default: pr_err("audiolib: invalid tx device group %d\n", audio_tx_device_group); break; } } static void _audio_rx_clk_reinit(uint32_t rx_device) { uint32_t device_group = q6_device_to_codec(rx_device); AUDIO_INFO("%s\n", __func__); if (device_group != audio_rx_device_group) _audio_rx_clk_disable(); audio_rx_device_id = rx_device; audio_rx_path_id = q6_device_to_path(rx_device); if (device_group != audio_rx_device_group) _audio_rx_clk_enable(); } static void _audio_tx_clk_reinit(uint32_t tx_device) { uint32_t device_group = q6_device_to_codec(tx_device); AUDIO_INFO("%s\n", __func__); if (device_group != audio_tx_device_group) _audio_tx_clk_disable(); audio_tx_device_id = tx_device; audio_tx_path_id = q6_device_to_path(tx_device); if (device_group != audio_tx_device_group) _audio_tx_clk_enable(); } static DEFINE_MUTEX(audio_path_lock); static int audio_rx_path_refcount; static int audio_tx_path_refcount; static int audio_rx_path_enable(int en, uint32_t acdb_id) { AUDIO_INFO("%s\n", __func__); mutex_lock(&audio_path_lock); if (en) { audio_rx_path_refcount++; if (audio_rx_path_refcount == 1) { _audio_rx_clk_enable(); _audio_rx_path_enable(0, acdb_id); } } else { audio_rx_path_refcount--; if (audio_rx_path_refcount == 0) { _audio_rx_path_disable(); _audio_rx_clk_disable(); } } mutex_unlock(&audio_path_lock); return 0; } static int audio_tx_path_enable(int en, uint32_t acdb_id) { AUDIO_INFO("%s\n", __func__); mutex_lock(&audio_path_lock); if (en) { audio_tx_path_refcount++; if (audio_tx_path_refcount == 1) { _audio_tx_clk_enable(); _audio_tx_path_enable(0, acdb_id); } } else { audio_tx_path_refcount--; if (audio_tx_path_refcount == 0) { _audio_tx_path_disable(); _audio_tx_clk_disable(); } } mutex_unlock(&audio_path_lock); return 0; } int q6audio_reinit_acdb(char* filename) { int res; if (q6audio_init()) return 0; mutex_lock(&audio_path_lock); if (strlen(filename) < 0) { res = -EINVAL; goto done; } res = acdb_init(filename); if (!res) strcpy(acdb_file, filename); done: mutex_unlock(&audio_path_lock); return res; } int q6audio_update_acdb(uint32_t id_src, uint32_t id_dst) { int res; if (q6audio_init()) return 0; mutex_lock(&audio_path_lock); res = audio_update_acdb(id_dst, id_src); if (res) goto done; if (q6_device_to_dir(id_dst) == Q6_RX) qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, id_dst); else qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, id_dst); qdsp6_standby(ac_control); qdsp6_start(ac_control); done: mutex_unlock(&audio_path_lock); return res; } int q6audio_set_tx_mute(int mute) { uint32_t adev; int rc; AUDIO_INFO("%s\n", __func__); if (q6audio_init()) return 0; mutex_lock(&audio_path_lock); if (mute == tx_mute_status) { mutex_unlock(&audio_path_lock); return 0; } adev = audio_tx_device_id; rc = audio_tx_mute(ac_control, adev, mute); tx_mute_status = mute; mutex_unlock(&audio_path_lock); return 0; } int q6audio_set_stream_volume(struct audio_client *ac, int vol) { AUDIO_INFO("%s\n", __func__); if (vol > 1200 || vol < -4000) { pr_err("unsupported volume level %d\n", vol); return -EINVAL; } mutex_lock(&audio_path_lock); audio_stream_mute(ac, 0); audio_stream_volume(ac, vol); mutex_unlock(&audio_path_lock); return 0; } int q6audio_set_stream_eq(struct audio_client *ac, struct cad_audio_eq_cfg *eq_cfg) { AUDIO_INFO("%s\n", __func__); mutex_lock(&audio_path_lock); audio_stream_eq(ac, eq_cfg); mutex_unlock(&audio_path_lock); return 0; } int q6audio_set_rx_dev_volume(int level) { int vol; AUDIO_INFO("%s\n", __func__); mutex_lock(&audio_path_lock); vol = q6_device_volume(audio_rx_device_id, level); TRACE("$$ DEV=%08X: vol is %d\n", audio_rx_device_id, vol); audio_rx_volume(ac_control, audio_rx_device_id, vol); mutex_unlock(&audio_path_lock); return 0; } int q6audio_set_rx_volume(int level) { #if 0 uint32_t adev; int vol; AUDIO_INFO("%s\n", __func__); if (q6audio_init()) return 0; if (level < 0 || level > 100) return -EINVAL; mutex_lock(&audio_path_lock); adev = ADSP_AUDIO_DEVICE_ID_VOICE; vol = q6_device_volume(audio_rx_device_id, level); audio_rx_mute(ac_control, adev, 0); printk("@@@@ rx volume: adev=%d, rx_dev_id=%d, level=%d @@@@\n", adev, audio_rx_device_id, vol); audio_rx_volume(ac_control, adev, vol); rx_vol_level = level; mutex_unlock(&audio_path_lock); #else q6audio_set_rx_dev_volume(level); #endif return 0; } static int q6audio_init_rx_volumes() { int vol; struct q6_device_info *di = q6_audio_devices; AUDIO_INFO("%s\n", __func__); mutex_lock(&audio_path_lock); TRACE("$$$ q6audio_init_rx_volumes\n"); while (1) { if (di->id == 0) break; vol = q6_device_volume(di->id, 100); audio_rx_volume(ac_control, di->id, vol); TRACE("$$ DEV=%08X: vol is %d\n", di->id, vol); di++; } mutex_unlock(&audio_path_lock); return 0; } int q6audio_set_rx_mute(int mute) { uint32_t adev; AUDIO_INFO("%s\n", __func__); if (q6audio_init()) return 0; if (mute < 0 || mute > 1) return -EINVAL; mutex_lock(&audio_path_lock); AUDIO_INFO("%s: set mute status %d\n", __func__, mute); adev = ADSP_AUDIO_DEVICE_ID_VOICE; audio_rx_mute(ac_control, adev, mute); mutex_unlock(&audio_path_lock); return 0; } static void do_rx_routing(uint32_t device_id, uint32_t acdb_id) { AUDIO_INFO("%s\n", __func__); if (device_id == audio_rx_device_id) { if (acdb_id != rx_acdb) { audio_update_acdb(device_id, acdb_id); qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); } return; } if (audio_rx_path_refcount > 0) { qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, device_id); _audio_rx_path_disable(); _audio_rx_clk_reinit(device_id); _audio_rx_path_enable(1, acdb_id); } else { audio_rx_device_id = device_id; audio_rx_path_id = q6_device_to_path(device_id); } } static void do_tx_routing(uint32_t device_id, uint32_t acdb_id) { AUDIO_INFO("%s\n", __func__); if (device_id == audio_tx_device_id) { if (acdb_id != tx_acdb) { audio_update_acdb(device_id, acdb_id); qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); } return; } if (audio_tx_path_refcount > 0) { qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, device_id); _audio_tx_path_disable(); _audio_tx_clk_reinit(device_id); _audio_tx_path_enable(1, acdb_id); } else { audio_tx_device_id = device_id; audio_tx_path_id = q6_device_to_path(device_id); } } int q6audio_do_routing(uint32_t device_id, uint32_t acdb_id) { AUDIO_INFO("%s\n", __func__); if (q6audio_init()) return 0; mutex_lock(&audio_path_lock); switch(q6_device_to_dir(device_id)) { case Q6_RX: do_rx_routing(device_id, acdb_id); break; case Q6_TX: do_tx_routing(device_id, acdb_id); break; } mutex_unlock(&audio_path_lock); return 0; } int q6audio_set_route(const char *name) { uint32_t route; AUDIO_INFO("%s\n", __func__); if (!strcmp(name, "speaker")) { route = ADIE_PATH_SPEAKER_STEREO_RX; } else if (!strcmp(name, "headphones")) { route = ADIE_PATH_HEADSET_STEREO_RX; } else if (!strcmp(name, "handset")) { route = ADIE_PATH_HANDSET_RX; } else { return -EINVAL; } mutex_lock(&audio_path_lock); if (route == audio_rx_path_id) goto done; audio_rx_path_id = route; if (audio_rx_path_refcount > 0) { _audio_rx_path_disable(); _audio_rx_path_enable(1, 0); } if (audio_tx_path_refcount > 0) { _audio_tx_path_disable(); _audio_tx_path_enable(1, 0); } done: mutex_unlock(&audio_path_lock); return 0; } struct audio_client *q6audio_open_pcm(uint32_t bufsz, uint32_t rate, uint32_t channels, uint32_t flags, uint32_t acdb_id) { #if 1 int rc, retry = 5; struct audio_client *ac; AUDIO_INFO("%s\n", __func__); if (q6audio_init()) return 0; // printk("afer init();\n"); ac = audio_client_alloc(bufsz); if (!ac) { pr_err("audio_client_alloc failed\n"); return 0; } // printk("after alloc);\n"); ac->flags = flags; mutex_lock(&audio_path_lock); if (ac->flags & AUDIO_FLAG_WRITE) { audio_rx_path_refcount++; if (audio_rx_path_refcount == 1) { _audio_rx_clk_enable(); // audio_update_acdb(audio_rx_device_id, acdb_id); qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); audio_update_acdb(audio_rx_device_id, acdb_id); } } else { /* TODO: consider concurrency with voice call */ tx_clk_freq = rate; audio_tx_path_refcount++; if (audio_tx_path_refcount == 1) { _audio_tx_clk_enable(); _audio_tx_path_enable(0, acdb_id); } } // printk("about to open\n"); for (retry = 5;;retry--) { if (ac->flags & AUDIO_FLAG_WRITE) rc = audio_out_open(ac, bufsz, rate, channels); else rc = audio_in_open(ac, bufsz, rate, channels); if (rc == 0) break; if (retry == 0) { // BUG(); break; } pr_err("q6audio: open pcm error %d, retrying\n", rc); msleep(1); } if (retry == 0) { if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(0, 0); else audio_tx_path_enable(0, 0); audio_client_free(ac); pr_err("q6audio: open pcm error\n"); return NULL; } // printk("after open\n"); if (ac->flags & AUDIO_FLAG_WRITE) { if (audio_rx_path_refcount == 1) { adie_enable(); adie_set_path(adie, audio_rx_path_id, ADIE_PATH_RX); adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); audio_rx_analog_enable(1); } } // printk("about to start session\n"); mutex_unlock(&audio_path_lock); for (retry = 5;;retry--) { rc = audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); if (rc == 0) break; if (retry == 0) { // BUG(); break; } pr_err("q6audio: stream start error %d, retrying\n", rc); } if (retry == 0) { audio_close(ac); if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(0, 0); else audio_tx_path_enable(0, 0); audio_client_free(ac); pr_err("q6audio: open pcm error2\n"); return NULL; } if (!(ac->flags & AUDIO_FLAG_WRITE)) { ac->buf[0].used = 1; ac->buf[1].used = 1; q6audio_read(ac, &ac->buf[0]); q6audio_read(ac, &ac->buf[1]); } audio_prevent_sleep(); return ac; #else return audio_test(); #endif } int q6audio_close(struct audio_client *ac) { AUDIO_INFO("%s\n", __func__); audio_close(ac); if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(0, 0); else audio_tx_path_enable(0, 0); audio_client_free(ac); audio_allow_sleep(); return 0; } struct audio_client *q6voice_open(uint32_t flags, uint32_t acdb_id) { struct audio_client *ac; AUDIO_INFO("%s\n", __func__); if (q6audio_init()) return 0; ac = audio_client_alloc(0); if (!ac) return 0; ac->flags = flags; if (ac->flags & AUDIO_FLAG_WRITE) { audio_rx_path_enable(1, acdb_id); audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 0); // fix for non update acdb at start of call, if other stream is active audio_update_acdb(audio_rx_device_id, rx_acdb); qdsp6_devchg_notify(ac_control, ADSP_AUDIO_RX_DEVICE, audio_rx_device_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); } else { tx_clk_freq = 8000; audio_tx_path_enable(1, acdb_id); // fix for non update acdb at start of call, if other stream is active audio_update_acdb(audio_tx_device_id, tx_acdb); qdsp6_devchg_notify(ac_control, ADSP_AUDIO_TX_DEVICE, audio_tx_device_id); qdsp6_standby(ac_control); qdsp6_start(ac_control); } return ac; } int q6voice_close(struct audio_client *ac) { AUDIO_INFO("%s\n", __func__); if (ac->flags & AUDIO_FLAG_WRITE) { audio_rx_mute(ac_control, ADSP_AUDIO_DEVICE_ID_VOICE, 1); audio_rx_path_enable(0, 0); } else audio_tx_path_enable(0, 0); audio_client_free(ac); return 0; } struct audio_client *q6audio_open_mp3(uint32_t bufsz, uint32_t rate, uint32_t channels, uint32_t acdb_id) { struct audio_client *ac; TRACE("q6audio_open_mp3()\n"); if (q6audio_init()) return 0; ac = audio_client_alloc(bufsz); if (!ac) return 0; ac->flags = AUDIO_FLAG_WRITE; audio_rx_path_enable(1, acdb_id); audio_mp3_open(ac, bufsz, rate, channels); audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); return ac; } int q6audio_mp3_close(struct audio_client *ac) { AUDIO_INFO("%s\n", __func__); audio_close(ac); audio_rx_path_enable(0, 0); audio_client_free(ac); return 0; } int q6audio_async(struct audio_client *ac) { struct adsp_command_hdr rpc; memset(&rpc, 0, sizeof(rpc)); // rpc.response_type = ADSP_AUDIO_RESPONSE_ASYNC; return audio_ioctl(ac, ADSP_AUDIO_IOCTL_CMD_STREAM_EOS, &rpc, sizeof(rpc)); } struct audio_client *q6audio_open_aac(uint32_t bufsz, uint32_t rate, uint32_t flags, void *data, uint32_t acdb_id) { struct audio_client *ac; TRACE("q6audio_open_aac flags=%d rate=%d\n", flags, rate); if (q6audio_init()) return 0; ac = audio_client_alloc(bufsz); if (!ac) return 0; ac->flags = flags; if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(1, acdb_id); else { /* TODO: consider concourrency with voice call */ tx_clk_freq = rate; audio_tx_path_enable(1, acdb_id); } audio_aac_open(ac, bufsz, data); audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); if (!(ac->flags & AUDIO_FLAG_WRITE)) { ac->buf[0].used = 1; ac->buf[1].used = 1; q6audio_read(ac, &ac->buf[0]); q6audio_read(ac, &ac->buf[1]); } audio_prevent_sleep(); return ac; } int q6audio_aac_close(struct audio_client *ac) { audio_close(ac); if (ac->flags & AUDIO_FLAG_WRITE) audio_rx_path_enable(0, 0); else audio_tx_path_enable(0, 0); audio_client_free(ac); audio_allow_sleep(); return 0; } struct audio_client *q6fm_open(void) { struct audio_client *ac; printk("q6fm_open()\n"); if (q6audio_init()) return 0; if (audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_HEADSET_SPKR_STEREO && audio_rx_device_id != ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO) return 0; ac = audio_client_alloc(0); if (!ac) return 0; ac->flags = AUDIO_FLAG_WRITE; audio_rx_path_enable(1, 0); enable_aux_loopback(1); return ac; } int q6fm_close(struct audio_client *ac) { printk("q6fm_close\n"); audio_rx_path_enable(0, 0); enable_aux_loopback(0); audio_client_free(ac); return 0; } struct audio_client *q6audio_open_qcelp(uint32_t bufsz, uint32_t rate, void *data, uint32_t acdb_id) { struct audio_client *ac; AUDIO_INFO("%s\n", __func__); if (q6audio_init()) return 0; ac = audio_client_alloc(bufsz); if (!ac) { return 0; } ac->flags = AUDIO_FLAG_READ; tx_clk_freq = rate; audio_tx_path_enable(1, acdb_id); audio_qcelp_open(ac, bufsz, data); audio_command(ac, ADSP_AUDIO_IOCTL_CMD_SESSION_START); ac->buf[0].used = 1; ac->buf[1].used = 1; q6audio_read(ac, &ac->buf[0]); q6audio_read(ac, &ac->buf[1]); audio_prevent_sleep(); return ac; } int q6audio_qcelp_close(struct audio_client *ac) { audio_close(ac); audio_tx_path_enable(0, 0); audio_client_free(ac); audio_allow_sleep(); return 0; } /////////////////////////////////////////////////////////////////////////// int acdb_get_table(int dev_id, int sample_rate) { struct acdb_cmd_device_table rpc; struct acdb_result res; int r; memset(audio_data, 0, 4096); memset(&rpc, 0, sizeof(rpc)); rpc.size = sizeof(rpc) - (2 * sizeof(uint32_t)); rpc.command_id = ACDB_GET_DEVICE_TABLE; rpc.device_id = dev_id; rpc.sample_rate_id = sample_rate; rpc.total_bytes = 4096; rpc.unmapped_buf = audio_phys; rpc.res_size = sizeof(res) - (2 * sizeof(uint32_t)); r = dal_call(acdb, ACDB_OP_IOCTL, 8, &rpc, sizeof(rpc), &res, sizeof(res)); if ((r == sizeof(res)) && (res.dal_status == 0)) { pr_info("acdb: %d bytes for device %d, rate %d.\n", res.used_bytes, dev_id, sample_rate); return res.used_bytes; } return 0; } static struct audio_client * audio_test(void) { struct audio_client *ac = 0; struct audio_client *ac2 = 0; int size; struct rpc_info info; pr_info("audio: init: codecs\n"); icodec_rx_clk = clk_get(0, "icodec_rx_clk"); icodec_tx_clk = clk_get(0, "icodec_tx_clk"); ecodec_clk = clk_get(0, "ecodec_clk"); sdac_clk = clk_get(0, "sdac_clk"); audio_data = dma_alloc_coherent(NULL, 4096, &audio_phys, GFP_KERNEL); params_data = dma_alloc_coherent(NULL, 4096, ¶ms_phys, GFP_KERNEL); TRACE("allocated: %p %08X\n", params_data, params_phys); clk_set_rate(icodec_rx_clk, 12288000); clk_enable(icodec_rx_clk); clk_set_rate(ecodec_clk, 2048000); clk_enable(ecodec_clk); clk_set_rate(sdac_clk, 12288000); clk_enable(sdac_clk); // 1. attach ADIE adie = dal_attach_ex(ADIE_DAL_DEVICE, "NULL", ADIE_DAL_PORT , 0, 0); if (!adie) { pr_err("audio_init: cannot attach to adie\n"); return 0; } if (analog_ops->init) analog_ops->init(); audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO; audio_rx_analog_enable(1); dal_call_f9(adie, DAL_OP_INFO, &info, sizeof(info)); acdb = dal_attach(ACDB_DAL_DEVICE, ACDB_DAL_PORT, 0, 0); if (!acdb) { pr_err("audio_init: cannot attach to acdb channel\n"); return 0; } dal_call_f9(acdb, DAL_OP_INFO, &info, sizeof(info)); audio_client_alloc(0); // audio_client_alloc(0); ac = audio_client_alloc(0); audio_open_control(ac); //mdelay(1000); audio_rx_mute(ac, ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR, 0); audio_rx_volume(ac, ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR, 0); acdb_get_table(7, 48000); audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_SPKR; qdsp6_devchg_notify(ac, ADSP_AUDIO_RX_DEVICE, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO); qdsp6_standby(ac); qdsp6_start(ac); size = acdb_get_table(0x25F, 48000); audio_set_table(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, size); audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO; qdsp6_devchg_notify(ac, ADSP_AUDIO_RX_DEVICE, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO); qdsp6_standby(ac); qdsp6_start(ac); audio_rx_mute(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, 0); audio_rx_volume(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, 500); size = acdb_get_table(507, 8000); audio_set_table(ac, ADSP_AUDIO_DEVICE_ID_HANDSET_MIC, size); audio_rx_device_id = ADSP_AUDIO_DEVICE_ID_HANDSET_MIC; qdsp6_devchg_notify(ac, ADSP_AUDIO_TX_DEVICE, ADSP_AUDIO_DEVICE_ID_HANDSET_MIC); qdsp6_standby(ac); qdsp6_start(ac); size = acdb_get_table(0x25F, 48000); audio_set_table(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, size); ac2 = audio_client_alloc(4096); audio_out_open(ac2, 4096, 44100, 2); adie_open(adie); adie_set_path(adie, ADIE_PATH_SPEAKER_RX, ADIE_PATH_RX); adie_set_path_freq_plan(adie, ADIE_PATH_RX, 48000); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_READY); adie_proceed_to_stage(adie, ADIE_PATH_RX, ADIE_STAGE_DIGITAL_ANALOG_READY); audio_command(ac2, ADSP_AUDIO_IOCTL_CMD_SESSION_START); audio_rx_mute(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, 0); audio_rx_volume(ac, ADSP_AUDIO_DEVICE_ID_SPKR_PHONE_MONO, 500); return ac2; } // END OF FILE