| /* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| #include <linux/module.h> |
| #include <linux/fs.h> |
| #include <linux/miscdevice.h> |
| #include <linux/msm_audio.h> |
| #include <asm/uaccess.h> |
| #include <asm/atomic.h> |
| #include <mach/qdsp5v2/audio_dev_ctl.h> |
| #include <linux/wait.h> |
| #include <linux/sched.h> |
| #include <mach/debug_mm.h> |
| #include <mach/qdsp5v2/qdsp5audppmsg.h> |
| #include <mach/qdsp5v2/audpp.h> |
| #include <linux/slab.h> |
| #include <linux/debugfs.h> |
| |
| #ifndef MAX |
| #define MAX(x, y) (((x) > (y)) ? (x) : (y)) |
| #endif |
| |
| |
| static DEFINE_MUTEX(session_lock); |
| |
| struct audio_dev_ctrl_state { |
| struct msm_snddev_info *devs[AUDIO_DEV_CTL_MAX_DEV]; |
| u32 num_dev; |
| atomic_t opened; |
| struct msm_snddev_info *voice_rx_dev; |
| struct msm_snddev_info *voice_tx_dev; |
| wait_queue_head_t wait; |
| }; |
| |
| static struct audio_dev_ctrl_state audio_dev_ctrl; |
| struct event_listner event; |
| #define MAX_DEC_SESSIONS 7 |
| #define MAX_ENC_SESSIONS 3 |
| |
| struct session_freq { |
| int freq; |
| int evt; |
| }; |
| |
| |
| struct audio_routing_info { |
| unsigned short mixer_mask[MAX_DEC_SESSIONS]; |
| unsigned short audrec_mixer_mask[MAX_ENC_SESSIONS]; |
| struct session_freq dec_freq[MAX_DEC_SESSIONS]; |
| struct session_freq enc_freq[MAX_ENC_SESSIONS]; |
| int dual_mic_setting[MAX_ENC_SESSIONS]; |
| int voice_tx_dev_id; |
| int voice_rx_dev_id; |
| int voice_tx_sample_rate; |
| int voice_rx_sample_rate; |
| signed int voice_tx_vol; |
| signed int voice_rx_vol; |
| int tx_mute; |
| int rx_mute; |
| int voice_state; |
| }; |
| |
| static struct audio_routing_info routing_info; |
| |
| #ifdef CONFIG_DEBUG_FS |
| |
| static struct dentry *dentry; |
| static int rtc_getdevice_dbg_open(struct inode *inode, struct file *file) |
| { |
| file->private_data = inode->i_private; |
| MM_INFO("debug intf %s\n", (char *) file->private_data); |
| return 0; |
| } |
| bool is_dev_opened(u32 adb_id) |
| { |
| |
| int dev_id = 0; |
| struct msm_snddev_info *dev_info = NULL; |
| |
| for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) { |
| dev_info = audio_dev_ctrl_find_dev(dev_id); |
| if (IS_ERR(dev_info)) { |
| MM_ERR("pass invalid dev_id %d\n", dev_id); |
| return false; |
| } |
| if (dev_info->opened && (dev_info->acdb_id == adb_id)) |
| return true; |
| } |
| |
| return false; |
| } |
| static ssize_t rtc_getdevice_dbg_read(struct file *file, char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| static char buffer[1024]; |
| static char swap_buf[1024]; |
| const int debug_bufmax = sizeof(buffer); |
| int n = 0; |
| int swap_count = 0; |
| int rc = 0; |
| int dev_count = 0; |
| int dev_id = 0; |
| struct msm_snddev_info *dev_info = NULL; |
| |
| |
| if (audio_dev_ctrl.num_dev <= 0) { |
| MM_ERR("Invalid no Device present\n"); |
| dev_count = 0; |
| n = scnprintf(buffer, debug_bufmax, "DEV_NO:0x%x\n", dev_count); |
| } else { |
| for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) { |
| dev_info = audio_dev_ctrl_find_dev(dev_id); |
| if (IS_ERR(dev_info)) { |
| MM_ERR("pass invalid dev_id %d\n", dev_id); |
| rc = PTR_ERR(dev_info); |
| return rc; |
| } |
| if (dev_info->opened) { |
| n += scnprintf(swap_buf + n, debug_bufmax - n, |
| "ACDB_ID:0x%x;CAPB:0x%x\n", |
| dev_info->acdb_id, |
| dev_info->capability); |
| dev_count++; |
| MM_DBG("RTC Get Device %x COPP %x Session Mask \ |
| %x Capb %x Dev Count %x\n", |
| dev_id , dev_info->copp_id, dev_info->sessions, |
| dev_info->capability, dev_count); |
| |
| } |
| } |
| |
| swap_count = scnprintf(buffer, debug_bufmax, \ |
| "DEV_NO:0x%x\n", dev_count); |
| |
| memcpy(buffer+swap_count, swap_buf, n*sizeof(char)); |
| n = n+swap_count; |
| |
| buffer[n] = 0; |
| } |
| return simple_read_from_buffer(buf, count, ppos, buffer, n); |
| } |
| |
| static const struct file_operations rtc_acdb_debug_fops = { |
| .open = rtc_getdevice_dbg_open, |
| .read = rtc_getdevice_dbg_read |
| }; |
| #endif |
| int msm_reset_all_device(void) |
| { |
| int rc = 0; |
| int dev_id = 0; |
| struct msm_snddev_info *dev_info = NULL; |
| |
| for (dev_id = 0; dev_id < audio_dev_ctrl.num_dev; dev_id++) { |
| dev_info = audio_dev_ctrl_find_dev(dev_id); |
| if (IS_ERR(dev_info)) { |
| MM_ERR("pass invalid dev_id %d\n", dev_id); |
| rc = PTR_ERR(dev_info); |
| return rc; |
| } |
| if (!dev_info->opened) |
| continue; |
| MM_DBG("Resetting device %d active on COPP %d" |
| "with 0x%08x as routing\n", |
| dev_id, dev_info->copp_id, dev_info->sessions); |
| broadcast_event(AUDDEV_EVT_REL_PENDING, |
| dev_id, |
| SESSION_IGNORE); |
| rc = dev_info->dev_ops.close(dev_info); |
| if (rc < 0) { |
| MM_ERR("Snd device %d failed close!\n", dev_id); |
| return rc; |
| } else { |
| dev_info->opened = 0; |
| broadcast_event(AUDDEV_EVT_DEV_RLS, |
| dev_id, |
| SESSION_IGNORE); |
| } |
| dev_info->sessions = 0; |
| } |
| return 0; |
| } |
| EXPORT_SYMBOL(msm_reset_all_device); |
| |
| int msm_set_dual_mic_config(int enc_session_id, int config) |
| { |
| int i; |
| if (enc_session_id >= MAX_ENC_SESSIONS) |
| return -EINVAL; |
| /*config is set(1) dual mic recording is selected */ |
| /*config is reset (0) dual mic recording is not selected*/ |
| routing_info.dual_mic_setting[enc_session_id] = config; |
| for (i = 0; i < MAX_ENC_SESSIONS; i++) |
| MM_DBG("dual_mic_setting[%d] = %d\n", |
| i, routing_info.dual_mic_setting[i]); |
| return 0; |
| } |
| EXPORT_SYMBOL(msm_set_dual_mic_config); |
| |
| int msm_get_dual_mic_config(int enc_session_id) |
| { |
| if (enc_session_id >= MAX_ENC_SESSIONS) |
| return -EINVAL; |
| return routing_info.dual_mic_setting[enc_session_id]; |
| } |
| EXPORT_SYMBOL(msm_get_dual_mic_config); |
| |
| int msm_get_voice_state(void) |
| { |
| MM_DBG("voice state %d\n", routing_info.voice_state); |
| return routing_info.voice_state; |
| } |
| EXPORT_SYMBOL(msm_get_voice_state); |
| |
| int msm_set_voice_mute(int dir, int mute) |
| { |
| MM_DBG("dir %x mute %x\n", dir, mute); |
| if (!audio_dev_ctrl.voice_rx_dev |
| || !audio_dev_ctrl.voice_tx_dev) |
| return -EPERM; |
| if (dir == DIR_TX) { |
| routing_info.tx_mute = mute; |
| broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, |
| routing_info.voice_tx_dev_id, SESSION_IGNORE); |
| } else |
| return -EPERM; |
| return 0; |
| } |
| EXPORT_SYMBOL(msm_set_voice_mute); |
| |
| int msm_set_voice_vol(int dir, s32 volume) |
| { |
| if (!audio_dev_ctrl.voice_rx_dev |
| || !audio_dev_ctrl.voice_tx_dev) |
| return -EPERM; |
| if (dir == DIR_TX) { |
| routing_info.voice_tx_vol = volume; |
| broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, |
| routing_info.voice_tx_dev_id, |
| SESSION_IGNORE); |
| } else if (dir == DIR_RX) { |
| routing_info.voice_rx_vol = volume; |
| broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, |
| routing_info.voice_rx_dev_id, |
| SESSION_IGNORE); |
| } else |
| return -EINVAL; |
| return 0; |
| } |
| EXPORT_SYMBOL(msm_set_voice_vol); |
| |
| void msm_snddev_register(struct msm_snddev_info *dev_info) |
| { |
| mutex_lock(&session_lock); |
| if (audio_dev_ctrl.num_dev < AUDIO_DEV_CTL_MAX_DEV) { |
| audio_dev_ctrl.devs[audio_dev_ctrl.num_dev] = dev_info; |
| dev_info->dev_volume = 50; /* 50% */ |
| dev_info->sessions = 0x0; |
| dev_info->usage_count = 0; |
| dev_info->set_sample_rate = 0; |
| audio_dev_ctrl.num_dev++; |
| } else |
| MM_ERR("%s: device registry max out\n", __func__); |
| mutex_unlock(&session_lock); |
| } |
| EXPORT_SYMBOL(msm_snddev_register); |
| |
| int msm_snddev_devcount(void) |
| { |
| return audio_dev_ctrl.num_dev; |
| } |
| EXPORT_SYMBOL(msm_snddev_devcount); |
| |
| int msm_snddev_query(int dev_id) |
| { |
| if (dev_id <= audio_dev_ctrl.num_dev) |
| return 0; |
| return -ENODEV; |
| } |
| EXPORT_SYMBOL(msm_snddev_query); |
| |
| int msm_snddev_is_set(int popp_id, int copp_id) |
| { |
| return routing_info.mixer_mask[popp_id] & (0x1 << copp_id); |
| } |
| EXPORT_SYMBOL(msm_snddev_is_set); |
| |
| unsigned short msm_snddev_route_enc(int enc_id) |
| { |
| if (enc_id >= MAX_ENC_SESSIONS) |
| return -EINVAL; |
| return routing_info.audrec_mixer_mask[enc_id]; |
| } |
| EXPORT_SYMBOL(msm_snddev_route_enc); |
| |
| unsigned short msm_snddev_route_dec(int popp_id) |
| { |
| if (popp_id >= MAX_DEC_SESSIONS) |
| return -EINVAL; |
| return routing_info.mixer_mask[popp_id]; |
| } |
| EXPORT_SYMBOL(msm_snddev_route_dec); |
| |
| int msm_snddev_set_dec(int popp_id, int copp_id, int set) |
| { |
| if (set) |
| routing_info.mixer_mask[popp_id] |= (0x1 << copp_id); |
| else |
| routing_info.mixer_mask[popp_id] &= ~(0x1 << copp_id); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(msm_snddev_set_dec); |
| |
| int msm_snddev_set_enc(int popp_id, int copp_id, int set) |
| { |
| if (set) |
| routing_info.audrec_mixer_mask[popp_id] |= (0x1 << copp_id); |
| else |
| routing_info.audrec_mixer_mask[popp_id] &= ~(0x1 << copp_id); |
| return 0; |
| } |
| EXPORT_SYMBOL(msm_snddev_set_enc); |
| |
| int msm_device_is_voice(int dev_id) |
| { |
| if ((dev_id == routing_info.voice_rx_dev_id) |
| || (dev_id == routing_info.voice_tx_dev_id)) |
| return 0; |
| else |
| return -EINVAL; |
| } |
| EXPORT_SYMBOL(msm_device_is_voice); |
| |
| int msm_set_voc_route(struct msm_snddev_info *dev_info, |
| int stream_type, int dev_id) |
| { |
| int rc = 0; |
| u32 session_mask = 0; |
| |
| mutex_lock(&session_lock); |
| switch (stream_type) { |
| case AUDIO_ROUTE_STREAM_VOICE_RX: |
| if (audio_dev_ctrl.voice_rx_dev) |
| audio_dev_ctrl.voice_rx_dev->sessions &= ~0xFF; |
| |
| if (!(dev_info->capability & SNDDEV_CAP_RX) | |
| !(dev_info->capability & SNDDEV_CAP_VOICE)) { |
| rc = -EINVAL; |
| break; |
| } |
| audio_dev_ctrl.voice_rx_dev = dev_info; |
| if (audio_dev_ctrl.voice_rx_dev) { |
| session_mask = |
| 0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1)); |
| audio_dev_ctrl.voice_rx_dev->sessions |= |
| session_mask; |
| } |
| routing_info.voice_rx_dev_id = dev_id; |
| break; |
| case AUDIO_ROUTE_STREAM_VOICE_TX: |
| if (audio_dev_ctrl.voice_tx_dev) |
| audio_dev_ctrl.voice_tx_dev->sessions &= ~0xFF; |
| |
| if (!(dev_info->capability & SNDDEV_CAP_TX) | |
| !(dev_info->capability & SNDDEV_CAP_VOICE)) { |
| rc = -EINVAL; |
| break; |
| } |
| |
| audio_dev_ctrl.voice_tx_dev = dev_info; |
| if (audio_dev_ctrl.voice_rx_dev) { |
| session_mask = |
| 0x1 << (8 * ((int)AUDDEV_CLNT_VOC-1)); |
| audio_dev_ctrl.voice_tx_dev->sessions |= |
| session_mask; |
| } |
| routing_info.voice_tx_dev_id = dev_id; |
| break; |
| default: |
| rc = -EINVAL; |
| } |
| mutex_unlock(&session_lock); |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_set_voc_route); |
| |
| void msm_release_voc_thread(void) |
| { |
| wake_up(&audio_dev_ctrl.wait); |
| } |
| EXPORT_SYMBOL(msm_release_voc_thread); |
| |
| int msm_snddev_get_enc_freq(session_id) |
| { |
| return routing_info.enc_freq[session_id].freq; |
| } |
| EXPORT_SYMBOL(msm_snddev_get_enc_freq); |
| |
| int msm_get_voc_freq(int *tx_freq, int *rx_freq) |
| { |
| *tx_freq = routing_info.voice_tx_sample_rate; |
| *rx_freq = routing_info.voice_rx_sample_rate; |
| return 0; |
| } |
| EXPORT_SYMBOL(msm_get_voc_freq); |
| |
| int msm_get_voc_route(u32 *rx_id, u32 *tx_id) |
| { |
| int rc = 0; |
| |
| if (!rx_id || !tx_id) |
| return -EINVAL; |
| |
| mutex_lock(&session_lock); |
| if (!audio_dev_ctrl.voice_rx_dev || !audio_dev_ctrl.voice_tx_dev) { |
| rc = -ENODEV; |
| mutex_unlock(&session_lock); |
| return rc; |
| } |
| |
| *rx_id = audio_dev_ctrl.voice_rx_dev->acdb_id; |
| *tx_id = audio_dev_ctrl.voice_tx_dev->acdb_id; |
| |
| mutex_unlock(&session_lock); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_get_voc_route); |
| |
| struct msm_snddev_info *audio_dev_ctrl_find_dev(u32 dev_id) |
| { |
| struct msm_snddev_info *info; |
| |
| if ((audio_dev_ctrl.num_dev - 1) < dev_id) { |
| info = ERR_PTR(-ENODEV); |
| goto error; |
| } |
| |
| info = audio_dev_ctrl.devs[dev_id]; |
| error: |
| return info; |
| |
| } |
| EXPORT_SYMBOL(audio_dev_ctrl_find_dev); |
| |
| int snddev_voice_set_volume(int vol, int path) |
| { |
| if (audio_dev_ctrl.voice_rx_dev |
| && audio_dev_ctrl.voice_tx_dev) { |
| if (path) |
| audio_dev_ctrl.voice_tx_dev->dev_volume = vol; |
| else |
| audio_dev_ctrl.voice_rx_dev->dev_volume = vol; |
| } else |
| return -ENODEV; |
| return 0; |
| } |
| EXPORT_SYMBOL(snddev_voice_set_volume); |
| |
| static int audio_dev_ctrl_get_devices(struct audio_dev_ctrl_state *dev_ctrl, |
| void __user *arg) |
| { |
| int rc = 0; |
| u32 index; |
| struct msm_snd_device_list work_list; |
| struct msm_snd_device_info *work_tbl; |
| |
| if (copy_from_user(&work_list, arg, sizeof(work_list))) { |
| rc = -EFAULT; |
| goto error; |
| } |
| |
| if (work_list.num_dev > dev_ctrl->num_dev) { |
| rc = -EINVAL; |
| goto error; |
| } |
| |
| work_tbl = kmalloc(work_list.num_dev * |
| sizeof(struct msm_snd_device_info), GFP_KERNEL); |
| if (!work_tbl) { |
| rc = -ENOMEM; |
| goto error; |
| } |
| |
| for (index = 0; index < dev_ctrl->num_dev; index++) { |
| work_tbl[index].dev_id = index; |
| work_tbl[index].dev_cap = dev_ctrl->devs[index]->capability; |
| strlcpy(work_tbl[index].dev_name, dev_ctrl->devs[index]->name, |
| 64); |
| } |
| |
| if (copy_to_user((void *) (work_list.list), work_tbl, |
| work_list.num_dev * sizeof(struct msm_snd_device_info))) |
| rc = -EFAULT; |
| kfree(work_tbl); |
| error: |
| return rc; |
| } |
| |
| |
| int auddev_register_evt_listner(u32 evt_id, u32 clnt_type, u32 clnt_id, |
| void (*listner)(u32 evt_id, |
| union auddev_evt_data *evt_payload, |
| void *private_data), |
| void *private_data) |
| { |
| int rc; |
| struct msm_snd_evt_listner *callback = NULL; |
| struct msm_snd_evt_listner *new_cb; |
| |
| new_cb = kzalloc(sizeof(struct msm_snd_evt_listner), GFP_KERNEL); |
| if (!new_cb) { |
| MM_ERR("No memory to add new listener node\n"); |
| return -ENOMEM; |
| } |
| |
| mutex_lock(&session_lock); |
| new_cb->cb_next = NULL; |
| new_cb->auddev_evt_listener = listner; |
| new_cb->evt_id = evt_id; |
| new_cb->clnt_type = clnt_type; |
| new_cb->clnt_id = clnt_id; |
| new_cb->private_data = private_data; |
| if (event.cb == NULL) { |
| event.cb = new_cb; |
| new_cb->cb_prev = NULL; |
| } else { |
| callback = event.cb; |
| for (; ;) { |
| if (callback->cb_next == NULL) |
| break; |
| else { |
| callback = callback->cb_next; |
| continue; |
| } |
| } |
| callback->cb_next = new_cb; |
| new_cb->cb_prev = callback; |
| } |
| event.num_listner++; |
| mutex_unlock(&session_lock); |
| rc = 0; |
| return rc; |
| } |
| EXPORT_SYMBOL(auddev_register_evt_listner); |
| |
| int auddev_unregister_evt_listner(u32 clnt_type, u32 clnt_id) |
| { |
| struct msm_snd_evt_listner *callback = event.cb; |
| struct msm_snddev_info *info; |
| u32 session_mask = 0; |
| int i = 0; |
| |
| mutex_lock(&session_lock); |
| while (callback != NULL) { |
| if ((callback->clnt_type == clnt_type) |
| && (callback->clnt_id == clnt_id)) |
| break; |
| callback = callback->cb_next; |
| } |
| if (callback == NULL) { |
| mutex_unlock(&session_lock); |
| return -EINVAL; |
| } |
| |
| if ((callback->cb_next == NULL) && (callback->cb_prev == NULL)) |
| event.cb = NULL; |
| else if (callback->cb_next == NULL) |
| callback->cb_prev->cb_next = NULL; |
| else if (callback->cb_prev == NULL) { |
| callback->cb_next->cb_prev = NULL; |
| event.cb = callback->cb_next; |
| } else { |
| callback->cb_prev->cb_next = callback->cb_next; |
| callback->cb_next->cb_prev = callback->cb_prev; |
| } |
| kfree(callback); |
| |
| session_mask = (0x1 << (clnt_id)) << (8 * ((int)clnt_type-1)); |
| for (i = 0; i < audio_dev_ctrl.num_dev; i++) { |
| info = audio_dev_ctrl.devs[i]; |
| info->sessions &= ~session_mask; |
| } |
| if (clnt_type == AUDDEV_CLNT_ENC) |
| msm_set_dual_mic_config(clnt_id, 0); |
| mutex_unlock(&session_lock); |
| return 0; |
| } |
| EXPORT_SYMBOL(auddev_unregister_evt_listner); |
| |
| int msm_snddev_withdraw_freq(u32 session_id, u32 capability, u32 clnt_type) |
| { |
| int i = 0; |
| struct msm_snddev_info *info; |
| u32 session_mask = 0; |
| |
| if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0)) |
| return -EINVAL; |
| if ((clnt_type == AUDDEV_CLNT_DEC) |
| && (session_id >= MAX_DEC_SESSIONS)) |
| return -EINVAL; |
| if ((clnt_type == AUDDEV_CLNT_ENC) |
| && (session_id >= MAX_ENC_SESSIONS)) |
| return -EINVAL; |
| |
| session_mask = (0x1 << (session_id)) << (8 * ((int)clnt_type-1)); |
| |
| for (i = 0; i < audio_dev_ctrl.num_dev; i++) { |
| info = audio_dev_ctrl.devs[i]; |
| if ((info->sessions & session_mask) |
| && (info->capability & capability)) { |
| if (!(info->sessions & ~(session_mask))) |
| info->set_sample_rate = 0; |
| } |
| } |
| if (clnt_type == AUDDEV_CLNT_DEC) |
| routing_info.dec_freq[session_id].freq |
| = 0; |
| else if (clnt_type == AUDDEV_CLNT_ENC) |
| routing_info.enc_freq[session_id].freq |
| = 0; |
| else if (capability == SNDDEV_CAP_TX) |
| routing_info.voice_tx_sample_rate = 0; |
| else |
| routing_info.voice_rx_sample_rate = 48000; |
| return 0; |
| } |
| |
| int msm_snddev_request_freq(int *freq, u32 session_id, |
| u32 capability, u32 clnt_type) |
| { |
| int i = 0; |
| int rc = 0; |
| struct msm_snddev_info *info; |
| u32 set_freq; |
| u32 session_mask = 0; |
| u32 clnt_type_mask = 0; |
| |
| MM_DBG(": clnt_type 0x%08x\n", clnt_type); |
| |
| if ((clnt_type == AUDDEV_CLNT_VOC) && (session_id != 0)) |
| return -EINVAL; |
| if ((clnt_type == AUDDEV_CLNT_DEC) |
| && (session_id >= MAX_DEC_SESSIONS)) |
| return -EINVAL; |
| if ((clnt_type == AUDDEV_CLNT_ENC) |
| && (session_id >= MAX_ENC_SESSIONS)) |
| return -EINVAL; |
| session_mask = ((0x1 << session_id)) << (8 * (clnt_type-1)); |
| clnt_type_mask = (0xFF << (8 * (clnt_type-1))); |
| if (!(*freq == 8000) && !(*freq == 11025) && |
| !(*freq == 12000) && !(*freq == 16000) && |
| !(*freq == 22050) && !(*freq == 24000) && |
| !(*freq == 32000) && !(*freq == 44100) && |
| !(*freq == 48000)) |
| return -EINVAL; |
| |
| for (i = 0; i < audio_dev_ctrl.num_dev; i++) { |
| info = audio_dev_ctrl.devs[i]; |
| if ((info->sessions & session_mask) |
| && (info->capability & capability)) { |
| rc = 0; |
| if ((info->sessions & ~clnt_type_mask) |
| && ((*freq != 8000) && (*freq != 16000) |
| && (*freq != 48000))) { |
| if (clnt_type == AUDDEV_CLNT_ENC) { |
| routing_info.enc_freq[session_id].freq |
| = 0; |
| return -EPERM; |
| } else if (clnt_type == AUDDEV_CLNT_DEC) { |
| routing_info.dec_freq[session_id].freq |
| = 0; |
| return -EPERM; |
| } |
| } |
| if (*freq == info->set_sample_rate) { |
| rc = info->set_sample_rate; |
| continue; |
| } |
| set_freq = MAX(*freq, info->set_sample_rate); |
| |
| |
| if (clnt_type == AUDDEV_CLNT_DEC) |
| routing_info.dec_freq[session_id].freq |
| = set_freq; |
| else if (clnt_type == AUDDEV_CLNT_ENC) |
| routing_info.enc_freq[session_id].freq |
| = set_freq; |
| else if (capability == SNDDEV_CAP_TX) |
| routing_info.voice_tx_sample_rate = set_freq; |
| |
| rc = set_freq; |
| *freq = set_freq; |
| /* There is difference in device sample rate to |
| * requested sample rate. So update device sample rate |
| * and propagate sample rate change event to active |
| * sessions of the device. |
| */ |
| if (info->set_sample_rate != set_freq) { |
| info->set_sample_rate = set_freq; |
| if (info->opened) { |
| /* Ignore propagating sample rate |
| * change event to requested client |
| * session |
| */ |
| if (clnt_type == AUDDEV_CLNT_DEC) |
| routing_info.\ |
| dec_freq[session_id].evt = 1; |
| else if (clnt_type == AUDDEV_CLNT_ENC) |
| routing_info.\ |
| enc_freq[session_id].evt = 1; |
| broadcast_event(AUDDEV_EVT_FREQ_CHG, i, |
| SESSION_IGNORE); |
| set_freq = info->dev_ops.set_freq(info, |
| set_freq); |
| broadcast_event(AUDDEV_EVT_DEV_RDY, i, |
| SESSION_IGNORE); |
| } |
| } |
| } |
| MM_DBG("info->set_sample_rate = %d\n", info->set_sample_rate); |
| MM_DBG("routing_info.enc_freq.freq = %d\n", |
| routing_info.enc_freq[session_id].freq); |
| } |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_snddev_request_freq); |
| |
| int msm_snddev_enable_sidetone(u32 dev_id, u32 enable) |
| { |
| int rc; |
| struct msm_snddev_info *dev_info; |
| |
| MM_DBG("dev_id %d enable %d\n", dev_id, enable); |
| |
| dev_info = audio_dev_ctrl_find_dev(dev_id); |
| |
| if (IS_ERR(dev_info)) { |
| MM_ERR("bad dev_id %d\n", dev_id); |
| rc = -EINVAL; |
| } else if (!dev_info->dev_ops.enable_sidetone) { |
| MM_DBG("dev %d no sidetone support\n", dev_id); |
| rc = -EPERM; |
| } else |
| rc = dev_info->dev_ops.enable_sidetone(dev_info, enable); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_snddev_enable_sidetone); |
| |
| static long audio_dev_ctrl_ioctl(struct file *file, |
| unsigned int cmd, unsigned long arg) |
| { |
| int rc = 0; |
| struct audio_dev_ctrl_state *dev_ctrl = file->private_data; |
| |
| mutex_lock(&session_lock); |
| switch (cmd) { |
| case AUDIO_GET_NUM_SND_DEVICE: |
| rc = put_user(dev_ctrl->num_dev, (uint32_t __user *) arg); |
| break; |
| case AUDIO_GET_SND_DEVICES: |
| rc = audio_dev_ctrl_get_devices(dev_ctrl, (void __user *) arg); |
| break; |
| case AUDIO_ENABLE_SND_DEVICE: { |
| struct msm_snddev_info *dev_info; |
| u32 dev_id; |
| |
| if (get_user(dev_id, (u32 __user *) arg)) { |
| rc = -EFAULT; |
| break; |
| } |
| dev_info = audio_dev_ctrl_find_dev(dev_id); |
| if (IS_ERR(dev_info)) |
| rc = PTR_ERR(dev_info); |
| else { |
| rc = dev_info->dev_ops.open(dev_info); |
| if (!rc) |
| dev_info->opened = 1; |
| wake_up(&audio_dev_ctrl.wait); |
| } |
| break; |
| |
| } |
| |
| case AUDIO_DISABLE_SND_DEVICE: { |
| struct msm_snddev_info *dev_info; |
| u32 dev_id; |
| |
| if (get_user(dev_id, (u32 __user *) arg)) { |
| rc = -EFAULT; |
| break; |
| } |
| dev_info = audio_dev_ctrl_find_dev(dev_id); |
| if (IS_ERR(dev_info)) |
| rc = PTR_ERR(dev_info); |
| else { |
| rc = dev_info->dev_ops.close(dev_info); |
| dev_info->opened = 0; |
| } |
| break; |
| } |
| |
| case AUDIO_ROUTE_STREAM: { |
| struct msm_audio_route_config route_cfg; |
| struct msm_snddev_info *dev_info; |
| |
| if (copy_from_user(&route_cfg, (void __user *) arg, |
| sizeof(struct msm_audio_route_config))) { |
| rc = -EFAULT; |
| break; |
| } |
| MM_DBG("%s: route cfg %d %d type\n", __func__, |
| route_cfg.dev_id, route_cfg.stream_type); |
| dev_info = audio_dev_ctrl_find_dev(route_cfg.dev_id); |
| if (IS_ERR(dev_info)) { |
| MM_ERR("%s: pass invalid dev_id\n", __func__); |
| rc = PTR_ERR(dev_info); |
| break; |
| } |
| |
| switch (route_cfg.stream_type) { |
| |
| case AUDIO_ROUTE_STREAM_VOICE_RX: |
| if (!(dev_info->capability & SNDDEV_CAP_RX) | |
| !(dev_info->capability & SNDDEV_CAP_VOICE)) { |
| rc = -EINVAL; |
| break; |
| } |
| dev_ctrl->voice_rx_dev = dev_info; |
| break; |
| case AUDIO_ROUTE_STREAM_VOICE_TX: |
| if (!(dev_info->capability & SNDDEV_CAP_TX) | |
| !(dev_info->capability & SNDDEV_CAP_VOICE)) { |
| rc = -EINVAL; |
| break; |
| } |
| dev_ctrl->voice_tx_dev = dev_info; |
| break; |
| } |
| break; |
| } |
| |
| default: |
| rc = -EINVAL; |
| } |
| mutex_unlock(&session_lock); |
| return rc; |
| } |
| |
| static int audio_dev_ctrl_open(struct inode *inode, struct file *file) |
| { |
| MM_DBG("open audio_dev_ctrl\n"); |
| atomic_inc(&audio_dev_ctrl.opened); |
| file->private_data = &audio_dev_ctrl; |
| return 0; |
| } |
| |
| static int audio_dev_ctrl_release(struct inode *inode, struct file *file) |
| { |
| MM_DBG("release audio_dev_ctrl\n"); |
| atomic_dec(&audio_dev_ctrl.opened); |
| return 0; |
| } |
| |
| static const struct file_operations audio_dev_ctrl_fops = { |
| .owner = THIS_MODULE, |
| .open = audio_dev_ctrl_open, |
| .release = audio_dev_ctrl_release, |
| .unlocked_ioctl = audio_dev_ctrl_ioctl, |
| }; |
| |
| |
| struct miscdevice audio_dev_ctrl_misc = { |
| .minor = MISC_DYNAMIC_MINOR, |
| .name = "msm_audio_dev_ctrl", |
| .fops = &audio_dev_ctrl_fops, |
| }; |
| |
| /* session id is 32 bit routing mask per device |
| * 0-7 for voice clients |
| * 8-15 for Decoder clients |
| * 16-23 for Encoder clients |
| * 24-31 Do not care |
| */ |
| void broadcast_event(u32 evt_id, u32 dev_id, u32 session_id) |
| { |
| int clnt_id = 0, i; |
| union auddev_evt_data *evt_payload; |
| struct msm_snd_evt_listner *callback; |
| struct msm_snddev_info *dev_info = NULL; |
| u32 session_mask = 0; |
| static int pending_sent; |
| |
| MM_DBG(": evt_id = %d\n", evt_id); |
| |
| if ((evt_id != AUDDEV_EVT_START_VOICE) |
| && (evt_id != AUDDEV_EVT_END_VOICE) |
| && (evt_id != AUDDEV_EVT_STREAM_VOL_CHG) |
| && (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) { |
| dev_info = audio_dev_ctrl_find_dev(dev_id); |
| if (IS_ERR(dev_info)) { |
| MM_ERR("pass invalid dev_id\n"); |
| return; |
| } |
| } |
| |
| if (event.cb != NULL) |
| callback = event.cb; |
| else |
| return; |
| |
| evt_payload = kzalloc(sizeof(union auddev_evt_data), |
| GFP_KERNEL); |
| if (evt_payload == NULL) { |
| MM_ERR("Memory allocation for event payload failed\n"); |
| return; |
| } |
| |
| mutex_lock(&session_lock); |
| |
| if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) |
| routing_info.voice_state = dev_id; |
| |
| for (; ;) { |
| if (!(evt_id & callback->evt_id)) { |
| if (callback->cb_next == NULL) |
| break; |
| else { |
| callback = callback->cb_next; |
| continue; |
| } |
| } |
| clnt_id = callback->clnt_id; |
| memset(evt_payload, 0, sizeof(union auddev_evt_data)); |
| |
| if ((evt_id == AUDDEV_EVT_START_VOICE) |
| || (evt_id == AUDDEV_EVT_END_VOICE)) |
| goto skip_check; |
| if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) |
| goto aud_cal; |
| |
| session_mask = (0x1 << (clnt_id)) |
| << (8 * ((int)callback->clnt_type-1)); |
| |
| if ((evt_id == AUDDEV_EVT_STREAM_VOL_CHG) || \ |
| (evt_id == AUDDEV_EVT_VOICE_STATE_CHG)) { |
| MM_DBG("AUDDEV_EVT_STREAM_VOL_CHG or\ |
| AUDDEV_EVT_VOICE_STATE_CHG\n"); |
| goto volume_strm; |
| } |
| |
| MM_DBG("dev_info->sessions = %08x\n", dev_info->sessions); |
| |
| if ((!session_id && !(dev_info->sessions & session_mask)) || |
| (session_id && ((dev_info->sessions & session_mask) != |
| session_id))) { |
| if (callback->cb_next == NULL) |
| break; |
| else { |
| callback = callback->cb_next; |
| continue; |
| } |
| } |
| if (evt_id == AUDDEV_EVT_DEV_CHG_VOICE) |
| goto voc_events; |
| |
| volume_strm: |
| if (callback->clnt_type == AUDDEV_CLNT_DEC) { |
| MM_DBG("AUDDEV_CLNT_DEC\n"); |
| if (evt_id == AUDDEV_EVT_STREAM_VOL_CHG) { |
| MM_DBG("clnt_id = %d, session_id = 0x%8x\n", |
| clnt_id, session_id); |
| if (session_mask != session_id) |
| goto sent_dec; |
| else |
| evt_payload->session_vol = |
| msm_vol_ctl.volume; |
| } else if (evt_id == AUDDEV_EVT_FREQ_CHG) { |
| if (routing_info.dec_freq[clnt_id].evt) { |
| routing_info.dec_freq[clnt_id].evt |
| = 0; |
| goto sent_dec; |
| } else if (routing_info.dec_freq[clnt_id].freq |
| == dev_info->set_sample_rate) |
| goto sent_dec; |
| else { |
| evt_payload->freq_info.sample_rate |
| = dev_info->set_sample_rate; |
| evt_payload->freq_info.dev_type |
| = dev_info->capability; |
| evt_payload->freq_info.acdb_dev_id |
| = dev_info->acdb_id; |
| } |
| /* Propogate device information to client */ |
| } else if (evt_id == AUDDEV_EVT_DEVICE_INFO) { |
| evt_payload->devinfo.dev_id |
| = dev_info->copp_id; |
| evt_payload->devinfo.acdb_id |
| = dev_info->acdb_id; |
| evt_payload->devinfo.dev_type = |
| (dev_info->capability & SNDDEV_CAP_TX) ? |
| SNDDEV_CAP_TX : SNDDEV_CAP_RX; |
| evt_payload->devinfo.sample_rate |
| = dev_info->sample_rate; |
| if (session_id == SESSION_IGNORE) |
| evt_payload->devinfo.sessions |
| = dev_info->sessions; |
| else |
| evt_payload->devinfo.sessions |
| = session_id; |
| evt_payload->devinfo.sessions = |
| (evt_payload->devinfo.sessions >> |
| ((AUDDEV_CLNT_DEC-1) * 8)); |
| } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) |
| evt_payload->voice_state = |
| routing_info.voice_state; |
| else |
| evt_payload->routing_id = dev_info->copp_id; |
| callback->auddev_evt_listener( |
| evt_id, |
| evt_payload, |
| callback->private_data); |
| sent_dec: |
| if ((evt_id != AUDDEV_EVT_STREAM_VOL_CHG) && |
| (evt_id != AUDDEV_EVT_VOICE_STATE_CHG)) |
| routing_info.dec_freq[clnt_id].freq |
| = dev_info->set_sample_rate; |
| |
| if (callback->cb_next == NULL) |
| break; |
| else { |
| callback = callback->cb_next; |
| continue; |
| } |
| } |
| if (callback->clnt_type == AUDDEV_CLNT_ENC) { |
| |
| MM_DBG("AUDDEV_CLNT_ENC\n"); |
| if (evt_id == AUDDEV_EVT_FREQ_CHG) { |
| if (routing_info.enc_freq[clnt_id].evt) { |
| routing_info.enc_freq[clnt_id].evt |
| = 0; |
| goto sent_enc; |
| } else { |
| evt_payload->freq_info.sample_rate |
| = dev_info->set_sample_rate; |
| evt_payload->freq_info.dev_type |
| = dev_info->capability; |
| evt_payload->freq_info.acdb_dev_id |
| = dev_info->acdb_id; |
| } |
| /* Propogate device information to client */ |
| } else if (evt_id == AUDDEV_EVT_DEVICE_INFO) { |
| evt_payload->devinfo.dev_id |
| = dev_info->copp_id; |
| evt_payload->devinfo.acdb_id |
| = dev_info->acdb_id; |
| evt_payload->devinfo.dev_type = |
| (dev_info->capability & SNDDEV_CAP_TX) ? |
| SNDDEV_CAP_TX : SNDDEV_CAP_RX; |
| evt_payload->devinfo.sample_rate |
| = dev_info->sample_rate; |
| if (session_id == SESSION_IGNORE) |
| evt_payload->devinfo.sessions |
| = dev_info->sessions; |
| else |
| evt_payload->devinfo.sessions |
| = session_id; |
| evt_payload->devinfo.sessions = |
| (evt_payload->devinfo.sessions >> |
| ((AUDDEV_CLNT_ENC-1) * 8)); |
| } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) |
| evt_payload->voice_state = |
| routing_info.voice_state; |
| else |
| evt_payload->routing_id = dev_info->copp_id; |
| callback->auddev_evt_listener( |
| evt_id, |
| evt_payload, |
| callback->private_data); |
| sent_enc: |
| if (callback->cb_next == NULL) |
| break; |
| else { |
| callback = callback->cb_next; |
| continue; |
| } |
| } |
| aud_cal: |
| if (callback->clnt_type == AUDDEV_CLNT_AUDIOCAL) { |
| int temp_sessions; |
| MM_DBG("AUDDEV_CLNT_AUDIOCAL\n"); |
| if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) |
| evt_payload->voice_state = |
| routing_info.voice_state; |
| else if (!dev_info->sessions) |
| goto sent_aud_cal; |
| else { |
| evt_payload->audcal_info.dev_id = |
| dev_info->copp_id; |
| evt_payload->audcal_info.acdb_id = |
| dev_info->acdb_id; |
| evt_payload->audcal_info.dev_type = |
| (dev_info->capability & SNDDEV_CAP_TX) ? |
| SNDDEV_CAP_TX : SNDDEV_CAP_RX; |
| evt_payload->audcal_info.sample_rate = |
| dev_info->set_sample_rate ? |
| dev_info->set_sample_rate : |
| dev_info->sample_rate; |
| } |
| if (evt_payload->audcal_info.dev_type == |
| SNDDEV_CAP_TX) { |
| if (session_id == SESSION_IGNORE) |
| temp_sessions = dev_info->sessions; |
| else |
| temp_sessions = session_id; |
| evt_payload->audcal_info.sessions = |
| (temp_sessions >> |
| ((AUDDEV_CLNT_ENC-1) * 8)); |
| } else { |
| if (session_id == SESSION_IGNORE) |
| temp_sessions = dev_info->sessions; |
| else |
| temp_sessions = session_id; |
| evt_payload->audcal_info.sessions = |
| (temp_sessions >> |
| ((AUDDEV_CLNT_DEC-1) * 8)); |
| } |
| callback->auddev_evt_listener( |
| evt_id, |
| evt_payload, |
| callback->private_data); |
| |
| sent_aud_cal: |
| if (callback->cb_next == NULL) |
| break; |
| else { |
| callback = callback->cb_next; |
| continue; |
| } |
| } |
| skip_check: |
| voc_events: |
| if (callback->clnt_type == AUDDEV_CLNT_VOC) { |
| MM_DBG("AUDDEV_CLNT_VOC\n"); |
| if (evt_id == AUDDEV_EVT_DEV_RLS) { |
| if (!pending_sent) |
| goto sent_voc; |
| else |
| pending_sent = 0; |
| } |
| if (evt_id == AUDDEV_EVT_REL_PENDING) |
| pending_sent = 1; |
| |
| if (evt_id == AUDDEV_EVT_DEVICE_VOL_MUTE_CHG) { |
| if (dev_info->capability & SNDDEV_CAP_TX) { |
| evt_payload->voc_vm_info.dev_type = |
| SNDDEV_CAP_TX; |
| evt_payload->voc_vm_info.acdb_dev_id = |
| dev_info->acdb_id; |
| evt_payload-> |
| voc_vm_info.dev_vm_val.mute = |
| routing_info.tx_mute; |
| } else { |
| evt_payload->voc_vm_info.dev_type = |
| SNDDEV_CAP_RX; |
| evt_payload->voc_vm_info.acdb_dev_id = |
| dev_info->acdb_id; |
| evt_payload-> |
| voc_vm_info.dev_vm_val.vol = |
| routing_info.voice_rx_vol; |
| } |
| } else if ((evt_id == AUDDEV_EVT_START_VOICE) |
| || (evt_id == AUDDEV_EVT_END_VOICE)) |
| memset(evt_payload, 0, |
| sizeof(union auddev_evt_data)); |
| else if (evt_id == AUDDEV_EVT_FREQ_CHG) { |
| if (routing_info.voice_tx_sample_rate |
| != dev_info->set_sample_rate) { |
| routing_info.voice_tx_sample_rate |
| = dev_info->set_sample_rate; |
| evt_payload->freq_info.sample_rate |
| = dev_info->set_sample_rate; |
| evt_payload->freq_info.dev_type |
| = dev_info->capability; |
| evt_payload->freq_info.acdb_dev_id |
| = dev_info->acdb_id; |
| } else |
| goto sent_voc; |
| } else if (evt_id == AUDDEV_EVT_VOICE_STATE_CHG) |
| evt_payload->voice_state = |
| routing_info.voice_state; |
| else { |
| evt_payload->voc_devinfo.dev_type = |
| (dev_info->capability & SNDDEV_CAP_TX) ? |
| SNDDEV_CAP_TX : SNDDEV_CAP_RX; |
| evt_payload->voc_devinfo.acdb_dev_id = |
| dev_info->acdb_id; |
| evt_payload->voc_devinfo.dev_sample = |
| dev_info->set_sample_rate ? |
| dev_info->set_sample_rate : |
| dev_info->sample_rate; |
| evt_payload->voc_devinfo.dev_id = dev_id; |
| if (dev_info->capability & SNDDEV_CAP_RX) { |
| for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; |
| i++) { |
| evt_payload-> |
| voc_devinfo.max_rx_vol[i] = |
| dev_info->max_voc_rx_vol[i]; |
| evt_payload |
| ->voc_devinfo.min_rx_vol[i] = |
| dev_info->min_voc_rx_vol[i]; |
| } |
| } |
| } |
| callback->auddev_evt_listener( |
| evt_id, |
| evt_payload, |
| callback->private_data); |
| if (evt_id == AUDDEV_EVT_DEV_RLS) |
| dev_info->sessions &= ~(0xFF); |
| sent_voc: |
| if (callback->cb_next == NULL) |
| break; |
| else { |
| callback = callback->cb_next; |
| continue; |
| } |
| } |
| } |
| kfree(evt_payload); |
| mutex_unlock(&session_lock); |
| } |
| EXPORT_SYMBOL(broadcast_event); |
| |
| |
| void mixer_post_event(u32 evt_id, u32 id) |
| { |
| |
| MM_DBG("evt_id = %d\n", evt_id); |
| switch (evt_id) { |
| case AUDDEV_EVT_DEV_CHG_VOICE: /* Called from Voice_route */ |
| broadcast_event(AUDDEV_EVT_DEV_CHG_VOICE, id, SESSION_IGNORE); |
| break; |
| case AUDDEV_EVT_DEV_RDY: |
| broadcast_event(AUDDEV_EVT_DEV_RDY, id, SESSION_IGNORE); |
| break; |
| case AUDDEV_EVT_DEV_RLS: |
| broadcast_event(AUDDEV_EVT_DEV_RLS, id, SESSION_IGNORE); |
| break; |
| case AUDDEV_EVT_REL_PENDING: |
| broadcast_event(AUDDEV_EVT_REL_PENDING, id, SESSION_IGNORE); |
| break; |
| case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG: |
| broadcast_event(AUDDEV_EVT_DEVICE_VOL_MUTE_CHG, id, |
| SESSION_IGNORE); |
| break; |
| case AUDDEV_EVT_STREAM_VOL_CHG: |
| broadcast_event(AUDDEV_EVT_STREAM_VOL_CHG, id, |
| SESSION_IGNORE); |
| break; |
| case AUDDEV_EVT_START_VOICE: |
| broadcast_event(AUDDEV_EVT_START_VOICE, |
| id, SESSION_IGNORE); |
| break; |
| case AUDDEV_EVT_END_VOICE: |
| broadcast_event(AUDDEV_EVT_END_VOICE, |
| id, SESSION_IGNORE); |
| break; |
| case AUDDEV_EVT_FREQ_CHG: |
| broadcast_event(AUDDEV_EVT_FREQ_CHG, id, SESSION_IGNORE); |
| break; |
| default: |
| break; |
| } |
| } |
| EXPORT_SYMBOL(mixer_post_event); |
| |
| static int __init audio_dev_ctrl_init(void) |
| { |
| #ifdef CONFIG_DEBUG_FS |
| char name[sizeof "rtc_get_device"+1]; |
| #endif |
| |
| init_waitqueue_head(&audio_dev_ctrl.wait); |
| |
| event.cb = NULL; |
| |
| atomic_set(&audio_dev_ctrl.opened, 0); |
| audio_dev_ctrl.num_dev = 0; |
| audio_dev_ctrl.voice_tx_dev = NULL; |
| audio_dev_ctrl.voice_rx_dev = NULL; |
| routing_info.voice_state = VOICE_STATE_INVALID; |
| #ifdef CONFIG_DEBUG_FS |
| snprintf(name, sizeof name, "rtc_get_device"); |
| dentry = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUGO, |
| NULL, NULL, &rtc_acdb_debug_fops); |
| if (IS_ERR(dentry)) |
| MM_DBG("debugfs_create_file failed\n"); |
| #endif |
| |
| return misc_register(&audio_dev_ctrl_misc); |
| } |
| |
| static void __exit audio_dev_ctrl_exit(void) |
| { |
| #ifdef CONFIG_DEBUG_FS |
| if (dentry) |
| debugfs_remove(dentry); |
| #endif |
| |
| } |
| module_init(audio_dev_ctrl_init); |
| module_exit(audio_dev_ctrl_exit); |
| |
| MODULE_DESCRIPTION("MSM 7K Audio Device Control driver"); |
| MODULE_LICENSE("GPL v2"); |