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 */ int i, ret; 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); } /* 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; /* set master volume to 0 dB */ ret = regmap_write(priv->regmap, TAS5086_MASTER_VOL, 0x30); 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; }
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; }
static int tas5086_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec); int val; int ret; priv->rate = params_rate(params); /* Look up the sample rate and refer to the offset in the list */ val = index_in_array(tas5086_sample_rates, ARRAY_SIZE(tas5086_sample_rates), priv->rate); if (val < 0) { dev_err(codec->dev, "Invalid sample rate\n"); return -EINVAL; } ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, TAS5086_CLOCK_RATE_MASK, TAS5086_CLOCK_RATE(val)); if (ret < 0) return ret; /* MCLK / Fs ratio */ val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios), priv->mclk / priv->rate); if (val < 0) { dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n"); return -EINVAL; } ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, TAS5086_CLOCK_RATIO_MASK, TAS5086_CLOCK_RATIO(val)); if (ret < 0) return ret; ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, TAS5086_CLOCK_SCLK_RATIO_48, (priv->sclk == 48 * priv->rate) ? TAS5086_CLOCK_SCLK_RATIO_48 : 0); if (ret < 0) return ret; /* * The chip has a very unituitive register mapping and muxes information * about data format and sample depth into the same register, but not on * a logical bit-boundary. Hence, we have to refer to the format passed * in the set_dai_fmt() callback and set up everything from here. * * First, determine the 'base' value, using the format ... */ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_RIGHT_J: val = 0x00; break; case SND_SOC_DAIFMT_I2S: val = 0x03; break; case SND_SOC_DAIFMT_LEFT_J: val = 0x06; break; default: dev_err(codec->dev, "Invalid DAI format\n"); return -EINVAL; } /* ... then add the offset for the sample bit depth. */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: val += 0; break; case SNDRV_PCM_FORMAT_S20_3LE: val += 1; break; case SNDRV_PCM_FORMAT_S24_3LE: val += 2; break; default: dev_err(codec->dev, "Invalid bit width\n"); return -EINVAL; } ret = regmap_write(priv->regmap, TAS5086_SERIAL_DATA_IF, val); if (ret < 0) return ret; /* clock is considered valid now */ ret = regmap_update_bits(priv->regmap, TAS5086_CLOCK_CONTROL, TAS5086_CLOCK_VALID, TAS5086_CLOCK_VALID); if (ret < 0) return ret; return tas5086_set_deemph(codec); }