[alsa-devel] [PATCH 18/19] ALSA: ymu831: add hwdep ioctl

Yoichi Yuasa yuasa at linux-mips.org
Wed Jan 16 09:44:29 CET 2013


Signed-off-by: Yoichi Yuasa <yuasa at linux-mips.org>
---
 include/uapi/sound/asound.h           |    3 +-
 include/uapi/sound/ymu831.h           |   81 +++++
 sound/soc/codecs/ymu831/ymu831.c      |  585 +++++++++++++++++++++++++++++++++
 sound/soc/codecs/ymu831/ymu831_priv.h |    5 +-
 4 files changed, 672 insertions(+), 2 deletions(-)
 create mode 100644 include/uapi/sound/ymu831.h

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 1774a5c..e13b05e 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -93,9 +93,10 @@ enum {
 	SNDRV_HWDEP_IFACE_SB_RC,	/* SB Extigy/Audigy2NX remote control */
 	SNDRV_HWDEP_IFACE_HDA,		/* HD-audio */
 	SNDRV_HWDEP_IFACE_USB_STREAM,	/* direct access to usb stream */
+	SNDRV_HWDEP_IFACE_YMU831,	/* Yamaha YMU831 */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_YMU831
 };
 
 struct snd_hwdep_info {
diff --git a/include/uapi/sound/ymu831.h b/include/uapi/sound/ymu831.h
new file mode 100644
index 0000000..d5c22b3
--- /dev/null
+++ b/include/uapi/sound/ymu831.h
@@ -0,0 +1,81 @@
+/*
+ * YMU831 ASoC codec driver
+ *
+ * Copyright (c) 2012 Yamaha Corporation
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty.  In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ *    claim that you wrote the original software. If you use this software
+ *    in a product, an acknowledgment in the product documentation would be
+ *    appreciated but is not required.
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ *    misrepresented as being the original software.
+ * 3. This notice may not be removed or altered from any source distribution.
+ */
+/*
+ * changelog:
+ * - split from sound/soc/codecs/ymu831/ymu831.h
+ */
+#ifndef _UAPI__SOUND_YMU831_H
+#define _UAPI__SOUND_YMU831_H
+
+#define MC_ASOC_MAGIC			'N'
+#define MC_ASOC_IOCTL_SET_CTRL		1
+#define MC_ASOC_IOCTL_NOTIFY_HOLD	4
+
+struct ymc_ctrl_args {
+	void *param;
+	unsigned long size;
+	unsigned long option;
+};
+
+#define YMC_IOCTL_SET_CTRL \
+	_IOW(MC_ASOC_MAGIC, MC_ASOC_IOCTL_SET_CTRL, struct ymc_ctrl_args)
+
+#define YMC_IOCTL_NOTIFY_HOLD \
+	_IOWR(MC_ASOC_MAGIC, MC_ASOC_IOCTL_NOTIFY_HOLD, unsigned long)
+
+#define YMC_DSP_OUTPUT_BASE		0x00000000
+#define YMC_DSP_OUTPUT_SP		0x00000001
+#define YMC_DSP_OUTPUT_RC		0x00000002
+#define YMC_DSP_OUTPUT_HP		0x00000003
+#define YMC_DSP_OUTPUT_LO1		0x00000004
+#define YMC_DSP_OUTPUT_LO2		0x00000005
+#define YMC_DSP_OUTPUT_BT		0x00000006
+
+#define YMC_DSP_INPUT_BASE		0x00000010
+#define YMC_DSP_INPUT_MAINMIC		0x00000020
+#define YMC_DSP_INPUT_SUBMIC		0x00000030
+#define YMC_DSP_INPUT_2MIC		0x00000040
+#define YMC_DSP_INPUT_HEADSET		0x00000050
+#define YMC_DSP_INPUT_BT		0x00000060
+#define YMC_DSP_INPUT_LINEIN1		0x00000070
+#define YMC_DSP_INPUT_LINEIN2		0x00000080
+
+#define YMC_DSP_VOICECALL_BASE_1MIC	0x00000100
+#define YMC_DSP_VOICECALL_BASE_2MIC	0x00000200
+#define YMC_DSP_VOICECALL_SP_1MIC	0x00000300
+#define YMC_DSP_VOICECALL_SP_2MIC	0x00000400
+#define YMC_DSP_VOICECALL_RC_1MIC	0x00000500
+#define YMC_DSP_VOICECALL_RC_2MIC	0x00000600
+#define YMC_DSP_VOICECALL_HP_1MIC	0x00000700
+#define YMC_DSP_VOICECALL_HP_2MIC	0x00000800
+#define YMC_DSP_VOICECALL_LO1_1MIC	0x00000900
+#define YMC_DSP_VOICECALL_LO1_2MIC	0x00000a00
+#define YMC_DSP_VOICECALL_LO2_1MIC	0x00000b00
+#define YMC_DSP_VOICECALL_LO2_2MIC	0x00000c00
+#define YMC_DSP_VOICECALL_HEADSET	0x00000d00
+#define YMC_DSP_VOICECALL_BT		0x00000e00
+#define YMC_DSP_VOICECALL_BASE_COMMON	0x00000f00
+
+#define YMC_NOTITY_HOLD_OFF		0
+#define YMC_NOTITY_HOLD_ON		1
+
+#endif /* _UAPI__SOUND_YMU831_H */
diff --git a/sound/soc/codecs/ymu831/ymu831.c b/sound/soc/codecs/ymu831/ymu831.c
index 62c4a90..f6a32ce 100644
--- a/sound/soc/codecs/ymu831/ymu831.c
+++ b/sound/soc/codecs/ymu831/ymu831.c
@@ -46,6 +46,8 @@
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 
+#include <uapi/sound/ymu831.h>
+
 #include "mcdevif.h"
 #include "mcdriver.h"
 #include "ymu831.h"
@@ -62,6 +64,8 @@
 				 SNDRV_PCM_FMTBIT_S20_3LE |	\
 				 SNDRV_PCM_FMTBIT_S24_3LE)
 
+#define MC_ASOC_HWDEP_ID	MC_ASOC_NAME
+
 #define get_port_id(id)		(id - 1)
 
 #define PORT_MUSIC		0
@@ -129,6 +133,7 @@ static const struct mc_asoc_info_store info_store_tbl[] = {
 struct snd_soc_codec *mc_asoc_codec;
 
 static u8 mc_asoc_ver_id = 1;
+static u8 mc_asoc_hold = YMC_NOTITY_HOLD_OFF;
 static bool mc_asoc_suspended;
 static u8 mc_asoc_hpimpclass = 0xff;
 static u8 mc_asoc_jack_status;
@@ -3238,6 +3243,33 @@ static void auto_powerdown(struct snd_soc_codec *codec)
 #endif
 }
 
+static int add_dsp_param(struct mc_asoc_data *mc_asoc, int i, int j,
+			 u8 *param, u32 size)
+{
+	struct mc_asoc_dsp_params *dsp_params, *next;
+
+	dsp_params = &mc_asoc->params_store[i][j];
+	while (dsp_params->pab_param) {
+		if (!dsp_params->next) {
+			next = kzalloc(sizeof(struct mc_asoc_dsp_params),
+				       GFP_KERNEL);
+			if (!next)
+				return -ENOMEM;
+
+			dsp_params->next = next;
+			dsp_params = dsp_params->next;
+			break;
+		}
+
+		dsp_params = dsp_params->next;
+	}
+
+	dsp_params->pab_param = param;
+	dsp_params->size = size;
+
+	return 0;
+}
+
 static void del_dsp_param(struct mc_asoc_data *mc_asoc)
 {
 	struct mc_asoc_dsp_params *dsp_params, *next;
@@ -3287,6 +3319,9 @@ static int set_audio_mode_play(struct snd_soc_codec *codec, unsigned int value)
 	if (!value)
 		del_dsp_param(mc_asoc);
 
+	if (mc_asoc_hold == YMC_NOTITY_HOLD_ON)
+		return 0;
+
 	ret = connect_path(codec);
 	if (!value)
 		auto_powerdown(codec);
@@ -3319,6 +3354,9 @@ static int set_audio_mode_cap(struct snd_soc_codec *codec, unsigned int value)
 	if (!value)
 		del_dsp_param(mc_asoc);
 
+	if (mc_asoc_hold == YMC_NOTITY_HOLD_ON)
+		return 0;
+
 	ret = connect_path(codec);
 	if (!value)
 		auto_powerdown(codec);
@@ -3361,6 +3399,9 @@ static int set_incall_mic(struct snd_soc_codec *codec,
 		dsp_params = dsp_params->next;
 	}
 
+	if (mc_asoc_hold == YMC_NOTITY_HOLD_ON)
+		return 0;
+
 	return connect_path(codec);
 }
 
@@ -3395,6 +3436,9 @@ static int set_ain_playback(struct snd_soc_codec *codec,
 	    && audio_mode_cap == MC_ASOC_AUDIO_MODE_INCOMM)
 		return 0;
 
+	if (mc_asoc_hold == YMC_NOTITY_HOLD_ON)
+		return 0;
+
 	ret = connect_path(codec);
 	if (!value)
 		auto_powerdown(codec);
@@ -3411,6 +3455,9 @@ static int set_dtmf_control(struct snd_soc_codec *codec,
 	if (ret < 0)
 		return ret;
 
+	if (mc_asoc_hold == YMC_NOTITY_HOLD_ON)
+		return 0;
+
 	ret = connect_path(codec);
 	if (!value)
 		auto_powerdown(codec);
@@ -3427,6 +3474,9 @@ static int set_dtmf_output(struct snd_soc_codec *codec,
 	if (ret < 0)
 		return ret;
 
+	if (mc_asoc_hold == YMC_NOTITY_HOLD_ON)
+		return 0;
+
 	return connect_path(codec);
 }
 
@@ -4233,6 +4283,535 @@ static int mc_asoc_add_widgets(struct snd_soc_codec *codec)
 	return snd_soc_dapm_new_widgets(&codec->dapm);
 }
 
+static int mc_asoc_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+			       unsigned int cmd, unsigned long arg)
+{
+	struct snd_soc_codec *codec;
+	struct mc_asoc_data *mc_asoc;
+	struct ymc_ctrl_args ymc_ctrl_arg;
+	u32 hold;
+	u8 *param;
+	int output_path, input_path, incall_mic;
+	int err = 0;
+
+	if (!hw)
+		return -EINVAL;
+
+	codec = hw->private_data;
+	if (!codec)
+		return -EINVAL;
+
+	mc_asoc = snd_soc_codec_get_drvdata(codec);
+	if (!mc_asoc)
+		return -EINVAL;
+
+	output_path = read_cache(codec, MC_ASOC_OUTPUT_PATH);
+	if (output_path < 0)
+		return -EIO;
+
+	input_path = read_cache(codec, MC_ASOC_INPUT_PATH);
+	if (input_path < 0)
+		return -EIO;
+
+	incall_mic = get_incall_mic(codec, output_path);
+	if (incall_mic < 0)
+		return -EIO;
+
+	switch (cmd) {
+	case YMC_IOCTL_SET_CTRL:
+		if (!access_ok(VERIFY_READ,
+			       (struct ymc_ctrl_args *)arg,
+			       sizeof(struct ymc_ctrl_args)))
+			return -EFAULT;
+		if (copy_from_user(&ymc_ctrl_arg,
+				   (struct ymc_ctrl_args *)arg,
+				   sizeof(struct ymc_ctrl_args))) {
+			return -EFAULT;
+		}
+
+		if (ymc_ctrl_arg.size == 0)
+			break;
+		if (ymc_ctrl_arg.size > MAX_YMS_CTRL_PARAM_SIZE)
+			return -ENOMEM;
+
+		param = kzalloc(ymc_ctrl_arg.size, GFP_KERNEL);
+		if (!param)
+			return -ENOMEM;
+
+		if (copy_from_user(param, ymc_ctrl_arg.param,
+				   ymc_ctrl_arg.size)) {
+			err = -EFAULT;
+			goto error;
+		}
+
+		switch (ymc_ctrl_arg.option) {
+		case YMC_DSP_OUTPUT_BASE:
+			err = add_dsp_param(mc_asoc, DSP_PRM_OUTPUT,
+					    DSP_PRM_BASE, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_INPUT_BASE:
+			err = add_dsp_param(mc_asoc, DSP_PRM_INPUT,
+					    DSP_PRM_BASE, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_VOICECALL_BASE_COMMON:
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_1MIC,
+					    DSP_PRM_BASE, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			param = kzalloc(ymc_ctrl_arg.size, GFP_KERNEL);
+			if (!param) {
+				err = -ENOMEM;
+				goto error;
+			}
+
+			if (copy_from_user(param, ymc_ctrl_arg.param,
+					   ymc_ctrl_arg.size)) {
+				err = -EFAULT;
+				goto error;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_2MIC,
+					    DSP_PRM_BASE, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_VOICECALL_BASE_1MIC:
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_1MIC,
+					    DSP_PRM_BASE, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_MAINMIC
+			    && incall_mic != MC_ASOC_INCALL_MIC_SUBMIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_BASE_2MIC:
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_2MIC,
+					    DSP_PRM_BASE, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_2MIC)
+				goto exit;
+			break;
+		case YMC_DSP_OUTPUT_SP:
+			if (output_path != MC_ASOC_OUTPUT_PATH_SP) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_OUTPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_OUTPUT_RC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_RC) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_OUTPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_OUTPUT_HP:
+			if (output_path != MC_ASOC_OUTPUT_PATH_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_HS
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_HP) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_OUTPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_OUTPUT_LO1:
+			if (output_path != MC_ASOC_OUTPUT_PATH_LO1
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_LO1
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_LO1) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_OUTPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_OUTPUT_LO2:
+			if (output_path != MC_ASOC_OUTPUT_PATH_LO2
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_LO2
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_LO2) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_OUTPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_OUTPUT_BT:
+			if (output_path != MC_ASOC_OUTPUT_PATH_BT
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_BT
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_BT
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_BT) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_OUTPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_INPUT_MAINMIC:
+			if (input_path != MC_ASOC_INPUT_PATH_MAINMIC) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_INPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_INPUT_SUBMIC:
+			if (input_path != MC_ASOC_INPUT_PATH_SUBMIC) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_INPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_INPUT_2MIC:
+			if (input_path != MC_ASOC_INPUT_PATH_2MIC) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_INPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_INPUT_HEADSET:
+			if (input_path != MC_ASOC_INPUT_PATH_HS) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_INPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_INPUT_BT:
+			if (input_path != MC_ASOC_INPUT_PATH_BT) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_INPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_INPUT_LINEIN1:
+			if (input_path != MC_ASOC_INPUT_PATH_LIN1) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_INPUT,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			break;
+		case YMC_DSP_VOICECALL_SP_1MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_SP) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_1MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+			if (incall_mic != MC_ASOC_INCALL_MIC_MAINMIC
+			    && incall_mic != MC_ASOC_INCALL_MIC_SUBMIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_RC_1MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_RC) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_1MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_MAINMIC
+			    && incall_mic != MC_ASOC_INCALL_MIC_SUBMIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_HP_1MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_HP) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_1MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_MAINMIC
+			    && incall_mic != MC_ASOC_INCALL_MIC_SUBMIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_LO1_1MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_LO1
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_LO1
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_LO1) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_1MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_MAINMIC
+			    && incall_mic != MC_ASOC_INCALL_MIC_SUBMIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_LO2_1MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_LO2
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_LO2
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_LO2) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_1MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_MAINMIC
+			    && incall_mic != MC_ASOC_INCALL_MIC_SUBMIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_HEADSET:
+			if (output_path != MC_ASOC_OUTPUT_PATH_HS) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_1MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_MAINMIC
+			    && incall_mic != MC_ASOC_INCALL_MIC_SUBMIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_BT:
+			if (output_path != MC_ASOC_OUTPUT_PATH_BT
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_BT
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_BT
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_BT) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_2MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_MAINMIC
+			    && incall_mic != MC_ASOC_INCALL_MIC_SUBMIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_SP_2MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_SP) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_2MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_2MIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_RC_2MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_RC
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_RC) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_2MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_2MIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_HP_2MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_HS
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_HP
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_HP) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_2MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_2MIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_LO1_2MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_LO1
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_LO1
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO2_LO1) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_2MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_2MIC)
+				goto exit;
+			break;
+		case YMC_DSP_VOICECALL_LO2_2MIC:
+			if (output_path != MC_ASOC_OUTPUT_PATH_LO2
+			    && output_path != MC_ASOC_OUTPUT_PATH_SP_LO2
+			    && output_path != MC_ASOC_OUTPUT_PATH_LO1_LO2) {
+				kfree(param);
+				goto exit;
+			}
+
+			err = add_dsp_param(mc_asoc, DSP_PRM_VC_2MIC,
+					    DSP_PRM_USER, param,
+					    ymc_ctrl_arg.size);
+			if (err)
+				goto error;
+
+			if (incall_mic != MC_ASOC_INCALL_MIC_2MIC)
+				goto exit;
+			break;
+		default:
+			err = -EINVAL;
+			break;
+		}
+
+		if (err)
+			goto error;
+
+		err =
+		    mc_control(MCDRV_SET_DSP, (unsigned long)param,
+			       ymc_ctrl_arg.size);
+		break;
+	case YMC_IOCTL_NOTIFY_HOLD:
+		if (!access_ok(VERIFY_READ, (u32 *) arg, sizeof(u32)))
+			return -EFAULT;
+		if (copy_from_user(&hold, (u32 *) arg, sizeof(u32))) {
+			err = -EFAULT;
+			break;
+		}
+
+		switch (hold) {
+		case YMC_NOTITY_HOLD_OFF:
+			err = connect_path(codec);
+			if (!err)
+				auto_powerdown(codec);
+		case YMC_NOTITY_HOLD_ON:
+			mc_asoc_hold = hold;
+			break;
+		default:
+			err = -EINVAL;
+			break;
+		}
+		break;
+	default:
+		err = -EINVAL;
+	}
+exit:
+	return err;
+
+error:
+	kfree(param);
+
+	return err;
+}
+
+static int mc_asoc_add_hwdep(struct snd_soc_codec *codec)
+{
+	struct snd_hwdep *hw;
+	struct mc_asoc_data *mc_asoc;
+	int err;
+
+	mc_asoc = snd_soc_codec_get_drvdata(codec);
+	if (!mc_asoc)
+		return -EINVAL;
+
+	err = snd_hwdep_new(codec->card->snd_card, MC_ASOC_HWDEP_ID, 0, &hw);
+	if (err < 0)
+		return err;
+
+	hw->iface = SNDRV_HWDEP_IFACE_YMU831;
+	hw->private_data = codec;
+	hw->ops.ioctl = mc_asoc_hwdep_ioctl;
+	hw->exclusive = 1;
+	strcpy(hw->name, MC_ASOC_HWDEP_ID);
+	mc_asoc->hwdep = hw;
+
+	return 0;
+}
+
 static struct input_dev *inp_dev;
 
 static struct snd_soc_jack hs_jack;
@@ -4674,6 +5253,12 @@ static int mc_asoc_probe(struct snd_soc_codec *codec)
 		goto error;
 	}
 
+	err = mc_asoc_add_hwdep(codec);
+	if (err < 0) {
+		dev_err(dev, "Error in mc_asoc_add_hwdep: %d\n", err);
+		goto error;
+	}
+
 	snd_soc_cache_write(codec, MC_ASOC_EXT_MASTERSLAVE,
 			    ext_port_def.dio_common.master_slave);
 	snd_soc_cache_write(codec, MC_ASOC_EXT_RATE,
diff --git a/sound/soc/codecs/ymu831/ymu831_priv.h b/sound/soc/codecs/ymu831/ymu831_priv.h
index 10bce05..36ee29b 100644
--- a/sound/soc/codecs/ymu831/ymu831_priv.h
+++ b/sound/soc/codecs/ymu831/ymu831_priv.h
@@ -30,6 +30,8 @@
 
 #include <linux/spi/spi.h>
 
+#include <sound/hwdep.h>
+
 #include "mcdriver.h"
 
 #undef MC_ASOC_TEST
@@ -173,7 +175,7 @@ enum {
 	MC_ASOC_MIC2_BIAS,
 	MC_ASOC_MIC3_BIAS,
 	MC_ASOC_MIC4_BIAS,
-#endif
+#endif /* MC_ASOC_TEST */
 
 	MC_ASOC_N_REG
 };
@@ -273,6 +275,7 @@ struct mc_asoc_data {
 	struct mcdrv_swap_info swap_store;
 	struct mcdrv_hsdet_info hsdet_store;
 	struct mc_asoc_dsp_params params_store[4][2];
+	struct snd_hwdep *hwdep;
 };
 
 #endif /* _YMU831_PRIV_H */
-- 
1.7.9.5



More information about the Alsa-devel mailing list