Merge remote-tracking branches 'asoc/topic/stm32', 'asoc/topic/sun4i-i2s', 'asoc...
authorMark Brown <broonie@kernel.org>
Thu, 18 Jan 2018 11:56:17 +0000 (11:56 +0000)
committerMark Brown <broonie@kernel.org>
Thu, 18 Jan 2018 11:56:17 +0000 (11:56 +0000)
Documentation/devicetree/bindings/sound/st,stm32-sai.txt
Documentation/devicetree/bindings/sound/sun4i-i2s.txt
Documentation/devicetree/bindings/sound/tas5720.txt
include/sound/soc-dai.h
sound/soc/codecs/tas5720.c
sound/soc/codecs/tas5720.h
sound/soc/codecs/wm2200.c
sound/soc/stm/stm32_sai.c
sound/soc/sunxi/sun4i-codec.c
sound/soc/sunxi/sun4i-i2s.c

index 1f9cd7095337881b5ee97ef51e9143efcbc0574e..b1acc1a256baacb81f46d02286b9a5664d088333 100644 (file)
@@ -20,11 +20,6 @@ Required properties:
 
 Optional properties:
   - resets: Reference to a reset controller asserting the SAI
-  - st,sync: specify synchronization mode.
-       By default SAI sub-block is in asynchronous mode.
-       This property sets SAI sub-block as slave of another SAI sub-block.
-       Must contain the phandle and index of the sai sub-block providing
-       the synchronization.
 
 SAI subnodes:
 Two subnodes corresponding to SAI sub-block instances A et B can be defined.
@@ -44,6 +39,13 @@ SAI subnodes required properties:
   - pinctrl-names: should contain only value "default"
   - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
 
+SAI subnodes Optional properties:
+  - st,sync: specify synchronization mode.
+       By default SAI sub-block is in asynchronous mode.
+       This property sets SAI sub-block as slave of another SAI sub-block.
+       Must contain the phandle and index of the sai sub-block providing
+       the synchronization.
+
 The device node should contain one 'port' child node with one child 'endpoint'
 node, according to the bindings defined in Documentation/devicetree/bindings/
 graph.txt.
index 05d7135a8d2ffa7bb3732dd013f0b47c4dcfa9a8..b9d50d6cdef30cb402f354c11958f3c77a759a22 100644 (file)
@@ -8,6 +8,7 @@ Required properties:
 - compatible: should be one of the following:
    - "allwinner,sun4i-a10-i2s"
    - "allwinner,sun6i-a31-i2s"
+   - "allwinner,sun8i-a83t-i2s"
    - "allwinner,sun8i-h3-i2s"
 - reg: physical base address of the controller and length of memory mapped
   region.
@@ -23,6 +24,7 @@ Required properties:
 
 Required properties for the following compatibles:
        - "allwinner,sun6i-a31-i2s"
+       - "allwinner,sun8i-a83t-i2s"
        - "allwinner,sun8i-h3-i2s"
 - resets: phandle to the reset line for this codec
 
index 40d94f82beb364c1056e75d9f8e089368f63951a..7481653fe8e3eba498667dcebe9afb8d97dc3391 100644 (file)
@@ -6,10 +6,12 @@ audio playback. For more product information please see the links below:
 
 http://www.ti.com/product/TAS5720L
 http://www.ti.com/product/TAS5720M
+http://www.ti.com/product/TAS5722L
 
 Required properties:
 
-- compatible : "ti,tas5720"
+- compatible : "ti,tas5720",
+               "ti,tas5722"
 - reg : I2C slave address
 - dvdd-supply : phandle to a 3.3-V supply for the digital circuitry
 - pvdd-supply : phandle to a supply used for the Class-D amp and the analog
index d970879944fc32e94175a8dfc8652f6c14cda889..8ad11669e4d8f346ae3e654587386af88d08281e 100644 (file)
@@ -296,9 +296,6 @@ struct snd_soc_dai {
        /* DAI runtime info */
        unsigned int capture_active:1;          /* stream is in use */
        unsigned int playback_active:1;         /* stream is in use */
-       unsigned int symmetric_rates:1;
-       unsigned int symmetric_channels:1;
-       unsigned int symmetric_samplebits:1;
        unsigned int probed:1;
 
        unsigned int active;
index a736a2a6976c2fb6eefd2994c413ea35159b3724..f3006f301fe8c7cf0cd10b364c7159a07238e598 100644 (file)
 /* Define how often to check (and clear) the fault status register (in ms) */
 #define TAS5720_FAULT_CHECK_INTERVAL           200
 
+enum tas572x_type {
+       TAS5720,
+       TAS5722,
+};
+
 static const char * const tas5720_supply_names[] = {
        "dvdd",         /* Digital power supply. Connect to 3.3-V supply. */
        "pvdd",         /* Class-D amp and analog power supply (connected). */
@@ -47,6 +52,7 @@ struct tas5720_data {
        struct snd_soc_codec *codec;
        struct regmap *regmap;
        struct i2c_client *tas5720_client;
+       enum tas572x_type devtype;
        struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
        struct delayed_work fault_check_work;
        unsigned int last_fault;
@@ -264,7 +270,7 @@ out:
 static int tas5720_codec_probe(struct snd_soc_codec *codec)
 {
        struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
-       unsigned int device_id;
+       unsigned int device_id, expected_device_id;
        int ret;
 
        tas5720->codec = codec;
@@ -276,6 +282,11 @@ static int tas5720_codec_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
+       /*
+        * Take a liberal approach to checking the device ID to allow the
+        * driver to be used even if the device ID does not match, however
+        * issue a warning if there is a mismatch.
+        */
        ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id);
        if (ret < 0) {
                dev_err(codec->dev, "failed to read device ID register: %d\n",
@@ -283,13 +294,22 @@ static int tas5720_codec_probe(struct snd_soc_codec *codec)
                goto probe_fail;
        }
 
-       if (device_id != TAS5720_DEVICE_ID) {
-               dev_err(codec->dev, "wrong device ID. expected: %u read: %u\n",
-                       TAS5720_DEVICE_ID, device_id);
-               ret = -ENODEV;
-               goto probe_fail;
+       switch (tas5720->devtype) {
+       case TAS5720:
+               expected_device_id = TAS5720_DEVICE_ID;
+               break;
+       case TAS5722:
+               expected_device_id = TAS5722_DEVICE_ID;
+               break;
+       default:
+               dev_err(codec->dev, "unexpected private driver data\n");
+               return -EINVAL;
        }
 
+       if (device_id != expected_device_id)
+               dev_warn(codec->dev, "wrong device ID. expected: %u read: %u\n",
+                        expected_device_id, device_id);
+
        /* Set device to mute */
        ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
                                  TAS5720_MUTE, TAS5720_MUTE);
@@ -446,6 +466,15 @@ static const struct regmap_config tas5720_regmap_config = {
        .volatile_reg = tas5720_is_volatile_reg,
 };
 
+static const struct regmap_config tas5722_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = TAS5722_MAX_REG,
+       .cache_type = REGCACHE_RBTREE,
+       .volatile_reg = tas5720_is_volatile_reg,
+};
+
 /*
  * DAC analog gain. There are four discrete values to select from, ranging
  * from 19.2 dB to 26.3dB.
@@ -544,6 +573,7 @@ static int tas5720_probe(struct i2c_client *client,
 {
        struct device *dev = &client->dev;
        struct tas5720_data *data;
+       const struct regmap_config *regmap_config;
        int ret;
        int i;
 
@@ -552,7 +582,20 @@ static int tas5720_probe(struct i2c_client *client,
                return -ENOMEM;
 
        data->tas5720_client = client;
-       data->regmap = devm_regmap_init_i2c(client, &tas5720_regmap_config);
+       data->devtype = id->driver_data;
+
+       switch (id->driver_data) {
+       case TAS5720:
+               regmap_config = &tas5720_regmap_config;
+               break;
+       case TAS5722:
+               regmap_config = &tas5722_regmap_config;
+               break;
+       default:
+               dev_err(dev, "unexpected private driver data\n");
+               return -EINVAL;
+       }
+       data->regmap = devm_regmap_init_i2c(client, regmap_config);
        if (IS_ERR(data->regmap)) {
                ret = PTR_ERR(data->regmap);
                dev_err(dev, "failed to allocate register map: %d\n", ret);
@@ -592,7 +635,8 @@ static int tas5720_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id tas5720_id[] = {
-       { "tas5720", 0 },
+       { "tas5720", TAS5720 },
+       { "tas5722", TAS5722 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, tas5720_id);
@@ -600,6 +644,7 @@ MODULE_DEVICE_TABLE(i2c, tas5720_id);
 #if IS_ENABLED(CONFIG_OF)
 static const struct of_device_id tas5720_of_match[] = {
        { .compatible = "ti,tas5720", },
+       { .compatible = "ti,tas5722", },
        { },
 };
 MODULE_DEVICE_TABLE(of, tas5720_of_match);
index 3d077c779b1260f5077541131f5bc03262810e81..1dda3095961d11592f93beddcc568a992d77322f 100644 (file)
 #define TAS5720_DIGITAL_CLIP1_REG      0x11
 #define TAS5720_MAX_REG                        TAS5720_DIGITAL_CLIP1_REG
 
+/* Additional TAS5722-specific Registers */
+#define TAS5722_DIGITAL_CTRL2_REG      0x13
+#define TAS5722_ANALOG_CTRL2_REG       0x14
+#define TAS5722_MAX_REG                        TAS5722_ANALOG_CTRL2_REG
+
 /* TAS5720_DEVICE_ID_REG */
 #define TAS5720_DEVICE_ID              0x01
+#define TAS5722_DEVICE_ID              0x12
 
 /* TAS5720_POWER_CTRL_REG */
 #define TAS5720_DIG_CLIP_MASK          GENMASK(7, 2)
@@ -51,6 +57,7 @@
 #define TAS5720_SAIF_FORMAT_MASK       GENMASK(2, 0)
 
 /* TAS5720_DIGITAL_CTRL2_REG */
+#define TAS5722_VOL_RAMP_RATE          BIT(6)
 #define TAS5720_MUTE                   BIT(4)
 #define TAS5720_TDM_SLOT_SEL_MASK      GENMASK(2, 0)
 
 #define TAS5720_CLIP1_MASK             GENMASK(7, 2)
 #define TAS5720_CLIP1_SHIFT            (0x2)
 
+/* TAS5722_DIGITAL_CTRL2_REG */
+#define TAS5722_HPF_3_7HZ              (0x0 << 5)
+#define TAS5722_HPF_7_4HZ              (0x1 << 5)
+#define TAS5722_HPF_14_9HZ             (0x2 << 5)
+#define TAS5722_HPF_29_7HZ             (0x3 << 5)
+#define TAS5722_HPF_59_4HZ             (0x4 << 5)
+#define TAS5722_HPF_118_4HZ            (0x5 << 5)
+#define TAS5722_HPF_235_0HZ            (0x6 << 5)
+#define TAS5722_HPF_463_2HZ            (0x7 << 5)
+#define TAS5722_HPF_MASK               GENMASK(7, 5)
+#define TAS5722_AUTO_SLEEP_OFF         (0x0 << 3)
+#define TAS5722_AUTO_SLEEP_1024LR      (0x1 << 3)
+#define TAS5722_AUTO_SLEEP_65536LR     (0x2 << 3)
+#define TAS5722_AUTO_SLEEP_262144LR    (0x3 << 3)
+#define TAS5722_AUTO_SLEEP_MASK                GENMASK(4, 3)
+#define TAS5722_TDM_SLOT_16B           BIT(2)
+#define TAS5722_MCLK_PIN_CFG           BIT(1)
+#define TAS5722_VOL_CONTROL_LSB                BIT(0)
+
+/* TAS5722_ANALOG_CTRL2_REG */
+#define TAS5722_FAULTZ_PU              BIT(3)
+#define TAS5722_VREG_LVL               BIT(2)
+#define TAS5722_PWR_TUNE               BIT(0)
+
 #endif /* __TAS5720_H__ */
index d83dab57a1d1f5171afbc9bf6b9d1c2f9c6a40ba..5c2f5727244d7914c46531276b295650703e6bd3 100644 (file)
@@ -98,6 +98,8 @@ struct wm2200_priv {
 
        int rev;
        int sysclk;
+
+       unsigned int symmetric_rates:1;
 };
 
 #define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1)
@@ -1550,7 +1552,7 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = {
 
 static int wm2200_probe(struct snd_soc_codec *codec)
 {
-       struct wm2200_priv *wm2200 = dev_get_drvdata(codec->dev);
+       struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
        int ret;
 
        wm2200->codec = codec;
@@ -1758,7 +1760,7 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream,
        lrclk = bclk_rates[bclk] / params_rate(params);
        dev_dbg(codec->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk);
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
-           dai->symmetric_rates)
+           wm2200->symmetric_rates)
                snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_7,
                                    WM2200_AIF1RX_BCPF_MASK, lrclk);
        else
@@ -2059,13 +2061,14 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 static int wm2200_dai_probe(struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
+       struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
        unsigned int val = 0;
        int ret;
 
        ret = snd_soc_read(codec, WM2200_GPIO_CTRL_1);
        if (ret >= 0) {
                if ((ret & WM2200_GP1_FN_MASK) != 0) {
-                       dai->symmetric_rates = true;
+                       wm2200->symmetric_rates = true;
                        val = WM2200_AIF1TX_LRCLK_SRC;
                }
        } else {
index d6f71a3406e91e5c09679d85c9f31268ada71649..d743b7dd52fb9e1fc508ceca54c9d25a67182609 100644 (file)
 
 #include "stm32_sai.h"
 
-static LIST_HEAD(sync_providers);
-static DEFINE_MUTEX(sync_mutex);
-
-struct sync_provider {
-       struct list_head link;
-       struct device_node *node;
-       int  (*sync_conf)(void *data, int synco);
-       void *data;
-};
-
 static const struct stm32_sai_conf stm32_sai_conf_f4 = {
        .version = SAI_STM32F4,
 };
@@ -70,9 +60,8 @@ static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
        return 0;
 }
 
-static int stm32_sai_sync_conf_provider(void *data, int synco)
+static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
 {
-       struct stm32_sai_data *sai = (struct stm32_sai_data *)data;
        u32 prev_synco;
        int ret;
 
@@ -103,83 +92,42 @@ static int stm32_sai_sync_conf_provider(void *data, int synco)
        return 0;
 }
 
-static int stm32_sai_set_sync_provider(struct device_node *np, int synco)
+static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
+                             struct device_node *np_provider,
+                             int synco, int synci)
 {
-       struct sync_provider *provider;
+       struct platform_device *pdev = of_find_device_by_node(np_provider);
+       struct stm32_sai_data *sai_provider;
        int ret;
 
-       mutex_lock(&sync_mutex);
-       list_for_each_entry(provider, &sync_providers, link) {
-               if (provider->node == np) {
-                       ret = provider->sync_conf(provider->data, synco);
-                       mutex_unlock(&sync_mutex);
-                       return ret;
-               }
+       if (!pdev) {
+               dev_err(&sai_client->pdev->dev,
+                       "Device not found for node %s\n", np_provider->name);
+               return -ENODEV;
        }
-       mutex_unlock(&sync_mutex);
 
-       /* SAI sync provider not found */
-       return -ENODEV;
-}
-
-static int stm32_sai_set_sync(struct stm32_sai_data *sai,
-                             struct device_node *np_provider,
-                             int synco, int synci)
-{
-       int ret;
+       sai_provider = platform_get_drvdata(pdev);
+       if (!sai_provider) {
+               dev_err(&sai_client->pdev->dev,
+                       "SAI sync provider data not found\n");
+               return -EINVAL;
+       }
 
        /* Configure sync client */
-       stm32_sai_sync_conf_client(sai, synci);
+       ret = stm32_sai_sync_conf_client(sai_client, synci);
+       if (ret < 0)
+               return ret;
 
        /* Configure sync provider */
-       ret = stm32_sai_set_sync_provider(np_provider, synco);
-
-       return ret;
-}
-
-static int stm32_sai_sync_add_provider(struct platform_device *pdev,
-                                      void *data)
-{
-       struct sync_provider *sp;
-
-       sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL);
-       if (!sp)
-               return -ENOMEM;
-
-       sp->node = of_node_get(pdev->dev.of_node);
-       sp->data = data;
-       sp->sync_conf = &stm32_sai_sync_conf_provider;
-
-       mutex_lock(&sync_mutex);
-       list_add(&sp->link, &sync_providers);
-       mutex_unlock(&sync_mutex);
-
-       return 0;
-}
-
-static void stm32_sai_sync_del_provider(struct device_node *np)
-{
-       struct sync_provider *sp;
-
-       mutex_lock(&sync_mutex);
-       list_for_each_entry(sp, &sync_providers, link) {
-               if (sp->node == np) {
-                       list_del(&sp->link);
-                       of_node_put(sp->node);
-                       break;
-               }
-       }
-       mutex_unlock(&sync_mutex);
+       return stm32_sai_sync_conf_provider(sai_provider, synco);
 }
 
 static int stm32_sai_probe(struct platform_device *pdev)
 {
-       struct device_node *np = pdev->dev.of_node;
        struct stm32_sai_data *sai;
        struct reset_control *rst;
        struct resource *res;
        const struct of_device_id *of_id;
-       int ret;
 
        sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
        if (!sai)
@@ -231,28 +179,11 @@ static int stm32_sai_probe(struct platform_device *pdev)
                reset_control_deassert(rst);
        }
 
-       ret = stm32_sai_sync_add_provider(pdev, sai);
-       if (ret < 0)
-               return ret;
-       sai->set_sync = &stm32_sai_set_sync;
-
        sai->pdev = pdev;
+       sai->set_sync = &stm32_sai_set_sync;
        platform_set_drvdata(pdev, sai);
 
-       ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
-       if (ret < 0)
-               stm32_sai_sync_del_provider(np);
-
-       return ret;
-}
-
-static int stm32_sai_remove(struct platform_device *pdev)
-{
-       of_platform_depopulate(&pdev->dev);
-
-       stm32_sai_sync_del_provider(pdev->dev.of_node);
-
-       return 0;
+       return devm_of_platform_populate(&pdev->dev);
 }
 
 MODULE_DEVICE_TABLE(of, stm32_sai_ids);
@@ -263,7 +194,6 @@ static struct platform_driver stm32_sai_driver = {
                .of_match_table = stm32_sai_ids,
        },
        .probe = stm32_sai_probe,
-       .remove = stm32_sai_remove,
 };
 
 module_platform_driver(stm32_sai_driver);
index 5da4efe7a5505bb90fef17f13e7b73c1529cd7b5..886281673972898cee7950d32ca0ce9c2804039a 100644 (file)
@@ -590,12 +590,28 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
                                             hwrate);
 }
 
+
+static unsigned int sun4i_codec_src_rates[] = {
+       8000, 11025, 12000, 16000, 22050, 24000, 32000,
+       44100, 48000, 96000, 192000
+};
+
+
+static struct snd_pcm_hw_constraint_list sun4i_codec_constraints = {
+       .count  = ARRAY_SIZE(sun4i_codec_src_rates),
+       .list   = sun4i_codec_src_rates,
+};
+
+
 static int sun4i_codec_startup(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
 
+       snd_pcm_hw_constraint_list(substream->runtime, 0,
+                               SNDRV_PCM_HW_PARAM_RATE, &sun4i_codec_constraints);
+
        /*
         * Stop issuing DRQ when we have room for less than 16 samples
         * in our TX FIFO
@@ -633,9 +649,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
                .channels_max   = 2,
                .rate_min       = 8000,
                .rate_max       = 192000,
-               .rates          = SNDRV_PCM_RATE_8000_48000 |
-                                 SNDRV_PCM_RATE_96000 |
-                                 SNDRV_PCM_RATE_192000,
+               .rates          = SNDRV_PCM_RATE_CONTINUOUS,
                .formats        = SNDRV_PCM_FMTBIT_S16_LE |
                                  SNDRV_PCM_FMTBIT_S32_LE,
                .sig_bits       = 24,
@@ -645,11 +659,8 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
                .channels_min   = 1,
                .channels_max   = 2,
                .rate_min       = 8000,
-               .rate_max       = 192000,
-               .rates          = SNDRV_PCM_RATE_8000_48000 |
-                                 SNDRV_PCM_RATE_96000 |
-                                 SNDRV_PCM_RATE_192000 |
-                                 SNDRV_PCM_RATE_KNOT,
+               .rate_max       = 48000,
+               .rates          = SNDRV_PCM_RATE_CONTINUOUS,
                .formats        = SNDRV_PCM_FMTBIT_S16_LE |
                                  SNDRV_PCM_FMTBIT_S32_LE,
                .sig_bits       = 24,
@@ -1128,7 +1139,7 @@ static const struct snd_soc_component_driver sun4i_codec_component = {
        .name = "sun4i-codec",
 };
 
-#define SUN4I_CODEC_RATES      SNDRV_PCM_RATE_8000_192000
+#define SUN4I_CODEC_RATES      SNDRV_PCM_RATE_CONTINUOUS
 #define SUN4I_CODEC_FORMATS    (SNDRV_PCM_FMTBIT_S16_LE | \
                                 SNDRV_PCM_FMTBIT_S32_LE)
 
index 04f92583a9696569aeac2a92084643a94cbe57d6..dca1143c1150ac58aa148a49db54c33226e366c8 100644 (file)
@@ -269,10 +269,11 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample)
        return false;
 }
 
-static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
+static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
                                  unsigned int rate,
                                  unsigned int word_size)
 {
+       struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
        unsigned int oversample_rate, clk_rate;
        int bclk_div, mclk_div;
        int ret;
@@ -300,6 +301,7 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
                break;
 
        default:
+               dev_err(dai->dev, "Unsupported sample rate: %u\n", rate);
                return -EINVAL;
        }
 
@@ -308,18 +310,25 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
                return ret;
 
        oversample_rate = i2s->mclk_freq / rate;
-       if (!sun4i_i2s_oversample_is_valid(oversample_rate))
+       if (!sun4i_i2s_oversample_is_valid(oversample_rate)) {
+               dev_err(dai->dev, "Unsupported oversample rate: %d\n",
+                       oversample_rate);
                return -EINVAL;
+       }
 
        bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
                                          word_size);
-       if (bclk_div < 0)
+       if (bclk_div < 0) {
+               dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div);
                return -EINVAL;
+       }
 
        mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
                                          clk_rate, rate);
-       if (mclk_div < 0)
+       if (mclk_div < 0) {
+               dev_err(dai->dev, "Unsupported MCLK divider: %d\n", mclk_div);
                return -EINVAL;
+       }
 
        /* Adjust the clock division values if needed */
        bclk_div += i2s->variant->bclk_offset;
@@ -349,8 +358,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
        u32 width;
 
        channels = params_channels(params);
-       if (channels != 2)
+       if (channels != 2) {
+               dev_err(dai->dev, "Unsupported number of channels: %d\n",
+                       channels);
                return -EINVAL;
+       }
 
        if (i2s->variant->has_chcfg) {
                regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
@@ -382,6 +394,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
                width = DMA_SLAVE_BUSWIDTH_2_BYTES;
                break;
        default:
+               dev_err(dai->dev, "Unsupported physical sample width: %d\n",
+                       params_physical_width(params));
                return -EINVAL;
        }
        i2s->playback_dma_data.addr_width = width;
@@ -393,6 +407,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
                break;
 
        default:
+               dev_err(dai->dev, "Unsupported sample width: %d\n",
+                       params_width(params));
                return -EINVAL;
        }
 
@@ -401,7 +417,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
        regmap_field_write(i2s->field_fmt_sr,
                           sr + i2s->variant->fmt_offset);
 
-       return sun4i_i2s_set_clk_rate(i2s, params_rate(params),
+       return sun4i_i2s_set_clk_rate(dai, params_rate(params),
                                      params_width(params));
 }
 
@@ -426,6 +442,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
                break;
        default:
+               dev_err(dai->dev, "Unsupported format: %d\n",
+                       fmt & SND_SOC_DAIFMT_FORMAT_MASK);
                return -EINVAL;
        }
 
@@ -464,6 +482,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        case SND_SOC_DAIFMT_NB_NF:
                break;
        default:
+               dev_err(dai->dev, "Unsupported clock polarity: %d\n",
+                       fmt & SND_SOC_DAIFMT_INV_MASK);
                return -EINVAL;
        }
 
@@ -482,6 +502,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                        val = SUN4I_I2S_CTRL_MODE_SLAVE;
                        break;
                default:
+                       dev_err(dai->dev, "Unsupported slave setting: %d\n",
+                               fmt & SND_SOC_DAIFMT_MASTER_MASK);
                        return -EINVAL;
                }
                regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
@@ -504,6 +526,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                        val = 0;
                        break;
                default:
+                       dev_err(dai->dev, "Unsupported slave setting: %d\n",
+                               fmt & SND_SOC_DAIFMT_MASTER_MASK);
                        return -EINVAL;
                }
                regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
@@ -897,6 +921,23 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
        .field_rxchansel        = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
 };
 
+static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
+       .has_reset              = true,
+       .reg_offset_txdata      = SUN8I_I2S_FIFO_TX_REG,
+       .sun4i_i2s_regmap       = &sun4i_i2s_regmap_config,
+       .field_clkdiv_mclk_en   = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7),
+       .field_fmt_wss          = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
+       .field_fmt_sr           = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
+       .field_fmt_bclk         = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
+       .field_fmt_lrclk        = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
+       .has_slave_select_bit   = true,
+       .field_fmt_mode         = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
+       .field_txchanmap        = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
+       .field_rxchanmap        = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
+       .field_txchansel        = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
+       .field_rxchansel        = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
+};
+
 static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
        .has_reset              = true,
        .reg_offset_txdata      = SUN8I_I2S_FIFO_TX_REG,
@@ -1120,6 +1161,10 @@ static const struct of_device_id sun4i_i2s_match[] = {
                .compatible = "allwinner,sun6i-a31-i2s",
                .data = &sun6i_a31_i2s_quirks,
        },
+       {
+               .compatible = "allwinner,sun8i-a83t-i2s",
+               .data = &sun8i_a83t_i2s_quirks,
+       },
        {
                .compatible = "allwinner,sun8i-h3-i2s",
                .data = &sun8i_h3_i2s_quirks,