On Thu, 2009-10-08 at 13:58 +0200, Valentin Eduardo (Nokia-D/Helsinki) wrote: > From: Eduardo Valentin <eduardo.valentin@xxxxxxxxx> > > Introduce RX-51 Machine driver for ASoC and AIC34b_dummy (block B) i2c driver. > > Also move the request_gpio of speaker_enabled > from board-rx51-peripherals.c to this machine driver. > > These drivers were originally written by Jarkko Nikula. > > Signed-off-by: Eduardo Valentin <eduardo.valentin@xxxxxxxxx> > --- > arch/arm/mach-omap2/board-rx51-peripherals.c | 2 - > sound/soc/omap/Kconfig | 10 + > sound/soc/omap/Makefile | 2 + > sound/soc/omap/aic34b_dummy.c | 271 +++++++++ > sound/soc/omap/aic34b_dummy.h | 32 + > sound/soc/omap/rx51.c | 793 ++++++++++++++++++++++++++ > sound/soc/omap/rx51.h | 29 + > 7 files changed, 1137 insertions(+), 2 deletions(-) > create mode 100644 sound/soc/omap/aic34b_dummy.c > create mode 100644 sound/soc/omap/aic34b_dummy.h > create mode 100644 sound/soc/omap/rx51.c > create mode 100644 sound/soc/omap/rx51.h > > diff --git a/arch/arm/mach-omap2/board-rx51-peripherals.c b/arch/arm/mach-omap2/board-rx51-peripherals.c > index c1af532..b227475 100644 > --- a/arch/arm/mach-omap2/board-rx51-peripherals.c > +++ b/arch/arm/mach-omap2/board-rx51-peripherals.c > @@ -262,8 +262,6 @@ static int rx51_twlgpio_setup(struct device *dev, unsigned gpio, unsigned n) > /* FIXME this gpio setup is just a placeholder for now */ > gpio_request(gpio + 6, "backlight_pwm"); > gpio_direction_output(gpio + 6, 0); > - gpio_request(gpio + 7, "speaker_en"); > - gpio_direction_output(gpio + 7, 1); > > /* set up MMC adapters, linking their regulators to them */ > twl4030_mmc_init(mmc); > diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig > index 2dee983..bdcd4be 100644 > --- a/sound/soc/omap/Kconfig > +++ b/sound/soc/omap/Kconfig > @@ -15,6 +15,16 @@ config SND_OMAP_SOC_N810 > help > Say Y if you want to add support for SoC audio on Nokia N810. > > +config SND_OMAP_SOC_RX51 > + tristate "SoC Audio support for Nokia RX51" > + depends on SND_OMAP_SOC && MACH_NOKIA_RX51 > + select OMAP_MCBSP > + select SND_OMAP_SOC_MCBSP > + select SND_SOC_TLV320AIC3X > + select SND_SOC_TPA6130A2 > + help > + Say Y if you want to add support for SoC audio on Nokia RX51. > + > config SND_OMAP_SOC_AMS_DELTA > tristate "SoC Audio support for Amstrad E3 (Delta) videophone" > depends on SND_OMAP_SOC && MACH_AMS_DELTA > diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile > index 02d6947..7dec270 100644 > --- a/sound/soc/omap/Makefile > +++ b/sound/soc/omap/Makefile > @@ -16,8 +16,10 @@ snd-soc-sdp3430-objs := sdp3430.o > snd-soc-omap3pandora-objs := omap3pandora.o > snd-soc-omap3beagle-objs := omap3beagle.o > snd-soc-zoom2-objs := zoom2.o > +snd-soc-rx51-objs := rx51.o > > obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o > +obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o aic34b_dummy.o > obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o > obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o > obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o > diff --git a/sound/soc/omap/aic34b_dummy.c b/sound/soc/omap/aic34b_dummy.c > new file mode 100644 > index 0000000..17c4d9c > --- /dev/null > +++ b/sound/soc/omap/aic34b_dummy.c > @@ -0,0 +1,271 @@ > +/* > + * aic34b_dummy.c -- Dummy driver for AIC34 block B parts used in Nokia RX51 > + * > + * Purpose for this driver is to cover few audio connections on Nokia RX51 HW > + * which are connected into block B of TLV320AIC34 dual codec. > + * > + * Copyright (C) 2008 - 2009 Nokia Corporation > + * > + * Contact: Peter Ujfalusi <peter.ujfalusi@xxxxxxxxx> > + * Eduardo Valentin <eduardo.valentin@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA > + * 02110-1301 USA > + * > + * TODO: > + * - Get rid of this driver, at least when ASoC v2 is merged and when > + * we can support multiple codec instances in tlv320aic3x.c driver. > + * This driver is hacked only for Nokia RX51 HW. > + */ > + > +#include <linux/module.h> > +#include <linux/errno.h> > +#include <linux/device.h> > +#include <linux/i2c.h> > +#include <sound/soc.h> > + > +#include "../codecs/tlv320aic3x.h" > + > +struct i2c_client *aic34b_client; > +static DEFINE_MUTEX(aic34b_mutex); > +static DEFINE_MUTEX(button_press_mutex); > +static ktime_t button_press_denial_start; > +static int aic34b_volume; > +static int button_press_denied; > +static int aic34b_bias; > + > + > +static int aic34b_read(struct i2c_client *client, unsigned int reg, > + u8 *value) > +{ > + int err; > + > + err = i2c_smbus_read_byte_data(client, reg); > + *value = err; > + return (err >= 0) ? 0 : err; > +} > + > +static int aic34b_write(struct i2c_client *client, unsigned int reg, > + u8 value) > +{ > + u8 data[2]; > + > + data[0] = reg & 0xff; > + data[1] = value & 0xff; > + > + return (i2c_master_send(client, data, 2) == 2) ? 0 : -EIO; > +} > + > +/* > + * Introduce a derivative FIR filter to detect unnecessary button > + * presses caused by a change in the MICBIAS. The filter returns > + * TRUE in the event there has not been a change in MICBIAS within > + * the time window (500ms). If the rate of change within the window > + * is >= 1, all button presses are denied. In addition, if bias is > + * zero, then all button presses are also denied explicitly. > + */ > +int allow_button_press(void) > +{ > + /* If bias is not on, no chance for button presses */ > + if (!aic34b_bias) > + return 0; > + > + /* If explicitly granted a button press */ > + if (!button_press_denied) { > + return 1; > + } else { > + int64_t delta; > + /* This is the FIR portion with specified time window */ > + mutex_lock(&button_press_mutex); > + delta = ktime_to_ns(ktime_sub(ktime_get(), > + button_press_denial_start)); > + > + if (delta < 0) { > + button_press_denied = 0; > + /* If the clock ever wraps */ > + button_press_denial_start.tv.sec = 0; > + button_press_denial_start.tv.nsec = 0; > + mutex_unlock(&button_press_mutex); > + return 1; > + } > + do_div(delta, 1000000); > + /* Time window is 500ms */ > + if (delta >= 500) { > + button_press_denied = 0; > + mutex_unlock(&button_press_mutex); > + return 1; > + } > + mutex_unlock(&button_press_mutex); > + } > + > + /* There was a change in MICBIAS within time window */ > + return 0; > +} > +EXPORT_SYMBOL(allow_button_press); > + > +static void deny_button_press(void) > +{ > + mutex_lock(&button_press_mutex); > + button_press_denied = 1; > + button_press_denial_start = ktime_get(); > + mutex_unlock(&button_press_mutex); > +} > + > +void aic34b_set_mic_bias(int bias) > +{ > + if (aic34b_client == NULL) > + return; > + > + mutex_lock(&aic34b_mutex); > + aic34b_write(aic34b_client, MICBIAS_CTRL, (bias & 0x3) << 6); > + aic34b_bias = bias; > + deny_button_press(); > + mutex_unlock(&aic34b_mutex); > +} > +EXPORT_SYMBOL(aic34b_set_mic_bias); > + > +int aic34b_set_volume(u8 volume) > +{ > + u8 val; > + > + if (aic34b_client == NULL) > + return 0; > + > + mutex_lock(&aic34b_mutex); > + > + /* Volume control for Right PGA to HPLOUT */ > + aic34b_read(aic34b_client, 49, &val); > + val &= ~0x7f; > + aic34b_write(aic34b_client, 49, val | (~volume & 0x7f)); > + > + /* Volume control for Right PGA to HPLCOM */ > + aic34b_read(aic34b_client, 56, &val); > + val &= ~0x7f; > + aic34b_write(aic34b_client, 56, val | (~volume & 0x7f)); > + > + aic34b_volume = volume; > + mutex_unlock(&aic34b_mutex); > + > + return 0; > +} > +EXPORT_SYMBOL(aic34b_set_volume); > + > +void aic34b_ear_enable(int enable) > +{ > + u8 val; > + > + if (aic34b_client == NULL) > + return; > + > + mutex_lock(&aic34b_mutex); > + if (enable) { > + /* Connect LINE2R to RADC */ > + aic34b_write(aic34b_client, LINE2R_2_RADC_CTRL, 0x80); > + /* Unmute Right ADC-PGA */ > + aic34b_write(aic34b_client, RADC_VOL, 0x00); > + /* Right PGA -> HPLOUT */ > + aic34b_read(aic34b_client, 49, &val); > + aic34b_write(aic34b_client, 49, val | 0x80); > + /* Unmute HPLOUT with 1 dB gain */ > + aic34b_write(aic34b_client, HPLOUT_CTRL, 0x19); > + /* Right PGA -> HPLCOM */ > + aic34b_read(aic34b_client, 56, &val); > + aic34b_write(aic34b_client, 56, val | 0x80); > + /* Unmute HPLCOM with 1 dB gain */ > + aic34b_write(aic34b_client, HPLCOM_CTRL, 0x19); > + } else { > + /* Disconnect LINE2R from RADC */ > + aic34b_write(aic34b_client, LINE2R_2_RADC_CTRL, 0xF8); > + /* Mute Right ADC-PGA */ > + aic34b_write(aic34b_client, RADC_VOL, 0x80); > + /* Detach Right PGA from HPLOUT */ > + aic34b_write(aic34b_client, 49, (~aic34b_volume & 0x7f)); > + /* Power down HPLOUT */ > + aic34b_write(aic34b_client, HPLOUT_CTRL, 0x06); > + /* Detach Right PGA from HPLCOM */ > + aic34b_write(aic34b_client, 56, (~aic34b_volume & 0x7f)); > + /* Power down HPLCOM */ > + aic34b_write(aic34b_client, HPLCOM_CTRL, 0x06); > + /* Deny any possible keypresses for a second */ > + deny_button_press(); > + /* To regain low power consumption, reset is needed */ > + aic34b_write(aic34b_client, AIC3X_RESET, SOFT_RESET); > + /* And need to restore volume level */ > + aic34b_write(aic34b_client, 49, (~aic34b_volume & 0x7f)); > + aic34b_write(aic34b_client, 56, (~aic34b_volume & 0x7f)); > + /* Need to restore MICBIAS if set */ > + if (aic34b_bias) > + aic34b_write(aic34b_client, MICBIAS_CTRL, > + (aic34b_bias & 0x3) << 6); > + } > + mutex_unlock(&aic34b_mutex); > +} > +EXPORT_SYMBOL(aic34b_ear_enable); > + > +static int aic34b_dummy_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + u8 val; > + > + if (aic34b_read(client, AIC3X_PLL_PROGA_REG, &val) || val != 0x10) { > + /* Chip not present */ > + return -ENODEV; > + } > + aic34b_client = client; > + > + /* Configure LINE2R for differential mode */ > + aic34b_read(client, LINE2R_2_RADC_CTRL, &val); > + aic34b_write(client, LINE2R_2_RADC_CTRL, val | 0x80); > + > + return 0; > +} > + > +static int aic34b_dummy_remove(struct i2c_client *client) > +{ > + aic34b_client = NULL; > + > + return 0; > +} > + > +static const struct i2c_device_id aic34b_dummy_id[] = { > + { "aic34b_dummy", 0 }, > + { } > +}; > +MODULE_DEVICE_TABLE(i2c, aic34b_dummy_id); > + > +static struct i2c_driver aic34b_dummy_driver = { > + .driver = { > + .name = "aic34b_dummy" > + }, > + .probe = aic34b_dummy_probe, > + .remove = aic34b_dummy_remove, > + .id_table = aic34b_dummy_id, > +}; > + > +static int __init aic34b_dummy_init(void) > +{ > + return i2c_add_driver(&aic34b_dummy_driver); > +} > + > +static void __exit aic34b_dummy_exit(void) > +{ > + i2c_del_driver(&aic34b_dummy_driver); > +} > + > +MODULE_AUTHOR("Nokia Corporation"); > +MODULE_DESCRIPTION("Dummy driver for AIC34 block B parts used on Nokia RX51"); > +MODULE_LICENSE("GPL"); > + > +module_init(aic34b_dummy_init); > +module_exit(aic34b_dummy_exit); > diff --git a/sound/soc/omap/aic34b_dummy.h b/sound/soc/omap/aic34b_dummy.h > new file mode 100644 > index 0000000..2d386bf > --- /dev/null > +++ b/sound/soc/omap/aic34b_dummy.h > @@ -0,0 +1,32 @@ > +/* > + * aic34b_dummy.h > + * > + * Copyright (C) 2008 - 2009 Nokia Corporation > + * > + * Contact: Peter Ujfalusi <peter.ujfalusi@xxxxxxxxx> > + * Eduardo Valentin <eduardo.valentin@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA > + * 02110-1301 USA > + * > + */ > + > +#ifndef __AIC34B_DUMMY__ > +#define __AIC34B_DUMMY__ > + > +extern void aic34b_ear_enable(int enable); > +void aic34b_set_mic_bias(int bias); > +int aic34b_set_volume(u8 volume); > + > +#endif > diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c > new file mode 100644 > index 0000000..74bafb2 > --- /dev/null > +++ b/sound/soc/omap/rx51.c > @@ -0,0 +1,793 @@ > +/* > + * rx51.c -- SoC audio for Nokia RX51 > + * > + * Copyright (C) 2008 - 2009 Nokia Corporation > + * > + * Contact: Peter Ujfalusi <peter.ujfalusi@xxxxxxxxx> > + * Eduardo Valentin <eduardo.valentin@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA > + * 02110-1301 USA > + * > + */ > + > +#include <linux/clk.h> > +#include <linux/delay.h> > +#include <linux/platform_device.h> > +#include <sound/core.h> > +#include <sound/pcm.h> > +#include <sound/soc.h> > +#include <sound/soc-dapm.h> > +#include <sound/jack.h> > +#include <sound/tlv.h> > + > +#include <linux/i2c/twl4030.h> > +#include <asm/mach-types.h> > +#include <mach/hardware.h> > +#include <linux/gpio.h> > +#include <mach/mcbsp.h> > + > +#include "omap-mcbsp.h" > +#include "omap-pcm.h" > +#include "../codecs/tlv320aic3x.h" > +#include "../codecs/tpa6130a2.h" > +#include "aic34b_dummy.h" > + > +#define RX51_CODEC_RESET_GPIO 60 > +#define RX51_TVOUT_SEL_GPIO 40 > +#define RX51_ECI_SWITCH_1_GPIO 178 > +#define RX51_ECI_SWITCH_2_GPIO 182 > +/* REVISIT: TWL4030 GPIO base in RX51. Now statically defined to 192 */ > +#define RX51_SPEAKER_AMP_TWL_GPIO (192 + 7) > + > +enum { > + RX51_JACK_DISABLED, > + RX51_JACK_HP, /* headphone: stereo output, no mic */ > + RX51_JACK_HS, /* headset: stereo output with mic */ > + RX51_JACK_MIC, /* mic input only */ > + RX51_JACK_ECI, /* ECI headset */ > + RX51_JACK_TVOUT, /* stereo output with tv-out */ > +}; > + > +static int rx51_spk_func; > +static int rx51_jack_func; > +static int rx51_fmtx_func; > +static int rx51_dmic_func; > +static int rx51_ear_func; > +static struct snd_jack *rx51_jack; > + > +static DEFINE_MUTEX(eci_mutex); > +static int rx51_eci_mode = 1; > +static int rx51_dapm_jack_bias; > +static int tpa6130_enable; > +static int aic34b_volume; > + > +static void rx51_set_eci_switches(int mode) > +{ > + switch (mode) { > + case 0: /* Bias off */ > + case 1: /* Bias according to rx51_dapm_jack_bias */ > + case 4: /* Bias on */ > + /* Codec connected to mic/bias line */ > + gpio_set_value(RX51_ECI_SWITCH_1_GPIO, 0); > + gpio_set_value(RX51_ECI_SWITCH_2_GPIO, 1); > + break; > + case 2: > + /* ECI INT#2 detect connected to mic/bias line */ > + gpio_set_value(RX51_ECI_SWITCH_1_GPIO, 0); > + gpio_set_value(RX51_ECI_SWITCH_2_GPIO, 0); > + break; > + case 3: > + /* ECI RX/TX connected to mic/bias line */ > + gpio_set_value(RX51_ECI_SWITCH_1_GPIO, 1); > + gpio_set_value(RX51_ECI_SWITCH_2_GPIO, 0); > + break; > + } > +} > + > +static void rx51_set_jack_bias(void) > +{ > + int enable_bias = 0; > + > + mutex_lock(&eci_mutex); > + if ((rx51_eci_mode == 1 && rx51_dapm_jack_bias) || rx51_eci_mode == 4) > + enable_bias = 1; > + else if (rx51_eci_mode == 1 && rx51_jack_func == RX51_JACK_ECI) > + enable_bias = 1; > + mutex_unlock(&eci_mutex); > + if (enable_bias) > + aic34b_set_mic_bias(2); /* 2.5 V */ > + else > + aic34b_set_mic_bias(0); > +} > + > +static void rx51_set_jack_bias_handler(struct work_struct *unused) > +{ > + rx51_set_jack_bias(); > +} > +DECLARE_WORK(rx51_jack_bias_work, rx51_set_jack_bias_handler); > + > +static void rx51_ext_control(struct snd_soc_codec *codec) > +{ > + int hp = 0, mic = 0, tvout = 0; > + > + switch (rx51_jack_func) { > + case RX51_JACK_ECI: > + case RX51_JACK_HS: > + mic = 1; > + case RX51_JACK_HP: > + hp = 1; > + break; > + case RX51_JACK_MIC: > + mic = 1; > + break; > + case RX51_JACK_TVOUT: > + hp = 1; > + tvout = 1; > + break; > + } > + > + gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout); > + > + if (rx51_spk_func) > + snd_soc_dapm_enable_pin(codec, "Ext Spk"); > + else > + snd_soc_dapm_disable_pin(codec, "Ext Spk"); > + if (hp) > + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); > + else > + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); > + if (mic) > + snd_soc_dapm_enable_pin(codec, "Mic Jack"); > + else > + snd_soc_dapm_disable_pin(codec, "Mic Jack"); > + if (rx51_fmtx_func) > + snd_soc_dapm_enable_pin(codec, "FM Transmitter"); > + else > + snd_soc_dapm_disable_pin(codec, "FM Transmitter"); > + if (rx51_dmic_func) > + snd_soc_dapm_enable_pin(codec, "DMic"); > + else > + snd_soc_dapm_disable_pin(codec, "DMic"); > + if (rx51_ear_func) > + snd_soc_dapm_enable_pin(codec, "Earphone"); > + else > + snd_soc_dapm_disable_pin(codec, "Earphone"); > + > + snd_soc_dapm_sync(codec); > +} > + > +int rx51_set_eci_mode(int mode) > +{ > + if (mode < 0 || mode > 4) > + return -EINVAL; > + > + mutex_lock(&eci_mutex); > + if (rx51_eci_mode == mode) { > + mutex_unlock(&eci_mutex); > + return 0; > + } > + > + rx51_eci_mode = mode; > + rx51_set_eci_switches(rx51_eci_mode); > + mutex_unlock(&eci_mutex); > + > + rx51_set_jack_bias(); > + > + return 0; > +} > +EXPORT_SYMBOL(rx51_set_eci_mode); > + > +static ssize_t eci_mode_show(struct device *dev, struct device_attribute *attr, > + char *buf) > +{ > + return sprintf(buf, "%d\n", rx51_eci_mode); > +} > + > +static ssize_t eci_mode_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + int mode, retval; > + if (sscanf(buf, "%d", &mode) != 1) > + return -EINVAL; > + retval = rx51_set_eci_mode(mode); > + > + return (retval < 0) ? retval : count; > +} > + > +static DEVICE_ATTR(eci_mode, S_IRUGO | S_IWUSR, > + eci_mode_show, eci_mode_store); > + > +void rx51_jack_report(int status) > +{ > + snd_jack_report(rx51_jack, status); > +} > +EXPORT_SYMBOL(rx51_jack_report); > + > +static int rx51_startup(struct snd_pcm_substream *substream) > +{ > + struct snd_pcm_runtime *runtime = substream->runtime; > + struct snd_soc_pcm_runtime *rtd = substream->private_data; > + struct snd_soc_codec *codec = rtd->socdev->card->codec; > + > + snd_pcm_hw_constraint_minmax(runtime, > + SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); > + > + rx51_ext_control(codec); > + > + return 0; > +} > + > +static int rx51_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->dai->codec_dai; > + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; > + int err; > + > + /* Set codec DAI configuration */ > + err = snd_soc_dai_set_fmt(codec_dai, > + SND_SOC_DAIFMT_DSP_A | > + SND_SOC_DAIFMT_IB_NF | > + SND_SOC_DAIFMT_CBM_CFM); > + if (err < 0) > + return err; > + > + /* Set cpu DAI configuration */ > + err = snd_soc_dai_set_fmt(cpu_dai, > + SND_SOC_DAIFMT_DSP_A | > + SND_SOC_DAIFMT_IB_NF | > + SND_SOC_DAIFMT_CBM_CFM); > + if (err < 0) > + return err; > + > + /* Set the codec system clock for DAC and ADC */ > + return snd_soc_dai_set_sysclk(codec_dai, 0, 19200000, > + SND_SOC_CLOCK_IN); > +} > + > +static int rx51_bt_hw_params(struct snd_pcm_substream *substream, > + struct snd_pcm_hw_params *params) > +{ > + struct snd_soc_pcm_runtime *rtd = substream->private_data; > + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; > + > + /* Set cpu DAI configuration */ > + return cpu_dai->ops->set_fmt(cpu_dai, > + SND_SOC_DAIFMT_DSP_A | > + SND_SOC_DAIFMT_IB_NF | > + SND_SOC_DAIFMT_CBM_CFM); > +} > + > +static struct snd_soc_ops rx51_bt_ops = { > + .hw_params = rx51_bt_hw_params, > +}; > + > +static struct snd_soc_ops rx51_ops = { > + .startup = rx51_startup, > + .hw_params = rx51_hw_params, > +}; > + > +static int rx51_get_spk(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + ucontrol->value.integer.value[0] = rx51_spk_func; > + > + return 0; > +} > + > +static int rx51_set_spk(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); > + > + if (rx51_spk_func == ucontrol->value.integer.value[0]) > + return 0; > + > + rx51_spk_func = ucontrol->value.integer.value[0]; > + rx51_ext_control(codec); > + > + return 1; > +} > + > +static int rx51_spk_event(struct snd_soc_dapm_widget *w, > + struct snd_kcontrol *k, int event) > +{ > + if (SND_SOC_DAPM_EVENT_ON(event)) > + gpio_set_value(RX51_SPEAKER_AMP_TWL_GPIO, 1); > + else > + gpio_set_value(RX51_SPEAKER_AMP_TWL_GPIO, 0); > + > + return 0; > +} > + > +static int rx51_get_jack(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + ucontrol->value.integer.value[0] = rx51_jack_func; > + > + return 0; > +} > + > +static int rx51_set_jack(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); > + > + if (rx51_jack_func == ucontrol->value.integer.value[0]) > + return 0; > + > + rx51_jack_func = ucontrol->value.integer.value[0]; > + > + mutex_lock(&eci_mutex); > + if (rx51_jack_func == RX51_JACK_ECI) { > + /* Set ECI switches according to ECI mode */ > + rx51_set_eci_switches(rx51_eci_mode); > + schedule_work(&rx51_jack_bias_work); > + } else { > + /* > + * Let the codec always be connected to mic/bias line when > + * jack is in non-ECI function > + */ > + rx51_set_eci_switches(1); > + schedule_work(&rx51_jack_bias_work); > + } > + mutex_unlock(&eci_mutex); > + > + rx51_ext_control(codec); > + > + return 1; > +} > + > +static int rx51_jack_hp_event(struct snd_soc_dapm_widget *w, > + struct snd_kcontrol *k, int event) > +{ > + struct snd_soc_codec *codec = snd_kcontrol_chip(k); > + /* > + * Note: HP amp and fmtx must not be enabled at the same > + * time. We keep a shadow copy of the desired tpa_enable value but > + * keep the hpamp really disabled whenever fmtx is enabled. If > + * hpamp is requested on but fmtx is enabled, hpamp is kept > + * disabled and enabled later from rx51_set_fmtx function when > + * user disables fmtx. > + */ > + if (SND_SOC_DAPM_EVENT_ON(event)) { > + if (!rx51_fmtx_func) > + snd_soc_dapm_enable_pin(codec, "TPA6130A2 Headphone"); > + tpa6130_enable = 1; > + } else { > + tpa6130_enable = 1; > + snd_soc_dapm_disable_pin(codec, "TPA6130A2 Headphone"); > + tpa6130_enable = 0; > + } > + > + return 0; > +} > + > +static int rx51_jack_mic_event(struct snd_soc_dapm_widget *w, > + struct snd_kcontrol *k, int event) > +{ > + if (SND_SOC_DAPM_EVENT_ON(event)) > + rx51_dapm_jack_bias = 1; > + else > + rx51_dapm_jack_bias = 0; > + schedule_work(&rx51_jack_bias_work); > + > + return 0; > +} > + > +static int rx51_get_fmtx(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + ucontrol->value.integer.value[0] = rx51_fmtx_func; > + > + return 0; > +} > + > +static int rx51_set_fmtx(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); > + > + if (rx51_fmtx_func == ucontrol->value.integer.value[0]) > + return 0; > + > + rx51_fmtx_func = ucontrol->value.integer.value[0]; > + rx51_ext_control(codec); > + > + /* fmtx and tpa must not be enabled at the same time */ > + if (rx51_fmtx_func && tpa6130_enable) > + snd_soc_dapm_disable_pin(codec, "TPA6130A2 Headphone"); > + if (!rx51_fmtx_func && tpa6130_enable) > + snd_soc_dapm_enable_pin(codec, "TPA6130A2 Headphone"); > + > + return 1; > +} > + > +static int rx51_get_input(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + ucontrol->value.integer.value[0] = rx51_dmic_func; > + > + return 0; > +} > + > +static int rx51_set_input(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); > + > + if (rx51_dmic_func == ucontrol->value.integer.value[0]) > + return 0; > + > + rx51_dmic_func = ucontrol->value.integer.value[0]; > + rx51_ext_control(codec); > + > + return 1; > +} > + > +static int rx51_get_ear(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + ucontrol->value.integer.value[0] = rx51_ear_func; > + > + return 0; > +} > + > +static int rx51_set_ear(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); > + > + if (rx51_ear_func == ucontrol->value.integer.value[0]) > + return 0; > + > + rx51_ear_func = ucontrol->value.integer.value[0]; > + rx51_ext_control(codec); > + > + return 1; > +} > + > +static int rx51_ear_event(struct snd_soc_dapm_widget *w, > + struct snd_kcontrol *k, int event) > +{ > + if (SND_SOC_DAPM_EVENT_ON(event)) > + aic34b_ear_enable(1); > + else > + aic34b_ear_enable(0); > + > + return 0; > +} > + > +enum { > + RX51_EXT_API_AIC34B, > +}; > +#define SOC_RX51_EXT_SINGLE_TLV(xname, ext_api, max, tlv_array) \ > +{ \ > + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ > + .name = xname, \ > + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ > + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ > + .tlv.p = (tlv_array), \ > + .info = rx51_ext_info_volsw, \ > + .get = rx51_ext_get_volsw, \ > + .put = rx51_ext_put_volsw, \ > + .private_value = (ext_api) << 26 | (max) << 16, \ > +} > + > +static int rx51_ext_info_volsw(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_info *uinfo) > +{ > + int max = (kcontrol->private_value >> 16) & 0xff; > + > + if (max == 1) > + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; > + else > + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; > + > + uinfo->count = 1; > + uinfo->value.integer.min = 0; > + uinfo->value.integer.max = max; > + > + return 0; > +} > + > +static int rx51_ext_get_volsw(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + int ext_api = (kcontrol->private_value >> 26) & 0x0f; > + > + switch (ext_api) { > + case RX51_EXT_API_AIC34B: > + ucontrol->value.integer.value[0] = aic34b_volume; > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int rx51_ext_put_volsw(struct snd_kcontrol *kcontrol, > + struct snd_ctl_elem_value *ucontrol) > +{ > + int ext_api = (kcontrol->private_value >> 26) & 0x0f; > + int change = 0; > + > + switch (ext_api) { > + case RX51_EXT_API_AIC34B: > + change = (aic34b_volume != ucontrol->value.integer.value[0]); > + aic34b_volume = ucontrol->value.integer.value[0]; > + aic34b_set_volume(aic34b_volume); > + break; > + default: > + return -EINVAL; > + } > + > + return change; > +} > + > +static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { > + SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event), > + SND_SOC_DAPM_SPK("Headphone Jack", rx51_jack_hp_event), > + SND_SOC_DAPM_MIC("Mic Jack", rx51_jack_mic_event), > + SND_SOC_DAPM_OUTPUT("FM Transmitter"), > + SND_SOC_DAPM_MIC("DMic", NULL), > + SND_SOC_DAPM_SPK("Earphone", rx51_ear_event), > +}; > + > +static const struct snd_soc_dapm_route audio_map[] = { > + {"Ext Spk", NULL, "HPLOUT"}, > + {"Ext Spk", NULL, "HPROUT"}, > + > + {"TPA6130A2 Headphone", NULL, "LLOUT"}, > + {"TPA6130A2 Headphone", NULL, "RLOUT"}, > + {"LINE1L", NULL, "Mic Jack"}, > + > + {"FM Transmitter", NULL, "LLOUT"}, > + {"FM Transmitter", NULL, "RLOUT"}, > + > + {"Earphone", NULL, "MONO_LOUT"}, > + > + {"DMic Rate 64", NULL, "Mic Bias 2V"}, > + {"Mic Bias 2V", NULL, "DMic"}, > +}; > + > +static const char *spk_function[] = {"Off", "On"}; > +static const char *jack_function[] = {"Off", "Headphone", "Headset", > + "Mic", "ECI Headset", "TV-OUT"}; > +static const char *fmtx_function[] = {"Off", "On"}; > +static const char *input_function[] = {"ADC", "Digital Mic"}; > +static const char *ear_function[] = {"Off", "On"}; > + > +static const struct soc_enum rx51_enum[] = { > + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), > + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function), > + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(fmtx_function), fmtx_function), > + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), > + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ear_function), ear_function), > +}; > + > +/* > + * TLV320AIC3x output stage volumes. From -78.3 to 0 dB. Muted below -78.3 dB. > + * Step size is approximately 0.5 dB over most of the scale but increasing > + * near the very low levels. > + * Define dB scale so that it is mostly correct for range about -55 to 0 dB > + * but having increasing dB difference below that (and where it doesn't count > + * so much). This setting shows -50 dB (actual is -50.3 dB) for register > + * value 100 and -58.5 dB (actual is -78.3 dB) for register value 117. > + */ > +static DECLARE_TLV_DB_SCALE(aic3x_output_stage_tlv, -5900, 50, 1); > + > +static const struct snd_kcontrol_new aic34_rx51_controls[] = { > + SOC_ENUM_EXT("Speaker Function", rx51_enum[0], > + rx51_get_spk, rx51_set_spk), > + SOC_ENUM_EXT("Jack Function", rx51_enum[1], > + rx51_get_jack, rx51_set_jack), > + SOC_ENUM_EXT("FMTX Function", rx51_enum[2], > + rx51_get_fmtx, rx51_set_fmtx), > + SOC_ENUM_EXT("Input Select", rx51_enum[3], > + rx51_get_input, rx51_set_input), > + SOC_ENUM_EXT("Earphone Function", rx51_enum[4], > + rx51_get_ear, rx51_set_ear), > + SOC_RX51_EXT_SINGLE_TLV("Earphone Playback Volume", > + RX51_EXT_API_AIC34B, 118, > + aic3x_output_stage_tlv), > +}; > + > +static int rx51_aic34_init(struct snd_soc_codec *codec) > +{ > + int i, err; > + > + /* Add TPA6130A2 controls */ > + tpa6130a2_add_controls(codec); > + > + /* set up NC codec pins */ > + snd_soc_dapm_nc_pin(codec, "MIC3L"); > + snd_soc_dapm_nc_pin(codec, "MIC3R"); > + snd_soc_dapm_nc_pin(codec, "LINE1R"); > + > + /* Create jack for accessory reporting */ > + err = snd_jack_new(codec->card, "Jack", SND_JACK_MECHANICAL | > + SND_JACK_HEADSET | SND_JACK_AVOUT, &rx51_jack); > + if (err < 0) > + return err; > + > + /* Add RX51 specific controls */ > + for (i = 0; i < ARRAY_SIZE(aic34_rx51_controls); i++) { > + err = snd_ctl_add(codec->card, > + snd_soc_cnew(&aic34_rx51_controls[i], codec, NULL)); > + if (err < 0) > + return err; > + } > + > + /* Add RX51 specific widgets */ > + snd_soc_dapm_new_controls(codec, aic34_dapm_widgets, > + ARRAY_SIZE(aic34_dapm_widgets)); > + > + /* Set up RX51 specific audio path audio_map */ > + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); > + > + snd_soc_dapm_enable_pin(codec, "Earphone"); > + > + snd_soc_dapm_sync(codec); > + > + return 0; > +} > + > +/* Since all codec control is done by Bluetooth hardware > + only some constrains need to be set for it */ > +struct snd_soc_dai btcodec_dai = { > + .name = "Bluetooth codec", > + .playback = { > + .stream_name = "BT Playback", > + .channels_min = 1, > + .channels_max = 1, > + .rates = SNDRV_PCM_RATE_8000, > + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, > + .capture = { > + .stream_name = "BT Capture", > + .channels_min = 1, > + .channels_max = 1, > + .rates = SNDRV_PCM_RATE_8000, > + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, > +}; > + > +/* Digital audio interface glue - connects codec <--> CPU */ > +static struct snd_soc_dai_link rx51_dai[] = { > + { > + .name = "TLV320AIC34", > + .stream_name = "AIC34", > + .cpu_dai = &omap_mcbsp_dai[0], > + .codec_dai = &aic3x_dai, > + .init = rx51_aic34_init, > + .ops = &rx51_ops, > + }, { > + .name = "Bluetooth PCM", > + .stream_name = "Bluetooth", > + .cpu_dai = &omap_mcbsp_dai[1], > + .codec_dai = &btcodec_dai, > + .ops = &rx51_bt_ops, > + } > +}; > + > +/* Audio private data */ > +static struct aic3x_setup_data rx51_aic34_setup = { > + .gpio_func[0] = AIC3X_GPIO1_FUNC_DISABLED, > + .gpio_func[1] = AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT, > +}; > + > +/* Audio card */ > +static struct snd_soc_card rx51_sound_card = { > + .name = "RX51", > + .dai_link = rx51_dai, > + .num_links = ARRAY_SIZE(rx51_dai), > + .platform = &omap_soc_platform, > +}; > + > +/* Audio subsystem */ > +static struct snd_soc_device rx51_snd_devdata = { > + .card = &rx51_sound_card, > + .codec_dev = &soc_codec_dev_aic3x, > + .codec_data = &rx51_aic34_setup, > +}; > + > +static struct platform_device *rx51_snd_device; > + > +#define REMAP_OFFSET 2 > +#define DEDICATED_OFFSET 3 > +#define VMMC2_DEV_GRP 0x2B > +#define VMMC2_285V 0x0a > + These defines appear unused? > +static int __init rx51_soc_init(void) > +{ > + int err; > + struct device *dev; > + > + if (!machine_is_nokia_rx51()) > + return -ENODEV; > + > + if (gpio_request(RX51_CODEC_RESET_GPIO, NULL) < 0) > + BUG(); > + if (gpio_request(RX51_TVOUT_SEL_GPIO, "tvout_sel") < 0) > + BUG(); > + if (gpio_request(RX51_ECI_SWITCH_1_GPIO, "ECI switch 1") < 0) > + BUG(); > + if (gpio_request(RX51_ECI_SWITCH_2_GPIO, "ECI switch 2") < 0) > + BUG(); > + gpio_direction_output(RX51_CODEC_RESET_GPIO, 0); > + gpio_direction_output(RX51_TVOUT_SEL_GPIO, 0); > + gpio_direction_output(RX51_ECI_SWITCH_1_GPIO, 0); > + gpio_direction_output(RX51_ECI_SWITCH_2_GPIO, 1); > + > + gpio_set_value(RX51_CODEC_RESET_GPIO, 0); > + udelay(1); > + gpio_set_value(RX51_CODEC_RESET_GPIO, 1); > + msleep(1); > + > + if (gpio_request(RX51_SPEAKER_AMP_TWL_GPIO, NULL) < 0) > + BUG(); > + gpio_direction_output(RX51_SPEAKER_AMP_TWL_GPIO, 0); > + > + err = snd_soc_register_dai(&btcodec_dai); > + if (err) > + return err; > + > + rx51_snd_device = platform_device_alloc("soc-audio", -1); > + if (!rx51_snd_device) { > + err = -ENOMEM; > + goto err0; > + } > + > + platform_set_drvdata(rx51_snd_device, &rx51_snd_devdata); > + rx51_snd_devdata.dev = &rx51_snd_device->dev; > + err = platform_device_add(rx51_snd_device); > + if (err) > + goto err1; > + > + dev = &rx51_snd_device->dev; > + > + *(unsigned int *)rx51_dai[0].cpu_dai->private_data = 1; > + *(unsigned int *)rx51_dai[1].cpu_dai->private_data = 2; > + > + err = device_create_file(dev, &dev_attr_eci_mode); > + if (err) > + goto err2; > + > + return err; > +err2: > + platform_device_del(rx51_snd_device); > +err1: > + platform_device_put(rx51_snd_device); > +err0: > + snd_soc_unregister_dai(&btcodec_dai); > + > + return err; > + > +} > + > +static void __exit rx51_soc_exit(void) > +{ > + platform_device_unregister(rx51_snd_device); > + snd_soc_unregister_dai(&btcodec_dai); > +} > + > +module_init(rx51_soc_init); > +module_exit(rx51_soc_exit); > + > +MODULE_AUTHOR("Nokia Corporation"); > +MODULE_DESCRIPTION("ALSA SoC Nokia RX51"); > +MODULE_LICENSE("GPL"); > diff --git a/sound/soc/omap/rx51.h b/sound/soc/omap/rx51.h > new file mode 100644 > index 0000000..ee55260 > --- /dev/null > +++ b/sound/soc/omap/rx51.h > @@ -0,0 +1,29 @@ > +#ifndef _RX51_H_ > +#define _RX51_H_ > + > +/* > + * rx51.h - SoC audio for Nokia RX51 > + * > + * Copyright (C) 2008 - 2009 Nokia Corporation > + * > + * Contact: Peter Ujfalusi <peter.ujfalusi@xxxxxxxxx> > + * Eduardo Valentin <eduardo.valentin@xxxxxxxxx> > + * > + * 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; version 2 of the License. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +int rx51_set_eci_mode(int mode); > +void rx51_jack_report(int status); > + > +#endif /* _RX51_H_ */ > -- > 1.6.4.183.g04423 > -- To unsubscribe from this list: send the line "unsubscribe alsa-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel