123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- /* linux/sound/soc/msm/msm7201.c
- *
- * Copyright (c) 2008-2009, 2011, 2012 The Linux Foundation. All rights reserved.
- *
- * All source code in this file is licensed under the following license except
- * where indicated.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, you can find it at http://www.fsf.org.
- */
- #include <linux/init.h>
- #include <linux/err.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/time.h>
- #include <linux/wait.h>
- #include <linux/platform_device.h>
- #include <linux/msm_audio.h>
- #include <sound/core.h>
- #include <sound/soc.h>
- #include <sound/soc-dapm.h>
- #include <sound/pcm.h>
- #include <sound/tlv.h>
- #include <sound/initval.h>
- #include <sound/control.h>
- #include <asm/dma.h>
- #include <linux/dma-mapping.h>
- #include "msm-pcm.h"
- #include <asm/mach-types.h>
- #include <mach/msm_rpcrouter.h>
- static struct msm_rpc_endpoint *snd_ep;
- static uint32_t snd_mute_ear_mute;
- static uint32_t snd_mute_mic_mute;
- struct msm_snd_rpc_ids {
- unsigned long prog;
- unsigned long vers;
- unsigned long rpc_set_snd_device;
- unsigned long rpc_set_device_vol;
- struct cad_devices_type device;
- };
- struct rpc_cad_set_device_args {
- struct cad_devices_type device;
- uint32_t ear_mute;
- uint32_t mic_mute;
- uint32_t cb_func;
- uint32_t client_data;
- };
- struct rpc_cad_set_volume_args {
- struct cad_devices_type device;
- uint32_t method;
- uint32_t volume;
- uint32_t cb_func;
- uint32_t client_data;
- };
- static struct msm_snd_rpc_ids snd_rpc_ids;
- static struct platform_device *msm_audio_snd_device;
- static int snd_msm_volume_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1; /* Volume Param, in dB */
- uinfo->value.integer.min = MIN_DB;
- uinfo->value.integer.max = MAX_DB;
- return 0;
- }
- static int snd_msm_volume_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- spin_lock_irq(&the_locks.mixer_lock);
- ucontrol->value.integer.value[0] = msm_vol_ctl.volume;
- spin_unlock_irq(&the_locks.mixer_lock);
- return 0;
- }
- static int snd_msm_volume_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- int change;
- int volume;
- volume = ucontrol->value.integer.value[0];
- spin_lock_irq(&the_locks.mixer_lock);
- change = (msm_vol_ctl.volume != volume);
- if (change) {
- msm_vol_ctl.volume = volume;
- msm_audio_volume_update(PCMPLAYBACK_DECODERID,
- msm_vol_ctl.volume, msm_vol_ctl.pan);
- }
- spin_unlock_irq(&the_locks.mixer_lock);
- return 0;
- }
- static int snd_msm_device_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 4; /* Device */
- /*
- * The number of devices supported is 26 (0 to 25)
- */
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 36;
- return 0;
- }
- static int snd_msm_device_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- ucontrol->value.integer.value[0]
- = (uint32_t)snd_rpc_ids.device.rx_device;
- ucontrol->value.integer.value[1]
- = (uint32_t)snd_rpc_ids.device.tx_device;
- ucontrol->value.integer.value[2] = snd_mute_ear_mute;
- ucontrol->value.integer.value[3] = snd_mute_mic_mute;
- return 0;
- }
- int msm_snd_init_rpc_ids(void)
- {
- snd_rpc_ids.prog = 0x30000002;
- snd_rpc_ids.vers = 0x00030003;
- /*
- * The magic number 2 corresponds to the rpc call
- * index for snd_set_device
- */
- snd_rpc_ids.rpc_set_snd_device = 40;
- snd_rpc_ids.rpc_set_device_vol = 39;
- return 0;
- }
- int msm_snd_rpc_connect(void)
- {
- if (snd_ep) {
- printk(KERN_INFO "%s: snd_ep already connected\n", __func__);
- return 0;
- }
- /* Initialize rpc ids */
- if (msm_snd_init_rpc_ids()) {
- pr_err("%s: snd rpc ids initialization failed\n"
- , __func__);
- return -ENODATA;
- }
- snd_ep = msm_rpc_connect_compatible(snd_rpc_ids.prog,
- snd_rpc_ids.vers, 0);
- if (IS_ERR(snd_ep)) {
- pr_err("%s: failed (compatible VERS = %ld)\n",
- __func__, snd_rpc_ids.vers);
- snd_ep = NULL;
- return -EAGAIN;
- }
- return 0;
- }
- int msm_snd_rpc_close(void)
- {
- int rc = 0;
- if (IS_ERR(snd_ep)) {
- pr_err("%s: snd handle unavailable, rc = %ld\n",
- __func__, PTR_ERR(snd_ep));
- return -EAGAIN;
- }
- rc = msm_rpc_close(snd_ep);
- snd_ep = NULL;
- if (rc < 0) {
- pr_err("%s: close rpc failed! rc = %d\n",
- __func__, rc);
- return -EAGAIN;
- } else
- printk(KERN_INFO "rpc close success\n");
- return rc;
- }
- static int snd_msm_device_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- int rc = 0;
- struct snd_cad_set_device_msg {
- struct rpc_request_hdr hdr;
- struct rpc_cad_set_device_args args;
- } dmsg;
- snd_rpc_ids.device.rx_device
- = (int)ucontrol->value.integer.value[0];
- snd_rpc_ids.device.tx_device
- = (int)ucontrol->value.integer.value[1];
- snd_rpc_ids.device.pathtype = CAD_DEVICE_PATH_RX_TX;
- dmsg.args.device.rx_device
- = cpu_to_be32(snd_rpc_ids.device.rx_device);
- dmsg.args.device.tx_device
- = cpu_to_be32(snd_rpc_ids.device.tx_device);
- dmsg.args.device.pathtype = cpu_to_be32(CAD_DEVICE_PATH_RX_TX);
- dmsg.args.ear_mute = cpu_to_be32(ucontrol->value.integer.value[2]);
- dmsg.args.mic_mute = cpu_to_be32(ucontrol->value.integer.value[3]);
- if (!(dmsg.args.ear_mute == SND_MUTE_MUTED ||
- dmsg.args.ear_mute == SND_MUTE_UNMUTED) ||
- (!(dmsg.args.mic_mute == SND_MUTE_MUTED ||
- dmsg.args.ear_mute == SND_MUTE_UNMUTED))) {
- pr_err("snd_cad_ioctl set device: invalid mute status\n");
- rc = -EINVAL;
- return rc;
- }
- dmsg.args.cb_func = -1;
- dmsg.args.client_data = 0;
- rc = msm_rpc_call(snd_ep, snd_rpc_ids.rpc_set_snd_device ,
- &dmsg, sizeof(dmsg), 5 * HZ);
- if (rc < 0) {
- pr_err("%s: snd rpc call failed! rc = %d\n",
- __func__, rc);
- } else {
- printk(KERN_INFO "snd device connected\n");
- snd_mute_ear_mute = ucontrol->value.integer.value[2];
- snd_mute_mic_mute = ucontrol->value.integer.value[3];
- pr_err("%s: snd_mute_ear_mute =%d, snd_mute_mic_mute = %d\n",
- __func__, snd_mute_ear_mute, snd_mute_mic_mute);
- }
- return rc;
- }
- static int snd_msm_device_vol_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
- {
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1; /* Device/Volume */
- /*
- * The volume ranges from (0 to 6)
- */
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 6;
- return 0;
- }
- static int snd_msm_device_vol_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
- {
- int rc = 0;
- struct snd_cad_set_volume_msg {
- struct rpc_request_hdr hdr;
- struct rpc_cad_set_volume_args args;
- } vmsg;
- vmsg.args.device.rx_device
- = cpu_to_be32(snd_rpc_ids.device.rx_device);
- vmsg.args.device.tx_device
- = cpu_to_be32(snd_rpc_ids.device.tx_device);
- vmsg.args.method = cpu_to_be32(SND_METHOD_VOICE);
- vmsg.args.volume = cpu_to_be32(ucontrol->value.integer.value[0]);
- vmsg.args.cb_func = -1;
- vmsg.args.client_data = 0;
- rc = msm_rpc_call(snd_ep, snd_rpc_ids.rpc_set_device_vol ,
- &vmsg, sizeof(vmsg), 5 * HZ);
- if (rc < 0) {
- pr_err("%s: snd rpc call failed! rc = %d\n",
- __func__, rc);
- } else {
- pr_debug("%s:rx device [%d]", __func__,
- snd_rpc_ids.device.rx_device);
- pr_debug("%s:tx device [%d]", __func__,
- snd_rpc_ids.device.tx_device);
- pr_debug("%s:volume set to [%ld]\n", __func__,
- snd_rpc_ids.rpc_set_device_vol);
- }
- return rc;
- }
- /* Supported range -50dB to 18dB */
- static const DECLARE_TLV_DB_LINEAR(db_scale_linear, -5000, 1800);
- #define MSM_EXT(xname, xindex, fp_info, fp_get, fp_put, addr) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
- .name = xname, .index = xindex, \
- .info = fp_info,\
- .get = fp_get, .put = fp_put, \
- .private_value = addr, \
- }
- #define MSM_EXT_TLV(xname, xindex, fp_info, fp_get, fp_put, addr, tlv_array) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
- .access = (SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
- SNDRV_CTL_ELEM_ACCESS_READWRITE), \
- .name = xname, .index = xindex, \
- .info = fp_info,\
- .get = fp_get, .put = fp_put, .tlv.p = tlv_array, \
- .private_value = addr, \
- }
- static struct snd_kcontrol_new snd_msm_controls[] = {
- MSM_EXT_TLV("PCM Playback Volume", 0, snd_msm_volume_info, \
- snd_msm_volume_get, snd_msm_volume_put, 0, db_scale_linear),
- MSM_EXT("device", 0, snd_msm_device_info, snd_msm_device_get, \
- snd_msm_device_put, 0),
- MSM_EXT("Device Volume", 0, snd_msm_device_vol_info, NULL, \
- snd_msm_device_vol_put, 0),
- };
- static int msm_new_mixer(struct snd_soc_codec *codec)
- {
- unsigned int idx;
- int err;
- pr_err("msm_soc: ALSA MSM Mixer Setting\n");
- strcpy(codec->card->snd_card->mixername, "MSM Mixer");
- for (idx = 0; idx < ARRAY_SIZE(snd_msm_controls); idx++) {
- err = snd_ctl_add(codec->card->snd_card,
- snd_ctl_new1(&snd_msm_controls[idx], NULL));
- if (err < 0)
- return err;
- }
- return 0;
- }
- static int msm_soc_dai_init(
- struct snd_soc_pcm_runtime *rtd)
- {
- int ret = 0;
- struct snd_soc_codec *codec = rtd->codec;
- mutex_init(&the_locks.lock);
- mutex_init(&the_locks.write_lock);
- mutex_init(&the_locks.read_lock);
- spin_lock_init(&the_locks.read_dsp_lock);
- spin_lock_init(&the_locks.write_dsp_lock);
- spin_lock_init(&the_locks.mixer_lock);
- init_waitqueue_head(&the_locks.eos_wait);
- init_waitqueue_head(&the_locks.write_wait);
- init_waitqueue_head(&the_locks.read_wait);
- msm_vol_ctl.volume = MSM_PLAYBACK_DEFAULT_VOLUME;
- msm_vol_ctl.pan = MSM_PLAYBACK_DEFAULT_PAN;
- ret = msm_new_mixer(codec);
- if (ret < 0) {
- pr_err("msm_soc: ALSA MSM Mixer Fail\n");
- }
- return ret;
- }
- static struct snd_soc_dai_link msm_dai[] = {
- {
- .name = "MSM Primary I2S",
- .stream_name = "DSP 1",
- .cpu_dai_name = "msm-cpu-dai.0",
- .platform_name = "msm-dsp-audio.0",
- .codec_name = "msm-codec-dai.0",
- .codec_dai_name = "msm-codec-dai",
- .init = &msm_soc_dai_init,
- },
- };
- static struct snd_soc_card snd_soc_card_msm = {
- .name = "msm-audio",
- .dai_link = msm_dai,
- .num_links = ARRAY_SIZE(msm_dai),
- };
- static int __init msm_audio_init(void)
- {
- int ret;
- msm_audio_snd_device = platform_device_alloc("soc-audio", -1);
- if (!msm_audio_snd_device)
- return -ENOMEM;
- platform_set_drvdata(msm_audio_snd_device, &snd_soc_card_msm);
- ret = platform_device_add(msm_audio_snd_device);
- if (ret) {
- platform_device_put(msm_audio_snd_device);
- return ret;
- }
- ret = msm_snd_rpc_connect();
- snd_mute_ear_mute = 0;
- snd_mute_mic_mute = 0;
- return ret;
- }
- static void __exit msm_audio_exit(void)
- {
- msm_snd_rpc_close();
- platform_device_unregister(msm_audio_snd_device);
- }
- module_init(msm_audio_init);
- module_exit(msm_audio_exit);
- MODULE_DESCRIPTION("PCM module");
- MODULE_LICENSE("GPL v2");
|