Merge branch 'topic/asoc' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6 into for-2.6.35

This commit is contained in:
Mark Brown 2010-05-07 16:38:26 +01:00
commit 3057876498
11 changed files with 619 additions and 143 deletions

View File

@ -0,0 +1,17 @@
/*
* Platform data for Texas Instruments TLV320AIC3x codec
*
* Author: Jarkko Nikula <jhnikula@gmail.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.
*/
#ifndef __TLV320AIC3x_H__
#define __TLV320AIC3x_H__
struct aic3x_pdata {
int gpio_reset; /* < 0 if not used */
};
#endif

View File

@ -31,6 +31,7 @@ enum tpa_model {
struct tpa6130a2_platform_data {
enum tpa_model id;
int power_gpio;
int limit_gain;
};
#endif

View File

@ -38,6 +38,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <sound/core.h>
@ -47,6 +48,7 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/tlv320aic3x.h>
#include "tlv320aic3x.h"
@ -64,6 +66,7 @@ struct aic3x_priv {
struct regulator_bulk_data supplies[AIC3X_NUM_SUPPLIES];
unsigned int sysclk;
int master;
int gpio_reset;
};
/*
@ -1278,6 +1281,10 @@ static int aic3x_unregister(struct aic3x_priv *aic3x)
snd_soc_unregister_dai(&aic3x_dai);
snd_soc_unregister_codec(&aic3x->codec);
if (aic3x->gpio_reset >= 0) {
gpio_set_value(aic3x->gpio_reset, 0);
gpio_free(aic3x->gpio_reset);
}
regulator_bulk_disable(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
@ -1302,6 +1309,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
{
struct snd_soc_codec *codec;
struct aic3x_priv *aic3x;
struct aic3x_pdata *pdata = i2c->dev.platform_data;
int ret, i;
aic3x = kzalloc(sizeof(struct aic3x_priv), GFP_KERNEL);
@ -1318,6 +1326,15 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, aic3x);
aic3x->gpio_reset = -1;
if (pdata && pdata->gpio_reset >= 0) {
ret = gpio_request(pdata->gpio_reset, "tlv320aic3x reset");
if (ret != 0)
goto err_gpio;
aic3x->gpio_reset = pdata->gpio_reset;
gpio_direction_output(aic3x->gpio_reset, 0);
}
for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
aic3x->supplies[i].supply = aic3x_supply_names[i];
@ -1335,11 +1352,19 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
goto err_enable;
}
if (aic3x->gpio_reset >= 0) {
udelay(1);
gpio_set_value(aic3x->gpio_reset, 1);
}
return aic3x_register(codec);
err_enable:
regulator_bulk_free(ARRAY_SIZE(aic3x->supplies), aic3x->supplies);
err_get:
if (aic3x->gpio_reset >= 0)
gpio_free(aic3x->gpio_reset);
err_gpio:
kfree(aic3x);
return ret;
}

View File

@ -61,6 +61,8 @@
#define US_TO_SAMPLES(rate, us) \
(rate / (1000000 / us))
static void dac33_calculate_times(struct snd_pcm_substream *substream);
static int dac33_prepare_chip(struct snd_pcm_substream *substream);
static struct snd_soc_codec *tlv320dac33_codec;
@ -91,6 +93,7 @@ struct tlv320dac33_priv {
struct work_struct work;
struct snd_soc_codec codec;
struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
struct snd_pcm_substream *substream;
int power_gpio;
int chip_power;
int irq;
@ -284,45 +287,47 @@ static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
return ret;
}
static void dac33_restore_regs(struct snd_soc_codec *codec)
static void dac33_init_chip(struct snd_soc_codec *codec)
{
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
u8 *cache = codec->reg_cache;
u8 data[2];
int i, ret;
if (!dac33->chip_power)
if (unlikely(!dac33->chip_power))
return;
for (i = DAC33_PWR_CTRL; i <= DAC33_INTP_CTRL_B; i++) {
data[0] = i;
data[1] = cache[i];
/* Skip the read only registers */
if ((i >= DAC33_INT_OSC_STATUS &&
i <= DAC33_INT_OSC_FREQ_RAT_READ_B) ||
(i >= DAC33_FIFO_WPTR_MSB && i <= DAC33_FIFO_IRQ_FLAG) ||
i == DAC33_DAC_STATUS_FLAGS ||
i == DAC33_SRC_EST_REF_CLK_RATIO_A ||
i == DAC33_SRC_EST_REF_CLK_RATIO_B)
continue;
ret = codec->hw_write(codec->control_data, data, 2);
if (ret != 2)
dev_err(codec->dev, "Write failed (%d)\n", ret);
}
for (i = DAC33_LDAC_PWR_CTRL; i <= DAC33_LINEL_TO_LLO_VOL; i++) {
data[0] = i;
data[1] = cache[i];
ret = codec->hw_write(codec->control_data, data, 2);
if (ret != 2)
dev_err(codec->dev, "Write failed (%d)\n", ret);
}
for (i = DAC33_LINER_TO_RLO_VOL; i <= DAC33_OSC_TRIM; i++) {
data[0] = i;
data[1] = cache[i];
ret = codec->hw_write(codec->control_data, data, 2);
if (ret != 2)
dev_err(codec->dev, "Write failed (%d)\n", ret);
}
/* 44-46: DAC Control Registers */
/* A : DAC sample rate Fsref/1.5 */
dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(0));
/* B : DAC src=normal, not muted */
dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
DAC33_DACSRCL_LEFT);
/* C : (defaults) */
dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
/* 73 : volume soft stepping control,
clock source = internal osc (?) */
dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB);
/* Restore only selected registers (gains mostly) */
dac33_write(codec, DAC33_LDAC_DIG_VOL_CTRL,
dac33_read_reg_cache(codec, DAC33_LDAC_DIG_VOL_CTRL));
dac33_write(codec, DAC33_RDAC_DIG_VOL_CTRL,
dac33_read_reg_cache(codec, DAC33_RDAC_DIG_VOL_CTRL));
dac33_write(codec, DAC33_LINEL_TO_LLO_VOL,
dac33_read_reg_cache(codec, DAC33_LINEL_TO_LLO_VOL));
dac33_write(codec, DAC33_LINER_TO_RLO_VOL,
dac33_read_reg_cache(codec, DAC33_LINER_TO_RLO_VOL));
}
static inline void dac33_read_id(struct snd_soc_codec *codec)
{
u8 reg;
dac33_read(codec, DAC33_DEVICE_ID_MSB, &reg);
dac33_read(codec, DAC33_DEVICE_ID_LSB, &reg);
dac33_read(codec, DAC33_DEVICE_REV_ID, &reg);
}
static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
@ -341,9 +346,17 @@ static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
static int dac33_hard_power(struct snd_soc_codec *codec, int power)
{
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
int ret;
int ret = 0;
mutex_lock(&dac33->mutex);
/* Safety check */
if (unlikely(power == dac33->chip_power)) {
dev_warn(codec->dev, "Trying to set the same power state: %s\n",
power ? "ON" : "OFF");
goto exit;
}
if (power) {
ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
dac33->supplies);
@ -357,11 +370,6 @@ static int dac33_hard_power(struct snd_soc_codec *codec, int power)
gpio_set_value(dac33->power_gpio, 1);
dac33->chip_power = 1;
/* Restore registers */
dac33_restore_regs(codec);
dac33_soft_power(codec, 1);
} else {
dac33_soft_power(codec, 0);
if (dac33->power_gpio >= 0)
@ -383,6 +391,22 @@ exit:
return ret;
}
static int playback_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(w->codec);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (likely(dac33->substream)) {
dac33_calculate_times(dac33->substream);
dac33_prepare_chip(dac33->substream);
}
break;
}
return 0;
}
static int dac33_get_nsample(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@ -512,6 +536,8 @@ static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0),
SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power",
DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0),
SND_SOC_DAPM_PRE("Prepare Playback", playback_event),
};
static const struct snd_soc_dapm_route audio_map[] = {
@ -554,18 +580,18 @@ static int dac33_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Coming from OFF, switch on the codec */
ret = dac33_hard_power(codec, 1);
if (ret != 0)
return ret;
}
dac33_soft_power(codec, 0);
dac33_init_chip(codec);
}
break;
case SND_SOC_BIAS_OFF:
ret = dac33_hard_power(codec, 0);
if (ret != 0)
return ret;
break;
}
codec->bias_level = level;
@ -708,6 +734,31 @@ static void dac33_oscwait(struct snd_soc_codec *codec)
"internal oscillator calibration failed\n");
}
static int dac33_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
/* Stream started, save the substream pointer */
dac33->substream = substream;
return 0;
}
static void dac33_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
dac33->substream = NULL;
}
static int dac33_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@ -791,6 +842,16 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream)
}
mutex_lock(&dac33->mutex);
if (!dac33->chip_power) {
/*
* Chip is not powered yet.
* Do the init in the dac33_set_bias_level later.
*/
mutex_unlock(&dac33->mutex);
return 0;
}
dac33_soft_power(codec, 0);
dac33_soft_power(codec, 1);
@ -997,15 +1058,6 @@ static void dac33_calculate_times(struct snd_pcm_substream *substream)
}
static int dac33_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
dac33_calculate_times(substream);
dac33_prepare_chip(substream);
return 0;
}
static int dac33_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
@ -1269,35 +1321,6 @@ static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
static void dac33_init_chip(struct snd_soc_codec *codec)
{
/* 44-46: DAC Control Registers */
/* A : DAC sample rate Fsref/1.5 */
dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(0));
/* B : DAC src=normal, not muted */
dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
DAC33_DACSRCL_LEFT);
/* C : (defaults) */
dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
/* 64-65 : L&R DAC power control
Line In -> OUT 1V/V Gain, DAC -> OUT 4V/V Gain*/
dac33_write(codec, DAC33_LDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
dac33_write(codec, DAC33_RDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
/* 73 : volume soft stepping control,
clock source = internal osc (?) */
dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
/* 66 : LOP/LOM Modes */
dac33_write(codec, DAC33_OUT_AMP_CM_CTRL, 0xff);
/* 68 : LOM inverted from LOP */
dac33_write(codec, DAC33_OUT_AMP_CTRL, (3<<2));
dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB);
}
static int dac33_soc_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@ -1311,11 +1334,6 @@ static int dac33_soc_probe(struct platform_device *pdev)
socdev->card->codec = codec;
dac33 = snd_soc_codec_get_drvdata(codec);
/* Power up the codec */
dac33_hard_power(codec, 1);
/* Set default configuration */
dac33_init_chip(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
@ -1332,12 +1350,6 @@ static int dac33_soc_probe(struct platform_device *pdev)
dac33_add_widgets(codec);
/* power on device */
dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Bias level configuration has enabled regulator an extra time */
regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), dac33->supplies);
return 0;
pcm_err:
@ -1374,6 +1386,8 @@ static int dac33_soc_resume(struct platform_device *pdev)
struct snd_soc_codec *codec = socdev->card->codec;
dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
dac33_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
dac33_set_bias_level(codec, codec->suspend_bias_level);
return 0;
@ -1392,8 +1406,9 @@ EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320dac33);
#define DAC33_FORMATS SNDRV_PCM_FMTBIT_S16_LE
static struct snd_soc_dai_ops dac33_dai_ops = {
.startup = dac33_startup,
.shutdown = dac33_shutdown,
.hw_params = dac33_hw_params,
.prepare = dac33_pcm_prepare,
.trigger = dac33_pcm_trigger,
.delay = dac33_dai_delay,
.set_sysclk = dac33_set_dai_sysclk,
@ -1447,6 +1462,7 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client,
codec->hw_write = (hw_write_t) i2c_master_send;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = dac33_set_bias_level;
codec->idle_bias_off = 1;
codec->dai = &dac33_dai;
codec->num_dai = 1;
codec->reg_cache_size = ARRAY_SIZE(dac33_reg);
@ -1487,8 +1503,6 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client,
goto error_gpio;
}
gpio_direction_output(dac33->power_gpio, 0);
} else {
dac33->chip_power = 1;
}
/* Check if the IRQ number is valid and request it */
@ -1526,12 +1540,14 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client,
goto err_get;
}
ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
dac33->supplies);
/* Read the tlv320dac33 ID registers */
ret = dac33_hard_power(codec, 1);
if (ret != 0) {
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
goto err_enable;
dev_err(codec->dev, "Failed to power up codec: %d\n", ret);
goto error_codec;
}
dac33_read_id(codec);
dac33_hard_power(codec, 0);
ret = snd_soc_register_codec(codec);
if (ret != 0) {
@ -1546,14 +1562,9 @@ static int __devinit dac33_i2c_probe(struct i2c_client *client,
goto error_codec;
}
/* Shut down the codec for now */
dac33_hard_power(codec, 0);
return ret;
error_codec:
regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), dac33->supplies);
err_enable:
regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
err_get:
if (dac33->irq >= 0) {
@ -1577,7 +1588,9 @@ static int __devexit dac33_i2c_remove(struct i2c_client *client)
struct tlv320dac33_priv *dac33;
dac33 = i2c_get_clientdata(client);
dac33_hard_power(&dac33->codec, 0);
if (unlikely(dac33->chip_power))
dac33_hard_power(&dac33->codec, 0);
if (dac33->power_gpio >= 0)
gpio_free(dac33->power_gpio);

View File

@ -46,6 +46,9 @@ static const char *tpa6140a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
"AVdd",
};
#define TPA6130A2_GAIN_MAX 0x3f
#define TPA6140A2_GAIN_MAX 0x1f
/* This struct is used to save the context */
struct tpa6130a2_data {
struct mutex mutex;
@ -53,6 +56,8 @@ struct tpa6130a2_data {
struct regulator_bulk_data supplies[TPA6130A2_NUM_SUPPLIES];
int power_gpio;
unsigned char power_state;
enum tpa_model id;
int gain_limit;
};
static int tpa6130a2_i2c_read(int reg)
@ -175,6 +180,40 @@ exit:
return ret;
}
static int tpa6130a2_info_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tpa6130a2_data *data;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
mutex_lock(&data->mutex);
switch (mc->reg) {
case TPA6130A2_REG_VOL_MUTE:
if (data->gain_limit != mc->max)
mc->max = data->gain_limit;
break;
default:
dev_err(&tpa6130a2_client->dev,
"Invalid register: 0x02%x\n", mc->reg);
goto out;
}
if (unlikely(mc->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 = mc->max;
out:
mutex_unlock(&data->mutex);
return 0;
}
static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@ -238,6 +277,15 @@ static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
return 1;
}
#define SOC_SINGLE_EXT_TLV_TPA(xname, xreg, xshift, xmax, xinvert, 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 = tpa6130a2_info_volsw, \
.get = tpa6130a2_get_reg, .put = tpa6130a2_set_reg, \
.private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
/*
* TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
* down in gain.
@ -257,10 +305,22 @@ static const unsigned int tpa6130_tlv[] = {
};
static const struct snd_kcontrol_new tpa6130a2_controls[] = {
SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
tpa6130a2_get_reg, tpa6130a2_set_reg,
tpa6130_tlv),
SOC_SINGLE_EXT_TLV_TPA("TPA6130A2 Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 0, TPA6130A2_GAIN_MAX, 0,
tpa6130_tlv),
};
static const unsigned int tpa6140_tlv[] = {
TLV_DB_RANGE_HEAD(3),
0, 8, TLV_DB_SCALE_ITEM(-5900, 400, 0),
9, 16, TLV_DB_SCALE_ITEM(-2500, 200, 0),
17, 31, TLV_DB_SCALE_ITEM(-1000, 100, 0),
};
static const struct snd_kcontrol_new tpa6140a2_controls[] = {
SOC_SINGLE_EXT_TLV_TPA("TPA6140A2 Headphone Playback Volume",
TPA6130A2_REG_VOL_MUTE, 1, TPA6140A2_GAIN_MAX, 0,
tpa6140_tlv),
};
/*
@ -368,13 +428,22 @@ static const struct snd_soc_dapm_route audio_map[] = {
int tpa6130a2_add_controls(struct snd_soc_codec *codec)
{
struct tpa6130a2_data *data;
BUG_ON(tpa6130a2_client == NULL);
data = i2c_get_clientdata(tpa6130a2_client);
snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets,
ARRAY_SIZE(tpa6130a2_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
return snd_soc_add_controls(codec, tpa6130a2_controls,
ARRAY_SIZE(tpa6130a2_controls));
if (data->id == TPA6140A2)
return snd_soc_add_controls(codec, tpa6140a2_controls,
ARRAY_SIZE(tpa6140a2_controls));
else
return snd_soc_add_controls(codec, tpa6130a2_controls,
ARRAY_SIZE(tpa6130a2_controls));
}
EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
@ -407,6 +476,7 @@ static int __devinit tpa6130a2_probe(struct i2c_client *client,
pdata = client->dev.platform_data;
data->power_gpio = pdata->power_gpio;
data->id = pdata->id;
mutex_init(&data->mutex);
@ -425,20 +495,35 @@ static int __devinit tpa6130a2_probe(struct i2c_client *client,
gpio_direction_output(data->power_gpio, 0);
}
switch (pdata->id) {
switch (data->id) {
case TPA6130A2:
for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6130a2_supply_names[i];
if (pdata->limit_gain > 0 &&
pdata->limit_gain < TPA6130A2_GAIN_MAX)
data->gain_limit = pdata->limit_gain;
else
data->gain_limit = TPA6130A2_GAIN_MAX;
break;
case TPA6140A2:
for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6140a2_supply_names[i];;
if (pdata->limit_gain > 0 &&
pdata->limit_gain < TPA6140A2_GAIN_MAX)
data->gain_limit = pdata->limit_gain;
else
data->gain_limit = TPA6140A2_GAIN_MAX;
break;
default:
dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
pdata->id);
for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
data->supplies[i].supply = tpa6130a2_supply_names[i];
if (pdata->limit_gain > 0 &&
pdata->limit_gain < TPA6130A2_GAIN_MAX)
data->gain_limit = pdata->limit_gain;
else
data->gain_limit = TPA6130A2_GAIN_MAX;
}
ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),

View File

@ -123,6 +123,8 @@ struct twl4030_priv {
struct snd_soc_codec codec;
unsigned int codec_powered;
/* reference counts of AIF/APLL users */
unsigned int apll_enabled;
struct snd_pcm_substream *master_substream;
@ -259,22 +261,22 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
int status;
int status = -1;
if (enable == twl4030->apll_enabled)
return;
if (enable)
/* Enable PLL */
status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
else
/* Disable PLL */
status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
if (enable) {
twl4030->apll_enabled++;
if (twl4030->apll_enabled == 1)
status = twl4030_codec_enable_resource(
TWL4030_CODEC_RES_APLL);
} else {
twl4030->apll_enabled--;
if (!twl4030->apll_enabled)
status = twl4030_codec_disable_resource(
TWL4030_CODEC_RES_APLL);
}
if (status >= 0)
twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
twl4030->apll_enabled = enable;
}
static void twl4030_power_up(struct snd_soc_codec *codec)
@ -672,6 +674,31 @@ static int apll_event(struct snd_soc_dapm_widget *w,
return 0;
}
static int aif_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
u8 audio_if;
audio_if = twl4030_read_reg_cache(w->codec, TWL4030_REG_AUDIO_IF);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Enable AIF */
/* enable the PLL before we use it to clock the DAI */
twl4030_apll_enable(w->codec, 1);
twl4030_write(w->codec, TWL4030_REG_AUDIO_IF,
audio_if | TWL4030_AIF_EN);
break;
case SND_SOC_DAPM_POST_PMD:
/* disable the DAI before we stop it's source PLL */
twl4030_write(w->codec, TWL4030_REG_AUDIO_IF,
audio_if & ~TWL4030_AIF_EN);
twl4030_apll_enable(w->codec, 0);
break;
}
return 0;
}
static void headset_ramp(struct snd_soc_codec *codec, int ramp)
{
struct snd_soc_device *socdev = codec->socdev;
@ -1167,8 +1194,6 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("DIGIMIC1"),
/* Outputs */
SND_SOC_DAPM_OUTPUT("OUTL"),
SND_SOC_DAPM_OUTPUT("OUTR"),
SND_SOC_DAPM_OUTPUT("EARPIECE"),
SND_SOC_DAPM_OUTPUT("PREDRIVEL"),
SND_SOC_DAPM_OUTPUT("PREDRIVER"),
@ -1180,6 +1205,11 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("HFR"),
SND_SOC_DAPM_OUTPUT("VIBRA"),
/* AIF and APLL clocks for running DAIs (including loopback) */
SND_SOC_DAPM_OUTPUT("Virtual HiFi OUT"),
SND_SOC_DAPM_INPUT("Virtual HiFi IN"),
SND_SOC_DAPM_OUTPUT("Virtual Voice OUT"),
/* DACs */
SND_SOC_DAPM_DAC("DAC Right1", "Right Front HiFi Playback",
SND_SOC_NOPM, 0, 0),
@ -1243,7 +1273,8 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("AIF Enable", TWL4030_REG_AUDIO_IF, 0, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("AIF Enable", SND_SOC_NOPM, 0, 0, aif_event,
SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
/* Output MIXER controls */
/* Earpiece */
@ -1373,10 +1404,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Digital Voice Playback Mixer", NULL, "DAC Voice"},
/* Supply for the digital part (APLL) */
{"Digital R1 Playback Mixer", NULL, "APLL Enable"},
{"Digital L1 Playback Mixer", NULL, "APLL Enable"},
{"Digital R2 Playback Mixer", NULL, "APLL Enable"},
{"Digital L2 Playback Mixer", NULL, "APLL Enable"},
{"Digital Voice Playback Mixer", NULL, "APLL Enable"},
{"Digital R1 Playback Mixer", NULL, "AIF Enable"},
@ -1450,8 +1477,14 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Vibra Mux", "AudioR2", "DAC Right2"},
/* outputs */
{"OUTL", NULL, "Analog L2 Playback Mixer"},
{"OUTR", NULL, "Analog R2 Playback Mixer"},
/* Must be always connected (for AIF and APLL) */
{"Virtual HiFi OUT", NULL, "Digital L1 Playback Mixer"},
{"Virtual HiFi OUT", NULL, "Digital R1 Playback Mixer"},
{"Virtual HiFi OUT", NULL, "Digital L2 Playback Mixer"},
{"Virtual HiFi OUT", NULL, "Digital R2 Playback Mixer"},
/* Must be always connected (for APLL) */
{"Virtual Voice OUT", NULL, "Digital Voice Playback Mixer"},
/* Physical outputs */
{"EARPIECE", NULL, "Earpiece PGA"},
{"PREDRIVEL", NULL, "PredriveL PGA"},
{"PREDRIVER", NULL, "PredriveR PGA"},
@ -1465,6 +1498,12 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VIBRA", NULL, "Vibra Route"},
/* Capture path */
/* Must be always connected (for AIF and APLL) */
{"ADC Virtual Left1", NULL, "Virtual HiFi IN"},
{"ADC Virtual Right1", NULL, "Virtual HiFi IN"},
{"ADC Virtual Left2", NULL, "Virtual HiFi IN"},
{"ADC Virtual Right2", NULL, "Virtual HiFi IN"},
/* Physical inputs */
{"Analog Left", "Main Mic Capture Switch", "MAINMIC"},
{"Analog Left", "Headset Mic Capture Switch", "HSMIC"},
{"Analog Left", "AUXL Capture Switch", "AUXL"},
@ -1497,11 +1536,6 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
{"ADC Virtual Left1", NULL, "APLL Enable"},
{"ADC Virtual Right1", NULL, "APLL Enable"},
{"ADC Virtual Left2", NULL, "APLL Enable"},
{"ADC Virtual Right2", NULL, "APLL Enable"},
{"ADC Virtual Left1", NULL, "AIF Enable"},
{"ADC Virtual Right1", NULL, "AIF Enable"},
{"ADC Virtual Left2", NULL, "AIF Enable"},

View File

@ -18,6 +18,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 RX-51"
depends on SND_OMAP_SOC && MACH_NOKIA_RX51
select OMAP_MCBSP
select SND_OMAP_SOC_MCBSP
select SND_SOC_TLV320AIC3X
help
Say Y if you want to add support for SoC audio on Nokia RX-51
hardware. This is also known as Nokia N900 product.
config SND_OMAP_SOC_AMS_DELTA
tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
depends on SND_OMAP_SOC && MACH_AMS_DELTA

View File

@ -9,6 +9,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
# OMAP Machine Support
snd-soc-n810-objs := n810.o
snd-soc-rx51-objs := rx51.o
snd-soc-ams-delta-objs := ams-delta.o
snd-soc-osk5912-objs := osk5912.o
snd-soc-overo-objs := overo.o
@ -22,6 +23,7 @@ snd-soc-zoom2-objs := zoom2.o
snd-soc-igep0020-objs := igep0020.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.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

View File

@ -188,8 +188,6 @@ static int omap3pandora_out_init(struct snd_soc_codec *codec)
int ret;
/* All TWL4030 output pins are floating */
snd_soc_dapm_nc_pin(codec, "OUTL");
snd_soc_dapm_nc_pin(codec, "OUTR");
snd_soc_dapm_nc_pin(codec, "EARPIECE");
snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
snd_soc_dapm_nc_pin(codec, "PREDRIVER");

294
sound/soc/omap/rx51.c Normal file
View File

@ -0,0 +1,294 @@
/*
* rx51.c -- SoC audio for Nokia RX-51
*
* Copyright (C) 2008 - 2009 Nokia Corporation
*
* Contact: Peter Ujfalusi <peter.ujfalusi@nokia.com>
* Eduardo Valentin <eduardo.valentin@nokia.com>
* Jarkko Nikula <jhnikula@gmail.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/delay.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include "omap-mcbsp.h"
#include "omap-pcm.h"
#include "../codecs/tlv320aic3x.h"
/*
* REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This
* gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c
*/
#define RX51_SPEAKER_AMP_TWL_GPIO (192 + 7)
static int rx51_spk_func;
static int rx51_dmic_func;
static void rx51_ext_control(struct snd_soc_codec *codec)
{
if (rx51_spk_func)
snd_soc_dapm_enable_pin(codec, "Ext Spk");
else
snd_soc_dapm_disable_pin(codec, "Ext Spk");
if (rx51_dmic_func)
snd_soc_dapm_enable_pin(codec, "DMic");
else
snd_soc_dapm_disable_pin(codec, "DMic");
snd_soc_dapm_sync(codec);
}
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 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_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 const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
SND_SOC_DAPM_MIC("DMic", NULL),
};
static const struct snd_soc_dapm_route audio_map[] = {
{"Ext Spk", NULL, "HPLOUT"},
{"Ext Spk", NULL, "HPROUT"},
{"DMic Rate 64", NULL, "Mic Bias 2V"},
{"Mic Bias 2V", NULL, "DMic"},
};
static const char *spk_function[] = {"Off", "On"};
static const char *input_function[] = {"ADC", "Digital Mic"};
static const struct soc_enum rx51_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function),
};
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("Input Select", rx51_enum[1],
rx51_get_input, rx51_set_input),
};
static int rx51_aic34_init(struct snd_soc_codec *codec)
{
int err;
/* 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");
/* Add RX-51 specific controls */
err = snd_soc_add_controls(codec, aic34_rx51_controls,
ARRAY_SIZE(aic34_rx51_controls));
if (err < 0)
return err;
/* Add RX-51 specific widgets */
snd_soc_dapm_new_controls(codec, aic34_dapm_widgets,
ARRAY_SIZE(aic34_dapm_widgets));
/* Set up RX-51 specific audio path audio_map */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_sync(codec);
return 0;
}
/* 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,
},
};
/* 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 = "RX-51",
.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;
static int __init rx51_soc_init(void)
{
int err;
if (!machine_is_nokia_rx51())
return -ENODEV;
rx51_snd_device = platform_device_alloc("soc-audio", -1);
if (!rx51_snd_device) {
err = -ENOMEM;
goto err1;
}
platform_set_drvdata(rx51_snd_device, &rx51_snd_devdata);
rx51_snd_devdata.dev = &rx51_snd_device->dev;
*(unsigned int *)rx51_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
err = platform_device_add(rx51_snd_device);
if (err)
goto err2;
return 0;
err2:
platform_device_put(rx51_snd_device);
err1:
return err;
}
static void __exit rx51_soc_exit(void)
{
platform_device_unregister(rx51_snd_device);
}
module_init(rx51_soc_init);
module_exit(rx51_soc_exit);
MODULE_AUTHOR("Nokia Corporation");
MODULE_DESCRIPTION("ALSA SoC Nokia RX-51");
MODULE_LICENSE("GPL");

View File

@ -181,9 +181,6 @@ static int zoom2_twl4030_init(struct snd_soc_codec *codec)
snd_soc_dapm_nc_pin(codec, "CARKITMIC");
snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
snd_soc_dapm_nc_pin(codec, "OUTL");
snd_soc_dapm_nc_pin(codec, "OUTR");
snd_soc_dapm_nc_pin(codec, "EARPIECE");
snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
snd_soc_dapm_nc_pin(codec, "PREDRIVER");