ASoC: codecs: add tc358749x codec driver
authorLuoXiaoTan <lxt@rock-chips.com>
Fri, 14 Apr 2017 09:44:58 +0000 (02:44 -0700)
committerHuang, Tao <huangtao@rock-chips.com>
Mon, 17 Apr 2017 03:04:26 +0000 (11:04 +0800)
add tc358749x codec driver for hdmiin function

Change-Id: I819ac80ced59b5d81d547f7ba2c7ebc7bee7f845
Signed-off-by: LuoXiaoTan <lxt@rock-chips.com>
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/tc358749x.c [new file with mode: 0644]
sound/soc/codecs/tc358749x.h [new file with mode: 0644]

index 4af77fc06b723b5d4b2127c1c1cc65f39436b898..c47ea699a54728aa790219c5324ddb16d3d8b478 100644 (file)
@@ -689,6 +689,10 @@ config SND_SOC_TAS571X
        tristate "Texas Instruments TAS5711/TAS5717/TAS5719 power amplifiers"
        depends on I2C
 
+config SND_SOC_TC358749X
+       tristate "Toshiba TC358749X HDMI in Audio codec"
+       depends on I2C
+
 config SND_SOC_TFA9879
        tristate "NXP Semiconductors TFA9879 amplifier"
        depends on I2C
index fa9d942653c8ddf8b075d120b17e3e9076246145..e4baa257aad2497bb73cb78a01be34824d824596 100644 (file)
@@ -121,6 +121,7 @@ snd-soc-stac9766-objs := stac9766.o
 snd-soc-sti-sas-objs := sti-sas.o
 snd-soc-tas5086-objs := tas5086.o
 snd-soc-tas571x-objs := tas571x.o
+snd-soc-tc358749x-objs := tc358749x.o
 snd-soc-tfa9879-objs := tfa9879.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
@@ -319,6 +320,7 @@ obj-$(CONFIG_SND_SOC_STI_SAS)       += snd-soc-sti-sas.o
 obj-$(CONFIG_SND_SOC_TAS2552)  += snd-soc-tas2552.o
 obj-$(CONFIG_SND_SOC_TAS5086)  += snd-soc-tas5086.o
 obj-$(CONFIG_SND_SOC_TAS571X)  += snd-soc-tas571x.o
+obj-$(CONFIG_SND_SOC_TC358749X)        += snd-soc-tc358749x.o
 obj-$(CONFIG_SND_SOC_TFA9879)  += snd-soc-tfa9879.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)      += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C)  += snd-soc-tlv320aic23-i2c.o
diff --git a/sound/soc/codecs/tc358749x.c b/sound/soc/codecs/tc358749x.c
new file mode 100644 (file)
index 0000000..761c6b1
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * tc358749x.c TC358749XBG ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Roy <luoxiaotan@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.*
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "tc358749x.h"
+
+static int snd_tc358749x_dai_hw_params(struct snd_pcm_substream *substream,
+                                      struct snd_pcm_hw_params *params,
+                                      struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       unsigned int fs;
+
+       switch (params_rate(params)) {
+       case 32000:
+               fs = FS_32000;
+               break;
+       case 44100:
+               fs = FS_44100;
+               break;
+       case 48000:
+               fs = FS_48000;
+               break;
+       case 88200:
+               fs = FS_88200;
+               break;
+       case 96000:
+               fs = FS_96000;
+               break;
+       case 176400:
+               fs = FS_176400;
+               break;
+       case 192000:
+               fs = FS_192000;
+               break;
+       default:
+               dev_err(codec->dev, "Enter:%s, %d, Error rate=%d\n",
+                       __func__, __LINE__, params_rate(params));
+               return -EINVAL;
+       }
+       snd_soc_update_bits(codec, TC358749X_FS_SET, FS_SET_MASK, fs);
+       return 0;
+}
+
+static int snd_tc358749x_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       if (mute)
+               snd_soc_update_bits(codec, TC358749X_FORCE_MUTE,
+                                   FORCE_DMUTE_MASK, MUTE);
+       else
+               snd_soc_update_bits(codec, TC358749X_FORCE_MUTE,
+                                   FORCE_DMUTE_MASK, !MUTE);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops tc358749x_dai_ops = {
+       .hw_params = snd_tc358749x_dai_hw_params,
+       .digital_mute = snd_tc358749x_mute,
+};
+
+static struct snd_soc_dai_driver tc358749x_dai = {
+       .name = "tc358749x-audio",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = SNDRV_PCM_RATE_32000 |
+                        SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+                        SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+                        SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+       },
+
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = SNDRV_PCM_RATE_32000 |
+                        SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+                        SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+                        SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+       },
+       .ops = &tc358749x_dai_ops,
+};
+
+static int tc358749x_probe(struct snd_soc_codec *codec)
+{
+       snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int tc358749_set_bias_level(struct snd_soc_codec *codec,
+                                  enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               snd_soc_update_bits(codec, TC358749X_FORCE_MUTE,
+                                   FORCE_DMUTE_MASK, !MUTE);
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, TC358749X_FORCE_MUTE,
+                                   FORCE_DMUTE_MASK, MUTE);
+               break;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tc358749x = {
+       .probe = tc358749x_probe,
+       .set_bias_level = tc358749_set_bias_level,
+};
+
+static bool tc358749x_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TC358749X_FORCE_MUTE:
+       case TC358749X_FS_SET:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct reg_default tc358749x_reg_defaults[] = {
+       { TC358749X_FORCE_MUTE, 0xb1 },
+       { TC358749X_FS_SET, 0x00 },
+};
+
+const struct regmap_config tc358749x_regmap_config = {
+       .reg_bits       = 16,
+       .val_bits       = 8,
+       .max_register   = TC358749X_FS_SET,
+       .cache_type     = REGCACHE_RBTREE,
+       .reg_defaults = tc358749x_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(tc358749x_reg_defaults),
+       .readable_reg = tc358749x_readable_register,
+};
+
+static const struct i2c_device_id tc358749x_i2c_id[] = {
+       { "tc358749x", 0 },
+       { }
+};
+
+MODULE_DEVICE_TABLE(i2c, tc358749x_i2c_id);
+
+static int tc358749x_parse_dts(struct i2c_client *i2c,
+                              struct tc358749x_priv *tc358749x)
+{
+       int ret = 0;
+       struct device *dev = &i2c->dev;
+
+       tc358749x->gpio_int = devm_gpiod_get_optional(dev, "int",
+                                                       GPIOD_OUT_HIGH);
+       if (IS_ERR(tc358749x->gpio_int)) {
+               ret = PTR_ERR(tc358749x->gpio_int);
+               dev_err(&i2c->dev, "Unable to claim gpio \"int\".\n");
+               return ret;
+       }
+       /* I2C Slave Address selection through boot-strap  */
+       gpiod_direction_output(tc358749x->gpio_int, 0);
+
+       tc358749x->gpio_power = devm_gpiod_get_optional(dev, "power",
+                                                       GPIOD_OUT_HIGH);
+       if (IS_ERR(tc358749x->gpio_power)) {
+               ret = PTR_ERR(tc358749x->gpio_power);
+               dev_err(&i2c->dev, "Unable to claim gpio \"power\".\n");
+               return ret;
+       }
+       gpiod_direction_output(tc358749x->gpio_power, 1);
+
+       tc358749x->gpio_power18 = devm_gpiod_get_optional(dev, "power18",
+                                                         GPIOD_OUT_HIGH);
+       if (IS_ERR(tc358749x->gpio_power18)) {
+               ret = PTR_ERR(tc358749x->gpio_power18);
+               dev_err(&i2c->dev, "Unable to claim gpio \"power18\".\n");
+               return ret;
+       }
+       gpiod_direction_output(tc358749x->gpio_power18, 1);
+
+       tc358749x->gpio_power33 = devm_gpiod_get_optional(dev, "power33",
+                                                         GPIOD_OUT_HIGH);
+       if (IS_ERR(tc358749x->gpio_power33)) {
+               ret = PTR_ERR(tc358749x->gpio_power33);
+               dev_err(&i2c->dev, "Unable to claim gpio \"power33\".\n");
+               return ret;
+       }
+       gpiod_direction_output(tc358749x->gpio_power33, 1);
+
+       tc358749x->gpio_csi_ctl = devm_gpiod_get_optional(dev, "csi-ctl",
+                                                         GPIOD_OUT_LOW);
+       if (IS_ERR(tc358749x->gpio_csi_ctl)) {
+               ret = PTR_ERR(tc358749x->gpio_csi_ctl);
+               dev_err(&i2c->dev, "Unable to claim gpio \"csi-ctl\".\n");
+               return ret;
+       }
+       gpiod_direction_output(tc358749x->gpio_csi_ctl, 0);
+
+       tc358749x->gpio_reset = devm_gpiod_get_optional(dev, "reset",
+                                                       GPIOD_OUT_HIGH);
+       if (IS_ERR(tc358749x->gpio_reset)) {
+               ret = PTR_ERR(tc358749x->gpio_reset);
+               dev_err(&i2c->dev, "Unable to claim gpio \"reset\".\n");
+               return ret;
+       }
+       gpiod_direction_output(tc358749x->gpio_reset, 1);
+
+       tc358749x->gpio_stanby = devm_gpiod_get_optional(dev, "stanby",
+                                                        GPIOD_OUT_LOW);
+       if (IS_ERR(tc358749x->gpio_stanby)) {
+               ret = PTR_ERR(tc358749x->gpio_stanby);
+               dev_err(&i2c->dev, "Unable to claim gpio \"stanby\".\n");
+               return ret;
+       }
+       gpiod_direction_output(tc358749x->gpio_stanby, 1);
+
+       /* Wait 10ms tc358749x lock I2C Slave address */
+       usleep_range(10000, 11000);
+       /* after I2C address has been lock and set it input */
+       gpiod_direction_input(tc358749x->gpio_int);
+       return 0;
+}
+
+static int tc358749x_i2c_probe(struct i2c_client *i2c,
+                              const struct i2c_device_id *id)
+{
+       struct tc358749x_priv *tc358749x;
+       int ret;
+
+       tc358749x = devm_kzalloc(&i2c->dev, sizeof(*tc358749x),
+                                GFP_KERNEL);
+       if (!tc358749x)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, tc358749x);
+       tc358749x_parse_dts(i2c, tc358749x);
+
+       tc358749x->regmap = devm_regmap_init_i2c(i2c, &tc358749x_regmap_config);
+       if (IS_ERR(tc358749x->regmap)) {
+               ret = PTR_ERR(tc358749x->regmap);
+               dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tc358749x,
+                                    &tc358749x_dai, 1);
+
+       dev_info(&i2c->dev, "%s success\n", __func__);
+       return ret;
+}
+
+static int tc358749x_i2c_remove(struct i2c_client *i2c)
+{
+       snd_soc_unregister_codec(&i2c->dev);
+
+       return 0;
+}
+
+static struct i2c_driver tc358749x_i2c_driver = {
+       .driver = {
+               .name = "tc358749x",
+       },
+       .probe = tc358749x_i2c_probe,
+       .remove   = tc358749x_i2c_remove,
+       .id_table = tc358749x_i2c_id,
+};
+module_i2c_driver(tc358749x_i2c_driver);
+
+MODULE_AUTHOR("Roy <luoxiaotan@rock-chips.com>");
+MODULE_DESCRIPTION("TC358749X HDMI Audio RX ASoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tc358749x.h b/sound/soc/codecs/tc358749x.h
new file mode 100644 (file)
index 0000000..5a78cb7
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * tc358749x.h TC358749XBG ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Roy <luoxiaotan@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.*
+ */
+
+#ifndef _TC358749X_H
+#define _TC358749X_H
+
+#define TC358749X_FORCE_MUTE           0x8600
+#define MUTE                           0x1
+#define FORCE_DMUTE_MASK               BIT(0)
+#define FORCE_AMUTE_MASK               BIT(4)
+
+#define TC358749X_FS_SET               0x8621
+#define FS_SET_MASK                    0xf
+#define FS_44100                       0x0
+#define FS_48000                       0x2
+#define FS_32000                       0x3
+#define FS_22050                       0x4
+#define FS_24000                       0x6
+#define FS_88200                       0x8
+#define FS_96000                       0xa
+#define FS_176400                      0xc
+#define FS_192000                      0xe
+
+struct tc358749x_priv {
+       struct regmap                   *regmap;
+       struct i2c_client               *client;
+       struct device                   *dev;
+       struct gpio_desc                *gpio_power;
+       struct gpio_desc                *gpio_power18;
+       struct gpio_desc                *gpio_power33;
+       struct gpio_desc                *gpio_csi_ctl;
+       struct gpio_desc                *gpio_reset;
+       struct gpio_desc                *gpio_stanby;
+       struct gpio_desc                *gpio_int;
+};
+
+#endif