From 80eba805595ad0e91874f73f5934020ca4ebe178 Mon Sep 17 00:00:00 2001 From: LuoXiaoTan Date: Fri, 14 Apr 2017 02:44:58 -0700 Subject: [PATCH] ASoC: codecs: add tc358749x codec driver add tc358749x codec driver for hdmiin function Change-Id: I819ac80ced59b5d81d547f7ba2c7ebc7bee7f845 Signed-off-by: LuoXiaoTan --- sound/soc/codecs/Kconfig | 4 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tc358749x.c | 308 +++++++++++++++++++++++++++++++++++ sound/soc/codecs/tc358749x.h | 53 ++++++ 4 files changed, 367 insertions(+) create mode 100644 sound/soc/codecs/tc358749x.c create mode 100644 sound/soc/codecs/tc358749x.h diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 4af77fc06b72..c47ea699a547 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -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 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index fa9d942653c8..e4baa257aad2 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -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 index 000000000000..761c6b181d35 --- /dev/null +++ b/sound/soc/codecs/tc358749x.c @@ -0,0 +1,308 @@ +/* + * tc358749x.c TC358749XBG ALSA SoC audio codec driver + * + * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd + * Author: Roy + * + * 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 .* + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 index 000000000000..5a78cb73fd1c --- /dev/null +++ b/sound/soc/codecs/tc358749x.h @@ -0,0 +1,53 @@ +/* + * tc358749x.h TC358749XBG ALSA SoC audio codec driver + * + * Copyright (c) 2016 Fuzhou Rockchip Electronics Co., Ltd + * Author: Roy + * + * 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 .* + */ + +#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 -- 2.34.1