diff options
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/codecs/Kconfig | 3 | ||||
-rw-r--r-- | sound/soc/codecs/twl4030.c | 22 | ||||
-rw-r--r-- | sound/soc/codecs/twl6040.c | 733 | ||||
-rw-r--r-- | sound/soc/codecs/twl6040.h | 119 | ||||
-rw-r--r-- | sound/soc/imx/imx-pcm-dma-mx2.c | 4 | ||||
-rw-r--r-- | sound/soc/omap/sdp3430.c | 2 | ||||
-rw-r--r-- | sound/soc/omap/sdp4430.c | 52 | ||||
-rw-r--r-- | sound/soc/omap/zoom2.c | 2 |
8 files changed, 584 insertions, 353 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 922f59f9b82..98175a096df 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -236,11 +236,10 @@ config SND_SOC_TLV320DAC33 tristate config SND_SOC_TWL4030 - select MFD_TWL4030_AUDIO + select TWL4030_CODEC tristate config SND_SOC_TWL6040 - select TWL6040_CORE tristate config SND_SOC_UDA134X diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c index 71674bec960..bec788b1261 100644 --- a/sound/soc/codecs/twl4030.c +++ b/sound/soc/codecs/twl4030.c @@ -36,7 +36,7 @@ #include <sound/tlv.h> /* Register descriptions are here */ -#include <linux/mfd/twl4030-audio.h> +#include <linux/mfd/twl4030-codec.h> /* Shadow register used by the audio driver */ #define TWL4030_REG_SW_SHADOW 0x4A @@ -251,9 +251,9 @@ static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) return; if (enable) - mode = twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); + mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER); else - mode = twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); + mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER); if (mode >= 0) { twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode); @@ -297,7 +297,7 @@ static inline void twl4030_reset_registers(struct snd_soc_codec *codec) static void twl4030_init_chip(struct snd_soc_codec *codec) { - struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); + struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev); struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); u8 reg, byte; int i = 0; @@ -375,13 +375,13 @@ static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable) if (enable) { twl4030->apll_enabled++; if (twl4030->apll_enabled == 1) - status = twl4030_audio_enable_resource( - TWL4030_AUDIO_RES_APLL); + status = twl4030_codec_enable_resource( + TWL4030_CODEC_RES_APLL); } else { twl4030->apll_enabled--; if (!twl4030->apll_enabled) - status = twl4030_audio_disable_resource( - TWL4030_AUDIO_RES_APLL); + status = twl4030_codec_disable_resource( + TWL4030_CODEC_RES_APLL); } if (status >= 0) @@ -732,7 +732,7 @@ static int aif_event(struct snd_soc_dapm_widget *w, static void headset_ramp(struct snd_soc_codec *codec, int ramp) { - struct twl4030_codec_data *pdata = codec->dev->platform_data; + struct twl4030_codec_audio_data *pdata = codec->dev->platform_data; unsigned char hs_gain, hs_pop; struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); /* Base values for ramp delay calculation: 2^19 - 2^26 */ @@ -2260,7 +2260,7 @@ static int twl4030_soc_probe(struct snd_soc_codec *codec) } snd_soc_codec_set_drvdata(codec, twl4030); /* Set the defaults, and power up the codec */ - twl4030->sysclk = twl4030_audio_get_mclk() / 1000; + twl4030->sysclk = twl4030_codec_get_mclk() / 1000; codec->dapm.idle_bias_off = 1; twl4030_init_chip(codec); @@ -2297,7 +2297,7 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = { static int __devinit twl4030_codec_probe(struct platform_device *pdev) { - struct twl4030_codec_data *pdata = pdev->dev.platform_data; + struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "platform_data is missing\n"); diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c index 342c5a3c527..4c336636d4f 100644 --- a/sound/soc/codecs/twl6040.c +++ b/sound/soc/codecs/twl6040.c @@ -24,10 +24,11 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/i2c/twl.h> -#include <linux/mfd/twl6040.h> #include <sound/core.h> #include <sound/pcm.h> @@ -76,19 +77,14 @@ struct twl6040_jack_data { /* codec private data */ struct twl6040_data { - int plug_irq; + int audpwron; + int naudint; int codec_powered; int pll; int non_lp; - int pll_power_mode; - int hs_power_mode; - int hs_power_mode_locked; - unsigned int clk_in; unsigned int sysclk; - u16 hs_left_step; - u16 hs_right_step; - u16 hf_left_step; - u16 hf_right_step; + struct snd_pcm_hw_constraint_list *sysclk_constraints; + struct completion ready; struct twl6040_jack_data hs_jack; struct snd_soc_codec *codec; struct workqueue_struct *workqueue; @@ -210,32 +206,6 @@ static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = { TWL6040_REG_DLB, }; -/* set of rates for each pll: low-power and high-performance */ -static unsigned int lp_rates[] = { - 8000, - 11250, - 16000, - 22500, - 32000, - 44100, - 48000, - 88200, - 96000, -}; - -static unsigned int hp_rates[] = { - 8000, - 16000, - 32000, - 48000, - 96000, -}; - -static struct snd_pcm_hw_constraint_list sysclk_constraints[] = { - { .count = ARRAY_SIZE(lp_rates), .list = lp_rates, }, - { .count = ARRAY_SIZE(hp_rates), .list = hp_rates, }, -}; - /* * read twl6040 register cache */ @@ -269,13 +239,12 @@ static inline void twl6040_write_reg_cache(struct snd_soc_codec *codec, static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, unsigned int reg) { - struct twl6040 *twl6040 = codec->control_data; u8 value; if (reg >= TWL6040_CACHEREGNUM) return -EIO; - value = twl6040_reg_read(twl6040, reg); + twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &value, reg); twl6040_write_reg_cache(codec, reg, value); return value; @@ -287,13 +256,11 @@ static int twl6040_read_reg_volatile(struct snd_soc_codec *codec, static int twl6040_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { - struct twl6040 *twl6040 = codec->control_data; - if (reg >= TWL6040_CACHEREGNUM) return -EIO; twl6040_write_reg_cache(codec, reg, value); - return twl6040_reg_write(twl6040, reg, value); + return twl_i2c_write_u8(TWL_MODULE_AUDIO_VOICE, value, reg); } static void twl6040_init_vio_regs(struct snd_soc_codec *codec) @@ -301,21 +268,15 @@ static void twl6040_init_vio_regs(struct snd_soc_codec *codec) u8 *cache = codec->reg_cache; int reg, i; + /* allow registers to be accessed by i2c */ + twl6040_write(codec, TWL6040_REG_ACCCTL, cache[TWL6040_REG_ACCCTL]); + for (i = 0; i < TWL6040_VIOREGNUM; i++) { reg = twl6040_vio_reg[i]; - /* - * skip read-only registers (ASICID, ASICREV, STATUS) - * and registers shared among MFD children - */ + /* skip read-only registers (ASICID, ASICREV, STATUS) */ switch (reg) { case TWL6040_REG_ASICID: case TWL6040_REG_ASICREV: - case TWL6040_REG_INTID: - case TWL6040_REG_INTMR: - case TWL6040_REG_NCPCTL: - case TWL6040_REG_LDOCTL: - case TWL6040_REG_GPOCTL: - case TWL6040_REG_ACCCTL: case TWL6040_REG_STATUS: continue; default: @@ -332,20 +293,6 @@ static void twl6040_init_vdd_regs(struct snd_soc_codec *codec) for (i = 0; i < TWL6040_VDDREGNUM; i++) { reg = twl6040_vdd_reg[i]; - /* skip vibra and PLL registers */ - switch (reg) { - case TWL6040_REG_VIBCTLL: - case TWL6040_REG_VIBDATL: - case TWL6040_REG_VIBCTLR: - case TWL6040_REG_VIBDATR: - case TWL6040_REG_HPPLLCTL: - case TWL6040_REG_LPPLLCTL: - case TWL6040_REG_LPPLLDIV: - continue; - default: - break; - } - twl6040_write(codec, reg, cache[reg]); } } @@ -370,11 +317,7 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, if (headset->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < headset->left_vol) { - if (val + left_step > headset->left_vol) - val = headset->left_vol; - else - val += left_step; - + val += left_step; reg &= ~TWL6040_HSL_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, (reg | (~val & TWL6040_HSL_VOL_MASK))); @@ -384,11 +327,7 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, } else if (headset->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0x0) { - if ((int)val - (int)left_step < 0) - val = 0; - else - val -= left_step; - + val -= left_step; reg &= ~TWL6040_HSL_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, reg | (~val & TWL6040_HSL_VOL_MASK)); @@ -405,11 +344,7 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, if (headset->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < headset->right_vol) { - if (val + right_step > headset->right_vol) - val = headset->right_vol; - else - val += right_step; - + val += right_step; reg &= ~TWL6040_HSR_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, (reg | (~val << TWL6040_HSR_VOL_SHIFT))); @@ -419,11 +354,7 @@ static inline int twl6040_hs_ramp_step(struct snd_soc_codec *codec, } else if (headset->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0x0) { - if ((int)val - (int)right_step < 0) - val = 0; - else - val -= right_step; - + val -= right_step; reg &= ~TWL6040_HSR_VOL_MASK; twl6040_write(codec, TWL6040_REG_HSGAIN, reg | (~val << TWL6040_HSR_VOL_SHIFT)); @@ -454,11 +385,7 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, if (handsfree->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < handsfree->left_vol) { - if (val + left_step > handsfree->left_vol) - val = handsfree->left_vol; - else - val += left_step; - + val += left_step; reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFLGAIN, reg | (0x1D - val)); @@ -468,11 +395,7 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0) { - if ((int)val - (int)left_step < 0) - val = 0; - else - val -= left_step; - + val -= left_step; reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFLGAIN, reg | (0x1D - val)); @@ -489,11 +412,7 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, if (handsfree->ramp == TWL6040_RAMP_UP) { /* ramp step up */ if (val < handsfree->right_vol) { - if (val + right_step > handsfree->right_vol) - val = handsfree->right_vol; - else - val += right_step; - + val += right_step; reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFRGAIN, reg | (0x1D - val)); @@ -503,11 +422,7 @@ static inline int twl6040_hf_ramp_step(struct snd_soc_codec *codec, } else if (handsfree->ramp == TWL6040_RAMP_DOWN) { /* ramp step down */ if (val > 0) { - if ((int)val - (int)right_step < 0) - val = 0; - else - val -= right_step; - + val -= right_step; reg &= ~TWL6040_HF_VOL_MASK; twl6040_write(codec, TWL6040_REG_HFRGAIN, reg | (0x1D - val)); @@ -536,9 +451,11 @@ static void twl6040_pga_hs_work(struct work_struct *work) /* HS PGA volumes have 4 bits of resolution to ramp */ for (i = 0; i <= 16; i++) { - headset_complete = twl6040_hs_ramp_step(codec, - headset->left_step, - headset->right_step); + headset_complete = 1; + if (headset->ramp != TWL6040_RAMP_NONE) + headset_complete = twl6040_hs_ramp_step(codec, + headset->left_step, + headset->right_step); /* ramp finished ? */ if (headset_complete) @@ -579,9 +496,11 @@ static void twl6040_pga_hf_work(struct work_struct *work) /* HF PGA volumes have 5 bits of resolution to ramp */ for (i = 0; i <= 32; i++) { - handsfree_complete = twl6040_hf_ramp_step(codec, - handsfree->left_step, - handsfree->right_step); + handsfree_complete = 1; + if (handsfree->ramp != TWL6040_RAMP_NONE) + handsfree_complete = twl6040_hf_ramp_step(codec, + handsfree->left_step, + handsfree->right_step); /* ramp finished ? */ if (handsfree_complete) @@ -622,16 +541,12 @@ static int pga_event(struct snd_soc_dapm_widget *w, out = &priv->headset; work = &priv->hs_delayed_work; queue = priv->hs_workqueue; - out->left_step = priv->hs_left_step; - out->right_step = priv->hs_right_step; out->step_delay = 5; /* 5 ms between volume ramp steps */ break; case 4: out = &priv->handsfree; work = &priv->hf_delayed_work; queue = priv->hf_workqueue; - out->left_step = priv->hf_left_step; - out->right_step = priv->hf_right_step; out->step_delay = 5; /* 5 ms between volume ramp steps */ if (SND_SOC_DAPM_EVENT_ON(event)) priv->non_lp++; @@ -664,6 +579,8 @@ static int pga_event(struct snd_soc_dapm_widget *w, if (!delayed_work_pending(work)) { /* use volume ramp for power-down */ + out->left_step = 1; + out->right_step = 1; out->ramp = TWL6040_RAMP_DOWN; INIT_COMPLETION(out->ramp_done); @@ -679,6 +596,88 @@ static int pga_event(struct snd_soc_dapm_widget *w, return 0; } +/* twl6040 codec manual power-up sequence */ +static void twl6040_power_up(struct snd_soc_codec *codec) +{ + u8 ncpctl, ldoctl, lppllctl, accctl; + + ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); + ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); + lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); + accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); + + /* enable reference system */ + ldoctl |= TWL6040_REFENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + msleep(10); + /* enable internal oscillator */ + ldoctl |= TWL6040_OSCENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + udelay(10); + /* enable high-side ldo */ + ldoctl |= TWL6040_HSLDOENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + udelay(244); + /* enable negative charge pump */ + ncpctl |= TWL6040_NCPENA | TWL6040_NCPOPEN; + twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); + udelay(488); + /* enable low-side ldo */ + ldoctl |= TWL6040_LSLDOENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + udelay(244); + /* enable low-power pll */ + lppllctl |= TWL6040_LPLLENA; + twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + /* reset state machine */ + accctl |= TWL6040_RESETSPLIT; + twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); + mdelay(5); + accctl &= ~TWL6040_RESETSPLIT; + twl6040_write(codec, TWL6040_REG_ACCCTL, accctl); + /* disable internal oscillator */ + ldoctl &= ~TWL6040_OSCENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); +} + +/* twl6040 codec manual power-down sequence */ +static void twl6040_power_down(struct snd_soc_codec *codec) +{ + u8 ncpctl, ldoctl, lppllctl, accctl; + + ncpctl = twl6040_read_reg_cache(codec, TWL6040_REG_NCPCTL); + ldoctl = twl6040_read_reg_cache(codec, TWL6040_REG_LDOCTL); + lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); + accctl = twl6040_read_reg_cache(codec, TWL6040_REG_ACCCTL); + + /* enable internal oscillator */ + ldoctl |= TWL6040_OSCENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + udelay(10); + /* disable low-power pll */ + lppllctl &= ~TWL6040_LPLLENA; + twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + /* disable low-side ldo */ + ldoctl &= ~TWL6040_LSLDOENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + udelay(244); + /* disable negative charge pump */ + ncpctl &= ~(TWL6040_NCPENA | TWL6040_NCPOPEN); + twl6040_write(codec, TWL6040_REG_NCPCTL, ncpctl); + udelay(488); + /* disable high-side ldo */ + ldoctl &= ~TWL6040_HSLDOENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + udelay(244); + /* disable internal oscillator */ + ldoctl &= ~TWL6040_OSCENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + /* disable reference system */ + ldoctl &= ~TWL6040_REFENA; + twl6040_write(codec, TWL6040_REG_LDOCTL, ldoctl); + msleep(10); +} + /* set headset dac and driver power mode */ static int headset_power_mode(struct snd_soc_codec *codec, int high_perf) { @@ -714,26 +713,15 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int ret = 0; - if (SND_SOC_DAPM_EVENT_ON(event)) { + if (SND_SOC_DAPM_EVENT_ON(event)) priv->non_lp++; - if (!strcmp(w->name, "Earphone Driver")) { - /* Earphone doesn't support low power mode */ - priv->hs_power_mode_locked = 1; - ret = headset_power_mode(codec, 1); - } - } else { + else priv->non_lp--; - if (!strcmp(w->name, "Earphone Driver")) { - priv->hs_power_mode_locked = 0; - ret = headset_power_mode(codec, priv->hs_power_mode); - } - } msleep(1); - return ret; + return 0; } static void twl6040_hs_jack_report(struct snd_soc_codec *codec, @@ -778,19 +766,33 @@ static void twl6040_accessory_work(struct work_struct *work) } /* audio interrupt handler */ -static irqreturn_t twl6040_audio_handler(int irq, void *data) +static irqreturn_t twl6040_naudint_handler(int irq, void *data) { struct snd_soc_codec *codec = data; - struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); u8 intid; - intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID); + twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, TWL6040_REG_INTID); + + if (intid & TWL6040_THINT) + dev_alert(codec->dev, "die temp over-limit detection\n"); if ((intid & TWL6040_PLUGINT) || (intid & TWL6040_UNPLUGINT)) queue_delayed_work(priv->workqueue, &priv->delayed_work, msecs_to_jiffies(200)); + if (intid & TWL6040_HOOKINT) + dev_info(codec->dev, "hook detection\n"); + + if (intid & TWL6040_HFINT) + dev_alert(codec->dev, "hf drivers over current detection\n"); + + if (intid & TWL6040_VIBINT) + dev_alert(codec->dev, "vib drivers over current detection\n"); + + if (intid & TWL6040_READYINT) + complete(&priv->ready); + return IRQ_HANDLED; } @@ -1038,73 +1040,6 @@ static const struct snd_kcontrol_new hfr_mux_controls = static const struct snd_kcontrol_new ep_driver_switch_controls = SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0); -/* Headset power mode */ -static const char *twl6040_power_mode_texts[] = { - "Low-Power", "High-Perfomance", -}; - -static const struct soc_enum twl6040_power_mode_enum = - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_power_mode_texts), - twl6040_power_mode_texts); - -static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.enumerated.item[0] = priv->hs_power_mode; - - return 0; -} - -static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int high_perf = ucontrol->value.enumerated.item[0]; - int ret = 0; - - if (!priv->hs_power_mode_locked) - ret = headset_power_mode(codec, high_perf); - - if (!ret) - priv->hs_power_mode = high_perf; - - return ret; -} - -static int twl6040_pll_get_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.enumerated.item[0] = priv->pll_power_mode; - - return 0; -} - -static int twl6040_pll_put_enum(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - - priv->pll_power_mode = ucontrol->value.enumerated.item[0]; - - return 0; -} - -int twl6040_get_clk_id(struct snd_soc_codec *codec) -{ - struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - - return priv->pll_power_mode; -} -EXPORT_SYMBOL_GPL(twl6040_get_clk_id); - static const struct snd_kcontrol_new twl6040_snd_controls[] = { /* Capture gains */ SOC_DOUBLE_TLV("Capture Preamplifier Volume", @@ -1123,13 +1058,6 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = { TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv), SOC_SINGLE_TLV("Earphone Playback Volume", TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv), - - SOC_ENUM_EXT("Headset Power Mode", twl6040_power_mode_enum, - twl6040_headset_power_get_enum, - twl6040_headset_power_put_enum), - - SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum, - twl6040_pll_get_enum, twl6040_pll_put_enum), }; static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { @@ -1303,11 +1231,36 @@ static int twl6040_add_widgets(struct snd_soc_codec *codec) return 0; } +static int twl6040_power_up_completion(struct snd_soc_codec *codec, + int naudint) +{ + struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int time_left; + u8 intid; + + time_left = wait_for_completion_timeout(&priv->ready, + msecs_to_jiffies(144)); + + if (!time_left) { + twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &intid, + TWL6040_REG_INTID); + if (!(intid & TWL6040_READYINT)) { + dev_err(codec->dev, "timeout waiting for READYINT\n"); + return -ETIMEDOUT; + } + } + + priv->codec_powered = 1; + + return 0; +} + static int twl6040_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int audpwron = priv->audpwron; + int naudint = priv->naudint; int ret; switch (level) { @@ -1319,23 +1272,58 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, if (priv->codec_powered) break; - ret = twl6040_power(twl6040, 1); - if (ret) - return ret; + if (gpio_is_valid(audpwron)) { + /* use AUDPWRON line */ + gpio_set_value(audpwron, 1); - priv->codec_powered = 1; + /* wait for power-up completion */ + ret = twl6040_power_up_completion(codec, naudint); + if (ret) + return ret; + + /* sync registers updated during power-up sequence */ + twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); + twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); + twl6040_read_reg_volatile(codec, TWL6040_REG_LPPLLCTL); + } else { + /* use manual power-up sequence */ + twl6040_power_up(codec); + priv->codec_powered = 1; + } /* initialize vdd/vss registers with reg_cache */ twl6040_init_vdd_regs(codec); /* Set external boost GPO */ twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02); + + /* Set initial minimal gain values */ + twl6040_write(codec, TWL6040_REG_HSGAIN, 0xFF); + twl6040_write(codec, TWL6040_REG_EARCTL, 0x1E); + twl6040_write(codec, TWL6040_REG_HFLGAIN, 0x1D); + twl6040_write(codec, TWL6040_REG_HFRGAIN, 0x1D); break; case SND_SOC_BIAS_OFF: if (!priv->codec_powered) break; - twl6040_power(twl6040, 0); + if (gpio_is_valid(audpwron)) { + /* use AUDPWRON line */ + gpio_set_value(audpwron, 0); + + /* power-down sequence latency */ + udelay(500); + + /* sync registers updated during power-down sequence */ + twl6040_read_reg_volatile(codec, TWL6040_REG_NCPCTL); + twl6040_read_reg_volatile(codec, TWL6040_REG_LDOCTL); + twl6040_write_reg_cache(codec, TWL6040_REG_LPPLLCTL, + 0x00); + } else { + /* use manual power-down sequence */ + twl6040_power_down(codec); + } + priv->codec_powered = 0; break; } @@ -1345,6 +1333,27 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec, return 0; } +/* set of rates for each pll: low-power and high-performance */ + +static unsigned int lp_rates[] = { + 88200, + 96000, +}; + +static struct snd_pcm_hw_constraint_list lp_constraints = { + .count = ARRAY_SIZE(lp_rates), + .list = lp_rates, +}; + +static unsigned int hp_rates[] = { + 96000, +}; + +static struct snd_pcm_hw_constraint_list hp_constraints = { + .count = ARRAY_SIZE(hp_rates), + .list = hp_rates, +}; + static int twl6040_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -1354,7 +1363,7 @@ static int twl6040_startup(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &sysclk_constraints[priv->pll_power_mode]); + priv->sysclk_constraints); return 0; } @@ -1366,27 +1375,22 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + u8 lppllctl; int rate; + /* nothing to do for high-perf pll, it supports only 48 kHz */ + if (priv->pll == TWL6040_HPPLL_ID) + return 0; + + lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); + rate = params_rate(params); switch (rate) { case 11250: case 22500: case 44100: case 88200: - /* These rates are not supported when HPPLL is in use */ - if (unlikely(priv->pll == TWL6040_SYSCLK_SEL_HPPLL)) { - dev_err(codec->dev, "HPPLL does not support rate %d\n", - rate); - return -EINVAL; - } - /* Capture is not supported with 17.64MHz sysclk */ - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - dev_err(codec->dev, - "capture mode is not supported at %dHz\n", - rate); - return -EINVAL; - } + lppllctl |= TWL6040_LPLLFIN; priv->sysclk = 17640000; break; case 8000: @@ -1394,6 +1398,7 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, case 32000: case 48000: case 96000: + lppllctl &= ~TWL6040_LPLLFIN; priv->sysclk = 19200000; break; default: @@ -1401,6 +1406,8 @@ static int twl6040_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + return 0; } @@ -1409,9 +1416,7 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_codec *codec = rtd->codec; - struct twl6040 *twl6040 = codec->control_data; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); - int ret; if (!priv->sysclk) { dev_err(codec->dev, @@ -1419,19 +1424,24 @@ static int twl6040_prepare(struct snd_pcm_substream *substream, return -EINVAL; } + /* + * capture is not supported at 17.64 MHz, + * it's reserved for headset low-power playback scenario + */ + if ((priv->sysclk == 17640000) && + substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dev_err(codec->dev, + "capture mode is not supported at %dHz\n", + priv->sysclk); + return -EINVAL; + } + if ((priv->sysclk == 17640000) && priv->non_lp) { dev_err(codec->dev, "some enabled paths aren't supported at %dHz\n", priv->sysclk); return -EPERM; } - - ret = twl6040_set_pll(twl6040, priv->pll, priv->clk_in, priv->sysclk); - if (ret) { - dev_err(codec->dev, "Can not set PLL (%d)\n", ret); - return -EPERM; - } - return 0; } @@ -1440,12 +1450,99 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai, { struct snd_soc_codec *codec = codec_dai->codec; struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + u8 hppllctl, lppllctl; + + hppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_HPPLLCTL); + lppllctl = twl6040_read_reg_cache(codec, TWL6040_REG_LPPLLCTL); switch (clk_id) { case TWL6040_SYSCLK_SEL_LPPLL: + switch (freq) { + case 32768: + /* headset dac and driver must be in low-power mode */ + headset_power_mode(codec, 0); + + /* clk32k input requires low-power pll */ + lppllctl |= TWL6040_LPLLENA; + twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + mdelay(5); + lppllctl &= ~TWL6040_HPLLSEL; + twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + hppllctl &= ~TWL6040_HPLLENA; + twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); + break; + default: + dev_err(codec->dev, "unknown mclk freq %d\n", freq); + return -EINVAL; + } + + /* lppll divider */ + switch (priv->sysclk) { + case 17640000: + lppllctl |= TWL6040_LPLLFIN; + break; + case 19200000: + lppllctl &= ~TWL6040_LPLLFIN; + break; + default: + /* sysclk not yet configured */ + lppllctl &= ~TWL6040_LPLLFIN; + priv->sysclk = 19200000; + break; + } + + twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + + priv->pll = TWL6040_LPPLL_ID; + priv->sysclk_constraints = &lp_constraints; + break; case TWL6040_SYSCLK_SEL_HPPLL: - priv->pll = clk_id; - priv->clk_in = freq; + hppllctl &= ~TWL6040_MCLK_MSK; + + switch (freq) { + case 12000000: + /* mclk input, pll enabled */ + hppllctl |= TWL6040_MCLK_12000KHZ | + TWL6040_HPLLSQRBP | + TWL6040_HPLLENA; + break; + case 19200000: + /* mclk input, pll disabled */ + hppllctl |= TWL6040_MCLK_19200KHZ | + TWL6040_HPLLSQRENA | + TWL6040_HPLLBP; + break; + case 26000000: + /* mclk input, pll enabled */ + hppllctl |= TWL6040_MCLK_26000KHZ | + TWL6040_HPLLSQRBP | + TWL6040_HPLLENA; + break; + case 38400000: + /* clk slicer, pll disabled */ + hppllctl |= TWL6040_MCLK_38400KHZ | + TWL6040_HPLLSQRENA | + TWL6040_HPLLBP; + break; + default: + dev_err(codec->dev, "unknown mclk freq %d\n", freq); + return -EINVAL; + } + + /* headset dac and driver must be in high-performance mode */ + headset_power_mode(codec, 1); + + twl6040_write(codec, TWL6040_REG_HPPLLCTL, hppllctl); + udelay(500); + lppllctl |= TWL6040_HPLLSEL; + twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + lppllctl &= ~TWL6040_LPLLENA; + twl6040_write(codec, TWL6040_REG_LPPLLCTL, lppllctl); + + /* high-performance pll can provide only 19.2 MHz */ + priv->pll = TWL6040_HPPLL_ID; + priv->sysclk = 19200000; + priv->sysclk_constraints = &hp_constraints; break; default: dev_err(codec->dev, "unknown clk_id %d\n", clk_id); @@ -1462,27 +1559,15 @@ static struct snd_soc_dai_ops twl6040_dai_ops = { .set_sysclk = twl6040_set_dai_sysclk, }; -static struct snd_soc_dai_driver twl6040_dai[] = { -{ +static struct snd_soc_dai_driver twl6040_dai = { .name = "twl6040-hifi", .playback = { .stream_name = "Playback", .channels_min = 1, - .channels_max = 2, - .rates = TWL6040_RATES, - .formats = TWL6040_FORMATS, - }, - .capture = { - .stream_name = "Capture", - .channels_min = 1, - .channels_max = 2, + .channels_max = 4, .rates = TWL6040_RATES, .formats = TWL6040_FORMATS, }, - .ops = &twl6040_dai_ops, -}, -{ - .name = "twl6040-ul", .capture = { .stream_name = "Capture", .channels_min = 1, @@ -1491,40 +1576,6 @@ static struct snd_soc_dai_driver twl6040_dai[] = { .formats = TWL6040_FORMATS, }, .ops = &twl6040_dai_ops, -}, -{ - .name = "twl6040-dl1", - .playback = { - .stream_name = "Headset Playback", - .channels_min = 1, - .channels_max = 2, - .rates = TWL6040_RATES, - .formats = TWL6040_FORMATS, - }, - .ops = &twl6040_dai_ops, -}, -{ - .name = "twl6040-dl2", - .playback = { - .stream_name = "Handsfree Playback", - .channels_min = 1, - .channels_max = 2, - .rates = TWL6040_RATES, - .formats = TWL6040_FORMATS, - }, - .ops = &twl6040_dai_ops, -}, -{ - .name = "twl6040-vib", - .playback = { - .stream_name = "Vibra Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_CONTINUOUS, - .formats = TWL6040_FORMATS, - }, - .ops = &twl6040_dai_ops, -}, }; #ifdef CONFIG_PM @@ -1549,11 +1600,11 @@ static int twl6040_resume(struct snd_soc_codec *codec) static int twl6040_probe(struct snd_soc_codec *codec) { + struct twl4030_codec_data *twl_codec = codec->dev->platform_data; struct twl6040_data *priv; - struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev); - struct platform_device *pdev = container_of(codec->dev, - struct platform_device, dev); + int audpwron, naudint; int ret = 0; + u8 icrev, intmr = TWL6040_ALLINT_MSK; priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL); if (priv == NULL) @@ -1561,32 +1612,23 @@ static int twl6040_probe(struct snd_soc_codec *codec) snd_soc_codec_set_drvdata(codec, priv); priv->codec = codec; - codec->control_data = dev_get_drvdata(codec->dev->parent); - if (pdata && pdata->hs_left_step && pdata->hs_right_step) { - priv->hs_left_step = pdata->hs_left_step; - priv->hs_right_step = pdata->hs_right_step; - } else { - priv->hs_left_step = 1; - priv->hs_right_step = 1; - } + twl_i2c_read_u8(TWL_MODULE_AUDIO_VOICE, &icrev, TWL6040_REG_ASICREV); - if (pdata && pdata->hf_left_step && pdata->hf_right_step) { - priv->hf_left_step = pdata->hf_left_step; - priv->hf_right_step = pdata->hf_right_step; - } else { - priv->hf_left_step = 1; - priv->hf_right_step = 1; - } + if (twl_codec && (icrev > 0)) + audpwron = twl_codec->audpwron_gpio; + else + audpwron = -EINVAL; - priv->plug_irq = platform_get_irq(pdev, 0); - if (priv->plug_irq < 0) { - dev_err(codec->dev, "invalid irq\n"); - ret = -EINVAL; - goto work_err; - } + if (twl_codec) + naudint = twl_codec->naudint_irq; + else + naudint = 0; + priv->audpwron = audpwron; + priv->naudint = naudint; priv->workqueue = create_singlethread_workqueue("twl6040-codec"); + if (!priv->workqueue) { ret = -ENOMEM; goto work_err; @@ -1596,33 +1638,56 @@ static int twl6040_probe(struct snd_soc_codec *codec) mutex_init(&priv->mutex); + init_completion(&priv->ready); init_completion(&priv->headset.ramp_done); init_completion(&priv->handsfree.ramp_done); + if (gpio_is_valid(audpwron)) { + ret = gpio_request(audpwron, "audpwron"); + if (ret) + goto gpio1_err; + + ret = gpio_direction_output(audpwron, 0); + if (ret) + goto gpio2_err; + + priv->codec_powered = 0; + + /* enable only codec ready interrupt */ + intmr &= ~(TWL6040_READYMSK | TWL6040_PLUGMSK); + + /* reset interrupt status to allow correct power up sequence */ + twl6040_read_reg_volatile(codec, TWL6040_REG_INTID); + } + twl6040_write(codec, TWL6040_REG_INTMR, intmr); + + if (naudint) { + /* audio interrupt */ + ret = request_threaded_irq(naudint, NULL, + twl6040_naudint_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "twl6040_codec", codec); + if (ret) + goto gpio2_err; + } + + /* init vio registers */ + twl6040_init_vio_regs(codec); + priv->hf_workqueue = create_singlethread_workqueue("twl6040-hf"); if (priv->hf_workqueue == NULL) { ret = -ENOMEM; - goto hfwq_err; + goto irq_err; } priv->hs_workqueue = create_singlethread_workqueue("twl6040-hs"); if (priv->hs_workqueue == NULL) { ret = -ENOMEM; - goto hswq_err; + goto wq_err; } INIT_DELAYED_WORK(&priv->hs_delayed_work, twl6040_pga_hs_work); INIT_DELAYED_WORK(&priv->hf_delayed_work, twl6040_pga_hf_work); - ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler, - 0, "twl6040_irq_plug", codec); - if (ret) { - dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret); - goto plugirq_err; - } - - /* init vio registers */ - twl6040_init_vio_regs(codec); - /* power on device */ ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret) @@ -1635,12 +1700,16 @@ static int twl6040_probe(struct snd_soc_codec *codec) return 0; bias_err: - free_irq(priv->plug_irq, codec); -plugirq_err: destroy_workqueue(priv->hs_workqueue); -hswq_err: +wq_err: destroy_workqueue(priv->hf_workqueue); -hfwq_err: +irq_err: + if (naudint) + free_irq(naudint, codec); +gpio2_err: + if (gpio_is_valid(audpwron)) + gpio_free(audpwron); +gpio1_err: destroy_workqueue(priv->workqueue); work_err: kfree(priv); @@ -1650,9 +1719,17 @@ work_err: static int twl6040_remove(struct snd_soc_codec *codec) { struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec); + int audpwron = priv->audpwron; + int naudint = priv->naudint; twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF); - free_irq(priv->plug_irq, codec); + + if (gpio_is_valid(audpwron)) + gpio_free(audpwron); + + if (naudint) + free_irq(naudint, codec); + destroy_workqueue(priv->workqueue); destroy_workqueue(priv->hf_workqueue); destroy_workqueue(priv->hs_workqueue); @@ -1676,8 +1753,8 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = { static int __devinit twl6040_codec_probe(struct platform_device *pdev) { - return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl6040, - twl6040_dai, ARRAY_SIZE(twl6040_dai)); + return snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_twl6040, &twl6040_dai, 1); } static int __devexit twl6040_codec_remove(struct platform_device *pdev) diff --git a/sound/soc/codecs/twl6040.h b/sound/soc/codecs/twl6040.h index d8de67869dd..23aeed0963e 100644 --- a/sound/soc/codecs/twl6040.h +++ b/sound/soc/codecs/twl6040.h @@ -22,8 +22,125 @@ #ifndef __TWL6040_H__ #define __TWL6040_H__ +#define TWL6040_REG_ASICID 0x01 +#define TWL6040_REG_ASICREV 0x02 +#define TWL6040_REG_INTID 0x03 +#define TWL6040_REG_INTMR 0x04 +#define TWL6040_REG_NCPCTL 0x05 +#define TWL6040_REG_LDOCTL 0x06 +#define TWL6040_REG_HPPLLCTL 0x07 +#define TWL6040_REG_LPPLLCTL 0x08 +#define TWL6040_REG_LPPLLDIV 0x09 +#define TWL6040_REG_AMICBCTL 0x0A +#define TWL6040_REG_DMICBCTL 0x0B +#define TWL6040_REG_MICLCTL 0x0C +#define TWL6040_REG_MICRCTL 0x0D +#define TWL6040_REG_MICGAIN 0x0E +#define TWL6040_REG_LINEGAIN 0x0F +#define TWL6040_REG_HSLCTL 0x10 +#define TWL6040_REG_HSRCTL 0x11 +#define TWL6040_REG_HSGAIN 0x12 +#define TWL6040_REG_EARCTL 0x13 +#define TWL6040_REG_HFLCTL 0x14 +#define TWL6040_REG_HFLGAIN 0x15 +#define TWL6040_REG_HFRCTL 0x16 +#define TWL6040_REG_HFRGAIN 0x17 +#define TWL6040_REG_VIBCTLL 0x18 +#define TWL6040_REG_VIBDATL 0x19 +#define TWL6040_REG_VIBCTLR 0x1A +#define TWL6040_REG_VIBDATR 0x1B +#define TWL6040_REG_HKCTL1 0x1C +#define TWL6040_REG_HKCTL2 0x1D +#define TWL6040_REG_GPOCTL 0x1E +#define TWL6040_REG_ALB 0x1F +#define TWL6040_REG_DLB 0x20 +#define TWL6040_REG_TRIM1 0x28 +#define TWL6040_REG_TRIM2 0x29 +#define TWL6040_REG_TRIM3 0x2A +#define TWL6040_REG_HSOTRIM 0x2B +#define TWL6040_REG_HFOTRIM 0x2C +#define TWL6040_REG_ACCCTL 0x2D +#define TWL6040_REG_STATUS 0x2E + +#define TWL6040_CACHEREGNUM (TWL6040_REG_STATUS + 1) + +#define TWL6040_VIOREGNUM 18 +#define TWL6040_VDDREGNUM 21 + +/* INTID (0x03) fields */ + +#define TWL6040_THINT 0x01 +#define TWL6040_PLUGINT 0x02 +#define TWL6040_UNPLUGINT 0x04 +#define TWL6040_HOOKINT 0x08 +#define TWL6040_HFINT 0x10 +#define TWL6040_VIBINT 0x20 +#define TWL6040_READYINT 0x40 + +/* INTMR (0x04) fields */ + +#define TWL6040_PLUGMSK 0x02 +#define TWL6040_READYMSK 0x40 +#define TWL6040_ALLINT_MSK 0x7B + +/* NCPCTL (0x05) fields */ + +#define TWL6040_NCPENA 0x01 +#define TWL6040_NCPOPEN 0x40 + +/* LDOCTL (0x06) fields */ + +#define TWL6040_LSLDOENA 0x01 +#define TWL6040_HSLDOENA 0x04 +#define TWL6040_REFENA 0x40 +#define TWL6040_OSCENA 0x80 + +/* HPPLLCTL (0x07) fields */ + +#define TWL6040_HPLLENA 0x01 +#define TWL6040_HPLLRST 0x02 +#define TWL6040_HPLLBP 0x04 +#define TWL6040_HPLLSQRENA 0x08 +#define TWL6040_HPLLSQRBP 0x10 +#define TWL6040_MCLK_12000KHZ (0 << 5) +#define TWL6040_MCLK_19200KHZ (1 << 5) +#define TWL6040_MCLK_26000KHZ (2 << 5) +#define TWL6040_MCLK_38400KHZ (3 << 5) +#define TWL6040_MCLK_MSK 0x60 + +/* LPPLLCTL (0x08) fields */ + +#define TWL6040_LPLLENA 0x01 +#define TWL6040_LPLLRST 0x02 +#define TWL6040_LPLLSEL 0x04 +#define TWL6040_LPLLFIN 0x08 +#define TWL6040_HPLLSEL 0x10 + +/* HSLCTL (0x10) fields */ + +#define TWL6040_HSDACMODEL 0x02 +#define TWL6040_HSDRVMODEL 0x08 + +/* HSRCTL (0x11) fields */ + +#define TWL6040_HSDACMODER 0x02 +#define TWL6040_HSDRVMODER 0x08 + +/* ACCCTL (0x2D) fields */ + +#define TWL6040_RESETSPLIT 0x04 + +#define TWL6040_SYSCLK_SEL_LPPLL 1 +#define TWL6040_SYSCLK_SEL_HPPLL 2 + +#define TWL6040_HPPLL_ID 1 +#define TWL6040_LPPLL_ID 2 + +/* STATUS (0x2E) fields */ + +#define TWL6040_PLUGCOMP 0x02 + void twl6040_hs_jack_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int report); -int twl6040_get_clk_id(struct snd_soc_codec *codec); #endif /* End of __TWL6040_H__ */ diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c index 43fdc24f7e8..4173b3d87f9 100644 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ b/sound/soc/imx/imx-pcm-dma-mx2.c @@ -110,12 +110,12 @@ static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream, slave_config.direction = DMA_TO_DEVICE; slave_config.dst_addr = dma_params->dma_addr; slave_config.dst_addr_width = buswidth; - slave_config.dst_maxburst = dma_params->burstsize; + slave_config.dst_maxburst = dma_params->burstsize * buswidth; } else { slave_config.direction = DMA_FROM_DEVICE; slave_config.src_addr = dma_params->dma_addr; slave_config.src_addr_width = buswidth; - slave_config.src_maxburst = dma_params->burstsize; + slave_config.src_maxburst = dma_params->burstsize * buswidth; } ret = dmaengine_slave_config(iprtd->dma_chan, &slave_config); diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c index 9f6a758029d..3f72d17d1ef 100644 --- a/sound/soc/omap/sdp3430.c +++ b/sound/soc/omap/sdp3430.c @@ -36,7 +36,7 @@ #include <plat/mcbsp.h> /* Register descriptions for twl4030 codec part */ -#include <linux/mfd/twl4030-audio.h> +#include <linux/mfd/twl4030-codec.h> #include "omap-mcbsp.h" #include "omap-pcm.h" diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c index b80efb02bfc..189e0390063 100644 --- a/sound/soc/omap/sdp4430.c +++ b/sound/soc/omap/sdp4430.c @@ -21,8 +21,6 @@ #include <linux/clk.h> #include <linux/platform_device.h> -#include <linux/mfd/twl6040.h> - #include <sound/core.h> #include <sound/pcm.h> #include <sound/soc.h> @@ -36,6 +34,8 @@ #include "omap-pcm.h" #include "../codecs/twl6040.h" +static int twl6040_power_mode; + static int sdp4430_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -44,13 +44,13 @@ static int sdp4430_hw_params(struct snd_pcm_substream *substream, int clk_id, freq; int ret; - clk_id = twl6040_get_clk_id(rtd->codec); - if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) + if (twl6040_power_mode) { + clk_id = TWL6040_SYSCLK_SEL_HPPLL; freq = 38400000; - else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) + } else { + clk_id = TWL6040_SYSCLK_SEL_LPPLL; freq = 32768; - else - return -EINVAL; + } /* set the codec mclk */ ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq, @@ -81,6 +81,35 @@ static struct snd_soc_jack_pin hs_jack_pins[] = { }, }; +static int sdp4430_get_power_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = twl6040_power_mode; + return 0; +} + +static int sdp4430_set_power_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + if (twl6040_power_mode == ucontrol->value.integer.value[0]) + return 0; + + twl6040_power_mode = ucontrol->value.integer.value[0]; + + return 1; +} + +static const char *power_texts[] = {"Low-Power", "High-Performance"}; + +static const struct soc_enum sdp4430_enum[] = { + SOC_ENUM_SINGLE_EXT(2, power_texts), +}; + +static const struct snd_kcontrol_new sdp4430_controls[] = { + SOC_ENUM_EXT("TWL6040 Power Mode", sdp4430_enum[0], + sdp4430_get_power_mode, sdp4430_set_power_mode), +}; + /* SDP4430 machine DAPM */ static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = { SND_SOC_DAPM_MIC("Ext Mic", NULL), @@ -123,6 +152,12 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; int ret; + /* Add SDP4430 specific controls */ + ret = snd_soc_add_controls(codec, sdp4430_controls, + ARRAY_SIZE(sdp4430_controls)); + if (ret) + return ret; + /* Add SDP4430 specific widgets */ ret = snd_soc_dapm_new_controls(dapm, sdp4430_twl6040_dapm_widgets, ARRAY_SIZE(sdp4430_twl6040_dapm_widgets)); @@ -202,6 +237,9 @@ static int __init sdp4430_soc_init(void) if (ret) goto err; + /* Codec starts in HP mode */ + twl6040_power_mode = 1; + return 0; err: diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c index 9a2666ffc16..01709940a43 100644 --- a/sound/soc/omap/zoom2.c +++ b/sound/soc/omap/zoom2.c @@ -32,7 +32,7 @@ #include <plat/mcbsp.h> /* Register descriptions for twl4030 codec part */ -#include <linux/mfd/twl4030-audio.h> +#include <linux/mfd/twl4030-codec.h> #include "omap-mcbsp.h" #include "omap-pcm.h" |