|
@@ -37,6 +37,7 @@
|
|
|
#include <linux/i2c.h>
|
|
|
#include <linux/regmap.h>
|
|
|
#include <linux/spi/spi.h>
|
|
|
+#include <linux/of.h>
|
|
|
#include <linux/of_device.h>
|
|
|
#include <linux/of_gpio.h>
|
|
|
#include <sound/pcm.h>
|
|
@@ -244,6 +245,8 @@ struct tas5086_private {
|
|
|
unsigned int mclk, sclk;
|
|
|
unsigned int format;
|
|
|
bool deemph;
|
|
|
+ unsigned int charge_period;
|
|
|
+ unsigned int pwm_start_mid_z;
|
|
|
/* Current sample rate for de-emphasis control */
|
|
|
int rate;
|
|
|
/* GPIO driving Reset pin, if any */
|
|
@@ -456,6 +459,75 @@ static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
|
|
|
return regmap_write(priv->regmap, TAS5086_SOFT_MUTE, val);
|
|
|
}
|
|
|
|
|
|
+static void tas5086_reset(struct tas5086_private *priv)
|
|
|
+{
|
|
|
+ if (gpio_is_valid(priv->gpio_nreset)) {
|
|
|
+ /* Reset codec - minimum assertion time is 400ns */
|
|
|
+ gpio_direction_output(priv->gpio_nreset, 0);
|
|
|
+ udelay(1);
|
|
|
+ gpio_set_value(priv->gpio_nreset, 1);
|
|
|
+
|
|
|
+ /* Codec needs ~15ms to wake up */
|
|
|
+ msleep(15);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* charge period values in microseconds */
|
|
|
+static const int tas5086_charge_period[] = {
|
|
|
+ 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200,
|
|
|
+ 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000,
|
|
|
+ 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000,
|
|
|
+};
|
|
|
+
|
|
|
+static int tas5086_init(struct device *dev, struct tas5086_private *priv)
|
|
|
+{
|
|
|
+ int ret, i;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If any of the channels is configured to start in Mid-Z mode,
|
|
|
+ * configure 'part 1' of the PWM starts to use Mid-Z, and tell
|
|
|
+ * all configured mid-z channels to start start under 'part 1'.
|
|
|
+ */
|
|
|
+ if (priv->pwm_start_mid_z)
|
|
|
+ regmap_write(priv->regmap, TAS5086_PWM_START,
|
|
|
+ TAS5086_PWM_START_MIDZ_FOR_START_1 |
|
|
|
+ priv->pwm_start_mid_z);
|
|
|
+
|
|
|
+ /* lookup and set split-capacitor charge period */
|
|
|
+ if (priv->charge_period == 0) {
|
|
|
+ regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0);
|
|
|
+ } else {
|
|
|
+ i = index_in_array(tas5086_charge_period,
|
|
|
+ ARRAY_SIZE(tas5086_charge_period),
|
|
|
+ priv->charge_period);
|
|
|
+ if (i >= 0)
|
|
|
+ regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE,
|
|
|
+ i + 0x08);
|
|
|
+ else
|
|
|
+ dev_warn(dev,
|
|
|
+ "Invalid split-cap charge period of %d ns.\n",
|
|
|
+ priv->charge_period);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* enable factory trim */
|
|
|
+ ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* start all channels */
|
|
|
+ ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* mute all channels for now */
|
|
|
+ ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE,
|
|
|
+ TAS5086_SOFT_MUTE_ALL);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/* TAS5086 controls */
|
|
|
static const DECLARE_TLV_DB_SCALE(tas5086_dac_tlv, -10350, 50, 1);
|
|
|
|
|
@@ -691,14 +763,39 @@ static struct snd_soc_dai_driver tas5086_dai = {
|
|
|
};
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
+static int tas5086_soc_suspend(struct snd_soc_codec *codec)
|
|
|
+{
|
|
|
+ struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* Shut down all channels */
|
|
|
+ ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x60);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int tas5086_soc_resume(struct snd_soc_codec *codec)
|
|
|
{
|
|
|
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ tas5086_reset(priv);
|
|
|
+ regcache_mark_dirty(priv->regmap);
|
|
|
+
|
|
|
+ ret = tas5086_init(codec->dev, priv);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = regcache_sync(priv->regmap);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
|
|
|
- /* Restore codec state */
|
|
|
- return regcache_sync(priv->regmap);
|
|
|
+ return 0;
|
|
|
}
|
|
|
#else
|
|
|
+#define tas5086_soc_suspend NULL
|
|
|
#define tas5086_soc_resume NULL
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
|
@@ -710,23 +807,19 @@ static const struct of_device_id tas5086_dt_ids[] = {
|
|
|
MODULE_DEVICE_TABLE(of, tas5086_dt_ids);
|
|
|
#endif
|
|
|
|
|
|
-/* charge period values in microseconds */
|
|
|
-static const int tas5086_charge_period[] = {
|
|
|
- 13000, 16900, 23400, 31200, 41600, 54600, 72800, 96200,
|
|
|
- 130000, 156000, 234000, 312000, 416000, 546000, 728000, 962000,
|
|
|
- 1300000, 169000, 2340000, 3120000, 4160000, 5460000, 7280000, 9620000,
|
|
|
-};
|
|
|
-
|
|
|
static int tas5086_probe(struct snd_soc_codec *codec)
|
|
|
{
|
|
|
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
|
|
|
- int charge_period = 1300000; /* hardware default is 1300 ms */
|
|
|
- u8 pwm_start_mid_z = 0;
|
|
|
int i, ret;
|
|
|
|
|
|
+ priv->pwm_start_mid_z = 0;
|
|
|
+ priv->charge_period = 1300000; /* hardware default is 1300 ms */
|
|
|
+
|
|
|
if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) {
|
|
|
struct device_node *of_node = codec->dev->of_node;
|
|
|
- of_property_read_u32(of_node, "ti,charge-period", &charge_period);
|
|
|
+
|
|
|
+ of_property_read_u32(of_node, "ti,charge-period",
|
|
|
+ &priv->charge_period);
|
|
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
|
char name[25];
|
|
@@ -735,43 +828,11 @@ static int tas5086_probe(struct snd_soc_codec *codec)
|
|
|
"ti,mid-z-channel-%d", i + 1);
|
|
|
|
|
|
if (of_get_property(of_node, name, NULL) != NULL)
|
|
|
- pwm_start_mid_z |= 1 << i;
|
|
|
+ priv->pwm_start_mid_z |= 1 << i;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * If any of the channels is configured to start in Mid-Z mode,
|
|
|
- * configure 'part 1' of the PWM starts to use Mid-Z, and tell
|
|
|
- * all configured mid-z channels to start start under 'part 1'.
|
|
|
- */
|
|
|
- if (pwm_start_mid_z)
|
|
|
- regmap_write(priv->regmap, TAS5086_PWM_START,
|
|
|
- TAS5086_PWM_START_MIDZ_FOR_START_1 |
|
|
|
- pwm_start_mid_z);
|
|
|
-
|
|
|
- /* lookup and set split-capacitor charge period */
|
|
|
- if (charge_period == 0) {
|
|
|
- regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0);
|
|
|
- } else {
|
|
|
- i = index_in_array(tas5086_charge_period,
|
|
|
- ARRAY_SIZE(tas5086_charge_period),
|
|
|
- charge_period);
|
|
|
- if (i >= 0)
|
|
|
- regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE,
|
|
|
- i + 0x08);
|
|
|
- else
|
|
|
- dev_warn(codec->dev,
|
|
|
- "Invalid split-cap charge period of %d ns.\n",
|
|
|
- charge_period);
|
|
|
- }
|
|
|
-
|
|
|
- /* enable factory trim */
|
|
|
- ret = regmap_write(priv->regmap, TAS5086_OSC_TRIM, 0x00);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
- /* start all channels */
|
|
|
- ret = regmap_write(priv->regmap, TAS5086_SYS_CONTROL_2, 0x20);
|
|
|
+ ret = tas5086_init(codec->dev, priv);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
@@ -780,12 +841,6 @@ static int tas5086_probe(struct snd_soc_codec *codec)
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|
|
|
- /* mute all channels for now */
|
|
|
- ret = regmap_write(priv->regmap, TAS5086_SOFT_MUTE,
|
|
|
- TAS5086_SOFT_MUTE_ALL);
|
|
|
- if (ret < 0)
|
|
|
- return ret;
|
|
|
-
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -803,6 +858,7 @@ static int tas5086_remove(struct snd_soc_codec *codec)
|
|
|
static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
|
|
|
.probe = tas5086_probe,
|
|
|
.remove = tas5086_remove,
|
|
|
+ .suspend = tas5086_soc_suspend,
|
|
|
.resume = tas5086_soc_resume,
|
|
|
.controls = tas5086_controls,
|
|
|
.num_controls = ARRAY_SIZE(tas5086_controls),
|
|
@@ -862,17 +918,8 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
|
|
|
if (devm_gpio_request(dev, gpio_nreset, "TAS5086 Reset"))
|
|
|
gpio_nreset = -EINVAL;
|
|
|
|
|
|
- if (gpio_is_valid(gpio_nreset)) {
|
|
|
- /* Reset codec - minimum assertion time is 400ns */
|
|
|
- gpio_direction_output(gpio_nreset, 0);
|
|
|
- udelay(1);
|
|
|
- gpio_set_value(gpio_nreset, 1);
|
|
|
-
|
|
|
- /* Codec needs ~15ms to wake up */
|
|
|
- msleep(15);
|
|
|
- }
|
|
|
-
|
|
|
priv->gpio_nreset = gpio_nreset;
|
|
|
+ tas5086_reset(priv);
|
|
|
|
|
|
/* The TAS5086 always returns 0x03 in its TAS5086_DEV_ID register */
|
|
|
ret = regmap_read(priv->regmap, TAS5086_DEV_ID, &i);
|