提交 72a04d6b 编写于 作者: M Mark Brown

Merge remote-tracking branches 'asoc/topic/adau', 'asoc/topic/adau7002',...

Merge remote-tracking branches 'asoc/topic/adau', 'asoc/topic/adau7002', 'asoc/topic/adsp', 'asoc/topic/ak4613' and 'asoc/topic/ak4642' into asoc-next
......@@ -13,6 +13,11 @@ Required properties:
- reg: The i2c address. Value depends on the state of ADDR0
and ADDR1, as wired in hardware.
Optional properties:
- clock-names: If provided must be "mclk".
- clocks: phandle + clock-specifiers for the clock that provides
the audio master clock for the device.
Examples:
#include <dt-bindings/sound/adau17x1.h>
......@@ -20,5 +25,8 @@ Examples:
adau1361@38 {
compatible = "adi,adau1761";
reg = <0x38>;
clock-names = "mclk";
clocks = <&audio_clock>;
};
};
Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter
Required properties:
- compatible: Must be "adi,adau7002"
Optional properties:
- IOVDD-supply: Phandle and specifier for the power supply providing the IOVDD
supply as covered in Documentation/devicetree/bindings/regulator/regulator.txt
If this property is not present it is assumed that the supply pin is
hardwired to always on.
Example:
adau7002: pdm-to-i2s {
compatible = "adi,adau7002";
IOVDD-supply = <&supply>;
};
......@@ -14,6 +14,7 @@
#define _WM_ARIZONA_CORE_H
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/arizona/pdata.h>
......@@ -148,8 +149,17 @@ struct arizona {
uint16_t dac_comp_coeff;
uint8_t dac_comp_enabled;
struct mutex dac_comp_lock;
struct blocking_notifier_head notifier;
};
static inline int arizona_call_notifiers(struct arizona *arizona,
unsigned long event,
void *data)
{
return blocking_notifier_call_chain(&arizona->notifier, event, data);
}
int arizona_clk32k_enable(struct arizona *arizona);
int arizona_clk32k_disable(struct arizona *arizona);
......
......@@ -68,6 +68,7 @@ struct snd_compr_runtime {
* @ops: pointer to DSP callbacks
* @runtime: pointer to runtime structure
* @device: device pointer
* @error_work: delayed work used when closing the stream due to an error
* @direction: stream direction, playback/recording
* @metadata_set: metadata set flag, true when set
* @next_track: has userspace signal next track transition, true when set
......@@ -78,6 +79,7 @@ struct snd_compr_stream {
struct snd_compr_ops *ops;
struct snd_compr_runtime *runtime;
struct snd_compr *device;
struct delayed_work error_work;
enum snd_compr_direction direction;
bool metadata_set;
bool next_track;
......@@ -187,4 +189,7 @@ static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
wake_up(&stream->runtime->sleep);
}
int snd_compr_stop_error(struct snd_compr_stream *stream,
snd_pcm_state_t state);
#endif
......@@ -67,6 +67,8 @@ struct snd_compr_file {
struct snd_compr_stream stream;
};
static void error_delayed_work(struct work_struct *work);
/*
* a note on stream states used:
* we use following states in the compressed core
......@@ -123,6 +125,9 @@ static int snd_compr_open(struct inode *inode, struct file *f)
snd_card_unref(compr->card);
return -ENOMEM;
}
INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
data->stream.ops = compr->ops;
data->stream.direction = dirn;
data->stream.private_data = compr->private_data;
......@@ -153,6 +158,8 @@ static int snd_compr_free(struct inode *inode, struct file *f)
struct snd_compr_file *data = f->private_data;
struct snd_compr_runtime *runtime = data->stream.runtime;
cancel_delayed_work_sync(&data->stream.error_work);
switch (runtime->state) {
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_DRAINING:
......@@ -237,6 +244,15 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
avail = snd_compr_calc_avail(stream, &ioctl_avail);
ioctl_avail.avail = avail;
switch (stream->runtime->state) {
case SNDRV_PCM_STATE_OPEN:
return -EBADFD;
case SNDRV_PCM_STATE_XRUN:
return -EPIPE;
default:
break;
}
if (copy_to_user((__u64 __user *)arg,
&ioctl_avail, sizeof(ioctl_avail)))
return -EFAULT;
......@@ -346,11 +362,13 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
switch (stream->runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_PREPARED:
case SNDRV_PCM_STATE_XRUN:
case SNDRV_PCM_STATE_SUSPENDED:
case SNDRV_PCM_STATE_DISCONNECTED:
retval = -EBADFD;
goto out;
case SNDRV_PCM_STATE_XRUN:
retval = -EPIPE;
goto out;
}
avail = snd_compr_get_avail(stream);
......@@ -399,10 +417,16 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
stream = &data->stream;
mutex_lock(&stream->device->lock);
if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
switch (stream->runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_XRUN:
retval = snd_compr_get_poll(stream) | POLLERR;
goto out;
default:
break;
}
poll_wait(f, &stream->runtime->sleep, wait);
avail = snd_compr_get_avail(stream);
......@@ -697,6 +721,45 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
return retval;
}
static void error_delayed_work(struct work_struct *work)
{
struct snd_compr_stream *stream;
stream = container_of(work, struct snd_compr_stream, error_work.work);
mutex_lock(&stream->device->lock);
stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
wake_up(&stream->runtime->sleep);
mutex_unlock(&stream->device->lock);
}
/*
* snd_compr_stop_error: Report a fatal error on a stream
* @stream: pointer to stream
* @state: state to transition the stream to
*
* Stop the stream and set its state.
*
* Should be called with compressed device lock held.
*/
int snd_compr_stop_error(struct snd_compr_stream *stream,
snd_pcm_state_t state)
{
if (stream->runtime->state == state)
return 0;
stream->runtime->state = state;
pr_debug("Changing state to: %d\n", state);
queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
return 0;
}
EXPORT_SYMBOL_GPL(snd_compr_stop_error);
static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
{
int ret;
......
......@@ -32,6 +32,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ADAU1977_SPI if SPI_MASTER
select SND_SOC_ADAU1977_I2C if I2C
select SND_SOC_ADAU1701 if I2C
select SND_SOC_ADAU7002
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
......@@ -269,8 +270,12 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
config SND_SOC_ADAU_UTILS
tristate
config SND_SOC_ADAU1373
tristate
select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
......@@ -280,6 +285,7 @@ config SND_SOC_ADAU1701
config SND_SOC_ADAU17X1
tristate
select SND_SOC_SIGMADSP_REGMAP
select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1761
tristate
......@@ -322,6 +328,9 @@ config SND_SOC_ADAU1977_I2C
select SND_SOC_ADAU1977
select REGMAP_I2C
config SND_SOC_ADAU7002
tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter"
config SND_SOC_ADAV80X
tristate
......
......@@ -7,6 +7,7 @@ snd-soc-ad193x-spi-objs := ad193x-spi.o
snd-soc-ad193x-i2c-objs := ad193x-i2c.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-adau-utils-objs := adau-utils.o
snd-soc-adau1373-objs := adau1373.o
snd-soc-adau1701-objs := adau1701.o
snd-soc-adau17x1-objs := adau17x1.o
......@@ -19,6 +20,7 @@ snd-soc-adau1781-spi-objs := adau1781-spi.o
snd-soc-adau1977-objs := adau1977.o
snd-soc-adau1977-spi-objs := adau1977-spi.o
snd-soc-adau1977-i2c-objs := adau1977-i2c.o
snd-soc-adau7002-objs := adau7002.o
snd-soc-adav80x-objs := adav80x.o
snd-soc-adav801-objs := adav801.o
snd-soc-adav803-objs := adav803.o
......@@ -220,6 +222,7 @@ obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o
obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_ADAU_UTILS) += snd-soc-adau-utils.o
obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
......@@ -232,6 +235,7 @@ obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
......
/*
* Shared helper functions for devices from the ADAU family
*
* Copyright 2011-2016 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/gcd.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "adau-utils.h"
int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
uint8_t regs[5])
{
unsigned int r, n, m, i, j;
unsigned int div;
if (!freq_out) {
r = 0;
n = 0;
m = 0;
div = 0;
} else {
if (freq_out % freq_in != 0) {
div = DIV_ROUND_UP(freq_in, 13500000);
freq_in /= div;
r = freq_out / freq_in;
i = freq_out % freq_in;
j = gcd(i, freq_in);
n = i / j;
m = freq_in / j;
div--;
} else {
r = freq_out / freq_in;
n = 0;
m = 0;
div = 0;
}
if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
return -EINVAL;
}
regs[0] = m >> 8;
regs[1] = m & 0xff;
regs[2] = n >> 8;
regs[3] = n & 0xff;
regs[4] = (r << 3) | (div << 1);
if (m != 0)
regs[4] |= 1; /* Fractional mode */
return 0;
}
EXPORT_SYMBOL_GPL(adau_calc_pll_cfg);
MODULE_DESCRIPTION("ASoC ADAU audio CODECs shared helper functions");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL v2");
#ifndef SOUND_SOC_CODECS_ADAU_PLL_H
#define SOUND_SOC_CODECS_ADAU_PLL_H
int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
uint8_t regs[5]);
#endif
......@@ -23,6 +23,7 @@
#include <sound/adau1373.h>
#include "adau1373.h"
#include "adau-utils.h"
struct adau1373_dai {
unsigned int clk_src;
......@@ -1254,7 +1255,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
{
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
unsigned int dpll_div = 0;
unsigned int x, r, n, m, i, j, mode;
uint8_t pll_regs[5];
int ret;
switch (pll_id) {
case ADAU1373_PLL1:
......@@ -1295,27 +1297,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
dpll_div++;
}
if (freq_out % freq_in != 0) {
/* fout = fin * (r + (n/m)) / x */
x = DIV_ROUND_UP(freq_in, 13500000);
freq_in /= x;
r = freq_out / freq_in;
i = freq_out % freq_in;
j = gcd(i, freq_in);
n = i / j;
m = freq_in / j;
x--;
mode = 1;
} else {
/* fout = fin / r */
r = freq_out / freq_in;
n = 0;
m = 0;
x = 0;
mode = 0;
}
if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
ret = adau_calc_pll_cfg(freq_in, freq_out, pll_regs);
if (ret)
return -EINVAL;
if (dpll_div) {
......@@ -1330,12 +1313,11 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id),
(source << 4) | dpll_div);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id),
(r << 3) | (x << 1) | mode);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), pll_regs[0]);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), pll_regs[1]);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), pll_regs[2]);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), pll_regs[3]);
regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id), pll_regs[4]);
/* Set sysclk to pll_rate / 4 */
regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
......
......@@ -31,7 +31,7 @@ static int adau1761_i2c_probe(struct i2c_client *client,
static int adau1761_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
adau17x1_remove(&client->dev);
return 0;
}
......
......@@ -48,7 +48,7 @@ static int adau1761_spi_probe(struct spi_device *spi)
static int adau1761_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
adau17x1_remove(&spi->dev);
return 0;
}
......
......@@ -31,7 +31,7 @@ static int adau1781_i2c_probe(struct i2c_client *client,
static int adau1781_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_codec(&client->dev);
adau17x1_remove(&client->dev);
return 0;
}
......
......@@ -48,7 +48,7 @@ static int adau1781_spi_probe(struct spi_device *spi)
static int adau1781_spi_remove(struct spi_device *spi)
{
snd_soc_unregister_codec(&spi->dev);
adau17x1_remove(&spi->dev);
return 0;
}
......
......@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
......@@ -23,6 +24,7 @@
#include "sigmadsp.h"
#include "adau17x1.h"
#include "adau-utils.h"
static const char * const adau17x1_capture_mixer_boost_text[] = {
"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
......@@ -302,6 +304,116 @@ bool adau17x1_has_dsp(struct adau *adau)
}
EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
if (freq_in < 8000000 || freq_in > 27000000)
return -EINVAL;
ret = adau_calc_pll_cfg(freq_in, freq_out, adau->pll_regs);
if (ret < 0)
return ret;
/* The PLL register is 6 bytes long and can only be written at once. */
ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
if (ret)
return ret;
adau->pll_freq = freq_out;
return 0;
}
static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
bool is_pll;
bool was_pll;
switch (clk_id) {
case ADAU17X1_CLK_SRC_MCLK:
is_pll = false;
break;
case ADAU17X1_CLK_SRC_PLL_AUTO:
if (!adau->mclk)
return -EINVAL;
/* Fall-through */
case ADAU17X1_CLK_SRC_PLL:
is_pll = true;
break;
default:
return -EINVAL;
}
switch (adau->clk_src) {
case ADAU17X1_CLK_SRC_MCLK:
was_pll = false;
break;
case ADAU17X1_CLK_SRC_PLL:
case ADAU17X1_CLK_SRC_PLL_AUTO:
was_pll = true;
break;
default:
return -EINVAL;
}
adau->sysclk = freq;
if (is_pll != was_pll) {
if (is_pll) {
snd_soc_dapm_add_routes(dapm,
&adau17x1_dapm_pll_route, 1);
} else {
snd_soc_dapm_del_routes(dapm,
&adau17x1_dapm_pll_route, 1);
}
}
adau->clk_src = clk_id;
return 0;
}
static int adau17x1_auto_pll(struct snd_soc_dai *dai,
struct snd_pcm_hw_params *params)
{
struct adau *adau = snd_soc_dai_get_drvdata(dai);
unsigned int pll_rate;
switch (params_rate(params)) {
case 48000:
case 8000:
case 12000:
case 16000:
case 24000:
case 32000:
case 96000:
pll_rate = 48000 * 1024;
break;
case 44100:
case 7350:
case 11025:
case 14700:
case 22050:
case 29400:
case 88200:
pll_rate = 44100 * 1024;
break;
default:
return -EINVAL;
}
return adau17x1_set_dai_pll(dai, ADAU17X1_PLL, ADAU17X1_PLL_SRC_MCLK,
clk_get_rate(adau->mclk), pll_rate);
}
static int adau17x1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
......@@ -311,10 +423,19 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
unsigned int freq;
int ret;
if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
switch (adau->clk_src) {
case ADAU17X1_CLK_SRC_PLL_AUTO:
ret = adau17x1_auto_pll(dai, params);
if (ret)
return ret;
/* Fall-through */
case ADAU17X1_CLK_SRC_PLL:
freq = adau->pll_freq;
else
break;
default:
freq = adau->sysclk;
break;
}
if (freq % params_rate(params) != 0)
return -EINVAL;
......@@ -386,93 +507,6 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
}
static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
struct snd_soc_codec *codec = dai->codec;
struct adau *adau = snd_soc_codec_get_drvdata(codec);
unsigned int r, n, m, i, j;
unsigned int div;
int ret;
if (freq_in < 8000000 || freq_in > 27000000)
return -EINVAL;
if (!freq_out) {
r = 0;
n = 0;
m = 0;
div = 0;
} else {
if (freq_out % freq_in != 0) {
div = DIV_ROUND_UP(freq_in, 13500000);
freq_in /= div;
r = freq_out / freq_in;
i = freq_out % freq_in;
j = gcd(i, freq_in);
n = i / j;
m = freq_in / j;
div--;
} else {
r = freq_out / freq_in;
n = 0;
m = 0;
div = 0;
}
if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
return -EINVAL;
}
adau->pll_regs[0] = m >> 8;
adau->pll_regs[1] = m & 0xff;
adau->pll_regs[2] = n >> 8;
adau->pll_regs[3] = n & 0xff;
adau->pll_regs[4] = (r << 3) | (div << 1);
if (m != 0)
adau->pll_regs[4] |= 1; /* Fractional mode */
/* The PLL register is 6 bytes long and can only be written at once. */
ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
if (ret)
return ret;
adau->pll_freq = freq_out;
return 0;
}
static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
switch (clk_id) {
case ADAU17X1_CLK_SRC_MCLK:
case ADAU17X1_CLK_SRC_PLL:
break;
default:
return -EINVAL;
}
adau->sysclk = freq;
if (adau->clk_src != clk_id) {
if (clk_id == ADAU17X1_CLK_SRC_PLL) {
snd_soc_dapm_add_routes(dapm,
&adau17x1_dapm_pll_route, 1);
} else {
snd_soc_dapm_del_routes(dapm,
&adau17x1_dapm_pll_route, 1);
}
}
adau->clk_src = clk_id;
return 0;
}
static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
......@@ -857,6 +891,10 @@ int adau17x1_add_routes(struct snd_soc_codec *codec)
ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
}
if (adau->clk_src != ADAU17X1_CLK_SRC_MCLK)
snd_soc_dapm_add_routes(dapm, &adau17x1_dapm_pll_route, 1);
return ret;
}
EXPORT_SYMBOL_GPL(adau17x1_add_routes);
......@@ -879,6 +917,7 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
const char *firmware_name)
{
struct adau *adau;
int ret;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
......@@ -887,6 +926,30 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
if (!adau)
return -ENOMEM;
adau->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(adau->mclk)) {
if (PTR_ERR(adau->mclk) != -ENOENT)
return PTR_ERR(adau->mclk);
/* Clock is optional (for the driver) */
adau->mclk = NULL;
} else if (adau->mclk) {
adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO;
/*
* Any valid PLL output rate will work at this point, use one
* that is likely to be chosen later as well. The register will
* be written when the PLL is powered up for the first time.
*/
ret = adau_calc_pll_cfg(clk_get_rate(adau->mclk), 48000 * 1024,
adau->pll_regs);
if (ret < 0)
return ret;
ret = clk_prepare_enable(adau->mclk);
if (ret)
return ret;
}
adau->regmap = regmap;
adau->switch_mode = switch_mode;
adau->type = type;
......@@ -910,6 +973,16 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
}
EXPORT_SYMBOL_GPL(adau17x1_probe);
void adau17x1_remove(struct device *dev)
{
struct adau *adau = dev_get_drvdata(dev);
snd_soc_unregister_codec(dev);
if (adau->mclk)
clk_disable_unprepare(adau->mclk);
}
EXPORT_SYMBOL_GPL(adau17x1_remove);
MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");
......@@ -22,13 +22,18 @@ enum adau17x1_pll_src {
};
enum adau17x1_clk_src {
/* Automatically configure PLL based on the sample rate */
ADAU17X1_CLK_SRC_PLL_AUTO,
ADAU17X1_CLK_SRC_MCLK,
ADAU17X1_CLK_SRC_PLL,
};
struct clk;
struct adau {
unsigned int sysclk;
unsigned int pll_freq;
struct clk *mclk;
enum adau17x1_clk_src clk_src;
enum adau17x1_type type;
......@@ -52,6 +57,7 @@ int adau17x1_add_routes(struct snd_soc_codec *codec);
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev),
const char *firmware_name);
void adau17x1_remove(struct device *dev);
int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
enum adau17x1_micbias_voltage micbias);
bool adau17x1_readable_register(struct device *dev, unsigned int reg);
......
/*
* ADAU7002 Stereo PDM-to-I2S/TDM converter driver
*
* Copyright 2014-2016 Analog Devices
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
static const struct snd_soc_dapm_widget adau7002_widgets[] = {
SND_SOC_DAPM_INPUT("PDM_DAT"),
SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0),
};
static const struct snd_soc_dapm_route adau7002_routes[] = {
{ "Capture", NULL, "PDM_DAT" },
{ "Capture", NULL, "IOVDD" },
};
static struct snd_soc_dai_driver adau7002_dai = {
.name = "adau7002-hifi",
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
.sig_bits = 20,
},
};
static const struct snd_soc_codec_driver adau7002_codec_driver = {
.dapm_widgets = adau7002_widgets,
.num_dapm_widgets = ARRAY_SIZE(adau7002_widgets),
.dapm_routes = adau7002_routes,
.num_dapm_routes = ARRAY_SIZE(adau7002_routes),
};
static int adau7002_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &adau7002_codec_driver,
&adau7002_dai, 1);
}
static int adau7002_remove(struct platform_device *pdev)
{
snd_soc_unregister_codec(&pdev->dev);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id adau7002_dt_ids[] = {
{ .compatible = "adi,adau7002", },
{ }
};
MODULE_DEVICE_TABLE(of, adau7002_dt_ids);
#endif
static struct platform_driver adau7002_driver = {
.driver = {
.name = "adau7002",
.of_match_table = of_match_ptr(adau7002_dt_ids),
},
.probe = adau7002_probe,
.remove = adau7002_remove,
};
module_platform_driver(adau7002_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ADAU7002 Stereo PDM-to-I2S/TDM Converter driver");
MODULE_LICENSE("GPL v2");
......@@ -437,15 +437,25 @@ static struct snd_soc_dai_driver ak4613_dai = {
.symmetric_rates = 1,
};
static int ak4613_resume(struct snd_soc_codec *codec)
static int ak4613_suspend(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
return 0;
}
static int ak4613_resume(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
regcache_cache_only(regmap, false);
return regcache_sync(regmap);
}
static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
.suspend = ak4613_suspend,
.resume = ak4613_resume,
.set_bias_level = ak4613_set_bias_level,
.controls = ak4613_snd_controls,
......
......@@ -523,15 +523,23 @@ static struct snd_soc_dai_driver ak4642_dai = {
.symmetric_rates = 1,
};
static int ak4642_resume(struct snd_soc_codec *codec)
static int ak4642_suspend(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
regcache_sync(regmap);
return 0;
}
static int ak4642_resume(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
regcache_cache_only(regmap, false);
regcache_sync(regmap);
return 0;
}
static int ak4642_probe(struct snd_soc_codec *codec)
{
struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
......@@ -544,6 +552,7 @@ static int ak4642_probe(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
.probe = ak4642_probe,
.suspend = ak4642_suspend,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
.controls = ak4642_snd_controls,
......
......@@ -324,6 +324,17 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
int arizona_init_notifiers(struct snd_soc_codec *codec)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
return 0;
}
EXPORT_SYMBOL_GPL(arizona_init_notifiers);
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
......@@ -2573,6 +2584,30 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
int arizona_register_notifier(struct snd_soc_codec *codec,
struct notifier_block *nb,
int (*notify)(struct notifier_block *nb,
unsigned long action, void *data))
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
nb->notifier_call = notify;
return blocking_notifier_chain_register(&arizona->notifier, nb);
}
EXPORT_SYMBOL_GPL(arizona_register_notifier);
int arizona_unregister_notifier(struct snd_soc_codec *codec,
struct notifier_block *nb)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
return blocking_notifier_chain_unregister(&arizona->notifier, nb);
}
EXPORT_SYMBOL_GPL(arizona_unregister_notifier);
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
......@@ -63,6 +63,9 @@
#define ARIZONA_DVFS_SR1_RQ 0x001
#define ARIZONA_DVFS_ADSP1_RQ 0x100
/* Notifier events */
#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
struct arizona;
struct wm_adsp;
......@@ -95,6 +98,10 @@ struct arizona_priv {
bool dvfs_cached;
};
struct arizona_voice_trigger_info {
int core;
};
#define ARIZONA_NUM_MIXER_INPUTS 104
extern const unsigned int arizona_mixer_tlv[];
......@@ -306,6 +313,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
extern int arizona_init_spk(struct snd_soc_codec *codec);
extern int arizona_init_gpio(struct snd_soc_codec *codec);
extern int arizona_init_mono(struct snd_soc_codec *codec);
extern int arizona_init_notifiers(struct snd_soc_codec *codec);
extern int arizona_free_spk(struct snd_soc_codec *codec);
......@@ -317,4 +325,13 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
extern int arizona_register_notifier(struct snd_soc_codec *codec,
struct notifier_block *nb,
int (*notify)(struct notifier_block *nb,
unsigned long action,
void *data));
extern int arizona_unregister_notifier(struct snd_soc_codec *codec,
struct notifier_block *nb);
#endif
......@@ -1067,6 +1067,7 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
{
struct cs47l24_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
struct arizona_voice_trigger_info info;
int serviced = 0;
int i, ret;
......@@ -1074,6 +1075,12 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
if (ret != -ENODEV)
serviced++;
if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
info.core = i;
arizona_call_notifiers(arizona,
ARIZONA_NOTIFY_VOICE_TRIGGER,
&info);
}
}
if (!serviced) {
......@@ -1096,6 +1103,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
arizona_init_notifiers(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
......
......@@ -2223,6 +2223,7 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
{
struct wm5110_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
struct arizona_voice_trigger_info info;
int serviced = 0;
int i, ret;
......@@ -2230,6 +2231,12 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
if (ret != -ENODEV)
serviced++;
if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
info.core = i;
arizona_call_notifiers(arizona,
ARIZONA_NOTIFY_VOICE_TRIGGER,
&info);
}
}
if (!serviced) {
......@@ -2252,6 +2259,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
arizona_init_notifiers(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", wm5110_adsp2_irq,
......
......@@ -2366,13 +2366,15 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
dsp->running = false;
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
ADSP2_SYS_ENA | ADSP2_CORE_ENA |
ADSP2_START, 0);
ADSP2_CORE_ENA | ADSP2_START, 0);
/* Make sure DMAs are quiesced */
regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
ADSP2_SYS_ENA, 0);
list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
......@@ -3037,12 +3039,8 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
buf = compr->buf;
if (!compr->buf) {
ret = -ENXIO;
goto out;
}
if (compr->buf->error) {
if (!compr->buf || compr->buf->error) {
snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
ret = -EIO;
goto out;
}
......@@ -3060,8 +3058,12 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
*/
if (buf->avail < wm_adsp_compr_frag_words(compr)) {
ret = wm_adsp_buffer_get_error(buf);
if (ret < 0)
if (ret < 0) {
if (compr->buf->error)
snd_compr_stop_error(stream,
SNDRV_PCM_STATE_XRUN);
goto out;
}
ret = wm_adsp_buffer_reenable_irq(buf);
if (ret < 0) {
......@@ -3156,11 +3158,10 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
if (!compr->buf)
return -ENXIO;
if (compr->buf->error)
if (!compr->buf || compr->buf->error) {
snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
return -EIO;
}
count /= WM_ADSP_DATA_WORD_SIZE;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册