blob: f51bb0f288ecb54a5ea78ffaf79d45d1025d0adf [file] [log] [blame]
/*
* rt_codec_ioctl.h -- RT56XX ALSA SoC audio driver IO control
*
* Copyright 2012 Realtek Microelectronics
* Author: Bard <bardliao@realtek.com>
*
* 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.
*/
#define DEBUG 1
#include <linux/spi/spi.h>
#include <sound/soc.h>
#include "rt_codec_ioctl.h"
static struct rt_codec_ops rt_codec_ioctl_ops;
#if defined(CONFIG_SND_HWDEP) || defined(CONFIG_SND_HWDEP_MODULE)
#define RT_CE_CODEC_HWDEP_NAME "rt_codec hwdep "
static int rt_codec_hwdep_open(struct snd_hwdep *hw, struct file *file)
{
struct snd_soc_codec *codec = hw->private_data;
dev_dbg(codec->dev, "%s()\n", __func__);
return 0;
}
static int rt_codec_hwdep_release(struct snd_hwdep *hw, struct file *file)
{
struct snd_soc_codec *codec = hw->private_data;
dev_dbg(codec->dev, "%s()\n", __func__);
return 0;
}
static int rt_codec_hwdep_ioctl_common(struct snd_hwdep *hw,
struct file *file, unsigned int cmd, unsigned long arg)
{
struct snd_soc_codec *codec = hw->private_data;
struct rt_codec_cmd __user *_rt_codec = (struct rt_codec_cmd *)arg;
struct rt_codec_cmd rt_codec;
int *buf, *p;
if (copy_from_user(&rt_codec, _rt_codec, sizeof(rt_codec))) {
dev_err(codec->dev,"copy_from_user faild\n");
return -EFAULT;
}
dev_dbg(codec->dev, "%s(): rt_codec.number=%d, cmd=%d\n",
__func__, rt_codec.number, cmd);
buf = kmalloc(sizeof(*buf) * rt_codec.number, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
if (copy_from_user(buf, rt_codec.buf, sizeof(*buf) * rt_codec.number)) {
goto err;
}
switch (cmd) {
case RT_READ_CODEC_REG_IOCTL:
for (p = buf; p < buf + rt_codec.number / 2; p++) {
*(p + rt_codec.number / 2) = snd_soc_read(codec, *p);
}
if (copy_to_user(rt_codec.buf, buf, sizeof(*buf) * rt_codec.number))
goto err;
break;
case RT_WRITE_CODEC_REG_IOCTL:
for (p = buf; p < buf + rt_codec.number / 2; p++)
snd_soc_write(codec, *p, *(p + rt_codec.number / 2));
break;
case RT_READ_CODEC_INDEX_IOCTL:
if (NULL == rt_codec_ioctl_ops.index_read)
goto err;
for (p = buf; p < buf + rt_codec.number / 2; p++)
*(p+rt_codec.number/2) = rt_codec_ioctl_ops.index_read(
codec, *p);
if (copy_to_user(rt_codec.buf, buf,
sizeof(*buf) * rt_codec.number))
goto err;
break;
case RT_WRITE_CODEC_INDEX_IOCTL:
if (NULL == rt_codec_ioctl_ops.index_write)
goto err;
for (p = buf; p < buf + rt_codec.number / 2; p++)
{
dev_dbg(codec->dev, "%x , %x\n",
*p,*(p+rt_codec.number/2));
rt_codec_ioctl_ops.index_write(codec, *p,
*(p+rt_codec.number/2));
}
break;
default:
if (NULL == rt_codec_ioctl_ops.ioctl_common)
goto err;
rt_codec_ioctl_ops.ioctl_common(hw, file, cmd, arg);
break;
}
kfree(buf);
return 0;
err:
kfree(buf);
return -EFAULT;
}
static int rt_codec_codec_dump_reg(struct snd_hwdep *hw,
struct file *file, unsigned long arg)
{
struct snd_soc_codec *codec = hw->private_data;
struct rt_codec_cmd __user *_rt_codec =(struct rt_codec_cmd *)arg;
struct rt_codec_cmd rt_codec;
int i, *buf, number = codec->driver->reg_cache_size;
dev_dbg(codec->dev, "enter %s, number = %d\n", __func__, number);
if (copy_from_user(&rt_codec, _rt_codec, sizeof(rt_codec)))
return -EFAULT;
buf = kmalloc(sizeof(*buf) * number, GFP_KERNEL);
if (buf == NULL)
return -ENOMEM;
for (i = 0; i < number/2; i++) {
buf[i] = i << 1;
buf[i + number / 2] = codec->read(codec, buf[i]);
}
if (copy_to_user(rt_codec.buf, buf, sizeof(*buf) * i))
goto err;
rt_codec.number = number;
if (copy_to_user(_rt_codec, &rt_codec, sizeof(rt_codec)))
goto err;
kfree(buf);
return 0;
err:
kfree(buf);
return -EFAULT;
}
static int rt_codec_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case RT_READ_ALL_CODEC_REG_IOCTL:
return rt_codec_codec_dump_reg(hw, file, arg);
default:
return rt_codec_hwdep_ioctl_common(hw, file, cmd, arg);
}
return 0;
}
int realtek_ce_init_hwdep(struct snd_soc_codec *codec)
{
struct snd_hwdep *hw;
struct snd_card *card = codec->card->snd_card;
int err;
dev_dbg(codec->dev, "enter %s\n", __func__);
if ((err = snd_hwdep_new(card, RT_CE_CODEC_HWDEP_NAME, 0, &hw)) < 0)
return err;
strcpy(hw->name, RT_CE_CODEC_HWDEP_NAME);
hw->private_data = codec;
hw->ops.open = rt_codec_hwdep_open;
hw->ops.release = rt_codec_hwdep_release;
hw->ops.ioctl = rt_codec_hwdep_ioctl;
return 0;
}
EXPORT_SYMBOL_GPL(realtek_ce_init_hwdep);
#endif
struct rt_codec_ops *rt_codec_get_ioctl_ops(void)
{
return &rt_codec_ioctl_ops;
}
EXPORT_SYMBOL_GPL(rt_codec_get_ioctl_ops);