alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] Add i.MX6q Kosagi Novena support
@ 2014-02-07  5:05 Sean Cross
       [not found] ` <1391749517-11787-1-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
  0 siblings, 1 reply; 14+ messages in thread
From: Sean Cross @ 2014-02-07  5:05 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw
  Cc: Shawn Guo, Sascha Hauer, Mark Brown, Liam Girdwood, Sean Cross

The Kosagi Novena board contains an i.MX6 Quad processor.  This patchset
adds support for this platform, along with support for its own audio codec.

The platform uses an ES8328-based codec, which is currently unsupported in
the Linux kernel.  The audio codec supports playback and recording, as well
as jack detection support for the headset.  Audio codec support is required
as an uninitialized audio codec pulls I2C2 lines down, preventing the bus
from working.

There are two known issues with this patch, and I would like some feedback
on them:

    * I'm unclear on how audio routing works.  The ES8328 has five outputs:
      LOUT1, ROUT1, LOUT2, ROUT2, and OUT3.  It also has four inputs.  The
      board maps Headphone L/R to L/ROUT2, and Speaker to L/ROUT1.  What is
      the proper way to describe this audio routing?
    * The snd-soc-imx-novena.ko module oopses when removing it.  The backtrace
      shows that it's caused by "Unable to handle kernel paging request at
      virtual address", due to snd_jack_dev_free possibly getting called twice.
      What could be causing this?  I never explicitly free the jack device:
 (snd_ctl_remove) from [<804c73d0>] (snd_jack_dev_free+0x48/0x68)
 (snd_jack_dev_free) from [<804c6ef8>] (snd_device_free+0xd0/0x148)
 (snd_device_free) from [<804c72b0>] (snd_device_free_all+0x90/0xb0)
 (snd_device_free_all) from [<804c20ac>] (snd_card_do_free+0x40/0xec)
 (snd_card_do_free) from [<804c22b4>] (snd_card_free+0x74/0x80)
 (snd_card_free) from [<804de444>] (soc_cleanup_card_resources+0xb4/0xbc)
 (soc_cleanup_card_resources) from [<804de460>] (snd_soc_unregister_card+0x14/0x1c)
 (snd_soc_unregister_card) from [<7f032014>] (imx_novena_remove+0x14/0x1c [snd_soc_imx_novena])
 (imx_novena_remove [snd_soc_imx_novena]) from [<8035c258>] (__device_release_driver+0x64/0xb0)
 (__device_release_driver) from [<8035c81c>] (driver_detach+0xa4/0xcc)
 (driver_detach) from [<8035be88>] (bus_remove_driver+0x64/0x9c)
 (bus_remove_driver) from [<8007ea44>] (SyS_delete_module+0x110/0x188)
 (SyS_delete_module) from [<8000e080>] (ret_fast_syscall+0x0/0x30)


Sean Cross (3):
  sound: soc: codecs: Add es8328 codec
  sound: soc: fsl: Add support for Novena onboard audio
  dts: imx: add kosagi novena imx6q dts file

 arch/arm/boot/dts/imx6q-novena.dts | 517 +++++++++++++++++++++++++++++++
 sound/soc/codecs/Kconfig           |   4 +
 sound/soc/codecs/Makefile          |   2 +
 sound/soc/codecs/es8328.c          | 611 +++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/es8328.h          | 240 +++++++++++++++
 sound/soc/fsl/Kconfig              |  13 +
 sound/soc/fsl/Makefile             |   2 +
 sound/soc/fsl/imx-novena.c         | 344 +++++++++++++++++++++
 8 files changed, 1733 insertions(+)
 create mode 100644 arch/arm/boot/dts/imx6q-novena.dts
 create mode 100644 sound/soc/codecs/es8328.c
 create mode 100644 sound/soc/codecs/es8328.h
 create mode 100644 sound/soc/fsl/imx-novena.c

-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/3] sound: soc: codecs: Add es8328 codec
       [not found] ` <1391749517-11787-1-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
@ 2014-02-07  5:05   ` Sean Cross
  2014-02-07 18:12     ` Mark Brown
       [not found]     ` <1391749517-11787-2-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
  2014-02-07  5:05   ` [PATCH 2/3] sound: soc: fsl: Add support for Novena onboard audio Sean Cross
  2014-02-07  5:05   ` [PATCH 3/3] dts: imx: add kosagi novena imx6q dts file Sean Cross
  2 siblings, 2 replies; 14+ messages in thread
From: Sean Cross @ 2014-02-07  5:05 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw
  Cc: Shawn Guo, Sascha Hauer, Mark Brown, Liam Girdwood, Sean Cross

Add support for the ES8328 audio codec.
---
 sound/soc/codecs/Kconfig  |   4 +
 sound/soc/codecs/Makefile |   2 +
 sound/soc/codecs/es8328.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/es8328.h | 240 ++++++++++++++++++
 4 files changed, 857 insertions(+)
 create mode 100644 sound/soc/codecs/es8328.c
 create mode 100644 sound/soc/codecs/es8328.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 983d087a..2b0a821 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -43,6 +43,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_DA732X if I2C
 	select SND_SOC_DA9055 if I2C
 	select SND_SOC_BT_SCO
+	select SND_SOC_ES8328 if I2C
 	select SND_SOC_ISABELLE if I2C
 	select SND_SOC_JZ4740_CODEC
 	select SND_SOC_LM4857 if I2C
@@ -283,6 +284,9 @@ config SND_SOC_BT_SCO
 config SND_SOC_DMIC
 	tristate
 
+config SND_SOC_ES8328
+	tristate
+
 config SND_SOC_ISABELLE
         tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index bc12676..1b62c93 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -30,6 +30,7 @@ snd-soc-da732x-objs := da732x.o
 snd-soc-da9055-objs := da9055.o
 snd-soc-bt-sco-objs := bt-sco.o
 snd-soc-dmic-objs := dmic.o
+snd-soc-es8328-objs := es8328.o
 snd-soc-isabelle-objs := isabelle.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-l3-objs := l3.o
@@ -163,6 +164,7 @@ obj-$(CONFIG_SND_SOC_DA732X)	+= snd-soc-da732x.o
 obj-$(CONFIG_SND_SOC_DA9055)	+= snd-soc-da9055.o
 obj-$(CONFIG_SND_SOC_BT_SCO)	+= snd-soc-bt-sco.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_ES8328)    += snd-soc-es8328.o
 obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
new file mode 100644
index 0000000..db118f4
--- /dev/null
+++ b/sound/soc/codecs/es8328.c
@@ -0,0 +1,611 @@
+/*
+ * es8328.c  --  ES8328 ALSA SoC Audio driver
+ *
+ * Copyright 2014 Sutajio Ko-Usagi PTE LTD
+ *
+ * Author: Sean Cross <xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
+ *
+ * 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.
+ */
+
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include "es8328.h"
+
+/* Run the codec at 22.5792 MHz to support these rates */
+enum es8328_rate {
+	ES8328_RATE_8019,
+	ES8328_RATE_11025,
+	ES8328_RATE_22050,
+	ES8328_RATE_44100,
+};
+
+uint8_t sample_ratios[] = {
+	[ES8328_RATE_8019 ] = 0x9,
+	[ES8328_RATE_11025] = 0x7,
+	[ES8328_RATE_22050] = 0x4,
+	[ES8328_RATE_44100] = 0x2,
+};
+
+#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
+		SNDRV_PCM_RATE_22050 | \
+		SNDRV_PCM_RATE_11025)
+#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+/* codec private data */
+struct es8328_priv {
+	struct regmap *regmap;
+	int sysclk;
+};
+
+static const DECLARE_TLV_DB_SCALE(play_tlv, -3000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cap_tlv, -9600, 50, 0);
+static const DECLARE_TLV_DB_SCALE(pga_tlv, 0, 300, 0);
+
+static const struct snd_kcontrol_new es8328_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Speaker Playback Volume",
+		ES8328_DACCONTROL26, ES8328_DACCONTROL27, 0, 0x24, 0, play_tlv),
+SOC_DOUBLE_R_TLV("Headphone Playback Volume",
+		ES8328_DACCONTROL24, ES8328_DACCONTROL25, 0, 0x24, 0, play_tlv),
+
+SOC_DOUBLE_R_TLV("Mic Capture Volume",
+		ES8328_ADCCONTROL8, ES8328_ADCCONTROL9, 0, 0xc0, 1, cap_tlv),
+SOC_DOUBLE_TLV("Mic PGA Volume",
+		ES8328_ADCCONTROL1, 4, 0, 0x08, 0, pga_tlv),
+};
+
+/*
+ * DAPM controls.
+ */
+static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("Speaker Volume", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_OUTPUT("VOUTL"),
+	SND_SOC_DAPM_OUTPUT("VOUTR"),
+        SND_SOC_DAPM_INPUT("LINE_IN"),
+        SND_SOC_DAPM_INPUT("MIC_IN"),
+        SND_SOC_DAPM_OUTPUT("HP_OUT"),
+        SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+};
+
+
+static const struct snd_soc_dapm_route es8328_intercon[] = {
+	{"VOUTL", NULL, "DAC"},
+	{"VOUTR", NULL, "DAC"},
+};
+
+static int es8328_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = snd_soc_read(codec, ES8328_DACCONTROL3);
+
+	if (mute)
+		mute_reg |= ES8328_DACCONTROL3_DACMUTE;
+	else
+		mute_reg &= ~ES8328_DACCONTROL3_DACMUTE;
+	return snd_soc_write(codec, ES8328_DACCONTROL3, mute_reg);
+}
+
+static int es8328_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		u8 dac = snd_soc_read(codec, ES8328_DACCONTROL2);
+		dac &= ~ES8328_DACCONTROL2_RATEMASK;
+
+		switch (params_rate(params)) {
+		case 8000:
+			dac |= sample_ratios[ES8328_RATE_8019];
+			break;
+		case 11025:
+			dac |= sample_ratios[ES8328_RATE_11025];
+			break;
+		case 22050:
+			dac |= sample_ratios[ES8328_RATE_22050];
+			break;
+		case 44100:
+			dac |= sample_ratios[ES8328_RATE_44100];
+			break;
+		default:
+			dev_err(codec->dev, "%s: unknown rate %d\n",
+				 __func__, params_rate(params));
+			return -EINVAL;
+		}
+		snd_soc_write(codec, ES8328_DACCONTROL2, dac);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		u8 adc = snd_soc_read(codec, ES8328_ADCCONTROL5);
+		adc &= ~ES8328_ADCCONTROL5_RATEMASK;
+
+		switch (params_rate(params)) {
+		case 8000:
+			adc |= sample_ratios[ES8328_RATE_8019];
+			break;
+		case 11025:
+			adc |= sample_ratios[ES8328_RATE_11025];
+			break;
+		case 22050:
+			adc |= sample_ratios[ES8328_RATE_22050];
+			break;
+		case 44100:
+			adc |= sample_ratios[ES8328_RATE_44100];
+			break;
+		default:
+			dev_err(codec->dev, "%s: unknown rate %d\n",
+				 __func__, params_rate(params));
+			return -EINVAL;
+		}
+		snd_soc_write(codec, ES8328_ADCCONTROL5, adc);
+	}
+
+	return 0;
+}
+
+static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S)
+		return -EINVAL;
+
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
+		return -EINVAL;
+
+	if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int es8328_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+			         int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+
+	switch (clk_id) {
+	case 0:
+		es8328->sysclk = freq;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int es8328_adc_enable(struct snd_soc_codec *codec)
+{
+	u16 reg = snd_soc_read(codec, ES8328_CHIPPOWER);
+	reg &= ~(ES8328_CHIPPOWER_ADCVREF_OFF |
+		 ES8328_CHIPPOWER_ADCPLL_OFF |
+		 ES8328_CHIPPOWER_ADCSTM_RESET |
+		 ES8328_CHIPPOWER_ADCDIG_OFF);
+	snd_soc_write(codec, ES8328_CHIPPOWER, reg);
+
+	/* Set up microphone to be differential input */
+	snd_soc_write(codec, ES8328_ADCCONTROL2, 0xf0);
+
+	/* Set ADC to act as I2S master */
+	snd_soc_write(codec, ES8328_ADCCONTROL3, 0x02);
+
+	/* Set I2S to 16-bit mode */
+	snd_soc_write(codec, ES8328_ADCCONTROL4, 0x18);
+
+	/* Frequency clock of 272 */
+	snd_soc_write(codec, ES8328_ADCCONTROL5, 0x02);
+
+	return 0;
+}
+
+static int es8328_dac_enable(struct snd_soc_codec *codec)
+{
+	u16 old_volumes[4];
+	u16 reg;
+	int i;
+
+	for (i = 0; i < 4; i++) {
+		old_volumes[i] = snd_soc_read(codec, i + ES8328_DACCONTROL24);
+		snd_soc_write(codec, i + ES8328_DACCONTROL24, 0);
+	}
+
+	/* Power up LOUT2 ROUT2, and power down xOUT1 */
+	snd_soc_write(codec, ES8328_DACPOWER,
+			ES8328_DACPOWER_ROUT2_ON |
+			ES8328_DACPOWER_LOUT2_ON);
+
+	/* Enable click-free power up */
+	snd_soc_write(codec, ES8328_DACCONTROL6, ES8328_DACCONTROL6_CLICKFREE);
+	snd_soc_write(codec, ES8328_DACCONTROL3, 0x36);
+
+	/* Set I2S to 16-bit mode */
+	snd_soc_write(codec, ES8328_DACCONTROL1, ES8328_DACCONTROL1_DACWL_16);
+
+	/* No attenuation */
+	snd_soc_write(codec, ES8328_DACCONTROL4, 0x00);
+	snd_soc_write(codec, ES8328_DACCONTROL5, 0x00);
+
+	/* Set LIN2 for the output mixer */
+	snd_soc_write(codec, ES8328_DACCONTROL16,
+			ES8328_DACCONTROL16_RMIXSEL_RIN2 |
+			ES8328_DACCONTROL16_LMIXSEL_LIN2);
+
+	/* Point the left DAC at the left mixer */
+	snd_soc_write(codec, ES8328_DACCONTROL17, ES8328_DACCONTROL17_LD2LO);
+	/* Point the right DAC at the right mixer */
+	snd_soc_write(codec, ES8328_DACCONTROL20, ES8328_DACCONTROL20_RD2RO);
+
+	/* Disable all other outputs */
+	snd_soc_write(codec, ES8328_DACCONTROL18, 0x00);
+	snd_soc_write(codec, ES8328_DACCONTROL19, 0x00);
+
+
+	/* Disable mono mode for DACL, and mute DACR */
+	snd_soc_write(codec, ES8328_DACCONTROL7, 0x00);
+
+	for (i = 0; i < 4; i++)
+		snd_soc_write(codec, i + ES8328_DACCONTROL24, old_volumes[i]);
+
+	reg = snd_soc_read(codec, ES8328_CHIPPOWER);
+	reg &= ~(ES8328_CHIPPOWER_DACVREF_OFF |
+		 ES8328_CHIPPOWER_DACPLL_OFF |
+		 ES8328_CHIPPOWER_DACSTM_RESET |
+		 ES8328_CHIPPOWER_DACDIG_OFF);
+	snd_soc_write(codec, ES8328_CHIPPOWER, reg);
+	snd_soc_write(codec, ES8328_DACCONTROL3, 0x32);
+
+	return 0;
+}
+
+static int es8328_pcm_prepare(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		es8328_dac_enable(codec);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		es8328_adc_enable(codec);
+
+	return 0;
+}
+
+static void es8328_pcm_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 reg;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* Mute DAC */
+		snd_soc_write(codec, ES8328_DACCONTROL3,
+				ES8328_DACCONTROL3_DACZEROCROSS |
+				ES8328_DACCONTROL3_DACSOFTRAMP |
+				ES8328_DACCONTROL3_DACMUTE);
+
+		/* Power down DAC and disable LOUT/ROUT */
+		snd_soc_write(codec, ES8328_DACPOWER,
+				ES8328_DACPOWER_LDAC_OFF |
+				ES8328_DACPOWER_RDAC_OFF);
+
+		/* Power down DEM and STM */
+		reg = snd_soc_read(codec, ES8328_CHIPPOWER);
+		reg |= (ES8328_CHIPPOWER_DACVREF_OFF |
+			ES8328_CHIPPOWER_DACPLL_OFF |
+			ES8328_CHIPPOWER_DACDIG_OFF);
+		snd_soc_write(codec, ES8328_CHIPPOWER, reg);
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		/* Mute ADC */
+		snd_soc_write(codec, ES8328_ADCCONTROL7,
+			ES8328_ADCCONTROL7_ADC_LER |
+			ES8328_ADCCONTROL7_ADC_ZERO_CROSS |
+			ES8328_ADCCONTROL7_ADC_SOFT_RAMP);
+
+		/* Power down ADC */
+		snd_soc_write(codec, ES8328_ADCPOWER,
+			ES8328_ADCPOWER_ADC_BIAS_GEN_OFF |
+			ES8328_ADCPOWER_MIC_BIAS_OFF |
+			ES8328_ADCPOWER_ADCR_OFF |
+			ES8328_ADCPOWER_ADCL_OFF |
+			ES8328_ADCPOWER_AINR_OFF |
+			ES8328_ADCPOWER_AINL_OFF);
+
+		/* Power down DEM and STM */
+		reg = snd_soc_read(codec, ES8328_CHIPPOWER);
+		reg |= (ES8328_CHIPPOWER_ADCVREF_OFF |
+			ES8328_CHIPPOWER_ADCPLL_OFF |
+			ES8328_CHIPPOWER_ADCDIG_OFF);
+		snd_soc_write(codec, ES8328_CHIPPOWER, reg);
+	}
+
+	return;
+}
+
+static int es8328_init(struct snd_soc_codec *codec)
+{
+	/* Master serial port mode */
+	snd_soc_write(codec, ES8328_MASTERMODE,
+			ES8328_MASTERMODE_MCLKDIV2 |
+			ES8328_MASTERMODE_MSC);
+
+	/* Power everything down and reset the cip */
+	snd_soc_write(codec, ES8328_CHIPPOWER,
+			ES8328_CHIPPOWER_DACSTM_RESET |
+			ES8328_CHIPPOWER_ADCSTM_RESET |
+			ES8328_CHIPPOWER_DACDIG_OFF |
+			ES8328_CHIPPOWER_ADCDIG_OFF |
+			ES8328_CHIPPOWER_DACVREF_OFF |
+			ES8328_CHIPPOWER_ADCVREF_OFF);
+
+	/* Power up.  Set ADC and DAC to use different frequency ratios */
+	snd_soc_write(codec, ES8328_CONTROL1,
+			ES8328_CONTROL1_VMIDSEL_50k |
+			ES8328_CONTROL1_ENREF);
+
+	/* Power up more blocks */
+	snd_soc_write(codec, ES8328_CONTROL2,
+			ES8328_CONTROL2_OVERCURRENT_ON |
+			ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
+
+
+	/* Power on the chip (but leave VRET off) */
+	/*
+	snd_soc_write(codec, ES8328_CHIPPOWER,
+			ES8328_CHIPPOWER_DACVREF_OFF |
+			ES8328_CHIPPOWER_ADCVREF_OFF);
+	*/
+
+
+	/* Enable muting, and turn on zerocross */
+	snd_soc_write(codec, ES8328_DACCONTROL3,
+			ES8328_DACCONTROL3_DACZEROCROSS |
+			ES8328_DACCONTROL3_DACSOFTRAMP |
+			ES8328_DACCONTROL3_DACMUTE);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops es8328_dai_ops = {
+	.hw_params	= es8328_hw_params,
+	.prepare	= es8328_pcm_prepare,
+	.shutdown	= es8328_pcm_shutdown,
+//	.digital_mute	= es8328_mute,
+	.set_fmt	= es8328_set_dai_fmt,
+	.set_sysclk	= es8328_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver es8328_dai = {
+	.name = "es8328-hifi-analog",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = ES8328_RATES,
+		.formats = ES8328_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = ES8328_RATES,
+		.formats = ES8328_FORMATS,
+	},
+	.ops = &es8328_dai_ops,
+};
+
+static int es8328_suspend(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+
+static int es8328_resume(struct snd_soc_codec *codec)
+{
+	es8328_init(codec);
+	return 0;
+}
+
+static int es8328_probe(struct snd_soc_codec *codec)
+{
+	int ret;
+	struct device *dev = codec->dev;
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
+	if (ret < 0) {
+		dev_err(dev, "failed to configure cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	/* power on device */
+	es8328_init(codec);
+
+	return 0;
+}
+
+static int es8328_remove(struct snd_soc_codec *codec)
+{
+	/* Power everything down and reset the cip */
+	snd_soc_write(codec, ES8328_CHIPPOWER,
+			ES8328_CHIPPOWER_DACSTM_RESET |
+			ES8328_CHIPPOWER_ADCSTM_RESET |
+			ES8328_CHIPPOWER_DACDIG_OFF |
+			ES8328_CHIPPOWER_ADCDIG_OFF |
+			ES8328_CHIPPOWER_DACVREF_OFF |
+			ES8328_CHIPPOWER_ADCVREF_OFF);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_es8328 = {
+	.probe =		es8328_probe,
+	.remove =		es8328_remove,
+	.suspend =		es8328_suspend,
+	.resume =		es8328_resume,
+	.controls =		es8328_snd_controls,
+	.num_controls =		ARRAY_SIZE(es8328_snd_controls),
+	.dapm_widgets =		es8328_dapm_widgets,
+	.num_dapm_widgets =	ARRAY_SIZE(es8328_dapm_widgets),
+	.dapm_routes =		es8328_intercon,
+	.num_dapm_routes =	ARRAY_SIZE(es8328_intercon),
+};
+
+static const struct of_device_id es8328_of_match[] = {
+	{ .compatible = "everest,es8328", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, es8328_of_match);
+
+static const struct regmap_config es8328_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = ES8328_REG_MAX,
+
+	.cache_type = REGCACHE_NONE,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int es8328_spi_probe(struct spi_device *spi)
+{
+	struct es8328_priv *es8328;
+	int ret;
+
+	es8328 = devm_kzalloc(&spi->dev, sizeof(struct es8328_priv),
+			      GFP_KERNEL);
+	if (es8328 == NULL)
+		return -ENOMEM;
+
+	es8328->regmap = devm_regmap_init_spi(spi, &es8328_regmap);
+	if (IS_ERR(es8328->regmap))
+		return PTR_ERR(es8328->regmap);
+
+	spi_set_drvdata(spi, es8328);
+
+	ret = snd_soc_register_codec(&spi->dev,
+			&soc_codec_dev_es8328, &es8328_dai, 1);
+	if (ret < 0)
+		dev_err(&spi->dev, "unable to register codec: %d\n", ret);
+
+	return ret;
+}
+
+static int es8328_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+
+	return 0;
+}
+
+static struct spi_driver es8328_spi_driver = {
+	.driver = {
+		.name	= "es8328",
+		.owner	= THIS_MODULE,
+		.of_match_table = es8328_of_match,
+	},
+	.probe		= es8328_spi_probe,
+	.remove		= es8328_spi_remove,
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int es8328_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct es8328_priv *es8328;
+	int ret;
+
+	es8328 = devm_kzalloc(&i2c->dev, sizeof(struct es8328_priv),
+			      GFP_KERNEL);
+	if (es8328 == NULL)
+		return -ENOMEM;
+
+	es8328->regmap = devm_regmap_init_i2c(i2c, &es8328_regmap);
+	if (IS_ERR(es8328->regmap))
+		return PTR_ERR(es8328->regmap);
+
+	i2c_set_clientdata(i2c, es8328);
+
+	ret =  snd_soc_register_codec(&i2c->dev,
+			&soc_codec_dev_es8328, &es8328_dai, 1);
+
+	return ret;
+}
+
+static int es8328_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id es8328_i2c_id[] = {
+	{ "es8328", 0x11 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, es8328_i2c_id);
+
+static struct i2c_driver es8328_i2c_driver = {
+	.driver = {
+		.name = "es8328-codec",
+		.owner = THIS_MODULE,
+		.of_match_table = es8328_of_match,
+	},
+	.probe =    es8328_i2c_probe,
+	.remove =   es8328_i2c_remove,
+	.id_table = es8328_i2c_id,
+};
+#endif
+
+static int __init es8328_modinit(void)
+{
+	int ret = 0;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&es8328_i2c_driver);
+	if (ret != 0) {
+		pr_err("failed to register es8328 I2C driver: %d\n", ret);
+	}
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	ret = spi_register_driver(&es8328_spi_driver);
+	if (ret != 0) {
+		pr_err("Failed to register es8328 SPI driver: %d\n", ret);
+	}
+#endif
+	return ret;
+}
+module_init(es8328_modinit);
+
+static void __exit es8328_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&es8328_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&es8328_spi_driver);
+#endif
+}
+module_exit(es8328_exit);
+
+MODULE_DESCRIPTION("ASoC ES8328 driver");
+MODULE_AUTHOR("Sean Cross <xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h
new file mode 100644
index 0000000..234a6cb
--- /dev/null
+++ b/sound/soc/codecs/es8328.h
@@ -0,0 +1,240 @@
+/*
+ * es8328.h  --  ES8328 ALSA SoC Audio driver
+ */
+
+#ifndef _ES8328_H
+#define _ES8328_H
+
+#define ES8328_DACLVOL 46
+#define ES8328_DACRVOL 47
+#define ES8328_DACCTL 28
+
+#define ES8328_CONTROL1		0x00
+#define ES8328_CONTROL1_VMIDSEL_OFF (0 << 0)
+#define ES8328_CONTROL1_VMIDSEL_50k (1 << 0)
+#define ES8328_CONTROL1_VMIDSEL_500k (2 << 0)
+#define ES8328_CONTROL1_VMIDSEL_5k (3 << 0)
+#define ES8328_CONTROL1_ENREF (1 << 2)
+#define ES8328_CONTROL1_SEQEN (1 << 3)
+#define ES8328_CONTROL1_SAMEFS (1 << 4)
+#define ES8328_CONTROL1_DACMCLK_ADC (0 << 5)
+#define ES8328_CONTROL1_DACMCLK_DAC (1 << 5)
+#define ES8328_CONTROL1_LRCM (1 << 6)
+#define ES8328_CONTROL1_SCP_RESET (1 << 7)
+
+#define ES8328_CONTROL2		0x01
+#define ES8328_CONTROL2_VREF_BUF_OFF (1 << 0)
+#define ES8328_CONTROL2_VREF_LOWPOWER (1 << 1)
+#define ES8328_CONTROL2_IBIASGEN_OFF (1 << 2)
+#define ES8328_CONTROL2_ANALOG_OFF (1 << 3)
+#define ES8328_CONTROL2_VREF_BUF_LOWPOWER (1 << 4)
+#define ES8328_CONTROL2_VCM_MOD_LOWPOWER (1 << 5)
+#define ES8328_CONTROL2_OVERCURRENT_ON (1 << 6)
+#define ES8328_CONTROL2_THERMAL_SHUTDOWN_ON (1 << 7)
+
+#define ES8328_CHIPPOWER	0x02
+#define ES8328_CHIPPOWER_DACVREF_OFF (1 << 0)
+#define ES8328_CHIPPOWER_ADCVREF_OFF (1 << 1)
+#define ES8328_CHIPPOWER_DACPLL_OFF (1 << 2)
+#define ES8328_CHIPPOWER_ADCPLL_OFF (1 << 3)
+#define ES8328_CHIPPOWER_DACSTM_RESET (1 << 4)
+#define ES8328_CHIPPOWER_ADCSTM_RESET (1 << 5)
+#define ES8328_CHIPPOWER_DACDIG_OFF (1 << 6)
+#define ES8328_CHIPPOWER_ADCDIG_OFF (1 << 7)
+
+#define ES8328_ADCPOWER		0x03
+#define ES8328_ADCPOWER_INT1_LOWPOWER (1 << 0)
+#define ES8328_ADCPOWER_FLASH_ADC_LOWPOWER (1 << 1)
+#define ES8328_ADCPOWER_ADC_BIAS_GEN_OFF (1 << 2)
+#define ES8328_ADCPOWER_MIC_BIAS_OFF (1 << 3)
+#define ES8328_ADCPOWER_ADCR_OFF (1 << 4)
+#define ES8328_ADCPOWER_ADCL_OFF (1 << 5)
+#define ES8328_ADCPOWER_AINR_OFF (1 << 6)
+#define ES8328_ADCPOWER_AINL_OFF (1 << 7)
+
+#define ES8328_DACPOWER		0x04
+#define ES8328_DACPOWER_OUT3_ON (1 << 0)
+#define ES8328_DACPOWER_MONO_ON (1 << 1)
+#define ES8328_DACPOWER_ROUT2_ON (1 << 2)
+#define ES8328_DACPOWER_LOUT2_ON (1 << 3)
+#define ES8328_DACPOWER_ROUT1_ON (1 << 4)
+#define ES8328_DACPOWER_LOUT1_ON (1 << 5)
+#define ES8328_DACPOWER_RDAC_OFF (1 << 6)
+#define ES8328_DACPOWER_LDAC_OFF (1 << 7)
+
+#define ES8328_CHIPLOPOW1	0x05
+#define ES8328_CHIPLOPOW2	0x06
+#define ES8328_ANAVOLMANAG	0x07
+
+#define ES8328_MASTERMODE	0x08
+#define ES8328_MASTERMODE_BCLKDIV (0 << 0)
+#define ES8328_MASTERMODE_BCLK_INV (1 << 5)
+#define ES8328_MASTERMODE_MCLKDIV2 (1 << 6)
+#define ES8328_MASTERMODE_MSC (1 << 7)
+
+#define ES8328_ADCCONTROL1	0x09
+#define ES8328_ADCCONTROL2	0x0a
+#define ES8328_ADCCONTROL3	0x0b
+#define ES8328_ADCCONTROL4	0x0c
+#define ES8328_ADCCONTROL5	0x0d
+#define ES8328_ADCCONTROL5_RATEMASK (0x1f << 0)
+
+#define ES8328_ADCCONTROL6	0x0e
+
+#define ES8328_ADCCONTROL7	0x0f
+#define ES8328_ADCCONTROL7_ADC_MUTE (1 << 2)
+#define ES8328_ADCCONTROL7_ADC_LER (1 << 3)
+#define ES8328_ADCCONTROL7_ADC_ZERO_CROSS (1 << 4)
+#define ES8328_ADCCONTROL7_ADC_SOFT_RAMP (1 << 5)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_4 (0 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_8 (1 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_16 (2 << 6)
+#define ES8328_ADCCONTROL7_ADC_RAMP_RATE_32 (3 << 6)
+
+#define ES8328_ADCCONTROL8	0x10
+#define ES8328_ADCCONTROL9	0x11
+#define ES8328_ADCCONTROL10	0x12
+#define ES8328_ADCCONTROL11	0x13
+#define ES8328_ADCCONTROL12	0x14
+#define ES8328_ADCCONTROL13	0x15
+#define ES8328_ADCCONTROL14	0x16
+
+#define ES8328_DACCONTROL1	0x17
+#define ES8328_DACCONTROL1_DACFORMAT_I2S (0 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_LJUST (1 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_RJUST (2 << 1)
+#define ES8328_DACCONTROL1_DACFORMAT_PCM (3 << 1)
+#define ES8328_DACCONTROL1_DACWL_24 (0 << 3)
+#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
+#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
+#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
+#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_NORMAL (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_I2S_POL_INV (1 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK2 (0 << 6)
+#define ES8328_DACCONTROL1_DACLRP_PCM_MSB_CLK1 (0 << 6)
+#define ES8328_DACCONTROL1_LRSWAP (1 << 7)
+
+#define ES8328_DACCONTROL2	0x18
+#define ES8328_DACCONTROL2_RATEMASK (0x1f << 0)
+
+#define ES8328_DACCONTROL3	0x19
+#define ES8328_DACCONTROL3_AUTOMUTE (1 << 2)
+#define ES8328_DACCONTROL3_DACMUTE (1 << 2)
+#define ES8328_DACCONTROL3_LEFTGAINVOL (1 << 3)
+#define ES8328_DACCONTROL3_DACZEROCROSS (1 << 4)
+#define ES8328_DACCONTROL3_DACSOFTRAMP (1 << 5)
+#define ES8328_DACCONTROL3_DACRAMPRATE (3 << 6)
+
+#define ES8328_DACCONTROL4	0x1a
+#define ES8328_DACCONTROL5	0x1b
+
+#define ES8328_DACCONTROL6	0x1c
+#define ES8328_DACCONTROL6_CLICKFREE (1 << 3)
+#define ES8328_DACCONTROL6_DAC_INVR (1 << 4)
+#define ES8328_DACCONTROL6_DAC_INVL (1 << 5)
+#define ES8328_DACCONTROL6_DEEMPH_OFF (0 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_32k (1 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_44_1k (2 << 6)
+#define ES8328_DACCONTROL6_DEEMPH_48k (3 << 6)
+
+#define ES8328_DACCONTROL7	0x1d
+#define ES8328_DACCONTROL8	0x1e
+#define ES8328_DACCONTROL9	0x1f
+#define ES8328_DACCONTROL10	0x20
+#define ES8328_DACCONTROL11	0x21
+#define ES8328_DACCONTROL12	0x22
+#define ES8328_DACCONTROL13	0x23
+#define ES8328_DACCONTROL14	0x24
+#define ES8328_DACCONTROL15	0x25
+
+#define ES8328_DACCONTROL16	0x26
+#define ES8328_DACCONTROL16_RMIXSEL_RIN1 (0 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN2 (1 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RIN3 (2 << 0)
+#define ES8328_DACCONTROL16_RMIXSEL_RADC (3 << 0)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN1 (0 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN2 (1 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LIN3 (2 << 3)
+#define ES8328_DACCONTROL16_LMIXSEL_LADC (3 << 3)
+
+#define ES8328_DACCONTROL17	0x27
+#define ES8328_DACCONTROL17_LI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL17_LI2LO (1 << 6)
+#define ES8328_DACCONTROL17_LD2LO (1 << 7)
+
+#define ES8328_DACCONTROL18	0x28
+#define ES8328_DACCONTROL18_RI2LOVOL (7 << 3)
+#define ES8328_DACCONTROL18_RI2LO (1 << 6)
+#define ES8328_DACCONTROL18_RD2LO (1 << 7)
+
+#define ES8328_DACCONTROL19	0x29
+#define ES8328_DACCONTROL19_LI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL19_LI2RO (1 << 6)
+#define ES8328_DACCONTROL19_LD2RO (1 << 7)
+
+#define ES8328_DACCONTROL20	0x2a
+#define ES8328_DACCONTROL20_RI2ROVOL (7 << 3)
+#define ES8328_DACCONTROL20_RI2RO (1 << 6)
+#define ES8328_DACCONTROL20_RD2RO (1 << 7)
+
+#define ES8328_DACCONTROL21	0x2b
+#define ES8328_DACCONTROL22	0x2c
+#define ES8328_DACCONTROL23	0x2d
+#define ES8328_DACCONTROL24	0x2e
+#define ES8328_DACCONTROL25	0x2f
+#define ES8328_DACCONTROL26	0x30
+#define ES8328_DACCONTROL27	0x31
+#define ES8328_DACCONTROL28	0x32
+#define ES8328_DACCONTROL29	0x33
+#define ES8328_DACCONTROL30	0x34
+#define ES8328_SYSCLK		0
+
+#define ES8328_REG_MAX		0x35
+
+#define ES8328_PLL1		0
+#define ES8328_PLL2		1
+
+/* clock inputs */
+#define ES8328_MCLK		0
+#define ES8328_PCMCLK		1
+
+/* clock divider id's */
+#define ES8328_PCMDIV		0
+#define ES8328_BCLKDIV		1
+#define ES8328_VXCLKDIV		2
+
+/* PCM clock dividers */
+#define ES8328_PCM_DIV_1	(0 << 6)
+#define ES8328_PCM_DIV_3	(2 << 6)
+#define ES8328_PCM_DIV_5_5	(3 << 6)
+#define ES8328_PCM_DIV_2	(4 << 6)
+#define ES8328_PCM_DIV_4	(5 << 6)
+#define ES8328_PCM_DIV_6	(6 << 6)
+#define ES8328_PCM_DIV_8	(7 << 6)
+
+/* BCLK clock dividers */
+#define ES8328_BCLK_DIV_1	(0 << 7)
+#define ES8328_BCLK_DIV_2	(1 << 7)
+#define ES8328_BCLK_DIV_4	(2 << 7)
+#define ES8328_BCLK_DIV_8	(3 << 7)
+
+/* VXCLK clock dividers */
+#define ES8328_VXCLK_DIV_1	(0 << 6)
+#define ES8328_VXCLK_DIV_2	(1 << 6)
+#define ES8328_VXCLK_DIV_4	(2 << 6)
+#define ES8328_VXCLK_DIV_8	(3 << 6)
+#define ES8328_VXCLK_DIV_16	(4 << 6)
+
+#define ES8328_DAI_HIFI		0
+#define ES8328_DAI_VOICE	1
+
+#define ES8328_1536FS		1536
+#define ES8328_1024FS		1024
+#define ES8328_768FS		768
+#define ES8328_512FS		512
+#define ES8328_384FS		384
+#define ES8328_256FS		256
+#define ES8328_128FS		128
+
+#endif
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 2/3] sound: soc: fsl: Add support for Novena onboard audio
       [not found] ` <1391749517-11787-1-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
  2014-02-07  5:05   ` [PATCH 1/3] sound: soc: codecs: Add es8328 codec Sean Cross
@ 2014-02-07  5:05   ` Sean Cross
       [not found]     ` <1391749517-11787-3-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
  2014-02-07  5:05   ` [PATCH 3/3] dts: imx: add kosagi novena imx6q dts file Sean Cross
  2 siblings, 1 reply; 14+ messages in thread
From: Sean Cross @ 2014-02-07  5:05 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw
  Cc: Shawn Guo, Sascha Hauer, Mark Brown, Liam Girdwood, Sean Cross

Novena uses an ES8328 audio codec connected via I2S.
---
 sound/soc/fsl/Kconfig      |  13 ++
 sound/soc/fsl/Makefile     |   2 +
 sound/soc/fsl/imx-novena.c | 344 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 359 insertions(+)
 create mode 100644 sound/soc/fsl/imx-novena.c

diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 07f8f14..88e7fbf 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -163,6 +163,19 @@ config SND_SOC_PHYCORE_AC97
 	  Say Y if you want to add support for SoC audio on Phytec phyCORE
 	  and phyCARD boards in AC97 mode
 
+config SND_SOC_IMX_NOVENA
+	tristate "SoC Audio support for i.MX6-based Novena boards"
+	depends on OF && I2C
+	select SND_SOC_ES8328
+	select SND_SOC_IMX_PCM_DMA
+	select SND_SOC_IMX_AUDMUX
+	select SND_SOC_FSL_SSI
+	select SND_SOC_FSL_UTILS
+	select SND_SOC_IMX_PCM_FIQ
+	help
+	  Say Y if you want to add support for SoC audio on an i.MX board with
+	  an audio codec connected via SSI/I2S.
+
 config SND_SOC_EUKREA_TLV320
 	tristate "Eukrea TLV320"
 	depends on MACH_EUKREA_MBIMX27_BASEBOARD \
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index b12ad4b..fc89017 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -47,6 +47,7 @@ snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
 snd-soc-phycore-ac97-objs := phycore-ac97.o
 snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
+snd-soc-imx-novena-objs := imx-novena.o
 snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
 snd-soc-imx-wm8962-objs := imx-wm8962.o
 snd-soc-imx-spdif-objs := imx-spdif.o
@@ -56,6 +57,7 @@ obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
 obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
 obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
+obj-$(CONFIG_SND_SOC_IMX_NOVENA) += snd-soc-imx-novena.o
 obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
 obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
 obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
diff --git a/sound/soc/fsl/imx-novena.c b/sound/soc/fsl/imx-novena.c
new file mode 100644
index 0000000..6e06de9
--- /dev/null
+++ b/sound/soc/fsl/imx-novena.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/clk-provider.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "imx-audmux.h"
+
+#define DAI_NAME_SIZE	32
+#define IMX6Q_SYSCLK 0x00
+
+struct imx_novena_data {
+	struct device *dev;
+	struct snd_soc_dai_link dai;
+	struct snd_soc_card card;
+	char codec_dai_name[DAI_NAME_SIZE];
+	char platform_name[DAI_NAME_SIZE];
+	struct clk *codec_clk;
+	struct clk *codec_clk_src;
+	struct clk *codec_clk_sel;
+	struct clk *codec_clk_post_div;
+	struct clk *system_cko;
+	unsigned int clk_freq_src;
+	unsigned int clk_frequency;
+	int power_gpio;
+	int jack_gpio;
+};
+
+static struct snd_soc_jack_gpio headset_jack_gpios[] = {
+	{
+		.gpio = -1,
+		.name = "headset-gpio",
+		.report = SND_JACK_HEADSET,
+		.invert = 0,
+		.debounce_time = 200,
+	},
+};
+
+static struct snd_soc_jack headset_jack;
+
+static int imx_novena_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct imx_novena_data *data = container_of(rtd->card,
+					struct imx_novena_data, card);
+	struct device *dev = rtd->card->dev;
+	int ret;
+
+	ret = snd_soc_dai_set_sysclk(rtd->codec_dai, IMX6Q_SYSCLK,
+				     data->clk_frequency, SND_SOC_CLOCK_IN);
+	if (ret) {
+		dev_err(dev, "could not set codec driver clock params to %d\n",
+			data->clk_frequency);
+		return ret;
+	}
+
+	/* Headphone jack detection */
+	if (gpio_is_valid(data->jack_gpio)) {
+		ret = snd_soc_jack_new(rtd->codec, "Headset",
+				       SND_JACK_HEADSET | SND_JACK_BTN_0,
+				       &headset_jack);
+		if (ret)
+			return ret;
+
+		headset_jack_gpios[0].gpio = data->jack_gpio;
+		ret = snd_soc_jack_add_gpios(&headset_jack,
+					     ARRAY_SIZE(headset_jack_gpios),
+					     headset_jack_gpios);
+	}
+
+	return ret;
+}
+
+static const struct snd_soc_dapm_widget imx_novena_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static int imx_set_frequency(struct imx_novena_data *data, int freq) {
+	int ret;
+
+	ret = clk_set_parent(data->system_cko, data->codec_clk);
+	if (ret) {
+		dev_err(data->dev, "unable to set clk output");
+		return ret;
+	}
+
+	ret = clk_set_parent(data->codec_clk_sel, data->codec_clk_post_div);
+	if (ret) {
+		dev_err(data->dev, "unable to set clk parent");
+		return ret;
+	}
+
+	data->clk_freq_src = clk_round_rate(data->codec_clk_src, freq*32);
+	data->clk_frequency = clk_round_rate(data->codec_clk, freq);
+	dev_dbg(data->dev, "clock source frequency: %d\n", data->clk_freq_src);
+	dev_dbg(data->dev, "clock frequency: %d\n", data->clk_frequency);
+
+	ret = clk_set_rate(data->codec_clk_src, data->clk_freq_src);
+	if (ret) {
+		dev_err(data->dev, "unable to set source clock rate\n");
+		return ret;
+	}
+
+	ret = clk_set_rate(data->codec_clk, data->clk_frequency);
+	if (ret) {
+		dev_err(data->dev, "unable to set codec clock rate\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(data->codec_clk);
+	if (ret) {
+		dev_err(data->dev, "unable to prepare codec clk\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(data->codec_clk_src);
+	if (ret) {
+		dev_err(data->dev, "unable to prepare codec clk source\n");
+		return ret;
+	}
+	return ret;
+}
+
+static int imx_novena_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct device_node *ssi_np, *codec_np;
+	struct platform_device *ssi_pdev;
+	struct i2c_client *codec_dev;
+	struct imx_novena_data *data;
+	int int_port, ext_port;
+	int ret;
+	struct device *dev = &pdev->dev;
+
+	ret = of_property_read_u32(np, "mux-int-port", &int_port);
+	if (ret) {
+		dev_err(&pdev->dev, "mux-int-port missing or invalid\n");
+		return ret;
+	}
+	ret = of_property_read_u32(np, "mux-ext-port", &ext_port);
+	if (ret) {
+		dev_err(&pdev->dev, "mux-ext-port missing or invalid\n");
+		return ret;
+	}
+
+	/*
+	 * The port numbering in the hardware manual starts at 1, while
+	 * the audmux API expects it starts at 0.
+	 */
+	int_port--;
+	ext_port--;
+	ret = imx_audmux_v2_configure_port(int_port,
+			IMX_AUDMUX_V2_PTCR_SYN |
+			IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) |
+			IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+			IMX_AUDMUX_V2_PTCR_TFSDIR |
+			IMX_AUDMUX_V2_PTCR_TCLKDIR,
+			IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
+	if (ret) {
+		dev_err(&pdev->dev, "audmux internal port setup failed\n");
+		return ret;
+	}
+	ret = imx_audmux_v2_configure_port(ext_port,
+			IMX_AUDMUX_V2_PTCR_SYN,
+			IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
+	if (ret) {
+		dev_err(&pdev->dev, "audmux external port setup failed\n");
+		return ret;
+	}
+
+	ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0);
+	codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0);
+	if (!ssi_np || !codec_np) {
+		dev_err(&pdev->dev, "phandle missing or invalid\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	ssi_pdev = of_find_device_by_node(ssi_np);
+	if (!ssi_pdev) {
+		dev_err(&pdev->dev, "failed to find SSI platform device\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+	codec_dev = of_find_i2c_device_by_node(codec_np);
+	if (!codec_dev) {
+		dev_err(&pdev->dev, "failed to find codec platform device\n");
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	data->dev = dev;
+
+	data->jack_gpio = of_get_named_gpio(pdev->dev.of_node,
+				"jack-gpio", 0);
+
+	data->power_gpio = of_get_named_gpio(pdev->dev.of_node,
+				"power-gpio", 0);
+	if (gpio_is_valid(data->power_gpio))
+		devm_gpio_request_one(&pdev->dev, data->power_gpio,
+				    GPIOF_OUT_INIT_HIGH,
+				    "audio codec power switch");
+
+	/* Setup clocks */
+	data->codec_clk = devm_clk_get(dev, "cko1");
+	if (IS_ERR(data->codec_clk)) {
+		dev_err(dev,
+			"codec clock missing or invalid\n");
+		goto fail;
+	}
+
+	data->codec_clk_sel = devm_clk_get(dev, "cko1_sel");
+	if (IS_ERR(data->codec_clk_sel)) {
+		dev_err(dev,
+			"codec clock select missing or invalid\n");
+		goto fail;
+	}
+
+	data->codec_clk_src = devm_clk_get(dev, "pll4_audio");
+	if (IS_ERR(data->codec_clk_src)) {
+		dev_err(dev,
+			"codec clock source missing or invalid\n");
+		goto fail;
+	}
+
+	data->codec_clk_post_div = devm_clk_get(dev, "pll4_post_div");
+	if (IS_ERR(data->codec_clk_post_div)) {
+		dev_err(dev,
+			"codec clock post-div missing or invalid\n");
+		goto fail;
+	}
+
+	data->system_cko = devm_clk_get(dev, "cko_sel");
+	if (IS_ERR(data->system_cko)) {
+		dev_err(dev,
+			"system clock missing or invalid\n");
+		goto fail;
+	}
+
+	ret = imx_set_frequency(data, 22579200);
+	if (ret)
+		goto fail;
+
+
+	data->dai.name = "hifi";
+	data->dai.stream_name = "hifi";
+	data->dai.codec_dai_name = "es8328-hifi-analog";
+	data->dai.codec_of_node = codec_np;
+	data->dai.cpu_of_node = ssi_np;
+	data->dai.platform_of_node = ssi_np;
+	data->dai.init = &imx_novena_dai_init;
+	data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			    SND_SOC_DAIFMT_CBM_CFM;
+
+	data->card.dev = &pdev->dev;
+	ret = snd_soc_of_parse_card_name(&data->card, "model");
+	if (ret)
+		goto fail;
+	ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
+	if (ret)
+		goto fail;
+	data->card.num_links = 1;
+	data->card.owner = THIS_MODULE;
+	data->card.dai_link = &data->dai;
+	data->card.dapm_widgets = imx_novena_dapm_widgets;
+	data->card.num_dapm_widgets = ARRAY_SIZE(imx_novena_dapm_widgets);
+
+	ret = snd_soc_register_card(&data->card);
+	if (ret)
+		goto fail;
+
+	platform_set_drvdata(pdev, data);
+fail:
+	if (ssi_np)
+		of_node_put(ssi_np);
+	if (codec_np)
+		of_node_put(codec_np);
+
+	return ret;
+}
+
+static int imx_novena_remove(struct platform_device *pdev)
+{
+	struct imx_novena_data *data = platform_get_drvdata(pdev);
+
+	snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios),
+				headset_jack_gpios);
+
+	if (data->codec_clk)
+		clk_disable_unprepare(data->codec_clk);
+
+	if (data->codec_clk_src)
+		clk_disable_unprepare(data->codec_clk_src);
+
+	snd_soc_unregister_card(&data->card);
+
+	return 0;
+}
+
+static const struct of_device_id imx_novena_dt_ids[] = {
+	{ .compatible = "kosagi,imx-audio-novena", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_novena_dt_ids);
+
+static struct platform_driver imx_novena_driver = {
+	.driver = {
+		.name = "imx-novena",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_novena_dt_ids,
+	},
+	.probe = imx_novena_probe,
+	.remove = imx_novena_remove,
+};
+module_platform_driver(imx_novena_driver);
+
+MODULE_AUTHOR("Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
+MODULE_DESCRIPTION("Kosagi i.MX6 Novena ASoC machine driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-audio-novena");
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH 3/3] dts: imx: add kosagi novena imx6q dts file
       [not found] ` <1391749517-11787-1-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
  2014-02-07  5:05   ` [PATCH 1/3] sound: soc: codecs: Add es8328 codec Sean Cross
  2014-02-07  5:05   ` [PATCH 2/3] sound: soc: fsl: Add support for Novena onboard audio Sean Cross
@ 2014-02-07  5:05   ` Sean Cross
  2 siblings, 0 replies; 14+ messages in thread
From: Sean Cross @ 2014-02-07  5:05 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw
  Cc: Shawn Guo, Sascha Hauer, Mark Brown, Liam Girdwood, Sean Cross

Add a device tree file for the Novena i.MX6q-based board.
---
 arch/arm/boot/dts/imx6q-novena.dts | 517 +++++++++++++++++++++++++++++++++++++
 1 file changed, 517 insertions(+)
 create mode 100644 arch/arm/boot/dts/imx6q-novena.dts

diff --git a/arch/arm/boot/dts/imx6q-novena.dts b/arch/arm/boot/dts/imx6q-novena.dts
new file mode 100644
index 0000000..ea95168
--- /dev/null
+++ b/arch/arm/boot/dts/imx6q-novena.dts
@@ -0,0 +1,517 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2011 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/dts-v1/;
+#include "imx6q.dtsi"
+
+/ {
+	model = "Kosagi Novena (i.MX6 Quad)";
+	compatible = "kosagi,imx6q-novena", "fsl,imx6q";
+
+	memory {
+		reg = <0x10000000 0x40000000>;
+	};
+
+	regulators {
+		compatible = "simple-bus";
+
+		reg_2p5v: 2p5v {
+			compatible = "regulator-fixed";
+			regulator-name = "2P5V";
+			regulator-min-microvolt = <2500000>;
+			regulator-max-microvolt = <2500000>;
+			regulator-always-on;
+		};
+
+		reg_3p3v: 3p3v {
+			compatible = "regulator-fixed";
+			regulator-name = "3P3V";
+			regulator-min-microvolt = <3300000>;
+			regulator-max-microvolt = <3300000>;
+			regulator-always-on;
+		};
+
+		reg_usb_otg_vbus: usb_otg_vbus {
+			compatible = "regulator-fixed";
+			regulator-name = "usb_otg_vbus";
+			regulator-min-microvolt = <5000000>;
+			regulator-max-microvolt = <5000000>;
+			enable-active-high;
+		};
+		reg_audio_codec: es8328-regulator {
+			compatible = "regulator-fixed";
+			regulator-name = "es8328-power";
+			regulator-boot-on;
+			regulator-min-microvolt = <5000000>;
+			regulator-max-microvolt = <5000000>;
+			startup-delay-us = <400000>;
+			gpio = <&gpio5 17 0>;
+			enable-active-high;
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+
+		user-button {
+			label = "User Button";
+			gpios = <&gpio4 14 1>;
+			gpio-key,wakeup;
+			linux,code = <116>; /* KEY_POWER */
+		};
+	};
+
+	leds {
+		compatible = "gpio-leds";
+		heartbeat {
+			label = "novena::usr0";
+			gpios = <&gpio3 19 0>;
+			linux,default-trigger = "heartbeat";
+		};
+	};
+
+        backlight {
+                compatible = "pwm-backlight";
+                pwms = <&pwm1 0 10000>;
+
+                brightness-levels = <0 3 6 12 16 24 32 48 64 96 128 192 255>;
+                default-brightness-level = <12>;
+        };
+
+        imx-drm {
+		compatible = "fsl,imx-drm";
+		crtcs = <&ipu1 0>, <&ipu1 1>;
+		connectors = <&ldb>;
+	};
+
+	sound {
+		compatible = "fsl,imx-audio-novena";
+		model = "imx-audio-novena";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pinctrl_sound_novena>;
+		clocks = <&clks 169>, <&clks 57>, <&clks 173>, <&clks 203>, <&clks 201>;
+		clock-names = "cko1", "cko1_sel", "pll4_audio", "pll4_post_div", "cko_sel";
+		ssi-controller = <&ssi1>;
+		audio-codec = <&codec>;
+		jack-gpio = <&gpio5 15 0>;
+		audio-routing =
+			"MIC_IN", "Mic Jack",
+			"Mic Jack", "Mic Bias",
+			"Headphone Jack", "HP_OUT",
+			"Ext Spk", "SPK_OUT";
+		mux-int-port = <1>;
+		mux-ext-port = <3>;
+	};
+};
+
+&ssi1 {
+	fsl,mode = "i2s-slave";
+	status = "okay";
+};
+
+&uart2 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart2_1>;
+};
+
+&uart3 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart3_1>;
+};
+
+&uart4 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_uart4_novena>;
+};
+
+&sata {
+	status = "okay";
+};
+
+&iomuxc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_hog>;
+
+	hog {
+		pinctrl_hog: hoggrp {
+			fsl,pins = <
+				/* Touchscreen interrupt */
+				MX6QDL_PAD_DISP0_DAT19__GPIO5_IO13 0x80000000
+
+				/* GPIO LED */
+				MX6QDL_PAD_EIM_D19__GPIO3_IO19  0x80000000
+
+				/* FPGA power */
+				MX6QDL_PAD_SD1_DAT1__GPIO1_IO17 0x80000000
+
+				/* User button */
+				MX6QDL_PAD_SD2_DAT1__GPIO1_IO14 0x80000000
+			>;
+		};
+	};
+
+	pcie {
+		pinctrl_pcie_novena: pciegrp-novena {
+			fsl,pins = <
+				MX6QDL_PAD_EIM_D22__GPIO3_IO22  0x80000000 /* Wakeup */
+				MX6QDL_PAD_EIM_D29__GPIO3_IO29  0x80000000 /* Reset */
+				MX6QDL_PAD_GPIO_17__GPIO7_IO12  0x80000000 /* Power On */
+				MX6QDL_PAD_EIM_A22__GPIO2_IO16  0x80000000 /* Wifi kill */
+			>;
+		};
+	};
+
+	enet {
+		pinctrl_enet_novena: enetgrp-novena {
+			fsl,pins = <
+				MX6QDL_PAD_ENET_MDIO__ENET_MDIO       0x1b0b0
+				MX6QDL_PAD_ENET_MDC__ENET_MDC         0x1b0b0
+				MX6QDL_PAD_RGMII_TXC__RGMII_TXC       0x1b020
+				MX6QDL_PAD_RGMII_TD0__RGMII_TD0       0x1b028
+				MX6QDL_PAD_RGMII_TD1__RGMII_TD1       0x1b028
+				MX6QDL_PAD_RGMII_TD2__RGMII_TD2       0x1b028
+				MX6QDL_PAD_RGMII_TD3__RGMII_TD3       0x1b028
+				MX6QDL_PAD_RGMII_TX_CTL__RGMII_TX_CTL 0x1b028
+				MX6QDL_PAD_ENET_REF_CLK__ENET_TX_CLK  0x1b0b0
+				MX6QDL_PAD_RGMII_RXC__RGMII_RXC       0x1b0b0
+				MX6QDL_PAD_RGMII_RD0__RGMII_RD0       0x1b0b0
+				MX6QDL_PAD_RGMII_RD1__RGMII_RD1       0x1b0b0
+				MX6QDL_PAD_RGMII_RD2__RGMII_RD2       0x1b0b0
+				MX6QDL_PAD_RGMII_RD3__RGMII_RD3       0x1b0b0
+				MX6QDL_PAD_RGMII_RX_CTL__RGMII_RX_CTL 0x1b0b0
+				MX6QDL_PAD_GPIO_16__ENET_REF_CLK      0x4001b0a8
+
+				/* Ethernet reset */
+				MX6QDL_PAD_EIM_D23__GPIO3_IO23  0x80000000
+			>;
+		};
+	};
+
+	uart4 {
+		pinctrl_uart4_novena: uart4-novena {
+			fsl,pins = <
+				MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1
+				MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1
+			>;
+		};
+	};
+
+	sound {
+		pinctrl_sound_novena: sound-novena {
+			fsl,pins = <
+				/* Audio power regulator */
+				MX6QDL_PAD_DISP0_DAT23__GPIO5_IO17 0x80000000
+
+				/* Headphone plug */
+				MX6QDL_PAD_DISP0_DAT21__GPIO5_IO15 0x80000000
+
+				MX6QDL_PAD_GPIO_0__CCM_CLKO1       0x80000000
+			>;
+		};
+	};
+
+	usdhc2 {
+		pinctrl_usdhc2_novena: usdhc2-novena {
+			fsl,pins = <
+				MX6QDL_PAD_SD2_CMD__SD2_CMD    0x17059
+				MX6QDL_PAD_SD2_CLK__SD2_CLK    0x10059
+				MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x17059
+				MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x17059
+				MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x17059
+				MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x17059
+
+				/* Write protect */
+				MX6QDL_PAD_GPIO_2__GPIO1_IO02   0x80000000
+
+				/* Card detect */
+				MX6QDL_PAD_GPIO_4__GPIO1_IO04   0x80000000
+			>;
+		};
+	};
+};
+
+&fec {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_enet_novena>;
+	phy-mode = "rgmii";
+	phy-reset-gpios = <&gpio3 23 0>;
+	mac-address = [00 22 C6 87 72 03];
+	rxc-skew-ps = <3000>;
+	rxdv-skew-ps = <0>;
+	txc-skew-ps = <3000>;
+	txen-skew-ps = <0>;
+	rxd0-skew-ps = <0>;
+	rxd1-skew-ps = <0>;
+	rxd2-skew-ps = <0>;
+	rxd3-skew-ps = <0>;
+	txd0-skew-ps = <3000>;
+	txd1-skew-ps = <3000>;
+	txd2-skew-ps = <3000>;
+	txd3-skew-ps = <3000>;
+};
+
+&usdhc2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usdhc2_1>;
+	pinctrl-0 = <&pinctrl_usdhc2_novena>;
+	cd-gpios = <&gpio1 4 0>;
+	wp-gpios = <&gpio1 2 0>;
+	status = "okay";
+};
+
+&usdhc3 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usdhc3_2>;
+	non-removable;
+	status = "okay";
+};
+
+&usbotg {
+	vbus-supply = <&reg_usb_otg_vbus>;
+	dr_mode = "otg";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_usbotg_2>;
+	disable-over-current;
+	status = "okay";
+};
+
+&usbh1 {
+	status = "okay";
+};
+
+&audmux {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_audmux_2>;
+};
+
+&i2c1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c1_1>;
+	status = "okay";
+
+	stmpe610@0 {
+		compatible = "st,stmpe610";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x44>;
+		irq-over-gpio;
+		irq-gpios = <&gpio5 13 0>;
+		id = <0>;
+		blocks = <0x5>;
+		irq-trigger = <0x1>;
+
+		stmpe_adc {
+			compatible = "st,stmpe-adc";
+		};
+		stmpe_touchscreen {
+			compatible = "st,stmpe-ts";
+			ts,sample-time = <4>;
+			ts,mod-12b = <1>;
+			ts,ref-sel = <0>;
+			ts,adc-freq = <1>;
+			ts,ave-ctrl = <1>;
+			ts,touch-det-delay = <2>;
+			ts,settling = <2>;
+			ts,fraction-z = <7>;
+			ts,i-drive = <1>;
+		};
+	};
+};
+
+&i2c2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c2_1>;
+	status = "okay";
+
+	pmic: pfuze100@08 {
+		compatible = "fsl,pfuze100";
+		reg = <0x08>;
+
+		regulators {
+			sw1a_reg: sw1ab {
+				regulator-min-microvolt = <300000>;
+				regulator-max-microvolt = <1875000>;
+				regulator-boot-on;
+				regulator-always-on;
+				regulator-ramp-delay = <6250>;
+			};
+
+			sw1c_reg: sw1c {
+				regulator-min-microvolt = <300000>;
+				regulator-max-microvolt = <1875000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			sw2_reg: sw2 {
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			sw3a_reg: sw3a {
+				regulator-min-microvolt = <400000>;
+				regulator-max-microvolt = <1975000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			sw3b_reg: sw3b {
+				regulator-min-microvolt = <400000>;
+				regulator-max-microvolt = <1975000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			sw4_reg: sw4 {
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			swbst_reg: swbst {
+				regulator-min-microvolt = <5000000>;
+				regulator-max-microvolt = <5150000>;
+			};
+
+			snvs_reg: vsnvs {
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <3000000>;
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			vref_reg: vrefddr {
+				regulator-boot-on;
+				regulator-always-on;
+			};
+
+			vgen1_reg: vgen1 {
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1550000>;
+			};
+
+			vgen2_reg: vgen2 {
+				regulator-min-microvolt = <800000>;
+				regulator-max-microvolt = <1550000>;
+			};
+
+			vgen3_reg: vgen3 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vgen4_reg: vgen4 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vgen5_reg: vgen5 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vgen6_reg: vgen6 {
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			coin_reg: coin {
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+				regulator-boot-on;
+			};
+		};
+	};
+};
+
+&i2c3 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_i2c3_1>;
+	status = "okay";
+
+	codec: es8328@11 {
+		compatible = "everest,es8328";
+		reg = <0x11>;
+		status = "disabled";
+	};
+
+	stdp4028@73 {
+		compatible = "st,stdp4028";
+		power-switch = <&gpio5 28 0>;
+		reg = <0x73>;
+		status = "disabled";
+	};
+
+	/*
+	eepromoops@56 {
+		compatible = "kosagi,eepromoops";
+		reg = <0x56>;
+		status = "okay";
+	};
+	*/
+};
+
+&ecspi3 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_ecspi3_1>;
+	fsl,spi-num-chipselects = <3>;
+	status = "okay";
+
+	spidev@0x00 {
+		compatible = "spidev";
+		spi-max-frequency = <30000000>;
+		reg = <0>;
+	};
+};
+
+&pcie {
+	pinctrl-names = "default";
+	pinctrl-0 = <&pinctrl_pcie_novena>;
+	reset-gpio = <&gpio3 29 0>;
+	power-on-gpio = <&gpio7 12 0>;
+	wake-up-gpio = <&gpio3 22 0>;
+	disable-gpio = <&gpio2 16 0>;
+};
+
+&ldb {
+	fsl,dual-channel;
+	status = "disabled";
+        lvds-channel@0 {
+		fsl,data-mapping = "jeida";
+		fsl,data-width = <24>;
+		crtcs = <&ipu2 0>, <&ipu2 1>;
+                display-timings {
+			2560x1700p50 {
+				clock-frequency = <185000000>;
+				hactive = <2560>;
+				vactive = <1700>;
+				hback-porch = <80>;
+				hfront-porch = <48>;
+				hsync-len = <32>;
+				vback-porch = <36>;
+				vfront-porch = <3>;
+				vsync-len = <10>;
+				hsync-active = <1>;
+				vsync-active = <1>;
+			};
+                };
+        };
+};
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] sound: soc: codecs: Add es8328 codec
  2014-02-07  5:05   ` [PATCH 1/3] sound: soc: codecs: Add es8328 codec Sean Cross
@ 2014-02-07 18:12     ` Mark Brown
  2014-02-08  8:10       ` Sean Cross
       [not found]       ` <20140207181238.GL1757-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
       [not found]     ` <1391749517-11787-2-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
  1 sibling, 2 replies; 14+ messages in thread
From: Mark Brown @ 2014-02-07 18:12 UTC (permalink / raw)
  To: Sean Cross; +Cc: devicetree, alsa-devel, Shawn Guo, Liam Girdwood, Sascha Hauer


[-- Attachment #1.1: Type: text/plain, Size: 3747 bytes --]

On Fri, Feb 07, 2014 at 01:05:15PM +0800, Sean Cross wrote:

Please use subject likes matching the style for the subsystem.  If your
changelog looks different to others in the same area it probably needs
an update.

In general this looks like it should be making much more use of the
framework rather than open coding, it looks like it's very much hard
coded for one use cae.

> +config SND_SOC_ES8328
> +	tristate
> +

It looks like you're going to use this on an ARM system, you should add
DT support and make it visible in Kconfig.

> +static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
> +	SND_SOC_DAPM_DAC("Speaker Volume", "HiFi Playback", SND_SOC_NOPM, 0, 0),

Don't declare a stream by name, use DAPM routes to connect the stream to
the widget.

> +	SND_SOC_DAPM_OUTPUT("VOUTL"),
> +	SND_SOC_DAPM_OUTPUT("VOUTR"),
> +        SND_SOC_DAPM_INPUT("LINE_IN"),
> +        SND_SOC_DAPM_INPUT("MIC_IN"),
> +        SND_SOC_DAPM_OUTPUT("HP_OUT"),
> +        SND_SOC_DAPM_OUTPUT("SPK_OUT"),

Something is messed up with your indentation, spaces vs tabs I expect.

> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {

An else clause is more idiomatic here.

> +		snd_soc_write(codec, ES8328_ADCCONTROL5, adc);

It's more idiomatic to use update_bits() here and save doing a manual
read/modify/write cycle - it also avoids the write if not needed.

> +static int es8328_adc_enable(struct snd_soc_codec *codec)
> +{
> +	u16 reg = snd_soc_read(codec, ES8328_CHIPPOWER);
> +	reg &= ~(ES8328_CHIPPOWER_ADCVREF_OFF |
> +		 ES8328_CHIPPOWER_ADCPLL_OFF |
> +		 ES8328_CHIPPOWER_ADCSTM_RESET |
> +		 ES8328_CHIPPOWER_ADCDIG_OFF);
> +	snd_soc_write(codec, ES8328_CHIPPOWER, reg);

This looks like it should be done in DAPM.

> +
> +	/* Set up microphone to be differential input */
> +	snd_soc_write(codec, ES8328_ADCCONTROL2, 0xf0);

This looks like something that should be platform data and/or DAPM -
other platforms may be wired differently.

> +	/* Set ADC to act as I2S master */
> +	snd_soc_write(codec, ES8328_ADCCONTROL3, 0x02);

set_dai_fmt().

> +	/* Set I2S to 16-bit mode */
> +	snd_soc_write(codec, ES8328_ADCCONTROL4, 0x18);

This should be being done in hw_params.

> +	/* Frequency clock of 272 */
> +	snd_soc_write(codec, ES8328_ADCCONTROL5, 0x02);

What is the frequency clock in this context?

> +	/* Power up LOUT2 ROUT2, and power down xOUT1 */
> +	snd_soc_write(codec, ES8328_DACPOWER,
> +			ES8328_DACPOWER_ROUT2_ON |
> +			ES8328_DACPOWER_LOUT2_ON);

This looks like it should be being done in DAPM.

> +	/* Enable click-free power up */
> +	snd_soc_write(codec, ES8328_DACCONTROL6, ES8328_DACCONTROL6_CLICKFREE);
> +	snd_soc_write(codec, ES8328_DACCONTROL3, 0x36);

Just do this once on startup?

> +	/* Set I2S to 16-bit mode */
> +	snd_soc_write(codec, ES8328_DACCONTROL1, ES8328_DACCONTROL1_DACWL_16);

hw_params().

> +	/* No attenuation */
> +	snd_soc_write(codec, ES8328_DACCONTROL4, 0x00);
> +	snd_soc_write(codec, ES8328_DACCONTROL5, 0x00);

This and the rest of the function looks like it should be done in a
combination of DAPM and normal ALSA controls.

> +	for (i = 0; i < 4; i++)
> +		snd_soc_write(codec, i + ES8328_DACCONTROL24, old_volumes[i]);

You are probably looking for something like SOC_DAPM_SINGLE_AUTODISABLE.

> +static const struct snd_soc_dai_ops es8328_dai_ops = {
> +	.hw_params	= es8328_hw_params,
> +	.prepare	= es8328_pcm_prepare,
> +	.shutdown	= es8328_pcm_shutdown,
> +//	.digital_mute	= es8328_mute,

Hrm?

> +static const struct of_device_id es8328_of_match[] = {
> +	{ .compatible = "everest,es8328", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, es8328_of_match);

Any device tree device needs a binding document.

[-- Attachment #1.2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/3] sound: soc: fsl: Add support for Novena onboard audio
       [not found]     ` <1391749517-11787-3-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
@ 2014-02-07 18:14       ` Mark Brown
       [not found]         ` <20140207181433.GM1757-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
  0 siblings, 1 reply; 14+ messages in thread
From: Mark Brown @ 2014-02-07 18:14 UTC (permalink / raw)
  To: Sean Cross
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Shawn Guo, Sascha Hauer,
	Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 1197 bytes --]

On Fri, Feb 07, 2014 at 01:05:16PM +0800, Sean Cross wrote:
> Novena uses an ES8328 audio codec connected via I2S.

The CODEC looked pretty simple, can this use simple-card?

> +	/* Headphone jack detection */
> +	if (gpio_is_valid(data->jack_gpio)) {
> +		ret = snd_soc_jack_new(rtd->codec, "Headset",
> +				       SND_JACK_HEADSET | SND_JACK_BTN_0,
> +				       &headset_jack);
> +		if (ret)
> +			return ret;
> +
> +		headset_jack_gpios[0].gpio = data->jack_gpio;
> +		ret = snd_soc_jack_add_gpios(&headset_jack,
> +					     ARRAY_SIZE(headset_jack_gpios),
> +					     headset_jack_gpios);
> +	}

We'd need to add support for this but that shouldn't be too hard.

> +static int imx_set_frequency(struct imx_novena_data *data, int freq) {
> +	int ret;
> +
> +	ret = clk_set_parent(data->system_cko, data->codec_clk);
> +	if (ret) {
> +		dev_err(data->dev, "unable to set clk output");
> +		return ret;
> +	}
> +
> +	ret = clk_set_parent(data->codec_clk_sel, data->codec_clk_post_div);
> +	if (ret) {
> +		dev_err(data->dev, "unable to set clk parent");
> +		return ret;
> +	}

There's supposed to be support for this sort of thing going into the
clock API with some sort of generic binding.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] sound: soc: codecs: Add es8328 codec
  2014-02-07 18:12     ` Mark Brown
@ 2014-02-08  8:10       ` Sean Cross
       [not found]         ` <52F5E661.5010807-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
       [not found]       ` <20140207181238.GL1757-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
  1 sibling, 1 reply; 14+ messages in thread
From: Sean Cross @ 2014-02-08  8:10 UTC (permalink / raw)
  To: Mark Brown; +Cc: devicetree, alsa-devel, Shawn Guo, Liam Girdwood, Sascha Hauer

On 8/2/14 2:12 AM, Mark Brown wrote:
> On Fri, Feb 07, 2014 at 01:05:15PM +0800, Sean Cross wrote:
>
> Please use subject likes matching the style for the subsystem.  If your
> changelog looks different to others in the same area it probably needs
> an update.
>
> In general this looks like it should be making much more use of the
> framework rather than open coding, it looks like it's very much hard
> coded for one use cae.
Alright, I'll try to use more of the framework provided.  I tried to use
one of the Wolfson codecs as an example.  Is there a "canonical" driver
that's up-to-date and uses an appreciable amount of framework?
>
>> +config SND_SOC_ES8328
>> +    tristate
>> +
>
> It looks like you're going to use this on an ARM system, you should add
> DT support and make it visible in Kconfig.
>
>> +static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
>> +    SND_SOC_DAPM_DAC("Speaker Volume", "HiFi Playback",
SND_SOC_NOPM, 0, 0),
>
> Don't declare a stream by name, use DAPM routes to connect the stream to
> the widget.
Where can I read up on DAPM routes?  I don't see the word "route"
mentioned in the dapm.txt SoC documentation.
>
>> +    SND_SOC_DAPM_OUTPUT("VOUTL"),
>> +    SND_SOC_DAPM_OUTPUT("VOUTR"),
>> +        SND_SOC_DAPM_INPUT("LINE_IN"),
>> +        SND_SOC_DAPM_INPUT("MIC_IN"),
>> +        SND_SOC_DAPM_OUTPUT("HP_OUT"),
>> +        SND_SOC_DAPM_OUTPUT("SPK_OUT"),
>
> Something is messed up with your indentation, spaces vs tabs I expect.
Sloppy.  I'll fix this in the next revision.
>
>> +    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
>> +
>> +    if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
>
> An else clause is more idiomatic here.
>
>> +        snd_soc_write(codec, ES8328_ADCCONTROL5, adc);
>
> It's more idiomatic to use update_bits() here and save doing a manual
> read/modify/write cycle - it also avoids the write if not needed.
>
>> +static int es8328_adc_enable(struct snd_soc_codec *codec)
>> +{
>> +    u16 reg = snd_soc_read(codec, ES8328_CHIPPOWER);
>> +    reg &= ~(ES8328_CHIPPOWER_ADCVREF_OFF |
>> +         ES8328_CHIPPOWER_ADCPLL_OFF |
>> +         ES8328_CHIPPOWER_ADCSTM_RESET |
>> +         ES8328_CHIPPOWER_ADCDIG_OFF);
>> +    snd_soc_write(codec, ES8328_CHIPPOWER, reg);
>
> This looks like it should be done in DAPM.
The codec has very strict power sequencing.  If I'm understanding the
document correctly, I'd add a DAPM widget for the DAC, and then register
a widget event for when the DAC gets opened, and do power sequencing
there?  Or is there a way to have multiple register writes defined as a
single DAPM widget?

>> +
>> +    /* Set up microphone to be differential input */
>> +    snd_soc_write(codec, ES8328_ADCCONTROL2, 0xf0);
>
> This looks like something that should be platform data and/or DAPM -
> other platforms may be wired differently.
>
>> +    /* Set ADC to act as I2S master */
>> +    snd_soc_write(codec, ES8328_ADCCONTROL3, 0x02);
>
> set_dai_fmt().
>
>> +    /* Set I2S to 16-bit mode */
>> +    snd_soc_write(codec, ES8328_ADCCONTROL4, 0x18);
>
> This should be being done in hw_params.
>> +    /* Frequency clock of 272 */
>> +    snd_soc_write(codec, ES8328_ADCCONTROL5, 0x02);
>
> What is the frequency clock in this context?
The frequency is a specific divider defined in the datasheet.  There is
a table of frequencies and dividers for various frequencies.  One option
is to code this table into the driver and pick the frequency clocks out
of the table.

I decided to assume the codec has a fixed input frequency of 22.5792
MHz.  This means the codec can support 11.025, 22.05, and 44.1 kHz, but
cannot support 48 kHz.  It should be possible to instead feed the codec
a 24 MHz clock and run it at 48 kHz.  Pulseaudio runs at 44.1 by
default, so I went with that.
>
>> +    /* Power up LOUT2 ROUT2, and power down xOUT1 */
>> +    snd_soc_write(codec, ES8328_DACPOWER,
>> +            ES8328_DACPOWER_ROUT2_ON |
>> +            ES8328_DACPOWER_LOUT2_ON);
>
> This looks like it should be being done in DAPM.
>
>> +    /* Enable click-free power up */
>> +    snd_soc_write(codec, ES8328_DACCONTROL6,
ES8328_DACCONTROL6_CLICKFREE);
>> +    snd_soc_write(codec, ES8328_DACCONTROL3, 0x36);
>
> Just do this once on startup?
Correct.
>
>> +    /* Set I2S to 16-bit mode */
>> +    snd_soc_write(codec, ES8328_DACCONTROL1,
ES8328_DACCONTROL1_DACWL_16);
>
> hw_params().
>
>> +    /* No attenuation */
>> +    snd_soc_write(codec, ES8328_DACCONTROL4, 0x00);
>> +    snd_soc_write(codec, ES8328_DACCONTROL5, 0x00);
>
> This and the rest of the function looks like it should be done in a
> combination of DAPM and normal ALSA controls.
>
>> +    for (i = 0; i < 4; i++)
>> +        snd_soc_write(codec, i + ES8328_DACCONTROL24, old_volumes[i]);
>
> You are probably looking for something like SOC_DAPM_SINGLE_AUTODISABLE.
>
>> +static const struct snd_soc_dai_ops es8328_dai_ops = {
>> +    .hw_params    = es8328_hw_params,
>> +    .prepare    = es8328_pcm_prepare,
>> +    .shutdown    = es8328_pcm_shutdown,
>> +//    .digital_mute    = es8328_mute,
>
> Hrm?
Oops.  Leftover from some anti-pop work I did earlier.  I'll remove it.
>
>> +static const struct of_device_id es8328_of_match[] = {
>> +    { .compatible = "everest,es8328", },
>> +    { }
>> +};
>> +MODULE_DEVICE_TABLE(of, es8328_of_match);
>
> Any device tree device needs a binding document.
My fault for omitting one.
Much of the code in adc_enable() and dac_enable() come from the
reference manual.  They indicate that the codec has very strict ordering
requirements on registers that must be set during playback and
recording.  Again, is there any way to ensure that hw_params(),
set_dai_fmt(), and the DAPM widgets are called in a specific order?

Thank you for the feedback.

Sean

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] sound: soc: codecs: Add es8328 codec
       [not found]       ` <20140207181238.GL1757-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
@ 2014-02-08  8:17         ` Sean Cross
  2014-02-10  9:24         ` Sean Cross
  1 sibling, 0 replies; 14+ messages in thread
From: Sean Cross @ 2014-02-08  8:17 UTC (permalink / raw)
  To: Mark Brown
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Shawn Guo, Sascha Hauer,
	Liam Girdwood

On 8/2/14 2:12 AM, Mark Brown wrote:
> On Fri, Feb 07, 2014 at 01:05:15PM +0800, Sean Cross wrote:
> 
> Please use subject likes matching the style for the subsystem.  If
> your changelog looks different to others in the same area it
> probably needs an update.
> 
> In general this looks like it should be making much more use of
> the framework rather than open coding, it looks like it's very much
> hard coded for one use cae.

Alright, I'll try to use more of the framework provided.  I tried to
use one of the Wolfson codecs as an example.  Is there a "canonical"
driver that's up-to-date and uses an appreciable amount of framework?

>> +config SND_SOC_ES8328 +	tristate +
> 
> It looks like you're going to use this on an ARM system, you should
> add DT support and make it visible in Kconfig.
> 
>> +static const struct snd_soc_dapm_widget es8328_dapm_widgets[] =
>> { +	SND_SOC_DAPM_DAC("Speaker Volume", "HiFi Playback",
>> SND_SOC_NOPM, 0, 0),
> 
> Don't declare a stream by name, use DAPM routes to connect the
> stream to the widget.

Where can I read up on DAPM routes?  I don't see the word "route"
mentioned in the dapm.txt SoC documentation.

>> +	SND_SOC_DAPM_OUTPUT("VOUTL"), +	SND_SOC_DAPM_OUTPUT("VOUTR"), +
>> SND_SOC_DAPM_INPUT("LINE_IN"), +
>> SND_SOC_DAPM_INPUT("MIC_IN"), +
>> SND_SOC_DAPM_OUTPUT("HP_OUT"), +
>> SND_SOC_DAPM_OUTPUT("SPK_OUT"),
> 
> Something is messed up with your indentation, spaces vs tabs I
> expect.

Sloppy.  I'll fix this in the next revision.

Also, this is hard-coding widgets for this platform.  In reality,
there's an output LOUT1, ROUT1, LOUT2, ROUT2, in addition to LDAC and
RDAC.  The DAC outputs to both OUT1 and OUT2.  I feel this is the sort
of thing DAPM was designed to accommodate.

How can I specify LOUT1 etc., and how can I define the mapping of
outputs on my particular platform?  E.g. OUT2 goes to the Headset on
this board.

>> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + +	if
>> (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> 
> An else clause is more idiomatic here.
> 
>> +		snd_soc_write(codec, ES8328_ADCCONTROL5, adc);
> 
> It's more idiomatic to use update_bits() here and save doing a
> manual read/modify/write cycle - it also avoids the write if not
> needed.

That makes sense.

>> +static int es8328_adc_enable(struct snd_soc_codec *codec) +{ +
>> u16 reg = snd_soc_read(codec, ES8328_CHIPPOWER); +	reg &=
>> ~(ES8328_CHIPPOWER_ADCVREF_OFF | +		 ES8328_CHIPPOWER_ADCPLL_OFF
>> | +		 ES8328_CHIPPOWER_ADCSTM_RESET | +
>> ES8328_CHIPPOWER_ADCDIG_OFF); +	snd_soc_write(codec,
>> ES8328_CHIPPOWER, reg);
> 
> This looks like it should be done in DAPM.

The codec has very strict power sequencing.  If I'm understanding the
document correctly, I'd add a DAPM widget for the DAC, and then
register a widget event for when the DAC gets opened, and do power
sequencing there?  Or is there a way to have multiple register writes
defined as a single DAPM widget?

>> + +	/* Set up microphone to be differential input */ +
>> snd_soc_write(codec, ES8328_ADCCONTROL2, 0xf0);
> 
> This looks like something that should be platform data and/or DAPM
> - other platforms may be wired differently.
> 
>> +	/* Set ADC to act as I2S master */ +	snd_soc_write(codec,
>> ES8328_ADCCONTROL3, 0x02);
> 
> set_dai_fmt().
> 
>> +	/* Set I2S to 16-bit mode */ +	snd_soc_write(codec,
>> ES8328_ADCCONTROL4, 0x18);
> 
> This should be being done in hw_params.
> 
>> +	/* Frequency clock of 272 */ +	snd_soc_write(codec,
>> ES8328_ADCCONTROL5, 0x02);
> 
> What is the frequency clock in this context?

The frequency is a specific divider defined in the datasheet.  There
is a table of frequencies and dividers for various frequencies.  One
option is to code this table into the driver and pick the frequency
clocks out of the table.

I decided to assume the codec has a fixed input frequency of 22.5792
MHz.  This means the codec can support 11.025, 22.05, and 44.1 kHz,
but cannot support 48 kHz.  It should be possible to instead feed the
codec a 24 MHz clock and run it at 48 kHz.  Pulseaudio runs at 44.1 by
default, so I went with that.

>> +	/* Power up LOUT2 ROUT2, and power down xOUT1 */ +
>> snd_soc_write(codec, ES8328_DACPOWER, +
>> ES8328_DACPOWER_ROUT2_ON | +			ES8328_DACPOWER_LOUT2_ON);
> 
> This looks like it should be being done in DAPM.
> 
>> +	/* Enable click-free power up */ +	snd_soc_write(codec,
>> ES8328_DACCONTROL6, ES8328_DACCONTROL6_CLICKFREE); +
>> snd_soc_write(codec, ES8328_DACCONTROL3, 0x36);
> 
> Just do this once on startup?

Correct.

>> +	/* Set I2S to 16-bit mode */ +	snd_soc_write(codec,
>> ES8328_DACCONTROL1, ES8328_DACCONTROL1_DACWL_16);
> 
> hw_params().
> 
>> +	/* No attenuation */ +	snd_soc_write(codec, ES8328_DACCONTROL4,
>> 0x00); +	snd_soc_write(codec, ES8328_DACCONTROL5, 0x00);
> 
> This and the rest of the function looks like it should be done in
> a combination of DAPM and normal ALSA controls.
> 
>> +	for (i = 0; i < 4; i++) +		snd_soc_write(codec, i +
>> ES8328_DACCONTROL24, old_volumes[i]);
> 
> You are probably looking for something like
> SOC_DAPM_SINGLE_AUTODISABLE.
> 
>> +static const struct snd_soc_dai_ops es8328_dai_ops = { +
>> .hw_params	= es8328_hw_params, +	.prepare	= es8328_pcm_prepare, +
>> .shutdown	= es8328_pcm_shutdown, +//	.digital_mute	=
>> es8328_mute,
> 
> Hrm?

Oops.  Leftover from some anti-pop work I did earlier.  I'll remove it.

>> +static const struct of_device_id es8328_of_match[] = { +	{
>> .compatible = "everest,es8328", }, +	{ } +}; 
>> +MODULE_DEVICE_TABLE(of, es8328_of_match);
> 
> Any device tree device needs a binding document.
> 

My fault for omitting one.

Much of the code in adc_enable() and dac_enable() come from the
reference manual.  They indicate that the codec has very strict
ordering requirements on registers that must be set during playback
and recording.  Again, is there any way to ensure that hw_params(),
set_dai_fmt(), and the DAPM widgets are called in a specific order?

Thank you for the feedback.

Sean

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/3] sound: soc: fsl: Add support for Novena onboard audio
       [not found]         ` <20140207181433.GM1757-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
@ 2014-02-08  8:27           ` Sean Cross
       [not found]             ` <52F5EA67.1080008-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
  0 siblings, 1 reply; 14+ messages in thread
From: Sean Cross @ 2014-02-08  8:27 UTC (permalink / raw)
  To: Mark Brown
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Shawn Guo, Sascha Hauer,
	Liam Girdwood

On 8/2/14 2:14 AM, Mark Brown wrote:
> On Fri, Feb 07, 2014 at 01:05:16PM +0800, Sean Cross wrote:
>> Novena uses an ES8328 audio codec connected via I2S.
> 
> The CODEC looked pretty simple, can this use simple-card?

I had never heard of simple-card.  It doesn't look like it supports
device tree.  Novena doesn't have a platform definition, and we run
entirely using DT.  If simple-card can work without a platform
definition, then we can use it.  Otherwise, it's nearly identical to
imx-sgtl5000.c, with the exception of the code you called out.

>> +	/* Headphone jack detection */ +	if
>> (gpio_is_valid(data->jack_gpio)) { +		ret =
>> snd_soc_jack_new(rtd->codec, "Headset", +
>> SND_JACK_HEADSET | SND_JACK_BTN_0, +				       &headset_jack); +
>> if (ret) +			return ret; + +		headset_jack_gpios[0].gpio =
>> data->jack_gpio; +		ret = snd_soc_jack_add_gpios(&headset_jack, +
>> ARRAY_SIZE(headset_jack_gpios), +					     headset_jack_gpios); +
>> }
> 
> We'd need to add support for this but that shouldn't be too hard.

It does seem simple.  I could imagine specifying the jack in a device
tree file, and mapping it to a GPIO.

Can you think of why this would oops on removal?  If I disable
headphone jack detection (by simply making data->jack_gpio an invalid
gpio), the module removes cleanly.

>> +static int imx_set_frequency(struct imx_novena_data *data, int
>> freq) { +	int ret; + +	ret = clk_set_parent(data->system_cko,
>> data->codec_clk); +	if (ret) { +		dev_err(data->dev, "unable to
>> set clk output"); +		return ret; +	} + +	ret =
>> clk_set_parent(data->codec_clk_sel, data->codec_clk_post_div); +
>> if (ret) { +		dev_err(data->dev, "unable to set clk parent"); +
>> return ret; +	}
> 
> There's supposed to be support for this sort of thing going into
> the clock API with some sort of generic binding.

Can you direct me to this patch?  Is it supposed to be defined in the
device tree file?  As of right now, the clock is hardcoded to 24 MHz
in clk-imx6q.c, which is why we need to change it here.

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [alsa-devel] [PATCH 1/3] sound: soc: codecs: Add es8328 codec
       [not found]     ` <1391749517-11787-2-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
@ 2014-02-08 16:06       ` Lars-Peter Clausen
  0 siblings, 0 replies; 14+ messages in thread
From: Lars-Peter Clausen @ 2014-02-08 16:06 UTC (permalink / raw)
  To: Sean Cross
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Mark Brown, Shawn Guo,
	Liam Girdwood, Sascha Hauer

On 02/07/2014 06:05 AM, Sean Cross wrote:
> Add support for the ES8328 audio codec.

Hi,

a couple of minor coments. Also when submitting a patch it is a good idea to 
run scripts/checkpatch.pl on the patch itself and `make C=2` and `make C=2 
CHECK=scripts/coccicheck` on your driver module (E.g. in your case `make C=2 
sound/soc/codecs/es8328.o`). These tools check your driver for a couple of 
known issues. Fixing those before the submission will make the reviews job 
easier.
> [...]
> +uint8_t sample_ratios[] = {

static const

> +	[ES8328_RATE_8019 ] = 0x9,
> +	[ES8328_RATE_11025] = 0x7,
> +	[ES8328_RATE_22050] = 0x4,
> +	[ES8328_RATE_44100] = 0x2,
> +};
> [...]
> +
> +static int es8328_hw_params(struct snd_pcm_substream *substream,
> +	struct snd_pcm_hw_params *params,
> +	struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_codec *codec = dai->codec;
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		u8 dac = snd_soc_read(codec, ES8328_DACCONTROL2);
> +		dac &= ~ES8328_DACCONTROL2_RATEMASK;
> +
> +		switch (params_rate(params)) {
> +		case 8000:
> +			dac |= sample_ratios[ES8328_RATE_8019];
> +			break;
> +		case 11025:
> +			dac |= sample_ratios[ES8328_RATE_11025];
> +			break;
> +		case 22050:
> +			dac |= sample_ratios[ES8328_RATE_22050];
> +			break;
> +		case 44100:
> +			dac |= sample_ratios[ES8328_RATE_44100];
> +			break;
> +		default:
> +			dev_err(codec->dev, "%s: unknown rate %d\n",
> +				 __func__, params_rate(params));
> +			return -EINVAL;
> +		}
> +		snd_soc_write(codec, ES8328_DACCONTROL2, dac);
> +	}
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> +		u8 adc = snd_soc_read(codec, ES8328_ADCCONTROL5);
> +		adc &= ~ES8328_ADCCONTROL5_RATEMASK;
> +
> +		switch (params_rate(params)) {
> +		case 8000:
> +			adc |= sample_ratios[ES8328_RATE_8019];
> +			break;
> +		case 11025:
> +			adc |= sample_ratios[ES8328_RATE_11025];
> +			break;
> +		case 22050:
> +			adc |= sample_ratios[ES8328_RATE_22050];
> +			break;
> +		case 44100:
> +			adc |= sample_ratios[ES8328_RATE_44100];
> +			break;
> +		default:
> +			dev_err(codec->dev, "%s: unknown rate %d\n",
> +				 __func__, params_rate(params));
> +			return -EINVAL;
> +		}
> +		snd_soc_write(codec, ES8328_ADCCONTROL5, adc);
> +	}

The only difference between the capture and the playback path seems to be 
the register that's written two. By adding a variable that holds the 
register number and gets initialized based on the direction you can elimate 
about half of the code in this function.

> +
> +	return 0;
[...]
> +static int es8328_set_dai_sysclk(struct snd_soc_dai *codec_dai,
> +			         int clk_id, unsigned int freq, int dir)
> +{
> +	struct snd_soc_codec *codec = codec_dai->codec;
> +	struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
> +
> +	switch (clk_id) {
> +	case 0:
> +		es8328->sysclk = freq;

es8328->sysclk seems to be unused. I guess it might be used if you add 
support for different base clock rates. For now you should probably return 
an error if freq != 22579200.

> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
[...]
> +static int es8328_dac_enable(struct snd_soc_codec *codec)
> +{
> +	u16 old_volumes[4];
> +	u16 reg;
> +	int i;
> +
> +	for (i = 0; i < 4; i++) {
> +		old_volumes[i] = snd_soc_read(codec, i + ES8328_DACCONTROL24);
> +		snd_soc_write(codec, i + ES8328_DACCONTROL24, 0);
> +	}

This looks like a job for AUTOMUTE controls. They'll set the volume to zero 
before the DAC is powered down and restore the original value after the DAC 
was powered up.

> +
> +	/* Power up LOUT2 ROUT2, and power down xOUT1 */
> +	snd_soc_write(codec, ES8328_DACPOWER,
> +			ES8328_DACPOWER_ROUT2_ON |
> +			ES8328_DACPOWER_LOUT2_ON);
> +
> +	/* Enable click-free power up */
> +	snd_soc_write(codec, ES8328_DACCONTROL6, ES8328_DACCONTROL6_CLICKFREE);
> +	snd_soc_write(codec, ES8328_DACCONTROL3, 0x36);
> +
> +	/* Set I2S to 16-bit mode */
> +	snd_soc_write(codec, ES8328_DACCONTROL1, ES8328_DACCONTROL1_DACWL_16);
> +
> +	/* No attenuation */
> +	snd_soc_write(codec, ES8328_DACCONTROL4, 0x00);
> +	snd_soc_write(codec, ES8328_DACCONTROL5, 0x00);
> +
> +	/* Set LIN2 for the output mixer */
> +	snd_soc_write(codec, ES8328_DACCONTROL16,
> +			ES8328_DACCONTROL16_RMIXSEL_RIN2 |
> +			ES8328_DACCONTROL16_LMIXSEL_LIN2);
> +
> +	/* Point the left DAC at the left mixer */
> +	snd_soc_write(codec, ES8328_DACCONTROL17, ES8328_DACCONTROL17_LD2LO);
> +	/* Point the right DAC at the right mixer */
> +	snd_soc_write(codec, ES8328_DACCONTROL20, ES8328_DACCONTROL20_RD2RO);
> +
> +	/* Disable all other outputs */
> +	snd_soc_write(codec, ES8328_DACCONTROL18, 0x00);
> +	snd_soc_write(codec, ES8328_DACCONTROL19, 0x00);
> +
> +
> +	/* Disable mono mode for DACL, and mute DACR */
> +	snd_soc_write(codec, ES8328_DACCONTROL7, 0x00);
> +
> +	for (i = 0; i < 4; i++)
> +		snd_soc_write(codec, i + ES8328_DACCONTROL24, old_volumes[i]);
> +
> +	reg = snd_soc_read(codec, ES8328_CHIPPOWER);
> +	reg &= ~(ES8328_CHIPPOWER_DACVREF_OFF |
> +		 ES8328_CHIPPOWER_DACPLL_OFF |
> +		 ES8328_CHIPPOWER_DACSTM_RESET |
> +		 ES8328_CHIPPOWER_DACDIG_OFF);
> +	snd_soc_write(codec, ES8328_CHIPPOWER, reg);
> +	snd_soc_write(codec, ES8328_DACCONTROL3, 0x32);
> +
> +	return 0;
> +}
[...]
> +static int es8328_suspend(struct snd_soc_codec *codec)
> +{
> +	return 0;
> +}
> +

If you don't do anything in suspend there is no need to provide a callback 
function.

> +static int es8328_resume(struct snd_soc_codec *codec)
> +{
> +	es8328_init(codec);
> +	return 0;
> +}
> +
> +static int es8328_probe(struct snd_soc_codec *codec)
> +{
> +	int ret;
> +	struct device *dev = codec->dev;
> +
> +	ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_REGMAP);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to configure cache I/O: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* power on device */
> +	es8328_init(codec);
> +
> +	return 0;
> +}
> +
> +static int es8328_remove(struct snd_soc_codec *codec)
> +{
> +	/* Power everything down and reset the cip */
> +	snd_soc_write(codec, ES8328_CHIPPOWER,
> +			ES8328_CHIPPOWER_DACSTM_RESET |
> +			ES8328_CHIPPOWER_ADCSTM_RESET |
> +			ES8328_CHIPPOWER_DACDIG_OFF |
> +			ES8328_CHIPPOWER_ADCDIG_OFF |
> +			ES8328_CHIPPOWER_DACVREF_OFF |
> +			ES8328_CHIPPOWER_ADCVREF_OFF);
> +
> +	return 0;
> +}
> +
> +static struct snd_soc_codec_driver soc_codec_dev_es8328 = {

es8328_codec_driver is a better name.

THe soc_codec_dev_... naming scheme you see in some drivers comes from a pre 
multi-component era where it was more firt

> +	.probe =		es8328_probe,
> +	.remove =		es8328_remove,
> +	.suspend =		es8328_suspend,
> +	.resume =		es8328_resume,
> +	.controls =		es8328_snd_controls,
> +	.num_controls =		ARRAY_SIZE(es8328_snd_controls),
> +	.dapm_widgets =		es8328_dapm_widgets,
> +	.num_dapm_widgets =	ARRAY_SIZE(es8328_dapm_widgets),
> +	.dapm_routes =		es8328_intercon,
> +	.num_dapm_routes =	ARRAY_SIZE(es8328_intercon),
> +};
[...]
> +#if defined(CONFIG_SPI_MASTER)
> +static int es8328_spi_probe(struct spi_device *spi)
> +{
> +	struct es8328_priv *es8328;
> +	int ret;
> +
> +	es8328 = devm_kzalloc(&spi->dev, sizeof(struct es8328_priv),
> +			      GFP_KERNEL);

sizeof(*es8328) is the preferred style for kernel code.

> +	if (es8328 == NULL)
> +		return -ENOMEM;
> +
> +	es8328->regmap = devm_regmap_init_spi(spi, &es8328_regmap);
> +	if (IS_ERR(es8328->regmap))
> +		return PTR_ERR(es8328->regmap);
> +
> +	spi_set_drvdata(spi, es8328);
> +
> +	ret = snd_soc_register_codec(&spi->dev,
> +			&soc_codec_dev_es8328, &es8328_dai, 1);
> +	if (ret < 0)
> +		dev_err(&spi->dev, "unable to register codec: %d\n", ret);
> +
> +	return ret;
> +}
[...]
> +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
> +static int es8328_i2c_probe(struct i2c_client *i2c,
> +			    const struct i2c_device_id *id)
> +{
> +	struct es8328_priv *es8328;
> +	int ret;
> +
> +	es8328 = devm_kzalloc(&i2c->dev, sizeof(struct es8328_priv),
> +			      GFP_KERNEL);

same here.

> +	if (es8328 == NULL)
> +		return -ENOMEM;
> +
> +	es8328->regmap = devm_regmap_init_i2c(i2c, &es8328_regmap);
> +	if (IS_ERR(es8328->regmap))
> +		return PTR_ERR(es8328->regmap);
> +
> +	i2c_set_clientdata(i2c, es8328);
> +
> +	ret =  snd_soc_register_codec(&i2c->dev,
> +			&soc_codec_dev_es8328, &es8328_dai, 1);
> +
> +	return ret;
> +}
> +
[...]
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] sound: soc: codecs: Add es8328 codec
       [not found]       ` <20140207181238.GL1757-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
  2014-02-08  8:17         ` Sean Cross
@ 2014-02-10  9:24         ` Sean Cross
       [not found]           ` <52F89ADF.8020305-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
  1 sibling, 1 reply; 14+ messages in thread
From: Sean Cross @ 2014-02-10  9:24 UTC (permalink / raw)
  To: Mark Brown
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Shawn Guo, Sascha Hauer,
	Liam Girdwood

On 8/2/14 2:12 AM, Mark Brown wrote:
> On Fri, Feb 07, 2014 at 01:05:15PM +0800, Sean Cross wrote:
> 
> Please use subject likes matching the style for the subsystem.  If your
> changelog looks different to others in the same area it probably needs
> an update.
> 
> In general this looks like it should be making much more use of the
> framework rather than open coding, it looks like it's very much hard
> coded for one use cae.

I've tried to simplify the code somewhat, but I'm still unclear about
how much of the DAPM code functions.  I'm running into two problems:
Mapping the codec names "OUT1" and "OUT2" onto "Speaker" and "Headphone"
respectively, and accommodating this codec's one DAC / two outputs
architecture.

First, this particular codec doesn't specify what the outputs are used
for, it only specifies the names OUT1 and OUT2.  It is permitted to
connect either a speaker or headphones to either output.  In the initial
patch, I hardcoded OUT1 to be named "Speaker", and OUT2 to "Headphone".
 However, I've genericised the codec and changed them back to "OUT1" and
"OUT2".  Inthe board definition I've added DAPM routes that map
"Speaker" and "Headphone" back onto these names.  amixer still shows
OUT1 and OUT2.

There seems to be some overlap between DAPM and kcontrols.  How can I
specify in the board definition file that OUT1 should have a kcontrol
named "Speaker" and OUT2 should be named "Headphone"?  Tracing shows
that DAPM at least recognizes the naming: e.g. "snd_soc_dapm_input_path:
*Speaker <- (direct) <- ROUT1"

Second, I'm having trouble getting the DAPM routing to function
properly.  There is one DAC that routes to two outputs, OUT1 and OUT2.
Both OUT1 and OUT2 have their own volume controls as well, but the main
state machine and amplification comes from the DAC.  This doesn't seem
to be very common.  Are there any codecs I can use as an example that
have one DAC routing to two outputs?


Sean
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] sound: soc: codecs: Add es8328 codec
       [not found]         ` <52F5E661.5010807-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
@ 2014-02-10 14:23           ` Mark Brown
  0 siblings, 0 replies; 14+ messages in thread
From: Mark Brown @ 2014-02-10 14:23 UTC (permalink / raw)
  To: Sean Cross
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Shawn Guo, Sascha Hauer,
	Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 3534 bytes --]

On Sat, Feb 08, 2014 at 04:10:09PM +0800, Sean Cross wrote:
> On 8/2/14 2:12 AM, Mark Brown wrote:

> > In general this looks like it should be making much more use of the
> > framework rather than open coding, it looks like it's very much hard
> > coded for one use cae.

> Alright, I'll try to use more of the framework provided.  I tried to use
> one of the Wolfson codecs as an example.  Is there a "canonical" driver
> that's up-to-date and uses an appreciable amount of framework?

Try wm8731 I guess, something similar to the device, there's a lot of
variation in device complexity and several generations of hardware?  I'm
not sure what driver you were looking at here.

> >> +static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
> >> +    SND_SOC_DAPM_DAC("Speaker Volume", "HiFi Playback",
> SND_SOC_NOPM, 0, 0),

> > Don't declare a stream by name, use DAPM routes to connect the stream to
> > the widget.

> Where can I read up on DAPM routes?  I don't see the word "route"
> mentioned in the dapm.txt SoC documentation.

Paths.

> >> +static int es8328_adc_enable(struct snd_soc_codec *codec)
> >> +{
> >> +    u16 reg = snd_soc_read(codec, ES8328_CHIPPOWER);
> >> +    reg &= ~(ES8328_CHIPPOWER_ADCVREF_OFF |
> >> +         ES8328_CHIPPOWER_ADCPLL_OFF |
> >> +         ES8328_CHIPPOWER_ADCSTM_RESET |
> >> +         ES8328_CHIPPOWER_ADCDIG_OFF);
> >> +    snd_soc_write(codec, ES8328_CHIPPOWER, reg);

> > This looks like it should be done in DAPM.

> The codec has very strict power sequencing.  If I'm understanding the
> document correctly, I'd add a DAPM widget for the DAC, and then register
> a widget event for when the DAC gets opened, and do power sequencing
> there?  Or is there a way to have multiple register writes defined as a
> single DAPM widget?

An event callback is the way to go.  However please check what the
datasheet is actually trying to say - I suspect you will find it's
giving you a procedure that's got common elements for core power up and
then is saying something like "keep the outputs muted while you power
them up" but is doing it with lots of examples.

> >> +    /* Frequency clock of 272 */
> >> +    snd_soc_write(codec, ES8328_ADCCONTROL5, 0x02);

> > What is the frequency clock in this context?

> The frequency is a specific divider defined in the datasheet.  There is
> a table of frequencies and dividers for various frequencies.  One option
> is to code this table into the driver and pick the frequency clocks out
> of the table.

That seems much better.

> I decided to assume the codec has a fixed input frequency of 22.5792
> MHz.  This means the codec can support 11.025, 22.05, and 44.1 kHz, but
> cannot support 48 kHz.  It should be possible to instead feed the codec
> a 24 MHz clock and run it at 48 kHz.  Pulseaudio runs at 44.1 by
> default, so I went with that.

This is what set_sysclk() is for, letting the driver know what the clock
rate is on the current board (if you are willing to go DT only you can
just use the clock API).

> My fault for omitting one.
> Much of the code in adc_enable() and dac_enable() come from the
> reference manual.  They indicate that the codec has very strict ordering
> requirements on registers that must be set during playback and
> recording.  Again, is there any way to ensure that hw_params(),
> set_dai_fmt(), and the DAPM widgets are called in a specific order?

No, there's no way to do that.  Like I say I am somewhat mistrustful of
the clarity/accuracy of the hardware limitations being described here.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/3] sound: soc: codecs: Add es8328 codec
       [not found]           ` <52F89ADF.8020305-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
@ 2014-02-10 14:27             ` Mark Brown
  0 siblings, 0 replies; 14+ messages in thread
From: Mark Brown @ 2014-02-10 14:27 UTC (permalink / raw)
  To: Sean Cross
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Shawn Guo, Sascha Hauer,
	Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 2483 bytes --]

On Mon, Feb 10, 2014 at 05:24:47PM +0800, Sean Cross wrote:
> On 8/2/14 2:12 AM, Mark Brown wrote:

> > In general this looks like it should be making much more use of the
> > framework rather than open coding, it looks like it's very much hard
> > coded for one use cae.

> I've tried to simplify the code somewhat, but I'm still unclear about
> how much of the DAPM code functions.  I'm running into two problems:
> Mapping the codec names "OUT1" and "OUT2" onto "Speaker" and "Headphone"
> respectively, and accommodating this codec's one DAC / two outputs
> architecture.

Describe the hardware accurately.  If the hardware has outputs called
OUT1 and OUT2 that aren't fixed function in some way then describe them
like that, if the CODEC has routing control just describe the routing
control.

> First, this particular codec doesn't specify what the outputs are used
> for, it only specifies the names OUT1 and OUT2.  It is permitted to
> connect either a speaker or headphones to either output.  In the initial
> patch, I hardcoded OUT1 to be named "Speaker", and OUT2 to "Headphone".
>  However, I've genericised the codec and changed them back to "OUT1" and
> "OUT2".  Inthe board definition I've added DAPM routes that map
> "Speaker" and "Headphone" back onto these names.  amixer still shows
> OUT1 and OUT2.

Good.

> There seems to be some overlap between DAPM and kcontrols.  How can I
> specify in the board definition file that OUT1 should have a kcontrol
> named "Speaker" and OUT2 should be named "Headphone"?  Tracing shows
> that DAPM at least recognizes the naming: e.g. "snd_soc_dapm_input_path:
> *Speaker <- (direct) <- ROUT1"

You can't, do this in your userspace configuration - UCM contains tools
to help do remapping if you are using raw ALSA.  Or extend the in kernel
frameworks but that's probably more trouble than it's worth and it's not
clear it's even a good idea to have the complexity in kernel.

> Second, I'm having trouble getting the DAPM routing to function
> properly.  There is one DAC that routes to two outputs, OUT1 and OUT2.
> Both OUT1 and OUT2 have their own volume controls as well, but the main
> state machine and amplification comes from the DAC.  This doesn't seem
> to be very common.  Are there any codecs I can use as an example that
> have one DAC routing to two outputs?

That's really common - the overwhelming majority of CODECs have such a
design if they have multiple outputs.  Could you articulate what's
unclear here?

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 2/3] sound: soc: fsl: Add support for Novena onboard audio
       [not found]             ` <52F5EA67.1080008-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
@ 2014-02-10 14:45               ` Mark Brown
  0 siblings, 0 replies; 14+ messages in thread
From: Mark Brown @ 2014-02-10 14:45 UTC (permalink / raw)
  To: Sean Cross
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Shawn Guo, Sascha Hauer,
	Liam Girdwood

[-- Attachment #1: Type: text/plain, Size: 1079 bytes --]

On Sat, Feb 08, 2014 at 04:27:19PM +0800, Sean Cross wrote:
> On 8/2/14 2:14 AM, Mark Brown wrote:

> > The CODEC looked pretty simple, can this use simple-card?

> I had never heard of simple-card.  It doesn't look like it supports
> device tree.  Novena doesn't have a platform definition, and we run

Yes it does, please look at current code (you should be submitting
against -next but the DT code is in Linus' tree).

> Can you think of why this would oops on removal?  If I disable
> headphone jack detection (by simply making data->jack_gpio an invalid
> gpio), the module removes cleanly.

Nothing immediate springs to mind, sorry.  What's the oops.

> > There's supposed to be support for this sort of thing going into
> > the clock API with some sort of generic binding.

> Can you direct me to this patch?  Is it supposed to be defined in the
> device tree file?  As of right now, the clock is hardcoded to 24 MHz
> in clk-imx6q.c, which is why we need to change it here.

I'm not sure it's actually been written yet, sorry - I'd ask Mike
Turquette what the status is.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2014-02-10 14:45 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-07  5:05 [PATCH 0/3] Add i.MX6q Kosagi Novena support Sean Cross
     [not found] ` <1391749517-11787-1-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
2014-02-07  5:05   ` [PATCH 1/3] sound: soc: codecs: Add es8328 codec Sean Cross
2014-02-07 18:12     ` Mark Brown
2014-02-08  8:10       ` Sean Cross
     [not found]         ` <52F5E661.5010807-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
2014-02-10 14:23           ` Mark Brown
     [not found]       ` <20140207181238.GL1757-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
2014-02-08  8:17         ` Sean Cross
2014-02-10  9:24         ` Sean Cross
     [not found]           ` <52F89ADF.8020305-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
2014-02-10 14:27             ` Mark Brown
     [not found]     ` <1391749517-11787-2-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
2014-02-08 16:06       ` [alsa-devel] " Lars-Peter Clausen
2014-02-07  5:05   ` [PATCH 2/3] sound: soc: fsl: Add support for Novena onboard audio Sean Cross
     [not found]     ` <1391749517-11787-3-git-send-email-xobs-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
2014-02-07 18:14       ` Mark Brown
     [not found]         ` <20140207181433.GM1757-GFdadSzt00ze9xe1eoZjHA@public.gmane.org>
2014-02-08  8:27           ` Sean Cross
     [not found]             ` <52F5EA67.1080008-nXMMniAx+RbQT0dZR+AlfA@public.gmane.org>
2014-02-10 14:45               ` Mark Brown
2014-02-07  5:05   ` [PATCH 3/3] dts: imx: add kosagi novena imx6q dts file Sean Cross

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).