From patchwork Thu Sep 18 07:21:05 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bard Liao X-Patchwork-Id: 4929511 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 4C3DFBEEA5 for ; Thu, 18 Sep 2014 07:22:11 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B2346201BB for ; Thu, 18 Sep 2014 07:22:08 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.kernel.org (Postfix) with ESMTP id C4E092017D for ; Thu, 18 Sep 2014 07:22:05 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 244C3265141; Thu, 18 Sep 2014 09:22:04 +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, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from alsa0.perex.cz (localhost [IPv6:::1]) by alsa0.perex.cz (Postfix) with ESMTP id CCACC265182; Thu, 18 Sep 2014 09:21:53 +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 CFA552652AE; Thu, 18 Sep 2014 09:21:52 +0200 (CEST) Received: from rtits2.realtek.com (rtits2.realtek.com [60.250.210.242]) by alsa0.perex.cz (Postfix) with ESMTP id 7C78F265141 for ; Thu, 18 Sep 2014 09:21:45 +0200 (CEST) Authenticated-By: X-SpamFilter-By: BOX Solutions SpamTrap 5.49 with qID s8I7LLrs007514, 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.9/2.40/5.63) with ESMTP id s8I7LLrs007514 (version=TLSv1/SSLv3 cipher=AES128-SHA bits=128 verify=NOT); Thu, 18 Sep 2014 15:21:21 +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.195.1; Thu, 18 Sep 2014 15:21:22 +0800 From: To: , Date: Thu, 18 Sep 2014 15:21:05 +0800 Message-ID: <1411024865-19219-1-git-send-email-bardliao@realtek.com> X-Mailer: git-send-email 1.8.1.1.439.g50a6b54 MIME-Version: 1.0 X-Originating-IP: [172.21.81.164] Cc: oder_chiou@realtek.com, Bard Liao , alsa-devel@alsa-project.org, lars@metafoo.de, flove@realtek.com Subject: [alsa-devel] [PATCH] ASoC: rt5670: Add dsp support 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 From: Bard Liao Add dsp part of rt5670 codec driver Signed-off-by: Bard Liao --- sound/soc/codecs/Makefile | 2 +- sound/soc/codecs/rt5670-dsp.c | 383 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/rt5670-dsp.h | 10 +- sound/soc/codecs/rt5670.c | 43 +++++ sound/soc/codecs/rt5670.h | 3 + 5 files changed, 434 insertions(+), 7 deletions(-) create mode 100644 sound/soc/codecs/rt5670-dsp.c diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index afba944..3e5bc33 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -78,7 +78,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-rt5670-objs := rt5670.o +snd-soc-rt5670-objs := rt5670.o rt5670-dsp.o snd-soc-rt5677-objs := rt5677.o snd-soc-sgtl5000-objs := sgtl5000.o snd-soc-alc5623-objs := alc5623.o diff --git a/sound/soc/codecs/rt5670-dsp.c b/sound/soc/codecs/rt5670-dsp.c new file mode 100644 index 0000000..bbe039d --- /dev/null +++ b/sound/soc/codecs/rt5670-dsp.c @@ -0,0 +1,383 @@ +/* + * rt5670-dsp.c -- RT5670 ALSA SoC DSP driver + * + * Copyright 2014 Realtek Semiconductor Corp. + * Author: Bard Liao + * + * 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 "rt5670.h" +#include "rt5670-dsp.h" + +#define DSP_CLK_RATE RT5670_DSP_CLK_96K + +static const struct firmware *rt5670_dsp_fw; + +/** + * rt5670_dsp_done - Wait until DSP is ready. + * @codec: SoC Audio Codec device. + * + * To check voice DSP status and confirm it's ready for next work. + * + * Returns 0 for success or negative error code. + */ +static int rt5670_dsp_done(struct snd_soc_codec *codec) +{ + unsigned int count = 0, dsp_val; + + dsp_val = snd_soc_read(codec, RT5670_DSP_CTRL1); + while (dsp_val & RT5670_DSP_BUSY_MASK) { + if (count > 10) + return -EBUSY; + dsp_val = snd_soc_read(codec, RT5670_DSP_CTRL1); + count++; + } + + return 0; +} + +/** + * rt5670_dsp_write - Write DSP register. + * @codec: SoC audio codec device. + * @param: DSP parameters. + * + * Modify voice DSP register for sound effect. The DSP can be controlled + * through DSP addr (0xe1), data (0xe2) and cmd (0xe0) + * registers. It has to wait until the DSP is ready. + * + * Returns 0 for success or negative error code. + */ +int rt5670_dsp_write(struct snd_soc_codec *codec, + unsigned int addr, unsigned int data) +{ + unsigned int dsp_val; + int ret; + + ret = snd_soc_write(codec, RT5670_DSP_CTRL2, addr); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP addr reg: %d\n", ret); + goto err; + } + ret = snd_soc_write(codec, RT5670_DSP_CTRL3, data); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP data reg: %d\n", ret); + goto err; + } + dsp_val = RT5670_DSP_I2C_AL_16 | RT5670_DSP_DL_2 | + RT5670_DSP_CMD_MW | DSP_CLK_RATE | RT5670_DSP_CMD_EN; + + ret = snd_soc_write(codec, RT5670_DSP_CTRL1, dsp_val); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP cmd reg: %d\n", ret); + goto err; + } + ret = rt5670_dsp_done(codec); + if (ret < 0) { + dev_err(codec->dev, "DSP is busy: %d\n", ret); + goto err; + } + + return 0; + +err: + return ret; +} + +/** + * rt5670_dsp_read - Read DSP register. + * @codec: SoC audio codec device. + * @reg: DSP register index. + * + * Read DSP setting value from voice DSP. The DSP can be controlled + * through DSP addr (0xe1), data (0xe2) and cmd (0xe0) registers. Each + * command has to wait until the DSP is ready. + * + * Returns DSP register value or negative error code. + */ +unsigned int rt5670_dsp_read( + struct snd_soc_codec *codec, unsigned int reg) +{ + unsigned int value; + unsigned int dsp_val; + int ret = 0; + + ret = rt5670_dsp_done(codec); + if (ret < 0) { + dev_err(codec->dev, "DSP is busy: %d\n", ret); + goto err; + } + + ret = snd_soc_write(codec, RT5670_DSP_CTRL2, reg); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP addr reg: %d\n", ret); + goto err; + } + dsp_val = RT5670_DSP_I2C_AL_16 | RT5670_DSP_DL_0 | RT5670_DSP_RW_MASK | + RT5670_DSP_CMD_MR | DSP_CLK_RATE | RT5670_DSP_CMD_EN; + + ret = snd_soc_write(codec, RT5670_DSP_CTRL1, dsp_val); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP cmd reg: %d\n", ret); + goto err; + } + + ret = rt5670_dsp_done(codec); + if (ret < 0) { + dev_err(codec->dev, "DSP is busy: %d\n", ret); + goto err; + } + + ret = snd_soc_write(codec, RT5670_DSP_CTRL2, 0x26); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP addr reg: %d\n", ret); + goto err; + } + dsp_val = RT5670_DSP_DL_1 | RT5670_DSP_CMD_RR | RT5670_DSP_RW_MASK | + DSP_CLK_RATE | RT5670_DSP_CMD_EN; + + ret = snd_soc_write(codec, RT5670_DSP_CTRL1, dsp_val); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP cmd reg: %d\n", ret); + goto err; + } + + ret = rt5670_dsp_done(codec); + if (ret < 0) { + dev_err(codec->dev, "DSP is busy: %d\n", ret); + goto err; + } + + ret = snd_soc_write(codec, RT5670_DSP_CTRL2, 0x25); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP addr reg: %d\n", ret); + goto err; + } + + dsp_val = RT5670_DSP_DL_1 | RT5670_DSP_CMD_RR | RT5670_DSP_RW_MASK | + DSP_CLK_RATE | RT5670_DSP_CMD_EN; + + ret = snd_soc_write(codec, RT5670_DSP_CTRL1, dsp_val); + if (ret < 0) { + dev_err(codec->dev, "Failed to write DSP cmd reg: %d\n", ret); + goto err; + } + + ret = rt5670_dsp_done(codec); + if (ret < 0) { + dev_err(codec->dev, "DSP is busy: %d\n", ret); + goto err; + } + + ret = snd_soc_read(codec, RT5670_DSP_CTRL5); + if (ret < 0) { + dev_err(codec->dev, "Failed to read DSP data reg: %d\n", ret); + goto err; + } + + value = ret; + return value; + +err: + return ret; +} + +static int rt5670_dsp_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = rt5670->dsp_sw; + + return 0; +} + +static int rt5670_dsp_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + if (rt5670->dsp_sw != ucontrol->value.integer.value[0]) + rt5670->dsp_sw = ucontrol->value.integer.value[0]; + + return 0; +} + +/* DSP SRC Control */ +static const char * const rt5670_src_rxdp_mode[] = { + "Normal", "Divided by 2", "Divided by 3" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_src_rxdp_enum, RT5670_DSP_PATH1, + RT5670_RXDP_SRC_SFT, rt5670_src_rxdp_mode); + +static const char * const rt5670_src_txdp_mode[] = { + "Normal", "Multiplied by 2", "Multiplied by 3" +}; + +static const SOC_ENUM_SINGLE_DECL( + rt5670_src_txdp_enum, RT5670_DSP_PATH1, + RT5670_TXDP_SRC_SFT, rt5670_src_txdp_mode); + +/* DSP Mode */ +static const char * const rt5670_dsp_mode[] = { + "Mode 1", "Mode 2", "Mode 3", "Mode 4", "Mode 5" +}; + +static const SOC_ENUM_SINGLE_DECL(rt5670_dsp_enum, 0, 0, + rt5670_dsp_mode); + +static const struct snd_kcontrol_new rt5670_dsp_snd_controls[] = { + SOC_ENUM("RxDP SRC Switch", rt5670_src_rxdp_enum), + SOC_ENUM("TxDP SRC Switch", rt5670_src_txdp_enum), + SOC_ENUM_EXT("DSP Function Switch", rt5670_dsp_enum, + rt5670_dsp_mode_get, rt5670_dsp_mode_put), +}; + +/** + * rt5670_dsp_set_mode - Set DSP mode parameters. + * + * @codec: SoC audio codec device. + * @mode: DSP mode. + * + * Set parameters of mode to DSP. + * + * Returns 0 for success or negative error code. + */ +static int rt5670_dsp_set_mode(struct snd_soc_codec *codec, int mode) +{ + int tab_num, n, ret = -EINVAL; + unsigned int pos = 0; + + if (rt5670_dsp_fw) { + n = rt5670_dsp_fw->data[0]; + + if (mode <= n) { + pos = rt5670_dsp_fw->data[mode * 3 + 2] | + rt5670_dsp_fw->data[mode * 3 + 1] << 8; + + tab_num = rt5670_dsp_fw->data[mode * 3 + 3]; + if (pos + tab_num * 5 > rt5670_dsp_fw->size) + return -EINVAL; + ret = rt5670_write_fw(codec, + rt5670_dsp_fw, pos, tab_num); + if (ret < 0) + dev_err(codec->dev, + "Fail to set mode %d parameters: %d\n", + mode, ret); + } + } + + return ret; +} + +static int rt5670_dsp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + rt5670_dsp_write(codec, 0x22f9, 1); + break; + + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, RT5670_DIG_MISC, + RT5670_RST_DSP, RT5670_RST_DSP); + snd_soc_update_bits(codec, RT5670_DIG_MISC, + RT5670_RST_DSP, 0); + mdelay(10); + rt5670_dsp_set_mode(codec, rt5670->dsp_sw); + break; + + default: + return 0; + } + + return 0; +} + +static const struct snd_soc_dapm_widget rt5670_dsp_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("Voice DSP", 1, SND_SOC_NOPM, + 0, 0, rt5670_dsp_event, + SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_PGA("DSP Downstream", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DSP Upstream", SND_SOC_NOPM, + 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route rt5670_dsp_dapm_routes[] = { + {"DSP Downstream", NULL, "Voice DSP"}, + {"DSP Downstream", NULL, "RxDP Mux"}, + {"DSP Upstream", NULL, "Voice DSP"}, + {"DSP Upstream", NULL, "TDM Data Mux"}, + {"DSP DL Mux", "DSP", "DSP Downstream"}, + {"DSP UL Mux", "DSP", "DSP Upstream"}, +}; + +static void rt5670_dsp_fw_loaded(const struct firmware *fw, void *context) +{ + if (fw) { + rt5670_dsp_fw = fw; + pr_debug("fw->size=%d\n", fw->size); + } + +} + +/** + * rt5670_dsp_probe - register DSP for rt5670 + * @codec: audio codec + * + * To register DSP function for rt5670. + * + * Returns 0 for success or negative error code. + */ +int rt5670_dsp_probe(struct snd_soc_codec *codec) +{ + if (codec == NULL) + return -EINVAL; + + snd_soc_update_bits(codec, RT5670_PWR_DIG2, + RT5670_PWR_I2S_DSP, RT5670_PWR_I2S_DSP); + + snd_soc_update_bits(codec, RT5670_DIG_MISC, RT5670_RST_DSP, + RT5670_RST_DSP); + snd_soc_update_bits(codec, RT5670_DIG_MISC, RT5670_RST_DSP, 0); + + mdelay(10); + + rt5670_dsp_set_mode(codec, 0); + /* power down DSP */ + mdelay(15); + rt5670_dsp_write(codec, 0x22f9, 1); + + snd_soc_update_bits(codec, RT5670_PWR_DIG2, + RT5670_PWR_I2S_DSP, 0); + + snd_soc_add_codec_controls(codec, rt5670_dsp_snd_controls, + ARRAY_SIZE(rt5670_dsp_snd_controls)); + snd_soc_dapm_new_controls(&codec->dapm, rt5670_dsp_dapm_widgets, + ARRAY_SIZE(rt5670_dsp_dapm_widgets)); + snd_soc_dapm_add_routes(&codec->dapm, rt5670_dsp_dapm_routes, + ARRAY_SIZE(rt5670_dsp_dapm_routes)); + + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + "rt567x_dsp.bin", codec->dev, GFP_KERNEL, + codec, rt5670_dsp_fw_loaded); + + return 0; +} diff --git a/sound/soc/codecs/rt5670-dsp.h b/sound/soc/codecs/rt5670-dsp.h index a34d0cd..cd13f61 100644 --- a/sound/soc/codecs/rt5670-dsp.h +++ b/sound/soc/codecs/rt5670-dsp.h @@ -43,12 +43,10 @@ #define RT5670_DSP_I2C_AL_16 (0x1 << 1) #define RT5670_DSP_CMD_EN (0x1) -struct rt5670_dsp_param { - u16 cmd_fmt; - u16 addr; - u16 data; - u8 cmd; -}; +int rt5670_dsp_write(struct snd_soc_codec *codec, + unsigned int addr, unsigned int data); +unsigned int rt5670_dsp_read( + struct snd_soc_codec *codec, unsigned int reg); #endif /* __RT5670_DSP_H__ */ diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index ba9d9b4..da4b689 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -401,6 +402,44 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg) } } +int rt5670_write_fw(struct snd_soc_codec *codec, const struct firmware *fw, + unsigned int pos, unsigned int num) +{ + int i, ret; + + for (i = 0; i < num; i++) { + switch (fw->data[pos]) { + case 1: /*PR*/ + ret = snd_soc_write(codec, RT5670_PR_BASE + + ((fw->data[pos + 1] << 8) | fw->data[pos + 2]), + (fw->data[pos + 3] << 8) | fw->data[pos + 4]); + if (ret < 0) + return -1; + break; + case 2: /*dsp*/ + ret = rt5670_dsp_write(codec, + (fw->data[pos + 1] << 8) | fw->data[pos + 2], + (fw->data[pos + 3] << 8) | fw->data[pos + 4]); + if (ret < 0) + return -1; + break; + default: /*register*/ + ret = snd_soc_write(codec, + (fw->data[pos + 1] << 8) | fw->data[pos + 2], + (fw->data[pos + 3] << 8) | fw->data[pos + 4]); + if (ret < 0) + return -1; + break; + } + pos += 5; + if (fw->size < pos) + return 0; + } + + return 0; + +} + 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); @@ -2332,6 +2371,7 @@ static int rt5670_probe(struct snd_soc_codec *codec) struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); rt5670->codec = codec; + rt5670_dsp_probe(codec); return 0; } @@ -2623,6 +2663,9 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, } + /*Give sysclk a default value*/ + rt5670->sysclk = 24576000; + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5670, rt5670_dai, ARRAY_SIZE(rt5670_dai)); if (ret < 0) diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index a0b5c85..5d2dea5 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1997,4 +1997,7 @@ struct rt5670_priv { int jack_type; }; +int rt5671_write_fw(struct snd_soc_codec *codec, const struct firmware *fw, + unsigned int pos, unsigned int num); + #endif /* __RT5670_H__ */