From patchwork Thu Mar 4 19:02:41 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vitaly Rodionov X-Patchwork-Id: 12116685 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.9 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED,USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 36A62C433E0 for ; Thu, 4 Mar 2021 19:05:40 +0000 (UTC) Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 8F25564F65 for ; Thu, 4 Mar 2021 19:05:39 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 8F25564F65 Authentication-Results: mail.kernel.org; dmarc=fail (p=reject dis=none) header.from=opensource.cirrus.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=alsa-devel-bounces@alsa-project.org Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 2315017D6; Thu, 4 Mar 2021 20:04:48 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 2315017D6 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1614884738; bh=hWhSg7Tqu+6wTs2lnPi8iRf/FLKmgD6pYKjq1UHH/zk=; h=From:To:Subject:Date:In-Reply-To:References:Cc:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From; b=H+CZA95pyo3q04v4K3k4Stz20STSYUAl2mBkjqif2CzFASwkRKfADDM1pgcOso+Ji OJT2aK9hcLugdljTeogeDh2V8ERZfTMfRiSDUtG9scvt/DTvCxQOziJAtWqJnFiPUC m0pv5GYWW8iVUwBUiSTyH+cMaZANYeYVbNO8aUqc= Received: from alsa1.perex.cz (localhost.localdomain [127.0.0.1]) by alsa1.perex.cz (Postfix) with ESMTP id AA39FF80475; Thu, 4 Mar 2021 20:03:08 +0100 (CET) Received: by alsa1.perex.cz (Postfix, from userid 50401) id B3615F80430; Thu, 4 Mar 2021 20:03:05 +0100 (CET) Received: from mx0b-001ae601.pphosted.com (mx0b-001ae601.pphosted.com [67.231.152.168]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa1.perex.cz (Postfix) with ESMTPS id 5AB8CF80273 for ; Thu, 4 Mar 2021 20:02:47 +0100 (CET) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa1.perex.cz 5AB8CF80273 Authentication-Results: alsa1.perex.cz; dkim=pass (2048-bit key) header.d=cirrus.com header.i=@cirrus.com header.b="n4EK8MZ0" Received: from pps.filterd (m0077474.ppops.net [127.0.0.1]) by mx0b-001ae601.pphosted.com (8.16.0.43/8.16.0.43) with SMTP id 124J1FD8032593; Thu, 4 Mar 2021 13:02:46 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cirrus.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=PODMain02222019; bh=xT5ZZIZaGTg7FvfMp9EC2XsO45yEr1j3wspIaT4A4a0=; b=n4EK8MZ04b8siqUGk2MHbvzx2MJ8aQHIZc0cvWkXpc7vMlht1PH+gu8MRTQzeHHeYXPE tN32OOzhvAaYjXBCTMwGd0ZsuDZHZAmd5RMyt/M+IjCI4t8McPEnibuk47zzWumPRHHR a+VaCpdvB3wwoZeLCbrdJ0D8mngI2drma9k7vjroXnWAtlMp0rAT5db6o/VuKSfvztoC P9NljZkxm30/xTu5/7A7GmzTiMCxIFPDkAANnXRtayPNCdgnD1KuIRr1Dy14lTFLbpq7 /ket/rvwlh/8p7ES8mUH8lB3K9aUf5mCIw/olIJxodv8xOrGq+fvIcK9kzAB/n/7XLOk 0A== Received: from ediex02.ad.cirrus.com ([87.246.76.36]) by mx0b-001ae601.pphosted.com with ESMTP id 36ykctq12a-3 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT); Thu, 04 Mar 2021 13:02:45 -0600 Received: from EDIEX01.ad.cirrus.com (198.61.84.80) by EDIEX02.ad.cirrus.com (198.61.84.81) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2176.2; Thu, 4 Mar 2021 19:02:43 +0000 Received: from ediswmail.ad.cirrus.com (198.61.86.93) by EDIEX01.ad.cirrus.com (198.61.84.80) with Microsoft SMTP Server id 15.1.2176.2 via Frontend Transport; Thu, 4 Mar 2021 19:02:43 +0000 Received: from vitaly-Inspiron-5415.ad.cirrus.com (unknown [198.90.238.45]) by ediswmail.ad.cirrus.com (Postfix) with ESMTP id EEAD811D0; Thu, 4 Mar 2021 19:02:42 +0000 (UTC) From: Vitaly Rodionov To: Jaroslav Kysela , Takashi Iwai Subject: [PATCH v2 4/4] ALSA: hda/cirrus: Add Headphone and Headset MIC Volume Control Date: Thu, 4 Mar 2021 19:02:41 +0000 Message-ID: <20210304190241.5363-5-vitalyr@opensource.cirrus.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210304190241.5363-1-vitalyr@opensource.cirrus.com> References: <20210304190241.5363-1-vitalyr@opensource.cirrus.com> MIME-Version: 1.0 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 lowpriorityscore=0 clxscore=1015 malwarescore=0 priorityscore=1501 suspectscore=0 spamscore=0 phishscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2103040090 Cc: patches@opensource.cirrus.com, alsa-devel@alsa-project.org, linux-kernel@vger.kernel.org, Stefan Binding X-BeenThere: alsa-devel@alsa-project.org X-Mailman-Version: 2.1.15 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" From: Stefan Binding CS8409 does not support Volume Control for NIDs 0x24 (the Headphones), or 0x34 (The Headset Mic). However, CS42L42 codec does support gain control for both. We can add support for Volume Controls, by writing the the CS42L42 regmap via i2c commands, using custom info, get and put volume functions, saved in the control. Tested on DELL Inspiron-3500, DELL Inspiron-3501, DELL Inspiron-3500 Signed-off-by: Stefan Binding Signed-off-by: Vitaly Rodionov --- sound/pci/hda/patch_cirrus.c | 201 ++++++++++++++++++++++++++++++++++- 1 file changed, 198 insertions(+), 3 deletions(-) diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index 0b8980240176..082420545ab7 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -21,6 +21,9 @@ /* */ +#define CS42L42_HP_CH (2U) +#define CS42L42_HS_MIC_CH (1U) + struct cs_spec { struct hda_gen_spec gen; @@ -42,6 +45,9 @@ struct cs_spec { unsigned int cs42l42_hp_jack_in:1; unsigned int cs42l42_mic_jack_in:1; + unsigned int cs42l42_volume_init:1; + char cs42l42_hp_volume[CS42L42_HP_CH]; + char cs42l42_hs_mic_volume[CS42L42_HS_MIC_CH]; struct mutex cs8409_i2c_mux; @@ -1260,6 +1266,14 @@ static int patch_cs4213(struct hda_codec *codec) #define CIR_I2C_QWRITE 0x005D #define CIR_I2C_QREAD 0x005E +#define CS8409_CS42L42_HP_VOL_REAL_MIN (-63) +#define CS8409_CS42L42_HP_VOL_REAL_MAX (0) +#define CS8409_CS42L42_AMIC_VOL_REAL_MIN (-97) +#define CS8409_CS42L42_AMIC_VOL_REAL_MAX (12) +#define CS8409_CS42L42_REG_HS_VOLUME_CHA (0x2301) +#define CS8409_CS42L42_REG_HS_VOLUME_CHB (0x2303) +#define CS8409_CS42L42_REG_AMIC_VOLUME (0x1D03) + struct cs8409_i2c_param { unsigned int addr; unsigned int reg; @@ -1401,7 +1415,6 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { { 0x1010, 0xB0 }, { 0x1D01, 0x00 }, { 0x1D02, 0x06 }, - { 0x1D03, 0x00 }, { 0x1107, 0x01 }, { 0x1009, 0x02 }, { 0x1007, 0x03 }, @@ -1431,8 +1444,6 @@ static const struct cs8409_i2c_param cs42l42_init_reg_seq[] = { { 0x2901, 0x01 }, { 0x1101, 0x0A }, { 0x1102, 0x84 }, - { 0x2301, 0x00 }, - { 0x2303, 0x00 }, { 0x2302, 0x3f }, { 0x2001, 0x03 }, { 0x1B75, 0xB6 }, @@ -1580,6 +1591,179 @@ static unsigned int cs8409_i2c_write(struct hda_codec *codec, return retval; } +static int cs8409_cs42l42_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + u16 nid = get_amp_nid(kcontrol); + u8 chs = get_amp_channels(kcontrol); + + codec_dbg(codec, "%s() nid: %d\n", __func__, nid); + switch (nid) { + case CS8409_CS42L42_HP_PIN_NID: + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = CS8409_CS42L42_HP_VOL_REAL_MIN; + uinfo->value.integer.max = CS8409_CS42L42_HP_VOL_REAL_MAX; + break; + case CS8409_CS42L42_AMIC_PIN_NID: + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = chs == 3 ? 2 : 1; + uinfo->value.integer.min = CS8409_CS42L42_AMIC_VOL_REAL_MIN; + uinfo->value.integer.max = CS8409_CS42L42_AMIC_VOL_REAL_MAX; + break; + default: + break; + } + return 0; +} + +static void cs8409_cs42l42_update_volume(struct hda_codec *codec) +{ + struct cs_spec *spec = codec->spec; + + mutex_lock(&spec->cs8409_i2c_mux); + spec->cs42l42_hp_volume[0] = -(cs8409_i2c_read(codec, CS42L42_I2C_ADDR, + CS8409_CS42L42_REG_HS_VOLUME_CHA, 1)); + spec->cs42l42_hp_volume[1] = -(cs8409_i2c_read(codec, CS42L42_I2C_ADDR, + CS8409_CS42L42_REG_HS_VOLUME_CHB, 1)); + spec->cs42l42_hs_mic_volume[0] = -(cs8409_i2c_read(codec, CS42L42_I2C_ADDR, + CS8409_CS42L42_REG_AMIC_VOLUME, 1)); + codec_dbg(codec, "%s() HP Volume: %d/%d, HS Mic Volume: %d\n", __func__, + spec->cs42l42_hp_volume[0], spec->cs42l42_hp_volume[1], + spec->cs42l42_hs_mic_volume[0]); + mutex_unlock(&spec->cs8409_i2c_mux); + spec->cs42l42_volume_init = 1; +} + +static int cs8409_cs42l42_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int chs = get_amp_channels(kcontrol); + long *valp = ucontrol->value.integer.value; + + codec_dbg(codec, "%s() nid: %d\n", __func__, nid); + if (!spec->cs42l42_volume_init) { + snd_hda_power_up(codec); + cs8409_cs42l42_update_volume(codec); + snd_hda_power_down(codec); + } + switch (nid) { + case CS8409_CS42L42_HP_PIN_NID: + if (chs & 1) { + *valp++ = spec->cs42l42_hp_volume[0]; + codec_dbg(codec, "%s() vol(a) = %d\n", __func__, spec->cs42l42_hp_volume[0]); + } + if (chs & 2) { + *valp++ = spec->cs42l42_hp_volume[1]; + codec_dbg(codec, "%s() vol(b) = %d\n", __func__, spec->cs42l42_hp_volume[1]); + } + break; + case CS8409_CS42L42_AMIC_PIN_NID: + if (chs & 1) { + *valp++ = spec->cs42l42_hs_mic_volume[0]; + codec_dbg(codec, "%s() vol() = %d\n", __func__, spec->cs42l42_hs_mic_volume[0]); + } + break; + default: + break; + } + return 0; +} + +static int cs8409_cs42l42_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct cs_spec *spec = codec->spec; + hda_nid_t nid = get_amp_nid(kcontrol); + int chs = get_amp_channels(kcontrol); + long *valp = ucontrol->value.integer.value; + int change = 0; + char vol = 0; + + codec_dbg(codec, "%s() nid: %d\n", __func__, nid); + snd_hda_power_up(codec); + switch (nid) { + case CS8409_CS42L42_HP_PIN_NID: + mutex_lock(&spec->cs8409_i2c_mux); + if (chs & 1) { + vol = -(*valp); + codec_dbg(codec, "%s() vol(a) = %d\n", __func__, vol); + change = cs8409_i2c_write(codec, CS42L42_I2C_ADDR, + CS8409_CS42L42_REG_HS_VOLUME_CHA, vol, 1); + valp++; + } + if (chs & 2) { + vol = -(*valp); + codec_dbg(codec, "%s() vol(b) = %d\n", __func__, vol); + change |= cs8409_i2c_write(codec, CS42L42_I2C_ADDR, + CS8409_CS42L42_REG_HS_VOLUME_CHB, vol, 1); + } + mutex_unlock(&spec->cs8409_i2c_mux); + break; + case CS8409_CS42L42_AMIC_PIN_NID: + mutex_lock(&spec->cs8409_i2c_mux); + if (chs & 1) { + codec_dbg(codec, "%s() vol() = %d\n", __func__, (char)*valp); + change = cs8409_i2c_write( + codec, CS42L42_I2C_ADDR, + CS8409_CS42L42_REG_AMIC_VOLUME, (char)*valp, 1); + valp++; + } + mutex_unlock(&spec->cs8409_i2c_mux); + break; + default: + break; + } + cs8409_cs42l42_update_volume(codec); + snd_hda_power_down(codec); + return change; +} + +static const DECLARE_TLV_DB_SCALE( + cs8409_cs42l42_hp_db_scale, + CS8409_CS42L42_HP_VOL_REAL_MIN * 100, 100, 1); + +static const DECLARE_TLV_DB_SCALE( + cs8409_cs42l42_amic_db_scale, + CS8409_CS42L42_AMIC_VOL_REAL_MIN * 100, 100, 1); + +static const struct snd_kcontrol_new cs8409_cs42l42_hp_volume_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .index = 0, + .name = "Headphone Playback Volume", + .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG), + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE + | SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .info = cs8409_cs42l42_volume_info, + .get = cs8409_cs42l42_volume_get, + .put = cs8409_cs42l42_volume_put, + .tlv = { .p = cs8409_cs42l42_hp_db_scale }, + .private_value = HDA_COMPOSE_AMP_VAL( + CS8409_CS42L42_HP_PIN_NID, 3, 0, HDA_OUTPUT) + | HDA_AMP_VAL_MIN_MUTE +}; + +static const struct snd_kcontrol_new cs8409_cs42l42_amic_volume_mixer = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .index = 0, + .name = "Headset Mic Capture Volume", + .subdevice = (HDA_SUBDEV_AMP_FLAG | HDA_SUBDEV_NID_FLAG), + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE + | SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .info = cs8409_cs42l42_volume_info, + .get = cs8409_cs42l42_volume_get, + .put = cs8409_cs42l42_volume_put, + .tlv = { .p = cs8409_cs42l42_amic_db_scale }, + .private_value = HDA_COMPOSE_AMP_VAL( + CS8409_CS42L42_AMIC_PIN_NID, 1, 0, HDA_INPUT) + | HDA_AMP_VAL_MIN_MUTE +}; + /* Assert/release RTS# line to CS42L42 */ static void cs8409_cs42l42_reset(struct hda_codec *codec) { @@ -1882,6 +2066,8 @@ static int cs8409_cs42l42_hw_init(struct hda_codec *codec) cs_vendor_coef_set(codec, 0x09, 0x0003); } + cs8409_cs42l42_update_volume(codec); + cs8409_cs42l42_enable_jack_detect(codec); /* Enable Unsolicited Response */ @@ -1983,6 +2169,14 @@ static int cs8409_cs42l42_fixup(struct hda_codec *codec) if (err < 0) return err; + if (!snd_hda_gen_add_kctl( + &spec->gen, NULL, &cs8409_cs42l42_hp_volume_mixer)) + return -1; + + if (!snd_hda_gen_add_kctl( + &spec->gen, NULL, &cs8409_cs42l42_amic_volume_mixer)) + return -1; + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return err; @@ -2064,6 +2258,7 @@ static int patch_cs8409(struct hda_codec *codec) spec->gen.suppress_auto_mute = 1; spec->gen.no_primary_hp = 1; + spec->gen.suppress_vmaster = 1; /* GPIO 5 out, 3,4 in */ spec->gpio_dir = GPIO5_INT; spec->gpio_data = 0;