mirror of https://gitee.com/openkylin/linux.git
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:
commit
3057876498
|
@ -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
|
|
@ -31,6 +31,7 @@ enum tpa_model {
|
|||
struct tpa6130a2_platform_data {
|
||||
enum tpa_model id;
|
||||
int power_gpio;
|
||||
int limit_gain;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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, ®);
|
||||
dac33_read(codec, DAC33_DEVICE_ID_LSB, ®);
|
||||
dac33_read(codec, DAC33_DEVICE_REV_ID, ®);
|
||||
}
|
||||
|
||||
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 @@ static int dac33_hard_power(struct snd_soc_codec *codec, int power)
|
|||
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);
|
||||
|
|
|
@ -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 @@ static int tpa6130a2_power(int power)
|
|||
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),
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue