From patchwork Tue Jan 23 11:14:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shenghao Ding X-Patchwork-Id: 13527268 Received: from fllv0016.ext.ti.com (fllv0016.ext.ti.com [198.47.19.142]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E01A5D720; Tue, 23 Jan 2024 11:15:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.142 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706008528; cv=none; b=h83FFFO7BcnUnRUBP/54FklbqvX2WSvsULdwhdnkTtJiDQ7bpvaufZIqAlmRGTbFWCMhn+iEghUnS7768FHLGzOS7A2xKKHwQB3Hf5GXsstrqoRf3xzBBeF5OijHysr7+EyBC3HLqyH/Ct3sUEkuFwt49VdMZaOb150PDfdKHP8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706008528; c=relaxed/simple; bh=C0YJNtTmKvcv7AgXO3AHKi4J+W/Xe537w6JbIaZYjSQ=; h=From:To:CC:Subject:Date:Message-ID:MIME-Version:Content-Type; b=OMtq0OM4NfBOuAwgFDGALjTB2qWn9oeGgHnl/LS36FcU/mHxzrnvuX0OtlTyRiMgV2ric6NsuT+b1K+dXy95QrathJ+uW4nUbWdmlmSbIPZ8+wZfZsqxsFgKQ4zuxeiAC/gxgArwAe1Wu/D9roPvAfzG+jAmSQtWCN84/iLPWLA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=UaK7VzPE; arc=none smtp.client-ip=198.47.19.142 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="UaK7VzPE" Received: from fllv0034.itg.ti.com ([10.64.40.246]) by fllv0016.ext.ti.com (8.15.2/8.15.2) with ESMTP id 40NBEPvQ129697; Tue, 23 Jan 2024 05:14:25 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1706008466; bh=L8h01L3ETpNr6OmLLzJel8Dg4UA3jbsII5e9OKixFzQ=; h=From:To:CC:Subject:Date; b=UaK7VzPEAyApLGlUHG4T5VUoFvJOd+mRAjtu75Gmc+nT1PJV3/VWr5r7LL469oh6G g5NV2qOmeKeljPPemNq+7hzzho/W2DF4azyXMK1j9o+cHK7e40qgv8dMPbadHUnFB0 fBLn6zs5k/nxAv3HZVmRqI+GewfkT1ua42jRn+jg= Received: from DLEE106.ent.ti.com (dlee106.ent.ti.com [157.170.170.36]) by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 40NBEPq0097994 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 23 Jan 2024 05:14:25 -0600 Received: from DLEE114.ent.ti.com (157.170.170.25) by DLEE106.ent.ti.com (157.170.170.36) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 23 Jan 2024 05:14:25 -0600 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DLEE114.ent.ti.com (157.170.170.25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 23 Jan 2024 05:14:25 -0600 Received: from LT5CG31242FY.dhcp.ti.com (lt5cg31242fy.dhcp.ti.com [10.85.14.124]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 40NBEHxQ083823; Tue, 23 Jan 2024 05:14:18 -0600 From: Shenghao Ding To: , , CC: , , , , , , , , <13916275206@139.com>, , , , , , , , , , Shenghao Ding Subject: [PATCH v1 1/4] ASoc: pcm6240: Create pcm6240 codec driver code Date: Tue, 23 Jan 2024 19:14:07 +0800 Message-ID: <20240123111411.850-1-shenghao-ding@ti.com> X-Mailer: git-send-email 2.33.0.windows.2 Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 PCM6240 driver implements a flexible and configurable setting for register and filter coefficients, to one, two or even multiple PCM6240 Family Audio chips. Signed-off-by: Shenghao Ding Change in v1: - Create pcm6240 codec driver code --- sound/soc/codecs/pcm6240.c | 2062 ++++++++++++++++++++++++++++++++++++ 1 file changed, 2062 insertions(+) create mode 100644 sound/soc/codecs/pcm6240.c diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c new file mode 100644 index 000000000000..d9e4d57dc115 --- /dev/null +++ b/sound/soc/codecs/pcm6240.c @@ -0,0 +1,2062 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC/Router +// +// Copyright (C) 2022 - 2024 Texas Instruments Incorporated +// https://www.ti.com +// +// The PCM6240 driver implements a flexible and configurable +// algo coefficient setting for one, two, or even multiple +// PCM6240 Family chips. +// +// Author: Shenghao Ding +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcm6240.h" + +static const struct i2c_device_id pcmdevice_i2c_id[] = { + { "adc3120", ADC3120 }, + { "adc5120", ADC5120 }, + { "adc6120", ADC6120 }, + { "dix4192", DIX4192 }, + { "pcm1690", PCM1690 }, + { "pcm3120", PCM3120 }, + { "pcm3140", PCM3140 }, + { "pcm5120", PCM5120 }, + { "pcm5140", PCM5140 }, + { "pcm6120", PCM6120 }, + { "pcm6140", PCM6140 }, + { "pcm6240", PCM6240 }, + { "pcm6260", PCM6260 }, + { "pcm9211", PCM9211 }, + { "pcmd3140", PCMD3140 }, + { "pcmd3180", PCMD3180 }, + { "pcmd512x", PCMD512X }, + { "taa5212", TAA5212 }, + { "taa5412", TAA5412 }, + { "tad5212", TAD5212 }, + { "tad5412", TAD5412 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, pcmdevice_i2c_id); + +static struct pcmdevice_mixer_control adc5120_analog_gain_ctl[] = { + { + .shift = 1, + .reg = ADC5120_REG_CH1_ANALOG_GAIN, + .max = 0x54, + .invert = 0, + }, + { + .shift = 1, + .reg = ADC5120_REG_CH2_ANALOG_GAIN, + .max = 0x54, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control adc5120_digital_gain_ctl[] = { + { + .shift = 0, + .reg = ADC5120_REG_CH1_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = ADC5120_REG_CH2_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control pcm1690_digital_gain_ctl[] = { + { + .shift = 0, + .reg = PCM1690_REG_CH1_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM1690_REG_CH2_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM1690_REG_CH3_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM1690_REG_CH4_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM1690_REG_CH5_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM1690_REG_CH6_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM1690_REG_CH7_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM1690_REG_CH8_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control pcm6240_analog_gain_ctl[] = { + { + .shift = 2, + .reg = PCM6240_REG_CH1_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + }, + { + .shift = 2, + .reg = PCM6240_REG_CH2_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + }, + { + .shift = 2, + .reg = PCM6240_REG_CH3_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + }, + { + .shift = 2, + .reg = PCM6240_REG_CH4_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control pcm6240_digital_gain_ctl[] = { + { + .shift = 0, + .reg = PCM6240_REG_CH1_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM6240_REG_CH2_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM6240_REG_CH3_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM6240_REG_CH4_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control pcm6260_analog_gain_ctl[] = { + { + .shift = 2, + .reg = PCM6260_REG_CH1_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + }, + { + .shift = 2, + .reg = PCM6260_REG_CH2_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + }, + { + .shift = 2, + .reg = PCM6260_REG_CH3_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + }, + { + .shift = 2, + .reg = PCM6260_REG_CH4_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + }, + { + .shift = 2, + .reg = PCM6260_REG_CH5_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + }, + { + .shift = 2, + .reg = PCM6260_REG_CH6_ANALOG_GAIN, + .max = 0x42, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control pcm6260_digital_gain_ctl[] = { + { + .shift = 0, + .reg = PCM6260_REG_CH1_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM6260_REG_CH2_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM6260_REG_CH3_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM6260_REG_CH4_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM6260_REG_CH5_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM6260_REG_CH6_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control pcm9211_digital_gain_ctl[] = { + { + .shift = 0, + .reg = PCM9211_REG_CH1_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCM9211_REG_CH2_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control pcmd3140_digital_gain_ctl[] = { + { + .shift = 0, + .reg = PCMD3140_REG_CH1_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3140_REG_CH2_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3140_REG_CH3_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3140_REG_CH4_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control pcmd3180_digital_gain_ctl[] = { + { + .shift = 0, + .reg = PCMD3180_REG_CH1_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3180_REG_CH2_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3180_REG_CH3_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3180_REG_CH4_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3180_REG_CH5_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3180_REG_CH6_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3180_REG_CH7_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = PCMD3180_REG_CH8_DIGITAL_GAIN, + .max = 0xff, + .invert = 0, + } +}; + +static struct pcmdevice_mixer_control taa5412_digital_volume_ctl[] = { + { + .shift = 0, + .reg = TAA5412_REG_CH1_DIGITAL_VOLUME, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = TAA5412_REG_CH2_DIGITAL_VOLUME, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = TAA5412_REG_CH3_DIGITAL_VOLUME, + .max = 0xff, + .invert = 0, + }, + { + .shift = 0, + .reg = TAA5412_REG_CH4_DIGITAL_VOLUME, + .max = 0xff, + .invert = 0, + }, +}; + +static struct pcmdevice_mixer_control taa5412_fine_gain_ctl[] = { + { + .shift = 4, + .reg = TAA5412_REG_CH1_FINE_GAIN, + .max = 0xf, + .invert = 0, + }, + { + .shift = 4, + .reg = TAA5412_REG_CH2_FINE_GAIN, + .max = 0xf, + .invert = 0, + }, + { + .shift = 4, + .reg = TAA5412_REG_CH3_FINE_GAIN, + .max = 0xf, + .invert = 4, + }, + { + .shift = 0, + .reg = TAA5412_REG_CH4_FINE_GAIN, + .max = 0xf, + .invert = 4, + }, +}; + +static const DECLARE_TLV_DB_MINMAX_MUTE(pcmd3180_dig_gain_tlv, + -10000, 2700); +static const DECLARE_TLV_DB_MINMAX_MUTE(pcmd3140_dig_gain_tlv, + -10000, 2700); +static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_fine_dig_gain_tlv, + -12750, 0); +static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_dig_gain_tlv, + -25500, 0); +static const DECLARE_TLV_DB_MINMAX_MUTE(pcm9211_dig_gain_tlv, + -11450, 2000); +static const DECLARE_TLV_DB_MINMAX_MUTE(adc5120_fgain_tlv, + -10050, 2700); +static const DECLARE_TLV_DB_LINEAR(adc5120_chgain_tlv, 0, 4200); +static const DECLARE_TLV_DB_MINMAX_MUTE(pcm6260_fgain_tlv, + -10000, 2700); +static const DECLARE_TLV_DB_LINEAR(pcm6260_chgain_tlv, 0, 4200); +static const DECLARE_TLV_DB_MINMAX_MUTE(taa5412_dig_vol_tlv, + -8050, 4700); +static const DECLARE_TLV_DB_LINEAR(taa5412_fine_gain_tlv, + -80, 70); + +static int pcmdev_change_dev(struct pcmdevice_priv *pcm_priv, + unsigned short dev_no) +{ + struct i2c_client *client = (struct i2c_client *)pcm_priv->client; + struct regmap *map = pcm_priv->regmap; + int ret = 0; + + if (client->addr != pcm_priv->addr[dev_no]) { + client->addr = pcm_priv->addr[dev_no]; + /* All pcmdevices share the same regmap, clear the page + * inside regmap once switching to another pcmdevice. + * Register 0 at any pages inside pcmdevice is the same + * one for page-switching. + */ + ret = regmap_write(map, PCMDEVICE_PAGE_SELECT, 0); + if (ret < 0) + dev_err(pcm_priv->dev, "%s, E=%d\n", __func__, ret); + } + + return ret; +} + +static int pcmdev_dev_read(struct pcmdevice_priv *pcm_dev, + unsigned int dev_no, unsigned int reg, unsigned int *val) +{ + int ret = -EINVAL; + + if (dev_no < pcm_dev->ndev) { + struct regmap *map = pcm_dev->regmap; + + ret = pcmdev_change_dev(pcm_dev, dev_no); + if (ret < 0) { + dev_err(pcm_dev->dev, "%s, E=%d\n", __func__, ret); + goto out; + } + + ret = regmap_read(map, reg, val); + if (ret < 0) + dev_err(pcm_dev->dev, "%s, E=%d\n", __func__, ret); + } else + dev_err(pcm_dev->dev, "%s, no such channel(%d)\n", __func__, + dev_no); + + +out: + return ret; +} + +static int pcmdev_dev_bulk_write(struct pcmdevice_priv *pcm_dev, + unsigned int dev_no, unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = -EINVAL; + + if (dev_no < pcm_dev->ndev) { + struct regmap *map = pcm_dev->regmap; + + ret = pcmdev_change_dev(pcm_dev, dev_no); + if (ret < 0) { + dev_err(pcm_dev->dev, "%s, E=%d\n", __func__, ret); + goto out; + } + + ret = regmap_bulk_write(map, reg, data, len); + if (ret < 0) + dev_err(pcm_dev->dev, "bulk_write ERROR, E=%d\n", + ret); + } else + dev_err(pcm_dev->dev, "%s, ERROR: no such channel(%d)\n", + __func__, dev_no); + +out: + return ret; +} + +static int pcmdev_dev_update_bits(struct pcmdevice_priv *pcm_dev, + unsigned int dev_no, unsigned int reg, unsigned int mask, + unsigned int value) +{ + struct regmap *map = pcm_dev->regmap; + int ret = -EINVAL; + + if (dev_no < pcm_dev->ndev) { + ret = pcmdev_change_dev(pcm_dev, dev_no); + if (ret < 0) { + dev_err(pcm_dev->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + + ret = regmap_update_bits(map, reg, mask, value); + if (ret < 0) + dev_err(pcm_dev->dev, "update_bits ERROR, E=%d\n", + ret); + } else + dev_err(pcm_dev->dev, "%s, ERROR: no such device(%d)\n", + __func__, dev_no); + +out: + return ret; +} + +static int pcmdev_dev_write(struct pcmdevice_priv *pcm_dev, + unsigned int dev_no, unsigned int reg, unsigned int value) +{ + struct regmap *map = pcm_dev->regmap; + int ret = 0; + + if (dev_no < pcm_dev->ndev) { + ret = pcmdev_change_dev(pcm_dev, dev_no); + if (ret < 0) { + dev_err(pcm_dev->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + + ret = regmap_write(map, reg, value); + if (ret < 0) + dev_err(pcm_dev->dev, "%s, E=%d\n", __func__, ret); + } else + dev_err(pcm_dev->dev, "%s, ERROR: no such device(%d)\n", + __func__, dev_no); + +out: + return ret; +} + +static int pcmdevice_info_profile( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = max(0, pcm_dev->regbin.ncfgs - 1); + + return 0; +} + +static int pcmdevice_get_profile_id( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pcm_dev->cur_conf; + + return 0; +} + +static int pcmdevice_set_profile_id( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec + = snd_soc_kcontrol_component(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (pcm_dev->cur_conf != ucontrol->value.integer.value[0]) { + pcm_dev->cur_conf = ucontrol->value.integer.value[0]; + ret = 1; + } + + return ret; +} + +static int pcmdevice_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct pcmdevice_mixer_control *mc = + (struct pcmdevice_mixer_control *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max; + return 0; +} + +static int pcmdevice_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(component); + struct pcmdevice_mixer_control *mc = + (struct pcmdevice_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int dev_no = mc->dev_no; + int max = mc->max; + int rc = 0; + unsigned int val; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(max)) - 1; + + + mutex_lock(&pcm_dev->codec_lock); + rc = pcmdev_dev_read(pcm_dev, dev_no, reg, &val); + if (rc) { + dev_err(pcm_dev->dev, "%s:read, ERROR, E=%d\n", + __func__, rc); + goto out; + } + + val = (val >> shift) & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; + ucontrol->value.integer.value[0] = val; +out: + mutex_unlock(&pcm_dev->codec_lock); + return rc; +} + +static int pcmdevice_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(component); + struct pcmdevice_mixer_control *mc = + (struct pcmdevice_mixer_control *)kcontrol->private_value; + int err = 0; + unsigned int reg = mc->reg; + unsigned int dev_no = mc->dev_no; + int max = mc->max; + unsigned int val, val_mask; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(max)) - 1; + + mutex_lock(&pcm_dev->codec_lock); + val = ucontrol->value.integer.value[0] & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; + val_mask = mask << shift; + val = val << shift; + err = pcmdev_dev_update_bits(pcm_dev, dev_no, reg, val_mask, + val); + if (err) { + dev_err(pcm_dev->dev, "%s:update_bits, ERROR, E=%d\n", + __func__, err); + goto out; + } +out: + mutex_unlock(&pcm_dev->codec_lock); + return err; +} + +static int pcm1690_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(component); + struct pcmdevice_mixer_control *mc = + (struct pcmdevice_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int dev_no = mc->dev_no; + int max = mc->max; + int rc = 0; + unsigned int val; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(max)) - 1; + + mutex_lock(&pcm_dev->codec_lock); + rc = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, + &val); + if (rc) { + dev_err(pcm_dev->dev, "%s:read mode, ERROR, E=%d\n", + __func__, rc); + goto out; + } + if (!(val & PCM1690_REG_MODE_CTRL_DAMS_MSK)) { + dev_info(pcm_dev->dev, + "%s: set to wide-range mode, before using this ctrl\n", + __func__); + ucontrol->value.integer.value[0] = -25500; + goto out; + } + rc = pcmdev_dev_read(pcm_dev, dev_no, reg, &val); + if (rc) { + dev_err(pcm_dev->dev, "%s:read, ERROR, E=%d\n", + __func__, rc); + goto out; + } + + val = (val >> shift) & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; + ucontrol->value.integer.value[0] = val; +out: + mutex_unlock(&pcm_dev->codec_lock); + return rc; +} + +static int pcm1690_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(component); + struct pcmdevice_mixer_control *mc = + (struct pcmdevice_mixer_control *)kcontrol->private_value; + int err = 0; + unsigned int reg = mc->reg; + unsigned int dev_no = mc->dev_no; + int max = mc->max; + unsigned int val, val_mask; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(max)) - 1; + + mutex_lock(&pcm_dev->codec_lock); + val = ucontrol->value.integer.value[0] & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; + val_mask = mask << shift; + val = val << shift; + err = pcmdev_dev_update_bits(pcm_dev, dev_no, reg, + PCM1690_REG_MODE_CTRL_DAMS_MSK | val_mask, + PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE | val); + if (err) { + dev_err(pcm_dev->dev, "%s:update_bits, ERROR, E=%d\n", + __func__, err); + goto out; + } +out: + mutex_unlock(&pcm_dev->codec_lock); + return err; +} + +static int pcm1690_get_finevolsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(component); + struct pcmdevice_mixer_control *mc = + (struct pcmdevice_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int dev_no = mc->dev_no; + int max = mc->max; + int rc = 0; + unsigned int val; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(max)) - 1; + + mutex_lock(&pcm_dev->codec_lock); + rc = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL, + &val); + if (rc) { + dev_err(pcm_dev->dev, "%s:read mode, ERROR, E=%d\n", + __func__, rc); + goto out; + } + if (val & PCM1690_REG_MODE_CTRL_DAMS_MSK) { + dev_info(pcm_dev->dev, + "%s: Set to fine mode, before using this ctrl\n", + __func__); + ucontrol->value.integer.value[0] = -12750; + goto out; + } + rc = pcmdev_dev_read(pcm_dev, dev_no, reg, &val); + if (rc) { + dev_err(pcm_dev->dev, "%s:read, ERROR, E=%d\n", + __func__, rc); + goto out; + } + + val = (val >> shift) & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; + ucontrol->value.integer.value[0] = val; +out: + mutex_unlock(&pcm_dev->codec_lock); + return rc; +} + +static int pcm1690_put_finevolsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct pcmdevice_priv *pcm_dev = + snd_soc_component_get_drvdata(component); + struct pcmdevice_mixer_control *mc = + (struct pcmdevice_mixer_control *)kcontrol->private_value; + int err = 0; + unsigned int reg = mc->reg; + unsigned int dev_no = mc->dev_no; + int max = mc->max; + unsigned int val, val_mask; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(max)) - 1; + + mutex_lock(&pcm_dev->codec_lock); + val = ucontrol->value.integer.value[0] & mask; + val = (val > max) ? max : val; + val = mc->invert ? max - val : val; + val_mask = mask << shift; + val = val << shift; + err = pcmdev_dev_update_bits(pcm_dev, dev_no, reg, + PCM1690_REG_MODE_CTRL_DAMS_MSK | val_mask, + PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP | val); + if (err) { + dev_err(pcm_dev->dev, "%s:update_bits, ERROR, E=%d\n", + __func__, err); + goto out; + } +out: + mutex_unlock(&pcm_dev->codec_lock); + return err; +} + +static int pcm1690_ctrl_add(struct pcmdevice_priv *pcm_dev) +{ + struct i2c_adapter *adap = pcm_dev->client->adapter; + struct pcmdev_ctrl_info fine_dig_ctl_info = {0}; + struct pcmdev_ctrl_info dig_ctl_info = {0}; + struct snd_kcontrol_new *pcmdev_controls; + int nr_controls = 1, ret = 0, mix_index = 0, i, chn; + char *name; + + dig_ctl_info.gain = pcm1690_dig_gain_tlv; + dig_ctl_info.pcmdev_ctrl = pcm1690_digital_gain_ctl; + dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcm1690_digital_gain_ctl); + + fine_dig_ctl_info.gain = pcm1690_fine_dig_gain_tlv; + fine_dig_ctl_info.pcmdev_ctrl = pcm1690_digital_gain_ctl; + fine_dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcm1690_digital_gain_ctl); + nr_controls += pcm_dev->ndev * (dig_ctl_info.ctrl_array_size + + fine_dig_ctl_info.ctrl_array_size); + + pcmdev_controls = devm_kzalloc(pcm_dev->dev, + nr_controls * sizeof(*pcmdev_controls), + GFP_KERNEL); + if (!pcmdev_controls) { + pcm_dev->pcm_ctrl.pcmdevice_profile_controls = NULL; + ret = -ENOMEM; + goto out; + } + pcm_dev->pcm_ctrl.pcmdevice_profile_controls = pcmdev_controls; + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(pcm_dev->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "PCMDEVICE Profile id"); + pcmdev_controls[mix_index].name = name; + pcmdev_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + pcmdev_controls[mix_index].info = pcmdevice_info_profile; + pcmdev_controls[mix_index].get = pcmdevice_get_profile_id; + pcmdev_controls[mix_index].put = pcmdevice_set_profile_id; + mix_index++; + + for (i = 0; i < pcm_dev->ndev; i++) { + if (mix_index >= nr_controls) { + dev_dbg(pcm_dev->dev, + "%s: mix_index: %d nr_controls: %d\n", + __func__, mix_index, nr_controls); + break; + } + for (chn = 1; chn <= dig_ctl_info.ctrl_array_size; + chn++) { + if (mix_index >= nr_controls) { + dev_dbg(pcm_dev->dev, + "%s: mix_idx = %d nr_controls = %d\n", + __func__, mix_index, + nr_controls); + break; + } + name = devm_kzalloc(pcm_dev->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s-i2c-%d-dev%d-ch%d-digi-gain", + pcm_dev->dev_name, adap->nr, i, chn); + pcmdev_controls[mix_index].tlv.p = dig_ctl_info.gain; + dig_ctl_info.pcmdev_ctrl[chn - 1].dev_no = i; + pcmdev_controls[mix_index].private_value = + (unsigned long) + &dig_ctl_info.pcmdev_ctrl[chn - 1]; + pcmdev_controls[mix_index].name = name; + pcmdev_controls[mix_index].access = + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE; + pcmdev_controls[mix_index].iface = + SNDRV_CTL_ELEM_IFACE_MIXER; + pcmdev_controls[mix_index].info = pcmdevice_info_volsw; + pcmdev_controls[mix_index].get = pcm1690_get_volsw; + pcmdev_controls[mix_index].put = pcm1690_put_volsw; + mix_index++; + } + } + + for (i = 0; i < pcm_dev->ndev; i++) { + if (mix_index >= nr_controls) { + dev_dbg(pcm_dev->dev, + "%s: mix_index = %d nr_controls = %d\n", + __func__, mix_index, nr_controls); + break; + } + for (chn = 1; chn <= fine_dig_ctl_info.ctrl_array_size; + chn++) { + if (mix_index >= nr_controls) { + dev_dbg(pcm_dev->dev, + "%s: mix_idx = %d nr_controls = %d\n", + __func__, mix_index, nr_controls); + break; + } + name = devm_kzalloc(pcm_dev->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s-i2c-%d-dev%d-ch%d-fine-digi-gain", + pcm_dev->dev_name, adap->nr, i, chn); + pcmdev_controls[mix_index].tlv.p = + fine_dig_ctl_info.gain; + fine_dig_ctl_info.pcmdev_ctrl[chn - 1].dev_no = i; + pcmdev_controls[mix_index].private_value = + (unsigned long) + &fine_dig_ctl_info.pcmdev_ctrl[chn - 1]; + pcmdev_controls[mix_index].name = name; + pcmdev_controls[mix_index].access = + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE; + pcmdev_controls[mix_index].iface = + SNDRV_CTL_ELEM_IFACE_MIXER; + pcmdev_controls[mix_index].info = pcmdevice_info_volsw; + pcmdev_controls[mix_index].get = + pcm1690_get_finevolsw; + pcmdev_controls[mix_index].put = + pcm1690_put_finevolsw; + mix_index++; + } + } + + ret = snd_soc_add_component_controls(pcm_dev->component, + pcmdev_controls, + nr_controls < mix_index ? nr_controls : mix_index); + if (ret) { + dev_err(pcm_dev->dev, + "%s: add_component_controls error = %d\n", + __func__, ret); + goto out; + } + pcm_dev->pcm_ctrl.nr_controls = + nr_controls < mix_index ? nr_controls : mix_index; +out: + return ret; +} + +static void pcm9211_sw_rst(struct pcmdevice_priv *pcm_dev) +{ + int ret, i; + + for (i = 0; i < pcm_dev->ndev; i++) { + ret = pcmdev_dev_update_bits(pcm_dev, i, + PCM9211_REG_SW_CTRL, PCM9211_REG_SW_CTRL_MRST_MSK, + PCM9211_REG_SW_CTRL_MRST); + if (ret < 0) + dev_err(pcm_dev->dev, "%s: dev %d swreset fail, %d\n", + __func__, i, ret); + } +} + +static void pcmdevice_sw_rst(struct pcmdevice_priv *pcm_dev) +{ + int ret, i; + + for (i = 0; i < pcm_dev->ndev; i++) { + ret = pcmdev_dev_write(pcm_dev, i, PCMDEVICE_REG_SWRESET, + PCMDEVICE_REG_SWRESET_RESET); + if (ret < 0) + dev_err(pcm_dev->dev, "%s: dev %d swreset fail, %d\n", + __func__, i, ret); + } +} + +static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt, + const unsigned char *config_data, unsigned int config_size, + int *status) +{ + struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt; + struct pcmdevice_config_info *cfg_info; + struct pcmdevice_block_data **bk_da; + unsigned int config_offset = 0, i; + + cfg_info = kzalloc(sizeof(struct pcmdevice_config_info), GFP_KERNEL); + if (!cfg_info) { + *status = -ENOMEM; + dev_err(pcm_dev->dev, "add config: cfg_info alloc failed!\n"); + goto out; + } + + if (pcm_dev->regbin.fw_hdr.binary_version_num >= 0x105) { + if (config_offset + 64 > (int)config_size) { + *status = -EINVAL; + dev_err(pcm_dev->dev, "add config: Out of boundary\n"); + goto out; + } + memcpy(cfg_info->cfg_name, &config_data[config_offset], 64); + config_offset += 64; + } + + if (config_offset + 4 > config_size) { + *status = -EINVAL; + dev_err(pcm_dev->dev, "add config: Out of boundary\n"); + goto out; + } + cfg_info->nblocks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks, + sizeof(struct pcmdevice_block_data *), GFP_KERNEL); + if (!bk_da) { + *status = -ENOMEM; + goto out; + } + cfg_info->real_nblocks = 0; + for (i = 0; i < cfg_info->nblocks; i++) { + if (config_offset + 12 > config_size) { + *status = -EINVAL; + dev_err(pcm_dev->dev, + "%s: Out of boundary: i = %d nblocks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + bk_da[i] = kzalloc(sizeof(struct pcmdevice_block_data), + GFP_KERNEL); + if (!bk_da[i]) { + *status = -ENOMEM; + break; + } + bk_da[i]->dev_idx = config_data[config_offset]; + config_offset++; + + bk_da[i]->block_type = config_data[config_offset]; + config_offset++; + + if (bk_da[i]->block_type == PCMDEVICE_BIN_BLK_PRE_POWER_UP) { + if (bk_da[i]->dev_idx == 0) + cfg_info->active_dev = + (1 << pcm_dev->ndev) - 1; + else + cfg_info->active_dev = + 1 << (bk_da[i]->dev_idx - 1); + } + + bk_da[i]->yram_checksum = + be16_to_cpup((__be16 *)&config_data[config_offset]); + config_offset += 2; + bk_da[i]->block_size = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + bk_da[i]->n_subblks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + + config_offset += 4; + + if (config_offset + bk_da[i]->block_size > config_size) { + *status = -EINVAL; + dev_err(pcm_dev->dev, + "%s: Out of boundary: i = %d blks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + + bk_da[i]->regdata = kmemdup(&config_data[config_offset], + bk_da[i]->block_size, GFP_KERNEL); + if (!bk_da[i]->regdata) { + *status = -ENOMEM; + goto out; + } + config_offset += bk_da[i]->block_size; + cfg_info->real_nblocks += 1; + } +out: + return cfg_info; +} + +static int pcmdev_ctrl_add(struct pcmdevice_priv *pcm_dev) +{ + struct i2c_adapter *adap = pcm_dev->client->adapter; + struct pcmdev_ctrl_info analog_ctl_info = {0}; + struct pcmdev_ctrl_info dig_ctl_info = {0}; + struct snd_kcontrol_new *pcmdev_controls; + int nr_controls = 1, ret = 0; + int mix_index = 0, dev, chn; + char *name; + + switch (pcm_dev->chip_id) { + case ADC3120: + case ADC5120: + case ADC6120: + case PCM3120: + case PCM5120: + case PCM6120: + dig_ctl_info.gain = adc5120_fgain_tlv; + analog_ctl_info.gain = adc5120_chgain_tlv; + analog_ctl_info.pcmdev_ctrl = adc5120_analog_gain_ctl; + analog_ctl_info.ctrl_array_size = + ARRAY_SIZE(adc5120_analog_gain_ctl); + dig_ctl_info.pcmdev_ctrl = adc5120_digital_gain_ctl; + dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(adc5120_digital_gain_ctl); + break; + case PCM3140: + case PCM5140: + case PCM6140: + case PCM6240: + dig_ctl_info.gain = pcm6260_fgain_tlv; + analog_ctl_info.gain = pcm6260_chgain_tlv; + analog_ctl_info.pcmdev_ctrl = pcm6240_analog_gain_ctl; + analog_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcm6240_analog_gain_ctl); + dig_ctl_info.pcmdev_ctrl = pcm6240_digital_gain_ctl; + dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcm6240_digital_gain_ctl); + break; + case PCM6260: + dig_ctl_info.gain = pcm6260_fgain_tlv; + analog_ctl_info.gain = pcm6260_chgain_tlv; + analog_ctl_info.pcmdev_ctrl = pcm6260_analog_gain_ctl; + analog_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcm6260_analog_gain_ctl); + dig_ctl_info.pcmdev_ctrl = pcm6260_digital_gain_ctl; + dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcm6260_digital_gain_ctl); + break; + case PCM9211: + dig_ctl_info.gain = pcm9211_dig_gain_tlv; + dig_ctl_info.pcmdev_ctrl = pcm9211_digital_gain_ctl; + dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcm9211_digital_gain_ctl); + break; + case PCMD3140: + dig_ctl_info.gain = pcmd3140_dig_gain_tlv; + dig_ctl_info.pcmdev_ctrl = pcmd3140_digital_gain_ctl; + dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcmd3140_digital_gain_ctl); + break; + case PCMD3180: + dig_ctl_info.gain = pcmd3180_dig_gain_tlv; + dig_ctl_info.pcmdev_ctrl = pcmd3180_digital_gain_ctl; + dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(pcmd3180_digital_gain_ctl); + break; + case TAA5212: + case TAA5412: + analog_ctl_info.gain = taa5412_fine_gain_tlv; + analog_ctl_info.pcmdev_ctrl = taa5412_fine_gain_ctl; + analog_ctl_info.ctrl_array_size = + ARRAY_SIZE(taa5412_fine_gain_ctl); + dig_ctl_info.gain = taa5412_dig_vol_tlv; + dig_ctl_info.pcmdev_ctrl = taa5412_digital_volume_ctl; + dig_ctl_info.ctrl_array_size = + ARRAY_SIZE(taa5412_digital_volume_ctl); + break; + } + + nr_controls += pcm_dev->ndev * (dig_ctl_info.ctrl_array_size + + analog_ctl_info.ctrl_array_size); + pcmdev_controls = devm_kzalloc(pcm_dev->dev, + nr_controls * sizeof(struct snd_kcontrol_new), + GFP_KERNEL); + if (!pcmdev_controls) { + ret = -ENOMEM; + pcm_dev->pcm_ctrl.pcmdevice_profile_controls = NULL; + goto out; + } + pcm_dev->pcm_ctrl.pcmdevice_profile_controls = pcmdev_controls; + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(pcm_dev->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "PCMDEVICE Profile id"); + pcmdev_controls[mix_index].name = name; + pcmdev_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + pcmdev_controls[mix_index].info = pcmdevice_info_profile; + pcmdev_controls[mix_index].get = pcmdevice_get_profile_id; + pcmdev_controls[mix_index].put = pcmdevice_set_profile_id; + mix_index++; + + if (analog_ctl_info.ctrl_array_size == 0) + goto dig_ctrl; + + for (dev = 0; dev < pcm_dev->ndev; dev++) { + if (mix_index >= nr_controls) { + dev_dbg(pcm_dev->dev, "mix_idx: %d nr_controls = %d\n", + mix_index, nr_controls); + break; + } + for (chn = 1; chn <= analog_ctl_info.ctrl_array_size; + chn++) { + if (mix_index >= nr_controls) { + dev_dbg(pcm_dev->dev, + "%s: mix_idx = %d nr_controls = %d\n", + __func__, mix_index, + nr_controls); + break; + } + name = devm_kzalloc(pcm_dev->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + (pcm_dev->chip_id != TAA5412) ? + "%s-i2c-%d-dev%d-ch%d-ana-gain" : + "%s-i2c-%d-dev%d-ch%d-fine-gain", + pcm_dev->dev_name, adap->nr, dev, chn); + pcmdev_controls[mix_index].tlv.p = + analog_ctl_info.gain; + analog_ctl_info.pcmdev_ctrl[chn - 1].dev_no = dev; + pcmdev_controls[mix_index].private_value = + (unsigned long) + &analog_ctl_info.pcmdev_ctrl[chn - 1]; + pcmdev_controls[mix_index].name = name; + pcmdev_controls[mix_index].access = + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE; + pcmdev_controls[mix_index].iface = + SNDRV_CTL_ELEM_IFACE_MIXER; + pcmdev_controls[mix_index].info = pcmdevice_info_volsw; + pcmdev_controls[mix_index].get = pcmdevice_get_volsw; + pcmdev_controls[mix_index].put = pcmdevice_put_volsw; + mix_index++; + } + } + +dig_ctrl: + if (dig_ctl_info.ctrl_array_size == 0) + goto add_ctrls; + + for (dev = 0; dev < pcm_dev->ndev; dev++) { + if (mix_index >= nr_controls) { + dev_dbg(pcm_dev->dev, "%s: mix_idx: %d nr_ctrls: %d\n", + __func__, mix_index, nr_controls); + break; + } + for (chn = 1; chn <= dig_ctl_info.ctrl_array_size; chn++) { + if (mix_index >= nr_controls) { + dev_dbg(pcm_dev->dev, + "%s: mix_idx: %d nr_ctrls: %d\n", + __func__, mix_index, nr_controls); + break; + } + name = devm_kzalloc(pcm_dev->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s-i2c-%d-dev%d-ch%d-digi-gain", + pcm_dev->dev_name, adap->nr, dev, chn); + pcmdev_controls[mix_index].tlv.p = dig_ctl_info.gain; + dig_ctl_info.pcmdev_ctrl[chn - 1].dev_no = dev; + pcmdev_controls[mix_index].private_value = + (unsigned long) + &dig_ctl_info.pcmdev_ctrl[chn - 1]; + pcmdev_controls[mix_index].name = name; + pcmdev_controls[mix_index].access = + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE; + pcmdev_controls[mix_index].iface = + SNDRV_CTL_ELEM_IFACE_MIXER; + pcmdev_controls[mix_index].info = pcmdevice_info_volsw; + pcmdev_controls[mix_index].get = pcmdevice_get_volsw; + pcmdev_controls[mix_index].put = pcmdevice_put_volsw; + mix_index++; + } + } + +add_ctrls: + ret = snd_soc_add_component_controls(pcm_dev->component, + pcmdev_controls, + nr_controls < mix_index ? nr_controls : mix_index); + if (ret) { + dev_err(pcm_dev->dev, "%s: add_controls error = %d\n", + __func__, ret); + goto out; + } + pcm_dev->pcm_ctrl.nr_controls = + nr_controls < mix_index ? nr_controls : mix_index; +out: + return ret; +} + +static int pcmdevice_create_controls(struct pcmdevice_priv *pcm_dev) +{ + int ret; + + if (pcm_dev->chip_id == PCM1690) + ret = pcm1690_ctrl_add(pcm_dev); + else + ret = pcmdev_ctrl_add(pcm_dev); + + return ret; +} + +static void pcmdevice_config_info_remove(void *pContext) +{ + struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *) pContext; + struct pcmdevice_regbin *regbin = &(pcm_dev->regbin); + struct pcmdevice_config_info **cfg_info = regbin->cfg_info; + int i, j; + + if (!cfg_info) + return; + for (i = 0; i < regbin->ncfgs; i++) { + if (!cfg_info[i]) + continue; + if (cfg_info[i]->blk_data) { + for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) { + if (!cfg_info[i]->blk_data[j]) + continue; + kfree(cfg_info[i]->blk_data[j]->regdata); + kfree(cfg_info[i]->blk_data[j]); + } + kfree(cfg_info[i]->blk_data); + } + kfree(cfg_info[i]); + } + kfree(cfg_info); +} + +static void pcmdev_regbin_ready(const struct firmware *fmw, void *ctxt) +{ + struct pcmdevice_config_info **cfg_info; + struct pcmdevice_priv *pcm_dev = ctxt; + struct pcmdevice_regbin_hdr *fw_hdr; + struct pcmdevice_regbin *regbin; + unsigned int total_config_sz = 0; + int offset = 0, ret = 0, i; + unsigned char *buf; + + mutex_lock(&pcm_dev->codec_lock); + regbin = &(pcm_dev->regbin); + fw_hdr = &(regbin->fw_hdr); + if (!fmw || !fmw->data) { + dev_err(pcm_dev->dev, "Failed to read %s\n", + pcm_dev->regbin_name); + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; + goto out; + } + buf = (unsigned char *)fmw->data; + + fw_hdr->img_sz = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_hdr->img_sz != fmw->size) { + dev_err(pcm_dev->dev, "File size not match, %d %u", + (int)fmw->size, fw_hdr->img_sz); + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; + goto out; + } + + fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + fw_hdr->binary_version_num = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->binary_version_num < 0x103) { + dev_err(pcm_dev->dev, "Bin version 0x%04x is out of date", + fw_hdr->binary_version_num); + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; + goto out; + } + offset += 4; + fw_hdr->drv_fw_version = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_hdr->plat_type = buf[offset]; + offset += 1; + fw_hdr->dev_family = buf[offset]; + offset += 1; + fw_hdr->reserve = buf[offset]; + offset += 1; + fw_hdr->ndev = buf[offset]; + offset += 1; + if (fw_hdr->ndev != pcm_dev->ndev) { + dev_err(pcm_dev->dev, "Invalid ndev(%u)\n", fw_hdr->ndev); + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; + goto out; + } + + if (offset + PCMDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(pcm_dev->dev, "regbin_ready: Out of boundary!\n"); + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; + goto out; + } + + for (i = 0; i < PCMDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + for (i = 0; i < PCMDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + total_config_sz += fw_hdr->config_size[i]; + } + + if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { + dev_err(pcm_dev->dev, "Bin file error!\n"); + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; + goto out; + } + cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; + goto out; + } + regbin->cfg_info = cfg_info; + regbin->ncfgs = 0; + for (i = 0; i < (int)fw_hdr->nconfig; i++) { + cfg_info[i] = pcmdevice_add_config(ctxt, &buf[offset], + fw_hdr->config_size[i], &ret); + if (ret) { + /* In case the bin file is partially destroyed. */ + if (regbin->ncfgs == 0) + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED; + break; + } + offset += (int)fw_hdr->config_size[i]; + regbin->ncfgs += 1; + } + if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_OK) + pcmdevice_create_controls(pcm_dev); +out: + if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) + pcmdevice_config_info_remove(pcm_dev); + + mutex_unlock(&pcm_dev->codec_lock); + if (fmw) + release_firmware(fmw); +} + +static int pcmdevice_codec_probe(struct snd_soc_component *codec) +{ + struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec); + struct i2c_adapter *adap = pcm_dev->client->adapter; + int ret; + + mutex_lock(&pcm_dev->codec_lock); + pcm_dev->component = codec; + pcm_dev->fw_state = PCMDEVICE_FW_LOAD_OK; + strscpy(pcm_dev->dev_name, pcmdevice_i2c_id[pcm_dev->chip_id].name, + sizeof(pcm_dev->dev_name)); + + /* device-name[defined in pcmdevice_i2c_id]-i2c-bus_id[0,1,...,N]- + * sum[1,2,...,4]dev-reg.bin stores the firmware including register + * setting and params for different filters inside chips, it must be + * copied into firmware folder. The same types of pcmdevices sitting + * on the same i2c bus will be aggregated as one single codec, + * all of them share the same bin file. + */ + scnprintf(pcm_dev->regbin_name, PCMDEVICE_REGBIN_FILENAME_LEN, + "%s-i2c-%d-%udev-reg.bin", pcm_dev->dev_name, adap->nr, + pcm_dev->ndev); + + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + pcm_dev->regbin_name, pcm_dev->dev, GFP_KERNEL, pcm_dev, + pcmdev_regbin_ready); + if (ret) { + dev_err(pcm_dev->dev, "load %s error = %d\n", + pcm_dev->regbin_name, ret); + goto out; + } + + if (pcm_dev->hw_rst) { + gpiod_set_value_cansleep(pcm_dev->hw_rst, 0); + usleep_range(500, 1000); + gpiod_set_value_cansleep(pcm_dev->hw_rst, 1); + } else { + if (pcm_dev->sw_rst) + pcm_dev->sw_rst(pcm_dev); + } + +out: + mutex_unlock(&pcm_dev->codec_lock); + return ret; +} + + +static void pcmdevice_codec_remove(struct snd_soc_component *codec) +{ + struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec); + + if (!pcm_dev) + return; + mutex_lock(&pcm_dev->codec_lock); + pcmdevice_config_info_remove(pcm_dev); + mutex_unlock(&pcm_dev->codec_lock); +} + +static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ASI1 OUT", "ASI1 Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_INPUT("MIC"), +}; + +static const struct snd_soc_dapm_route pcmdevice_audio_map[] = { + {"OUT", NULL, "ASI"}, + {"ASI1 OUT", NULL, "MIC"}, +}; + +static const struct snd_soc_component_driver + soc_codec_driver_pcmdevice = { + .probe = pcmdevice_codec_probe, + .remove = pcmdevice_codec_remove, + .dapm_widgets = pcmdevice_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcmdevice_dapm_widgets), + .dapm_routes = pcmdevice_audio_map, + .num_dapm_routes = ARRAY_SIZE(pcmdevice_audio_map), + .suspend_bias_off = 1, + .idle_bias_on = 0, + .use_pmdown_time = 1, + .endianness = 1, +}; + +static int pcmdevice_process_block(void *ctxt, unsigned char *data, + unsigned char dev_idx, int sublocksize) +{ + struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt; + int subblk_offset = 2, chn, chnend, rc; + unsigned char subblk_typ = data[1]; + + if (dev_idx) { + chn = dev_idx - 1; + chnend = dev_idx; + } else { + chn = 0; + chnend = pcm_dev->ndev; + } + + for (; chn < chnend; chn++) { + switch (subblk_typ) { + case PCMDEVICE_CMD_SING_W: { + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + int i = 0; + + subblk_offset += 2; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(pcm_dev->dev, + "process_block: Out of boundary\n"); + break; + } + + for (i = 0; i < len; i++) { + rc = pcmdev_dev_write(pcm_dev, chn, + PCMDEVICE_REG(data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) + dev_err(pcm_dev->dev, + "single write error\n"); + + subblk_offset += 4; + } + } + break; + case PCMDEVICE_CMD_BURST: { + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(pcm_dev->dev, + "BURST Out of boundary\n"); + break; + } + if (len % 4) { + dev_err(pcm_dev->dev, + "Bst-len(%u)not div by 4\n", len); + break; + } + rc = pcmdev_dev_bulk_write(pcm_dev, chn, + PCMDEVICE_REG(data[subblk_offset + 1], + data[subblk_offset + 2]), + &(data[subblk_offset + 4]), len); + if (rc < 0) + dev_err(pcm_dev->dev, + "bulk_write error = %d\n", rc); + + subblk_offset += (len + 4); + } + break; + case PCMDEVICE_CMD_DELAY: { + unsigned int delay_time = 0; + + if (subblk_offset + 2 > sublocksize) { + dev_err(pcm_dev->dev, + "deley Out of boundary\n"); + break; + } + delay_time = be16_to_cpup((__be16 *)&data[2]) * 1000; + usleep_range(delay_time, delay_time + 50); + subblk_offset += 2; + } + break; + case PCMDEVICE_CMD_FIELD_W: + if (subblk_offset + 6 > sublocksize) { + dev_err(pcm_dev->dev, + "process_block: bit write Out of memory\n"); + break; + } + rc = pcmdev_dev_update_bits(pcm_dev, chn, + PCMDEVICE_REG(data[subblk_offset + 3], + data[subblk_offset + 4]), + data[subblk_offset + 1], + data[subblk_offset + 5]); + if (rc < 0) + dev_err(pcm_dev->dev, + "process_block: update_bits error = %d\n", rc); + + subblk_offset += 6; + break; + default: + break; + } + } + + return subblk_offset; +} + + +static void pcmdevice_select_cfg_blk(void *ctxt, int conf_no, + unsigned char block_type) +{ + struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt; + struct pcmdevice_regbin *regbin = &(pcm_dev->regbin); + struct pcmdevice_config_info **cfg_info = regbin->cfg_info; + struct pcmdevice_block_data **blk_data; + int j, k, chn, chnend; + + if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) { + dev_err(pcm_dev->dev, "conf_no should be less than %u\n", + regbin->ncfgs); + goto out; + } + blk_data = cfg_info[conf_no]->blk_data; + + for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { + unsigned int length = 0, rc = 0; + + if (block_type > 5 || block_type < 2) { + dev_err(pcm_dev->dev, + "block_type should be in range from 2 to 5\n"); + goto out; + } + if (block_type != blk_data[j]->block_type) + continue; + + for (k = 0; k < (int)blk_data[j]->n_subblks; k++) { + if (blk_data[j]->dev_idx) { + chn = blk_data[j]->dev_idx - 1; + chnend = blk_data[j]->dev_idx; + } else { + chn = 0; + chnend = pcm_dev->ndev; + } + + rc = pcmdevice_process_block(pcm_dev, + blk_data[j]->regdata + length, + blk_data[j]->dev_idx, + blk_data[j]->block_size - length); + length += rc; + if (blk_data[j]->block_size < length) { + dev_err(pcm_dev->dev, + "%s: %u %u out of boundary\n", + __func__, length, + blk_data[j]->block_size); + break; + } + } + if (length != blk_data[j]->block_size) + dev_err(pcm_dev->dev, "%s: %u %u size is not same\n", + __func__, length, blk_data[j]->block_size); + } + +out: + return; +} + +static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *codec = dai->component; + struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec); + unsigned char block_type; + + if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) { + dev_err(pcm_dev->dev, "DSP bin file not loaded\n"); + return -EINVAL; + } + + if (mute) + block_type = PCMDEVICE_BIN_BLK_PRE_SHUTDOWN; + else + block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP; + + mutex_lock(&pcm_dev->codec_lock); + pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type); + mutex_unlock(&pcm_dev->codec_lock); + return 0; +} + +static int pcmdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct pcmdevice_priv *pcm_dev = snd_soc_dai_get_drvdata(codec_dai); + + pcm_dev->sysclk = freq; + + return 0; +} + +static int pcmdevice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct pcmdevice_priv *pcm_dev = snd_soc_dai_get_drvdata(dai); + unsigned int fsrate; + unsigned int slot_width; + int bclk_rate; + int rc = 0; + + fsrate = params_rate(params); + switch (fsrate) { + case 48000: + break; + case 44100: + break; + default: + dev_err(pcm_dev->dev, + "%s: incorrect sample rate = %u\n", + __func__, fsrate); + rc = -EINVAL; + goto out; + } + + slot_width = params_width(params); + switch (slot_width) { + case 16: + break; + case 20: + break; + case 24: + break; + case 32: + break; + default: + dev_err(pcm_dev->dev, "%s: incorrect slot width = %u\n", + __func__, slot_width); + rc = -EINVAL; + goto out; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) { + dev_err(pcm_dev->dev, "%s: incorrect bclk rate = %d\n", + __func__, bclk_rate); + rc = bclk_rate; + goto out; + } + +out: + return rc; +} + +static int pcmdevice_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct pcmdevice_priv *pcm_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (pcm_priv->fw_state != PCMDEVICE_FW_LOAD_OK) { + dev_err(pcm_priv->dev, "DSP bin file not loaded\n"); + ret = -EINVAL; + } + + return ret; +} + +static const struct snd_soc_dai_ops pcmdevice_dai_ops = { + .mute_stream = pcmdevice_mute, + .startup = pcmdevice_startup, + .hw_params = pcmdevice_hw_params, + .set_sysclk = pcmdevice_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver pcmdevice_dai_driver[] = { + { + .name = "pcmdevice-codec", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = PCMDEVICE_MAX_CHANNELS, + .rates = PCMDEVICE_RATES, + .formats = PCMDEVICE_FORMATS, + }, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = PCMDEVICE_MAX_CHANNELS, + .rates = PCMDEVICE_RATES, + .formats = PCMDEVICE_FORMATS, + }, + .ops = &pcmdevice_dai_ops, + .symmetric_rate = 1, + } +}; + +#ifdef CONFIG_OF +static const struct of_device_id pcmdevice_of_match[] = { + { .compatible = "ti,adc3120" }, + { .compatible = "ti,adc5120" }, + { .compatible = "ti,adc6120" }, + { .compatible = "ti,dix4192" }, + { .compatible = "ti,pcm1690" }, + { .compatible = "ti,pcm3120" }, + { .compatible = "ti,pcm3140" }, + { .compatible = "ti,pcm5120" }, + { .compatible = "ti,pcm5140" }, + { .compatible = "ti,pcm6120" }, + { .compatible = "ti,pcm6140" }, + { .compatible = "ti,pcm6240" }, + { .compatible = "ti,pcm6260" }, + { .compatible = "ti,pcm9211" }, + { .compatible = "ti,pcmd3140" }, + { .compatible = "ti,pcmd3180" }, + { .compatible = "ti,pcmd512x" }, + { .compatible = "ti,taa5212" }, + { .compatible = "ti,taa5412" }, + { .compatible = "ti,tad5212" }, + { .compatible = "ti,tad5412" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pcmdevice_of_match); +#endif + +static const struct regmap_range_cfg pcmdevice_ranges[] = { + { + .range_min = 0, + .range_max = 256 * 128, + .selector_reg = PCMDEVICE_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config pcmdevice_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .ranges = pcmdevice_ranges, + .num_ranges = ARRAY_SIZE(pcmdevice_ranges), + .max_register = 256 * 128, +}; + +static void pcmdevice_remove(struct pcmdevice_priv *pcm_dev) +{ + if (gpio_is_valid(pcm_dev->irq_info.gpio)) { + gpio_free(pcm_dev->irq_info.gpio); + free_irq(pcm_dev->irq_info.nmb, pcm_dev); + } + mutex_destroy(&pcm_dev->codec_lock); +} + +static int pcmdevice_i2c_probe(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_match_id(pcmdevice_i2c_id, i2c); + struct pcmdevice_priv *pcm_dev; + struct device_node *np; + unsigned int dev_addrs[PCMDEVICE_MAX_DEVICES]; + int ret = 0, i = 0, ndev = 0; +#ifdef CONFIG_OF + const __be32 *reg, *reg_end; + int len, sw, aw; +#endif + + pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL); + if (!pcm_dev) { + ret = -ENOMEM; + goto out; + } + + pcm_dev->chip_id = (id != NULL) ? id->driver_data : 0; + + pcm_dev->dev = &i2c->dev; + pcm_dev->client = i2c; + + if (pcm_dev->chip_id >= MAX_DEVICE) + pcm_dev->chip_id = 0; + + pcm_dev->regmap = devm_regmap_init_i2c(i2c, + &pcmdevice_i2c_regmap); + if (IS_ERR(pcm_dev->regmap)) { + ret = PTR_ERR(pcm_dev->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + goto out; + } + + np = pcm_dev->dev->of_node; +#ifdef CONFIG_OF + aw = of_n_addr_cells(np); + sw = of_n_size_cells(np); + if (sw == 0) { + reg = (const __be32 *)of_get_property(np, + "reg", &len); + reg_end = reg + len/sizeof(*reg); + ndev = 0; + do { + dev_addrs[ndev] = of_read_number(reg, aw); + reg += aw; + ndev++; + } while (reg < reg_end); + } else { + ndev = 1; + dev_addrs[0] = i2c->addr; + } +#else + ndev = 1; + dev_addrs[0] = i2c->addr; +#endif + pcm_dev->irq_info.gpio = of_irq_get(np, 0); + + for (i = 0; i < ndev; i++) + pcm_dev->addr[i] = dev_addrs[i]; + + pcm_dev->ndev = ndev; + + pcm_dev->hw_rst = devm_gpiod_get_optional(&i2c->dev, + "reset-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(pcm_dev->hw_rst)) + dev_dbg(&i2c->dev, "%s: No reset GPIO, no side-effect\n", + __func__); + + if (pcm_dev->chip_id == PCM9211 || pcm_dev->chip_id == PCM1690) + pcm_dev->sw_rst = pcm9211_sw_rst; + else + pcm_dev->sw_rst = pcmdevice_sw_rst; + + if (pcm_dev->chip_id == PCM1690) + goto skip_interrupt; + if (gpio_is_valid(pcm_dev->irq_info.gpio)) { + dev_dbg(pcm_dev->dev, "irq-gpio = %d", + pcm_dev->irq_info.gpio); + + ret = gpio_request(pcm_dev->irq_info.gpio, "PCMDEV-IRQ"); + if (!ret) { + int gpio = pcm_dev->irq_info.gpio; + + gpio_direction_input(gpio); + pcm_dev->irq_info.nmb = gpio_to_irq(gpio); + + } else + dev_err(pcm_dev->dev, "%s: GPIO %d request error\n", + __func__, pcm_dev->irq_info.gpio); + } else + dev_err(pcm_dev->dev, "Looking up irq-gpio failed %d\n", + pcm_dev->irq_info.gpio); + + +skip_interrupt: + mutex_init(&pcm_dev->codec_lock); + + i2c_set_clientdata(i2c, pcm_dev); + + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_driver_pcmdevice, pcmdevice_dai_driver, + ARRAY_SIZE(pcmdevice_dai_driver)); + if (ret < 0) + dev_err(&i2c->dev, "probe register comp failed %d\n", ret); + +out: + if (ret < 0) + pcmdevice_remove(pcm_dev); + return ret; +} + +static void pcmdevice_i2c_remove(struct i2c_client *i2c) +{ + struct pcmdevice_priv *pcm_dev = i2c_get_clientdata(i2c); + + pcmdevice_remove(pcm_dev); +} + +static struct i2c_driver pcmdevice_i2c_driver = { + .driver = { + .name = "pcmdevice-codec", + .of_match_table = of_match_ptr(pcmdevice_of_match), + }, + .probe = pcmdevice_i2c_probe, + .remove = pcmdevice_i2c_remove, + .id_table = pcmdevice_i2c_id, +}; + +module_i2c_driver(pcmdevice_i2c_driver); + +MODULE_AUTHOR("Shenghao Ding "); +MODULE_DESCRIPTION("ASoC PCM6240 Family Audio ADC/DAC/Router Driver"); +MODULE_LICENSE("GPL"); From patchwork Tue Jan 23 11:14:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shenghao Ding X-Patchwork-Id: 13527269 Received: from fllv0015.ext.ti.com (fllv0015.ext.ti.com [198.47.19.141]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E425C5D727; Tue, 23 Jan 2024 11:15:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.19.141 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706008538; cv=none; b=hx4N/C9QV5KvqGGDhBg2wLa0/rulU0Ls8FUy0XKyGnvrp0XnKTVZuRpunY0iYPplbV8WIcIJuA2sZhr6lKHB5CUHpszwLYNUzUCCvQjEdlS7927y6VdT/pPJbhLI0CbEIobJqsnNb/lSxSEY5WmApxIztIYrAm7D2+4T9WGlhK0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706008538; c=relaxed/simple; bh=6ooEC571bZ/oHIxdPh13j6rOggMRmIyjupuL6e9vmaQ=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qBP56DZVeET+GCJ5kAdWYE7xmBdmN7Yj5btqKfu67B5VaYhv2mwU7sOZUXW4lXj0Xrv4kc1WG8EZoZyMmihdtughZ/mSkjTaLPx5umY/aYBdRM+8vUgJ/SW4Dd7WXug11J4VoelTjgKMI1g90iAUQEdnerg+f0UJ6d18Bbvas04= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=nHB5bkP+; arc=none smtp.client-ip=198.47.19.141 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="nHB5bkP+" Received: from lelv0266.itg.ti.com ([10.180.67.225]) by fllv0015.ext.ti.com (8.15.2/8.15.2) with ESMTP id 40NBEXXr117896; Tue, 23 Jan 2024 05:14:33 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1706008473; bh=4Nc6+RtE5VXdMidjdz71LZrSVsobsrRuKc8k9UmvFQ8=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=nHB5bkP+z34s/OgtznwKjDjTejTK8XR7Gge3JWFtwpFbnqPkcdfkiZQ9VRMa/8Y+Z sbbQy4MDoRyCA1qX7bPz8AddQokpCmx9ksEyuJG8FDOaYT+6QX+4y4I/JA4hU0ThqW ejGbCG5196jFwLo2nAcQrGz/8yrrTdXgJUDMg70U= Received: from DLEE103.ent.ti.com (dlee103.ent.ti.com [157.170.170.33]) by lelv0266.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 40NBEXcu054995 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 23 Jan 2024 05:14:33 -0600 Received: from DLEE102.ent.ti.com (157.170.170.32) by DLEE103.ent.ti.com (157.170.170.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 23 Jan 2024 05:14:33 -0600 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DLEE102.ent.ti.com (157.170.170.32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 23 Jan 2024 05:14:33 -0600 Received: from LT5CG31242FY.dhcp.ti.com (lt5cg31242fy.dhcp.ti.com [10.85.14.124]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 40NBEHxR083823; Tue, 23 Jan 2024 05:14:26 -0600 From: Shenghao Ding To: , , CC: , , , , , , , , <13916275206@139.com>, , , , , , , , , , Shenghao Ding Subject: [PATCH v1 2/4] ASoc: pcm6240: Create header file for pcm6240 codec driver code Date: Tue, 23 Jan 2024 19:14:08 +0800 Message-ID: <20240123111411.850-2-shenghao-ding@ti.com> X-Mailer: git-send-email 2.33.0.windows.2 In-Reply-To: <20240123111411.850-1-shenghao-ding@ti.com> References: <20240123111411.850-1-shenghao-ding@ti.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 PCM6240 driver implements a flexible and configurable setting for register and filter coefficients, to one, two or even multiple PCM6240 Family Audio chips. Signed-off-by: Shenghao Ding --- Change in v1: - Create header file for pcm6240 codec driver code --- sound/soc/codecs/pcm6240.h | 235 +++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 sound/soc/codecs/pcm6240.h diff --git a/sound/soc/codecs/pcm6240.h b/sound/soc/codecs/pcm6240.h new file mode 100644 index 000000000000..2d077bd7fc41 --- /dev/null +++ b/sound/soc/codecs/pcm6240.h @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC/Router +// +// Copyright (C) 2022 - 2024 Texas Instruments Incorporated +// https://www.ti.com +// +// The PCM6240 driver implements a flexible and configurable +// algo coefficient setting for one, two, or even multiple +// PCM6240 Family Audio chips. +// +// Author: Shenghao Ding +// + +#ifndef __PCM6240_H__ +#define __PCM6240_H__ + +enum pcm_device { + ADC3120, + ADC5120, + ADC6120, + DIX4192, + PCM1690, + PCM3120, + PCM3140, + PCM5120, + PCM5140, + PCM6120, + PCM6140, + PCM6240, + PCM6260, + PCM9211, + PCMD3140, + PCMD3180, + PCMD512X, + TAA5212, + TAA5412, + TAD5212, + TAD5412, + MAX_DEVICE, +}; + +#define PCMDEVICE_MAX_DEVICES 4 +#define PCMDEVICE_DEVICE_SUM 8 +#define PCMDEVICE_CONFIG_SUM 64 +#define PCMDEVICE_REGBIN_FILENAME_LEN 64 + +#define PCMDEVICE_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000) +#define PCMDEVICE_MAX_CHANNELS 8 +#define PCMDEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/* PAGE Control Register (available in page0 of each book) */ +#define PCMDEVICE_PAGE_SELECT 0x00 +#define PCMDEVICE_REG(page, reg) ((page * 128) + reg) +#define PCMDEVICE_REG_SWRESET PCMDEVICE_REG(0X0, 0x01) +#define PCMDEVICE_REG_SWRESET_RESET BIT(0) + +#define ADC5120_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d) +#define ADC5120_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e) +#define ADC5120_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42) +#define ADC5120_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43) + +#define PCM1690_REG_MODE_CTRL PCMDEVICE_REG(0X0, 0x46) +#define PCM1690_REG_MODE_CTRL_DAMS_MSK BIT(7) +#define PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP 0x0 +#define PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE 0x80 + +#define PCM1690_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48) +#define PCM1690_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x49) +#define PCM1690_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4a) +#define PCM1690_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4b) +#define PCM1690_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4c) +#define PCM1690_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d) +#define PCM1690_REG_CH7_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4e) +#define PCM1690_REG_CH8_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4f) + +#define PCM6240_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d) +#define PCM6240_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e) +#define PCM6240_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42) +#define PCM6240_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43) +#define PCM6240_REG_CH3_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x47) +#define PCM6240_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48) +#define PCM6240_REG_CH4_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x4c) +#define PCM6240_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d) + +#define PCM6260_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d) +#define PCM6260_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e) +#define PCM6260_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42) +#define PCM6260_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43) +#define PCM6260_REG_CH3_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x47) +#define PCM6260_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48) +#define PCM6260_REG_CH4_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x4c) +#define PCM6260_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d) +#define PCM6260_REG_CH5_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x51) +#define PCM6260_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x52) +#define PCM6260_REG_CH6_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x56) +#define PCM6260_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x57) + +#define PCM9211_REG_SW_CTRL PCMDEVICE_REG(0X0, 0x40) +#define PCM9211_REG_SW_CTRL_MRST_MSK BIT(7) +#define PCM9211_REG_SW_CTRL_MRST 0x0 + +#define PCM9211_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x46) +#define PCM9211_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x47) + +#define PCMD3140_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3E) +#define PCMD3140_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43) +#define PCMD3140_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48) +#define PCMD3140_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4D) + +#define PCMD3180_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3E) +#define PCMD3180_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43) +#define PCMD3180_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48) +#define PCMD3180_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4D) +#define PCMD3180_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x52) +#define PCMD3180_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x57) +#define PCMD3180_REG_CH7_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x5C) +#define PCMD3180_REG_CH8_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x61) + +#define TAA5412_REG_CH1_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x52) +#define TAA5412_REG_CH2_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x57) +#define TAA5412_REG_CH3_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x5B) +#define TAA5412_REG_CH4_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x5F) + +#define TAA5412_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x53) +#define TAA5412_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x58) +#define TAA5412_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x5C) +#define TAA5412_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x60) + +#define PCMDEVICE_CMD_SING_W 0x1 +#define PCMDEVICE_CMD_BURST 0x2 +#define PCMDEVICE_CMD_DELAY 0x3 +#define PCMDEVICE_CMD_FIELD_W 0x4 + +enum pcmdevice_bin_blk_type { + PCMDEVICE_BIN_BLK_COEFF = 1, + PCMDEVICE_BIN_BLK_POST_POWER_UP, + PCMDEVICE_BIN_BLK_PRE_SHUTDOWN, + PCMDEVICE_BIN_BLK_PRE_POWER_UP, + PCMDEVICE_BIN_BLK_POST_SHUTDOWN +}; + +enum pcmdevice_fw_state { + PCMDEVICE_FW_LOAD_OK = 0, + PCMDEVICE_FW_LOAD_FAILED +}; + +struct pcmdevice_regbin_hdr { + unsigned int img_sz; + unsigned int checksum; + unsigned int binary_version_num; + unsigned int drv_fw_version; + unsigned int timestamp; + unsigned char plat_type; + unsigned char dev_family; + unsigned char reserve; + unsigned char ndev; + unsigned char devs[PCMDEVICE_MAX_DEVICES]; + unsigned int nconfig; + unsigned int config_size[PCMDEVICE_CONFIG_SUM]; +}; + +struct pcmdevice_block_data { + unsigned char dev_idx; + unsigned char block_type; + unsigned short yram_checksum; + unsigned int block_size; + unsigned int n_subblks; + unsigned char *regdata; +}; + +struct pcmdevice_config_info { + char cfg_name[64]; + unsigned int nblocks; + unsigned int real_nblocks; + unsigned char active_dev; + struct pcmdevice_block_data **blk_data; +}; + +struct pcmdevice_regbin { + struct pcmdevice_regbin_hdr fw_hdr; + int ncfgs; + struct pcmdevice_config_info **cfg_info; +}; + +struct pcm_control { + struct snd_kcontrol_new *pcmdevice_profile_controls; + int nr_controls; +}; + +struct pcmdevice_irqinfo { + int gpio; + int nmb; +}; + +struct pcmdevice_priv { + struct snd_soc_component *component; + struct i2c_client *client; + struct device *dev; + struct mutex codec_lock; + struct gpio_desc *hw_rst; + struct regmap *regmap; + struct pcmdevice_regbin regbin; + struct pcm_control pcm_ctrl; + struct pcmdevice_irqinfo irq_info; + unsigned int addr[PCMDEVICE_MAX_DEVICES]; + unsigned int chip_id; + unsigned int sysclk; + int cur_conf; + int fw_state; + int ndev; + unsigned char regbin_name[PCMDEVICE_REGBIN_FILENAME_LEN]; + unsigned char dev_name[I2C_NAME_SIZE]; + void (*sw_rst)(struct pcmdevice_priv *pcm_dev); +}; + +/* mixer control */ +struct pcmdevice_mixer_control { + int max; + int reg; + unsigned int dev_no; + unsigned int shift; + unsigned int invert; +}; +struct pcmdev_ctrl_info { + const unsigned int *gain; + struct pcmdevice_mixer_control *pcmdev_ctrl; + unsigned int ctrl_array_size; +}; +#endif /* __PCM6240_H__ */ From patchwork Tue Jan 23 11:14:09 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shenghao Ding X-Patchwork-Id: 13527271 Received: from lelv0143.ext.ti.com (lelv0143.ext.ti.com [198.47.23.248]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CC03F5D73C; Tue, 23 Jan 2024 11:15:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.248 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706008548; cv=none; b=dmDG1ePPNFDaO07VKipb9dM20lJx42gBfpbKZHDBvahoQ1g1f2DoZSUDzP5El9bxpAGxAm7DRACLpC5dJEMtNNkezKO5u7eMhWK/W9irPDP1ddsIDuBWClc8tN8tppvkVBQznS8inop4HGbPrS9kAu8Ojdf1hT85LGpslpi2D0c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706008548; c=relaxed/simple; bh=+C+E75lqxharvQbpo3PR/kRPaJd4SUH4GJkUH+xGIvI=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=rI0mAEHmGff0gVdpaIIo8BH+ESH7qauvbCgs3WUd590xk9926gq6vqjFJfoKFtGcB3GFiuHEQOYJWiMh0BVcf/Llz9QNGeIeqHlJkyUmBy3luHDy5euyUVmJMNVHbeSBgQQOwvw+Q+eJIshOhM9mfOtGII4dVpQBxEOq2xy3POI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=h2xoa/xu; arc=none smtp.client-ip=198.47.23.248 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="h2xoa/xu" Received: from fllv0034.itg.ti.com ([10.64.40.246]) by lelv0143.ext.ti.com (8.15.2/8.15.2) with ESMTP id 40NBEfMY103904; Tue, 23 Jan 2024 05:14:41 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1706008481; bh=4gJRUPiOEkN9y57oNCHDaWWI7fyUc/nGZE+6XBvaBP8=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=h2xoa/xu8k3SpZFg8INgl+8P60QQbUFRmIihZAFEZRJdYkTmIPxVN5wUnUBKo4S78 sW4TSXio9CNZcsMXGngwiFnWgj/lcn7JHCv30m3L1qLdyaomy5V5xaptb0HX0gFFV8 GS6jyjmxzzsuU5XFUvNl6ZDKPYqD7xmno7qMetf8= Received: from DFLE104.ent.ti.com (dfle104.ent.ti.com [10.64.6.25]) by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 40NBEfIv098189 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 23 Jan 2024 05:14:41 -0600 Received: from DFLE111.ent.ti.com (10.64.6.32) by DFLE104.ent.ti.com (10.64.6.25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 23 Jan 2024 05:14:41 -0600 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DFLE111.ent.ti.com (10.64.6.32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 23 Jan 2024 05:14:40 -0600 Received: from LT5CG31242FY.dhcp.ti.com (lt5cg31242fy.dhcp.ti.com [10.85.14.124]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 40NBEHxS083823; Tue, 23 Jan 2024 05:14:34 -0600 From: Shenghao Ding To: , , CC: , , , , , , , , <13916275206@139.com>, , , , , , , , , , Shenghao Ding Subject: [PATCH v1 3/4] ASoc: pcm6240: Add compile item for pcm6240 codec driver Date: Tue, 23 Jan 2024 19:14:09 +0800 Message-ID: <20240123111411.850-3-shenghao-ding@ti.com> X-Mailer: git-send-email 2.33.0.windows.2 In-Reply-To: <20240123111411.850-1-shenghao-ding@ti.com> References: <20240123111411.850-1-shenghao-ding@ti.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 PCM6240 driver implements a flexible and configurable setting for register and filter coefficients, to one, two or even multiple PCM6240 Family Audio chips. Signed-off-by: Shenghao Ding --- Change in v1: - Add compile item for pcm6240 codec driver --- sound/soc/codecs/Kconfig | 10 ++++++++++ sound/soc/codecs/Makefile | 2 ++ 2 files changed, 12 insertions(+) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 59f9742e9ff4..bab0ed032b5d 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -178,6 +178,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_PCM5102A imply SND_SOC_PCM512x_I2C imply SND_SOC_PCM512x_SPI + imply SND_SOC_PCM6240 imply SND_SOC_PEB2466 imply SND_SOC_RK3328 imply SND_SOC_RK817 @@ -1389,6 +1390,15 @@ config SND_SOC_PCM512x_SPI select SND_SOC_PCM512x select REGMAP_SPI +config SND_SOC_PCM6240 + tristate "Texas Instruments PCM6240 Family Audio chips based on I2C" + depends on I2C + help + Enable support for Texas Instruments PCM6240 Family Audio chips. + Note the PCM6240 driver implements a flexible and configurable + setting for register and filter coefficients, to one, two or + even multiple PCM6240 Family Audio chips. + config SND_SOC_PEB2466 tristate "Infineon PEB2466 quad PCM codec" depends on SPI diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f53baa2b9565..c2ae573b62dd 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -201,6 +201,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o +snd-soc-pcm6240-objs := pcm6240.o snd-soc-peb2466-objs := peb2466.o snd-soc-rk3328-objs := rk3328_codec.o snd-soc-rk817-objs := rk817_codec.o @@ -586,6 +587,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o +obj-$(CONFIG_SND_SOC_PCM6240) += snd-soc-pcm6240.o obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o From patchwork Tue Jan 23 11:14:10 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Shenghao Ding X-Patchwork-Id: 13527270 Received: from lelv0142.ext.ti.com (lelv0142.ext.ti.com [198.47.23.249]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D669A5D8E3; Tue, 23 Jan 2024 11:15:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=198.47.23.249 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706008543; cv=none; b=de6dImS3Uc9ufWsoKU/O9IYxjV5siBYOeq4Rn92d8EjE3x9y2lEclwWfTLCJF0lh/mIEcVMjNnuaLjRISawiNzv/8kS9GGhacnEtJsrrWjkUcvC5RFqPTZRCZHV0IIO1GzNbdZSzQdNjjaWtHfr9BV+p9SJy5M90ikdo2bZLMp0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706008543; c=relaxed/simple; bh=CQ7tFTTrMu33i0T+tPoAxNwOmkHva4ezG2UGpbm12ZI=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=TbA+ibRgdJvVzbmmf29rfwBFo8MLyeIVKfSKjTarrHZMGyHU4opIH5mjD2jalVM5LJGDNNB7IVY+PEcrWUpIXeuhHFNDReIZrQNM+W7Nnm7A29PEWLRg+XaWpI458RgrAJsnoDzL/aZ1DdvoarzcWnhz0LtGrwUc0yP3he/G9l0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com; spf=pass smtp.mailfrom=ti.com; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b=R3vVvDrI; arc=none smtp.client-ip=198.47.23.249 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ti.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ti.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ti.com header.i=@ti.com header.b="R3vVvDrI" Received: from fllv0034.itg.ti.com ([10.64.40.246]) by lelv0142.ext.ti.com (8.15.2/8.15.2) with ESMTP id 40NBEnQD008311; Tue, 23 Jan 2024 05:14:49 -0600 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ti.com; s=ti-com-17Q1; t=1706008489; bh=zk2vPEHry0BmB+vqIbKpDwwWZQyauRfgfOziNQ6D3K4=; h=From:To:CC:Subject:Date:In-Reply-To:References; b=R3vVvDrIAUP2aScn40NFAkO80XNzE6HzCco+omq9bNL/zwdHBEDzTGxkZACGteRCe 5u408G1ROsV5CvO7lkKTwVdfN0WTidITRxvrw+BcCDn1+vkyZnxMYmjwnqbGzP9cBZ Q8f+4brlT0iZrC0D9VpVsd7gD1fJ5MaA2idbabBg= Received: from DLEE104.ent.ti.com (dlee104.ent.ti.com [157.170.170.34]) by fllv0034.itg.ti.com (8.15.2/8.15.2) with ESMTPS id 40NBEnlO098291 (version=TLSv1.2 cipher=AES256-GCM-SHA384 bits=256 verify=FAIL); Tue, 23 Jan 2024 05:14:49 -0600 Received: from DLEE110.ent.ti.com (157.170.170.21) by DLEE104.ent.ti.com (157.170.170.34) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23; Tue, 23 Jan 2024 05:14:48 -0600 Received: from lelvsmtp6.itg.ti.com (10.180.75.249) by DLEE110.ent.ti.com (157.170.170.21) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2507.23 via Frontend Transport; Tue, 23 Jan 2024 05:14:49 -0600 Received: from LT5CG31242FY.dhcp.ti.com (lt5cg31242fy.dhcp.ti.com [10.85.14.124]) by lelvsmtp6.itg.ti.com (8.15.2/8.15.2) with ESMTP id 40NBEHxT083823; Tue, 23 Jan 2024 05:14:42 -0600 From: Shenghao Ding To: , , CC: , , , , , , , , <13916275206@139.com>, , , , , , , , , , Shenghao Ding Subject: [PATCH v1 4/4] ASoc: dt-bindings: Create yaml file for pcm6240 codec driver Date: Tue, 23 Jan 2024 19:14:10 +0800 Message-ID: <20240123111411.850-4-shenghao-ding@ti.com> X-Mailer: git-send-email 2.33.0.windows.2 In-Reply-To: <20240123111411.850-1-shenghao-ding@ti.com> References: <20240123111411.850-1-shenghao-ding@ti.com> Precedence: bulk X-Mailing-List: linux-sound@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-EXCLAIMER-MD-CONFIG: e1e8a2fd-e40a-4ac6-ac9b-f7e9cc9ee180 PCM6240 driver implements a flexible and configurable setting for register and filter coefficients, to one, two or even multiple PCM6240 Family Audio chips. Signed-off-by: Shenghao Ding --- Change in v1: - Create yaml file for pcm6240 codec driver --- .../devicetree/bindings/sound/ti,pcm6240.yaml | 303 ++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/ti,pcm6240.yaml diff --git a/Documentation/devicetree/bindings/sound/ti,pcm6240.yaml b/Documentation/devicetree/bindings/sound/ti,pcm6240.yaml new file mode 100644 index 000000000000..59fd48aa4445 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ti,pcm6240.yaml @@ -0,0 +1,303 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2022 - 2024 Texas Instruments Incorporated +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/ti,pcm6240.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments PCM6240 Family Audio ADC/DAC/Router + +maintainers: + - Shenghao Ding + +description: | + The PCM6240 Family driver offer a flexible architecture to set the device + number, registers and params for different filters in a bin file. + + Specifications about the audio chip can be found at: + https://www.ti.com/lit/gpn/tlv320adc3120 + https://www.ti.com/lit/gpn/tlv320adc5120 + https://www.ti.com/lit/gpn/tlv320adc6120 + https://www.ti.com/lit/gpn/dix4192 + https://www.ti.com/lit/gpn/pcm1690 + https://www.ti.com/lit/gpn/pcm3120-q1 + https://www.ti.com/lit/gpn/pcm3140-q1 + https://www.ti.com/lit/gpn/pcm5120-q1 + https://www.ti.com/lit/gpn/pcm6120-q1 + https://www.ti.com/lit/gpn/pcm6260-q1 + https://www.ti.com/lit/gpn/pcm9211 + https://www.ti.com/lit/gpn/pcmd3140 + https://www.ti.com/lit/gpn/pcmd3180 + https://www.ti.com/lit/gpn/taa5212 + https://www.ti.com/lit/gpn/tad5212 + +properties: + compatible: + description: | + ti,adc3120: Stereo-channel, 768-kHz, Burr-Brown™ audio analog-to- + digital converter (ADC) with 106-dB SNR. + + ti,adc5120: 2-Channel, 768-kHz, Burr-BrownTM Audio ADC with 120-dB SNR. + + ti,adc6120: Stereo-channel, 768-kHz, Burr-Brown™ audio analog-to- + digital converter (ADC) with 123-dB SNR. + + ti,pcm1690: 113dB SNR, 24-Bit, 192-kHz Sampling, Enhanced Multi-Level + ?S, Eight-Channel Audio Digital-to-Analog Converter with Differential + Outputs. + + ti,pcm3120: Automotive, stereo, 106-dB SNR, 768-kHz, low-power + software-controlled audio ADC. + + ti,pcm3140: Automotive, Quad-Channel, 768-kHz, Burr-BrownTM Audio ADC + with 106-dB SNR. + + ti,pcm5120: Automotive, stereo, 120-dB SNR, 768-kHz, low-power + software-controlled audio ADC. + + ti,pcm5140: Automotive, Quad-Channel, 768-kHz, Burr-BrownTM Audio ADC + with 120-dB SNR. + + ti,pcm6120: Automotive, stereo, 123-dB SNR, 768-kHz, low-power + software-controlled audio ADC. + + ti,pcm6140: Automotive, Quad-Channel, 768-kHz, Burr-BrownTM Audio ADC + with 123-dB SNR. + + ti,pcm6240: Automotive 4-ch audio ADC with integrated programmable mic + bias, boost and input diagnostics. + + ti,pcm6260: Automotive 6-ch audio ADC with integrated programmable mic + bias, boost and input diagnostics. + + ti,pcm9211: 216-kHz Digital Audio Interface Transceiver (DIX) + With Stereo ADC and Routing. + + ti,pcmd3140: Four-channel PDM-input to TDM or I�S output converter. + + ti,pcmd3180: Eight-channel pulse-density-modulation input to TDM or + I�S output converter. + + ti,taa5212: Low-power high-performance stereo audio ADC with 118-dB + dynamic range. + + ti,tad5212: Low-power stereo audio DAC with 120-dB dynamic range. + enum: + - ti,adc3120 + - ti,adc5120 + - ti,adc6120 + - ti,dix4192 + - ti,pcm1690 + - ti,pcm3120 + - ti,pcm3140 + - ti,pcm5120 + - ti,pcm5140 + - ti,pcm6120 + - ti,pcm6140 + - ti,pcm6240 + - ti,pcm6260 + - ti,pcm9211 + - ti,pcmd3140 + - ti,pcmd3180 + - ti,pcmd512x + - ti,taa5212 + - ti,taa5412 + - ti,tad5212 + - ti,tad5412 + + reg: + description: + I2C address, in multiple pcmdevices case, all the i2c address + aggregate as one Audio Device to support multiple audio slots. + maxItems: 4 + minItems: 1 + + reset-gpios: + maxItems: 1 + description: + A GPIO line handling reset of the chip. As the line is active high, + it should be marked GPIO_ACTIVE_HIGH. + + interrupts: + maxItems: 1 + description: + Invalid only for ti,pcm1690 because of no INT pin. + + '#sound-dai-cells': + const: 0 + +required: + - compatible + - reg + +allOf: + - $ref: dai-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - ti,pcm1690 + then: + properties: + reg: + description: + I2C address, in multiple pcmdevices case, all the i2c address + aggregate as one Audio Device to support multiple audio slots. + maxItems: 4 + minItems: 1 + items: + minimum: 0x4c + maximum: 0x4f + interrupts: false + + - if: + properties: + compatible: + contains: + enum: + - ti,pcm3140 + - ti,pcm5140 + - ti,pcm6140 + - ti,pcmd3180 + then: + properties: + reg: + description: + I2C address, in multiple pcmdevices case, all the i2c address + aggregate as one Audio Device to support multiple audio slots. + maxItems: 4 + minItems: 1 + items: + minimum: 0x4c + maximum: 0x4f + + - if: + properties: + compatible: + contains: + enum: + - ti,adc3120 + - ti,adc5120 + - ti,adc6120 + - ti,pcm3120 + - ti,pcm5120 + - ti,pcm6120 + - ti,pcmd3140 + then: + properties: + reg: + description: + I2C address. + maxItems: 1 + items: + maximum: 0x4e + + - if: + properties: + compatible: + contains: + enum: + - ti,dix4192 + then: + properties: + reg: + description: + I2C address, in multiple pcmdevices case, all the i2c address + aggregate as one Audio Device to support multiple audio slots. + maxItems: 4 + minItems: 1 + items: + minimum: 0x70 + maximum: 0x73 + + - if: + properties: + compatible: + contains: + enum: + - ti,pcm6240 + - ti,pcm6260 + then: + properties: + reg: + description: + I2C address, in multiple pcmdevices case, all the i2c address + aggregate as one Audio Device to support multiple audio slots. + maxItems: 4 + minItems: 1 + items: + minimum: 0x48 + maximum: 0x4b + + - if: + properties: + compatible: + contains: + enum: + - ti,pcm9211 + then: + properties: + reg: + description: + I2C address, in multiple pcmdevices case, all the i2c address + aggregate as one Audio Device to support multiple audio slots. + maxItems: 4 + minItems: 1 + items: + minimum: 0x40 + maximum: 0x43 + + - if: + properties: + compatible: + contains: + enum: + - ti,taa5212 + - ti,taa5412 + - ti,tad5212 + - ti,tad5412 + then: + properties: + reg: + description: + I2C address, in multiple pcmdevices case, all the i2c address + aggregate as one Audio Device to support multiple audio slots. + maxItems: 4 + minItems: 1 + items: + minimum: 0x50 + maximum: 0x53 +additionalProperties: false + +examples: + - | + #include + i2c { + /* example for two devices with interrupt support */ + #address-cells = <1>; + #size-cells = <0>; + two: pcmdevice@48 { + compatible = "ti,pcm6240"; + reg = <0x48>, /* primary-device */ + <0x4b>; /* secondary-device */ + #sound-dai-cells = <0>; + reset-gpios = < &gpio1 10 GPIO_ACTIVE_HIGH >; + interrupt-parent = <&gpio1>; + interrupts = <15>; + }; + }; + - | + #include + i2c { + /* example for one device without interrupt support*/ + #address-cells = <1>; + #size-cells = <0>; + one: pcmdevice@4c { + compatible = "ti,pcmd3180"; + reg = <0x4c>; + #sound-dai-cells = <0>; + reset-gpios = < &gpio1 10 GPIO_ACTIVE_HIGH >; + }; + }; +...