All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch 0/3] ASoC: t5325 audio support
@ 2010-09-07  7:01 Arnaud Patard
  2010-09-07  7:01 ` [patch 1/3] ASoC: add support for alc562[123] codecs Arnaud Patard
                   ` (2 more replies)
  0 siblings, 3 replies; 11+ messages in thread
From: Arnaud Patard @ 2010-09-07  7:01 UTC (permalink / raw)
  To: alsa-devel; +Cc: Mark Brown, Martin Michlmayr, Saeed Bishara, Liam Girdwood



Hi,

This patchset is the first try to get merged audio support for HP t5325 
thinclient. This board is using a ALC5621 so the major part of this series 
is made of the ALC5623 driver. It's supporting ALC5621/22/23 but has been
tested only with a ALC5621.


Arnaud

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

* [patch 1/3] ASoC: add support for alc562[123] codecs
  2010-09-07  7:01 [patch 0/3] ASoC: t5325 audio support Arnaud Patard
@ 2010-09-07  7:01 ` Arnaud Patard
  2010-09-07 10:21   ` Mark Brown
  2010-09-07  7:01 ` [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients Arnaud Patard
  2010-09-07  7:01 ` [patch 3/3] t5325: add audio support Arnaud Patard
  2 siblings, 1 reply; 11+ messages in thread
From: Arnaud Patard @ 2010-09-07  7:01 UTC (permalink / raw)
  To: alsa-devel
  Cc: Arnaud Patard, Mark Brown, Martin Michlmayr, Saeed Bishara,
	Liam Girdwood

[-- Attachment #1: alc56xx.patch --]
[-- Type: text/plain, Size: 45443 bytes --]

This patch is adding support for alc562[123] codecs. It's based
on the source code available in HP source code and other places.

Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>


Index: sound-2.6/sound/soc/codecs/Kconfig
===================================================================
--- sound-2.6.orig/sound/soc/codecs/Kconfig	2010-09-06 23:27:20.000000000 +0200
+++ sound-2.6/sound/soc/codecs/Kconfig	2010-09-06 23:29:31.000000000 +0200
@@ -159,6 +159,9 @@
 config SND_SOC_PCM3008
        tristate
 
+config SND_SOC_ALC5623
+       tristate
+
 config SND_SOC_SPDIF
 	tristate
 
@@ -305,3 +308,4 @@
 
 config SND_SOC_WM9090
 	tristate
+
Index: sound-2.6/sound/soc/codecs/Makefile
===================================================================
--- sound-2.6.orig/sound/soc/codecs/Makefile	2010-09-06 23:27:20.000000000 +0200
+++ sound-2.6/sound/soc/codecs/Makefile	2010-09-06 23:29:31.000000000 +0200
@@ -16,6 +16,7 @@
 snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
 snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-alc5623-objs := alc5623.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-stac9766-objs := stac9766.o
@@ -88,6 +89,7 @@
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
+obj-$(CONFIG_SND_SOC_ALC5623)    += snd-soc-alc5623.o
 obj-$(CONFIG_SND_SOC_SPDIF)	+= snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
Index: sound-2.6/sound/soc/codecs/alc5623.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ sound-2.6/sound/soc/codecs/alc5623.c	2010-09-06 23:29:31.000000000 +0200
@@ -0,0 +1,1161 @@
+/*
+ * alc5623.c  --  alc562[123] ALSA Soc Audio driver
+ *
+ * Copyright 2008 Realtek Microelectronics
+ * Author: flove <flove@realtek.com> Ethan <eku@marvell.com>
+ *
+ * Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *
+ * Based on WM8753.c
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/alc5623.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "alc5623.h"
+
+#define ALC5623_VERSION "0.01"
+
+static int caps_charge = 2000;
+module_param(caps_charge, int, 0);
+MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");
+
+/* codec private data */
+struct alc5623_priv {
+	enum snd_soc_control_type control_type;
+	void *control_data;
+	struct mutex mutex;
+	u8 id;
+	unsigned int sysclk;
+	u16 reg_cache[ALC5623_VENDOR_ID2+2];
+	unsigned int add_ctrl;
+	unsigned int jack_det_ctrl;
+};
+
+static void alc5623_fill_cache(struct snd_soc_codec *codec)
+{
+	int i, step = codec->driver->reg_cache_step;
+	u16 *cache = codec->reg_cache;
+
+	/* not really efficient ... */
+	for (i = 0 ; i < codec->driver->reg_cache_size ; i += step)
+		cache[i] = codec->hw_read(codec, i);
+}
+
+static int alc5623_write_mask(struct snd_soc_codec *codec, unsigned int reg,
+		unsigned int value, unsigned int mask)
+{
+
+	unsigned int data = value;
+
+	if (mask && (mask != 0xffff)) {
+		data = snd_soc_read(codec, reg);
+		data &= ~mask;
+		data |= (value & mask);
+	}
+
+	return snd_soc_write(codec, reg, data);
+}
+
+static inline int alc5623_reset(struct snd_soc_codec *codec)
+{
+	return snd_soc_write(codec, ALC5623_RESET, 0);
+}
+
+/*
+ * problem is :
+ * - hp mixer has 2 bits in PM reg 0x3C
+ * - adc input of the hp mixer has 2 mute bits
+ * - all the other inputs of the hp mixer have 1 bit
+ * =>  the bits for adc and pm can't be used separately
+ */
+static int hp_mixer_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	u16 reg;
+	u16 mask = ALC5623_PWR_ADD2_L_HP_MIXER|ALC5623_PWR_ADD2_R_HP_MIXER;
+
+	reg = snd_soc_read(w->codec, ALC5623_PWR_MANAG_ADD2);
+	reg &= ~mask;
+	if (event == SND_SOC_DAPM_POST_PMU)
+		reg |= mask;
+	return snd_soc_write(w->codec, ALC5623_PWR_MANAG_ADD2, reg);
+}
+
+static int amp_mixer_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	/* index 0x46: class-d internal register */
+	snd_soc_write(w->codec, ALC5623_HID_CTRL_INDEX, 0x46);
+	if (event == SND_SOC_DAPM_PRE_PMU)
+		snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0xFFFF);
+	else
+		snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0);
+	return 0;
+}
+
+/*
+ * ALC5623 Controls
+ */
+
+static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(adc_rec_tlv, -1650, 150, 0);
+static const unsigned int boost_tlv[] = {
+	TLV_DB_RANGE_HEAD(3),
+	0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+	1, 1, TLV_DB_SCALE_ITEM(2000, 0, 0),
+	2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const struct snd_kcontrol_new rt5621_vol_snd_controls[] = {
+	SOC_DOUBLE_TLV("Speaker Playback Volume",
+			ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+	SOC_DOUBLE("Speaker Playback Switch",
+			ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+	SOC_DOUBLE_TLV("Headphone Playback Volume",
+			ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+	SOC_DOUBLE("Headphone Playback Switch",
+			ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5622_vol_snd_controls[] = {
+	SOC_DOUBLE_TLV("Speaker Playback Volume",
+			ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+	SOC_DOUBLE("Speaker Playback Switch",
+			ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+	SOC_DOUBLE_TLV("Line Playback Volume",
+			ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+	SOC_DOUBLE("Line Playback Switch",
+			ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_vol_snd_controls[] = {
+	SOC_DOUBLE_TLV("Line Playback Volume",
+			ALC5623_SPK_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+	SOC_DOUBLE("Line Playback Switch",
+			ALC5623_SPK_OUT_VOL, 15, 7, 1, 1),
+	SOC_DOUBLE_TLV("Headphone Playback Volume",
+			ALC5623_HP_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+	SOC_DOUBLE("Headphone Playback Switch",
+			ALC5623_HP_OUT_VOL, 15, 7, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_snd_controls[] = {
+	SOC_DOUBLE_TLV("Auxout Playback Volume",
+			ALC5623_MONO_AUX_OUT_VOL, 8, 0, 31, 1, hp_tlv),
+	SOC_DOUBLE("Auxout Playback Switch",
+			ALC5623_MONO_AUX_OUT_VOL, 15, 7, 1, 1),
+	SOC_DOUBLE_TLV("PCM Playback Volume",
+			ALC5623_STEREO_DAC_VOL, 8, 0, 31, 1, vol_tlv),
+	SOC_DOUBLE_TLV("AuxI Capture Volume",
+			ALC5623_AUXIN_VOL, 8, 0, 31, 1, vol_tlv),
+	SOC_DOUBLE_TLV("LineIn Capture Volume",
+			ALC5623_LINE_IN_VOL, 8, 0, 31, 1, vol_tlv),
+	SOC_SINGLE_TLV("Mic1 Capture Volume",
+			ALC5623_MIC_VOL, 8, 31, 1, vol_tlv),
+	SOC_SINGLE_TLV("Mic2 Capture Volume",
+			ALC5623_MIC_VOL, 0, 31, 1, vol_tlv),
+	SOC_DOUBLE_TLV("Rec Capture Volume",
+			ALC5623_ADC_REC_GAIN, 7, 0, 31, 0, adc_rec_tlv),
+	SOC_SINGLE_TLV("Mic 1 Boost Volume",
+			ALC5623_MIC_CTRL, 10, 2, 0, boost_tlv),
+	SOC_SINGLE_TLV("Mic 2 Boost Volume",
+			ALC5623_MIC_CTRL, 8, 2, 0, boost_tlv),
+};
+
+/*
+ * DAPM Controls
+ */
+static const struct snd_kcontrol_new alc5623_hp_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2HP_L Playback Switch", ALC5623_ADC_REC_GAIN, 15, 1, 1),
+SOC_DAPM_SINGLE("ADC2HP_R Playback Switch", ALC5623_ADC_REC_GAIN, 14, 1, 1),
+SOC_DAPM_SINGLE("LI2HP Playback Switch", ALC5623_LINE_IN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("AUXI2HP Playback Switch", ALC5623_AUXIN_VOL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC12HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 15, 1, 1),
+SOC_DAPM_SINGLE("MIC22HP Playback Switch", ALC5623_MIC_ROUTING_CTRL, 7, 1, 1),
+SOC_DAPM_SINGLE("DAC2HP Playback Switch", ALC5623_STEREO_DAC_VOL, 15, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("ADC2MONO_L Playback Switch", ALC5623_ADC_REC_GAIN, 13, 1, 1),
+SOC_DAPM_SINGLE("ADC2MONO_R Playback Switch", ALC5623_ADC_REC_GAIN, 12, 1, 1),
+SOC_DAPM_SINGLE("LI2MONO Playback Switch", ALC5623_LINE_IN_VOL, 13, 1, 1),
+SOC_DAPM_SINGLE("AUXI2MONO Playback Switch", ALC5623_AUXIN_VOL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC12MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 13, 1, 1),
+SOC_DAPM_SINGLE("MIC22MONO Playback Switch", ALC5623_MIC_ROUTING_CTRL, 5, 1, 1),
+SOC_DAPM_SINGLE("DAC2MONO Playback Switch", ALC5623_STEREO_DAC_VOL, 13, 1, 1),
+};
+
+static const struct snd_kcontrol_new alc5623_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("LI2SPK Playback Switch", ALC5623_LINE_IN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("AUXI2SPK Playback Switch", ALC5623_AUXIN_VOL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC12SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 14, 1, 1),
+SOC_DAPM_SINGLE("MIC22SPK Playback Switch", ALC5623_MIC_ROUTING_CTRL, 6, 1, 1),
+SOC_DAPM_SINGLE("DAC2SPK Playback Switch", ALC5623_STEREO_DAC_VOL, 14, 1, 1),
+};
+
+/* Left Record Mixer */
+static const struct snd_kcontrol_new alc5623_captureL_mixer_controls[] = {
+SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 14, 1, 1),
+SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 13, 1, 1),
+SOC_DAPM_SINGLE("LineInL Capture Switch", ALC5623_ADC_REC_MIXER, 12, 1, 1),
+SOC_DAPM_SINGLE("Left AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 11, 1, 1),
+SOC_DAPM_SINGLE("HPMixerL Capture Switch", ALC5623_ADC_REC_MIXER, 10, 1, 1),
+SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 9, 1, 1),
+SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 8, 1, 1),
+};
+
+/* Right Record Mixer */
+static const struct snd_kcontrol_new alc5623_captureR_mixer_controls[] = {
+SOC_DAPM_SINGLE("Mic1 Capture Switch", ALC5623_ADC_REC_MIXER, 6, 1, 1),
+SOC_DAPM_SINGLE("Mic2 Capture Switch", ALC5623_ADC_REC_MIXER, 5, 1, 1),
+SOC_DAPM_SINGLE("LineInR Capture Switch", ALC5623_ADC_REC_MIXER, 4, 1, 1),
+SOC_DAPM_SINGLE("Right AuxI Capture Switch", ALC5623_ADC_REC_MIXER, 3, 1, 1),
+SOC_DAPM_SINGLE("HPMixerR Capture Switch", ALC5623_ADC_REC_MIXER, 2, 1, 1),
+SOC_DAPM_SINGLE("SPKMixer Capture Switch", ALC5623_ADC_REC_MIXER, 1, 1, 1),
+SOC_DAPM_SINGLE("MonoMixer Capture Switch", ALC5623_ADC_REC_MIXER, 0, 1, 1),
+};
+
+static const char *alc5623_spk_n_sour_sel[] = {
+		"RN/-R", "RP/+R", "LN/-R", "Vmid" };
+static const char *alc5623_hpl_out_input_sel[] = {
+		"Vmid", "HP Left Mix"};
+static const char *alc5623_hpr_out_input_sel[] = {
+		"Vmid", "HP Right Mix"};
+static const char *alc5623_spkout_input_sel[] = {
+		"Vmid", "HP Mix", "Speaker Mix", "Mono Mix"};
+static const char *alc5623_aux_out_input_sel[] = {
+		"Vmid", "HP Mix", "Speaker Mix", "Mono Mix"};
+
+static const struct soc_enum alc5623_enum[] = {
+SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 14, 4, alc5623_spk_n_sour_sel),
+SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 9, 2, alc5623_hpl_out_input_sel),
+SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 8, 2, alc5623_hpr_out_input_sel),
+SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 6, 4, alc5623_aux_out_input_sel),
+SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 10, 4, alc5623_spkout_input_sel),
+};
+
+/* auxout output mux */
+static const struct snd_kcontrol_new alc5623_auxout_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_enum[3]);
+
+/* speaker output mux */
+static const struct snd_kcontrol_new alc5623_spkout_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_enum[4]);
+
+/* headphone left output mux */
+static const struct snd_kcontrol_new alc5623_hpl_out_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_enum[1]);
+
+/* headphone right output mux */
+static const struct snd_kcontrol_new alc5623_hpr_out_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_enum[2]);
+
+/* speaker output N select */
+static const struct snd_kcontrol_new alc5623_spkoutn_mux_controls =
+SOC_DAPM_ENUM("Route", alc5623_enum[0]);
+
+static const struct snd_soc_dapm_widget alc5623_dapm_widgets[] = {
+/* Muxes */
+SND_SOC_DAPM_MUX("AuxOut Mux", SND_SOC_NOPM, 0, 0,
+	&alc5623_auxout_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut Mux", SND_SOC_NOPM, 0, 0,
+	&alc5623_spkout_mux_controls),
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0,
+	&alc5623_hpl_out_mux_controls),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0,
+	&alc5623_hpr_out_mux_controls),
+SND_SOC_DAPM_MUX("SpeakerOut N Mux", SND_SOC_NOPM, 0, 0,
+	&alc5623_spkoutn_mux_controls),
+
+/* output mixers */
+SND_SOC_DAPM_MIXER_E("HP Mix", SND_SOC_NOPM, 0, 0,
+	&alc5623_hp_mixer_controls[0], ARRAY_SIZE(alc5623_hp_mixer_controls),
+	hp_mixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_MIXER("Mono Mix", ALC5623_PWR_MANAG_ADD2, 2, 0,
+	&alc5623_mono_mixer_controls[0],
+	ARRAY_SIZE(alc5623_mono_mixer_controls)),
+SND_SOC_DAPM_MIXER("Speaker Mix", ALC5623_PWR_MANAG_ADD2, 3, 0,
+	&alc5623_speaker_mixer_controls[0],
+	ARRAY_SIZE(alc5623_speaker_mixer_controls)),
+
+/* input mixers */
+SND_SOC_DAPM_MIXER("Left Capture Mix", ALC5623_PWR_MANAG_ADD2, 1, 0,
+	&alc5623_captureL_mixer_controls[0],
+	ARRAY_SIZE(alc5623_captureL_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right Capture Mix", ALC5623_PWR_MANAG_ADD2, 0, 0,
+	&alc5623_captureR_mixer_controls[0],
+	ARRAY_SIZE(alc5623_captureR_mixer_controls)),
+
+SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+	ALC5623_PWR_MANAG_ADD2, 9, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+	ALC5623_PWR_MANAG_ADD2, 8, 0),
+SND_SOC_DAPM_MIXER("IIS Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("AuxI Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_MIXER("Line Mix", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+	ALC5623_PWR_MANAG_ADD2, 7, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+	ALC5623_PWR_MANAG_ADD2, 6, 0),
+SND_SOC_DAPM_PGA("Left Headphone", ALC5623_PWR_MANAG_ADD3, 10, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Headphone", ALC5623_PWR_MANAG_ADD3, 9, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpeakerOut", ALC5623_PWR_MANAG_ADD3, 12, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left AuxOut", ALC5623_PWR_MANAG_ADD3, 14, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right AuxOut", ALC5623_PWR_MANAG_ADD3, 13, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left LineIn", ALC5623_PWR_MANAG_ADD3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right LineIn", ALC5623_PWR_MANAG_ADD3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left AuxI", ALC5623_PWR_MANAG_ADD3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right AuxI", ALC5623_PWR_MANAG_ADD3, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 PGA", ALC5623_PWR_MANAG_ADD3, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 PGA", ALC5623_PWR_MANAG_ADD3, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC1 Pre Amp", ALC5623_PWR_MANAG_ADD3, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MIC2 Pre Amp", ALC5623_PWR_MANAG_ADD3, 0, 0, NULL, 0),
+SND_SOC_DAPM_MICBIAS("Mic Bias1", ALC5623_PWR_MANAG_ADD1, 11, 0),
+
+SND_SOC_DAPM_OUTPUT("AUXOUTL"),
+SND_SOC_DAPM_OUTPUT("AUXOUTR"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_OUTPUT("SPKOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_INPUT("LINEINL"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("AUXINL"),
+SND_SOC_DAPM_INPUT("AUXINR"),
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_VMID("Vmid"),
+};
+
+static const char *alc5623_amp_names[] = {"AB Amp", "D Amp"};
+static const struct soc_enum alc5623_amp_enum =
+	SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 13, 2, alc5623_amp_names);
+static const struct snd_kcontrol_new alc5623_amp_mux_controls =
+	SOC_DAPM_ENUM("Route", alc5623_amp_enum);
+
+static const struct snd_soc_dapm_widget alc5623_dapm_amp_widgets[] = {
+SND_SOC_DAPM_PGA_E("D Amp", ALC5623_PWR_MANAG_ADD2, 14, 0, NULL, 0,
+	amp_mixer_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA("AB Amp", ALC5623_PWR_MANAG_ADD2, 15, 0, NULL, 0),
+SND_SOC_DAPM_MUX("AB-D Amp Mux", SND_SOC_NOPM, 0, 0,
+	&alc5623_amp_mux_controls),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	/* virtual mixer - mixes left & right channels */
+	{"IIS Mix", NULL,				"Left DAC"},
+	{"IIS Mix", NULL,				"Right DAC"},
+	{"Line Mix", NULL,				"Right LineIn"},
+	{"Line Mix", NULL,				"Left LineIn"},
+	{"AuxI Mix", NULL,				"Left AuxI"},
+	{"AuxI Mix", NULL,				"Right AuxI"},
+	{"AUXOUTL", NULL,				"Left AuxOut"},
+	{"AUXOUTR", NULL,				"Right AuxOut"},
+
+	/* HP mixer */
+	{"HP Mix", "ADC2HP_L Playback Switch",		"Left Capture Mix"},
+	{"HP Mix", "ADC2HP_R Playback Switch",		"Right Capture Mix"},
+	{"HP Mix", "LI2HP Playback Switch",		"Line Mix"},
+	{"HP Mix", "AUXI2HP Playback Switch",		"AuxI Mix"},
+	{"HP Mix", "MIC12HP Playback Switch",		"MIC1 PGA"},
+	{"HP Mix", "MIC22HP Playback Switch",		"MIC2 PGA"},
+	{"HP Mix", "DAC2HP Playback Switch",		"IIS Mix"},
+
+	/* speaker mixer */
+	{"Speaker Mix", "LI2SPK Playback Switch",	"Line Mix"},
+	{"Speaker Mix", "AUXI2SPK Playback Switch",	"AuxI Mix"},
+	{"Speaker Mix", "MIC12SPK Playback Switch",	"MIC1 PGA"},
+	{"Speaker Mix", "MIC22SPK Playback Switch",	"MIC2 PGA"},
+	{"Speaker Mix", "DAC2SPK Playback Switch",	"IIS Mix"},
+
+	/* mono mixer */
+	{"Mono Mix", "ADC2MONO_L Playback Switch",	"Left Capture Mix"},
+	{"Mono Mix", "ADC2MONO_R Playback Switch",	"Right Capture Mix"},
+	{"Mono Mix", "LI2MONO Playback Switch",		"Line Mix"},
+	{"Mono Mix", "AUXI2MONO Playback Switch",	"AuxI Mix"},
+	{"Mono Mix", "MIC12MONO Playback Switch",	"MIC1 PGA"},
+	{"Mono Mix", "MIC22MONO Playback Switch",	"MIC2 PGA"},
+	{"Mono Mix", "DAC2MONO Playback Switch",	"IIS Mix"},
+
+	/* Left record mixer */
+	{"Left Capture Mix", "LineInL Capture Switch",	"LINEINL"},
+	{"Left Capture Mix", "Left AuxI Capture Switch", "AUXINL"},
+	{"Left Capture Mix", "Mic1 Capture Switch",	"MIC1 Pre Amp"},
+	{"Left Capture Mix", "Mic2 Capture Switch",	"MIC2 Pre Amp"},
+	{"Left Capture Mix", "HPMixerL Capture Switch", "HP Mix"},
+	{"Left Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"},
+	{"Left Capture Mix", "MonoMixer Capture Switch", "Mono Mix"},
+
+	/*Right record mixer */
+	{"Right Capture Mix", "LineInR Capture Switch", "LINEINR"},
+	{"Right Capture Mix", "Right AuxI Capture Switch",	"AUXINR"},
+	{"Right Capture Mix", "Mic1 Capture Switch",	"MIC1 Pre Amp"},
+	{"Right Capture Mix", "Mic2 Capture Switch",	"MIC2 Pre Amp"},
+	{"Right Capture Mix", "HPMixerR Capture Switch", "HP Mix"},
+	{"Right Capture Mix", "SPKMixer Capture Switch", "Speaker Mix"},
+	{"Right Capture Mix", "MonoMixer Capture Switch", "Mono Mix"},
+
+	/* headphone left mux */
+	{"Left Headphone Mux", "HP Left Mix",		"HP Mix"},
+	{"Left Headphone Mux", "Vmid",			"Vmid"},
+
+	/* headphone right mux */
+	{"Right Headphone Mux", "HP Right Mix",		"HP Mix"},
+	{"Right Headphone Mux", "Vmid",			"Vmid"},
+
+	/* speaker out mux */
+	{"SpeakerOut Mux", "Vmid",			"Vmid"},
+	{"SpeakerOut Mux", "HP Mix",			"HP Mix"},
+	{"SpeakerOut Mux", "Speaker Mix",		"Speaker Mix"},
+	{"SpeakerOut Mux", "Mono Mix",			"Mono Mix"},
+
+	/* Mono/Aux Out mux */
+	{"AuxOut Mux", "Vmid",				"Vmid"},
+	{"AuxOut Mux", "HP Mix",			"HP Mix"},
+	{"AuxOut Mux", "Speaker Mix",			"Speaker Mix"},
+	{"AuxOut Mux", "Mono Mix",			"Mono Mix"},
+
+	/* output pga */
+	{"HPL", NULL,					"Left Headphone"},
+	{"Left Headphone", NULL,			"Left Headphone Mux"},
+	{"HPR", NULL,					"Right Headphone"},
+	{"Right Headphone", NULL,			"Right Headphone Mux"},
+	{"Left AuxOut", NULL,				"AuxOut Mux"},
+	{"Right AuxOut", NULL,				"AuxOut Mux"},
+
+	/* input pga */
+	{"Left LineIn", NULL,				"LINEINL"},
+	{"Right LineIn", NULL,				"LINEINR"},
+	{"Left AuxI", NULL,				"AUXINL"},
+	{"Right AuxI", NULL,				"AUXINR"},
+	{"MIC1 Pre Amp", NULL,				"MIC1"},
+	{"MIC2 Pre Amp", NULL,				"MIC2"},
+	{"MIC1 PGA", NULL,				"MIC1 Pre Amp"},
+	{"MIC2 PGA", NULL,				"MIC2 Pre Amp"},
+
+	/* left ADC */
+	{"Left ADC", NULL,				"Left Capture Mix"},
+
+	/* right ADC */
+	{"Right ADC", NULL,				"Right Capture Mix"},
+
+	{"SpeakerOut N Mux", "RN/-R",			"SpeakerOut"},
+	{"SpeakerOut N Mux", "RP/+R",			"SpeakerOut"},
+	{"SpeakerOut N Mux", "LN/-R",			"SpeakerOut"},
+	{"SpeakerOut N Mux", "Vmid",			"Vmid"},
+
+	{"SPKOUT", NULL,				"SpeakerOut"},
+	{"SPKOUTN", NULL,				"SpeakerOut N Mux"},
+};
+
+static const struct snd_soc_dapm_route intercon_spk[] = {
+	{"SpeakerOut", NULL,				"SpeakerOut Mux"},
+};
+
+static const struct snd_soc_dapm_route intercon_amp_spk[] = {
+	{"AB Amp", NULL,				"SpeakerOut Mux"},
+	{"D Amp", NULL,					"SpeakerOut Mux"},
+	{"AB-D Amp Mux", "AB Amp",			"AB Amp"},
+	{"AB-D Amp Mux", "D Amp",			"D Amp"},
+	{"SpeakerOut", NULL,				"AB-D Amp Mux"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+	u32 pll_in;
+	u32 pll_out;
+	u16 regvalue;
+};
+
+/* Note : pll code from original alc5623 driver. Not sure of how good it is */
+/* usefull only for master mode */
+static const struct _pll_div codec_master_pll_div[] = {
+
+	{  2048000,  8192000,	0x0ea0},
+	{  3686400,  8192000,	0x4e27},
+	{ 12000000,  8192000,	0x456b},
+	{ 13000000,  8192000,	0x495f},
+	{ 13100000,  8192000,	0x0320},
+	{  2048000,  11289600,	0xf637},
+	{  3686400,  11289600,	0x2f22},
+	{ 12000000,  11289600,	0x3e2f},
+	{ 13000000,  11289600,	0x4d5b},
+	{ 13100000,  11289600,	0x363b},
+	{  2048000,  16384000,	0x1ea0},
+	{  3686400,  16384000,	0x9e27},
+	{ 12000000,  16384000,	0x452b},
+	{ 13000000,  16384000,	0x542f},
+	{ 13100000,  16384000,	0x03a0},
+	{  2048000,  16934400,	0xe625},
+	{  3686400,  16934400,	0x9126},
+	{ 12000000,  16934400,	0x4d2c},
+	{ 13000000,  16934400,	0x742f},
+	{ 13100000,  16934400,	0x3c27},
+	{  2048000,  22579200,	0x2aa0},
+	{  3686400,  22579200,	0x2f20},
+	{ 12000000,  22579200,	0x7e2f},
+	{ 13000000,  22579200,	0x742f},
+	{ 13100000,  22579200,	0x3c27},
+	{  2048000,  24576000,	0x2ea0},
+	{  3686400,  24576000,	0xee27},
+	{ 12000000,  24576000,	0x2915},
+	{ 13000000,  24576000,	0x772e},
+	{ 13100000,  24576000,	0x0d20},
+};
+
+static const struct _pll_div codec_slave_pll_div[] = {
+
+	{  1024000,  16384000,  0x3ea0},
+	{  1411200,  22579200,	0x3ea0},
+	{  1536000,  24576000,	0x3ea0},
+	{  2048000,  16384000,  0x1ea0},
+	{  2822400,  22579200,	0x1ea0},
+	{  3072000,  24576000,	0x1ea0},
+
+};
+
+static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
+{
+	int i;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int gbl_clk = 0, pll_div = 0;
+	u16 reg;
+
+	if (pll_id < ALC5623_PLL_FR_MCLK || pll_id > ALC5623_PLL_FR_BCK)
+		return -ENODEV;
+
+	/* Disable PLL power */
+	alc5623_write_mask(codec, ALC5623_PWR_MANAG_ADD2,
+				0,
+				ALC5623_PWR_ADD2_PLL);
+
+	/* pll is not used in slave mode */
+	reg = snd_soc_read(codec, ALC5623_DAI_CONTROL);
+	if (reg & ALC5623_DAI_SDP_SLAVE_MODE)
+		return 0;
+
+	if (!freq_in || !freq_out)
+		return 0;
+
+	if (pll_id == ALC5623_PLL_FR_MCLK) {
+		for (i = 0; i < ARRAY_SIZE(codec_master_pll_div); i++) {
+			if (codec_master_pll_div[i].pll_in == freq_in
+			   && codec_master_pll_div[i].pll_out == freq_out) {
+				/* PLL source from MCLK */
+				pll_div  = codec_master_pll_div[i].regvalue;
+				break;
+			}
+		}
+	} else {
+		for (i = 0; i < ARRAY_SIZE(codec_slave_pll_div); i++) {
+			if (codec_slave_pll_div[i].pll_in == freq_in
+			   && codec_slave_pll_div[i].pll_out == freq_out) {
+				/* PLL source from Bitclk */
+				gbl_clk = ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK;
+				pll_div = codec_slave_pll_div[i].regvalue;
+				break;
+			}
+		}
+	}
+
+	if (pll_div) {
+		snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
+		snd_soc_write(codec, ALC5623_PLL_CTRL, pll_div);
+		alc5623_write_mask(codec, ALC5623_PWR_MANAG_ADD2,
+					ALC5623_PWR_ADD2_PLL,
+					ALC5623_PWR_ADD2_PLL);
+		gbl_clk |= ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL;
+		snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
+	}
+
+	return 0;
+}
+
+struct _coeff_div {
+	u16 fs;
+	u16 regvalue;
+};
+
+/* codec hifi mclk (after PLL) clock divider coefficients */
+/* values inspired from column BCLK=32Fs of Appendix A table */
+static const struct _coeff_div coeff_div[] = {
+	{256*8, 0x3a69},
+	{384*8, 0x3c6b},
+	{256*4, 0x2a69},
+	{384*4, 0x2c6b},
+	{256*2, 0x1a69},
+	{384*2, 0x1c6b},
+	{256*1, 0x0a69},
+	{384*1, 0x0c6b},
+};
+
+static int get_coeff(struct snd_soc_codec *codec, int rate)
+{
+	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].fs * rate == alc5623->sysclk)
+			return i;
+	}
+	return -EINVAL;
+}
+
+/*
+ * Clock after PLL and dividers
+ */
+static int alc5623_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 alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+
+	switch (freq) {
+	case  8192000:
+	case 11289600:
+	case 12288000:
+	case 16384000:
+	case 16934400:
+	case 18432000:
+	case 22579200:
+	case 24576000:
+		alc5623->sysclk = freq;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		iface = ALC5623_DAI_SDP_MASTER_MODE;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		iface = ALC5623_DAI_SDP_SLAVE_MODE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= ALC5623_DAI_I2S_DF_I2S;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		iface |= ALC5623_DAI_I2S_DF_RIGHT;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= ALC5623_DAI_I2S_DF_LEFT;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= ALC5623_DAI_I2S_DF_PCM;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= ALC5623_DAI_I2S_DF_PCM | ALC5623_DAI_I2S_PCM_MODE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+}
+
+static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+	int coeff, rate;
+	u16 iface;
+
+	iface = snd_soc_read(codec, ALC5623_DAI_CONTROL);
+	iface &= ~ALC5623_DAI_I2S_DL_MASK;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		iface |= ALC5623_DAI_I2S_DL_16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= ALC5623_DAI_I2S_DL_20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= ALC5623_DAI_I2S_DL_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= ALC5623_DAI_I2S_DL_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set iface & srate */
+	snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+	rate = params_rate(params);
+	coeff = get_coeff(codec, rate);
+	if (coeff >= 0) {
+		coeff = coeff_div[coeff].regvalue;
+		dev_dbg(codec->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n",
+			__func__, alc5623->sysclk, rate, coeff);
+		snd_soc_write(codec, ALC5623_STEREO_AD_DA_CLK_CTRL, coeff);
+	}
+	return 0;
+}
+
+static int alc5623_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 hp_mute = ALC5623_MISC_M_DAC_L_INPUT | ALC5623_MISC_M_DAC_R_INPUT;
+	u16 mute_reg = snd_soc_read(codec, ALC5623_MISC_CTRL) & ~hp_mute;
+
+	if (mute)
+		mute_reg |= hp_mute;
+
+	return snd_soc_write(codec, ALC5623_MISC_CTRL, mute_reg);
+}
+
+#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \
+	| ALC5623_PWR_ADD2_DAC_REF_CIR)
+
+#define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \
+	| ALC5623_PWR_ADD3_MIC1_BOOST_AD)
+
+#define ALC5623_ADD1_POWER_EN \
+	(ALC5623_PWR_ADD1_MAIN_I2S_EN | ALC5623_PWR_ADD1_MIC1_BIAS_EN \
+	| ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \
+	| ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \
+	| ALC5623_PWR_ADD1_HP_OUT_ENH_AMP)
+
+#define ALC5623_ADD1_POWER_EN_5622 \
+	(ALC5623_PWR_ADD1_MAIN_I2S_EN | ALC5623_PWR_ADD1_MIC1_BIAS_EN \
+	| ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
+	| ALC5623_PWR_ADD1_HP_OUT_AMP)
+
+static void enable_power_depop(struct snd_soc_codec *codec)
+{
+	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+
+	alc5623_write_mask(codec, ALC5623_PWR_MANAG_ADD1,
+				ALC5623_PWR_ADD1_SOFTGEN_EN,
+				ALC5623_PWR_ADD1_SOFTGEN_EN);
+
+	snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN);
+
+	alc5623_write_mask(codec, ALC5623_MISC_CTRL,
+				ALC5623_MISC_HP_DEPOP_MODE2_EN,
+				ALC5623_MISC_HP_DEPOP_MODE2_EN);
+
+	msleep(500);
+
+	snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN);
+
+	/* avoid writing '1' into 5622 reserved bits */
+	if (alc5623->id == 0x22)
+		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+			ALC5623_ADD1_POWER_EN_5622);
+	else
+		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+			ALC5623_ADD1_POWER_EN);
+
+	/* disable HP Depop2 */
+	alc5623_write_mask(codec, ALC5623_MISC_CTRL, 0,
+				ALC5623_MISC_HP_DEPOP_MODE2_EN);
+
+}
+
+static int alc5623_set_bias_level(struct snd_soc_codec *codec,
+				      enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		enable_power_depop(codec);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* everything off except vref/vmid, */
+		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2,
+				ALC5623_PWR_ADD2_VREF);
+		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3,
+				ALC5623_PWR_ADD3_MAIN_BIAS);
+		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+				ALC5623_PWR_ADD1_MIC1_BIAS_EN);
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* everything off, dac mute, inactive */
+		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2, 0);
+		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, 0);
+		snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define ALC5623_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE \
+			| SNDRV_PCM_FMTBIT_S24_LE \
+			| SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops alc5623_dai_ops = {
+		.hw_params = alc5623_pcm_hw_params,
+		.digital_mute = alc5623_mute,
+		.set_fmt = alc5623_set_dai_fmt,
+		.set_sysclk = alc5623_set_dai_sysclk,
+		.set_pll = alc5623_set_dai_pll,
+};
+
+static struct snd_soc_dai_driver alc5623_dai = {
+	.name = "alc5623-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rate_min =	8000,
+		.rate_max =	48000,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = ALC5623_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rate_min =	8000,
+		.rate_max =	48000,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = ALC5623_FORMATS,},
+
+	.ops = &alc5623_dai_ops,
+};
+
+static void alc5623_work(struct work_struct *work)
+{
+	struct snd_soc_codec *codec =
+		container_of(work, struct snd_soc_codec, delayed_work.work);
+	alc5623_set_bias_level(codec, codec->bias_level);
+}
+
+static int alc5623_suspend(struct snd_soc_codec *codec, pm_message_t mesg)
+{
+	/* we only need to suspend if we are a valid card */
+	if (!codec->card)
+		return 0;
+
+	alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int alc5623_resume(struct snd_soc_codec *codec)
+{
+	int i, step = codec->driver->reg_cache_step;
+	u16 *cache = codec->reg_cache;
+
+	/* we only need to resume if we are a valid card */
+	if (!codec->card)
+		return 0;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 2 ; i < codec->driver->reg_cache_size ; i += step)
+		snd_soc_write(codec, i, cache[i]);
+
+	alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* charge alc5623 caps */
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
+		alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+		codec->bias_level = SND_SOC_BIAS_ON;
+		schedule_delayed_work(&codec->delayed_work,
+			msecs_to_jiffies(caps_charge));
+
+	}
+
+	return 0;
+}
+
+static int alc5623_probe(struct snd_soc_codec *codec)
+{
+	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	INIT_DELAYED_WORK(&codec->delayed_work, alc5623_work);
+	schedule_delayed_work(&codec->delayed_work,
+				msecs_to_jiffies(caps_charge));
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, alc5623->control_type);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	alc5623_reset(codec);
+	alc5623_fill_cache(codec);
+
+	/* power on device */
+	alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	if (alc5623->add_ctrl) {
+		snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
+				alc5623->add_ctrl);
+	}
+
+	if (alc5623->jack_det_ctrl) {
+		snd_soc_write(codec, ALC5623_JACK_DET_CTRL,
+				alc5623->jack_det_ctrl);
+	}
+
+	switch (alc5623->id) {
+	default:
+	case 0x21:
+		snd_soc_add_controls(codec, rt5621_vol_snd_controls,
+			ARRAY_SIZE(rt5621_vol_snd_controls));
+		break;
+	case 0x22:
+		snd_soc_add_controls(codec, rt5622_vol_snd_controls,
+			ARRAY_SIZE(rt5622_vol_snd_controls));
+		break;
+	case 0x23:
+		snd_soc_add_controls(codec, alc5623_vol_snd_controls,
+			ARRAY_SIZE(alc5623_vol_snd_controls));
+		break;
+	}
+
+	snd_soc_add_controls(codec, alc5623_snd_controls,
+			ARRAY_SIZE(alc5623_snd_controls));
+
+	snd_soc_dapm_new_controls(codec, alc5623_dapm_widgets,
+					ARRAY_SIZE(alc5623_dapm_widgets));
+
+	/* set up audio path interconnects */
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	switch (alc5623->id) {
+	default:
+	case 0x21:
+	case 0x22:
+		snd_soc_dapm_new_controls(codec, alc5623_dapm_amp_widgets,
+					ARRAY_SIZE(alc5623_dapm_amp_widgets));
+		snd_soc_dapm_add_routes(codec, intercon_amp_spk,
+						ARRAY_SIZE(intercon_amp_spk));
+		break;
+	case 0x23:
+		snd_soc_dapm_add_routes(codec, intercon_spk,
+						ARRAY_SIZE(intercon_spk));
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * This function forces any delayed work to be queued and run.
+ */
+static int run_delayed_work(struct delayed_work *dwork)
+{
+	int ret;
+
+	/* cancel any work waiting to be queued. */
+	ret = cancel_delayed_work(dwork);
+
+	/* if there was any work waiting then we run it now and
+	 * wait for it's completion */
+	if (ret) {
+		schedule_delayed_work(dwork, 0);
+		flush_scheduled_work();
+	}
+	return ret;
+}
+
+/* power down chip */
+static int alc5623_remove(struct snd_soc_codec *codec)
+{
+	run_delayed_work(&codec->delayed_work);
+	alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_device_alc5623 = {
+	.probe = alc5623_probe,
+	.remove = alc5623_remove,
+	.suspend = alc5623_suspend,
+	.resume = alc5623_resume,
+	.set_bias_level = alc5623_set_bias_level,
+	.reg_cache_size = ALC5623_VENDOR_ID2+2,
+	.reg_word_size = sizeof(u16),
+	.reg_cache_step = 2,
+};
+
+/*
+ * ALC5623 2 wire address is determined by A1 pin
+ * state during powerup.
+ *    low  = 0x1a
+ *    high = 0x1b
+ */
+static int alc5623_i2c_probe(struct i2c_client *client,
+				const struct i2c_device_id *id)
+{
+	struct alc5623_platform_data *pdata;
+	struct alc5623_priv *alc5623;
+	int ret, vid1, vid2;
+
+	vid1 = i2c_smbus_read_word_data(client, ALC5623_VENDOR_ID1);
+	if (vid1 < 0) {
+		dev_err(&client->dev, "failed to read I2C\n");
+		return -EIO;
+	}
+	vid1 = ((vid1 & 0xff) << 8) | (vid1 >> 8);
+
+	vid2 = i2c_smbus_read_byte_data(client, ALC5623_VENDOR_ID2);
+	if (vid2 < 0) {
+		dev_err(&client->dev, "failed to read I2C\n");
+		return -EIO;
+	}
+
+	if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
+		dev_err(&client->dev, "unknown or wrong codec\n");
+		return -ENODEV;
+	}
+
+	dev_dbg(&client->dev, "Found codec id : alc56%02x\n", vid2);
+
+	alc5623 = kzalloc(sizeof(struct alc5623_priv), GFP_KERNEL);
+	if (alc5623 == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	pdata = client->dev.platform_data;
+	if (pdata) {
+		alc5623->add_ctrl = pdata->add_ctrl;
+		alc5623->jack_det_ctrl = pdata->jack_det_ctrl;
+	}
+
+	alc5623->id = vid2;
+	switch (alc5623->id) {
+	case 0x21:
+		alc5623_dai.name = "alc5621-hifi";
+		break;
+	case 0x22:
+		alc5623_dai.name = "alc5622-hifi";
+		break;
+	default:
+	case 0x23:
+		alc5623_dai.name = "alc5623-hifi";
+		break;
+	}
+
+	i2c_set_clientdata(client, alc5623);
+	alc5623->control_data = client;
+	alc5623->control_type = SND_SOC_I2C;
+	mutex_init(&alc5623->mutex);
+
+	ret =  snd_soc_register_codec(&client->dev,
+		&soc_codec_device_alc5623, &alc5623_dai, 1);
+	if (ret != 0) {
+		dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	return ret;
+}
+
+static int alc5623_i2c_remove(struct i2c_client *client)
+{
+	struct alc5623_priv *alc5623 = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	kfree(alc5623);
+	return 0;
+}
+
+static const struct i2c_device_id alc5623_i2c_table[] = {
+	{"alc5621", 0x21},
+	{"alc5622", 0x22},
+	{"alc5623", 0x23},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
+
+/*  i2c codec control layer */
+static struct i2c_driver alc5623_i2c_driver = {
+	.driver = {
+		.name = "alc562x-codec",
+		.owner = THIS_MODULE,
+	},
+	.probe = alc5623_i2c_probe,
+	.remove =  __devexit_p(alc5623_i2c_remove),
+	.id_table = alc5623_i2c_table,
+};
+
+static int __init alc5623_modinit(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&alc5623_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "%s: can't add i2c driver", __func__);
+		return ret;
+	}
+
+	return ret;
+}
+module_init(alc5623_modinit);
+
+static void __exit alc5623_modexit(void)
+{
+	i2c_del_driver(&alc5623_i2c_driver);
+}
+module_exit(alc5623_modexit);
+
+MODULE_DESCRIPTION("ASoC alc5621/2/3 driver");
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_LICENSE("GPL");
+
Index: sound-2.6/sound/soc/codecs/alc5623.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ sound-2.6/sound/soc/codecs/alc5623.h	2010-09-06 23:29:31.000000000 +0200
@@ -0,0 +1,161 @@
+/*
+ * rt5623.h  --  audio driver for ALC5623
+ *
+ * Copyright 2008 Realtek Microelectronics
+ * Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ * Author: flove <flove@realtek.com>
+ * Arnaud Patard <arnaud.patard@rtp-net.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.
+ *
+ */
+
+#ifndef _ALC5623_H
+#define _ALC5623_H
+
+#define ALC5623_RESET				0x00
+/*				5621 5622 5623  */
+/* speaker output vol		   2    2       */
+/* line output vol                      4    2  */
+/* HP output vol		   4    0    4  */
+#define ALC5623_SPK_OUT_VOL			0x02
+#define ALC5623_HP_OUT_VOL			0x04
+#define ALC5623_MONO_AUX_OUT_VOL		0x06
+#define ALC5623_AUXIN_VOL			0x08
+#define ALC5623_LINE_IN_VOL			0x0A
+#define ALC5623_STEREO_DAC_VOL			0x0C
+#define ALC5623_MIC_VOL				0x0E
+#define ALC5623_MIC_ROUTING_CTRL		0x10
+#define ALC5623_ADC_REC_GAIN			0x12
+#define ALC5623_ADC_REC_MIXER			0x14
+#define ALC5623_SOFT_VOL_CTRL_TIME		0x16
+/* ALC5623_OUTPUT_MIXER_CTRL :			*/
+/* same remark as for reg 2 line vs speaker	*/
+#define ALC5623_OUTPUT_MIXER_CTRL		0x1C
+#define ALC5623_MIC_CTRL			0x22
+
+#define	ALC5623_DAI_CONTROL			0x34
+#define ALC5623_DAI_SDP_MASTER_MODE		(0 << 15)
+#define ALC5623_DAI_SDP_SLAVE_MODE		(1 << 15)
+#define ALC5623_DAI_I2S_PCM_MODE		(1 << 14)
+#define ALC5623_DAI_MAIN_I2S_BCLK_POL_CTRL	(1 <<  7)
+#define ALC5623_DAI_ADC_DATA_L_R_SWAP		(1 <<  5)
+#define ALC5623_DAI_DAC_DATA_L_R_SWAP		(1 <<  4)
+#define ALC5623_DAI_I2S_DL_MASK			(3 <<  2)
+#define ALC5623_DAI_I2S_DL_32			(3 <<  2)
+#define	ALC5623_DAI_I2S_DL_24			(2 <<  2)
+#define ALC5623_DAI_I2S_DL_20			(1 <<  2)
+#define ALC5623_DAI_I2S_DL_16			(0 <<  2)
+#define ALC5623_DAI_I2S_DF_PCM			(3 <<  0)
+#define	ALC5623_DAI_I2S_DF_LEFT			(2 <<  0)
+#define ALC5623_DAI_I2S_DF_RIGHT		(1 <<  0)
+#define ALC5623_DAI_I2S_DF_I2S			(0 <<  0)
+
+#define ALC5623_STEREO_AD_DA_CLK_CTRL		0x36
+#define	ALC5623_COMPANDING_CTRL			0x38
+
+#define	ALC5623_PWR_MANAG_ADD1			0x3A
+#define ALC5623_PWR_ADD1_MAIN_I2S_EN		(1 << 15)
+#define ALC5623_PWR_ADD1_ZC_DET_PD_EN		(1 << 14)
+#define ALC5623_PWR_ADD1_MIC1_BIAS_EN		(1 << 11)
+#define ALC5623_PWR_ADD1_SHORT_CURR_DET_EN	(1 << 10)
+#define ALC5623_PWR_ADD1_SOFTGEN_EN		(1 <<  8) /* rsvd on 5622 */
+#define	ALC5623_PWR_ADD1_DEPOP_BUF_HP		(1 <<  6) /* rsvd on 5622 */
+#define	ALC5623_PWR_ADD1_HP_OUT_AMP		(1 <<  5)
+#define	ALC5623_PWR_ADD1_HP_OUT_ENH_AMP		(1 <<  4) /* rsvd on 5622 */
+#define ALC5623_PWR_ADD1_DEPOP_BUF_AUX		(1 <<  2)
+#define ALC5623_PWR_ADD1_AUX_OUT_AMP		(1 <<  1)
+#define ALC5623_PWR_ADD1_AUX_OUT_ENH_AMP	(1 <<  0) /* rsvd on 5622 */
+
+#define ALC5623_PWR_MANAG_ADD2			0x3C
+#define ALC5623_PWR_ADD2_LINEOUT		(1 << 15) /* rt5623 */
+#define ALC5623_PWR_ADD2_CLASS_AB		(1 << 15) /* rt5621 */
+#define ALC5623_PWR_ADD2_CLASS_D		(1 << 14) /* rt5621 */
+#define ALC5623_PWR_ADD2_VREF			(1 << 13)
+#define ALC5623_PWR_ADD2_PLL			(1 << 12)
+#define ALC5623_PWR_ADD2_DAC_REF_CIR		(1 << 10)
+#define ALC5623_PWR_ADD2_L_DAC_CLK		(1 <<  9)
+#define ALC5623_PWR_ADD2_R_DAC_CLK		(1 <<  8)
+#define ALC5623_PWR_ADD2_L_ADC_CLK_GAIN		(1 <<  7)
+#define ALC5623_PWR_ADD2_R_ADC_CLK_GAIN		(1 <<  6)
+#define ALC5623_PWR_ADD2_L_HP_MIXER		(1 <<  5)
+#define ALC5623_PWR_ADD2_R_HP_MIXER		(1 <<  4)
+#define ALC5623_PWR_ADD2_SPK_MIXER		(1 <<  3)
+#define ALC5623_PWR_ADD2_MONO_MIXER		(1 <<  2)
+#define ALC5623_PWR_ADD2_L_ADC_REC_MIXER	(1 <<  1)
+#define ALC5623_PWR_ADD2_R_ADC_REC_MIXER	(1 <<  0)
+
+#define ALC5623_PWR_MANAG_ADD3			0x3E
+#define ALC5623_PWR_ADD3_MAIN_BIAS		(1 << 15)
+#define ALC5623_PWR_ADD3_AUXOUT_L_VOL_AMP	(1 << 14)
+#define ALC5623_PWR_ADD3_AUXOUT_R_VOL_AMP	(1 << 13)
+#define ALC5623_PWR_ADD3_SPK_OUT		(1 << 12)
+#define ALC5623_PWR_ADD3_HP_L_OUT_VOL		(1 << 10)
+#define ALC5623_PWR_ADD3_HP_R_OUT_VOL		(1 <<  9)
+#define ALC5623_PWR_ADD3_LINEIN_L_VOL		(1 <<  7)
+#define ALC5623_PWR_ADD3_LINEIN_R_VOL		(1 <<  6)
+#define ALC5623_PWR_ADD3_AUXIN_L_VOL		(1 <<  5)
+#define ALC5623_PWR_ADD3_AUXIN_R_VOL		(1 <<  4)
+#define ALC5623_PWR_ADD3_MIC1_FUN_CTRL		(1 <<  3)
+#define ALC5623_PWR_ADD3_MIC2_FUN_CTRL		(1 <<  2)
+#define ALC5623_PWR_ADD3_MIC1_BOOST_AD		(1 <<  1)
+#define ALC5623_PWR_ADD3_MIC2_BOOST_AD		(1 <<  0)
+
+#define ALC5623_ADD_CTRL_REG			0x40
+
+#define	ALC5623_GLOBAL_CLK_CTRL_REG		0x42
+#define ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL	(1 << 15)
+#define ALC5623_GBL_CLK_SYS_SOUR_SEL_MCLK	(0 << 15)
+#define ALC5623_GBL_CLK_PLL_SOUR_SEL_BITCLK	(1 << 14)
+#define ALC5623_GBL_CLK_PLL_SOUR_SEL_MCLK	(0 << 14)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV8	(3 <<  1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV4	(2 <<  1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV2	(1 <<  1)
+#define ALC5623_GBL_CLK_PLL_DIV_RATIO_DIV1	(0 <<  1)
+#define ALC5623_GBL_CLK_PLL_PRE_DIV2		(1 <<  0)
+#define ALC5623_GBL_CLK_PLL_PRE_DIV1		(0 <<  0)
+
+#define ALC5623_PLL_CTRL			0x44
+#define ALC5623_PLL_CTRL_N_VAL(n)		(((n)&0xff) << 8)
+#define ALC5623_PLL_CTRL_K_VAL(k)		(((k)&0x7)  << 4)
+#define ALC5623_PLL_CTRL_M_VAL(m)		((m)&0xf)
+
+#define ALC5623_GPIO_OUTPUT_PIN_CTRL		0x4A
+#define ALC5623_GPIO_PIN_CONFIG			0x4C
+#define ALC5623_GPIO_PIN_POLARITY		0x4E
+#define ALC5623_GPIO_PIN_STICKY			0x50
+#define ALC5623_GPIO_PIN_WAKEUP			0x52
+#define ALC5623_GPIO_PIN_STATUS			0x54
+#define ALC5623_GPIO_PIN_SHARING		0x56
+#define	ALC5623_OVER_CURR_STATUS		0x58
+#define ALC5623_JACK_DET_CTRL			0x5A
+
+#define ALC5623_MISC_CTRL			0x5E
+#define ALC5623_MISC_DISABLE_FAST_VREG		(1 << 15)
+#define ALC5623_MISC_SPK_CLASS_AB_OC_PD		(1 << 13) /* 5621 */
+#define ALC5623_MISC_SPK_CLASS_AB_OC_DET	(1 << 12) /* 5621 */
+#define ALC5623_MISC_HP_DEPOP_MODE3_EN		(1 << 10)
+#define ALC5623_MISC_HP_DEPOP_MODE2_EN		(1 <<  9)
+#define ALC5623_MISC_HP_DEPOP_MODE1_EN		(1 <<  8)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE3_EN	(1 <<  6)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE2_EN	(1 <<  5)
+#define ALC5623_MISC_AUXOUT_DEPOP_MODE1_EN	(1 <<  4)
+#define ALC5623_MISC_M_DAC_L_INPUT		(1 <<  3)
+#define ALC5623_MISC_M_DAC_R_INPUT		(1 <<  2)
+#define ALC5623_MISC_IRQOUT_INV_CTRL		(1 <<  0)
+
+#define	ALC5623_PSEDUEO_SPATIAL_CTRL		0x60
+#define ALC5623_EQ_CTRL				0x62
+#define ALC5623_EQ_MODE_ENABLE			0x66
+#define ALC5623_AVC_CTRL			0x68
+#define ALC5623_HID_CTRL_INDEX			0x6A
+#define ALC5623_HID_CTRL_DATA			0x6C
+#define ALC5623_VENDOR_ID1			0x7C
+#define ALC5623_VENDOR_ID2			0x7E
+
+#define ALC5623_PLL_FR_MCLK			0
+#define ALC5623_PLL_FR_BCK			1
+#endif
Index: sound-2.6/include/linux/alc5623.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ sound-2.6/include/linux/alc5623.h	2010-09-06 23:29:31.000000000 +0200
@@ -0,0 +1,8 @@
+#ifndef _INCLUDE_LINUX_ALC5623_H
+#define _INCLUDE_LINUX_ALC5623_H
+struct alc5623_platform_data {
+	unsigned int add_ctrl;
+	unsigned int jack_det_ctrl;
+};
+#endif
+

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

* [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients
  2010-09-07  7:01 [patch 0/3] ASoC: t5325 audio support Arnaud Patard
  2010-09-07  7:01 ` [patch 1/3] ASoC: add support for alc562[123] codecs Arnaud Patard
@ 2010-09-07  7:01 ` Arnaud Patard
  2010-09-07 10:21   ` Mark Brown
  2010-09-07  7:01 ` [patch 3/3] t5325: add audio support Arnaud Patard
  2 siblings, 1 reply; 11+ messages in thread
From: Arnaud Patard @ 2010-09-07  7:01 UTC (permalink / raw)
  To: alsa-devel
  Cc: Arnaud Patard, Mark Brown, Martin Michlmayr, Saeed Bishara,
	Liam Girdwood

[-- Attachment #1: t5325.patch --]
[-- Type: text/plain, Size: 5412 bytes --]

This patch is adding support for hp t5325 thin clients.
There's a alc5623 codec connected to the i2s interface.

Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>


Index: sound-2.6/sound/soc/kirkwood/Kconfig
===================================================================
--- sound-2.6.orig/sound/soc/kirkwood/Kconfig	2010-09-06 23:39:39.000000000 +0200
+++ sound-2.6/sound/soc/kirkwood/Kconfig	2010-09-06 23:42:11.000000000 +0200
@@ -18,3 +18,13 @@
 	  Say Y if you want to add support for SoC audio on
 	  Openrd Client.
 
+config SND_KIRKWOOD_SOC_T5325
+	tristate "SoC Audio support for HP t5325"
+	#depends on SND_KIRKWOOD_SOC && MACH_T5325
+	depends on SND_KIRKWOOD_SOC
+	select SND_KIRKWOOD_SOC_I2S
+	select SND_SOC_ALC5623
+	help
+	  Say Y if you want to add support for SoC audio on
+	  the HP t5325 thin client.
+
Index: sound-2.6/sound/soc/kirkwood/Makefile
===================================================================
--- sound-2.6.orig/sound/soc/kirkwood/Makefile	2010-09-06 23:39:39.000000000 +0200
+++ sound-2.6/sound/soc/kirkwood/Makefile	2010-09-06 23:42:11.000000000 +0200
@@ -5,5 +5,7 @@
 obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o
 
 snd-soc-openrd-objs := kirkwood-openrd.o
+snd-soc-t5325-objs := kirkwood-t5325.o
 
 obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o
+obj-$(CONFIG_SND_KIRKWOOD_SOC_T5325) += snd-soc-t5325.o
Index: sound-2.6/sound/soc/kirkwood/kirkwood-t5325.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ sound-2.6/sound/soc/kirkwood/kirkwood-t5325.c	2010-09-06 23:42:11.000000000 +0200
@@ -0,0 +1,155 @@
+/*
+ * kirkwood-t5325.c
+ *
+ * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <mach/kirkwood.h>
+#include <plat/audio.h>
+#include <asm/mach-types.h>
+#include "../codecs/alc5623.h"
+
+static int t5325_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret;
+	unsigned int freq, fmt;
+
+	fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
+	ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+	if (ret < 0)
+		return ret;
+
+	switch (params_rate(params)) {
+	default:
+	case 44100:
+		freq = 11289600;
+		break;
+	case 48000:
+		freq = 12288000;
+		break;
+	case 96000:
+		freq = 24576000;
+		break;
+	}
+
+	return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
+
+}
+
+static struct snd_soc_ops t5325_ops = {
+	.hw_params = t5325_hw_params,
+};
+
+static const struct snd_soc_dapm_widget t5325_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route t5325_route[] = {
+	{ "Headphone Jack",	NULL,	"HPL" },
+	{ "Headphone Jack",	NULL,	"HPR" },
+
+	{"Speaker",		NULL,	"SPKOUT"},
+	{"Speaker",		NULL,	"SPKOUTN"},
+
+	{ "MIC1",		NULL,	"Mic Jack" },
+	{ "MIC2",		NULL,	"Mic Jack" },
+};
+
+static int t5325_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+
+	snd_soc_dapm_new_controls(codec, t5325_dapm_widgets,
+				ARRAY_SIZE(t5325_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, t5325_route, ARRAY_SIZE(t5325_route));
+
+	snd_soc_dapm_enable_pin(codec, "Mic Jack");
+	snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+	snd_soc_dapm_enable_pin(codec, "Speaker");
+
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_dai_link t5325_dai[] = {
+{
+	.name = "ALC5621",
+	.stream_name = "ALC5621 HiFi",
+	.cpu_dai_name = "kirkwood-i2s",
+	.platform_name = "kirkwood-pcm-audio",
+	.codec_dai_name = "alc5621-hifi",
+	.codec_name = "alc562x-codec.0-001a",
+	.ops = &t5325_ops,
+	.init = t5325_dai_init,
+},
+};
+
+
+static struct snd_soc_card t5325 = {
+	.name = "t5325",
+	.dai_link = t5325_dai,
+	.num_links = ARRAY_SIZE(t5325_dai),
+};
+
+static struct platform_device *t5325_snd_device;
+
+static int __init t5325_init(void)
+{
+	int ret;
+
+	if (!machine_is_t5325())
+		return 0;
+
+	t5325_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!t5325_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(t5325_snd_device,
+			&t5325);
+
+	ret = platform_device_add(t5325_snd_device);
+	if (ret) {
+		printk(KERN_ERR "%s: platform_device_add failed\n", __func__);
+		platform_device_put(t5325_snd_device);
+	}
+
+	return ret;
+}
+
+static void __exit t5325_exit(void)
+{
+	platform_device_unregister(t5325_snd_device);
+}
+
+module_init(t5325_init);
+module_exit(t5325_exit);
+
+/* Module information */
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("ALSA SoC t5325 audio client");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:soc-audio");

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

* [patch 3/3] t5325: add audio support
  2010-09-07  7:01 [patch 0/3] ASoC: t5325 audio support Arnaud Patard
  2010-09-07  7:01 ` [patch 1/3] ASoC: add support for alc562[123] codecs Arnaud Patard
  2010-09-07  7:01 ` [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients Arnaud Patard
@ 2010-09-07  7:01 ` Arnaud Patard
  2010-09-07 10:23   ` Mark Brown
  2 siblings, 1 reply; 11+ messages in thread
From: Arnaud Patard @ 2010-09-07  7:01 UTC (permalink / raw)
  To: alsa-devel
  Cc: Arnaud Patard, Mark Brown, Martin Michlmayr, Saeed Bishara,
	Liam Girdwood

[-- Attachment #1: t5325-2.patch --]
[-- Type: text/plain, Size: 1533 bytes --]

This patch declares the i2c audio codec and initialise audio.

Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>
Index: sound-2.6/arch/arm/mach-kirkwood/t5325-setup.c
===================================================================
--- sound-2.6.orig/arch/arm/mach-kirkwood/t5325-setup.c	2010-09-06 23:35:50.000000000 +0200
+++ sound-2.6/arch/arm/mach-kirkwood/t5325-setup.c	2010-09-06 23:42:21.000000000 +0200
@@ -23,6 +23,7 @@
 #include <linux/gpio.h>
 #include <linux/gpio_keys.h>
 #include <linux/input.h>
+#include <linux/alc5623.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <mach/kirkwood.h>
@@ -134,6 +135,7 @@
 	MPP33_GE1_TXCTL,
 	MPP39_AU_I2SBCLK,
 	MPP40_AU_I2SDO,
+	MPP43_AU_I2SDI,
 	MPP41_AU_I2SLRCLK,
 	MPP42_AU_I2SMCLK,
 	MPP45_GPIO,		/* Power button */
@@ -141,6 +143,18 @@
 	0
 };
 
+static struct alc5623_platform_data alc5621_data = {
+	.add_ctrl = 0x3700,
+	.jack_det_ctrl = 0x4810,
+};
+
+static struct i2c_board_info i2c_board_info[] __initdata = {
+	{
+		I2C_BOARD_INFO("alc5621", 0x1a),
+		.platform_data = &alc5621_data,
+	},
+};
+
 #define HP_T5325_GPIO_POWER_OFF		48
 
 static void hp_t5325_power_off(void)
@@ -166,6 +180,9 @@
 	kirkwood_ehci_init();
 	platform_device_register(&hp_t5325_button_device);
 
+	i2c_register_board_info(0, i2c_board_info, ARRAY_SIZE(i2c_board_info));
+	kirkwood_audio_init();
+
 	if (gpio_request(HP_T5325_GPIO_POWER_OFF, "power-off") == 0 &&
 	    gpio_direction_output(HP_T5325_GPIO_POWER_OFF, 0) == 0)
 		pm_power_off = hp_t5325_power_off;

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

* Re: [patch 1/3] ASoC: add support for alc562[123] codecs
  2010-09-07  7:01 ` [patch 1/3] ASoC: add support for alc562[123] codecs Arnaud Patard
@ 2010-09-07 10:21   ` Mark Brown
  2010-09-07 13:23     ` Arnaud Patard
  0 siblings, 1 reply; 11+ messages in thread
From: Mark Brown @ 2010-09-07 10:21 UTC (permalink / raw)
  To: Arnaud Patard; +Cc: alsa-devel, Saeed Bishara, Martin Michlmayr, Liam Girdwood

On Tue, Sep 07, 2010 at 09:01:28AM +0200, Arnaud Patard wrote:
> This patch is adding support for alc562[123] codecs. It's based
> on the source code available in HP source code and other places.
> 
> Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>

> +++ sound-2.6/sound/soc/codecs/Kconfig	2010-09-06 23:29:31.000000000 +0200
> @@ -159,6 +159,9 @@
>  config SND_SOC_PCM3008
>         tristate
>  
> +config SND_SOC_ALC5623
> +       tristate
> +

Keep this and the Makefile sorted.  Also remember to add this to
SND_SOC_ALL_CODECS.

> +#define ALC5623_VERSION "0.01"

git should provide enough tracking for version numbers.

> +static int caps_charge = 2000;
> +module_param(caps_charge, int, 0);
> +MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");

> +static int alc5623_write_mask(struct snd_soc_codec *codec, unsigned int reg,
> +		unsigned int value, unsigned int mask)

This is snd_soc_update_bits().

> +/*
> + * problem is :
> + * - hp mixer has 2 bits in PM reg 0x3C
> + * - adc input of the hp mixer has 2 mute bits
> + * - all the other inputs of the hp mixer have 1 bit
> + * =>  the bits for adc and pm can't be used separately
> + */
> +static int hp_mixer_event(struct snd_soc_dapm_widget *w,
> +	struct snd_kcontrol *kcontrol, int event)
> +{
> +	u16 reg;
> +	u16 mask = ALC5623_PWR_ADD2_L_HP_MIXER|ALC5623_PWR_ADD2_R_HP_MIXER;
> +
> +	reg = snd_soc_read(w->codec, ALC5623_PWR_MANAG_ADD2);
> +	reg &= ~mask;
> +	if (event == SND_SOC_DAPM_POST_PMU)
> +		reg |= mask;
> +	return snd_soc_write(w->codec, ALC5623_PWR_MANAG_ADD2, reg);
> +}

It looks like it'd be simpler to do something like:

	{ "HPL", NULL, "HP" },
	{ "HPR", NULL, "HP" },

then have the two ADC channels feed into the left and right channels of
the headphone while having the single switches feed into the HP widget
(which would be a NOPM dummy widget for routing purposes).

> +static int amp_mixer_event(struct snd_soc_dapm_widget *w,
> +	struct snd_kcontrol *kcontrol, int event)
> +{
> +	/* index 0x46: class-d internal register */
> +	snd_soc_write(w->codec, ALC5623_HID_CTRL_INDEX, 0x46);
> +	if (event == SND_SOC_DAPM_PRE_PMU)
> +		snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0xFFFF);
> +	else
> +		snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0);
> +	return 0;
> +}

A switch statement would be more idiomatic here.

> +static const struct soc_enum alc5623_enum[] = {
> +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 14, 4, alc5623_spk_n_sour_sel),
> +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 9, 2, alc5623_hpl_out_input_sel),

Don't put your enums in an array, declare them as variables.  The enum
arrays are harder to read and vastly more error prone.

> +	if (pll_div) {
> +		snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
> +		snd_soc_write(codec, ALC5623_PLL_CTRL, pll_div);
> +		alc5623_write_mask(codec, ALC5623_PWR_MANAG_ADD2,
> +					ALC5623_PWR_ADD2_PLL,
> +					ALC5623_PWR_ADD2_PLL);
> +		gbl_clk |= ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL;
> +		snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
> +	}

Should complain if we fail to find a configuration.

> +#define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \
> +	| ALC5623_PWR_ADD3_MIC1_BOOST_AD)

> +#define ALC5623_ADD1_POWER_EN \
> +	(ALC5623_PWR_ADD1_MAIN_I2S_EN | ALC5623_PWR_ADD1_MIC1_BIAS_EN \
> +	| ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \
> +	| ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \
> +	| ALC5623_PWR_ADD1_HP_OUT_ENH_AMP)

> +#define ALC5623_ADD1_POWER_EN_5622 \
> +	(ALC5623_PWR_ADD1_MAIN_I2S_EN | ALC5623_PWR_ADD1_MIC1_BIAS_EN \
> +	| ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
> +	| ALC5623_PWR_ADD1_HP_OUT_AMP)

Most of the stuff in here looks like it ought to be managed by DAPM?

> +static int alc5623_probe(struct snd_soc_codec *codec)
> +{
> +	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
> +	int ret;
> +
> +	INIT_DELAYED_WORK(&codec->delayed_work, alc5623_work);
> +	schedule_delayed_work(&codec->delayed_work,
> +				msecs_to_jiffies(caps_charge));

Since we now support out of line resume for ASoC devices (so we don't
hold up the rest of the system) it should be possible to just do this
stuff in the set_bias_level() function rather than faffing around with
delayed work like this.  This is less racy.

> +	alc5623_reset(codec);
> +	alc5623_fill_cache(codec);

Don't we know the defaults for the device?

> +	if (alc5623->add_ctrl) {
> +		snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
> +				alc5623->add_ctrl);
> +	}

Hrm?

> +	if (alc5623->jack_det_ctrl) {
> +		snd_soc_write(codec, ALC5623_JACK_DET_CTRL,
> +				alc5623->jack_det_ctrl);
> +	}

I didn't see any jack detection support in the driver, and I wouldn't
expect to see anything exported to userspace for it?

> +	if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
> +		dev_err(&client->dev, "unknown or wrong codec\n");
> +		return -ENODEV;
> +	}

Better to say what the IDs you were looking at are, especially for id2.

> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ sound-2.6/include/linux/alc5623.h	2010-09-06 23:29:31.000000000 +0200

include/sound

> @@ -0,0 +1,8 @@
> +#ifndef _INCLUDE_LINUX_ALC5623_H
> +#define _INCLUDE_LINUX_ALC5623_H
> +struct alc5623_platform_data {
> +	unsigned int add_ctrl;
> +	unsigned int jack_det_ctrl;
> +};

Some documentation explaining what these are would probably be useful.

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

* Re: [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients
  2010-09-07  7:01 ` [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients Arnaud Patard
@ 2010-09-07 10:21   ` Mark Brown
  0 siblings, 0 replies; 11+ messages in thread
From: Mark Brown @ 2010-09-07 10:21 UTC (permalink / raw)
  To: Arnaud Patard; +Cc: alsa-devel, Saeed Bishara, Martin Michlmayr, Liam Girdwood

On Tue, Sep 07, 2010 at 09:01:29AM +0200, Arnaud Patard wrote:
> This patch is adding support for hp t5325 thin clients.
> There's a alc5623 codec connected to the i2s interface.
> 
> Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>

This looks OK, though if you could move the module_init()/module_exit()
next to the functions that'd be better.

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

* Re: [patch 3/3] t5325: add audio support
  2010-09-07  7:01 ` [patch 3/3] t5325: add audio support Arnaud Patard
@ 2010-09-07 10:23   ` Mark Brown
  0 siblings, 0 replies; 11+ messages in thread
From: Mark Brown @ 2010-09-07 10:23 UTC (permalink / raw)
  To: Arnaud Patard; +Cc: alsa-devel, Liam Girdwood, Martin Michlmayr, Saeed Bishara

On Tue, Sep 07, 2010 at 09:01:30AM +0200, Arnaud Patard wrote:
> This patch declares the i2c audio codec and initialise audio.
> 
> Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>

This is going to depend on the ASoC CODEC driver for the device so will
need to be merged along with that - if it's OK to do that can the
Kirkwood guys please ack it?

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

* Re: [patch 1/3] ASoC: add support for alc562[123] codecs
  2010-09-07 10:21   ` Mark Brown
@ 2010-09-07 13:23     ` Arnaud Patard
  2010-09-08 10:08       ` Mark Brown
  0 siblings, 1 reply; 11+ messages in thread
From: Arnaud Patard @ 2010-09-07 13:23 UTC (permalink / raw)
  To: Mark Brown; +Cc: alsa-devel, Saeed Bishara, Martin Michlmayr, Liam Girdwood

Mark Brown <broonie@opensource.wolfsonmicro.com> writes:

> On Tue, Sep 07, 2010 at 09:01:28AM +0200, Arnaud Patard wrote:
>> This patch is adding support for alc562[123] codecs. It's based
>> on the source code available in HP source code and other places.
>> 
>> Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>
>
>> +++ sound-2.6/sound/soc/codecs/Kconfig	2010-09-06 23:29:31.000000000 +0200
>> @@ -159,6 +159,9 @@
>>  config SND_SOC_PCM3008
>>         tristate
>>  
>> +config SND_SOC_ALC5623
>> +       tristate
>> +
>
> Keep this and the Makefile sorted.  Also remember to add this to
> SND_SOC_ALL_CODECS.

ok

>
>> +#define ALC5623_VERSION "0.01"
>
> git should provide enough tracking for version numbers.

oops. forgot to kill it. It's unused.

>
>> +static int caps_charge = 2000;
>> +module_param(caps_charge, int, 0);
>> +MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");
>
>> +static int alc5623_write_mask(struct snd_soc_codec *codec, unsigned int reg,
>> +		unsigned int value, unsigned int mask)
>
> This is snd_soc_update_bits().

great. will use it.

>
>> +/*
>> + * problem is :
>> + * - hp mixer has 2 bits in PM reg 0x3C
>> + * - adc input of the hp mixer has 2 mute bits
>> + * - all the other inputs of the hp mixer have 1 bit
>> + * =>  the bits for adc and pm can't be used separately
>> + */
>> +static int hp_mixer_event(struct snd_soc_dapm_widget *w,
>> +	struct snd_kcontrol *kcontrol, int event)
>> +{
>> +	u16 reg;
>> +	u16 mask = ALC5623_PWR_ADD2_L_HP_MIXER|ALC5623_PWR_ADD2_R_HP_MIXER;
>> +
>> +	reg = snd_soc_read(w->codec, ALC5623_PWR_MANAG_ADD2);
>> +	reg &= ~mask;
>> +	if (event == SND_SOC_DAPM_POST_PMU)
>> +		reg |= mask;
>> +	return snd_soc_write(w->codec, ALC5623_PWR_MANAG_ADD2, reg);
>> +}
>
> It looks like it'd be simpler to do something like:
>
> 	{ "HPL", NULL, "HP" },
> 	{ "HPR", NULL, "HP" },
>
> then have the two ADC channels feed into the left and right channels of
> the headphone while having the single switches feed into the HP widget
> (which would be a NOPM dummy widget for routing purposes).

hmm... I'm not sure to understand correctly. The two bits
ALC5623_PWR_ADD2_?_HP_MIXER are for the HP mixers. There are 2 mixers
but I'm doing like there's only 1 mixer due to the single bit inputs.
Can you please give more details about your suggestion ?

>
>> +static int amp_mixer_event(struct snd_soc_dapm_widget *w,
>> +	struct snd_kcontrol *kcontrol, int event)
>> +{
>> +	/* index 0x46: class-d internal register */
>> +	snd_soc_write(w->codec, ALC5623_HID_CTRL_INDEX, 0x46);
>> +	if (event == SND_SOC_DAPM_PRE_PMU)
>> +		snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0xFFFF);
>> +	else
>> +		snd_soc_write(w->codec, ALC5623_HID_CTRL_DATA, 0);
>> +	return 0;
>> +}
>
> A switch statement would be more idiomatic here.
>

ok

>> +static const struct soc_enum alc5623_enum[] = {
>> +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 14, 4, alc5623_spk_n_sour_sel),
>> +SOC_ENUM_SINGLE(ALC5623_OUTPUT_MIXER_CTRL, 9, 2, alc5623_hpl_out_input_sel),
>
> Don't put your enums in an array, declare them as variables.  The enum
> arrays are harder to read and vastly more error prone.
>
ok

>> +	if (pll_div) {
>> +		snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
>> +		snd_soc_write(codec, ALC5623_PLL_CTRL, pll_div);
>> +		alc5623_write_mask(codec, ALC5623_PWR_MANAG_ADD2,
>> +					ALC5623_PWR_ADD2_PLL,
>> +					ALC5623_PWR_ADD2_PLL);
>> +		gbl_clk |= ALC5623_GBL_CLK_SYS_SOUR_SEL_PLL;
>> +		snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
>> +	}
>
> Should complain if we fail to find a configuration.
>
>> +#define ALC5623_ADD3_POWER_EN (ALC5623_PWR_ADD3_MAIN_BIAS \
>> +	| ALC5623_PWR_ADD3_MIC1_BOOST_AD)
>
>> +#define ALC5623_ADD1_POWER_EN \
>> +	(ALC5623_PWR_ADD1_MAIN_I2S_EN | ALC5623_PWR_ADD1_MIC1_BIAS_EN \
>> +	| ALC5623_PWR_ADD1_SHORT_CURR_DET_EN | ALC5623_PWR_ADD1_SOFTGEN_EN \
>> +	| ALC5623_PWR_ADD1_DEPOP_BUF_HP | ALC5623_PWR_ADD1_HP_OUT_AMP \
>> +	| ALC5623_PWR_ADD1_HP_OUT_ENH_AMP)
>
>> +#define ALC5623_ADD1_POWER_EN_5622 \
>> +	(ALC5623_PWR_ADD1_MAIN_I2S_EN | ALC5623_PWR_ADD1_MIC1_BIAS_EN \
>> +	| ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
>> +	| ALC5623_PWR_ADD1_HP_OUT_AMP)
>
> Most of the stuff in here looks like it ought to be managed by DAPM?

hmm... I thought it was easier/better done like this. Is it a hard
requirement ?

>
>> +static int alc5623_probe(struct snd_soc_codec *codec)
>> +{
>> +	struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
>> +	int ret;
>> +
>> +	INIT_DELAYED_WORK(&codec->delayed_work, alc5623_work);
>> +	schedule_delayed_work(&codec->delayed_work,
>> +				msecs_to_jiffies(caps_charge));
>
> Since we now support out of line resume for ASoC devices (so we don't
> hold up the rest of the system) it should be possible to just do this
> stuff in the set_bias_level() function rather than faffing around with
> delayed work like this.  This is less racy.
>

I was even wondering about removing that stuff but as it was there in
original driver, I choose to keep it. WDYT ?

>> +	alc5623_reset(codec);
>> +	alc5623_fill_cache(codec);
>
> Don't we know the defaults for the device?

as this driver is handling 3 codec with some small differencies, instead
of having to hunt for the changes between the default values, I prefered
reading them from the device.

>
>> +	if (alc5623->add_ctrl) {
>> +		snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
>> +				alc5623->add_ctrl);
>> +	}
>
> Hrm?

this register contains some board specific values and I didn't see how
it could be handled except by using platform_data.

>
>> +	if (alc5623->jack_det_ctrl) {
>> +		snd_soc_write(codec, ALC5623_JACK_DET_CTRL,
>> +				alc5623->jack_det_ctrl);
>> +	}
>
> I didn't see any jack detection support in the driver, and I wouldn't
> expect to see anything exported to userspace for it?

the jack detection is handled by the codec itself. The pin are connected
to gpio on it and the codec is switching channels on/off without user
intervention. Nothing to do on driver side except configuring this
register.

>
>> +	if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
>> +		dev_err(&client->dev, "unknown or wrong codec\n");
>> +		return -ENODEV;
>> +	}
>
> Better to say what the IDs you were looking at are, especially for id2.
>
>> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
>> +++ sound-2.6/include/linux/alc5623.h	2010-09-06 23:29:31.000000000 +0200
>
> include/sound
>
>> @@ -0,0 +1,8 @@
>> +#ifndef _INCLUDE_LINUX_ALC5623_H
>> +#define _INCLUDE_LINUX_ALC5623_H
>> +struct alc5623_platform_data {
>> +	unsigned int add_ctrl;
>> +	unsigned int jack_det_ctrl;
>> +};
>
> Some documentation explaining what these are would probably be useful.

The names are really near to the names in the specs. As you need to look
at the specs to have their values, I thought it would be enough.

Arnaud

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

* Re: [patch 1/3] ASoC: add support for alc562[123] codecs
  2010-09-07 13:23     ` Arnaud Patard
@ 2010-09-08 10:08       ` Mark Brown
  0 siblings, 0 replies; 11+ messages in thread
From: Mark Brown @ 2010-09-08 10:08 UTC (permalink / raw)
  To: Arnaud Patard; +Cc: alsa-devel, Saeed Bishara, Martin Michlmayr, Liam Girdwood

On Tue, Sep 07, 2010 at 03:23:20PM +0200, Arnaud Patard wrote:
> Mark Brown <broonie@opensource.wolfsonmicro.com> writes:

> > It looks like it'd be simpler to do something like:

> > 	{ "HPL", NULL, "HP" },
> > 	{ "HPR", NULL, "HP" },

> > then have the two ADC channels feed into the left and right channels of
> > the headphone while having the single switches feed into the HP widget
> > (which would be a NOPM dummy widget for routing purposes).

> hmm... I'm not sure to understand correctly. The two bits
> ALC5623_PWR_ADD2_?_HP_MIXER are for the HP mixers. There are 2 mixers
> but I'm doing like there's only 1 mixer due to the single bit inputs.
> Can you please give more details about your suggestion ?

Add on to the above things like this:

	{ "HPL", "ADC Switch", "ADCL" },
	{ "HPR", "ADC Switch", "ADCR" },

	{ "HP", "AUX Switch", "Aux" },

so you get separate left/right control where you have stereo control
while still getting the mono control on the virtual mono HP widget.

> >> +#define ALC5623_ADD1_POWER_EN_5622 \
> >> +	(ALC5623_PWR_ADD1_MAIN_I2S_EN | ALC5623_PWR_ADD1_MIC1_BIAS_EN \
> >> +	| ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
> >> +	| ALC5623_PWR_ADD1_HP_OUT_AMP)

> > Most of the stuff in here looks like it ought to be managed by DAPM?

> hmm... I thought it was easier/better done like this. Is it a hard
> requirement ?

Given that you're already defining widgets for things like the I2S (the
AIF widgets) it seems silly not to.  For the micbias I'd say it's a hard
requirement since driving the micbias when not needed is potenially
harmful.

> > Since we now support out of line resume for ASoC devices (so we don't
> > hold up the rest of the system) it should be possible to just do this
> > stuff in the set_bias_level() function rather than faffing around with
> > delayed work like this.  This is less racy.

> I was even wondering about removing that stuff but as it was there in
> original driver, I choose to keep it. WDYT ?

If you look at the current WM8753 driver you'll see that it's been
removed from there.

> >> +	if (alc5623->add_ctrl) {
> >> +		snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
> >> +				alc5623->add_ctrl);
> >> +	}

> > Hrm?

> this register contains some board specific values and I didn't see how
> it could be handled except by using platform_data.

What are they?

> >> +#ifndef _INCLUDE_LINUX_ALC5623_H
> >> +#define _INCLUDE_LINUX_ALC5623_H
> >> +struct alc5623_platform_data {
> >> +	unsigned int add_ctrl;
> >> +	unsigned int jack_det_ctrl;
> >> +};

> > Some documentation explaining what these are would probably be useful.

> The names are really near to the names in the specs. As you need to look
> at the specs to have their values, I thought it would be enough.

So saying something like "Values to be written to the add_ctrl and
jack_det_ctrl registers" would cover it.  Remember, if a software
engineer is picking up a preexisting driver they may never even look at
the datasheet.

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

* Re: [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients
  2010-10-12  9:44 ` [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients Arnaud Patard
@ 2010-10-12 17:17   ` Mark Brown
  0 siblings, 0 replies; 11+ messages in thread
From: Mark Brown @ 2010-10-12 17:17 UTC (permalink / raw)
  To: Arnaud Patard; +Cc: alsa-devel, Saeed Bishara, Martin Michlmayr, Liam Girdwood

On Tue, Oct 12, 2010 at 11:44:55AM +0200, Arnaud Patard wrote:

> +config SND_KIRKWOOD_SOC_T5325
> +	tristate "SoC Audio support for HP t5325"
> +	#depends on SND_KIRKWOOD_SOC && MACH_T5325

Hrm?

> +	switch (params_rate(params)) {
> +	default:
> +	case 44100:
> +		freq = 11289600;
> +		break;
> +	case 48000:
> +		freq = 12288000;
> +		break;
> +	case 96000:
> +		freq = 24576000;
> +		break;
> +	}

This should have a default: or (better) just be rewritten to use
params_rate(params) * 256 as the sysclk.

> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:soc-audio");

No, you're registering the device here not the driver.

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

* [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients
  2010-10-12  9:44 [patch 0/3] ALC562[123] / t5325 sound support - try 2 Arnaud Patard
@ 2010-10-12  9:44 ` Arnaud Patard
  2010-10-12 17:17   ` Mark Brown
  0 siblings, 1 reply; 11+ messages in thread
From: Arnaud Patard @ 2010-10-12  9:44 UTC (permalink / raw)
  To: alsa-devel
  Cc: Arnaud Patard, Mark Brown, Martin Michlmayr, Saeed Bishara,
	Liam Girdwood

[-- Attachment #1: t5325.patch --]
[-- Type: text/plain, Size: 5384 bytes --]

This patch is adding support for hp t5325 thin clients.
There's a alc5623 codec connected to the i2s interface.

Signed-off-by: Arnaud Patard <arnaud.patard@rtp-net.org>


Index: sound-2.6/sound/soc/kirkwood/Kconfig
===================================================================
--- sound-2.6.orig/sound/soc/kirkwood/Kconfig	2010-09-07 13:18:32.000000000 +0200
+++ sound-2.6/sound/soc/kirkwood/Kconfig	2010-09-07 13:19:27.000000000 +0200
@@ -18,3 +18,13 @@
 	  Say Y if you want to add support for SoC audio on
 	  Openrd Client.
 
+config SND_KIRKWOOD_SOC_T5325
+	tristate "SoC Audio support for HP t5325"
+	#depends on SND_KIRKWOOD_SOC && MACH_T5325
+	depends on SND_KIRKWOOD_SOC
+	select SND_KIRKWOOD_SOC_I2S
+	select SND_SOC_ALC5623
+	help
+	  Say Y if you want to add support for SoC audio on
+	  the HP t5325 thin client.
+
Index: sound-2.6/sound/soc/kirkwood/Makefile
===================================================================
--- sound-2.6.orig/sound/soc/kirkwood/Makefile	2010-09-07 13:18:32.000000000 +0200
+++ sound-2.6/sound/soc/kirkwood/Makefile	2010-09-07 13:19:27.000000000 +0200
@@ -5,5 +5,7 @@
 obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o
 
 snd-soc-openrd-objs := kirkwood-openrd.o
+snd-soc-t5325-objs := kirkwood-t5325.o
 
 obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o
+obj-$(CONFIG_SND_KIRKWOOD_SOC_T5325) += snd-soc-t5325.o
Index: sound-2.6/sound/soc/kirkwood/kirkwood-t5325.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ sound-2.6/sound/soc/kirkwood/kirkwood-t5325.c	2010-09-07 13:21:15.000000000 +0200
@@ -0,0 +1,153 @@
+/*
+ * kirkwood-t5325.c
+ *
+ * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <mach/kirkwood.h>
+#include <plat/audio.h>
+#include <asm/mach-types.h>
+#include "../codecs/alc5623.h"
+
+static int t5325_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret;
+	unsigned int freq, fmt;
+
+	fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS;
+	ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+	if (ret < 0)
+		return ret;
+
+	switch (params_rate(params)) {
+	default:
+	case 44100:
+		freq = 11289600;
+		break;
+	case 48000:
+		freq = 12288000;
+		break;
+	case 96000:
+		freq = 24576000;
+		break;
+	}
+
+	return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
+
+}
+
+static struct snd_soc_ops t5325_ops = {
+	.hw_params = t5325_hw_params,
+};
+
+static const struct snd_soc_dapm_widget t5325_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route t5325_route[] = {
+	{ "Headphone Jack",	NULL,	"HPL" },
+	{ "Headphone Jack",	NULL,	"HPR" },
+
+	{"Speaker",		NULL,	"SPKOUT"},
+	{"Speaker",		NULL,	"SPKOUTN"},
+
+	{ "MIC1",		NULL,	"Mic Jack" },
+	{ "MIC2",		NULL,	"Mic Jack" },
+};
+
+static int t5325_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+
+	snd_soc_dapm_new_controls(codec, t5325_dapm_widgets,
+				ARRAY_SIZE(t5325_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, t5325_route, ARRAY_SIZE(t5325_route));
+
+	snd_soc_dapm_enable_pin(codec, "Mic Jack");
+	snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+	snd_soc_dapm_enable_pin(codec, "Speaker");
+
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_dai_link t5325_dai[] = {
+{
+	.name = "ALC5621",
+	.stream_name = "ALC5621 HiFi",
+	.cpu_dai_name = "kirkwood-i2s",
+	.platform_name = "kirkwood-pcm-audio",
+	.codec_dai_name = "alc5621-hifi",
+	.codec_name = "alc562x-codec.0-001a",
+	.ops = &t5325_ops,
+	.init = t5325_dai_init,
+},
+};
+
+
+static struct snd_soc_card t5325 = {
+	.name = "t5325",
+	.dai_link = t5325_dai,
+	.num_links = ARRAY_SIZE(t5325_dai),
+};
+
+static struct platform_device *t5325_snd_device;
+
+static int __init t5325_init(void)
+{
+	int ret;
+
+	if (!machine_is_t5325())
+		return 0;
+
+	t5325_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!t5325_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(t5325_snd_device,
+			&t5325);
+
+	ret = platform_device_add(t5325_snd_device);
+	if (ret) {
+		printk(KERN_ERR "%s: platform_device_add failed\n", __func__);
+		platform_device_put(t5325_snd_device);
+	}
+
+	return ret;
+}
+module_init(t5325_init);
+
+static void __exit t5325_exit(void)
+{
+	platform_device_unregister(t5325_snd_device);
+}
+module_exit(t5325_exit);
+
+MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
+MODULE_DESCRIPTION("ALSA SoC t5325 audio client");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:soc-audio");

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

end of thread, other threads:[~2010-10-12 17:17 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-09-07  7:01 [patch 0/3] ASoC: t5325 audio support Arnaud Patard
2010-09-07  7:01 ` [patch 1/3] ASoC: add support for alc562[123] codecs Arnaud Patard
2010-09-07 10:21   ` Mark Brown
2010-09-07 13:23     ` Arnaud Patard
2010-09-08 10:08       ` Mark Brown
2010-09-07  7:01 ` [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients Arnaud Patard
2010-09-07 10:21   ` Mark Brown
2010-09-07  7:01 ` [patch 3/3] t5325: add audio support Arnaud Patard
2010-09-07 10:23   ` Mark Brown
2010-10-12  9:44 [patch 0/3] ALC562[123] / t5325 sound support - try 2 Arnaud Patard
2010-10-12  9:44 ` [patch 2/3] kirkwood: Add audio support to hp t5325 thin clients Arnaud Patard
2010-10-12 17:17   ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.