@@ -1,5 +1,5 @@
snd-soc-hda-skl-objs := hda_skl.o hda_skl_pcm.o hda_soc_dsp.o \
-hda_dsp_controls.o
+hda_dsp_controls.o hda_dsp_ssp_config.o
obj-$(CONFIG_SND_SOC_HDA_SKL) += snd-soc-hda-skl.o
@@ -25,8 +25,27 @@
#include <sound/pcm_params.h>
#include <sound/soc-hda-sst-dsp.h>
#include "hda_dsp_controls.h"
+#include "hda_dsp_ssp_config.h"
#include "hda_skl.h"
+#define CH_FIXUP (1 << 0)
+#define RATE_FIXUP (1 << 1)
+#define FMT_FIXUP (1 << 2)
+
+#define CH_FIXUP_MASK (1 << 0)
+#define RATE_FIXUP_MASK (1 << 1)
+#define FMT_FIXUP_MASK (1 << 2)
+
+#define CH_CONVERTER (1 << 0)
+#define RATE_CONVERTER (1 << 1)
+#define FMT_CONVERTER (1 << 2)
+
+#define CH_CONVERTER_MASK (1 << 0)
+#define RATE_CONVERTER_MASK (1 << 1)
+#define FMT_CONVERTER_MASK (1 << 2)
+
+#define REGS_OFFSET_CPR_BLOB 8
+
static int is_hda_widget_type(struct snd_soc_dapm_widget *w)
{
return ((w->id == snd_soc_dapm_dai_link) ||
@@ -161,6 +180,117 @@ static bool hda_sst_is_pipe_mcps_available(struct hda_platform_info *pinfo,
return true;
}
+static struct snd_soc_dai *hda_find_dai_in(struct list_head *sinks)
+{
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dai *dai = NULL;
+ list_for_each_entry(p, sinks, list_source) {
+ if (p->connect) {
+ if (p->sink->id == snd_soc_dapm_dai_in ||
+ p->sink->id == snd_soc_dapm_dai_out) {
+ dai = p->sink->priv;
+ return dai;
+ }
+ dai = hda_find_dai_in(&p->sink->sinks);
+ if (dai)
+ return dai;
+ }
+ }
+ return dai;
+}
+
+static struct snd_soc_dai *hda_find_dai_out(struct list_head *sources)
+{
+ struct snd_soc_dapm_path *p;
+ struct snd_soc_dai *dai = NULL;
+ list_for_each_entry(p, sources, list_sink) {
+ if (p->connect) {
+ if (p->source->id == snd_soc_dapm_dai_in ||
+ p->source->id == snd_soc_dapm_dai_out) {
+ dai = p->source->priv;
+ break;
+ }
+ dai = hda_find_dai_out(&p->source->sources);
+ if (dai)
+ return dai;
+ }
+ }
+ return dai;
+}
+
+static struct hda_dai_config *hda_sst_get_dai_config(struct snd_soc_dapm_widget *w,
+ struct ssth_module_config *mconfig, struct ssth_lib *ctx)
+{
+ struct snd_soc_dai *dai = NULL;
+ struct hda_soc_bus *hda = NULL;
+
+ if (mconfig->hw_conn_type == SSTH_CONN_SOURCE) {
+ if (mconfig->pipe->conn_type == SSTH_PIPE_CONN_TYPE_BE)
+ dai = hda_find_dai_in(&w->sinks);
+ else if (mconfig->pipe->conn_type == SSTH_PIPE_CONN_TYPE_FE)
+ dai = hda_find_dai_out(&w->sources);
+ } else if (mconfig->hw_conn_type == SSTH_CONN_SINK) {
+ if (mconfig->pipe->conn_type == SSTH_PIPE_CONN_TYPE_BE)
+ dai = hda_find_dai_out(&w->sources);
+ else if (mconfig->pipe->conn_type == SSTH_PIPE_CONN_TYPE_FE)
+ dai = hda_find_dai_in(&w->sinks);
+ }
+ if (!dai) {
+ dev_dbg(ctx->dev, "Dai not found for widget %s\n", w->name);
+ return NULL;
+ }
+ dev_dbg(ctx->dev, "Dai found %s for widget %s\n",
+ dai->name, w->name);
+ hda = dev_get_drvdata(dai->dev);
+ return &hda->pinfo->dai_config[dai->id - 1];
+
+}
+
+static void hda_dump_mconfig(struct ssth_lib *ctx,
+ struct ssth_module_config *mcfg)
+{
+ dev_dbg(ctx->dev, "Dumping Mconfig\n");
+ dev_dbg(ctx->dev, "Input Format:\n");
+ dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
+ dev_dbg(ctx->dev, "sampling_freq = %d\n", mcfg->in_fmt.sampling_freq);
+ dev_dbg(ctx->dev, "channel_config = %d\n", mcfg->in_fmt.channel_config);
+ dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->in_fmt.valid_bit_depth);
+ dev_dbg(ctx->dev, "Output Format:\n");
+ dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
+ dev_dbg(ctx->dev, "sampling_freq = %d\n", mcfg->out_fmt.sampling_freq);
+ dev_dbg(ctx->dev, "valid bit depth = %d\n", mcfg->out_fmt.valid_bit_depth);
+ dev_dbg(ctx->dev, "channel_config = %d\n", mcfg->out_fmt.channel_config);
+}
+static void hda_dump_dai_config(struct ssth_lib *ctx,
+ struct hda_dai_config *cfg)
+{
+ struct hda_ssp_dai_config *ssp_cfg = &cfg->ssp_dai_config;
+ dev_dbg(ctx->dev, "Dumping DAI config\n");
+ dev_dbg(ctx->dev, "slot_width = %d\n", ssp_cfg->slot_width);
+ dev_dbg(ctx->dev, "Slot = %d\n", ssp_cfg->slots);
+ dev_dbg(ctx->dev, "ssp_mode = %d\n", ssp_cfg->ssp_mode);
+ dev_dbg(ctx->dev, "sampling rate = %d\n", cfg->sampling_rate);
+ dev_dbg(ctx->dev, "s_fmt = %d\n", cfg->s_fmt);
+ dev_dbg(ctx->dev, "bclk_invert = %d\n", ssp_cfg->bclk_invert);
+ dev_dbg(ctx->dev, "fs_invert = %d\n", ssp_cfg->fs_invert);
+ dev_dbg(ctx->dev, "num_channels = %d\n", cfg->num_channels);
+ dev_dbg(ctx->dev, "fs_slave = %d\n", ssp_cfg->fs_slave);
+ dev_dbg(ctx->dev, "bclk_slave = %d\n", ssp_cfg->bclk_slave);
+}
+
+static void hda_update_mconfig(struct ssth_module_format *fmt,
+ struct hda_dai_config *ssp_cfg,
+ int params_fixup)
+{
+ if (params_fixup & RATE_FIXUP_MASK)
+ fmt->sampling_freq = ssp_cfg->sampling_rate;
+ if (params_fixup & CH_FIXUP_MASK)
+ fmt->channels = ssp_cfg->num_channels;
+ if (params_fixup & FMT_FIXUP_MASK)
+ fmt->valid_bit_depth = ssp_cfg->s_fmt;
+
+}
+
static void hda_update_slot_map(struct ssth_lib *ctx,
struct ssth_module_config *m_cfg)
{
@@ -213,6 +343,87 @@ static void hda_update_ch_config(struct ssth_module_config *m_cfg)
}
+static void hda_update_buffer_size(struct ssth_lib *ctx,
+ struct ssth_module_config *mcfg)
+{
+ int multiplier = 1;
+
+ if (mcfg->id.module_id == SSTH_SRCINT_MODULE)
+ multiplier = 5;
+ mcfg->ibs = (mcfg->in_fmt.sampling_freq / 1000) *
+ (mcfg->in_fmt.channels) *
+ (mcfg->in_fmt.bit_depth >> 3) *
+ multiplier;
+
+ mcfg->obs = (mcfg->out_fmt.sampling_freq / 1000) *
+ (mcfg->out_fmt.channels) *
+ (mcfg->out_fmt.bit_depth >> 3) *
+ multiplier;
+}
+
+static void hda_sst_configure_widget(struct snd_soc_dapm_widget *w,
+ struct ssth_lib *ctx, struct hda_platform_info *pinfo)
+{
+ struct ssth_module_config *m_cfg = w->priv;
+ struct hda_dai_config *dai_config;
+ union ssth_ssp_dma_node dma_id;
+ unsigned int *regs = NULL;
+
+ dai_config = hda_sst_get_dai_config(w, m_cfg, ctx);
+ if (!dai_config)
+ return;
+
+ if (m_cfg->id.module_id == SSTH_COPIER_MODULE &&
+ m_cfg->pipe->conn_type == SSTH_PIPE_CONN_TYPE_BE &&
+ dai_config->dai_type == HDA_DAI_TYPE_SSP) {
+ dma_id.val = 0;
+ dma_id.dma_node.i2s_instance =
+ dai_config->ssp_dai_config.i2s_instance;
+ m_cfg->dma_id = dma_id.val;
+ regs = m_cfg->formats_config.caps;
+ }
+ if (!m_cfg->params_fixup)
+ return;
+
+ hda_dump_dai_config(ctx, dai_config);
+ dev_dbg(ctx->dev, "Mconfig for widget %s BEFORE updation\n", w->name);
+ hda_dump_mconfig(ctx, m_cfg);
+
+ /* Based on whether the widget is in FE pipe or BE PIPE and playback direction
+ * or capture direction, fixups applied will be changed
+ */
+ if ((m_cfg->pipe->conn_type == SSTH_PIPE_CONN_TYPE_FE &&
+ (m_cfg->hw_conn_type == SSTH_CONN_SINK)) ||
+ (m_cfg->pipe->conn_type == SSTH_PIPE_CONN_TYPE_BE &&
+ m_cfg->hw_conn_type == SSTH_CONN_SOURCE)) {
+ hda_update_mconfig(&m_cfg->out_fmt, dai_config,
+ m_cfg->params_fixup);
+ hda_update_mconfig(&m_cfg->in_fmt, dai_config,
+ (~m_cfg->converter) & m_cfg->params_fixup);
+ }
+ if ((m_cfg->pipe->conn_type == SSTH_PIPE_CONN_TYPE_BE &&
+ (m_cfg->hw_conn_type == SSTH_CONN_SINK)) ||
+ (m_cfg->pipe->conn_type == SSTH_PIPE_CONN_TYPE_FE &&
+ m_cfg->hw_conn_type == SSTH_CONN_SOURCE)) {
+ hda_update_mconfig(&m_cfg->in_fmt, dai_config,
+ m_cfg->params_fixup);
+ hda_update_mconfig(&m_cfg->out_fmt, dai_config,
+ (~m_cfg->converter) & m_cfg->params_fixup);
+ }
+
+ hda_update_ch_config(m_cfg);
+ hda_update_buffer_size(ctx, m_cfg);
+ if (regs) {
+ /* Slot map only needs to be updated for copier */
+ hda_update_slot_map(ctx, m_cfg);
+ hda_calculate_ssp_regs(ctx, dai_config,
+ ®s[REGS_OFFSET_CPR_BLOB]);
+ }
+
+ dev_dbg(ctx->dev, "Mconfig for widget %s AFTER updation\n", w->name);
+ hda_dump_mconfig(ctx, m_cfg);
+}
+
static int hda_sst_get_pipe_widget(struct device *dev,
struct snd_soc_dapm_widget *w, struct ssth_pipe *pipe)
{
@@ -261,6 +472,7 @@ static int hda_init_pipe_modules(struct ssth_lib *ctx,
mconfig = w->priv;
/*TODO if loadable module, mconfig->is_loadable, load module */
+ hda_sst_configure_widget(w, ctx, pinfo);
ret = ssth_init_module(ctx, mconfig, NULL);
if (ret < 0)
return ret;
@@ -619,7 +831,6 @@ void hda_sst_set_copier_dma_id(struct snd_soc_dai *dai, int dma_id, int stream,
mconfig = hda_sst_get_module(dai, stream, is_fe, "cpr");
if (mconfig != NULL)
mconfig->dma_id = dma_id;
- return;
}
/*set BE copier I2s,DMIC, SLIMBUS config*/
@@ -18,9 +18,14 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
+
+#include <sound/pcm_params.h>
+#include <sound/soc-hda-sst-dsp.h>
+#include "hda_dsp_controls.h"
#include "hda_dsp_ssp_config.h"
#include "hda_skl.h"
+
#define HDA_SSP_MAX_FREQ_192 19200000
struct hda_ssp_regs {
@@ -128,8 +133,8 @@ static void azx_set_default_ssp_regs_v1(struct hda_ssp_regs *regs)
}
-static void azx_print_ssp_regs(struct sst_dsp_ctx *ctx,
- struct hda_ssp_regs *regs)
+static void azx_print_ssp_regs(struct ssth_lib *ctx,
+ struct hda_ssp_regs *regs)
{
dev_dbg(ctx->dev, "ssc0\t\t %x\n", regs->hda_ssc0);
dev_dbg(ctx->dev, "ssc1\t\t %x\n", regs->hda_ssc1);
@@ -143,7 +148,7 @@ static void azx_print_ssp_regs(struct sst_dsp_ctx *ctx,
dev_dbg(ctx->dev, "ssioc\t\t %x\n", regs->hda_ssioc);
}
-static int azx_find_ssp_clk_divisor(struct sst_dsp_ctx *ctx, int fs,
+static int azx_find_ssp_clk_divisor(struct ssth_lib *ctx, int fs,
int slots, int s_fmt, int *div, int *dummy_bits)
{
int divisor, mod;
@@ -190,7 +195,7 @@ static int azx_find_ssp_clk_divisor(struct sst_dsp_ctx *ctx, int fs,
return -EINVAL;
}
-int azx_calculate_ssp_regs(struct sst_dsp_ctx *ctx, struct azx_dai_config *cfg,
+int hda_calculate_ssp_regs(struct ssth_lib *ctx, struct hda_dai_config *cfg,
void *ssp_regs)
{
struct hda_ssp_regs regs;
@@ -198,7 +203,7 @@ int azx_calculate_ssp_regs(struct sst_dsp_ctx *ctx, struct azx_dai_config *cfg,
int div, dummy;
int dss, edss;
int edmystop, dmystop;
- struct azx_ssp_dai_config *ssp_cfg = &cfg->ssp_dai_config;
+ struct hda_ssp_dai_config *ssp_cfg = &cfg->ssp_dai_config;
azx_set_default_ssp_regs_v1(®s);
dev_dbg(ctx->dev, "Default value of registers set to:\n");
@@ -8,6 +8,12 @@
#define HDA_SKL_SUSPEND_DELAY 2000
+#define HDA_SSP_MODE_I2S 0
+#define HDA_SSP_MODE_DSP_A 1
+#define HDA_SSP_MODE_DSP_B 2
+#define HDA_SSP_MAX_SLOTS 8
+#define HDA_DAI_TYPE_SSP 1
+
struct hda_soc_bus {
struct hdac_bus chip;
struct device *dev;
@@ -39,6 +45,73 @@ struct hda_platform_info {
struct ssth_dsp_resource resource;
struct list_head ppl_list;
struct list_head ppl_start_list;
+ /* Structure to save dai_configuration, private data for each DAIs
+ * Memory will be allcated where platform driver is registered, based
+ * on number of DAIs getting registered.
+ */
+ struct hda_dai_config *dai_config;
+};
+
+/***
+ * struct soc_hda_ssp_dai_config - DAI configuration structure. SSP type of DAI
+ * configuration. Configuration specific to SSP DAIs will go here.
+ *
+ * @slot_width : * Number of slots per frame for tdm/pcm mode,
+ * for I2S mode this is dont care. Currently slot_width
+ * supported is same as active bits in slots. All dummy
+ * bits will be programmed after the last slot in TDM mode
+ * @slots : Number of slots per frame for tdm/pcm mode,
+ * for I2S mode this is dont care. Currently slot_width
+ * supported is same as active bits in slots. All dummy
+ * bits will be programmed after the last slot in TDM mode
+ *
+ * @ssp_mode : SP mode like DSP_A, I2S etc
+ * @tx_slot_mask: Indicates which tx slot active
+ * @rx_slot_mask: Indicates which rx slot active
+ * @bclk_invert : Clock invert,
+ * clock_invert = 1, data driven on rising edge of clock,
+ * sample on falling edge of clock.
+ * clock_invert = 0, data driver on falling edge of clock,
+ * sample on rising edge of clock.
+ *
+ * @fs_invert : Invert the frame sync,
+ * fs_invert = 0, frame sync active low
+ * fs_invert = 1, frame sync active high
+ *
+ * @fs_slave : Frame sync is slave or master 1 = slave, 0 = master
+ * @bclk_slave : BCLK is master of slave 1 = slave, 0 = master
+ * @i2s_instance: It its SSP dai, hardware I2S instance for this DAI
+ */
+struct hda_ssp_dai_config {
+ u8 slot_width;
+ u8 slots;
+ u8 ssp_mode;
+ u8 tx_slot_mask;
+ u8 rx_slot_mask;
+ bool bclk_invert;
+ bool fs_invert;
+ bool fs_slave;
+ bool bclk_slave;
+ u32 i2s_instance;
+};
+
+/***
+ * struct soc_hda_dai_config - DAI configuration structure. DSP widgets and
+ * SSP registers will be configured based on this structure. This
+ * structure will be filled in part based on number of call to DAI methods
+ * like hw_params, set_tdm_slot and set_fmt.
+ * @s_fmt: Sampling format likt 24bit per ch, 16 bits per ch
+ * @num_channels : number of active channels. This must be 2 for I2S mode
+ * @sampling_rate : Sampling frequency in hertz 48000 for 48K sampling freq
+ * @dai_type : if its a SSP Dai, need to configure somethings extra for SSP dai
+ * @ssp_dai_config: Configuration specific to SSP dai
+ */
+struct hda_dai_config {
+ u8 s_fmt;
+ u8 num_channels;
+ u32 sampling_rate;
+ u32 dai_type;
+ struct hda_ssp_dai_config ssp_dai_config;
};
int azx_get_delay_from_lpib(struct hdac_bus *chip,
@@ -48,4 +121,6 @@ void azx_position_check(struct hdac_bus *chip, struct hdac_stream *azx_dev);
int soc_hda_platform_unregister(struct device *dev);
int soc_hda_platform_register(struct device *dev);
+int hda_calculate_ssp_regs(struct ssth_lib *ctx,
+ struct hda_dai_config *cfg, void *ssp_regs);
#endif /* __SOUND_SOC_HDA_SKL_H */