ASoC: adau1701: allow configuration of PLL mode pins

The ADAU1701 has 2 hardware pins to configure the PLL mode in accordance
to the MCLK-to-LRCLK ratio. These pins have to be stable before the chip
is released from reset, and a full reset cycle, including a new firmware
download is needed whenever they change.

This patch adds GPIO properties to the DT bindings of the Codec, and
implements makes the set_sysclk memorize the configured sysclk.

Because the run-time parameters are unknown at probe time, the first
firmware download is postponed to the first hw_params call, when the
driver can determine the mclk/lrclk divider. Subsequent downloads
are only issued when the divider configuration changes.

Signed-off-by: Daniel Mack <zonque@gmail.com>
Acked-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@linaro.org>
This commit is contained in:
Daniel Mack
2013-06-24 16:31:30 +02:00
committed by Mark Brown
parent de9fc724da
commit 2352d4bf43
2 changed files with 104 additions and 7 deletions

View File

@@ -87,11 +87,16 @@
#define ADAU1701_OSCIPOW_OPD 0x04
#define ADAU1701_DACSET_DACINIT 1
#define ADAU1707_CLKDIV_UNSET (-1UL)
#define ADAU1701_FIRMWARE "adau1701.bin"
struct adau1701 {
int gpio_nreset;
int gpio_pll_mode[2];
unsigned int dai_fmt;
unsigned int pll_clkdiv;
unsigned int sysclk;
};
static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -184,12 +189,38 @@ static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg)
return value;
}
static int adau1701_reset(struct snd_soc_codec *codec)
static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
{
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *client = to_i2c_client(codec->dev);
int ret;
if (clkdiv != ADAU1707_CLKDIV_UNSET &&
gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
gpio_is_valid(adau1701->gpio_pll_mode[1])) {
switch (clkdiv) {
case 64:
gpio_set_value(adau1701->gpio_pll_mode[0], 0);
gpio_set_value(adau1701->gpio_pll_mode[1], 0);
break;
case 256:
gpio_set_value(adau1701->gpio_pll_mode[0], 0);
gpio_set_value(adau1701->gpio_pll_mode[1], 1);
break;
case 384:
gpio_set_value(adau1701->gpio_pll_mode[0], 1);
gpio_set_value(adau1701->gpio_pll_mode[1], 0);
break;
case 0: /* fallback */
case 512:
gpio_set_value(adau1701->gpio_pll_mode[0], 1);
gpio_set_value(adau1701->gpio_pll_mode[1], 1);
break;
}
}
adau1701->pll_clkdiv = clkdiv;
if (gpio_is_valid(adau1701->gpio_nreset)) {
gpio_set_value(adau1701->gpio_nreset, 0);
/* minimum reset time is 20ns */
@@ -199,10 +230,16 @@ static int adau1701_reset(struct snd_soc_codec *codec)
mdelay(85);
}
ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
if (ret) {
dev_warn(codec->dev, "Failed to load firmware\n");
return ret;
/*
* Postpone the firmware download to a point in time when we
* know the correct PLL setup
*/
if (clkdiv != ADAU1707_CLKDIV_UNSET) {
ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
if (ret) {
dev_warn(codec->dev, "Failed to load firmware\n");
return ret;
}
}
snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
@@ -285,8 +322,22 @@ static int adau1701_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 adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int clkdiv = adau1701->sysclk / params_rate(params);
snd_pcm_format_t format;
unsigned int val;
int ret;
/*
* If the mclk/lrclk ratio changes, the chip needs updated PLL
* mode GPIO settings, and a full reset cycle, including a new
* firmware upload.
*/
if (clkdiv != adau1701->pll_clkdiv) {
ret = adau1701_reset(codec, clkdiv);
if (ret < 0)
return ret;
}
switch (params_rate(params)) {
case 192000:
@@ -429,6 +480,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
unsigned int val;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
@@ -442,6 +494,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
}
snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val);
adau1701->sysclk = freq;
return 0;
}
@@ -489,11 +542,21 @@ MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
static int adau1701_probe(struct snd_soc_codec *codec)
{
int ret;
struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
codec->control_data = to_i2c_client(codec->dev);
ret = adau1701_reset(codec);
if (ret)
/*
* Let the pll_clkdiv variable default to something that won't happen
* at runtime. That way, we can postpone the firmware download from
* adau1701_reset() to a point in time when we know the correct PLL
* mode parameters.
*/
adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
/* initalize with pre-configured pll mode settings */
ret = adau1701_reset(codec, adau1701->pll_clkdiv);
if (ret < 0)
return ret;
return 0;
@@ -526,6 +589,7 @@ static int adau1701_i2c_probe(struct i2c_client *client,
struct adau1701 *adau1701;
struct device *dev = &client->dev;
int gpio_nreset = -EINVAL;
int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
int ret;
adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
@@ -536,6 +600,16 @@ static int adau1701_i2c_probe(struct i2c_client *client,
gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
return gpio_nreset;
gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
"adi,pll-mode-gpios", 0);
if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
return gpio_pll_mode[0];
gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
"adi,pll-mode-gpios", 1);
if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
return gpio_pll_mode[1];
}
if (gpio_is_valid(gpio_nreset)) {
@@ -545,7 +619,24 @@ static int adau1701_i2c_probe(struct i2c_client *client,
return ret;
}
if (gpio_is_valid(gpio_pll_mode[0]) &&
gpio_is_valid(gpio_pll_mode[1])) {
ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
GPIOF_OUT_INIT_LOW,
"ADAU1701 PLL mode 0");
if (ret < 0)
return ret;
ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
GPIOF_OUT_INIT_LOW,
"ADAU1701 PLL mode 1");
if (ret < 0)
return ret;
}
adau1701->gpio_nreset = gpio_nreset;
adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
i2c_set_clientdata(client, adau1701);
ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,