mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
![Jerome Brunet](/assets/img/avatar_default.png)
The content of SND_SOC_DAIFMT_FORMAT_MASK is a number, not a bitfield,
so the test to check if the format is i2s is wrong. Because of this the
clock setting may be wrong. For example, the sample clock gets inverted
in DSP B mode, when it should not.
Fix the lrclk invert helper function
Fixes: 1a11d88f49
("ASoC: meson: add tdm formatter base driver")
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
78 lines
1.9 KiB
C
78 lines
1.9 KiB
C
/* SPDX-License-Identifier: (GPL-2.0 OR MIT)
|
|
*
|
|
* Copyright (c) 2018 Baylibre SAS.
|
|
* Author: Jerome Brunet <jbrunet@baylibre.com>
|
|
*/
|
|
|
|
#ifndef _MESON_AXG_TDM_H
|
|
#define _MESON_AXG_TDM_H
|
|
|
|
#include <linux/clk.h>
|
|
#include <linux/regmap.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/soc-dai.h>
|
|
|
|
#define AXG_TDM_NUM_LANES 4
|
|
#define AXG_TDM_CHANNEL_MAX 128
|
|
#define AXG_TDM_RATES (SNDRV_PCM_RATE_5512 | \
|
|
SNDRV_PCM_RATE_8000_192000)
|
|
#define AXG_TDM_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
|
|
SNDRV_PCM_FMTBIT_S16_LE | \
|
|
SNDRV_PCM_FMTBIT_S20_LE | \
|
|
SNDRV_PCM_FMTBIT_S24_LE | \
|
|
SNDRV_PCM_FMTBIT_S32_LE)
|
|
|
|
struct axg_tdm_iface {
|
|
struct clk *sclk;
|
|
struct clk *lrclk;
|
|
struct clk *mclk;
|
|
unsigned long mclk_rate;
|
|
|
|
/* format is common to all the DAIs of the iface */
|
|
unsigned int fmt;
|
|
unsigned int slots;
|
|
unsigned int slot_width;
|
|
|
|
/* For component wide symmetry */
|
|
int rate;
|
|
};
|
|
|
|
static inline bool axg_tdm_lrclk_invert(unsigned int fmt)
|
|
{
|
|
return ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S) ^
|
|
!!(fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_NB_IF));
|
|
}
|
|
|
|
static inline bool axg_tdm_sclk_invert(unsigned int fmt)
|
|
{
|
|
return fmt & (SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_IB_NF);
|
|
}
|
|
|
|
struct axg_tdm_stream {
|
|
struct axg_tdm_iface *iface;
|
|
struct list_head formatter_list;
|
|
struct mutex lock;
|
|
unsigned int channels;
|
|
unsigned int width;
|
|
unsigned int physical_width;
|
|
u32 *mask;
|
|
bool ready;
|
|
};
|
|
|
|
struct axg_tdm_stream *axg_tdm_stream_alloc(struct axg_tdm_iface *iface);
|
|
void axg_tdm_stream_free(struct axg_tdm_stream *ts);
|
|
int axg_tdm_stream_start(struct axg_tdm_stream *ts);
|
|
void axg_tdm_stream_stop(struct axg_tdm_stream *ts);
|
|
|
|
static inline int axg_tdm_stream_reset(struct axg_tdm_stream *ts)
|
|
{
|
|
axg_tdm_stream_stop(ts);
|
|
return axg_tdm_stream_start(ts);
|
|
}
|
|
|
|
int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask,
|
|
u32 *rx_mask, unsigned int slots,
|
|
unsigned int slot_width);
|
|
|
|
#endif /* _MESON_AXG_TDM_H */
|