Commit 27d62d4a authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/stm32', 'asoc/topic/sun4i-i2s',...

Merge remote-tracking branches 'asoc/topic/stm32', 'asoc/topic/sun4i-i2s', 'asoc/topic/sunxi', 'asoc/topic/symmetry' and 'asoc/topic/tas5720' into asoc-next
...@@ -20,11 +20,6 @@ Required properties: ...@@ -20,11 +20,6 @@ Required properties:
Optional properties: Optional properties:
- resets: Reference to a reset controller asserting the SAI - resets: Reference to a reset controller asserting the SAI
- st,sync: specify synchronization mode.
By default SAI sub-block is in asynchronous mode.
This property sets SAI sub-block as slave of another SAI sub-block.
Must contain the phandle and index of the sai sub-block providing
the synchronization.
SAI subnodes: SAI subnodes:
Two subnodes corresponding to SAI sub-block instances A et B can be defined. Two subnodes corresponding to SAI sub-block instances A et B can be defined.
...@@ -44,6 +39,13 @@ SAI subnodes required properties: ...@@ -44,6 +39,13 @@ SAI subnodes required properties:
- pinctrl-names: should contain only value "default" - pinctrl-names: should contain only value "default"
- pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt - pinctrl-0: see Documentation/devicetree/bindings/pinctrl/pinctrl-stm32.txt
SAI subnodes Optional properties:
- st,sync: specify synchronization mode.
By default SAI sub-block is in asynchronous mode.
This property sets SAI sub-block as slave of another SAI sub-block.
Must contain the phandle and index of the sai sub-block providing
the synchronization.
The device node should contain one 'port' child node with one child 'endpoint' The device node should contain one 'port' child node with one child 'endpoint'
node, according to the bindings defined in Documentation/devicetree/bindings/ node, according to the bindings defined in Documentation/devicetree/bindings/
graph.txt. graph.txt.
......
...@@ -8,6 +8,7 @@ Required properties: ...@@ -8,6 +8,7 @@ Required properties:
- compatible: should be one of the following: - compatible: should be one of the following:
- "allwinner,sun4i-a10-i2s" - "allwinner,sun4i-a10-i2s"
- "allwinner,sun6i-a31-i2s" - "allwinner,sun6i-a31-i2s"
- "allwinner,sun8i-a83t-i2s"
- "allwinner,sun8i-h3-i2s" - "allwinner,sun8i-h3-i2s"
- reg: physical base address of the controller and length of memory mapped - reg: physical base address of the controller and length of memory mapped
region. region.
...@@ -23,6 +24,7 @@ Required properties: ...@@ -23,6 +24,7 @@ Required properties:
Required properties for the following compatibles: Required properties for the following compatibles:
- "allwinner,sun6i-a31-i2s" - "allwinner,sun6i-a31-i2s"
- "allwinner,sun8i-a83t-i2s"
- "allwinner,sun8i-h3-i2s" - "allwinner,sun8i-h3-i2s"
- resets: phandle to the reset line for this codec - resets: phandle to the reset line for this codec
......
...@@ -6,10 +6,12 @@ audio playback. For more product information please see the links below: ...@@ -6,10 +6,12 @@ audio playback. For more product information please see the links below:
http://www.ti.com/product/TAS5720L http://www.ti.com/product/TAS5720L
http://www.ti.com/product/TAS5720M http://www.ti.com/product/TAS5720M
http://www.ti.com/product/TAS5722L
Required properties: Required properties:
- compatible : "ti,tas5720" - compatible : "ti,tas5720",
"ti,tas5722"
- reg : I2C slave address - reg : I2C slave address
- dvdd-supply : phandle to a 3.3-V supply for the digital circuitry - dvdd-supply : phandle to a 3.3-V supply for the digital circuitry
- pvdd-supply : phandle to a supply used for the Class-D amp and the analog - pvdd-supply : phandle to a supply used for the Class-D amp and the analog
......
...@@ -296,9 +296,6 @@ struct snd_soc_dai { ...@@ -296,9 +296,6 @@ struct snd_soc_dai {
/* DAI runtime info */ /* DAI runtime info */
unsigned int capture_active:1; /* stream is in use */ unsigned int capture_active:1; /* stream is in use */
unsigned int playback_active:1; /* stream is in use */ unsigned int playback_active:1; /* stream is in use */
unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
unsigned int probed:1; unsigned int probed:1;
unsigned int active; unsigned int active;
......
...@@ -36,6 +36,11 @@ ...@@ -36,6 +36,11 @@
/* Define how often to check (and clear) the fault status register (in ms) */ /* Define how often to check (and clear) the fault status register (in ms) */
#define TAS5720_FAULT_CHECK_INTERVAL 200 #define TAS5720_FAULT_CHECK_INTERVAL 200
enum tas572x_type {
TAS5720,
TAS5722,
};
static const char * const tas5720_supply_names[] = { static const char * const tas5720_supply_names[] = {
"dvdd", /* Digital power supply. Connect to 3.3-V supply. */ "dvdd", /* Digital power supply. Connect to 3.3-V supply. */
"pvdd", /* Class-D amp and analog power supply (connected). */ "pvdd", /* Class-D amp and analog power supply (connected). */
...@@ -47,6 +52,7 @@ struct tas5720_data { ...@@ -47,6 +52,7 @@ struct tas5720_data {
struct snd_soc_codec *codec; struct snd_soc_codec *codec;
struct regmap *regmap; struct regmap *regmap;
struct i2c_client *tas5720_client; struct i2c_client *tas5720_client;
enum tas572x_type devtype;
struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES]; struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
struct delayed_work fault_check_work; struct delayed_work fault_check_work;
unsigned int last_fault; unsigned int last_fault;
...@@ -264,7 +270,7 @@ static void tas5720_fault_check_work(struct work_struct *work) ...@@ -264,7 +270,7 @@ static void tas5720_fault_check_work(struct work_struct *work)
static int tas5720_codec_probe(struct snd_soc_codec *codec) static int tas5720_codec_probe(struct snd_soc_codec *codec)
{ {
struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec); struct tas5720_data *tas5720 = snd_soc_codec_get_drvdata(codec);
unsigned int device_id; unsigned int device_id, expected_device_id;
int ret; int ret;
tas5720->codec = codec; tas5720->codec = codec;
...@@ -276,6 +282,11 @@ static int tas5720_codec_probe(struct snd_soc_codec *codec) ...@@ -276,6 +282,11 @@ static int tas5720_codec_probe(struct snd_soc_codec *codec)
return ret; return ret;
} }
/*
* Take a liberal approach to checking the device ID to allow the
* driver to be used even if the device ID does not match, however
* issue a warning if there is a mismatch.
*/
ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id); ret = regmap_read(tas5720->regmap, TAS5720_DEVICE_ID_REG, &device_id);
if (ret < 0) { if (ret < 0) {
dev_err(codec->dev, "failed to read device ID register: %d\n", dev_err(codec->dev, "failed to read device ID register: %d\n",
...@@ -283,13 +294,22 @@ static int tas5720_codec_probe(struct snd_soc_codec *codec) ...@@ -283,13 +294,22 @@ static int tas5720_codec_probe(struct snd_soc_codec *codec)
goto probe_fail; goto probe_fail;
} }
if (device_id != TAS5720_DEVICE_ID) { switch (tas5720->devtype) {
dev_err(codec->dev, "wrong device ID. expected: %u read: %u\n", case TAS5720:
TAS5720_DEVICE_ID, device_id); expected_device_id = TAS5720_DEVICE_ID;
ret = -ENODEV; break;
goto probe_fail; case TAS5722:
expected_device_id = TAS5722_DEVICE_ID;
break;
default:
dev_err(codec->dev, "unexpected private driver data\n");
return -EINVAL;
} }
if (device_id != expected_device_id)
dev_warn(codec->dev, "wrong device ID. expected: %u read: %u\n",
expected_device_id, device_id);
/* Set device to mute */ /* Set device to mute */
ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG, ret = snd_soc_update_bits(codec, TAS5720_DIGITAL_CTRL2_REG,
TAS5720_MUTE, TAS5720_MUTE); TAS5720_MUTE, TAS5720_MUTE);
...@@ -446,6 +466,15 @@ static const struct regmap_config tas5720_regmap_config = { ...@@ -446,6 +466,15 @@ static const struct regmap_config tas5720_regmap_config = {
.volatile_reg = tas5720_is_volatile_reg, .volatile_reg = tas5720_is_volatile_reg,
}; };
static const struct regmap_config tas5722_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = TAS5722_MAX_REG,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = tas5720_is_volatile_reg,
};
/* /*
* DAC analog gain. There are four discrete values to select from, ranging * DAC analog gain. There are four discrete values to select from, ranging
* from 19.2 dB to 26.3dB. * from 19.2 dB to 26.3dB.
...@@ -544,6 +573,7 @@ static int tas5720_probe(struct i2c_client *client, ...@@ -544,6 +573,7 @@ static int tas5720_probe(struct i2c_client *client,
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct tas5720_data *data; struct tas5720_data *data;
const struct regmap_config *regmap_config;
int ret; int ret;
int i; int i;
...@@ -552,7 +582,20 @@ static int tas5720_probe(struct i2c_client *client, ...@@ -552,7 +582,20 @@ static int tas5720_probe(struct i2c_client *client,
return -ENOMEM; return -ENOMEM;
data->tas5720_client = client; data->tas5720_client = client;
data->regmap = devm_regmap_init_i2c(client, &tas5720_regmap_config); data->devtype = id->driver_data;
switch (id->driver_data) {
case TAS5720:
regmap_config = &tas5720_regmap_config;
break;
case TAS5722:
regmap_config = &tas5722_regmap_config;
break;
default:
dev_err(dev, "unexpected private driver data\n");
return -EINVAL;
}
data->regmap = devm_regmap_init_i2c(client, regmap_config);
if (IS_ERR(data->regmap)) { if (IS_ERR(data->regmap)) {
ret = PTR_ERR(data->regmap); ret = PTR_ERR(data->regmap);
dev_err(dev, "failed to allocate register map: %d\n", ret); dev_err(dev, "failed to allocate register map: %d\n", ret);
...@@ -592,7 +635,8 @@ static int tas5720_remove(struct i2c_client *client) ...@@ -592,7 +635,8 @@ static int tas5720_remove(struct i2c_client *client)
} }
static const struct i2c_device_id tas5720_id[] = { static const struct i2c_device_id tas5720_id[] = {
{ "tas5720", 0 }, { "tas5720", TAS5720 },
{ "tas5722", TAS5722 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, tas5720_id); MODULE_DEVICE_TABLE(i2c, tas5720_id);
...@@ -600,6 +644,7 @@ MODULE_DEVICE_TABLE(i2c, tas5720_id); ...@@ -600,6 +644,7 @@ MODULE_DEVICE_TABLE(i2c, tas5720_id);
#if IS_ENABLED(CONFIG_OF) #if IS_ENABLED(CONFIG_OF)
static const struct of_device_id tas5720_of_match[] = { static const struct of_device_id tas5720_of_match[] = {
{ .compatible = "ti,tas5720", }, { .compatible = "ti,tas5720", },
{ .compatible = "ti,tas5722", },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, tas5720_of_match); MODULE_DEVICE_TABLE(of, tas5720_of_match);
......
...@@ -30,8 +30,14 @@ ...@@ -30,8 +30,14 @@
#define TAS5720_DIGITAL_CLIP1_REG 0x11 #define TAS5720_DIGITAL_CLIP1_REG 0x11
#define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG #define TAS5720_MAX_REG TAS5720_DIGITAL_CLIP1_REG
/* Additional TAS5722-specific Registers */
#define TAS5722_DIGITAL_CTRL2_REG 0x13
#define TAS5722_ANALOG_CTRL2_REG 0x14
#define TAS5722_MAX_REG TAS5722_ANALOG_CTRL2_REG
/* TAS5720_DEVICE_ID_REG */ /* TAS5720_DEVICE_ID_REG */
#define TAS5720_DEVICE_ID 0x01 #define TAS5720_DEVICE_ID 0x01
#define TAS5722_DEVICE_ID 0x12
/* TAS5720_POWER_CTRL_REG */ /* TAS5720_POWER_CTRL_REG */
#define TAS5720_DIG_CLIP_MASK GENMASK(7, 2) #define TAS5720_DIG_CLIP_MASK GENMASK(7, 2)
...@@ -51,6 +57,7 @@ ...@@ -51,6 +57,7 @@
#define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0) #define TAS5720_SAIF_FORMAT_MASK GENMASK(2, 0)
/* TAS5720_DIGITAL_CTRL2_REG */ /* TAS5720_DIGITAL_CTRL2_REG */
#define TAS5722_VOL_RAMP_RATE BIT(6)
#define TAS5720_MUTE BIT(4) #define TAS5720_MUTE BIT(4)
#define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0) #define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0)
...@@ -87,4 +94,28 @@ ...@@ -87,4 +94,28 @@
#define TAS5720_CLIP1_MASK GENMASK(7, 2) #define TAS5720_CLIP1_MASK GENMASK(7, 2)
#define TAS5720_CLIP1_SHIFT (0x2) #define TAS5720_CLIP1_SHIFT (0x2)
/* TAS5722_DIGITAL_CTRL2_REG */
#define TAS5722_HPF_3_7HZ (0x0 << 5)
#define TAS5722_HPF_7_4HZ (0x1 << 5)
#define TAS5722_HPF_14_9HZ (0x2 << 5)
#define TAS5722_HPF_29_7HZ (0x3 << 5)
#define TAS5722_HPF_59_4HZ (0x4 << 5)
#define TAS5722_HPF_118_4HZ (0x5 << 5)
#define TAS5722_HPF_235_0HZ (0x6 << 5)
#define TAS5722_HPF_463_2HZ (0x7 << 5)
#define TAS5722_HPF_MASK GENMASK(7, 5)
#define TAS5722_AUTO_SLEEP_OFF (0x0 << 3)
#define TAS5722_AUTO_SLEEP_1024LR (0x1 << 3)
#define TAS5722_AUTO_SLEEP_65536LR (0x2 << 3)
#define TAS5722_AUTO_SLEEP_262144LR (0x3 << 3)
#define TAS5722_AUTO_SLEEP_MASK GENMASK(4, 3)
#define TAS5722_TDM_SLOT_16B BIT(2)
#define TAS5722_MCLK_PIN_CFG BIT(1)
#define TAS5722_VOL_CONTROL_LSB BIT(0)
/* TAS5722_ANALOG_CTRL2_REG */
#define TAS5722_FAULTZ_PU BIT(3)
#define TAS5722_VREG_LVL BIT(2)
#define TAS5722_PWR_TUNE BIT(0)
#endif /* __TAS5720_H__ */ #endif /* __TAS5720_H__ */
...@@ -98,6 +98,8 @@ struct wm2200_priv { ...@@ -98,6 +98,8 @@ struct wm2200_priv {
int rev; int rev;
int sysclk; int sysclk;
unsigned int symmetric_rates:1;
}; };
#define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1) #define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1)
...@@ -1550,7 +1552,7 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = { ...@@ -1550,7 +1552,7 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = {
static int wm2200_probe(struct snd_soc_codec *codec) static int wm2200_probe(struct snd_soc_codec *codec)
{ {
struct wm2200_priv *wm2200 = dev_get_drvdata(codec->dev); struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
int ret; int ret;
wm2200->codec = codec; wm2200->codec = codec;
...@@ -1758,7 +1760,7 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream, ...@@ -1758,7 +1760,7 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream,
lrclk = bclk_rates[bclk] / params_rate(params); lrclk = bclk_rates[bclk] / params_rate(params);
dev_dbg(codec->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk); dev_dbg(codec->dev, "Setting %dHz LRCLK\n", bclk_rates[bclk] / lrclk);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK || if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
dai->symmetric_rates) wm2200->symmetric_rates)
snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_7, snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_7,
WM2200_AIF1RX_BCPF_MASK, lrclk); WM2200_AIF1RX_BCPF_MASK, lrclk);
else else
...@@ -2059,13 +2061,14 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source, ...@@ -2059,13 +2061,14 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
static int wm2200_dai_probe(struct snd_soc_dai *dai) static int wm2200_dai_probe(struct snd_soc_dai *dai)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec);
unsigned int val = 0; unsigned int val = 0;
int ret; int ret;
ret = snd_soc_read(codec, WM2200_GPIO_CTRL_1); ret = snd_soc_read(codec, WM2200_GPIO_CTRL_1);
if (ret >= 0) { if (ret >= 0) {
if ((ret & WM2200_GP1_FN_MASK) != 0) { if ((ret & WM2200_GP1_FN_MASK) != 0) {
dai->symmetric_rates = true; wm2200->symmetric_rates = true;
val = WM2200_AIF1TX_LRCLK_SRC; val = WM2200_AIF1TX_LRCLK_SRC;
} }
} else { } else {
......
...@@ -28,16 +28,6 @@ ...@@ -28,16 +28,6 @@
#include "stm32_sai.h" #include "stm32_sai.h"
static LIST_HEAD(sync_providers);
static DEFINE_MUTEX(sync_mutex);
struct sync_provider {
struct list_head link;
struct device_node *node;
int (*sync_conf)(void *data, int synco);
void *data;
};
static const struct stm32_sai_conf stm32_sai_conf_f4 = { static const struct stm32_sai_conf stm32_sai_conf_f4 = {
.version = SAI_STM32F4, .version = SAI_STM32F4,
}; };
...@@ -70,9 +60,8 @@ static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci) ...@@ -70,9 +60,8 @@ static int stm32_sai_sync_conf_client(struct stm32_sai_data *sai, int synci)
return 0; return 0;
} }
static int stm32_sai_sync_conf_provider(void *data, int synco) static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco)
{ {
struct stm32_sai_data *sai = (struct stm32_sai_data *)data;
u32 prev_synco; u32 prev_synco;
int ret; int ret;
...@@ -103,83 +92,42 @@ static int stm32_sai_sync_conf_provider(void *data, int synco) ...@@ -103,83 +92,42 @@ static int stm32_sai_sync_conf_provider(void *data, int synco)
return 0; return 0;
} }
static int stm32_sai_set_sync_provider(struct device_node *np, int synco) static int stm32_sai_set_sync(struct stm32_sai_data *sai_client,
struct device_node *np_provider,
int synco, int synci)
{ {
struct sync_provider *provider; struct platform_device *pdev = of_find_device_by_node(np_provider);
struct stm32_sai_data *sai_provider;
int ret; int ret;
mutex_lock(&sync_mutex); if (!pdev) {
list_for_each_entry(provider, &sync_providers, link) { dev_err(&sai_client->pdev->dev,
if (provider->node == np) { "Device not found for node %s\n", np_provider->name);
ret = provider->sync_conf(provider->data, synco); return -ENODEV;
mutex_unlock(&sync_mutex);
return ret;
}
} }
mutex_unlock(&sync_mutex);
/* SAI sync provider not found */ sai_provider = platform_get_drvdata(pdev);
return -ENODEV; if (!sai_provider) {
} dev_err(&sai_client->pdev->dev,
"SAI sync provider data not found\n");
static int stm32_sai_set_sync(struct stm32_sai_data *sai, return -EINVAL;
struct device_node *np_provider, }
int synco, int synci)
{
int ret;
/* Configure sync client */ /* Configure sync client */
stm32_sai_sync_conf_client(sai, synci); ret = stm32_sai_sync_conf_client(sai_client, synci);
if (ret < 0)
return ret;
/* Configure sync provider */ /* Configure sync provider */
ret = stm32_sai_set_sync_provider(np_provider, synco); return stm32_sai_sync_conf_provider(sai_provider, synco);
return ret;
}
static int stm32_sai_sync_add_provider(struct platform_device *pdev,
void *data)
{
struct sync_provider *sp;
sp = devm_kzalloc(&pdev->dev, sizeof(*sp), GFP_KERNEL);
if (!sp)
return -ENOMEM;
sp->node = of_node_get(pdev->dev.of_node);
sp->data = data;
sp->sync_conf = &stm32_sai_sync_conf_provider;
mutex_lock(&sync_mutex);
list_add(&sp->link, &sync_providers);
mutex_unlock(&sync_mutex);
return 0;
}
static void stm32_sai_sync_del_provider(struct device_node *np)
{
struct sync_provider *sp;
mutex_lock(&sync_mutex);
list_for_each_entry(sp, &sync_providers, link) {
if (sp->node == np) {
list_del(&sp->link);
of_node_put(sp->node);
break;
}
}
mutex_unlock(&sync_mutex);
} }
static int stm32_sai_probe(struct platform_device *pdev) static int stm32_sai_probe(struct platform_device *pdev)
{ {
struct device_node *np = pdev->dev.of_node;
struct stm32_sai_data *sai; struct stm32_sai_data *sai;
struct reset_control *rst; struct reset_control *rst;
struct resource *res; struct resource *res;
const struct of_device_id *of_id; const struct of_device_id *of_id;
int ret;
sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
if (!sai) if (!sai)
...@@ -231,28 +179,11 @@ static int stm32_sai_probe(struct platform_device *pdev) ...@@ -231,28 +179,11 @@ static int stm32_sai_probe(struct platform_device *pdev)
reset_control_deassert(rst); reset_control_deassert(rst);
} }
ret = stm32_sai_sync_add_provider(pdev, sai);
if (ret < 0)
return ret;
sai->set_sync = &stm32_sai_set_sync;
sai->pdev = pdev; sai->pdev = pdev;
sai->set_sync = &stm32_sai_set_sync;
platform_set_drvdata(pdev, sai); platform_set_drvdata(pdev, sai);
ret = of_platform_populate(np, NULL, NULL, &pdev->dev); return devm_of_platform_populate(&pdev->dev);
if (ret < 0)
stm32_sai_sync_del_provider(np);
return ret;
}
static int stm32_sai_remove(struct platform_device *pdev)
{
of_platform_depopulate(&pdev->dev);
stm32_sai_sync_del_provider(pdev->dev.of_node);
return 0;
} }
MODULE_DEVICE_TABLE(of, stm32_sai_ids); MODULE_DEVICE_TABLE(of, stm32_sai_ids);
...@@ -263,7 +194,6 @@ static struct platform_driver stm32_sai_driver = { ...@@ -263,7 +194,6 @@ static struct platform_driver stm32_sai_driver = {
.of_match_table = stm32_sai_ids, .of_match_table = stm32_sai_ids,
}, },
.probe = stm32_sai_probe, .probe = stm32_sai_probe,
.remove = stm32_sai_remove,
}; };
module_platform_driver(stm32_sai_driver); module_platform_driver(stm32_sai_driver);
......
...@@ -590,12 +590,28 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream, ...@@ -590,12 +590,28 @@ static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
hwrate); hwrate);
} }
static unsigned int sun4i_codec_src_rates[] = {
8000, 11025, 12000, 16000, 22050, 24000, 32000,
44100, 48000, 96000, 192000
};
static struct snd_pcm_hw_constraint_list sun4i_codec_constraints = {
.count = ARRAY_SIZE(sun4i_codec_src_rates),
.list = sun4i_codec_src_rates,
};
static int sun4i_codec_startup(struct snd_pcm_substream *substream, static int sun4i_codec_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card); struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &sun4i_codec_constraints);
/* /*
* Stop issuing DRQ when we have room for less than 16 samples * Stop issuing DRQ when we have room for less than 16 samples
* in our TX FIFO * in our TX FIFO
...@@ -633,9 +649,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { ...@@ -633,9 +649,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
.channels_max = 2, .channels_max = 2,
.rate_min = 8000, .rate_min = 8000,
.rate_max = 192000, .rate_max = 192000,
.rates = SNDRV_PCM_RATE_8000_48000 | .rates = SNDRV_PCM_RATE_CONTINUOUS,
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | .formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE, SNDRV_PCM_FMTBIT_S32_LE,
.sig_bits = 24, .sig_bits = 24,
...@@ -645,11 +659,8 @@ static struct snd_soc_dai_driver sun4i_codec_dai = { ...@@ -645,11 +659,8 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
.channels_min = 1, .channels_min = 1,
.channels_max = 2, .channels_max = 2,
.rate_min = 8000, .rate_min = 8000,
.rate_max = 192000, .rate_max = 48000,
.rates = SNDRV_PCM_RATE_8000_48000 | .rates = SNDRV_PCM_RATE_CONTINUOUS,
SNDRV_PCM_RATE_96000 |
SNDRV_PCM_RATE_192000 |
SNDRV_PCM_RATE_KNOT,
.formats = SNDRV_PCM_FMTBIT_S16_LE | .formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE, SNDRV_PCM_FMTBIT_S32_LE,
.sig_bits = 24, .sig_bits = 24,
...@@ -1128,7 +1139,7 @@ static const struct snd_soc_component_driver sun4i_codec_component = { ...@@ -1128,7 +1139,7 @@ static const struct snd_soc_component_driver sun4i_codec_component = {
.name = "sun4i-codec", .name = "sun4i-codec",
}; };
#define SUN4I_CODEC_RATES SNDRV_PCM_RATE_8000_192000 #define SUN4I_CODEC_RATES SNDRV_PCM_RATE_CONTINUOUS
#define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ #define SUN4I_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S32_LE) SNDRV_PCM_FMTBIT_S32_LE)
......
...@@ -269,10 +269,11 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample) ...@@ -269,10 +269,11 @@ static bool sun4i_i2s_oversample_is_valid(unsigned int oversample)
return false; return false;
} }
static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai,
unsigned int rate, unsigned int rate,
unsigned int word_size) unsigned int word_size)
{ {
struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int oversample_rate, clk_rate; unsigned int oversample_rate, clk_rate;
int bclk_div, mclk_div; int bclk_div, mclk_div;
int ret; int ret;
...@@ -300,6 +301,7 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, ...@@ -300,6 +301,7 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
break; break;
default: default:
dev_err(dai->dev, "Unsupported sample rate: %u\n", rate);
return -EINVAL; return -EINVAL;
} }
...@@ -308,18 +310,25 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s, ...@@ -308,18 +310,25 @@ static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
return ret; return ret;
oversample_rate = i2s->mclk_freq / rate; oversample_rate = i2s->mclk_freq / rate;
if (!sun4i_i2s_oversample_is_valid(oversample_rate)) if (!sun4i_i2s_oversample_is_valid(oversample_rate)) {
dev_err(dai->dev, "Unsupported oversample rate: %d\n",
oversample_rate);
return -EINVAL; return -EINVAL;
}
bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate, bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
word_size); word_size);
if (bclk_div < 0) if (bclk_div < 0) {
dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div);
return -EINVAL; return -EINVAL;
}
mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
clk_rate, rate); clk_rate, rate);
if (mclk_div < 0) if (mclk_div < 0) {
dev_err(dai->dev, "Unsupported MCLK divider: %d\n", mclk_div);
return -EINVAL; return -EINVAL;
}
/* Adjust the clock division values if needed */ /* Adjust the clock division values if needed */
bclk_div += i2s->variant->bclk_offset; bclk_div += i2s->variant->bclk_offset;
...@@ -349,8 +358,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -349,8 +358,11 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
u32 width; u32 width;
channels = params_channels(params); channels = params_channels(params);
if (channels != 2) if (channels != 2) {
dev_err(dai->dev, "Unsupported number of channels: %d\n",
channels);
return -EINVAL; return -EINVAL;
}
if (i2s->variant->has_chcfg) { if (i2s->variant->has_chcfg) {
regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG,
...@@ -382,6 +394,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -382,6 +394,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
width = DMA_SLAVE_BUSWIDTH_2_BYTES; width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break; break;
default: default:
dev_err(dai->dev, "Unsupported physical sample width: %d\n",
params_physical_width(params));
return -EINVAL; return -EINVAL;
} }
i2s->playback_dma_data.addr_width = width; i2s->playback_dma_data.addr_width = width;
...@@ -393,6 +407,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -393,6 +407,8 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
break; break;
default: default:
dev_err(dai->dev, "Unsupported sample width: %d\n",
params_width(params));
return -EINVAL; return -EINVAL;
} }
...@@ -401,7 +417,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -401,7 +417,7 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
regmap_field_write(i2s->field_fmt_sr, regmap_field_write(i2s->field_fmt_sr,
sr + i2s->variant->fmt_offset); sr + i2s->variant->fmt_offset);
return sun4i_i2s_set_clk_rate(i2s, params_rate(params), return sun4i_i2s_set_clk_rate(dai, params_rate(params),
params_width(params)); params_width(params));
} }
...@@ -426,6 +442,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -426,6 +442,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
val = SUN4I_I2S_FMT0_FMT_RIGHT_J; val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
break; break;
default: default:
dev_err(dai->dev, "Unsupported format: %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL; return -EINVAL;
} }
...@@ -464,6 +482,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -464,6 +482,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_NB_NF: case SND_SOC_DAIFMT_NB_NF:
break; break;
default: default:
dev_err(dai->dev, "Unsupported clock polarity: %d\n",
fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL; return -EINVAL;
} }
...@@ -482,6 +502,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -482,6 +502,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
val = SUN4I_I2S_CTRL_MODE_SLAVE; val = SUN4I_I2S_CTRL_MODE_SLAVE;
break; break;
default: default:
dev_err(dai->dev, "Unsupported slave setting: %d\n",
fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL; return -EINVAL;
} }
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
...@@ -504,6 +526,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) ...@@ -504,6 +526,8 @@ static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
val = 0; val = 0;
break; break;
default: default:
dev_err(dai->dev, "Unsupported slave setting: %d\n",
fmt & SND_SOC_DAIFMT_MASTER_MASK);
return -EINVAL; return -EINVAL;
} }
regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
...@@ -897,6 +921,23 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { ...@@ -897,6 +921,23 @@ static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = {
.field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
}; };
static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = {
.has_reset = true,
.reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
.sun4i_i2s_regmap = &sun4i_i2s_regmap_config,
.field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7),
.field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3),
.field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5),
.field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6),
.field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7),
.has_slave_select_bit = true,
.field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1),
.field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31),
.field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31),
.field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2),
.field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2),
};
static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = {
.has_reset = true, .has_reset = true,
.reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG,
...@@ -1120,6 +1161,10 @@ static const struct of_device_id sun4i_i2s_match[] = { ...@@ -1120,6 +1161,10 @@ static const struct of_device_id sun4i_i2s_match[] = {
.compatible = "allwinner,sun6i-a31-i2s", .compatible = "allwinner,sun6i-a31-i2s",
.data = &sun6i_a31_i2s_quirks, .data = &sun6i_a31_i2s_quirks,
}, },
{
.compatible = "allwinner,sun8i-a83t-i2s",
.data = &sun8i_a83t_i2s_quirks,
},
{ {
.compatible = "allwinner,sun8i-h3-i2s", .compatible = "allwinner,sun8i-h3-i2s",
.data = &sun8i_h3_i2s_quirks, .data = &sun8i_h3_i2s_quirks,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment