| /* |
| * 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); |