android_kernel_cmhtcleo/arch/arm/mach-msm/htc_acoustic.c
2010-08-27 11:19:57 +02:00

267 lines
5.8 KiB
C

/* arch/arm/mach-msm/htc_acoustic.c
*
* Copyright (C) 2007-2008 HTC Corporation
* Author: Laurence Chen <Laurence_Chen@htc.com>
*
* 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 <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <mach/msm_smd.h>
#include <mach/msm_rpcrouter.h>
#include <mach/msm_iomap.h>
#include "smd_private.h"
#define ACOUSTIC_IOCTL_MAGIC 'p'
#define ACOUSTIC_ARM11_DONE _IOW(ACOUSTIC_IOCTL_MAGIC, 22, unsigned int)
#define HTCRPOG 0x30100002
#define HTCVERS MSM_RPC_VERS(0,0)
#define ONCRPC_SET_MIC_BIAS_PROC (1)
#define ONCRPC_ACOUSTIC_INIT_PROC (5)
#define ONCRPC_ALLOC_ACOUSTIC_MEM_PROC (6)
#define HTC_ACOUSTIC_TABLE_SIZE (0x10000)
#define D(fmt, args...) printk(KERN_INFO "htc-acoustic: "fmt, ##args)
#define E(fmt, args...) printk(KERN_ERR "htc-acoustic: "fmt, ##args)
struct set_smem_req {
struct rpc_request_hdr hdr;
uint32_t size;
};
struct set_smem_rep {
struct rpc_reply_hdr hdr;
int n;
};
struct set_acoustic_req {
struct rpc_request_hdr hdr;
};
struct set_acoustic_rep {
struct rpc_reply_hdr hdr;
int n;
};
static uint32_t htc_acoustic_vir_addr;
static struct msm_rpc_endpoint *endpoint;
static struct mutex api_lock;
static struct mutex rpc_connect_mutex;
static int is_rpc_connect(void)
{
mutex_lock(&rpc_connect_mutex);
if (endpoint == NULL) {
endpoint = msm_rpc_connect(HTCRPOG, HTCVERS, 0);
if (IS_ERR(endpoint)) {
pr_err("%s: init rpc failed! rc = %ld\n",
__func__, PTR_ERR(endpoint));
mutex_unlock(&rpc_connect_mutex);
return 0;
}
}
mutex_unlock(&rpc_connect_mutex);
return 1;
}
int turn_mic_bias_on(int on)
{
struct mic_bias_req {
struct rpc_request_hdr hdr;
uint32_t on;
} req;
if (!is_rpc_connect())
return -1;
req.on = cpu_to_be32(on);
return msm_rpc_call(endpoint, ONCRPC_SET_MIC_BIAS_PROC,
&req, sizeof(req), 5 * HZ);
}
EXPORT_SYMBOL(turn_mic_bias_on);
static int acoustic_mmap(struct file *file, struct vm_area_struct *vma)
{
unsigned long pgoff, delta;
int rc = -EINVAL;
size_t size;
D("mmap\n");
mutex_lock(&api_lock);
size = vma->vm_end - vma->vm_start;
if (vma->vm_pgoff != 0) {
E("mmap failed: page offset %lx\n", vma->vm_pgoff);
goto done;
}
if (!htc_acoustic_vir_addr) {
E("mmap failed: smem region not allocated\n");
rc = -EIO;
goto done;
}
pgoff = MSM_SHARED_RAM_PHYS +
(htc_acoustic_vir_addr - (uint32_t)MSM_SHARED_RAM_BASE);
delta = PAGE_ALIGN(pgoff) - pgoff;
if (size + delta > HTC_ACOUSTIC_TABLE_SIZE) {
E("mmap failed: size %d\n", size);
goto done;
}
pgoff += delta;
vma->vm_flags |= VM_IO | VM_RESERVED;
rc = io_remap_pfn_range(vma, vma->vm_start, pgoff >> PAGE_SHIFT,
size, vma->vm_page_prot);
if (rc < 0)
E("mmap failed: remap error %d\n", rc);
done: mutex_unlock(&api_lock);
return rc;
}
static int acoustic_open(struct inode *inode, struct file *file)
{
int rc = -EIO;
struct set_smem_req req_smem;
struct set_smem_rep rep_smem;
D("open\n");
mutex_lock(&api_lock);
if (!htc_acoustic_vir_addr) {
if (!is_rpc_connect())
goto done;
req_smem.size = cpu_to_be32(HTC_ACOUSTIC_TABLE_SIZE);
rc = msm_rpc_call_reply(endpoint,
ONCRPC_ALLOC_ACOUSTIC_MEM_PROC,
&req_smem, sizeof(req_smem),
&rep_smem, sizeof(rep_smem),
5 * HZ);
if (rep_smem.n != 0 || rc < 0) {
E("open failed: ALLOC_ACOUSTIC_MEM_PROC error %d.\n",
rc);
goto done;
}
htc_acoustic_vir_addr =
(uint32_t)smem_alloc(SMEM_ID_VENDOR1,
HTC_ACOUSTIC_TABLE_SIZE);
if (!htc_acoustic_vir_addr) {
E("open failed: smem_alloc error\n");
goto done;
}
}
rc = 0;
done:
mutex_unlock(&api_lock);
return rc;
}
static int acoustic_release(struct inode *inode, struct file *file)
{
D("release\n");
return 0;
}
static long acoustic_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int rc, reply_value;
struct set_acoustic_req req;
struct set_acoustic_rep rep;
D("ioctl\n");
mutex_lock(&api_lock);
switch (cmd) {
case ACOUSTIC_ARM11_DONE:
D("ioctl: ACOUSTIC_ARM11_DONE called %d.\n", current->pid);
rc = msm_rpc_call_reply(endpoint,
ONCRPC_ACOUSTIC_INIT_PROC, &req,
sizeof(req), &rep, sizeof(rep),
5 * HZ);
reply_value = be32_to_cpu(rep.n);
if (reply_value != 0 || rc < 0) {
E("ioctl failed: ONCRPC_ACOUSTIC_INIT_PROC "\
"error %d.\n", rc);
if (rc >= 0)
rc = -EIO;
break;
}
D("ioctl: ONCRPC_ACOUSTIC_INIT_PROC success.\n");
break;
default:
E("ioctl: invalid command\n");
rc = -EINVAL;
}
mutex_unlock(&api_lock);
return 0;
}
static struct file_operations acoustic_fops = {
.owner = THIS_MODULE,
.open = acoustic_open,
.release = acoustic_release,
.mmap = acoustic_mmap,
.unlocked_ioctl = acoustic_ioctl,
};
static struct miscdevice acoustic_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "htc-acoustic",
.fops = &acoustic_fops,
};
static int __init acoustic_init(void)
{
mutex_init(&api_lock);
mutex_init(&rpc_connect_mutex);
return misc_register(&acoustic_misc);
}
static void __exit acoustic_exit(void)
{
misc_deregister(&acoustic_misc);
}
module_init(acoustic_init);
module_exit(acoustic_exit);
MODULE_AUTHOR("Laurence Chen <Laurence_Chen@htc.com>");
MODULE_DESCRIPTION("HTC acoustic driver");
MODULE_LICENSE("GPL");