Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c876ae3e authored by Takashi Iwai's avatar Takashi Iwai
Browse files

Merge branch 'for-2.6.36' of...

Merge branch 'for-2.6.36' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc-2.6 into topic/asoc
parents 67a3e12b a3a29b55
Loading
Loading
Loading
Loading
+166 −125
Original line number Diff line number Diff line
@@ -43,37 +43,37 @@
 */
static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
	0x00, /* this register not used		*/
	0x91, /* REG_CODEC_MODE		(0x1)	*/
	0xc3, /* REG_OPTION		(0x2)	*/
	0x00, /* REG_CODEC_MODE		(0x1)	*/
	0x00, /* REG_OPTION		(0x2)	*/
	0x00, /* REG_UNKNOWN		(0x3)	*/
	0x00, /* REG_MICBIAS_CTL	(0x4)	*/
	0x20, /* REG_ANAMICL		(0x5)	*/
	0x00, /* REG_ANAMICL		(0x5)	*/
	0x00, /* REG_ANAMICR		(0x6)	*/
	0x00, /* REG_AVADC_CTL		(0x7)	*/
	0x00, /* REG_ADCMICSEL		(0x8)	*/
	0x00, /* REG_DIGMIXING		(0x9)	*/
	0x0c, /* REG_ATXL1PGA		(0xA)	*/
	0x0c, /* REG_ATXR1PGA		(0xB)	*/
	0x00, /* REG_AVTXL2PGA		(0xC)	*/
	0x00, /* REG_AVTXR2PGA		(0xD)	*/
	0x0f, /* REG_ATXL1PGA		(0xA)	*/
	0x0f, /* REG_ATXR1PGA		(0xB)	*/
	0x0f, /* REG_AVTXL2PGA		(0xC)	*/
	0x0f, /* REG_AVTXR2PGA		(0xD)	*/
	0x00, /* REG_AUDIO_IF		(0xE)	*/
	0x00, /* REG_VOICE_IF		(0xF)	*/
	0x00, /* REG_ARXR1PGA		(0x10)	*/
	0x00, /* REG_ARXL1PGA		(0x11)	*/
	0x6c, /* REG_ARXR2PGA		(0x12)	*/
	0x6c, /* REG_ARXL2PGA		(0x13)	*/
	0x00, /* REG_VRXPGA		(0x14)	*/
	0x3f, /* REG_ARXR1PGA		(0x10)	*/
	0x3f, /* REG_ARXL1PGA		(0x11)	*/
	0x3f, /* REG_ARXR2PGA		(0x12)	*/
	0x3f, /* REG_ARXL2PGA		(0x13)	*/
	0x25, /* REG_VRXPGA		(0x14)	*/
	0x00, /* REG_VSTPGA		(0x15)	*/
	0x00, /* REG_VRX2ARXPGA		(0x16)	*/
	0x00, /* REG_AVDAC_CTL		(0x17)	*/
	0x00, /* REG_ARX2VTXPGA		(0x18)	*/
	0x00, /* REG_ARXL1_APGA_CTL	(0x19)	*/
	0x00, /* REG_ARXR1_APGA_CTL	(0x1A)	*/
	0x4a, /* REG_ARXL2_APGA_CTL	(0x1B)	*/
	0x4a, /* REG_ARXR2_APGA_CTL	(0x1C)	*/
	0x32, /* REG_ARXL1_APGA_CTL	(0x19)	*/
	0x32, /* REG_ARXR1_APGA_CTL	(0x1A)	*/
	0x32, /* REG_ARXL2_APGA_CTL	(0x1B)	*/
	0x32, /* REG_ARXR2_APGA_CTL	(0x1C)	*/
	0x00, /* REG_ATX2ARXPGA		(0x1D)	*/
	0x00, /* REG_BT_IF		(0x1E)	*/
	0x00, /* REG_BTPGA		(0x1F)	*/
	0x55, /* REG_BTPGA		(0x1F)	*/
	0x00, /* REG_BTSTPGA		(0x20)	*/
	0x00, /* REG_EAR_CTL		(0x21)	*/
	0x00, /* REG_HS_SEL		(0x22)	*/
@@ -85,32 +85,32 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
	0x00, /* REG_PRECKR_CTL		(0x28)	*/
	0x00, /* REG_HFL_CTL		(0x29)	*/
	0x00, /* REG_HFR_CTL		(0x2A)	*/
	0x00, /* REG_ALC_CTL		(0x2B)	*/
	0x05, /* REG_ALC_CTL		(0x2B)	*/
	0x00, /* REG_ALC_SET1		(0x2C)	*/
	0x00, /* REG_ALC_SET2		(0x2D)	*/
	0x00, /* REG_BOOST_CTL		(0x2E)	*/
	0x00, /* REG_SOFTVOL_CTL	(0x2F)	*/
	0x00, /* REG_DTMF_FREQSEL	(0x30)	*/
	0x13, /* REG_DTMF_FREQSEL	(0x30)	*/
	0x00, /* REG_DTMF_TONEXT1H	(0x31)	*/
	0x00, /* REG_DTMF_TONEXT1L	(0x32)	*/
	0x00, /* REG_DTMF_TONEXT2H	(0x33)	*/
	0x00, /* REG_DTMF_TONEXT2L	(0x34)	*/
	0x00, /* REG_DTMF_TONOFF	(0x35)	*/
	0x00, /* REG_DTMF_WANONOFF	(0x36)	*/
	0x79, /* REG_DTMF_TONOFF	(0x35)	*/
	0x11, /* REG_DTMF_WANONOFF	(0x36)	*/
	0x00, /* REG_I2S_RX_SCRAMBLE_H	(0x37)	*/
	0x00, /* REG_I2S_RX_SCRAMBLE_M	(0x38)	*/
	0x00, /* REG_I2S_RX_SCRAMBLE_L	(0x39)	*/
	0x06, /* REG_APLL_CTL		(0x3A)	*/
	0x00, /* REG_DTMF_CTL		(0x3B)	*/
	0x00, /* REG_DTMF_PGA_CTL2	(0x3C)	*/
	0x00, /* REG_DTMF_PGA_CTL1	(0x3D)	*/
	0x44, /* REG_DTMF_PGA_CTL2	(0x3C)	*/
	0x69, /* REG_DTMF_PGA_CTL1	(0x3D)	*/
	0x00, /* REG_MISC_SET_1		(0x3E)	*/
	0x00, /* REG_PCMBTMUX		(0x3F)	*/
	0x00, /* not used		(0x40)	*/
	0x00, /* not used		(0x41)	*/
	0x00, /* not used		(0x42)	*/
	0x00, /* REG_RX_PATH_SEL	(0x43)	*/
	0x00, /* REG_VDL_APGA_CTL	(0x44)	*/
	0x32, /* REG_VDL_APGA_CTL	(0x44)	*/
	0x00, /* REG_VIBRA_CTL		(0x45)	*/
	0x00, /* REG_VIBRA_SET		(0x46)	*/
	0x00, /* REG_VIBRA_PWM_SET	(0x47)	*/
@@ -244,58 +244,93 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
	udelay(10);
}

static void twl4030_init_chip(struct snd_soc_codec *codec)
static inline void twl4030_check_defaults(struct snd_soc_codec *codec)
{
	u8 *cache = codec->reg_cache;
	int i;
	int i, difference = 0;
	u8 val;

	/* clear CODECPDZ prior to setting register defaults */
	twl4030_codec_enable(codec, 0);
	dev_dbg(codec->dev, "Checking TWL audio default configuration\n");
	for (i = 1; i <= TWL4030_REG_MISC_SET_2; i++) {
		twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val, i);
		if (val != twl4030_reg[i]) {
			difference++;
			dev_dbg(codec->dev,
				 "Reg 0x%02x: chip: 0x%02x driver: 0x%02x\n",
				 i, val, twl4030_reg[i]);
		}
	}
	dev_dbg(codec->dev, "Found %d non maching registers. %s\n",
		 difference, difference ? "Not OK" : "OK");
}

static inline void twl4030_reset_registers(struct snd_soc_codec *codec)
{
	int i;

	/* set all audio section registers to reasonable defaults */
	for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
		if (i != TWL4030_REG_APLL_CTL)
			twl4030_write(codec, i,	cache[i]);
			twl4030_write(codec, i, twl4030_reg[i]);

}

static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
static void twl4030_init_chip(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct twl4030_setup_data *setup = socdev->codec_data;
	struct snd_soc_codec *codec = socdev->card->codec;
	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
	int status = -1;
	u8 reg, byte;
	int i = 0;

	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);
	}
	/* Check defaults, if instructed before anything else */
	if (setup && setup->check_defaults)
		twl4030_check_defaults(codec);

	if (status >= 0)
		twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
}
	/* Reset registers, if no setup data or if instructed to do so */
	if (!setup || (setup && setup->reset_registers))
		twl4030_reset_registers(codec);

static void twl4030_power_up(struct snd_soc_codec *codec)
{
	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
	u8 anamicl, regmisc1, byte;
	int i = 0;
	/* Refresh APLL_CTL register from HW */
	twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
			    TWL4030_REG_APLL_CTL);
	twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, byte);

	/* anti-pop when changing analog gain */
	reg = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
	twl4030_write(codec, TWL4030_REG_MISC_SET_1,
		reg | TWL4030_SMOOTH_ANAVOL_EN);

	twl4030_write(codec, TWL4030_REG_OPTION,
		TWL4030_ATXL1_EN | TWL4030_ATXR1_EN |
		TWL4030_ARXL2_EN | TWL4030_ARXR2_EN);

	if (twl4030->codec_powered)
	/* REG_ARXR2_APGA_CTL reset according to the TRM: 0dB, DA_EN */
	twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32);

	/* Machine dependent setup */
	if (!setup)
		return;

	/* set CODECPDZ to turn on codec */
	twl4030_codec_enable(codec, 1);
	/* Configuration for headset ramp delay from setup data */
	if (setup->sysclk != twl4030->sysclk)
		dev_warn(codec->dev,
				"Mismatch in APLL mclk: %u (configured: %u)\n",
				setup->sysclk, twl4030->sysclk);

	reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
	reg &= ~TWL4030_RAMP_DELAY;
	reg |= (setup->ramp_delay_value << 2);
	twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg);

	/* initiate offset cancellation */
	anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
	twl4030_codec_enable(codec, 1);

	reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
	reg &= ~TWL4030_OFFSET_CNCL_SEL;
	reg |= setup->offset_cncl_path;
	twl4030_write(codec, TWL4030_REG_ANAMICL,
		anamicl | TWL4030_CNCL_OFFSET_START);
		reg | TWL4030_CNCL_OFFSET_START);

	/* wait for offset cancellation to complete */
	do {
@@ -310,23 +345,28 @@ static void twl4030_power_up(struct snd_soc_codec *codec)
	/* Make sure that the reg_cache has the same value as the HW */
	twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);

	/* anti-pop when changing analog gain */
	regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
	twl4030_write(codec, TWL4030_REG_MISC_SET_1,
		regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);

	/* toggle CODECPDZ as per TRM */
	twl4030_codec_enable(codec, 0);
	twl4030_codec_enable(codec, 1);
}

/*
 * Unconditional power down
 */
static void twl4030_power_down(struct snd_soc_codec *codec)
static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
{
	/* power down */
	twl4030_codec_enable(codec, 0);
	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
	int status = -1;

	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);
}

/* Earpiece */
@@ -1605,10 +1645,10 @@ static int twl4030_set_bias_level(struct snd_soc_codec *codec,
		break;
	case SND_SOC_BIAS_STANDBY:
		if (codec->bias_level == SND_SOC_BIAS_OFF)
			twl4030_power_up(codec);
			twl4030_codec_enable(codec, 1);
		break;
	case SND_SOC_BIAS_OFF:
		twl4030_power_down(codec);
		twl4030_codec_enable(codec, 0);
		break;
	}
	codec->bias_level = level;
@@ -1794,13 +1834,6 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
		return -EINVAL;
	}

	if (mode != old_mode) {
		/* change rate and set CODECPDZ */
		twl4030_codec_enable(codec, 0);
		twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
		twl4030_codec_enable(codec, 1);
	}

	/* sample size */
	old_format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
	format = old_format;
@@ -1818,16 +1851,20 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
		return -EINVAL;
	}

	if (format != old_format) {

		/* clear CODECPDZ before changing format (codec requirement) */
	if (format != old_format || mode != old_mode) {
		if (twl4030->codec_powered) {
			/*
			 * If the codec is powered, than we need to toggle the
			 * codec power.
			 */
			twl4030_codec_enable(codec, 0);

		/* change format */
			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);

		/* set CODECPDZ afterwards */
			twl4030_codec_enable(codec, 1);
		} else {
			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
		}
	}

	/* Store the important parameters for the DAI configuration and set
@@ -1877,6 +1914,7 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
			     unsigned int fmt)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
	u8 old_format, format;

	/* get format */
@@ -1911,15 +1949,17 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
	}

	if (format != old_format) {

		/* clear CODECPDZ before changing format (codec requirement) */
		if (twl4030->codec_powered) {
			/*
			 * If the codec is powered, than we need to toggle the
			 * codec power.
			 */
			twl4030_codec_enable(codec, 0);

		/* change format */
			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);

		/* set CODECPDZ afterwards */
			twl4030_codec_enable(codec, 1);
		} else {
			twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
		}
	}

	return 0;
@@ -2011,6 +2051,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
	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 twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
	u8 old_mode, mode;

	/* Enable voice digital filters */
@@ -2035,10 +2076,17 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
	}

	if (mode != old_mode) {
		/* change rate and set CODECPDZ */
		if (twl4030->codec_powered) {
			/*
			 * If the codec is powered, than we need to toggle the
			 * codec power.
			 */
			twl4030_codec_enable(codec, 0);
			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
			twl4030_codec_enable(codec, 1);
		} else {
			twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
		}
	}

	return 0;
@@ -2068,6 +2116,7 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
		unsigned int fmt)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
	u8 old_format, format;

	/* get format */
@@ -2099,10 +2148,17 @@ static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
	}

	if (format != old_format) {
		/* change format and set CODECPDZ */
		if (twl4030->codec_powered) {
			/*
			 * If the codec is powered, than we need to toggle the
			 * codec power.
			 */
			twl4030_codec_enable(codec, 0);
			twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
			twl4030_codec_enable(codec, 1);
		} else {
			twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
		}
	}

	return 0;
@@ -2202,31 +2258,15 @@ static struct snd_soc_codec *twl4030_codec;
static int twl4030_soc_probe(struct platform_device *pdev)
{
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct twl4030_setup_data *setup = socdev->codec_data;
	struct snd_soc_codec *codec;
	struct twl4030_priv *twl4030;
	int ret;

	BUG_ON(!twl4030_codec);

	codec = twl4030_codec;
	twl4030 = snd_soc_codec_get_drvdata(codec);
	socdev->card->codec = codec;

	/* Configuration for headset ramp delay from setup data */
	if (setup) {
		unsigned char hs_pop;

		if (setup->sysclk != twl4030->sysclk)
			dev_warn(&pdev->dev,
				 "Mismatch in APLL mclk: %u (configured: %u)\n",
				 setup->sysclk, twl4030->sysclk);

		hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
		hs_pop &= ~TWL4030_RAMP_DELAY;
		hs_pop |= (setup->ramp_delay_value << 2);
		twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
	}
	twl4030_init_chip(pdev);

	/* register pcms */
	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -2247,6 +2287,8 @@ static int twl4030_soc_remove(struct platform_device *pdev)
	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
	struct snd_soc_codec *codec = socdev->card->codec;

	/* Reset registers to their chip default before leaving */
	twl4030_reset_registers(codec);
	twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
	snd_soc_free_pcms(socdev);
	snd_soc_dapm_free(socdev);
@@ -2287,6 +2329,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
	codec->read = twl4030_read_reg_cache;
	codec->write = twl4030_write;
	codec->set_bias_level = twl4030_set_bias_level;
	codec->idle_bias_off = 1;
	codec->dai = twl4030_dai;
	codec->num_dai = ARRAY_SIZE(twl4030_dai);
	codec->reg_cache_size = sizeof(twl4030_reg);
@@ -2302,9 +2345,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)

	/* Set the defaults, and power up the codec */
	twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
	twl4030_init_chip(codec);
	codec->bias_level = SND_SOC_BIAS_OFF;
	twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);

	ret = snd_soc_register_codec(codec);
	if (ret != 0) {
@@ -2322,7 +2363,7 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev)
	return 0;

error_codec:
	twl4030_power_down(codec);
	twl4030_codec_enable(codec, 0);
	kfree(codec->reg_cache);
error_cache:
	kfree(twl4030);
+3 −0
Original line number Diff line number Diff line
@@ -42,6 +42,9 @@ extern struct snd_soc_codec_device soc_codec_dev_twl4030;
struct twl4030_setup_data {
	unsigned int ramp_delay_value;
	unsigned int sysclk;
	unsigned int offset_cncl_path;
	unsigned int check_defaults:1;
	unsigned int reset_registers:1;
	unsigned int hs_extmute:1;
	void (*set_hs_extmute)(int mute);
};
+1 −1
Original line number Diff line number Diff line
@@ -928,7 +928,7 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
		case 19200000:
			/* mclk input, pll disabled */
			hppllctl |= TWL6040_MCLK_19200KHZ |
				    TWL6040_HPLLSQRBP |
				    TWL6040_HPLLSQRENA |
				    TWL6040_HPLLBP;
			break;
		case 26000000: