ASoC: codecs: tas5720: add support for TAS5720A-Q1 (automotive) variant
authorSteffen Aschbacher <steffen.aschbacher@stihl.de>
Sat, 28 Jan 2023 08:27:42 +0000 (10:27 +0200)
committerMark Brown <broonie@kernel.org>
Mon, 30 Jan 2023 13:35:15 +0000 (13:35 +0000)
This change adds support the TAS5720A-Q1 audio codec, in the same driver as
tas5720.

Functionally, this driver is pretty similar to it's TAS5720x variant.

The first 3 registers are the same, so the main control and device
identification can happen with these registers.

The next registers differ.
This variant offers control (in the registers) for 2 speakers, which is
implemented here (in a basic manner).

Signed-off-by: Steffen Aschbacher <steffen.aschbacher@stihl.de>
Signed-off-by: Alexandru Ardelean <alex@shruggie.ro>
Link: https://lore.kernel.org/r/20230128082744.41849-2-alex@shruggie.ro
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/tas5720.c
sound/soc/codecs/tas5720.h

index 0587c9b..2ad8fb2 100644 (file)
@@ -30,6 +30,7 @@
 
 enum tas572x_type {
        TAS5720,
+       TAS5720A_Q1,
        TAS5722,
 };
 
@@ -166,17 +167,26 @@ static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
                return -EINVAL;
        }
 
-       /* Enable manual TDM slot selection (instead of I2C ID based) */
-       ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
-                                 TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
-       if (ret < 0)
-               goto error_snd_soc_component_update_bits;
+       /*
+        * Enable manual TDM slot selection (instead of I2C ID based).
+        * This is not applicable to TAS5720A-Q1.
+        */
+       switch (tas5720->devtype) {
+       case TAS5720A_Q1:
+               break;
+       default:
+               ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
+                                         TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
+               if (ret < 0)
+                       goto error_snd_soc_component_update_bits;
 
-       /* Configure the TDM slot to process audio from */
-       ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
-                                 TAS5720_TDM_SLOT_SEL_MASK, first_slot);
-       if (ret < 0)
-               goto error_snd_soc_component_update_bits;
+               /* Configure the TDM slot to process audio from */
+               ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
+                                         TAS5720_TDM_SLOT_SEL_MASK, first_slot);
+               if (ret < 0)
+                       goto error_snd_soc_component_update_bits;
+               break;
+       }
 
        /* Configure TDM slot width. This is only applicable to TAS5722. */
        switch (tas5720->devtype) {
@@ -201,10 +211,22 @@ error_snd_soc_component_update_bits:
 
 static int tas5720_mute_soc_component(struct snd_soc_component *component, int mute)
 {
+       struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+       unsigned int reg, mask;
        int ret;
 
-       ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
-                                 TAS5720_MUTE, mute ? TAS5720_MUTE : 0);
+       switch (tas5720->devtype) {
+       case TAS5720A_Q1:
+               reg = TAS5720_Q1_VOLUME_CTRL_CFG_REG;
+               mask = TAS5720_Q1_MUTE;
+               break;
+       default:
+               reg = TAS5720_DIGITAL_CTRL2_REG;
+               mask = TAS5720_MUTE;
+               break;
+       }
+
+       ret = snd_soc_component_update_bits(component, reg, mask, mute ? mask : 0);
        if (ret < 0) {
                dev_err(component->dev, "error (un-)muting device: %d\n", ret);
                return ret;
@@ -309,6 +331,9 @@ static int tas5720_codec_probe(struct snd_soc_component *component)
        case TAS5720:
                expected_device_id = TAS5720_DEVICE_ID;
                break;
+       case TAS5720A_Q1:
+               expected_device_id = TAS5720A_Q1_DEVICE_ID;
+               break;
        case TAS5722:
                expected_device_id = TAS5722_DEVICE_ID;
                break;
@@ -474,6 +499,15 @@ static const struct regmap_config tas5720_regmap_config = {
        .volatile_reg = tas5720_is_volatile_reg,
 };
 
+static const struct regmap_config tas5720a_q1_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = TAS5720_MAX_REG,
+       .cache_type = REGCACHE_RBTREE,
+       .volatile_reg = tas5720_is_volatile_reg,
+};
+
 static const struct regmap_config tas5722_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
@@ -495,6 +529,16 @@ static const DECLARE_TLV_DB_RANGE(dac_analog_tlv,
 );
 
 /*
+ * DAC analog gain for TAS5720A-Q1. There are three discrete values to select from, ranging
+ * from 19.2 dB to 25.0dB.
+ */
+static const DECLARE_TLV_DB_RANGE(dac_analog_tlv_a_q1,
+       0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
+       0x1, 0x1, TLV_DB_SCALE_ITEM(2260, 0, 0),
+       0x2, 0x2, TLV_DB_SCALE_ITEM(2500, 0, 0),
+);
+
+/*
  * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps
  * depending on the device. Note that setting the gain below -100 dB
  * (register value <0x7) is effectively a MUTE as per device datasheet.
@@ -540,6 +584,15 @@ static const struct snd_kcontrol_new tas5720_snd_controls[] = {
                       TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
 };
 
+static const struct snd_kcontrol_new tas5720a_q1_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume",
+                               TAS5720_Q1_VOLUME_CTRL_LEFT_REG,
+                               TAS5720_Q1_VOLUME_CTRL_RIGHT_REG,
+                               0, 0xff, 0, tas5720_dac_tlv),
+       SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
+                               TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv_a_q1),
+};
+
 static const struct snd_kcontrol_new tas5722_snd_controls[] = {
        SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume",
                           0, 0, 511, 0,
@@ -577,6 +630,22 @@ static const struct snd_soc_component_driver soc_component_dev_tas5720 = {
        .endianness             = 1,
 };
 
+static const struct snd_soc_component_driver soc_component_dev_tas5720_a_q1 = {
+       .probe                  = tas5720_codec_probe,
+       .remove                 = tas5720_codec_remove,
+       .suspend                = tas5720_suspend,
+       .resume                 = tas5720_resume,
+       .controls               = tas5720a_q1_snd_controls,
+       .num_controls           = ARRAY_SIZE(tas5720a_q1_snd_controls),
+       .dapm_widgets           = tas5720_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(tas5720_dapm_widgets),
+       .dapm_routes            = tas5720_audio_map,
+       .num_dapm_routes        = ARRAY_SIZE(tas5720_audio_map),
+       .idle_bias_on           = 1,
+       .use_pmdown_time        = 1,
+       .endianness             = 1,
+};
+
 static const struct snd_soc_component_driver soc_component_dev_tas5722 = {
        .probe = tas5720_codec_probe,
        .remove = tas5720_codec_remove,
@@ -636,6 +705,7 @@ static struct snd_soc_dai_driver tas5720_dai[] = {
 
 static const struct i2c_device_id tas5720_id[] = {
        { "tas5720", TAS5720 },
+       { "tas5720a-q1", TAS5720A_Q1 },
        { "tas5722", TAS5722 },
        { }
 };
@@ -662,6 +732,9 @@ static int tas5720_probe(struct i2c_client *client)
        case TAS5720:
                regmap_config = &tas5720_regmap_config;
                break;
+       case TAS5720A_Q1:
+               regmap_config = &tas5720a_q1_regmap_config;
+               break;
        case TAS5722:
                regmap_config = &tas5722_regmap_config;
                break;
@@ -695,6 +768,12 @@ static int tas5720_probe(struct i2c_client *client)
                                        tas5720_dai,
                                        ARRAY_SIZE(tas5720_dai));
                break;
+       case TAS5720A_Q1:
+               ret = devm_snd_soc_register_component(&client->dev,
+                                       &soc_component_dev_tas5720_a_q1,
+                                       tas5720_dai,
+                                       ARRAY_SIZE(tas5720_dai));
+               break;
        case TAS5722:
                ret = devm_snd_soc_register_component(&client->dev,
                                        &soc_component_dev_tas5722,
@@ -716,6 +795,7 @@ static int tas5720_probe(struct i2c_client *client)
 #if IS_ENABLED(CONFIG_OF)
 static const struct of_device_id tas5720_of_match[] = {
        { .compatible = "ti,tas5720", },
+       { .compatible = "ti,tas5720a-q1", },
        { .compatible = "ti,tas5722", },
        { },
 };
index 223858f..b384595 100644 (file)
@@ -10,7 +10,7 @@
 #ifndef __TAS5720_H__
 #define __TAS5720_H__
 
-/* Register Address Map */
+/* Register Address Map - first 3 regs are common for all variants */
 #define TAS5720_DEVICE_ID_REG          0x00
 #define TAS5720_POWER_CTRL_REG         0x01
 #define TAS5720_DIGITAL_CTRL1_REG      0x02
 #define TAS5722_ANALOG_CTRL2_REG       0x14
 #define TAS5722_MAX_REG                        TAS5722_ANALOG_CTRL2_REG
 
+/* Register Address Map - volume controls for the TAS5720-Q1 variant */
+#define TAS5720_Q1_VOLUME_CTRL_CFG_REG         0x03
+#define TAS5720_Q1_VOLUME_CTRL_LEFT_REG                0x04
+#define TAS5720_Q1_VOLUME_CTRL_RIGHT_REG       0x05
+
 /* TAS5720_DEVICE_ID_REG */
+#define TAS5720A_Q1_DEVICE_ID          0x00
 #define TAS5720_DEVICE_ID              0x01
 #define TAS5722_DEVICE_ID              0x12
 
 #define TAS5720_MUTE                   BIT(4)
 #define TAS5720_TDM_SLOT_SEL_MASK      GENMASK(2, 0)
 
+/* TAS5720_Q1_VOLUME_CTRL_CFG_REG */
+#define TAS5720_Q1_FADE                        BIT(7)
+#define TAS5720_Q1_MUTE                        GENMASK(1, 0)
+
 /* TAS5720_ANALOG_CTRL_REG */
 #define TAS5720_PWM_RATE_6_3_FSYNC     (0x0 << 4)
 #define TAS5720_PWM_RATE_8_4_FSYNC     (0x1 << 4)
@@ -70,6 +80,9 @@
 #define TAS5720_ANALOG_GAIN_MASK       GENMASK(3, 2)
 #define TAS5720_ANALOG_GAIN_SHIFT      (0x2)
 
+/* TAS5720_Q1_ANALOG_CTRL_REG */
+#define TAS5720_Q1_CHAN_SEL            BIT(1)
+
 /* TAS5720_FAULT_REG */
 #define TAS5720_OC_THRESH_100PCT       (0x0 << 4)
 #define TAS5720_OC_THRESH_75PCT                (0x1 << 4)