On Thu, 2009-10-08 at 13:58 +0200, Valentin Eduardo (Nokia-D/Helsinki) wrote:
From: Eduardo Valentin eduardo.valentin@nokia.com
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@nokia.com
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_RX51select OMAP_MCBSPselect SND_OMAP_SOC_MCBSPselect SND_SOC_TLV320AIC3Xselect SND_SOC_TPA6130A2helpSay 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@nokia.com
Eduardo Valentin <eduardo.valentin@nokia.com>
- 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@nokia.com
Eduardo Valentin <eduardo.valentin@nokia.com>
- 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@nokia.com
Eduardo Valentin <eduardo.valentin@nokia.com>
- 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 */elseaic34b_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");elsesnd_soc_dapm_disable_pin(codec, "Ext Spk");if (hp)snd_soc_dapm_enable_pin(codec, "Headphone Jack");elsesnd_soc_dapm_disable_pin(codec, "Headphone Jack");if (mic)snd_soc_dapm_enable_pin(codec, "Mic Jack");elsesnd_soc_dapm_disable_pin(codec, "Mic Jack");if (rx51_fmtx_func)snd_soc_dapm_enable_pin(codec, "FM Transmitter");elsesnd_soc_dapm_disable_pin(codec, "FM Transmitter");if (rx51_dmic_func)snd_soc_dapm_enable_pin(codec, "DMic");elsesnd_soc_dapm_disable_pin(codec, "DMic");if (rx51_ear_func)snd_soc_dapm_enable_pin(codec, "Earphone");elsesnd_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);elsegpio_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;elserx51_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);elseaic34b_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;elseuinfo->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@nokia.com
Eduardo Valentin <eduardo.valentin@nokia.com>
- 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@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html