diff options
Diffstat (limited to 'sound/soc/codecs/wm8580.c')
-rw-r--r-- | sound/soc/codecs/wm8580.c | 321 |
1 files changed, 152 insertions, 169 deletions
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 72deeabef4f..a2e0ed59b37 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -94,6 +94,8 @@ #define WM8580_MAX_REGISTER 0x35 +#define WM8580_DACOSR 0x40 + /* PLLB4 (register 7h) */ #define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60 #define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20 @@ -112,19 +114,7 @@ /* AIF control 1 (registers 9h-bh) */ #define WM8580_AIF_RATE_MASK 0x7 -#define WM8580_AIF_RATE_128 0x0 -#define WM8580_AIF_RATE_192 0x1 -#define WM8580_AIF_RATE_256 0x2 -#define WM8580_AIF_RATE_384 0x3 -#define WM8580_AIF_RATE_512 0x4 -#define WM8580_AIF_RATE_768 0x5 -#define WM8580_AIF_RATE_1152 0x6 - #define WM8580_AIF_BCLKSEL_MASK 0x18 -#define WM8580_AIF_BCLKSEL_64 0x00 -#define WM8580_AIF_BCLKSEL_128 0x08 -#define WM8580_AIF_BCLKSEL_256 0x10 -#define WM8580_AIF_BCLKSEL_SYSCLK 0x18 #define WM8580_AIF_MS 0x20 @@ -199,11 +189,12 @@ static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = { /* codec private data */ struct wm8580_priv { - struct snd_soc_codec codec; + enum snd_soc_control_type control_type; struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES]; u16 reg_cache[WM8580_MAX_REGISTER + 1]; struct pll_state a; struct pll_state b; + int sysclk[2]; }; static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); @@ -273,8 +264,8 @@ SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 1), SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 1), SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 1), -SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0), -SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), +SOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1), +SOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), }; static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { @@ -476,6 +467,10 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, return 0; } +static const int wm8580_sysclk_ratios[] = { + 128, 192, 256, 384, 512, 768, 1152, +}; + /* * Set PCM DAI bit size and sample rate. */ @@ -484,29 +479,68 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_device *socdev = rtd->socdev; - struct snd_soc_codec *codec = socdev->card->codec; - u16 paifb = snd_soc_read(codec, WM8580_PAIF3 + dai->id); + struct snd_soc_codec *codec = rtd->codec; + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + u16 paifa = 0; + u16 paifb = 0; + int i, ratio, osr; - paifb &= ~WM8580_AIF_LENGTH_MASK; /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: + paifa |= 0x8; break; case SNDRV_PCM_FORMAT_S20_3LE: + paifa |= 0x10; paifb |= WM8580_AIF_LENGTH_20; break; case SNDRV_PCM_FORMAT_S24_LE: + paifa |= 0x10; paifb |= WM8580_AIF_LENGTH_24; break; case SNDRV_PCM_FORMAT_S32_LE: + paifa |= 0x10; paifb |= WM8580_AIF_LENGTH_24; break; default: return -EINVAL; } - snd_soc_write(codec, WM8580_PAIF3 + dai->id, paifb); + /* Look up the SYSCLK ratio; accept only exact matches */ + ratio = wm8580->sysclk[dai->id] / params_rate(params); + for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++) + if (ratio == wm8580_sysclk_ratios[i]) + break; + if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) { + dev_err(codec->dev, "Invalid clock ratio %d/%d\n", + wm8580->sysclk[dai->id], params_rate(params)); + return -EINVAL; + } + paifa |= i; + dev_dbg(codec->dev, "Running at %dfs with %dHz clock\n", + wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (ratio) { + case 128: + case 192: + osr = WM8580_DACOSR; + dev_dbg(codec->dev, "Selecting 64x OSR\n"); + break; + default: + osr = 0; + dev_dbg(codec->dev, "Selecting 128x OSR\n"); + break; + } + + snd_soc_update_bits(codec, WM8580_PAIF3, WM8580_DACOSR, osr); + } + + snd_soc_update_bits(codec, WM8580_PAIF1 + dai->driver->id, + WM8580_AIF_RATE_MASK | WM8580_AIF_BCLKSEL_MASK, + paifa); + snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id, + WM8580_AIF_LENGTH_MASK, paifb); return 0; } @@ -518,8 +552,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int aifb; int can_invert_lrclk; - aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->id); - aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->id); + aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id); + aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id); aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP); @@ -585,8 +619,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - snd_soc_write(codec, WM8580_PAIF1 + codec_dai->id, aifa); - snd_soc_write(codec, WM8580_PAIF3 + codec_dai->id, aifb); + snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa); + snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb); return 0; } @@ -624,28 +658,6 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, snd_soc_write(codec, WM8580_PLLB4, reg); break; - case WM8580_DAC_CLKSEL: - reg = snd_soc_read(codec, WM8580_CLKSEL); - reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK; - - switch (div) { - case WM8580_CLKSRC_MCLK: - break; - - case WM8580_CLKSRC_PLLA: - reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA; - break; - - case WM8580_CLKSRC_PLLB: - reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB; - break; - - default: - return -EINVAL; - } - snd_soc_write(codec, WM8580_CLKSEL, reg); - break; - case WM8580_CLKOUTSRC: reg = snd_soc_read(codec, WM8580_PLLB4); reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; @@ -679,6 +691,55 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, return 0; } +static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + int sel, sel_mask, sel_shift; + + switch (dai->driver->id) { + case WM8580_DAI_PAIFRX: + sel_mask = 0x3; + sel_shift = 0; + break; + + case WM8580_DAI_PAIFTX: + sel_mask = 0xc; + sel_shift = 2; + break; + + default: + BUG_ON("Unknown DAI driver ID\n"); + return -EINVAL; + } + + switch (clk_id) { + case WM8580_CLKSRC_ADCMCLK: + if (dai->id != WM8580_DAI_PAIFTX) + return -EINVAL; + sel = 0 << sel_shift; + break; + case WM8580_CLKSRC_PLLA: + sel = 1 << sel_shift; + break; + case WM8580_CLKSRC_PLLB: + sel = 2 << sel_shift; + break; + case WM8580_CLKSRC_MCLK: + sel = 3 << sel_shift; + break; + default: + dev_err(codec->dev, "Unknown clock %d\n", clk_id); + return -EINVAL; + } + + /* We really should validate PLL settings but not yet */ + wm8580->sysclk[dai->id] = freq; + + return snd_soc_update_bits(codec, WM8580_CLKSEL, sel_mask, sel); +} + static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; @@ -732,6 +793,7 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec, SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static struct snd_soc_dai_ops wm8580_dai_ops_playback = { + .set_sysclk = wm8580_set_sysclk, .hw_params = wm8580_paif_hw_params, .set_fmt = wm8580_set_paif_dai_fmt, .set_clkdiv = wm8580_set_dai_clkdiv, @@ -740,16 +802,17 @@ static struct snd_soc_dai_ops wm8580_dai_ops_playback = { }; static struct snd_soc_dai_ops wm8580_dai_ops_capture = { + .set_sysclk = wm8580_set_sysclk, .hw_params = wm8580_paif_hw_params, .set_fmt = wm8580_set_paif_dai_fmt, .set_clkdiv = wm8580_set_dai_clkdiv, .set_pll = wm8580_set_dai_pll, }; -struct snd_soc_dai wm8580_dai[] = { +static struct snd_soc_dai_driver wm8580_dai[] = { { - .name = "WM8580 PAIFRX", - .id = 0, + .name = "wm8580-hifi-playback", + .id = WM8580_DAI_PAIFRX, .playback = { .stream_name = "Playback", .channels_min = 1, @@ -760,8 +823,8 @@ struct snd_soc_dai wm8580_dai[] = { .ops = &wm8580_dai_ops_playback, }, { - .name = "WM8580 PAIFTX", - .id = 1, + .name = "wm8580-hifi-capture", + .id = WM8580_DAI_PAIFTX, .capture = { .stream_name = "Capture", .channels_min = 2, @@ -772,90 +835,16 @@ struct snd_soc_dai wm8580_dai[] = { .ops = &wm8580_dai_ops_capture, }, }; -EXPORT_SYMBOL_GPL(wm8580_dai); - -static struct snd_soc_codec *wm8580_codec; -static int wm8580_probe(struct platform_device *pdev) +static int wm8580_probe(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec; - int ret = 0; - - if (wm8580_codec == NULL) { - dev_err(&pdev->dev, "Codec device not registered\n"); - return -ENODEV; - } - - socdev->card->codec = wm8580_codec; - codec = wm8580_codec; - - /* register pcms */ - ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if (ret < 0) { - dev_err(codec->dev, "failed to create pcms: %d\n", ret); - goto pcm_err; - } - - snd_soc_add_controls(codec, wm8580_snd_controls, - ARRAY_SIZE(wm8580_snd_controls)); - wm8580_add_widgets(codec); - - return ret; - -pcm_err: - return ret; -} - -/* power down chip */ -static int wm8580_remove(struct platform_device *pdev) -{ - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); - - return 0; -} - -struct snd_soc_codec_device soc_codec_dev_wm8580 = { - .probe = wm8580_probe, - .remove = wm8580_remove, -}; -EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580); - -static int wm8580_register(struct wm8580_priv *wm8580, - enum snd_soc_control_type control) -{ - int ret, i; - struct snd_soc_codec *codec = &wm8580->codec; - - if (wm8580_codec) { - dev_err(codec->dev, "Another WM8580 is registered\n"); - ret = -EINVAL; - goto err; - } - - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); - - snd_soc_codec_set_drvdata(codec, wm8580); - codec->name = "WM8580"; - codec->owner = THIS_MODULE; - codec->bias_level = SND_SOC_BIAS_OFF; - codec->set_bias_level = wm8580_set_bias_level; - codec->dai = wm8580_dai; - codec->num_dai = ARRAY_SIZE(wm8580_dai); - codec->reg_cache_size = ARRAY_SIZE(wm8580->reg_cache); - codec->reg_cache = &wm8580->reg_cache; - - memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg)); + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + int ret = 0,i; - ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); + ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8580->control_type); if (ret < 0) { dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); - goto err; + return ret; } for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++) @@ -865,7 +854,7 @@ static int wm8580_register(struct wm8580_priv *wm8580, wm8580->supplies); if (ret != 0) { dev_err(codec->dev, "Failed to request supplies: %d\n", ret); - goto err; + return ret; } ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies), @@ -882,74 +871,68 @@ static int wm8580_register(struct wm8580_priv *wm8580, goto err_regulator_enable; } - for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++) - wm8580_dai[i].dev = codec->dev; - wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - wm8580_codec = codec; - - ret = snd_soc_register_codec(codec); - if (ret != 0) { - dev_err(codec->dev, "Failed to register codec: %d\n", ret); - goto err_regulator_enable; - } - - ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai)); - if (ret != 0) { - dev_err(codec->dev, "Failed to register DAI: %d\n", ret); - goto err_codec; - } + snd_soc_add_controls(codec, wm8580_snd_controls, + ARRAY_SIZE(wm8580_snd_controls)); + wm8580_add_widgets(codec); return 0; -err_codec: - snd_soc_unregister_codec(codec); err_regulator_enable: regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); err_regulator_get: regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); -err: - kfree(wm8580); return ret; } -static void wm8580_unregister(struct wm8580_priv *wm8580) +/* power down chip */ +static int wm8580_remove(struct snd_soc_codec *codec) { - wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF); - snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai)); - snd_soc_unregister_codec(&wm8580->codec); + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + + wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF); + regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); - kfree(wm8580); - wm8580_codec = NULL; + + return 0; } +static struct snd_soc_codec_driver soc_codec_dev_wm8580 = { + .probe = wm8580_probe, + .remove = wm8580_remove, + .set_bias_level = wm8580_set_bias_level, + .reg_cache_size = ARRAY_SIZE(wm8580_reg), + .reg_word_size = sizeof(u16), + .reg_cache_default = &wm8580_reg, +}; + #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) static int wm8580_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8580_priv *wm8580; - struct snd_soc_codec *codec; + int ret; wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL); if (wm8580 == NULL) return -ENOMEM; - codec = &wm8580->codec; - i2c_set_clientdata(i2c, wm8580); - codec->control_data = i2c; + wm8580->control_type = SND_SOC_I2C; - codec->dev = &i2c->dev; - - return wm8580_register(wm8580, SND_SOC_I2C); + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai)); + if (ret < 0) + kfree(wm8580); + return ret; } static int wm8580_i2c_remove(struct i2c_client *client) { - struct wm8580_priv *wm8580 = i2c_get_clientdata(client); - wm8580_unregister(wm8580); + snd_soc_unregister_codec(&client->dev); + kfree(i2c_get_clientdata(client)); return 0; } @@ -961,7 +944,7 @@ MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id); static struct i2c_driver wm8580_i2c_driver = { .driver = { - .name = "wm8580", + .name = "wm8580-codec", .owner = THIS_MODULE, }, .probe = wm8580_i2c_probe, @@ -972,7 +955,7 @@ static struct i2c_driver wm8580_i2c_driver = { static int __init wm8580_modinit(void) { - int ret; + int ret = 0; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) ret = i2c_add_driver(&wm8580_i2c_driver); @@ -981,7 +964,7 @@ static int __init wm8580_modinit(void) } #endif - return 0; + return ret; } module_init(wm8580_modinit); |