mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1093 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1093 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 3f5439d2371964db871b6c0a9e10067277d4dd16 Mon Sep 17 00:00:00 2001
 | 
						|
From: Raashid Muhammed <raashidmuhammed@zilogic.com>
 | 
						|
Date: Mon, 27 Mar 2017 12:35:00 +0530
 | 
						|
Subject: [PATCH 080/277] Add support for Allo Piano DAC 2.1 plus add-on board
 | 
						|
 for Raspberry Pi.
 | 
						|
 | 
						|
The Piano DAC 2.1 has support for 4 channels with subwoofer.
 | 
						|
 | 
						|
Signed-off-by: Baswaraj K <jaikumar@cem-solutions.net>
 | 
						|
Reviewed-by: Vijay Kumar B. <vijaykumar@zilogic.com>
 | 
						|
Reviewed-by: Raashid Muhammed <raashidmuhammed@zilogic.com>
 | 
						|
 | 
						|
Add clock changes and mute gpios (#1938)
 | 
						|
 | 
						|
Also improve code style and adhere to ALSA coding conventions.
 | 
						|
 | 
						|
Signed-off-by: Baswaraj K <jaikumar@cem-solutions.net>
 | 
						|
Reviewed-by: Vijay Kumar B. <vijaykumar@zilogic.com>
 | 
						|
Reviewed-by: Raashid Muhammed <raashidmuhammed@zilogic.com>
 | 
						|
 | 
						|
PianoPlus: Dual Mono & Dual Stereo features added (#2069)
 | 
						|
 | 
						|
allo-piano-dac-plus: Master volume added + fixes
 | 
						|
 | 
						|
Master volume added, which controls both DACs volumes.
 | 
						|
 | 
						|
See: https://github.com/raspberrypi/linux/pull/2149
 | 
						|
 | 
						|
Also fix initial max volume, default mode value, and unmute.
 | 
						|
 | 
						|
Signed-off-by: allocom <sparky-dev@allo.com>
 | 
						|
---
 | 
						|
 sound/soc/bcm/Kconfig               |    7 +
 | 
						|
 sound/soc/bcm/Makefile              |    2 +
 | 
						|
 sound/soc/bcm/allo-piano-dac-plus.c | 1014 +++++++++++++++++++++++++++++++++++
 | 
						|
 3 files changed, 1023 insertions(+)
 | 
						|
 create mode 100644 sound/soc/bcm/allo-piano-dac-plus.c
 | 
						|
 | 
						|
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
 | 
						|
index b600cfb98ac9..58c36f2e7bb7 100644
 | 
						|
--- a/sound/soc/bcm/Kconfig
 | 
						|
+++ b/sound/soc/bcm/Kconfig
 | 
						|
@@ -131,3 +131,10 @@ config SND_BCM2708_SOC_ALLO_PIANO_DAC
 | 
						|
 	select SND_SOC_PCM512x_I2C
 | 
						|
 	help
 | 
						|
 	  Say Y or M if you want to add support for Allo Piano DAC.
 | 
						|
+
 | 
						|
+config SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS
 | 
						|
+	tristate "Support for Allo Piano DAC Plus"
 | 
						|
+	depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
 | 
						|
+	select SND_SOC_PCM512x_I2C
 | 
						|
+	help
 | 
						|
+	  Say Y or M if you want to add support for Allo Piano DAC Plus.
 | 
						|
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
 | 
						|
index 64f007f8ba38..023b2c17098b 100644
 | 
						|
--- a/sound/soc/bcm/Makefile
 | 
						|
+++ b/sound/soc/bcm/Makefile
 | 
						|
@@ -25,6 +25,7 @@ snd-soc-audioinjector-pi-soundcard-objs := audioinjector-pi-soundcard.o
 | 
						|
 snd-soc-digidac1-soundcard-objs := digidac1-soundcard.o
 | 
						|
 snd-soc-dionaudio-loco-objs := dionaudio_loco.o
 | 
						|
 snd-soc-allo-piano-dac-objs := allo-piano-dac.o
 | 
						|
+snd-soc-allo-piano-dac-plus-objs := allo-piano-dac-plus.o
 | 
						|
 
 | 
						|
 obj-$(CONFIG_SND_BCM2708_SOC_ADAU1977_ADC) += snd-soc-adau1977-adc.o
 | 
						|
 obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_AMP) += snd-soc-hifiberry-amp.o
 | 
						|
@@ -42,3 +43,4 @@ obj-$(CONFIG_SND_AUDIOINJECTOR_PI_SOUNDCARD) += snd-soc-audioinjector-pi-soundca
 | 
						|
 obj-$(CONFIG_SND_DIGIDAC1_SOUNDCARD) += snd-soc-digidac1-soundcard.o
 | 
						|
 obj-$(CONFIG_SND_BCM2708_SOC_DIONAUDIO_LOCO) += snd-soc-dionaudio-loco.o
 | 
						|
 obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC) += snd-soc-allo-piano-dac.o
 | 
						|
+obj-$(CONFIG_SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS) += snd-soc-allo-piano-dac-plus.o
 | 
						|
diff --git a/sound/soc/bcm/allo-piano-dac-plus.c b/sound/soc/bcm/allo-piano-dac-plus.c
 | 
						|
new file mode 100644
 | 
						|
index 000000000000..1800f5e4d414
 | 
						|
--- /dev/null
 | 
						|
+++ b/sound/soc/bcm/allo-piano-dac-plus.c
 | 
						|
@@ -0,0 +1,1014 @@
 | 
						|
+/*
 | 
						|
+ * ALSA ASoC Machine Driver for Allo Piano DAC Plus Subwoofer
 | 
						|
+ *
 | 
						|
+ * Author:	Baswaraj K <jaikumar@cem-solutions.net>
 | 
						|
+ *		Copyright 2016
 | 
						|
+ *		based on code by Daniel Matuschek <info@crazy-audio.com>
 | 
						|
+ *		based on code by Florian Meier <florian.meier@koalo.de>
 | 
						|
+ *
 | 
						|
+ * 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.
 | 
						|
+ */
 | 
						|
+
 | 
						|
+#include <linux/module.h>
 | 
						|
+#include <linux/platform_device.h>
 | 
						|
+#include <linux/gpio/consumer.h>
 | 
						|
+#include <sound/core.h>
 | 
						|
+#include <sound/pcm.h>
 | 
						|
+#include <sound/pcm_params.h>
 | 
						|
+#include <sound/soc.h>
 | 
						|
+#include <linux/firmware.h>
 | 
						|
+#include <linux/delay.h>
 | 
						|
+#include <sound/tlv.h>
 | 
						|
+#include "../codecs/pcm512x.h"
 | 
						|
+
 | 
						|
+#define P_DAC_LEFT_MUTE		0x10
 | 
						|
+#define P_DAC_RIGHT_MUTE	0x01
 | 
						|
+#define P_DAC_MUTE		0x11
 | 
						|
+#define P_DAC_UNMUTE		0x00
 | 
						|
+#define P_MUTE			1
 | 
						|
+#define P_UNMUTE		0
 | 
						|
+
 | 
						|
+struct dsp_code {
 | 
						|
+	char i2c_addr;
 | 
						|
+	char offset;
 | 
						|
+	char val;
 | 
						|
+};
 | 
						|
+
 | 
						|
+struct glb_pool {
 | 
						|
+	struct mutex lock;
 | 
						|
+	unsigned int dual_mode;
 | 
						|
+	unsigned int set_lowpass;
 | 
						|
+	unsigned int set_mode;
 | 
						|
+	unsigned int set_rate;
 | 
						|
+	unsigned int dsp_page_number;
 | 
						|
+};
 | 
						|
+
 | 
						|
+static bool digital_gain_0db_limit = true;
 | 
						|
+bool glb_mclk;
 | 
						|
+
 | 
						|
+static struct gpio_desc *mute_gpio[2];
 | 
						|
+
 | 
						|
+static const char * const allo_piano_mode_texts[] = {
 | 
						|
+	"None",
 | 
						|
+	"2.0",
 | 
						|
+	"2.1",
 | 
						|
+	"2.2",
 | 
						|
+};
 | 
						|
+
 | 
						|
+static const SOC_ENUM_SINGLE_DECL(allo_piano_mode_enum,
 | 
						|
+		0, 0, allo_piano_mode_texts);
 | 
						|
+
 | 
						|
+static const char * const allo_piano_dual_mode_texts[] = {
 | 
						|
+	"None",
 | 
						|
+	"Dual-Mono",
 | 
						|
+	"Dual-Stereo",
 | 
						|
+};
 | 
						|
+
 | 
						|
+static const SOC_ENUM_SINGLE_DECL(allo_piano_dual_mode_enum,
 | 
						|
+		0, 0, allo_piano_dual_mode_texts);
 | 
						|
+
 | 
						|
+static const char * const allo_piano_dsp_low_pass_texts[] = {
 | 
						|
+	"60",
 | 
						|
+	"70",
 | 
						|
+	"80",
 | 
						|
+	"90",
 | 
						|
+	"100",
 | 
						|
+	"110",
 | 
						|
+	"120",
 | 
						|
+	"130",
 | 
						|
+	"140",
 | 
						|
+	"150",
 | 
						|
+	"160",
 | 
						|
+	"170",
 | 
						|
+	"180",
 | 
						|
+	"190",
 | 
						|
+	"200",
 | 
						|
+};
 | 
						|
+
 | 
						|
+static const SOC_ENUM_SINGLE_DECL(allo_piano_enum,
 | 
						|
+		0, 0, allo_piano_dsp_low_pass_texts);
 | 
						|
+
 | 
						|
+static int __snd_allo_piano_dsp_program(struct snd_soc_pcm_runtime *rtd,
 | 
						|
+		unsigned int mode, unsigned int rate, unsigned int lowpass)
 | 
						|
+{
 | 
						|
+	const struct firmware *fw;
 | 
						|
+	struct snd_soc_card *card = rtd->card;
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	char firmware_name[60];
 | 
						|
+	int ret = 0, dac = 0;
 | 
						|
+
 | 
						|
+	if (rate <= 46000)
 | 
						|
+		rate = 44100;
 | 
						|
+	else if (rate <= 68000)
 | 
						|
+		rate = 48000;
 | 
						|
+	else if (rate <= 92000)
 | 
						|
+		rate = 88200;
 | 
						|
+	else if (rate <= 136000)
 | 
						|
+		rate = 96000;
 | 
						|
+	else if (rate <= 184000)
 | 
						|
+		rate = 176400;
 | 
						|
+	else
 | 
						|
+		rate = 192000;
 | 
						|
+
 | 
						|
+	if (lowpass > 14)
 | 
						|
+		glb_ptr->set_lowpass = lowpass = 0;
 | 
						|
+
 | 
						|
+	if (mode > 3)
 | 
						|
+		glb_ptr->set_mode = mode = 0;
 | 
						|
+
 | 
						|
+	if (mode > 0)
 | 
						|
+		glb_ptr->dual_mode = 0;
 | 
						|
+
 | 
						|
+	/* same configuration loaded */
 | 
						|
+	if ((rate == glb_ptr->set_rate) && (lowpass == glb_ptr->set_lowpass)
 | 
						|
+			&& (mode == glb_ptr->set_mode))
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	switch (mode) {
 | 
						|
+	case 0: /* None */
 | 
						|
+		return 1;
 | 
						|
+
 | 
						|
+	case 1: /* 2.0 */
 | 
						|
+		snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_MUTE, P_DAC_UNMUTE);
 | 
						|
+		snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_MUTE, P_DAC_MUTE);
 | 
						|
+		glb_ptr->set_rate = rate;
 | 
						|
+		glb_ptr->set_mode = mode;
 | 
						|
+		glb_ptr->set_lowpass = lowpass;
 | 
						|
+		return 1;
 | 
						|
+
 | 
						|
+	default:
 | 
						|
+		snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_MUTE, P_DAC_UNMUTE);
 | 
						|
+		snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_MUTE, P_DAC_UNMUTE);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	for (dac = 0; dac < rtd->num_codecs; dac++) {
 | 
						|
+		struct dsp_code *dsp_code_read;
 | 
						|
+		struct snd_soc_codec *codec = rtd->codec_dais[dac]->codec;
 | 
						|
+		int i = 1;
 | 
						|
+
 | 
						|
+		if (dac == 0) { /* high */
 | 
						|
+			snprintf(firmware_name, sizeof(firmware_name),
 | 
						|
+				"allo/piano/2.2/allo-piano-dsp-%d-%d-%d.bin",
 | 
						|
+				rate, ((lowpass * 10) + 60), dac);
 | 
						|
+		} else { /* low */
 | 
						|
+			snprintf(firmware_name, sizeof(firmware_name),
 | 
						|
+				"allo/piano/2.%d/allo-piano-dsp-%d-%d-%d.bin",
 | 
						|
+				(mode - 1), rate, ((lowpass * 10) + 60), dac);
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		dev_info(codec->dev, "Dsp Firmware File Name: %s\n",
 | 
						|
+				firmware_name);
 | 
						|
+
 | 
						|
+		ret = request_firmware(&fw, firmware_name, codec->dev);
 | 
						|
+		if (ret < 0) {
 | 
						|
+			dev_err(codec->dev,
 | 
						|
+				"Error: Allo Piano Firmware %s missing. %d\n",
 | 
						|
+				firmware_name, ret);
 | 
						|
+			goto err;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		while (i < (fw->size - 1)) {
 | 
						|
+			dsp_code_read = (struct dsp_code *)&fw->data[i];
 | 
						|
+
 | 
						|
+			if (dsp_code_read->offset == 0) {
 | 
						|
+				glb_ptr->dsp_page_number = dsp_code_read->val;
 | 
						|
+				ret = snd_soc_write(rtd->codec_dais[dac]->codec,
 | 
						|
+						PCM512x_PAGE_BASE(0),
 | 
						|
+						dsp_code_read->val);
 | 
						|
+
 | 
						|
+			} else if (dsp_code_read->offset != 0) {
 | 
						|
+				ret = snd_soc_write(rtd->codec_dais[dac]->codec,
 | 
						|
+					(PCM512x_PAGE_BASE(
 | 
						|
+						glb_ptr->dsp_page_number) +
 | 
						|
+					dsp_code_read->offset),
 | 
						|
+					dsp_code_read->val);
 | 
						|
+			}
 | 
						|
+			if (ret < 0) {
 | 
						|
+				dev_err(codec->dev,
 | 
						|
+					"Failed to write Register: %d\n", ret);
 | 
						|
+				release_firmware(fw);
 | 
						|
+				goto err;
 | 
						|
+			}
 | 
						|
+			i = i + 3;
 | 
						|
+		}
 | 
						|
+		release_firmware(fw);
 | 
						|
+	}
 | 
						|
+	glb_ptr->set_rate = rate;
 | 
						|
+	glb_ptr->set_mode = mode;
 | 
						|
+	glb_ptr->set_lowpass = lowpass;
 | 
						|
+	return 1;
 | 
						|
+
 | 
						|
+err:
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dsp_program(struct snd_soc_pcm_runtime *rtd,
 | 
						|
+		unsigned int mode, unsigned int rate, unsigned int lowpass)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = rtd->card;
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	int ret = 0;
 | 
						|
+
 | 
						|
+	mutex_lock(&glb_ptr->lock);
 | 
						|
+
 | 
						|
+	ret = __snd_allo_piano_dsp_program(rtd, mode, rate, lowpass);
 | 
						|
+
 | 
						|
+	mutex_unlock(&glb_ptr->lock);
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dual_mode_get(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+
 | 
						|
+	ucontrol->value.integer.value[0] = glb_ptr->dual_mode;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dual_mode_put(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	struct snd_card *snd_card_ptr = card->snd_card;
 | 
						|
+	struct snd_kcontrol *kctl;
 | 
						|
+	struct soc_mixer_control *mc;
 | 
						|
+	unsigned int left_val = 0, right_val = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+
 | 
						|
+	if (ucontrol->value.integer.value[0] > 0) {
 | 
						|
+		glb_ptr->dual_mode = ucontrol->value.integer.value[0];
 | 
						|
+		glb_ptr->set_mode = 0;
 | 
						|
+	} else {
 | 
						|
+		if (glb_ptr->set_mode <= 0) {
 | 
						|
+			glb_ptr->dual_mode = 1;
 | 
						|
+			glb_ptr->set_mode = 0;
 | 
						|
+		} else {
 | 
						|
+			glb_ptr->dual_mode = 0;
 | 
						|
+			return 0;
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (glb_ptr->dual_mode == 1) { // Dual Mono
 | 
						|
+		snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_MUTE, P_DAC_RIGHT_MUTE);
 | 
						|
+		snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_MUTE, P_DAC_LEFT_MUTE);
 | 
						|
+		snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_3, 0xff);
 | 
						|
+		snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_2, 0xff);
 | 
						|
+
 | 
						|
+		list_for_each_entry(kctl, &snd_card_ptr->controls, list) {
 | 
						|
+			if (!strncmp(kctl->id.name, "Digital Playback Volume",
 | 
						|
+					sizeof(kctl->id.name))) {
 | 
						|
+				mc = (struct soc_mixer_control *)
 | 
						|
+					kctl->private_value;
 | 
						|
+				mc->rreg = mc->reg;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+	} else {
 | 
						|
+		left_val = snd_soc_read(rtd->codec_dais[0]->codec,
 | 
						|
+						PCM512x_DIGITAL_VOLUME_2);
 | 
						|
+		right_val = snd_soc_read(rtd->codec_dais[1]->codec,
 | 
						|
+						PCM512x_DIGITAL_VOLUME_3);
 | 
						|
+
 | 
						|
+		list_for_each_entry(kctl, &snd_card_ptr->controls, list) {
 | 
						|
+			if (!strncmp(kctl->id.name, "Digital Playback Volume",
 | 
						|
+					sizeof(kctl->id.name))) {
 | 
						|
+				mc = (struct soc_mixer_control *)
 | 
						|
+					kctl->private_value;
 | 
						|
+				mc->rreg = PCM512x_DIGITAL_VOLUME_3;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_3, left_val);
 | 
						|
+		snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_2, right_val);
 | 
						|
+		snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_MUTE, P_DAC_UNMUTE);
 | 
						|
+		snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_MUTE, P_DAC_UNMUTE);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_mode_get(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+
 | 
						|
+	ucontrol->value.integer.value[0] = glb_ptr->set_mode;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_mode_put(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	struct snd_card *snd_card_ptr = card->snd_card;
 | 
						|
+	struct snd_kcontrol *kctl;
 | 
						|
+	struct soc_mixer_control *mc;
 | 
						|
+	unsigned int left_val = 0, right_val = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+
 | 
						|
+	if ((glb_ptr->dual_mode == 1) &&
 | 
						|
+			(ucontrol->value.integer.value[0] > 0)) {
 | 
						|
+		left_val = snd_soc_read(rtd->codec_dais[0]->codec,
 | 
						|
+						PCM512x_DIGITAL_VOLUME_2);
 | 
						|
+		right_val = snd_soc_read(rtd->codec_dais[1]->codec,
 | 
						|
+						PCM512x_DIGITAL_VOLUME_2);
 | 
						|
+
 | 
						|
+		list_for_each_entry(kctl, &snd_card_ptr->controls, list) {
 | 
						|
+			if (!strncmp(kctl->id.name, "Digital Playback Volume",
 | 
						|
+					sizeof(kctl->id.name))) {
 | 
						|
+				mc = (struct soc_mixer_control *)
 | 
						|
+					kctl->private_value;
 | 
						|
+				mc->rreg = PCM512x_DIGITAL_VOLUME_3;
 | 
						|
+				break;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+		snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_3, left_val);
 | 
						|
+		snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_3, right_val);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return(snd_allo_piano_dsp_program(rtd,
 | 
						|
+				ucontrol->value.integer.value[0],
 | 
						|
+				glb_ptr->set_rate, glb_ptr->set_lowpass));
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_lowpass_get(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+
 | 
						|
+	ucontrol->value.integer.value[0] = glb_ptr->set_lowpass;
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_lowpass_put(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+	return(snd_allo_piano_dsp_program(rtd,
 | 
						|
+				glb_ptr->set_mode, glb_ptr->set_rate,
 | 
						|
+				ucontrol->value.integer.value[0]));
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pcm512x_get_reg_sub(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct soc_mixer_control *mc =
 | 
						|
+		(struct soc_mixer_control *)kcontrol->private_value;
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	unsigned int left_val = 0;
 | 
						|
+	unsigned int right_val = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+	right_val = snd_soc_read(rtd->codec_dais[1]->codec,
 | 
						|
+			PCM512x_DIGITAL_VOLUME_3);
 | 
						|
+	if (right_val < 0)
 | 
						|
+		return right_val;
 | 
						|
+
 | 
						|
+	if (glb_ptr->dual_mode != 1) {
 | 
						|
+		left_val = snd_soc_read(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_2);
 | 
						|
+		if (left_val < 0)
 | 
						|
+			return left_val;
 | 
						|
+
 | 
						|
+	} else {
 | 
						|
+		left_val = right_val;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ucontrol->value.integer.value[0] =
 | 
						|
+				(~(left_val >> mc->shift)) & mc->max;
 | 
						|
+	ucontrol->value.integer.value[1] =
 | 
						|
+				(~(right_val >> mc->shift)) & mc->max;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pcm512x_set_reg_sub(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct soc_mixer_control *mc =
 | 
						|
+		(struct soc_mixer_control *)kcontrol->private_value;
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	unsigned int left_val = (ucontrol->value.integer.value[0] & mc->max);
 | 
						|
+	unsigned int right_val = (ucontrol->value.integer.value[1] & mc->max);
 | 
						|
+	int ret = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+	if (glb_ptr->dual_mode != 1) {
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_2, (~left_val));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (digital_gain_0db_limit) {
 | 
						|
+		ret = snd_soc_limit_volume(card, "Subwoofer Playback Volume",
 | 
						|
+					207);
 | 
						|
+		if (ret < 0)
 | 
						|
+			dev_warn(card->dev, "Failed to set volume limit: %d\n",
 | 
						|
+				ret);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+			PCM512x_DIGITAL_VOLUME_3, (~right_val));
 | 
						|
+	if (ret < 0)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	return 1;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pcm512x_get_reg_sub_switch(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	int val = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+	val = snd_soc_read(rtd->codec_dais[1]->codec, PCM512x_MUTE);
 | 
						|
+	if (val < 0)
 | 
						|
+		return val;
 | 
						|
+
 | 
						|
+	ucontrol->value.integer.value[0] =
 | 
						|
+			(val & P_DAC_LEFT_MUTE) ? P_UNMUTE : P_MUTE;
 | 
						|
+	ucontrol->value.integer.value[1] =
 | 
						|
+			(val & P_DAC_RIGHT_MUTE) ? P_UNMUTE : P_MUTE;
 | 
						|
+
 | 
						|
+	return val;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pcm512x_set_reg_sub_switch(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	unsigned int left_val = (ucontrol->value.integer.value[0]);
 | 
						|
+	unsigned int right_val = (ucontrol->value.integer.value[1]);
 | 
						|
+	int ret = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+	if (glb_ptr->set_mode != 1) {
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[1]->codec, PCM512x_MUTE,
 | 
						|
+				~((left_val & 0x01)<<4 | (right_val & 0x01)));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+	}
 | 
						|
+	return 1;
 | 
						|
+
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pcm512x_get_reg_master(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct soc_mixer_control *mc =
 | 
						|
+		(struct soc_mixer_control *)kcontrol->private_value;
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	unsigned int left_val = 0, right_val = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+
 | 
						|
+	left_val = snd_soc_read(rtd->codec_dais[0]->codec,
 | 
						|
+			PCM512x_DIGITAL_VOLUME_2);
 | 
						|
+	if (left_val < 0)
 | 
						|
+		return left_val;
 | 
						|
+
 | 
						|
+	if (glb_ptr->dual_mode == 1) {
 | 
						|
+		right_val = snd_soc_read(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_3);
 | 
						|
+		if (right_val < 0)
 | 
						|
+			return right_val;
 | 
						|
+	} else {
 | 
						|
+		right_val = snd_soc_read(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_3);
 | 
						|
+		if (right_val < 0)
 | 
						|
+			return right_val;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ucontrol->value.integer.value[0] =
 | 
						|
+		(~(left_val  >> mc->shift)) & mc->max;
 | 
						|
+	ucontrol->value.integer.value[1] =
 | 
						|
+		(~(right_val >> mc->shift)) & mc->max;
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pcm512x_set_reg_master(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct soc_mixer_control *mc =
 | 
						|
+		(struct soc_mixer_control *)kcontrol->private_value;
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	unsigned int left_val = (ucontrol->value.integer.value[0] & mc->max);
 | 
						|
+	unsigned int right_val = (ucontrol->value.integer.value[1] & mc->max);
 | 
						|
+	int ret = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+
 | 
						|
+	if (digital_gain_0db_limit) {
 | 
						|
+		ret = snd_soc_limit_volume(card, "Master Playback Volume",
 | 
						|
+					207);
 | 
						|
+		if (ret < 0)
 | 
						|
+			dev_warn(card->dev, "Failed to set volume limit: %d\n",
 | 
						|
+				ret);
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	if (glb_ptr->dual_mode != 1) {
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_2, (~left_val));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+				PCM512x_DIGITAL_VOLUME_3, (~right_val));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = snd_soc_write(rtd->codec_dais[1]->codec,
 | 
						|
+			PCM512x_DIGITAL_VOLUME_3, (~right_val));
 | 
						|
+	if (ret < 0)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	ret = snd_soc_write(rtd->codec_dais[0]->codec,
 | 
						|
+			PCM512x_DIGITAL_VOLUME_2, (~left_val));
 | 
						|
+	if (ret < 0)
 | 
						|
+		return ret;
 | 
						|
+	return 1;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pcm512x_get_reg_master_switch(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	int val = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+
 | 
						|
+	val = snd_soc_read(rtd->codec_dais[0]->codec, PCM512x_MUTE);
 | 
						|
+	if (val < 0)
 | 
						|
+		return val;
 | 
						|
+
 | 
						|
+	ucontrol->value.integer.value[0] =
 | 
						|
+			(val & P_DAC_LEFT_MUTE) ? P_UNMUTE : P_MUTE;
 | 
						|
+
 | 
						|
+	if (glb_ptr->dual_mode == 1) {
 | 
						|
+		val = snd_soc_read(rtd->codec_dais[1]->codec, PCM512x_MUTE);
 | 
						|
+		if (val < 0)
 | 
						|
+			return val;
 | 
						|
+	}
 | 
						|
+	ucontrol->value.integer.value[1] =
 | 
						|
+			(val & P_DAC_RIGHT_MUTE) ? P_UNMUTE : P_MUTE;
 | 
						|
+
 | 
						|
+	return val;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int pcm512x_set_reg_master_switch(struct snd_kcontrol *kcontrol,
 | 
						|
+		struct snd_ctl_elem_value *ucontrol)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	unsigned int left_val = (ucontrol->value.integer.value[0]);
 | 
						|
+	unsigned int right_val = (ucontrol->value.integer.value[1]);
 | 
						|
+	int ret = 0;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+	if (glb_ptr->dual_mode == 1) {
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[0]->codec, PCM512x_MUTE,
 | 
						|
+				~((left_val & 0x01)<<4));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[1]->codec, PCM512x_MUTE,
 | 
						|
+				~((right_val & 0x01)));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+
 | 
						|
+	} else if (glb_ptr->set_mode == 1) {
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[0]->codec, PCM512x_MUTE,
 | 
						|
+				~((left_val & 0x01)<<4 | (right_val & 0x01)));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+
 | 
						|
+	} else {
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[0]->codec, PCM512x_MUTE,
 | 
						|
+				~((left_val & 0x01)<<4 | (right_val & 0x01)));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+
 | 
						|
+		ret = snd_soc_write(rtd->codec_dais[1]->codec, PCM512x_MUTE,
 | 
						|
+				~((left_val & 0x01)<<4 | (right_val & 0x01)));
 | 
						|
+		if (ret < 0)
 | 
						|
+			return ret;
 | 
						|
+	}
 | 
						|
+	return 1;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static const DECLARE_TLV_DB_SCALE(digital_tlv_sub, -10350, 50, 1);
 | 
						|
+static const DECLARE_TLV_DB_SCALE(digital_tlv_master, -10350, 50, 1);
 | 
						|
+
 | 
						|
+static const struct snd_kcontrol_new allo_piano_controls[] = {
 | 
						|
+	SOC_ENUM_EXT("Subwoofer mode Route",
 | 
						|
+			allo_piano_mode_enum,
 | 
						|
+			snd_allo_piano_mode_get,
 | 
						|
+			snd_allo_piano_mode_put),
 | 
						|
+
 | 
						|
+	SOC_ENUM_EXT("Dual Mode Route",
 | 
						|
+			allo_piano_dual_mode_enum,
 | 
						|
+			snd_allo_piano_dual_mode_get,
 | 
						|
+			snd_allo_piano_dual_mode_put),
 | 
						|
+
 | 
						|
+	SOC_ENUM_EXT("Lowpass Route", allo_piano_enum,
 | 
						|
+			snd_allo_piano_lowpass_get,
 | 
						|
+			snd_allo_piano_lowpass_put),
 | 
						|
+
 | 
						|
+	SOC_DOUBLE_R_EXT_TLV("Subwoofer Playback Volume",
 | 
						|
+			PCM512x_DIGITAL_VOLUME_2,
 | 
						|
+			PCM512x_DIGITAL_VOLUME_3, 0, 255, 1,
 | 
						|
+			pcm512x_get_reg_sub,
 | 
						|
+			pcm512x_set_reg_sub,
 | 
						|
+			digital_tlv_sub),
 | 
						|
+
 | 
						|
+	SOC_DOUBLE_EXT("Subwoofer Playback Switch",
 | 
						|
+			PCM512x_MUTE,
 | 
						|
+			PCM512x_RQML_SHIFT,
 | 
						|
+			PCM512x_RQMR_SHIFT, 1, 1,
 | 
						|
+			pcm512x_get_reg_sub_switch,
 | 
						|
+			pcm512x_set_reg_sub_switch),
 | 
						|
+
 | 
						|
+	SOC_DOUBLE_R_EXT_TLV("Master Playback Volume",
 | 
						|
+			PCM512x_DIGITAL_VOLUME_2,
 | 
						|
+			PCM512x_DIGITAL_VOLUME_3, 0, 255, 1,
 | 
						|
+			pcm512x_get_reg_master,
 | 
						|
+			pcm512x_set_reg_master,
 | 
						|
+			digital_tlv_master),
 | 
						|
+
 | 
						|
+	SOC_DOUBLE_EXT("Master Playback Switch",
 | 
						|
+			PCM512x_MUTE,
 | 
						|
+			PCM512x_RQML_SHIFT,
 | 
						|
+			PCM512x_RQMR_SHIFT, 1, 1,
 | 
						|
+			pcm512x_get_reg_master_switch,
 | 
						|
+			pcm512x_set_reg_master_switch),
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dac_init(struct snd_soc_pcm_runtime *rtd)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = rtd->card;
 | 
						|
+	struct glb_pool *glb_ptr;
 | 
						|
+
 | 
						|
+	glb_ptr = kmalloc(sizeof(struct glb_pool), GFP_KERNEL);
 | 
						|
+	if (!glb_ptr)
 | 
						|
+		return -ENOMEM;
 | 
						|
+
 | 
						|
+	memset(glb_ptr, 0x00, sizeof(glb_ptr));
 | 
						|
+	card->drvdata = glb_ptr;
 | 
						|
+	glb_ptr->dual_mode = 2;
 | 
						|
+	glb_ptr->set_mode = 0;
 | 
						|
+
 | 
						|
+	mutex_init(&glb_ptr->lock);
 | 
						|
+
 | 
						|
+	if (digital_gain_0db_limit) {
 | 
						|
+		int ret;
 | 
						|
+
 | 
						|
+		ret = snd_soc_limit_volume(card, "Digital Playback Volume",
 | 
						|
+					207);
 | 
						|
+		if (ret < 0)
 | 
						|
+			dev_warn(card->dev, "Failed to set volume limit: %d\n",
 | 
						|
+				ret);
 | 
						|
+	}
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void snd_allo_piano_gpio_mute(struct snd_soc_card *card)
 | 
						|
+{
 | 
						|
+	if (mute_gpio[0])
 | 
						|
+		gpiod_set_value_cansleep(mute_gpio[0], P_MUTE);
 | 
						|
+
 | 
						|
+	if (mute_gpio[1])
 | 
						|
+		gpiod_set_value_cansleep(mute_gpio[1], P_MUTE);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void snd_allo_piano_gpio_unmute(struct snd_soc_card *card)
 | 
						|
+{
 | 
						|
+	if (mute_gpio[0])
 | 
						|
+		gpiod_set_value_cansleep(mute_gpio[0], P_UNMUTE);
 | 
						|
+
 | 
						|
+	if (mute_gpio[1])
 | 
						|
+		gpiod_set_value_cansleep(mute_gpio[1], P_UNMUTE);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_set_bias_level(struct snd_soc_card *card,
 | 
						|
+	struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level)
 | 
						|
+{
 | 
						|
+	struct snd_soc_pcm_runtime *rtd;
 | 
						|
+	struct snd_soc_dai *codec_dai;
 | 
						|
+
 | 
						|
+	rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
 | 
						|
+	codec_dai = rtd->codec_dai;
 | 
						|
+
 | 
						|
+	if (dapm->dev != codec_dai->dev)
 | 
						|
+		return 0;
 | 
						|
+
 | 
						|
+	switch (level) {
 | 
						|
+	case SND_SOC_BIAS_PREPARE:
 | 
						|
+		if (dapm->bias_level != SND_SOC_BIAS_STANDBY)
 | 
						|
+			break;
 | 
						|
+		/* UNMUTE DAC */
 | 
						|
+		snd_allo_piano_gpio_unmute(card);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	case SND_SOC_BIAS_STANDBY:
 | 
						|
+		if (dapm->bias_level != SND_SOC_BIAS_PREPARE)
 | 
						|
+			break;
 | 
						|
+		/* MUTE DAC */
 | 
						|
+		snd_allo_piano_gpio_mute(card);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	default:
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dac_startup(
 | 
						|
+	struct snd_pcm_substream *substream)
 | 
						|
+{
 | 
						|
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 | 
						|
+	struct snd_soc_card *card = rtd->card;
 | 
						|
+
 | 
						|
+	snd_allo_piano_gpio_mute(card);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dac_hw_params(
 | 
						|
+		struct snd_pcm_substream *substream,
 | 
						|
+		struct snd_pcm_hw_params *params)
 | 
						|
+{
 | 
						|
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 | 
						|
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 | 
						|
+	unsigned int sample_bits =
 | 
						|
+		snd_pcm_format_physical_width(params_format(params));
 | 
						|
+	unsigned int rate = params_rate(params);
 | 
						|
+	struct snd_soc_card *card = rtd->card;
 | 
						|
+	struct glb_pool *glb_ptr = card->drvdata;
 | 
						|
+	int ret = 0, val = 0, dac;
 | 
						|
+
 | 
						|
+	for (dac = 0; (glb_mclk && dac < 2); dac++) {
 | 
						|
+		/* Configure the PLL clock reference for both the Codecs */
 | 
						|
+		val = snd_soc_read(rtd->codec_dais[dac]->codec,
 | 
						|
+					PCM512x_RATE_DET_4);
 | 
						|
+		if (val < 0) {
 | 
						|
+			dev_err(rtd->codec_dais[dac]->codec->dev,
 | 
						|
+				"Failed to read register PCM512x_RATE_DET_4\n");
 | 
						|
+			return val;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		if (val & 0x40) {
 | 
						|
+			snd_soc_write(rtd->codec_dais[dac]->codec,
 | 
						|
+					PCM512x_PLL_REF,
 | 
						|
+					PCM512x_SREF_BCK);
 | 
						|
+
 | 
						|
+			dev_info(rtd->codec_dais[dac]->codec->dev,
 | 
						|
+				"Setting BCLK as input clock & Enable PLL\n");
 | 
						|
+		} else {
 | 
						|
+			snd_soc_write(rtd->codec_dais[dac]->codec,
 | 
						|
+					PCM512x_PLL_EN,
 | 
						|
+					0x00);
 | 
						|
+
 | 
						|
+			snd_soc_write(rtd->codec_dais[dac]->codec,
 | 
						|
+					PCM512x_PLL_REF,
 | 
						|
+					PCM512x_SREF_SCK);
 | 
						|
+
 | 
						|
+			dev_info(rtd->codec_dais[dac]->codec->dev,
 | 
						|
+				"Setting SCLK as input clock & disabled PLL\n");
 | 
						|
+		}
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	ret = snd_allo_piano_dsp_program(rtd, glb_ptr->set_mode, rate,
 | 
						|
+						glb_ptr->set_lowpass);
 | 
						|
+	if (ret < 0)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	ret = snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2);
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dac_prepare(
 | 
						|
+	struct snd_pcm_substream *substream)
 | 
						|
+{
 | 
						|
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 | 
						|
+	struct snd_soc_card *card = rtd->card;
 | 
						|
+
 | 
						|
+	snd_allo_piano_gpio_unmute(card);
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
+/* machine stream operations */
 | 
						|
+static struct snd_soc_ops snd_allo_piano_dac_ops = {
 | 
						|
+	.startup = snd_allo_piano_dac_startup,
 | 
						|
+	.hw_params = snd_allo_piano_dac_hw_params,
 | 
						|
+	.prepare = snd_allo_piano_dac_prepare,
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct snd_soc_dai_link_component allo_piano_2_1_codecs[] = {
 | 
						|
+	{
 | 
						|
+		.dai_name = "pcm512x-hifi",
 | 
						|
+	},
 | 
						|
+	{
 | 
						|
+		.dai_name = "pcm512x-hifi",
 | 
						|
+	},
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct snd_soc_dai_link snd_allo_piano_dac_dai[] = {
 | 
						|
+	{
 | 
						|
+		.name		= "PianoDACPlus",
 | 
						|
+		.stream_name	= "PianoDACPlus",
 | 
						|
+		.cpu_dai_name	= "bcm2708-i2s.0",
 | 
						|
+		.platform_name	= "bcm2708-i2s.0",
 | 
						|
+		.codecs		= allo_piano_2_1_codecs,
 | 
						|
+		.num_codecs	= 2,
 | 
						|
+		.dai_fmt	= SND_SOC_DAIFMT_I2S |
 | 
						|
+				SND_SOC_DAIFMT_NB_NF |
 | 
						|
+				SND_SOC_DAIFMT_CBS_CFS,
 | 
						|
+		.ops		= &snd_allo_piano_dac_ops,
 | 
						|
+		.init		= snd_allo_piano_dac_init,
 | 
						|
+	},
 | 
						|
+};
 | 
						|
+
 | 
						|
+/* audio machine driver */
 | 
						|
+static struct snd_soc_card snd_allo_piano_dac = {
 | 
						|
+	.name = "PianoDACPlus",
 | 
						|
+	.owner = THIS_MODULE,
 | 
						|
+	.dai_link = snd_allo_piano_dac_dai,
 | 
						|
+	.num_links = ARRAY_SIZE(snd_allo_piano_dac_dai),
 | 
						|
+	.controls = allo_piano_controls,
 | 
						|
+	.num_controls = ARRAY_SIZE(allo_piano_controls),
 | 
						|
+};
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dac_probe(struct platform_device *pdev)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = &snd_allo_piano_dac;
 | 
						|
+	int ret = 0, i = 0;
 | 
						|
+
 | 
						|
+	card->dev = &pdev->dev;
 | 
						|
+	platform_set_drvdata(pdev, &snd_allo_piano_dac);
 | 
						|
+
 | 
						|
+	if (pdev->dev.of_node) {
 | 
						|
+		struct device_node *i2s_node;
 | 
						|
+		struct snd_soc_dai_link *dai;
 | 
						|
+
 | 
						|
+		dai = &snd_allo_piano_dac_dai[0];
 | 
						|
+		i2s_node = of_parse_phandle(pdev->dev.of_node,
 | 
						|
+						"i2s-controller", 0);
 | 
						|
+		if (i2s_node) {
 | 
						|
+			for (i = 0; i < card->num_links; i++) {
 | 
						|
+				dai->cpu_dai_name = NULL;
 | 
						|
+				dai->cpu_of_node = i2s_node;
 | 
						|
+				dai->platform_name = NULL;
 | 
						|
+				dai->platform_of_node = i2s_node;
 | 
						|
+			}
 | 
						|
+		}
 | 
						|
+		digital_gain_0db_limit =
 | 
						|
+			!of_property_read_bool(pdev->dev.of_node,
 | 
						|
+						"allo,24db_digital_gain");
 | 
						|
+
 | 
						|
+		glb_mclk = of_property_read_bool(pdev->dev.of_node,
 | 
						|
+						"allo,glb_mclk");
 | 
						|
+
 | 
						|
+		allo_piano_2_1_codecs[0].of_node =
 | 
						|
+			of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
 | 
						|
+		if (!allo_piano_2_1_codecs[0].of_node) {
 | 
						|
+			dev_err(&pdev->dev,
 | 
						|
+				"Property 'audio-codec' missing or invalid\n");
 | 
						|
+			return -EINVAL;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		allo_piano_2_1_codecs[1].of_node =
 | 
						|
+			of_parse_phandle(pdev->dev.of_node, "audio-codec", 1);
 | 
						|
+		if (!allo_piano_2_1_codecs[1].of_node) {
 | 
						|
+			dev_err(&pdev->dev,
 | 
						|
+				"Property 'audio-codec' missing or invalid\n");
 | 
						|
+			return -EINVAL;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		mute_gpio[0] = devm_gpiod_get_optional(&pdev->dev, "mute1",
 | 
						|
+							GPIOD_OUT_LOW);
 | 
						|
+		if (IS_ERR(mute_gpio[0])) {
 | 
						|
+			ret = PTR_ERR(mute_gpio[0]);
 | 
						|
+			dev_err(&pdev->dev,
 | 
						|
+				"failed to get mute1 gpio6: %d\n", ret);
 | 
						|
+			return ret;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		mute_gpio[1] = devm_gpiod_get_optional(&pdev->dev, "mute2",
 | 
						|
+							GPIOD_OUT_LOW);
 | 
						|
+		if (IS_ERR(mute_gpio[1])) {
 | 
						|
+			ret = PTR_ERR(mute_gpio[1]);
 | 
						|
+			dev_err(&pdev->dev,
 | 
						|
+				"failed to get mute2 gpio25: %d\n", ret);
 | 
						|
+			return ret;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		if (mute_gpio[0] && mute_gpio[1])
 | 
						|
+			snd_allo_piano_dac.set_bias_level =
 | 
						|
+				snd_allo_piano_set_bias_level;
 | 
						|
+
 | 
						|
+		ret = snd_soc_register_card(&snd_allo_piano_dac);
 | 
						|
+		if (ret < 0) {
 | 
						|
+			dev_err(&pdev->dev,
 | 
						|
+				"snd_soc_register_card() failed: %d\n", ret);
 | 
						|
+			return ret;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		if ((mute_gpio[0]) && (mute_gpio[1]))
 | 
						|
+			snd_allo_piano_gpio_mute(&snd_allo_piano_dac);
 | 
						|
+
 | 
						|
+		return 0;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return -EINVAL;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static int snd_allo_piano_dac_remove(struct platform_device *pdev)
 | 
						|
+{
 | 
						|
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
 | 
						|
+
 | 
						|
+	kfree(&card->drvdata);
 | 
						|
+	snd_allo_piano_gpio_mute(&snd_allo_piano_dac);
 | 
						|
+	return snd_soc_unregister_card(&snd_allo_piano_dac);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static const struct of_device_id snd_allo_piano_dac_of_match[] = {
 | 
						|
+	{ .compatible = "allo,piano-dac-plus", },
 | 
						|
+	{ /* sentinel */ },
 | 
						|
+};
 | 
						|
+
 | 
						|
+MODULE_DEVICE_TABLE(of, snd_allo_piano_dac_of_match);
 | 
						|
+
 | 
						|
+static struct platform_driver snd_allo_piano_dac_driver = {
 | 
						|
+	.driver = {
 | 
						|
+		.name = "snd-allo-piano-dac-plus",
 | 
						|
+		.owner = THIS_MODULE,
 | 
						|
+		.of_match_table = snd_allo_piano_dac_of_match,
 | 
						|
+	},
 | 
						|
+	.probe = snd_allo_piano_dac_probe,
 | 
						|
+	.remove = snd_allo_piano_dac_remove,
 | 
						|
+};
 | 
						|
+
 | 
						|
+module_platform_driver(snd_allo_piano_dac_driver);
 | 
						|
+
 | 
						|
+MODULE_AUTHOR("Baswaraj K <jaikumar@cem-solutions.net>");
 | 
						|
+MODULE_DESCRIPTION("ALSA ASoC Machine Driver for Allo Piano DAC Plus");
 | 
						|
+MODULE_LICENSE("GPL v2");
 | 
						|
-- 
 | 
						|
2.16.1
 | 
						|
 |