From patchwork Mon Jul 7 07:37:01 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oder Chiou X-Patchwork-Id: 4493201 Return-Path: X-Original-To: patchwork-alsa-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 1C8BABEEAA for ; Mon, 7 Jul 2014 07:38:12 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A27F22027D for ; Mon, 7 Jul 2014 07:38:10 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id CBF0D20272 for ; Mon, 7 Jul 2014 07:38:08 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id E11032655DA; Mon, 7 Jul 2014 09:38:07 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,NO_DNS_FOR_FROM, UNPARSEABLE_RELAY autolearn=no version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id CEB692654C7; Mon, 7 Jul 2014 09:37:29 +0200 (CEST) X-Original-To: alsa-devel@alsa-project.org Delivered-To: alsa-devel@alsa-project.org Received: by alsa0.perex.cz (Postfix, from userid 1000) id 1ABB3265475; Mon, 7 Jul 2014 09:37:27 +0200 (CEST) Received: from rtits2.realtek.com (rtits2.realtek.com [60.250.210.242]) by alsa0.perex.cz (Postfix) with ESMTP id 3371C265347 for ; Mon, 7 Jul 2014 09:37:17 +0200 (CEST) X-SpamFilter-By: BOX Solutions SpamTrap 5.39 with qID s677bEmw013709, This message is accepted by code: ctloc85258 Received: from mail.realtek.com (rtitcas11.realtek.com.tw[172.21.6.12]) by rtits2.realtek.com (8.14.5/2.37/5.60) with ESMTP id s677bEmw013709 (version=TLSv1/SSLv3 cipher=AES128-SHA bits=128 verify=NOT); Mon, 7 Jul 2014 15:37:14 +0800 Received: from sw-server.rtdomain (172.21.81.164) by RTITCAS11.realtek.com.tw (172.21.6.12) with Microsoft SMTP Server id 14.3.181.6; Mon, 7 Jul 2014 15:37:14 +0800 From: Oder Chiou To: , Date: Mon, 7 Jul 2014 15:37:01 +0800 Message-ID: <1404718621-15516-3-git-send-email-oder_chiou@realtek.com> X-Mailer: git-send-email 1.8.1.1.439.g50a6b54 In-Reply-To: <1404718621-15516-1-git-send-email-oder_chiou@realtek.com> References: <1404718621-15516-1-git-send-email-oder_chiou@realtek.com> MIME-Version: 1.0 X-Originating-IP: [172.21.81.164] Cc: oder_chiou@realtek.com, bardliao@realtek.com, alsa-devel@alsa-project.org, flove@realtek.com Subject: [alsa-devel] [PATCH 3/3] ASoC: rt5677: Support DSP function X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 + * + * 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 #include #include +#include #include #include #include @@ -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__ */