[alsa-devel] [PATCH 1/3] ASoC: rt5677: Remove the redundant definition in head file
The patch removes the redundant definition in head file
Signed-off-by: Oder Chiou oder_chiou@realtek.com --- sound/soc/codecs/rt5677.h | 7 ------- 1 file changed, 7 deletions(-)
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 08252e7..863393e 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1393,13 +1393,6 @@ #define RT5677_DSP_IB_9_L (0x1 << 1) #define RT5677_DSP_IB_9_L_SFT 1
-/* Debug String Length */ -#define RT5677_REG_DISP_LEN 23 - -#define RT5677_NO_JACK BIT(0) -#define RT5677_HEADSET_DET BIT(1) -#define RT5677_HEADPHO_DET BIT(2) - /* System Clock Source */ enum { RT5677_SCLK_S_MCLK,
The patch modifies the voltage level in the BIAS OFF stage
Signed-off-by: Oder Chiou oder_chiou@realtek.com --- sound/soc/codecs/rt5677.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 98370ae..67f1455 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -3124,7 +3124,7 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0); regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000); regmap_write(rt5677->regmap, RT5677_PWR_DIG2, 0x0000); - regmap_write(rt5677->regmap, RT5677_PWR_ANLG1, 0x0000); + regmap_write(rt5677->regmap, RT5677_PWR_ANLG1, 0x0022); regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 0x0000); regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000);
The ALC5677 has a programmable DSP, and there is a SPI that is dadicated for DSP firmware loading. Therefore, the patch includes a SPI driver for writing the DSP firmware.
Signed-off-by: Oder Chiou oder_chiou@realtek.com --- sound/soc/codecs/Makefile | 2 +- sound/soc/codecs/rt5677-spi.c | 151 ++++++++++++++++++++++++++++ sound/soc/codecs/rt5677-spi.h | 20 ++++ sound/soc/codecs/rt5677.c | 227 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5677.h | 6 ++ 5 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 sound/soc/codecs/rt5677-spi.c create mode 100644 sound/soc/codecs/rt5677-spi.h
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index be3377b..3534706 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -72,7 +72,7 @@ snd-soc-rt5631-objs := rt5631.o snd-soc-rt5640-objs := rt5640.o snd-soc-rt5645-objs := rt5645.o snd-soc-rt5651-objs := rt5651.o -snd-soc-rt5677-objs := rt5677.o +snd-soc-rt5677-objs := rt5677.o rt5677-spi.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o snd-soc-alc5632-objs := alc5632.o diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c new file mode 100644 index 0000000..6b98bf0 --- /dev/null +++ b/sound/soc/codecs/rt5677-spi.c @@ -0,0 +1,151 @@ +/* + * rt5677-spi.c -- RT5677 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Oder Chiou oder_chiou@realtek.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. + */ + +#include <linux/module.h> +#include <linux/input.h> +#include <linux/spi/spi.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_qos.h> +#include <linux/sysfs.h> +#include <linux/clk.h> +#include <linux/firmware.h> + +#include "rt5677-spi.h" + +static struct spi_device *g_spi; + +/** + * rt5677_spi_write - Write data to SPI. + * @txbuf: Data Buffer for writing. + * @len: Data length. + * + * + * Returns true for success. + */ +int rt5677_spi_write(u8 *txbuf, size_t len) +{ + static DEFINE_MUTEX(lock); + int status; + + mutex_lock(&lock); + + status = spi_write(g_spi, txbuf, len); + + mutex_unlock(&lock); + + if (status) + dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status); + + return status; +} + +/** + * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address. + * @addr: Start address. + * @txbuf: Data Buffer for writng. + * @len: Data length, it must be a multiple of 8. + * + * + * Returns true for success. + */ +int rt5677_spi_burst_write(u32 addr, const struct firmware *fw) +{ + u8 spi_cmd = 0x05; + u8 *write_buf; + unsigned int i, end, offset = 0; + + write_buf = kmalloc(8 * 30 + 6, GFP_KERNEL); + + if (write_buf == NULL) + return -ENOMEM; + + while (offset < fw->size) { + if (offset + SPI_BUF_LEN <= fw->size) + end = SPI_BUF_LEN; + else + end = fw->size % SPI_BUF_LEN; + + write_buf[0] = spi_cmd; + write_buf[1] = ((addr + offset) & 0xff000000) >> 24; + write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16; + write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8; + write_buf[4] = ((addr + offset) & 0x000000ff) >> 0; + + for (i = 0; i < end; i += 8) { + write_buf[i + 12] = fw->data[offset + i + 0]; + write_buf[i + 11] = fw->data[offset + i + 1]; + write_buf[i + 10] = fw->data[offset + i + 2]; + write_buf[i + 9] = fw->data[offset + i + 3]; + write_buf[i + 8] = fw->data[offset + i + 4]; + write_buf[i + 7] = fw->data[offset + i + 5]; + write_buf[i + 6] = fw->data[offset + i + 6]; + write_buf[i + 5] = fw->data[offset + i + 7]; + } + + write_buf[end + 5] = spi_cmd; + + rt5677_spi_write(write_buf, end + 6); + + offset += SPI_BUF_LEN; + } + + kfree(write_buf); + + return 0; +} + +static int rt5677_spi_probe(struct spi_device *spi) +{ + g_spi = spi; + return 0; +} + +static int rt5677_spi_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver rt5677_spi_driver = { + .driver = { + .name = "rt5677_spidev", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = rt5677_spi_probe, + .remove = rt5677_spi_remove, +}; + +static int __init rt5677_spi_init(void) +{ + return spi_register_driver(&rt5677_spi_driver); +} +module_init(rt5677_spi_init); + +static void __exit rt5677_spi_exit(void) +{ + spi_unregister_driver(&rt5677_spi_driver); +} +module_exit(rt5677_spi_exit); + +MODULE_DESCRIPTION("ASoC RT5677 SPI driver"); +MODULE_AUTHOR("Oder Chiou oder_chiou@realtek.com"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h new file mode 100644 index 0000000..0bfefae --- /dev/null +++ b/sound/soc/codecs/rt5677-spi.h @@ -0,0 +1,20 @@ +/* + * rt5677-spi.h -- RT5677 ALSA SoC audio codec driver + * + * Copyright 2013 Realtek Semiconductor Corp. + * Author: Oder Chiou oder_chiou@realtek.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 __RT5671_SPI_H__ +#define __RT5671_SPI_H__ + +#define SPI_BUF_LEN 240 + +int rt5677_spi_write(u8 *txbuf, size_t len); +int rt5677_spi_burst_write(u32 addr, const struct firmware *fw); + +#endif /* __RT5677_SPI_H__ */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 67f1455..a29be9a 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -19,6 +19,7 @@ #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/firmware.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -29,6 +30,7 @@
#include "rl6231.h" #include "rt5677.h" +#include "rt5677-spi.h"
#define RT5677_DEVICE_ID 0x6327
@@ -535,6 +537,134 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg) } }
+/** + * rt5677_dsp_mode_i2c_write - Write register on DSP mode. + * @codec: SoC audio codec device. + * @reg: Register index. + * @value: Register data. + * + * + * Returns 0 for success or negative error code. + */ +static int rt5677_dsp_mode_i2c_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_MSB, 0x1802); + if (ret < 0) { + dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_LSB, reg * 2); + if (ret < 0) { + dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_DATA_LSB, value); + if (ret < 0) { + dev_err(codec->dev, "Failed to set data lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_OP_CODE, 0x0001); + if (ret < 0) { + dev_err(codec->dev, "Failed to set op code value: %d\n", ret); + goto err; + } + +err: + mutex_unlock(&rt5677->dsp_cmd_lock); + + return ret; +} + +/** + * rt5677_dsp_mode_i2c_read - Read register on DSP mode. + * @codec: SoC audio codec device. + * @reg: Register index. + * + * + * Returns Register value or negative error code. + */ +static unsigned int rt5677_dsp_mode_i2c_read( + struct snd_soc_codec *codec, unsigned int reg) +{ + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + int ret; + + mutex_lock(&rt5677->dsp_cmd_lock); + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_MSB, 0x1802); + if (ret < 0) { + dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_ADDR_LSB, reg * 2); + if (ret < 0) { + dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret); + goto err; + } + + ret = regmap_write(rt5677->regmap, RT5677_DSP_I2C_OP_CODE, 0x0002); + if (ret < 0) { + dev_err(codec->dev, "Failed to set op code value: %d\n", ret); + goto err; + } + + regmap_read(rt5677->regmap, RT5677_DSP_I2C_DATA_LSB, &ret); + +err: + mutex_unlock(&rt5677->dsp_cmd_lock); + + return ret; +} + +/** + * rt5677_dsp_mode_i2c_update_bits - update register on DSP mode. + * @codec: audio codec + * @reg: register index. + * @mask: register mask + * @value: new value + * + * + * Returns 1 for change, 0 for no change, or negative error code. + */ +static int rt5677_dsp_mode_i2c_update_bits(struct snd_soc_codec *codec, + unsigned int reg, unsigned int mask, unsigned int value) +{ + unsigned int old, new; + int change, ret; + + ret = rt5677_dsp_mode_i2c_read(codec, reg); + if (ret < 0) { + dev_err(codec->dev, "Failed to read private reg: %d\n", ret); + goto err; + } + + old = ret; + new = (old & ~mask) | (value & mask); + change = old != new; + if (change) { + ret = rt5677_dsp_mode_i2c_write(codec, reg, new); + if (ret < 0) { + dev_err(codec->dev, + "Failed to write private reg: %d\n", ret); + goto err; + } + } + return change; + +err: + return ret; +} + static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); @@ -553,6 +683,69 @@ static unsigned int bst_tlv[] = { 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0), };
+static int rt5677_dsp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = rt5677->dsp_en; + + return 0; +} + +static int rt5677_dsp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] != 0 && rt5677->dsp_en == false) { + rt5677->dsp_en = true; + + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_LDO1_SEL_MASK, 0x0); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_LDO1, RT5677_PWR_LDO1); + regmap_write(rt5677->regmap, RT5677_GLB_CLK2, 0x0080); + regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff); + regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07ff); + + if (rt5677->fw1) + rt5677_spi_burst_write(0x50000000, rt5677->fw1); + + if (rt5677->fw2) + rt5677_spi_burst_write(0x60000000, rt5677->fw2); + + regcache_cache_bypass(rt5677->regmap, true); + rt5677_dsp_mode_i2c_update_bits(codec, RT5677_PWR_DSP1, 0x1, + 0x0); + rt5677_dsp_mode_i2c_write(codec, RT5677_PRIV_INDEX, 0x003e); + rt5677_dsp_mode_i2c_write(codec, RT5677_PRIV_DATA, 0x2208); + regcache_cache_bypass(rt5677->regmap, false); + } else if (ucontrol->value.integer.value[0] == 0 && + rt5677->dsp_en == true) { + rt5677->dsp_en = false; + + regmap_write(rt5677->regmap, RT5677_PR_BASE + 0x3e, 0x2008); + regcache_cache_bypass(rt5677->regmap, true); + rt5677_dsp_mode_i2c_update_bits(codec, RT5677_PWR_DSP1, 0x1, + 0x1); + rt5677_dsp_mode_i2c_write(codec, RT5677_PWR_DSP1, 0x0001); + regcache_cache_bypass(rt5677->regmap, false); + + regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001); + regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00); + regmap_write(rt5677->regmap, RT5677_GLB_CLK2, 0x0000); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, + RT5677_PWR_LDO1, 0); + regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1, + RT5677_LDO1_SEL_MASK, 0x1); + } + + return 0; +} + static const struct snd_kcontrol_new rt5677_snd_controls[] = { /* OUTPUT Control */ SOC_SINGLE("OUT1 Playback Switch", RT5677_LOUT1, @@ -620,6 +813,9 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = { SOC_DOUBLE_TLV("Mono ADC Boost Volume", RT5677_ADC_BST_CTRL2, RT5677_MONO_ADC_L_BST_SFT, RT5677_MONO_ADC_R_BST_SFT, 3, 0, adc_bst_tlv), + + SOC_SINGLE_EXT("DSP Switch", 0, 0, 1, 0, rt5677_dsp_get, + rt5677_dsp_put), };
/** @@ -3138,6 +3334,24 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec, return 0; }
+static void rt5677_fw1_loaded(const struct firmware *fw, void *context) +{ + struct snd_soc_codec *codec = context; + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + + if (fw) + rt5677->fw1 = fw; +} + +static void rt5677_fw2_loaded(const struct firmware *fw, void *context) +{ + struct snd_soc_codec *codec = context; + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + + if (fw) + rt5677->fw2 = fw; +} + static int rt5677_probe(struct snd_soc_codec *codec) { struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); @@ -3149,6 +3363,16 @@ static int rt5677_probe(struct snd_soc_codec *codec) regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020); regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
+ mutex_init(&rt5677->dsp_cmd_lock); + + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + RT5677_FIRMWARE1, codec->dev, GFP_KERNEL, codec, + rt5677_fw1_loaded); + + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + RT5677_FIRMWARE2, codec->dev, GFP_KERNEL, codec, + rt5677_fw2_loaded); + return 0; }
@@ -3158,6 +3382,9 @@ static int rt5677_remove(struct snd_soc_codec *codec)
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+ release_firmware(rt5677->fw1); + release_firmware(rt5677->fw2); + return 0; }
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index 863393e..32cbbf0 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1393,6 +1393,9 @@ #define RT5677_DSP_IB_9_L (0x1 << 1) #define RT5677_DSP_IB_9_L_SFT 1
+#define RT5677_FIRMWARE1 "rt5677_dsp_fw1.bin" +#define RT5677_FIRMWARE2 "rt5677_dsp_fw2.bin" + /* System Clock Source */ enum { RT5677_SCLK_S_MCLK, @@ -1422,6 +1425,8 @@ struct rt5677_priv { struct snd_soc_codec *codec; struct rt5677_platform_data pdata; struct regmap *regmap; + const struct firmware *fw1, *fw2; + struct mutex dsp_cmd_lock;
int sysclk; int sysclk_src; @@ -1431,6 +1436,7 @@ struct rt5677_priv { int pll_src; int pll_in; int pll_out; + bool dsp_en; };
#endif /* __RT5677_H__ */
On Mon, Jul 07, 2014 at 03:37:01PM +0800, Oder Chiou wrote:
+int rt5677_spi_write(u8 *txbuf, size_t len) +{
- static DEFINE_MUTEX(lock);
- int status;
- mutex_lock(&lock);
- status = spi_write(g_spi, txbuf, len);
- mutex_unlock(&lock);
Since this lock is local to the function it doesn't do anything, the SPI framework has its own locking which makes it reentrant.
+int rt5677_spi_burst_write(u32 addr, const struct firmware *fw) +{
- u8 spi_cmd = 0x05;
- u8 *write_buf;
- unsigned int i, end, offset = 0;
- write_buf = kmalloc(8 * 30 + 6, GFP_KERNEL);
Magic numbers! You also need to be aware that memory allocated by kmalloc() may not be contigous and while things using the SPI core DMA handling ought to be able to cope not everything will be.
+static int rt5677_spi_probe(struct spi_device *spi) +{
- g_spi = spi;
- return 0;
+}
+static int rt5677_spi_remove(struct spi_device *spi) +{
- return 0;
+}
Delete empty functions.
+static int __init rt5677_spi_init(void) +{
- return spi_register_driver(&rt5677_spi_driver);
+} +module_init(rt5677_spi_init);
module_spi_driver()
+static int rt5677_dsp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
+{
- struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
- if (ucontrol->value.integer.value[0] != 0 && rt5677->dsp_en == false) {
rt5677->dsp_en = true;
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK, 0x0);
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
RT5677_PWR_LDO1, RT5677_PWR_LDO1);
regmap_write(rt5677->regmap, RT5677_GLB_CLK2, 0x0080);
regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07ff);
This doesn't seem joined up with the rest of the CODEC power management which suggests there's going to be problems - for example LDO1_SEL_MASK is also being managed by the existing set_bias_level(). What other devices do is manage the power for the DSP using a DAPM widget, enabling it when there's audio flowing through it.
- mutex_init(&rt5677->dsp_cmd_lock);
- request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
RT5677_FIRMWARE1, codec->dev, GFP_KERNEL, codec,
rt5677_fw1_loaded);
- request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
RT5677_FIRMWARE2, codec->dev, GFP_KERNEL, codec,
rt5677_fw2_loaded);
It would be nicer to be able to only request firmware when needed rather than loading it once a boot time, that way we use a little less memory and we'll always have the latest firmware rather than what happened to be there when the system is booting.
participants (2)
-
Mark Brown
-
Oder Chiou