From patchwork Thu Jan 26 20:09:43 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pierre-Louis Bossart X-Patchwork-Id: 9540599 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 73BA1604A0 for ; Fri, 27 Jan 2017 04:42:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 5D713223A6 for ; Fri, 27 Jan 2017 04:42:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 4FB3728174; Fri, 27 Jan 2017 04:42:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 01D67223A6 for ; Fri, 27 Jan 2017 04:42:08 +0000 (UTC) Received: by alsa0.perex.cz (Postfix, from userid 1000) id 6D8422678DF; Fri, 27 Jan 2017 05:32:40 +0100 (CET) Received: from alsa0.perex.cz (localhost [127.0.0.1]) by alsa0.perex.cz (Postfix) with ESMTP id A18B32678E9; Fri, 27 Jan 2017 05:30:21 +0100 (CET) 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 509EE2677A9; Thu, 26 Jan 2017 21:10:32 +0100 (CET) Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) by alsa0.perex.cz (Postfix) with ESMTP id BA05C267774 for ; Thu, 26 Jan 2017 21:10:29 +0100 (CET) Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga104.fm.intel.com with ESMTP; 26 Jan 2017 12:10:26 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.33,291,1477983600"; d="scan'208";a="57637728" Received: from pbossart-mobl3.an.intel.com (HELO pbossart-mobl3.intel.com) ([10.255.71.40]) by orsmga005.jf.intel.com with ESMTP; 26 Jan 2017 12:10:26 -0800 From: Pierre-Louis Bossart To: alsa-devel@alsa-project.org Date: Thu, 26 Jan 2017 14:09:43 -0600 Message-Id: <1485461385-14544-16-git-send-email-pierre-louis.bossart@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1485461385-14544-1-git-send-email-pierre-louis.bossart@linux.intel.com> References: <1485461385-14544-1-git-send-email-pierre-louis.bossart@linux.intel.com> Cc: tiwai@suse.de, broonie@kernel.org, Pierre-Louis Bossart Subject: [alsa-devel] [PATCH 15/17] ASoC: Intel: cht-bsw-rt5645: add quirks for SSP0/AIF1/AIF2 routing 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: , MIME-Version: 1.0 Errors-To: alsa-devel-bounces@alsa-project.org Sender: alsa-devel-bounces@alsa-project.org X-Virus-Scanned: ClamAV using ClamSMTP This driver may be used on Baytrail CR platforms where SSP2 is not available. Add quirks and routing detection based on work done for RT5640. Signed-off-by: Pierre-Louis Bossart --- sound/soc/intel/boards/cht_bsw_rt5645.c | 238 +++++++++++++++++++++++++++++--- 1 file changed, 220 insertions(+), 18 deletions(-) diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index ca1222c..111d45f 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -36,7 +37,8 @@ #include "../common/sst-acpi.h" #define CHT_PLAT_CLK_3_HZ 19200000 -#define CHT_CODEC_DAI "rt5645-aif1" +#define CHT_CODEC_DAI1 "rt5645-aif1" +#define CHT_CODEC_DAI2 "rt5645-aif2" struct cht_acpi_card { char *codec_id; @@ -51,13 +53,33 @@ struct cht_mc_private { struct clk *mclk; }; +#define CHT_RT5645_MAP(quirk) ((quirk) & 0xff) +#define CHT_RT5645_SSP2_AIF2 BIT(16) /* default is using AIF1 */ +#define CHT_RT5645_SSP0_AIF1 BIT(17) +#define CHT_RT5645_SSP0_AIF2 BIT(18) + +static unsigned long cht_rt5645_quirk = 0; + +static void log_quirks(struct device *dev) +{ + if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) + dev_info(dev, "quirk SSP2_AIF2 enabled"); + if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) + dev_info(dev, "quirk SSP0_AIF1 enabled"); + if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) + dev_info(dev, "quirk SSP0_AIF2 enabled"); +} + static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *rtd; list_for_each_entry(rtd, &card->rtd_list, list) { - if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI, - strlen(CHT_CODEC_DAI))) + if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI1, + strlen(CHT_CODEC_DAI1))) + return rtd->codec_dai; + if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI2, + strlen(CHT_CODEC_DAI2))) return rtd->codec_dai; } return NULL; @@ -125,12 +147,6 @@ static const struct snd_soc_dapm_route cht_rt5645_audio_map[] = { {"Headphone", NULL, "HPOR"}, {"Ext Spk", NULL, "SPOL"}, {"Ext Spk", NULL, "SPOR"}, - {"AIF1 Playback", NULL, "ssp2 Tx"}, - {"ssp2 Tx", NULL, "codec_out0"}, - {"ssp2 Tx", NULL, "codec_out1"}, - {"codec_in0", NULL, "ssp2 Rx" }, - {"codec_in1", NULL, "ssp2 Rx" }, - {"ssp2 Rx", NULL, "AIF1 Capture"}, {"Headphone", NULL, "Platform Clock"}, {"Headset Mic", NULL, "Platform Clock"}, {"Int Mic", NULL, "Platform Clock"}, @@ -146,16 +162,42 @@ static const struct snd_soc_dapm_route cht_rt5650_audio_map[] = { {"Headphone", NULL, "HPOR"}, {"Ext Spk", NULL, "SPOL"}, {"Ext Spk", NULL, "SPOR"}, + {"Headphone", NULL, "Platform Clock"}, + {"Headset Mic", NULL, "Platform Clock"}, + {"Int Mic", NULL, "Platform Clock"}, + {"Ext Spk", NULL, "Platform Clock"}, +}; + +static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif1_map[] = { {"AIF1 Playback", NULL, "ssp2 Tx"}, {"ssp2 Tx", NULL, "codec_out0"}, {"ssp2 Tx", NULL, "codec_out1"}, {"codec_in0", NULL, "ssp2 Rx" }, {"codec_in1", NULL, "ssp2 Rx" }, {"ssp2 Rx", NULL, "AIF1 Capture"}, - {"Headphone", NULL, "Platform Clock"}, - {"Headset Mic", NULL, "Platform Clock"}, - {"Int Mic", NULL, "Platform Clock"}, - {"Ext Spk", NULL, "Platform Clock"}, +}; + +static const struct snd_soc_dapm_route cht_rt5645_ssp2_aif2_map[] = { + {"AIF2 Playback", NULL, "ssp2 Tx"}, + {"ssp2 Tx", NULL, "codec_out0"}, + {"ssp2 Tx", NULL, "codec_out1"}, + {"codec_in0", NULL, "ssp2 Rx" }, + {"codec_in1", NULL, "ssp2 Rx" }, + {"ssp2 Rx", NULL, "AIF2 Capture"}, +}; + +static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif1_map[] = { + {"AIF1 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "modem_out"}, + {"modem_in", NULL, "ssp0 Rx" }, + {"ssp0 Rx", NULL, "AIF1 Capture"}, +}; + +static const struct snd_soc_dapm_route cht_rt5645_ssp0_aif2_map[] = { + {"AIF2 Playback", NULL, "ssp0 Tx"}, + {"ssp0 Tx", NULL, "modem_out"}, + {"modem_in", NULL, "ssp0 Rx" }, + {"ssp0 Rx", NULL, "AIF2 Capture"}, }; static const struct snd_kcontrol_new cht_mc_controls[] = { @@ -201,11 +243,25 @@ static int cht_aif1_hw_params(struct snd_pcm_substream *substream, return 0; } +/* uncomment when we have a real quirk +static int cht_rt5645_quirk_cb(const struct dmi_system_id *id) +{ + cht_rt5645_quirk = (unsigned long)id->driver_data; + return 1; +} +*/ + +static const struct dmi_system_id cht_rt5645_quirk_table[] = { + { + }, +}; + static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) { int ret; int jack_type; struct snd_soc_codec *codec = runtime->codec; + struct snd_soc_card *card = runtime->card; struct snd_soc_dai *codec_dai = runtime->codec_dai; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card); @@ -217,6 +273,26 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) RT5645_AD_STEREO_FILTER, RT5645_CLK_SEL_I2S1_ASRC); + if (cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) { + ret = snd_soc_dapm_add_routes(&card->dapm, + cht_rt5645_ssp2_aif2_map, + ARRAY_SIZE(cht_rt5645_ssp2_aif2_map)); + } else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) { + ret = snd_soc_dapm_add_routes(&card->dapm, + cht_rt5645_ssp0_aif1_map, + ARRAY_SIZE(cht_rt5645_ssp0_aif1_map)); + } else if (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2) { + ret = snd_soc_dapm_add_routes(&card->dapm, + cht_rt5645_ssp0_aif2_map, + ARRAY_SIZE(cht_rt5645_ssp0_aif2_map)); + } else { + ret = snd_soc_dapm_add_routes(&card->dapm, + cht_rt5645_ssp2_aif1_map, + ARRAY_SIZE(cht_rt5645_ssp2_aif1_map)); + } + if (ret) + return ret; + /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); if (ret < 0) { @@ -267,6 +343,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { + int ret; struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); struct snd_interval *channels = hw_param_interval(params, @@ -276,8 +353,39 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = 48000; channels->min = channels->max = 2; - /* set SSP2 to 24-bit */ - params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) || + (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { + + /* set SSP0 to 16-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); + + /* + * Default mode for SSP configuration is TDM 4 slot, override config + * with explicit setting to I2S 2ch 16-bit. The word length is set with + * dai_set_tdm_slot() since there is no other API exposed + */ + ret = snd_soc_dai_set_fmt(rtd->cpu_dai, + SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBS_CFS + ); + if (ret < 0) { + dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 16); + if (ret < 0) { + dev_err(rtd->dev, "can't set I2S config, err %d\n", ret); + return ret; + } + + } else { + + /* set SSP2 to 24-bit */ + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); + + } return 0; } @@ -385,7 +493,9 @@ static struct cht_acpi_card snd_soc_cards[] = { {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650}, }; -static char cht_rt5640_codec_name[16]; /* i2c-:00 with HID being 8 chars */ +static char cht_rt5645_codec_name[16]; /* i2c-:00 with HID being 8 chars */ +static char cht_rt5645_codec_aif_name[12]; /* = "rt5645-aif[1|2]" */ +static char cht_rt5645_cpu_dai_name[10]; /* = "ssp[0|2]-port" */ static bool is_valleyview(void) { @@ -399,6 +509,11 @@ static bool is_valleyview(void) return true; } +struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ + u64 aif_value; /* 1: AIF1, 2: AIF2 */ + u64 mclock_value; /* usually 25MHz (0x17d7940), ignored */ +}; + static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; @@ -409,6 +524,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) const char *i2c_name = NULL; int dai_index = 0; bool found = false; + bool is_bytcr = false; drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); if (!drv) @@ -446,9 +562,95 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* fixup codec name based on HID */ i2c_name = sst_acpi_find_name_from_hid(mach->id); if (i2c_name != NULL) { - snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name), + snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name), "%s%s", "i2c-", i2c_name); - cht_dailink[dai_index].codec_name = cht_rt5640_codec_name; + cht_dailink[dai_index].codec_name = cht_rt5645_codec_name; + } + + /* + * swap SSP0 if bytcr is detected + * (will be overridden if DMI quirk is detected) + */ + if (is_valleyview()) { + struct sst_platform_info *p_info = mach->pdata; + const struct sst_res_info *res_info = p_info->res_info; + + if (res_info->acpi_ipc_irq_index == 0) + is_bytcr = true; + } + + if (is_bytcr) { + /* + * Baytrail CR platforms may have CHAN package in BIOS, try + * to find relevant routing quirk based as done on Windows + * platforms. We have to read the information directly from the + * BIOS, at this stage the card is not created and the links + * with the codec driver/pdata are non-existent + */ + + struct acpi_chan_package chan_package; + + /* format specified: 2 64-bit integers */ + struct acpi_buffer format = {sizeof("NN"), "NN"}; + struct acpi_buffer state = {0, NULL}; + struct sst_acpi_package_context pkg_ctx; + bool pkg_found = false; + + state.length = sizeof(chan_package); + state.pointer = &chan_package; + + pkg_ctx.name = "CHAN"; + pkg_ctx.length = 2; + pkg_ctx.format = &format; + pkg_ctx.state = &state; + pkg_ctx.data_valid = false; + + pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); + if (pkg_found) { + if (chan_package.aif_value == 1) { + dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); + cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF1; + } else if (chan_package.aif_value == 2) { + dev_info(&pdev->dev, "BIOS Routing: AIF2 connected\n"); + cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2; + } else { + dev_info(&pdev->dev, "BIOS Routing isn't valid, ignored\n"); + pkg_found = false; + } + } + + if (!pkg_found) { + /* no BIOS indications, assume SSP0-AIF2 connection */ + cht_rt5645_quirk |= CHT_RT5645_SSP0_AIF2; + } + } + + /* check quirks before creating card */ + dmi_check_system(cht_rt5645_quirk_table); + log_quirks(&pdev->dev); + + if ((cht_rt5645_quirk & CHT_RT5645_SSP2_AIF2) || + (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { + + /* fixup codec aif name */ + snprintf(cht_rt5645_codec_aif_name, + sizeof(cht_rt5645_codec_aif_name), + "%s", "rt5645-aif2"); + + cht_dailink[dai_index].codec_dai_name = + cht_rt5645_codec_aif_name; + } + + if ((cht_rt5645_quirk & CHT_RT5645_SSP0_AIF1) || + (cht_rt5645_quirk & CHT_RT5645_SSP0_AIF2)) { + + /* fixup cpu dai name name */ + snprintf(cht_rt5645_cpu_dai_name, + sizeof(cht_rt5645_cpu_dai_name), + "%s", "ssp0-port"); + + cht_dailink[dai_index].cpu_dai_name = + cht_rt5645_cpu_dai_name; } if (is_valleyview()) {