Received: by 10.192.165.148 with SMTP id m20csp3775326imm; Mon, 7 May 2018 19:34:41 -0700 (PDT) X-Google-Smtp-Source: AB8JxZq3Y43iBR/bwB+Q1AIK2XpXvvb/tpTeLJkg4iIG+FX4gIt5Jiu/P3UliZq8JkWuu7MvjP4S X-Received: by 2002:a63:3c47:: with SMTP id i7-v6mr31190250pgn.254.1525746881102; Mon, 07 May 2018 19:34:41 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1525746881; cv=none; d=google.com; s=arc-20160816; b=E41q71P/7B4yCaDXzTo9kZqdkMdKkM4wq/Gtd3lAeNLtOH3uSG5RH2Bp02uiPNbFou ITCLqfELSmxnrwLNWJoGaYIj2DWQAJ9dENPq78FTqeds7nv7UHzYp/Dfy8wbTzV9Zi9/ FkGoSHqzKuhCPrvxr6qGqZS/eEp1wU41AJZ5TQgWO7IsjWhHIX7yqejPEQzdzEtrkwuQ xhCPSZMozTUr6HnE+EqTLGHgsJ+BmfszyIm14P2w9gn0DXNg41rdRyhJ0BkcQMgRkDxc 2vetjHIFKtAUNRU7/K2zWRNnPT2GmF+HI4UfbewkiM6hi9aquy0d1waK0SRNU7VLvlOr gvdA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:to:references:in-reply-to:message-id:date :subject:cc:from:dkim-signature:arc-authentication-results; bh=aaJfVKzh9wX8gmBNGTKd2rQWN3ddQlDc2IyG8CpGWwU=; b=umfqoUVpmFDpy00tMJ7R1bSaGqCVHPMCTeyCIONzhx/J966XvPNgf6o2ceEAfTaeBr PqY9PnXUl3J7LVtTud2uXJbzxQonb1MVtG9aePfkDlwIWOPZFsetj7Fr/JXSv/zuVssb bt3Ky0v6PRjzUA6XLVVf3N5QkjuhZbo7fFnVN1fcmge9GRlEzUiYCrKgI8+g9OAV9E2e 9qEyzEJWNPZ/IyTeLrxYlkoM9DanrnhYXi8qTdqORDQi9pYFB1bogB4wJD4qWY7YJDcQ heG+DWRU4ZDEd8SEeERgCM8EteLp3xJrsM9P7FL/eADzILjYE0VQqz8nunlgex6CIpZA QrwQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=Vc4oKjUE; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id c7-v6si18317717plo.62.2018.05.07.19.34.26; Mon, 07 May 2018 19:34:41 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=fail header.i=@gmail.com header.s=20161025 header.b=Vc4oKjUE; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753680AbeEHCcW (ORCPT + 99 others); Mon, 7 May 2018 22:32:22 -0400 Received: from mail-io0-f193.google.com ([209.85.223.193]:47036 "EHLO mail-io0-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753818AbeEHCcO (ORCPT ); Mon, 7 May 2018 22:32:14 -0400 Received: by mail-io0-f193.google.com with SMTP id f21-v6so36591401iob.13 for ; Mon, 07 May 2018 19:32:13 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=aaJfVKzh9wX8gmBNGTKd2rQWN3ddQlDc2IyG8CpGWwU=; b=Vc4oKjUEWkmeN059shi5aejbcuyaUIeNRQ2d3K4p+C0bV8LNj2csQrp5fRra+1iymX kLIsnbpzv70akxa5uyxGEzdrg42iQVHjxd8SPeYiaYr2uBm6CB31uS32eI9cY8iq+X0P /YMgLmLgWer1o+SAiBUFHo2ai8Og16De62YkEgbQCfLNviIWIRFw5A6Jh6zd+ooEYRDC cxjTzDQB2j+YBepGSQy4u968mSeHlH5yplqhCZndl4lWlDw7GFYunvs35QJRZo5FGmQv 8VmPzsgunTi60wwU5Qs46Sj91eJTI0IWTcanjh3tQspHHa3McAXQA8HFtm8vE+AfJl6T pLVA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=aaJfVKzh9wX8gmBNGTKd2rQWN3ddQlDc2IyG8CpGWwU=; b=XqYPIStohkMx3Ccz9I8nw1U3tuIsTZy2ccJvKycLj9K6pBpJna4I5BctEHqk4mkK7k qSQNruzCzFwDBkyxB9Eb/0kZCGYmFJpUCr4Tir+USMaVh/uVMKbsvAMrHeXNhiHl6+84 9m/NzhcuvmTos73LYuk9qcG5/INceDqxDoiExIwRh9LlKCroMx679vSVg53ghBksSd99 KLw99+zoQ74u52aiUn3we/Smjvg6/VTPezj2KNIretrMICzOAoOs+2uQ2qoBjxdih4PQ obLw8FDrCPmue2aFyjOGOLePRhyt/8VXajLtVjrreJ3PIAOVLt7Zmz6RdHLmOwpwtJlo PINw== X-Gm-Message-State: ALQs6tDDnXX8jXLb3kpb4B8Yciq7S03wf9xjFAVECR01xGZdV1zsvNLA KYFqivVqI5dOJD0Yl4Tx1Tc= X-Received: by 2002:a6b:ab01:: with SMTP id u1-v6mr30752707ioe.214.1525746733125; Mon, 07 May 2018 19:32:13 -0700 (PDT) Received: from localhost.localdomain ([2605:a000:1316:4462:d951:8657:56b7:4002]) by smtp.googlemail.com with ESMTPSA id l22-v6sm4947954ita.8.2018.05.07.19.32.12 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 07 May 2018 19:32:12 -0700 (PDT) From: Connor McAdams Cc: o-takashi@sakamocchi.jp, Connor McAdams , Jaroslav Kysela , Takashi Iwai , =?UTF-8?q?J=C3=A9r=C3=A9my=20Lefaure?= , alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org Subject: [PATCH v4 05/13] ALSA: hda/ca0132: add extra init functions for r3di + sbz Date: Mon, 7 May 2018 22:31:13 -0400 Message-Id: <1525746683-18535-6-git-send-email-conmanx360@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1525746683-18535-1-git-send-email-conmanx360@gmail.com> References: <1525746683-18535-1-git-send-email-conmanx360@gmail.com> To: unlisted-recipients:; (no To-header on input) Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org This patch adds extra init functions for the Sound Blaster Z and Recon3Di. It also adds more checks to make sure that the DSP isn't downloaded twice on startup, by checking if the dsp_state is already set to DSP_DOWNLOADED. It also adds the ability to re-download the DSP on a resume. It also changes the init verbs table to apply to all codecs, and takes the two specific end verbs and puts them into a separate function in ca0132_init instead. GPIO functions are also added. Signed-off-by: Connor McAdams --- sound/pci/hda/patch_ca0132.c | 273 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 265 insertions(+), 8 deletions(-) diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 5cda7a5..5002311 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -710,6 +710,7 @@ struct ca0132_spec { const struct hda_verb *base_init_verbs; const struct hda_verb *base_exit_verbs; const struct hda_verb *chip_init_verbs; + const struct hda_verb *sbz_init_verbs; struct hda_verb *spec_init_verbs; struct auto_pin_cfg autocfg; @@ -743,6 +744,7 @@ struct ca0132_spec { unsigned int scp_resp_data[4]; unsigned int scp_resp_count; bool alt_firmware_present; + bool dsp_reload; /* mixer and effects related */ unsigned char dmic_ctl; @@ -2740,6 +2742,59 @@ static bool dspload_wait_loaded(struct hda_codec *codec) } /* + * Setup GPIO for the other variants of Core3D. + */ + +/* + * Sets up the GPIO pins so that they are discoverable. If this isn't done, + * the card shows as having no GPIO pins. + */ +static void ca0132_gpio_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + switch (spec->quirk) { + case QUIRK_SBZ: + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); + snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23); + break; + case QUIRK_R3DI: + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5B); + break; + } + +} + +/* Sets the GPIO for audio output. */ +static void ca0132_gpio_setup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + switch (spec->quirk) { + case QUIRK_SBZ: + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DIRECTION, 0x07); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_MASK, 0x07); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0x04); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0x06); + break; + case QUIRK_R3DI: + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DIRECTION, 0x1E); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_MASK, 0x1F); + snd_hda_codec_write(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0x0C); + break; + } +} + +/* * PCM callbacks */ static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -4490,11 +4545,14 @@ static void ca0132_download_dsp(struct hda_codec *codec) return; /* don't retry failures */ chipio_enable_clocks(codec); - spec->dsp_state = DSP_DOWNLOADING; - if (!ca0132_download_dsp_images(codec)) - spec->dsp_state = DSP_DOWNLOAD_FAILED; - else - spec->dsp_state = DSP_DOWNLOADED; + if (spec->dsp_state != DSP_DOWNLOADED) { + spec->dsp_state = DSP_DOWNLOADING; + + if (!ca0132_download_dsp_images(codec)) + spec->dsp_state = DSP_DOWNLOAD_FAILED; + else + spec->dsp_state = DSP_DOWNLOADED; + } if (spec->dsp_state == DSP_DOWNLOADED) ca0132_set_dsp_msr(codec, true); @@ -4569,6 +4627,7 @@ static struct hda_verb ca0132_base_exit_verbs[] = { }; /* Other verbs tables. Sends after DSP download. */ + static struct hda_verb ca0132_init_verbs0[] = { /* chip init verbs */ {0x15, 0x70D, 0xF0}, @@ -4598,8 +4657,27 @@ static struct hda_verb ca0132_init_verbs0[] = { {0x15, 0x546, 0xC9}, {0x15, 0x53B, 0xCE}, {0x15, 0x5E8, 0xC9}, - {0x15, 0x717, 0x0D}, - {0x15, 0x718, 0x20}, + {} +}; + +/* Extra init verbs for SBZ */ +static struct hda_verb sbz_init_verbs[] = { + {0x15, 0x70D, 0x20}, + {0x15, 0x70E, 0x19}, + {0x15, 0x707, 0x00}, + {0x15, 0x539, 0xCE}, + {0x15, 0x546, 0xC9}, + {0x15, 0x70D, 0xB7}, + {0x15, 0x70E, 0x09}, + {0x15, 0x707, 0x10}, + {0x15, 0x70D, 0xAF}, + {0x15, 0x70E, 0x09}, + {0x15, 0x707, 0x01}, + {0x15, 0x707, 0x05}, + {0x15, 0x70D, 0x73}, + {0x15, 0x70E, 0x09}, + {0x15, 0x707, 0x14}, + {0x15, 0x6FF, 0xC4}, {} }; @@ -4758,16 +4836,166 @@ static void ca0132_exit_chip(struct hda_codec *codec) dsp_reset(codec); } +/* + * This is for the extra volume verbs 0x797 (left) and 0x798 (right). These add + * extra precision for decibel values. If you had the dB value in floating point + * you would take the value after the decimal point, multiply by 64, and divide + * by 2. So for 8.59, it's (59 * 64) / 100. Useful if someone wanted to + * implement fixed point or floating point dB volumes. For now, I'll set them + * to 0 just incase a value has lingered from a boot into Windows. + */ +static void ca0132_alt_vol_setup(struct hda_codec *codec) +{ + snd_hda_codec_write(codec, 0x02, 0, 0x797, 0x00); + snd_hda_codec_write(codec, 0x02, 0, 0x798, 0x00); + snd_hda_codec_write(codec, 0x03, 0, 0x797, 0x00); + snd_hda_codec_write(codec, 0x03, 0, 0x798, 0x00); + snd_hda_codec_write(codec, 0x04, 0, 0x797, 0x00); + snd_hda_codec_write(codec, 0x04, 0, 0x798, 0x00); + snd_hda_codec_write(codec, 0x07, 0, 0x797, 0x00); + snd_hda_codec_write(codec, 0x07, 0, 0x798, 0x00); +} + +/* + * Extra commands that don't really fit anywhere else. + */ +static void sbz_pre_dsp_setup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + writel(0x00820680, spec->mem_base + 0x01C); + writel(0x00820680, spec->mem_base + 0x01C); + + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfc); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfd); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfe); + snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xff); + + chipio_write(codec, 0x18b0a4, 0x000000c2); + + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x44); +} + +/* + * Extra commands that don't really fit anywhere else. + */ +static void r3di_pre_dsp_setup(struct hda_codec *codec) +{ + chipio_write(codec, 0x18b0a4, 0x000000c2); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x1E); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x1C); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x5B); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x20); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0x19); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x00); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x40); + + snd_hda_codec_write(codec, 0x11, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x04); +} + + +/* + * These are sent before the DSP is downloaded. Not sure + * what they do, or if they're necessary. Could possibly + * be removed. Figure they're better to leave in. + */ +static void sbz_region2_startup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + writel(0x00000000, spec->mem_base + 0x400); + writel(0x00000000, spec->mem_base + 0x408); + writel(0x00000000, spec->mem_base + 0x40C); + writel(0x00880680, spec->mem_base + 0x01C); + writel(0x00000083, spec->mem_base + 0xC0C); + writel(0x00000030, spec->mem_base + 0xC00); + writel(0x00000000, spec->mem_base + 0xC04); + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x000000C1, spec->mem_base + 0xC08); + writel(0x000000F1, spec->mem_base + 0xC08); + writel(0x00000001, spec->mem_base + 0xC08); + writel(0x000000C7, spec->mem_base + 0xC08); + writel(0x000000C1, spec->mem_base + 0xC08); + writel(0x00000080, spec->mem_base + 0xC04); +} + +/* + * Extra init functions for alternative ca0132 codecs. Done + * here so they don't clutter up the main ca0132_init function + * anymore than they have to. + */ +static void ca0132_alt_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + ca0132_alt_vol_setup(codec); + + switch (spec->quirk) { + case QUIRK_SBZ: + codec_dbg(codec, "SBZ alt_init"); + ca0132_gpio_init(codec); + sbz_pre_dsp_setup(codec); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->sbz_init_verbs); + break; + case QUIRK_R3DI: + codec_dbg(codec, "R3DI alt_init"); + ca0132_gpio_init(codec); + ca0132_gpio_setup(codec); + r3di_pre_dsp_setup(codec); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x6FF, 0xC4); + break; + } +} + static int ca0132_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; int i; + bool dsp_loaded; + + /* + * If the DSP is already downloaded, and init has been entered again, + * there's only two reasons for it. One, the codec has awaken from a + * suspended state, and in that case dspload_is_loaded will return + * false, and the init will be ran again. The other reason it gets + * re entered is on startup for some reason it triggers a suspend and + * resume state. In this case, it will check if the DSP is downloaded, + * and not run the init function again. For codecs using alt_functions, + * it will check if the DSP is loaded properly. + */ + if (spec->dsp_state == DSP_DOWNLOADED) { + dsp_loaded = dspload_is_loaded(codec); + if (!dsp_loaded) { + spec->dsp_reload = true; + spec->dsp_state = DSP_DOWNLOAD_INIT; + } else + return 0; + } if (spec->dsp_state != DSP_DOWNLOAD_FAILED) spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; + if (spec->quirk == QUIRK_SBZ) + sbz_region2_startup(codec); + snd_hda_power_up_pm(codec); ca0132_init_unsol(codec); @@ -4775,8 +5003,16 @@ static int ca0132_init(struct hda_codec *codec) ca0132_init_params(codec); ca0132_init_flags(codec); snd_hda_sequence_write(codec, spec->base_init_verbs); + + if (spec->quirk != QUIRK_NONE) + ca0132_alt_init(codec); + ca0132_download_dsp(codec); ca0132_refresh_widget_caps(codec); + + if (spec->quirk == QUIRK_SBZ) + writew(0x0107, spec->mem_base + 0x320); + ca0132_setup_defaults(codec); ca0132_init_analog_mic2(codec); ca0132_init_dmic(codec); @@ -4791,7 +5027,17 @@ static int ca0132_init(struct hda_codec *codec) init_input(codec, cfg->dig_in_pin, spec->dig_in); - snd_hda_sequence_write(codec, spec->chip_init_verbs); + if (spec->quirk == QUIRK_ALIENWARE || spec->quirk == QUIRK_NONE) { + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_ID_SET, 0x0D); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PARAM_EX_VALUE_SET, 0x20); + } + + if (spec->quirk == QUIRK_SBZ) + ca0132_gpio_setup(codec); + snd_hda_sequence_write(codec, spec->spec_init_verbs); ca0132_select_out(codec); @@ -4799,6 +5045,15 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_jack_report_sync(codec); + /* + * Re set the PlayEnhancement switch on a resume event, because the + * controls will not be reloaded. + */ + if (spec->dsp_reload) { + spec->dsp_reload = false; + ca0132_pe_switch_set(codec); + } + snd_hda_power_down_pm(codec); return 0; @@ -4985,6 +5240,8 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) struct ca0132_spec *spec = codec->spec; spec->chip_init_verbs = ca0132_init_verbs0; + if (spec->quirk == QUIRK_SBZ) + spec->sbz_init_verbs = sbz_init_verbs; spec->spec_init_verbs = kzalloc(sizeof(struct hda_verb) * NUM_SPEC_VERBS, GFP_KERNEL); if (!spec->spec_init_verbs) return -ENOMEM; -- 2.7.4