diff options
Diffstat (limited to 'sound/soc/codecs/tas5086.c')
-rw-r--r-- | sound/soc/codecs/tas5086.c | 173 |
1 files changed, 63 insertions, 110 deletions
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index a895a5e..6d31d88 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -37,7 +37,6 @@ #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> @@ -245,8 +244,6 @@ 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 */ @@ -432,7 +429,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream, default: dev_err(codec->dev, "Invalid bit width\n"); return -EINVAL; - } + }; ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val); if (ret < 0) @@ -459,75 +456,6 @@ 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); @@ -763,39 +691,14 @@ 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; - return 0; + /* Restore codec state */ + return regcache_sync(priv->regmap); } #else -#define tas5086_soc_suspend NULL #define tas5086_soc_resume NULL #endif /* CONFIG_PM */ @@ -807,19 +710,23 @@ 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", - &priv->charge_period); + of_property_read_u32(of_node, "ti,charge-period", &charge_period); for (i = 0; i < 6; i++) { char name[25]; @@ -828,11 +735,43 @@ 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) - priv->pwm_start_mid_z |= 1 << i; + pwm_start_mid_z |= 1 << i; } } - ret = tas5086_init(codec->dev, priv); + /* + * 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); if (ret < 0) return ret; @@ -841,6 +780,12 @@ 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; } @@ -858,7 +803,6 @@ 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), @@ -918,8 +862,17 @@ 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); |