[alsa-devel] [RFC 0/7] ASoC: hda - ASoC DSP widget event handlers
The aDSP is triggered by DAPM. The DAPM code is atm dependent upon the DFW aka tplg RFC send by Liam. We will post missing tplg to driver links once that series is accepted
This series is dependent upon a) Takashi topic/hda which has core hdac splitup b) SKL PCM driver series, which adds the SKL HDA Platform driver c) SKL IPC series which sends IPCs to aDSP
This last series adds the missing link between b) and c).
Comments most welcome
From: Jeeja KP jeeja.kp@intel.com
This will be used by the controller to register/unregister with the SST IPC lib if processing pipe caps is supported
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- include/sound/soc-hda-sst-dsp.h | 3 ++ sound/soc/hda/Kconfig | 1 + sound/soc/hda/Makefile | 2 +- sound/soc/hda/hda_skl.h | 16 ++-------- sound/soc/hda/hda_soc_dsp.c | 62 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 sound/soc/hda/hda_soc_dsp.c
diff --git a/include/sound/soc-hda-sst-dsp.h b/include/sound/soc-hda-sst-dsp.h index 9adeb49cf126..a85f95be13c1 100644 --- a/include/sound/soc-hda-sst-dsp.h +++ b/include/sound/soc-hda-sst-dsp.h @@ -263,4 +263,7 @@ int ssth_cl_dma_prepare(struct ssth_lib *ctx); void ssth_process_cl_dma(struct work_struct *work); void ssth_cldma_int_disable(struct ssth_lib *ctx);
+int ssth_skl_init(struct device *dev, void __iomem *mmio_base, int irq, + struct ssth_dsp_loader_ops dsp_ops, struct ssth_lib **dsp); +void ssth_skl_cleanup(struct device *dev, struct ssth_lib *ctx); #endif /*__HDA_SST_DSP_H__*/ diff --git a/sound/soc/hda/Kconfig b/sound/soc/hda/Kconfig index 841ea5276fe3..445fa9842127 100644 --- a/sound/soc/hda/Kconfig +++ b/sound/soc/hda/Kconfig @@ -29,6 +29,7 @@ config SND_SOC_HDA_SKL
config SND_SOC_I2S_SKL_MACH tristate "SOC Machine Audio driver for SKL Onboard I2S" + select SND_HDA_DSP_LOADER select SND_SOC_HDA_SKL select SND_SOC_RT286 select SND_SOC_HDMI diff --git a/sound/soc/hda/Makefile b/sound/soc/hda/Makefile index d318d4acb8a7..2be667dc0724 100644 --- a/sound/soc/hda/Makefile +++ b/sound/soc/hda/Makefile @@ -1,4 +1,4 @@ -snd-soc-hda-skl-objs := hda_skl.o hda_skl_pcm.o +snd-soc-hda-skl-objs := hda_skl.o hda_skl_pcm.o hda_soc_dsp.o
obj-$(CONFIG_SND_SOC_HDA_SKL) += snd-soc-hda-skl.o
diff --git a/sound/soc/hda/hda_skl.h b/sound/soc/hda/hda_skl.h index 0cc2ac69a9ab..914ea05da6aa 100644 --- a/sound/soc/hda/hda_skl.h +++ b/sound/soc/hda/hda_skl.h @@ -1,15 +1,3 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ - #ifndef __SOUND_SOC_HDA_SKL_H #define __SOUND_SOC_HDA_SKL_H
@@ -22,13 +10,15 @@
struct hda_soc_bus { struct hdac_bus chip; + struct device *dev; struct pci_dev *pci;
unsigned int init_failed:1; /* delayed init failed */ unsigned int msi:1; + struct ssth_lib *dsp; /* ssth lib ctx */ };
-/* to pass dai dma data */ +/*to pass dai dma data */ struct soc_hda_dma_params { u32 format; u8 stream_tag; diff --git a/sound/soc/hda/hda_soc_dsp.c b/sound/soc/hda/hda_soc_dsp.c new file mode 100644 index 000000000000..3fa033ecaa90 --- /dev/null +++ b/sound/soc/hda/hda_soc_dsp.c @@ -0,0 +1,62 @@ +/* + * hda_soc_dsp.c - HDA DSP interface for FW registration, Pipe and Module + * configurations + * + * Copyright (C) 2015 Intel Corp + * Author:Rafal Redzimski rafal.f.redzimski@intel.com + * Jeeja KP jeeja.kp@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/slab.h> +#include <linux/pci.h> +#include <sound/core.h> +#include <sound/pcm_params.h> +#include <sound/pcm.h> +#include <sound/soc-hda-sst-dsp.h> +#include <sound/soc-hda-sst-ipc.h> +#include "hda_skl.h" + +int ssth_dsp_register(struct hda_soc_bus *schip) +{ + void __iomem *mmio_base; + int irq = schip->chip.irq; + int ret = 0; + struct ssth_dsp_loader_ops loader_ops; + + /*TODO assign dsp loader ops */ + + /*TODO enable ppcap interrupt */ + /*read the BAR of the ADSP MMIO */ + mmio_base = pci_ioremap_bar(schip->pci, 4); + if (mmio_base == NULL) { + dev_err(schip->dev, "ioremap error\n"); + return -ENXIO; + } + + ret = ssth_skl_init(schip->dev, mmio_base, irq, + loader_ops, &schip->dsp); + + dev_dbg(schip->dev, "dsp registeration status=%d\n", ret); + return ret; +} + +void ssth_dsp_unregister(struct hda_soc_bus *schip) +{ + /*TODO disbale ppcap interrupt */ + ssth_skl_cleanup(schip->dev, schip->dsp); + if (schip->dsp->mmio_base) + iounmap(schip->dsp->mmio_base); +}
On Sun, Apr 19, 2015 at 02:27:27AM +0530, Vinod Koul wrote:
config SND_SOC_I2S_SKL_MACH tristate "SOC Machine Audio driver for SKL Onboard I2S"
select SND_HDA_DSP_LOADER
Looking at what this is modifying it really looks like there's some other patch series I wanted to look at first?
diff --git a/sound/soc/hda/hda_skl.h b/sound/soc/hda/hda_skl.h index 0cc2ac69a9ab..914ea05da6aa 100644 --- a/sound/soc/hda/hda_skl.h +++ b/sound/soc/hda/hda_skl.h @@ -1,15 +1,3 @@ -/*
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- */
Oh?
On Fri, Apr 24, 2015 at 06:36:57PM +0100, Mark Brown wrote:
On Sun, Apr 19, 2015 at 02:27:27AM +0530, Vinod Koul wrote:
config SND_SOC_I2S_SKL_MACH tristate "SOC Machine Audio driver for SKL Onboard I2S"
select SND_HDA_DSP_LOADER
Looking at what this is modifying it really looks like there's some other patch series I wanted to look at first?
Yes that was the SKL PCM driver series. I will repost that in this week along with few more changes, so you can look at updated series.
diff --git a/sound/soc/hda/hda_skl.h b/sound/soc/hda/hda_skl.h index 0cc2ac69a9ab..914ea05da6aa 100644 --- a/sound/soc/hda/hda_skl.h +++ b/sound/soc/hda/hda_skl.h @@ -1,15 +1,3 @@ -/*
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- more details.
- */
Oh?
Terrible :(
Will try to see these dont creep in patches
Thanks
From: Jeeja KP jeeja.kp@intel.com
This add helper functions to create module parameter for module IPC msg
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/hda_dsp_controls.h | 338 ++++++++++++++++++++++++++++++++ sound/soc/hda/hda_soc_dsp.c | 395 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 733 insertions(+) create mode 100644 sound/soc/hda/hda_dsp_controls.h
diff --git a/sound/soc/hda/hda_dsp_controls.h b/sound/soc/hda/hda_dsp_controls.h new file mode 100644 index 000000000000..6bd83db94009 --- /dev/null +++ b/sound/soc/hda/hda_dsp_controls.h @@ -0,0 +1,338 @@ +/* + * soc-hda-controls.h - Intel HDA Platform controls header file + * + * Copyright (C) 2014-15 Intel Corp + * Author: Jeeja KP jeeja.kp@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#ifndef __SOC_HDA_DSP_CONTROLS_H__ +#define __SOC_HDA_DSP_CONTROLS_H__ + +#include <linux/types.h> + +#define BITS_PER_BYTE 8 + +/* Maximum number of coefficients up down mixer module */ +#define UP_DOWN_MIXER_MAX_COEFF 6 + +enum ssth_channel_index { + SSTH_CHANNEL_LEFT = 0, + SSTH_CHANNEL_CENTER = 1, + SSTH_CHANNEL_RIGHT = 2, + SSTH_CHANNEL_LEFT_SURROUND = 3, + SSTH_CHANNEL_CENTER_SURROUND = 3, + SSTH_CHANNEL_RIGHT_SURROUND = 4, + SSTH_CHANNEL_LFE = 7, + SSTH_CHANNEL_INVALID = 0xF, +}; + +enum ssth_channel_config { + SSTH_CHANNEL_CONFIG_MONO = 0, /* One channel only. */ + SSTH_CHANNEL_CONFIG_STEREO = 1, /* L & R. */ + SSTH_CHANNEL_CONFIG_2_1 = 2, /* L, R & LFE; PCM only. */ + SSTH_CHANNEL_CONFIG_3_0 = 3, /* L, C & R; MP3 & AAC only. */ + SSTH_CHANNEL_CONFIG_3_1 = 4, /* L, C, R & LFE; PCM only. */ + SSTH_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only. */ + SSTH_CHANNEL_CONFIG_4_0 = 6, /* L, C, R & Cs; MP3 & AAC only. */ + SSTH_CHANNEL_CONFIG_5_0 = 7, /* L, C, R, Ls & Rs. */ + SSTH_CHANNEL_CONFIG_5_1 = 8, /* L, C, R, Ls, Rs & LFE. */ + SSTH_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two. */ + SSTH_CHANNEL_CONFIG_I2S_DUAL_STEREO_0 = 10, /* Stereo(L,R) in 4 slots, + 1st stream:[ L, R, -, - ] + */ + SSTH_CHANNEL_CONFIG_I2S_DUAL_STEREO_1 = 11, /* Stereo(L,R) in 4 slots, + 2nd stream:[ -, -, L, R ] + */ + SSTH_CHANNEL_CONFIG_INVALID +}; + +enum ssth_bitdepth { + SSTH_DEPTH_8BIT = 8, + SSTH_DEPTH_16BIT = 16, + SSTH_DEPTH_24BIT = 24, /**< Default. */ + SSTH_DEPTH_32BIT = 32, + SSTH_DEPTH_INVALID +}; + +enum ssth_interleaving_style { + SSTH_INTERLEAVING_PER_CHANNEL = 0, /* [s1_ch1...s1_chN,...,sM_ch1...sM_chN] */ + SSTH_INTERLEAVING_PER_SAMPLE = 1, /* [s1_ch1...sM_ch1,...,s1_chN...sM_chN] */ +}; + +enum ssth_sampling_frequency { + SSTH_FS_8000HZ = 8000, + SSTH_FS_11025HZ = 11025, + SSTH_FS_12000HZ = 12000, /** Mp3, AAC, SRC only. */ + SSTH_FS_16000HZ = 16000, + SSTH_FS_22050HZ = 22050, + SSTH_FS_24000HZ = 24000, /** Mp3, AAC, SRC only. */ + SSTH_FS_32000HZ = 32000, + SSTH_FS_44100HZ = 44100, + SSTH_FS_48000HZ = 48000, /**< Default. */ + SSTH_FS_64000HZ = 64000, /** AAC, SRC only. */ + SSTH_FS_88200HZ = 88200, /** AAC, SRC only. */ + SSTH_FS_96000HZ = 96000, /** AAC, SRC only. */ + SSTH_FS_128000HZ = 128000, /** SRC only. */ + SSTH_FS_176400HZ = 176400, /** SRC only. */ + SSTH_FS_192000HZ = 192000, /** SRC only. */ + SSTH_FS_INVALID +}; + +enum ssth_widget_type { + SSTH_WIDGET_VMIXER = 1, + SSTH_WIDGET_MIXER = 2, + SSTH_WIDGET_PGA = 3, + SSTH_WIDGET_MUX = 4 +}; + +struct ssth_audio_data_format { + enum ssth_sampling_frequency sampling_frequency; + enum ssth_bitdepth bit_depth; + u32 channel_map; + enum ssth_channel_config channel_config; + enum ssth_interleaving_style interleaving_style; + u8 number_of_channels; + u8 valid_bit_depth; + u8 sample_type; + u8 reserved[1]; +} __packed; + +struct ssth_base_module_config { + u32 cps; + u32 ibs; + u32 obs; + u32 is_pages; + struct ssth_audio_data_format audio_fmt; +}; + +struct ssth_copiergateway_cfg { + u32 node_id; + u32 dma_buffer_size; + u32 config_length; + u32 config_data[1]; /*not mandatory; required only for DMIC/I2S */ +} __packed; + +struct ssth_copier_module_config { + struct ssth_base_module_config base_module_config; + struct ssth_audio_data_format out_fmt; + u32 cpr_feature_mask; + struct ssth_copiergateway_cfg gtw_cfg; +} __packed; + +struct ssth_src_config { + enum ssth_sampling_frequency s_freq; +} __packed; + +struct ssth_src_module_config { + struct ssth_base_module_config base_module_config; + struct ssth_src_config src_config; +} __packed; + +struct ssth_up_down_mixer_module_config { + struct ssth_base_module_config base_module_config; + enum ssth_channel_config out_channel_config; + /* This should be set to 1 if user coefficients are required */ + u32 coeff_sel; + /* Pass the user coeff in this array */ + s32 coeff[UP_DOWN_MIXER_MAX_COEFF]; +} __packed; + +enum ssth_dma_type { + SSTH_DMA_TYPE_HDA_HOST_OUTPUT_CLASS = 0, + SSTH_DMA_TYPE_HDA_HOST_INPUT_CLASS = 1, + SSTH_DMA_TYPE_HDA_HOST_INOUT_CLASS = 2, + SSTH_DMA_TYPE_HDA_LINK_OUTPUT_CLASS = 8, + SSTH_DMA_TYPE_HDA_LINK_INPUT_CLASS = 9, + SSTH_DMA_TYPE_HDA_LINK_INOUT_CLASS = 0xA, + SSTH_DMA_TYPE_DMIC_LINK_INPUT_CLASS = 0xB, + SSTH_DMA_TYPE_I2S_LINK_OUTPUT_CLASS = 0xC, + SSTH_DMA_TYPE_I2S_LINK_INPUT_CLASS = 0xD, +}; + +union ssth_ssp_dma_node { + u8 val; + struct { + u8 dual_mono:1; + u8 time_slot:3; + u8 i2s_instance:4; + } dma_node; +}; + +union ssth_connector_node_id { + u32 val; + struct { + u32 dma_id:8; /* DMA engine ID */ + u32 dma_type:4; + u32 rsvd:20; + } node; +}; + +enum ssth_fw_pipeline_type { + SSTH_PIPELINE_TYPE_DECODE = 0, + SSTH_PIPELINE_TYPE_MIXER = 1, + SSTH_PIPELINE_TYPE_REFERENCE = 2, + SSTH_PIPELINE_TYPE_CAPTURE = 3 +}; + +enum ssth_core_affinity { + SSTH_AFFINITY_CORE_0 = 0, + SSTH_AFFINITY_CORE_1, + SSTH_AFFINITY_CORE_MAX +}; + +struct ssth_module_format { + u32 channels; + u32 sampling_freq; + u32 bit_depth; + u32 valid_bit_depth; + u32 channel_config; +}; + +struct ssth_module_instance_id { + u32 module_id; + u32 instance_id; +}; + +struct ssth_specific_config { + u32 caps_size; + u32 *caps; +}; + +enum ssth_pipe_state { + SSTH_PIPE_STATE_INVALID = 0, + SSTH_PIPE_STATE_CREATED = 1, + SSTH_PIPE_STATE_PAUSED = 2, + SSTH_PIPE_STATE_STARTED = 3 +}; + +struct ssth_pipe_module { + struct snd_soc_dapm_widget *w; + struct list_head node; +}; + +struct ssth_pipe { + u8 ppl_id; + u8 pipe_priority; + u16 conn_type; + u32 memory_pages; + enum ssth_pipe_state state; + struct list_head w_list; +}; + +enum ssth_module_state { + SSTH_MODULE_STATE_UNINIT = 0, + SSTH_MODULE_STATE_INIT_DONE = 1, + SSTH_MODULE_STATE_LOADED = 2, + SSTH_MODULE_STATE_UNLOADED = 3, + SSTH_MODULE_STATE_BIND_DONE = 4 +}; + +enum ssth_module_type { + SSTH_BASE_FW_MODULE = 0, + SSTH_BASE_MIX_IN_MODULE, + SSTH_BASE_MIX_OUT_MODULE, + SSTH_COPIER_MODULE, + SSTH_UPDWMIX_MODULE, + SSTH_SRCINT_MODULE, + SSTH_MAX_MODULE +}; + +enum ssth_dev_type { + SSTH_DEVICE_BT = 0x0, + SSTH_DEVICE_DMIC = 0x1, + SSTH_DEVICE_I2S = 0x2, + SSTH_DEVICE_SLIMBUS = 0x3, + SSTH_DEVICE_HDALINK = 0x4, + SSTH_DEVICE_NONE +}; + +enum ssth_pipe_conn_type { + SSTH_PIPE_CONN_TYPE_NONE = 0, + SSTH_PIPE_CONN_TYPE_FE, + SSTH_PIPE_CONN_TYPE_BE +}; + +enum ssth_module_conn_type { + SSTH_CONN_NONE = 0, + SSTH_CONN_SOURCE = 1, + SSTH_CONN_SINK = 2 +}; + +struct ssth_sink_module { + struct list_head node; + struct ssth_module_instance_id id; + u8 in_queue; +}; + +struct ssth_module_config { + struct ssth_module_instance_id id; + struct ssth_module_format in_fmt; + struct ssth_module_format out_fmt; + u8 max_in_queue; + u8 max_out_queue; + u8 in_queue_mask; + u8 out_queue_mask; + u8 in_queue; + u8 out_queue; + u32 mcps; + u32 ibs; + u32 obs; + u8 is_loadable; + u8 core_id; + u8 conn_type; + u8 dev_type; + u8 dma_id; + u8 time_slot; + u32 params_fixup; + u32 converter; + struct list_head sink_list; + enum ssth_module_conn_type hw_conn_type; + enum ssth_module_state m_state; + struct ssth_pipe *pipe; + struct ssth_specific_config formats_config; +}; + +/* Event types goes here */ +/* Reserve event type 0 for no event handlers */ +enum ssth_event_types { + HDA_SST_EVENT_TYPE_NONE = 0, + HDA_SST_SET_MIXER, + HDA_SST_SET_MUX, + HDA_SST_SET_VMIXER, + HDA_SST_SET_PGA +}; + +struct hda_sst_algo_data { + u32 max; + char *params; +}; + +struct ssth_pipeline { + struct ssth_pipe *pipe; + struct list_head node; +}; + +int ssth_create_pipeline(struct ssth_lib *ctx, struct ssth_pipe *pipe); +int ssth_run_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe); +int ssth_pause_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe); +int ssth_delete_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe); +int ssth_stop_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe); +int ssth_init_module(struct ssth_lib *ctx, struct ssth_module_config *module_config, + struct hda_sst_algo_data *ac); +int ssth_bind_unbind_modules(struct ssth_lib *ctx, struct ssth_module_config + *src_module, struct ssth_module_config *dst_module, bool bind); +enum ssth_bitdepth ssth_get_bit_depth(struct snd_pcm_hw_params *params); +#endif diff --git a/sound/soc/hda/hda_soc_dsp.c b/sound/soc/hda/hda_soc_dsp.c index 3fa033ecaa90..fea47645368e 100644 --- a/sound/soc/hda/hda_soc_dsp.c +++ b/sound/soc/hda/hda_soc_dsp.c @@ -28,6 +28,7 @@ #include <sound/soc-hda-sst-dsp.h> #include <sound/soc-hda-sst-ipc.h> #include "hda_skl.h" +#include "hda_dsp_controls.h"
int ssth_dsp_register(struct hda_soc_bus *schip) { @@ -60,3 +61,397 @@ void ssth_dsp_unregister(struct hda_soc_bus *schip) if (schip->dsp->mmio_base) iounmap(schip->dsp->mmio_base); } + +enum ssth_bitdepth ssth_get_bit_depth(struct snd_pcm_hw_params *params) +{ + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + return SSTH_DEPTH_8BIT; + + case SNDRV_PCM_FORMAT_S16_LE: + return SSTH_DEPTH_16BIT; + + case SNDRV_PCM_FORMAT_S24_LE: + return SSTH_DEPTH_24BIT; + + case SNDRV_PCM_FORMAT_S32_LE: + return SSTH_DEPTH_32BIT; + default: + return SSTH_DEPTH_INVALID; + } +} + +static u32 hda_sst_create_channel_map(enum ssth_channel_config channel_config) +{ + u32 config; + + switch (channel_config) { + case SSTH_CHANNEL_CONFIG_MONO: + config = (0xFFFFFFF0 | SSTH_CHANNEL_CENTER); + break; + + case SSTH_CHANNEL_CONFIG_STEREO: + config = (0xFFFFFF00 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_RIGHT << 4)); + break; + + case SSTH_CHANNEL_CONFIG_2_1: + config = (0xFFFFF000 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_RIGHT << 4) + | (SSTH_CHANNEL_LFE << 8)); + break; + + case SSTH_CHANNEL_CONFIG_3_0: + config = (0xFFFFF000 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_CENTER << 4) + | (SSTH_CHANNEL_RIGHT << 8)); + break; + + case SSTH_CHANNEL_CONFIG_3_1: + config = (0xFFFF0000 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_CENTER << 4) + | (SSTH_CHANNEL_RIGHT << 8) + | (SSTH_CHANNEL_LFE << 12)); + break; + + case SSTH_CHANNEL_CONFIG_QUATRO: + config = (0xFFFF0000 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_RIGHT << 4) + | (SSTH_CHANNEL_LEFT_SURROUND << 8) + | (SSTH_CHANNEL_RIGHT_SURROUND << 12)); + break; + + case SSTH_CHANNEL_CONFIG_4_0: + config = (0xFFFF0000 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_CENTER << 4) + | (SSTH_CHANNEL_RIGHT << 8) + | (SSTH_CHANNEL_CENTER_SURROUND << 12)); + break; + + case SSTH_CHANNEL_CONFIG_5_0: + config = (0xFFF00000 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_CENTER << 4) + | (SSTH_CHANNEL_RIGHT << 8) + | (SSTH_CHANNEL_LEFT_SURROUND << 12) + | (SSTH_CHANNEL_RIGHT_SURROUND << 16)); + break; + + case SSTH_CHANNEL_CONFIG_5_1: + config = (0xFF000000 | SSTH_CHANNEL_CENTER + | (SSTH_CHANNEL_LEFT << 4) + | (SSTH_CHANNEL_RIGHT << 8) + | (SSTH_CHANNEL_LEFT_SURROUND << 12) + | (SSTH_CHANNEL_RIGHT_SURROUND << 16) + | (SSTH_CHANNEL_LFE << 20)); + break; + + case SSTH_CHANNEL_CONFIG_DUAL_MONO: + config = (0xFFFFFF00 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_LEFT << 4)); + break; + + case SSTH_CHANNEL_CONFIG_I2S_DUAL_STEREO_0: + config = (0xFFFFFF00 | SSTH_CHANNEL_LEFT + | (SSTH_CHANNEL_RIGHT << 4)); + break; + + case SSTH_CHANNEL_CONFIG_I2S_DUAL_STEREO_1: + config = (0xFFFF00FF | (SSTH_CHANNEL_LEFT << 8) + | (SSTH_CHANNEL_RIGHT << 12)); + break; + default: + config = 0xFFFFFFFF; + } + return config; +} + +/* + * Fills in base_module_config structure . + * base_module_config is sent as input buffer with INIT_INSTANCE IPC msg + */ +static void ssth_set_base_module_format(struct ssth_lib *ctx, + struct ssth_module_config *mconfig, + struct ssth_base_module_config *base_module_config) +{ + struct ssth_module_format *format = &mconfig->in_fmt; + + base_module_config->audio_fmt.number_of_channels = + (u8)format->channels; + + base_module_config->audio_fmt.sampling_frequency = format->sampling_freq; + base_module_config->audio_fmt.bit_depth = format->bit_depth; + base_module_config->audio_fmt.valid_bit_depth = format->valid_bit_depth; + base_module_config->audio_fmt.channel_config = format->channel_config; + + dev_dbg(ctx->dev, "bit_depth=%x valid_bd=%x ch_config=%x\n", format->bit_depth, + format->valid_bit_depth, format->channel_config); + + base_module_config->audio_fmt.channel_map = + hda_sst_create_channel_map( + base_module_config->audio_fmt.channel_config); + + base_module_config->audio_fmt.interleaving_style = + SSTH_INTERLEAVING_PER_CHANNEL; + + base_module_config->cps = mconfig->mcps; + base_module_config->ibs = mconfig->ibs; + base_module_config->obs = mconfig->obs; + base_module_config->is_pages = 0; + +} + +/* + * Copies copier capabilities into copier module and updates copier module + * config size. + */ +static void ssth_copy_copier_caps(struct ssth_module_config *mconfig, + struct ssth_copier_module_config *cpr_mconfig) +{ + if (mconfig->formats_config.caps_size == 0) + return; + + memcpy(cpr_mconfig->gtw_cfg.config_data, + mconfig->formats_config.caps, + mconfig->formats_config.caps_size); + + cpr_mconfig->gtw_cfg.config_length = + (mconfig->formats_config.caps_size) / 4; +} + +static void ssth_setup_cpr_gateway_cfg(struct ssth_lib *ctx, + struct ssth_module_config *mconfig, + struct ssth_copier_module_config *cpr_mconfig) +{ + union ssth_connector_node_id node_id = {0}; + + switch (mconfig->dev_type) { + case SSTH_DEVICE_BT: + node_id.node.dma_type = + (SSTH_CONN_SOURCE == mconfig->conn_type) ? + SSTH_DMA_TYPE_I2S_LINK_OUTPUT_CLASS : + SSTH_DMA_TYPE_I2S_LINK_INPUT_CLASS; + node_id.node.dma_id = mconfig->dma_id; + break; + + case SSTH_DEVICE_I2S: + node_id.node.dma_type = + (SSTH_CONN_SOURCE == mconfig->conn_type) ? + SSTH_DMA_TYPE_I2S_LINK_OUTPUT_CLASS : + SSTH_DMA_TYPE_I2S_LINK_INPUT_CLASS; + node_id.node.dma_id = mconfig->dma_id + + (mconfig->time_slot << 1); + break; + + case SSTH_DEVICE_DMIC: + node_id.node.dma_type = SSTH_DMA_TYPE_DMIC_LINK_INPUT_CLASS; + node_id.node.dma_id = mconfig->dma_id + + (mconfig->time_slot); + break; + + case SSTH_DEVICE_HDALINK: + node_id.node.dma_type = + (SSTH_CONN_SOURCE == mconfig->conn_type) ? + SSTH_DMA_TYPE_HDA_LINK_OUTPUT_CLASS : + SSTH_DMA_TYPE_HDA_LINK_INPUT_CLASS; + node_id.node.dma_id = mconfig->dma_id; + break; + + default: + node_id.node.dma_type = + (SSTH_CONN_SOURCE == mconfig->conn_type) ? + SSTH_DMA_TYPE_HDA_HOST_OUTPUT_CLASS : + SSTH_DMA_TYPE_HDA_HOST_INPUT_CLASS; + node_id.node.dma_id = mconfig->dma_id; + break; + } + + cpr_mconfig->gtw_cfg.node_id = node_id.val; + + if (SSTH_CONN_SOURCE == mconfig->conn_type) + cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs; + else + cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->ibs; + cpr_mconfig->cpr_feature_mask = 0; + cpr_mconfig->gtw_cfg.config_length = 0; + + ssth_copy_copier_caps(mconfig, cpr_mconfig); + dev_dbg(ctx->dev, "Copier module config:\n"); + dev_dbg(ctx->dev, "length: 0x%x\n", cpr_mconfig->gtw_cfg.config_length); +} + +static void ssth_setup_cpr_out_format(struct ssth_lib *ctx, + struct ssth_module_config *mconfig, + struct ssth_copier_module_config *cpr_mconfig) +{ + struct ssth_module_format *format = &mconfig->out_fmt; + + cpr_mconfig->out_fmt.number_of_channels = + (u8)format->channels; + + cpr_mconfig->out_fmt.sampling_frequency = format->sampling_freq; + + cpr_mconfig->out_fmt.bit_depth = format->bit_depth; + + cpr_mconfig->out_fmt.valid_bit_depth = format->valid_bit_depth; + + cpr_mconfig->out_fmt.channel_config = format->channel_config; + + cpr_mconfig->out_fmt.channel_map = + hda_sst_create_channel_map(cpr_mconfig->out_fmt.channel_config); + cpr_mconfig->out_fmt.interleaving_style = SSTH_INTERLEAVING_PER_CHANNEL; + + + dev_dbg(ctx->dev, "copier out format chan=%d fre=%d bitdepth=%d\n", + cpr_mconfig->out_fmt.number_of_channels, + format->sampling_freq, format->bit_depth); +} + +static int ssth_set_src_format(struct ssth_lib *ctx, + struct ssth_module_config *mconfig, + struct ssth_src_module_config *src_mconfig) +{ + struct ssth_module_format *fmt = &mconfig->out_fmt; + + ssth_set_base_module_format(ctx, + mconfig, + (struct ssth_base_module_config *)src_mconfig); + + src_mconfig->src_config.s_freq = fmt->sampling_freq; + + return 0; +} + +static int ssth_set_updown_mixer_format(struct ssth_lib *ctx, + struct ssth_module_config *mconfig, + struct ssth_up_down_mixer_module_config *mixer_mconfig) +{ + int i = 0; + struct ssth_module_format *fmt = &mconfig->out_fmt; + + ssth_set_base_module_format(ctx, + mconfig, + (struct ssth_base_module_config *)mixer_mconfig); + mixer_mconfig->out_channel_config = fmt->channel_config; + + + /* Select F/W defatult coefficient */ + mixer_mconfig->coeff_sel = 0x0; + + /* User coeff, dont care since we are selecting F/W defaults */ + for (i = 0; i < UP_DOWN_MIXER_MAX_COEFF; i++) + mixer_mconfig->coeff[i] = 0xDEADBEEF; + + return 0; +} + +/* + * copier_module_config is sent as input buffer with INIT_INSTANCE IPC msg + */ +static int ssth_set_copier_format(struct ssth_lib *ctx, + struct ssth_module_config *mconfig, + struct ssth_copier_module_config *cpr_mconfig) +{ + int ret = 0; + + ssth_set_base_module_format(ctx, + mconfig, + (struct ssth_base_module_config *)cpr_mconfig); + + ssth_setup_cpr_out_format(ctx, mconfig, cpr_mconfig); + ssth_setup_cpr_gateway_cfg(ctx, mconfig, cpr_mconfig); + + return ret; +} + +static u16 ssth_get_module_param_size(struct ssth_lib *ctx, + struct ssth_module_config *mconfig) +{ + u16 param_size = 0; + + if (mconfig->id.module_id == SSTH_COPIER_MODULE) { + param_size = sizeof(struct ssth_copier_module_config); + param_size += mconfig->formats_config.caps_size; + return param_size; + } else if (mconfig->id.module_id == SSTH_SRCINT_MODULE) + return sizeof(struct ssth_src_module_config); + else if (mconfig->id.module_id == SSTH_UPDWMIX_MODULE) + return sizeof(struct ssth_up_down_mixer_module_config); + else + return sizeof(struct ssth_base_module_config); + return param_size; +} + +static int ssth_set_module_format(struct ssth_lib *ctx, + struct ssth_module_config *module_config, + u16 *module_config_size, + void **param_data) +{ + u16 param_size; + int i = 0, ret = 0; + u32 *byte; + + param_size = ssth_get_module_param_size(ctx, module_config); + + *param_data = kzalloc(param_size, GFP_KERNEL); + + if (NULL == *param_data) + return -ENOMEM; + + memset(*param_data, 0, param_size); + + *module_config_size = param_size; + + if (module_config->id.module_id == SSTH_COPIER_MODULE) + ret = ssth_set_copier_format(ctx, module_config, + *param_data); + else if (module_config->id.module_id == SSTH_SRCINT_MODULE) + ret = ssth_set_src_format(ctx, module_config, *param_data); + else if (module_config->id.module_id == SSTH_UPDWMIX_MODULE) + ret = ssth_set_updown_mixer_format(ctx, module_config, + *param_data); + else + ssth_set_base_module_format(ctx, module_config, *param_data); + + *module_config_size = param_size; + + dev_dbg(ctx->dev, "Module type=%d config size: %d bytes\n", + module_config->id.module_id, param_size); + for (i = 0, byte = (u32 *) *param_data; + i < (param_size)/sizeof(u32); + i++, byte++) { + dev_dbg(ctx->dev, "0x%x: %08x\n", i, *byte); + } + return ret; +} + +/* + * Allocates queue in the link pipeline (mix in/out module) + */ +static u8 ssth_alloc_queue(u8 *queue_mask, u8 max_queue, u8 queue_index) +{ + while (*queue_mask & (1llu << queue_index)) { + queue_index++; + if (queue_index == max_queue) { + queue_index = 0; + *queue_mask = 0; + break; + } + } + + *queue_mask |= (1llu << queue_index); + + return queue_index; +} + +static int ssth_free_queue(u8 *queue_mask, u8 queue_index) +{ + if (queue_index > 0) + *queue_mask &= ~(queue_index); + else + *queue_mask &= queue_index; + if (queue_index > 0) + return queue_index - 1; + else + return queue_index; +}
On Sun, Apr 19, 2015 at 02:27:28AM +0530, Vinod Koul wrote:
From: Jeeja KP jeeja.kp@intel.com
This add helper functions to create module parameter for module IPC msg
This feels like it's abstracting between two different driver internal formats but it's not entirely clear to me. I'm lacking some big picture stuff here.
On Fri, Apr 24, 2015 at 06:38:30PM +0100, Mark Brown wrote:
On Sun, Apr 19, 2015 at 02:27:28AM +0530, Vinod Koul wrote:
From: Jeeja KP jeeja.kp@intel.com
This add helper functions to create module parameter for module IPC msg
This feels like it's abstracting between two different driver internal formats but it's not entirely clear to me. I'm lacking some big picture stuff here.
Okay, let me try :)
So before this we had posted SKL base PCM driver which was doing HDA stuff only. Earlier this week you reviewed the SST-IPC driver series, that was sending IPCs to DSP. The other series you reviewed with this invokes those IPCs from SKL platform driver. This series is about DAPM widget handlers and driver changes for handling DSP.
The SKL supports HDA + DSP and HDA/I2S codecs.
We have one more series which will be missing link. Since we use the topology/DFW for defining the widgets and map, they are not coded in driver. That will be sent once topology patches are accepted
Thanks for reviewing the RFCs, I will rework the series and post them in sequence which would be: - SKL PCM driver first - ASoC bus handling wrapper code - IPC driver - SKL dapm widget handling and aDSP support - lastly support for topology to load the DAIs/widgets/map
One more would be ASoC HDA codecs but that can merged independent of these
The core HDA changes are mostly done so that dependency is no longer issue. Though that code is targeted for next merge window, I think we need to merge topic/hda from Takashi's tree to yours as well or whatever way you both feel comfortable..
Do let me know if you have more questions, yes that is a lot of code coming you way
Thanks
From: Jeeja KP jeeja.kp@intel.com
This helper function creates module init and bind IPC message based on module parameter and calls IPC interface to send the IPC to FW.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/hda_soc_dsp.c | 133 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+)
diff --git a/sound/soc/hda/hda_soc_dsp.c b/sound/soc/hda/hda_soc_dsp.c index fea47645368e..5a9927b82852 100644 --- a/sound/soc/hda/hda_soc_dsp.c +++ b/sound/soc/hda/hda_soc_dsp.c @@ -455,3 +455,136 @@ static int ssth_free_queue(u8 *queue_mask, u8 queue_index) else return queue_index; } + +int ssth_init_module(struct ssth_lib *ctx, + struct ssth_module_config *mconfig, + struct hda_sst_algo_data *ac) +{ + u16 module_config_size = 0; + void *param_data = NULL; + int ret = 0; + struct ssth_init_instance_msg msg; + struct ssth_dxstate_info dx; + + dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + + ret = ssth_set_module_format(ctx, mconfig, + &module_config_size, ¶m_data); + if (ret < 0) { + dev_err(ctx->dev, "Failed to set module format ret=%d\n", ret); + goto exit; + } + + if (mconfig->core_id != SSTH_AFFINITY_CORE_0) { + dx.core_mask = mconfig->core_id; + dx.dx_mask = mconfig->core_id; + /*SET core to D0 */ + ret = ssth_ipc_set_dx(ctx->ipc, 0, 0, &dx); + if (ret < 0) + return ret; + + } + msg.module_id = mconfig->id.module_id; + msg.instance_id = mconfig->id.instance_id; + msg.ppl_instance_id = mconfig->pipe->ppl_id; + msg.param_data_size = module_config_size; + msg.core_id = mconfig->core_id; + + if (mconfig->pipe->state != SSTH_PIPE_STATE_CREATED) { + dev_err(ctx->dev, "Pipe not created for Module state= %d pipe_id= %d\n", + mconfig->pipe->state, mconfig->pipe->ppl_id); + return -EIO; + } + + ret = ssth_ipc_init_instance(ctx->ipc, &msg, param_data); + if (ret < 0) { + dev_err(ctx->dev, "Failed to init instance ret=%d\n", ret); + goto exit; + } + mconfig->m_state = SSTH_MODULE_STATE_INIT_DONE; + +exit: + if (param_data != NULL) + kfree(param_data); + + return ret; +} + +int ssth_bind_unbind_modules(struct ssth_lib *ctx, struct ssth_module_config + *src_module, struct ssth_module_config *dst_module, bool bind) +{ + int ret = 0; + struct ssth_bind_unbind_msg msg; + struct ssth_sink_module *sink_module, *__sink_module; + + dev_dbg(ctx->dev, "%s: src module_id = %d src_instance=%d dst_module=%d \ + dst_instacne=%d\n", __func__, src_module->id.module_id, + src_module->id.instance_id, dst_module->id.module_id, + dst_module->id.instance_id); + + dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n", + src_module->m_state, dst_module->m_state); + /*if module is not bind, then don't send unbind */ + if (!bind) { + if (src_module->m_state != SSTH_MODULE_STATE_BIND_DONE) + return ret; + /* if intra module unbind, check if both modules are BIND, + then send unbind + */ + if ((src_module->pipe->ppl_id != dst_module->pipe->ppl_id) && + dst_module->m_state != SSTH_MODULE_STATE_BIND_DONE) + return ret; + } else if (src_module->m_state != SSTH_MODULE_STATE_INIT_DONE && + dst_module->m_state != SSTH_MODULE_STATE_INIT_DONE) + return ret; + + msg.module_id = src_module->id.module_id; + msg.instance_id = src_module->id.instance_id; + msg.dst_module_id = dst_module->id.module_id; + msg.dst_instance_id = dst_module->id.instance_id; + if (bind) { + src_module->out_queue = ssth_alloc_queue(&src_module->out_queue_mask, + src_module->max_out_queue, src_module->out_queue); + dst_module->in_queue = ssth_alloc_queue(&dst_module->in_queue_mask, + dst_module->max_in_queue, dst_module->in_queue); + sink_module = devm_kzalloc(ctx->dev, sizeof(struct ssth_sink_module), GFP_KERNEL); + if (!sink_module) { + dev_err(ctx->dev, "Unable to allocate mem\n"); + return -ENOMEM; + } + sink_module->id.instance_id = dst_module->id.instance_id; + sink_module->id.module_id = dst_module->id.module_id; + sink_module->in_queue = dst_module->in_queue; + list_add_tail(&sink_module->node, &src_module->sink_list); + } else { + src_module->out_queue = ssth_free_queue(&src_module->out_queue_mask, + src_module->out_queue); + list_for_each_entry_safe(sink_module, __sink_module, &src_module->sink_list, node) { + if ((sink_module->id.module_id == dst_module->id.module_id) && + ((sink_module->id.instance_id == dst_module->id.instance_id))) { + list_del(&sink_module->node); + dst_module->in_queue = sink_module->in_queue; + devm_kfree(ctx->dev, sink_module); + break; + } + } + dst_module->in_queue = ssth_free_queue(&dst_module->in_queue_mask, + dst_module->in_queue); + } + dev_dbg(ctx->dev, "in queque = %d out_queue =%d\n", + src_module->out_queue, dst_module->in_queue); + msg.src_queue = src_module->out_queue; + msg.dst_queue = dst_module->in_queue; + msg.bind = bind; + + ret = ssth_ipc_bind_unbind(ctx->ipc, &msg); + + if (!ret) { + if (bind) + src_module->m_state = SSTH_MODULE_STATE_BIND_DONE; + else + src_module->m_state = SSTH_MODULE_STATE_UNINIT; + } + return ret; +}
From: Jeeja KP jeeja.kp@intel.com
This helper function is called when FW pipeline has to created/deleted/ to set the pipe to run/pause and uses IPC lib to send the msg to dsp.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/hda_soc_dsp.c | 112 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+)
diff --git a/sound/soc/hda/hda_soc_dsp.c b/sound/soc/hda/hda_soc_dsp.c index 5a9927b82852..5f36ebdc0fba 100644 --- a/sound/soc/hda/hda_soc_dsp.c +++ b/sound/soc/hda/hda_soc_dsp.c @@ -588,3 +588,115 @@ int ssth_bind_unbind_modules(struct ssth_lib *ctx, struct ssth_module_config } return ret; } + +static int ssth_set_pipe_state(struct ssth_lib *ctx, struct ssth_pipe *pipe, + enum ssth_pipe_state state) +{ + int ret = 0; + + dev_dbg(ctx->dev, "%s: pipe_satate = %d\n", __func__, state); + ret = ssth_ipc_set_pipeline_state(ctx->ipc, pipe->ppl_id, state); + return ret; +} + +/* + * Creates pipeline, by sending IPC messages to FW + */ +int ssth_create_pipeline(struct ssth_lib *ctx, struct ssth_pipe *pipe) +{ + int ret = 0; + + dev_dbg(ctx->dev, "%s: pipe_id = %d\n", __func__, pipe->ppl_id); + + ret = ssth_ipc_create_pipeline(ctx->ipc, pipe->memory_pages, + pipe->pipe_priority, pipe->ppl_id); + if (ret < 0) { + dev_err(ctx->dev, "Failed to create pipeline\n"); + return ret; + } + pipe->state = SSTH_PIPE_STATE_CREATED; + return ret; +} + +/* + * Sets pipe state to RUNNING + */ +int ssth_run_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe) +{ + int ret = 0; + + dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id); + + /* If pipe was not created in FW, do not try to pause or delete */ + if (pipe->state < SSTH_PIPE_STATE_CREATED) + return ret; + + /* Pipe has to be paused before it is started */ + ret = ssth_set_pipe_state(ctx, pipe, PPL_PAUSED); + if (ret < 0) { + dev_err(ctx->dev, "Failed to pause pipe\n"); + return ret; + } + pipe->state = SSTH_PIPE_STATE_PAUSED; + ret = ssth_set_pipe_state(ctx, pipe, PPL_RUNNING); + if (ret < 0) { + dev_err(ctx->dev, "Failed to start pipe\n"); + return ret; + } + + pipe->state = SSTH_PIPE_STATE_STARTED; + + return ret; +} + +/* + * Sets pipe state to PAUSED in FW, stops DMA engines and releases resources + */ +int hda_sst_delete_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe) +{ + int ret = 0; + + dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id); + + /* If pipe is not started, do not try to stop the pipe in FW. */ + if (pipe->state < SSTH_PIPE_STATE_STARTED) + goto delete_pipe; + + ret = ssth_set_pipe_state(ctx, pipe, PPL_PAUSED); + if (ret < 0) { + dev_err(ctx->dev, "Failed to stop pipeline\n"); + return ret; + } + pipe->state = SSTH_PIPE_STATE_PAUSED; + +delete_pipe: + /* If pipe was not created in FW, do not try to delete it */ + if (pipe->state < SSTH_PIPE_STATE_CREATED) + return ret; + + ret = ssth_ipc_delete_pipeline(ctx->ipc, pipe->ppl_id); + if (ret < 0) { + dev_err(ctx->dev, "Failed to delete pipeline\n"); + return ret; + } + return ret; +} + +int hda_sst_stop_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe) +{ + int ret = 0; + + dev_dbg(ctx->dev, "In %s pipe=%d\n", __func__, pipe->ppl_id); + + /* If pipe was not created in FW, do not try to pause or delete */ + if (pipe->state < SSTH_PIPE_STATE_PAUSED) + return ret; + ret = ssth_set_pipe_state(ctx, pipe, PPL_PAUSED); + if (ret < 0) { + dev_dbg(ctx->dev, "Failed to stop pipe\n"); + return ret; + } + pipe->state = SSTH_PIPE_STATE_CREATED; + + return ret; +}
From: Jeeja KP jeeja.kp@intel.com
This add widget event handlers for PRE/POST PMU event and PRE/POST PMD event. event handlers functions implement the FW sequence to enable path from source to sink.
Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/Makefile | 3 +- sound/soc/hda/hda_dsp_controls.c | 702 ++++++++++++++++++++++++++++++++++++++ sound/soc/hda/hda_skl.h | 17 + sound/soc/hda/hda_soc_dsp.c | 4 +- 4 files changed, 723 insertions(+), 3 deletions(-) create mode 100644 sound/soc/hda/hda_dsp_controls.c
diff --git a/sound/soc/hda/Makefile b/sound/soc/hda/Makefile index 2be667dc0724..668c831b196e 100644 --- a/sound/soc/hda/Makefile +++ b/sound/soc/hda/Makefile @@ -1,4 +1,5 @@ -snd-soc-hda-skl-objs := hda_skl.o hda_skl_pcm.o hda_soc_dsp.o +snd-soc-hda-skl-objs := hda_skl.o hda_skl_pcm.o hda_soc_dsp.o \ +hda_dsp_controls.o
obj-$(CONFIG_SND_SOC_HDA_SKL) += snd-soc-hda-skl.o
diff --git a/sound/soc/hda/hda_dsp_controls.c b/sound/soc/hda/hda_dsp_controls.c new file mode 100644 index 000000000000..ee53227fa751 --- /dev/null +++ b/sound/soc/hda/hda_dsp_controls.c @@ -0,0 +1,702 @@ +/* + * hda_dsp_control.c -HD Audio Platform component ALSA controls + * + * Copyright (C) 2015 Intel Corp + * Author: Jeeja KP jeeja.kp@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <linux/slab.h> +#include <linux/types.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-hda-sst-dsp.h> +#include "hda_dsp_controls.h" +#include "hda_skl.h" + +static int is_hda_widget_type(struct snd_soc_dapm_widget *w) +{ + return ((w->id == snd_soc_dapm_dai_link) || + (w->id == snd_soc_dapm_dai_in) || + (w->id == snd_soc_dapm_aif_in) || + (w->id == snd_soc_dapm_aif_out) || + (w->id == snd_soc_dapm_dai_out)) ? 1 : 0; + +} + +static int hda_bind_unbind_intra_pipes(struct ssth_module_config *src_module, + struct ssth_module_config *sink_module, struct ssth_lib *ctx, bool bind) +{ + int ret = 0; + + if (!bind) { + ret = ssth_stop_pipe(ctx, src_module->pipe); + if (ret < 0) + return ret; + } + return ssth_bind_unbind_modules(ctx, src_module, sink_module, bind); +} + +static int hda_bind_unbind_pipe_src(struct snd_soc_dapm_widget *w, + struct ssth_lib *ctx, bool bind) +{ + struct ssth_module_config *sink_module = w->priv; + struct ssth_module_config *src_module = NULL; + struct snd_soc_dapm_path *p = NULL; + struct ssth_pipe *sink_pipe = sink_module->pipe; + struct ssth_pipe *src_pipe = NULL; + int ret = 0; + + list_for_each_entry(p, &w->sources, list_sink) { + if (p->connected && !p->connected(w, p->source) + && (p->source->priv == NULL) + && (is_hda_widget_type(w))) + continue; + dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, w->name); + if (p->connect && + (p->source->priv != NULL) && + (!is_hda_widget_type(p->source))) { + src_module = p->source->priv; + src_pipe = src_module->pipe; + dev_dbg(ctx->dev, "src widget=%s\n", p->source->name); + if (sink_pipe->ppl_id != src_pipe->ppl_id) + return hda_bind_unbind_intra_pipes(src_module, + sink_module, ctx, bind); + } + } + return ret; +} + +static int hda_bind_unbind_pipe_sink(struct snd_soc_dapm_widget *w, + struct ssth_lib *ctx, bool bind) +{ + struct ssth_module_config *src_module = w->priv; + struct ssth_module_config *sink_module = NULL; + struct snd_soc_dapm_path *p = NULL; + struct ssth_pipe *src_pipe = src_module->pipe; + struct ssth_pipe *sink_pipe = NULL; + int ret = 0; + + list_for_each_entry(p, &w->sinks, list_source) { + if (p->connected && !p->connected(w, p->sink) + && (p->sink->priv == NULL) + && (is_hda_widget_type(w))) + continue; + + dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name); + if (p->connect && + (p->sink->priv != NULL) && + (!is_hda_widget_type(p->sink))) { + sink_module = p->sink->priv; + sink_pipe = sink_module->pipe; + dev_dbg(ctx->dev, "sink widget=%s\n", p->sink->name); + if (sink_pipe->ppl_id != src_pipe->ppl_id) + return hda_bind_unbind_intra_pipes(src_module, + sink_module, ctx, bind); + } + } + return ret; +} + +static int hda_sst_bind_unbind_pipes(struct snd_soc_dapm_widget *w, + struct ssth_lib *ctx, bool bind) +{ + struct ssth_module_config *sink_module = w->priv; + int ret = 0; + + dev_dbg(ctx->dev, "%s: widget = %s conn_type=%d\n", __func__, w->name, + sink_module->hw_conn_type); + ret = hda_bind_unbind_pipe_src(w, ctx, bind); + if (ret < 0) + return ret; + return hda_bind_unbind_pipe_sink(w, ctx, bind); +} + +static bool hda_sst_is_pipe_mem_available(struct hda_platform_info *pinfo, + struct ssth_lib *ctx, struct ssth_module_config *mconfig) +{ + dev_dbg(ctx->dev, "%s: module_id =%d instance=%d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + pinfo->resource.mem += mconfig->pipe->memory_pages; + + if (pinfo->resource.mem > pinfo->resource.max_mem) { + dev_err(ctx->dev, "exceeds ppl memory available=%d > mem=%d\n", + pinfo->resource.max_mem, pinfo->resource.mem); + /* TODO - handle failure case + pinfo->resource.mem -= mconfig->pipe->memory_pages; + return false; + */ + } + return true; +} + +static bool hda_sst_is_pipe_mcps_available(struct hda_platform_info *pinfo, + struct ssth_lib *ctx, struct ssth_module_config *mconfig) +{ + dev_dbg(ctx->dev, "%s: module_id = %d instance=%d\n", __func__, + mconfig->id.module_id, mconfig->id.instance_id); + pinfo->resource.mcps += mconfig->mcps; + + if (pinfo->resource.mcps > pinfo->resource.max_mcps) { + dev_err(ctx->dev, "exceeds ppl memory available=%d > mem=%d\n", + pinfo->resource.max_mcps, pinfo->resource.mcps); + /*TODO - handle failure case + pinfo->resource.mcps -= mconfig->mcps; + return false + */ + } + return true; +} + +static void hda_update_slot_map(struct ssth_lib *ctx, + struct ssth_module_config *m_cfg) +{ + int slot_map = 0xFFFFFFFF; + int num_slots = 0, i; + int start_slot; + + start_slot = m_cfg->formats_config.caps[(1 << (m_cfg->time_slot)) - 1]; + start_slot &= 0xF; + + if (m_cfg->hw_conn_type == SSTH_CONN_SOURCE) + num_slots = m_cfg->in_fmt.channels; + else + num_slots = m_cfg->out_fmt.channels; + + for (i = 0; i < num_slots; i++) { + /* + * For 2 channels with starting slot as 0, slot map will + * look like 0xFFFFFF10, for 4 channels with starting slot + * as 2, slot map will look like 0xFFFF5432 + * + */ + slot_map &= (~(0xF << (4 * i)) | (start_slot << (4 * i))); + start_slot++; + } + dev_dbg(ctx->dev, "slot_map = %x\n", slot_map); + m_cfg->formats_config.caps[(1 << (m_cfg->time_slot)) - 1] = slot_map; +} + +static void hda_update_ch_config(struct ssth_module_config *m_cfg) +{ + struct ssth_module_format *in_fmt, *out_fmt; + + in_fmt = &m_cfg->in_fmt; + out_fmt = &m_cfg->out_fmt; + + if (in_fmt->channels == 2) { + in_fmt->channel_config = SSTH_CHANNEL_CONFIG_STEREO; + if (out_fmt->channels == 1) + out_fmt->channel_config = SSTH_CHANNEL_CONFIG_MONO; + if (out_fmt->channels == 2) + out_fmt->channel_config = SSTH_CHANNEL_CONFIG_STEREO; + } else if (in_fmt->channels == 1) { + in_fmt->channel_config = SSTH_CHANNEL_CONFIG_MONO; + if (out_fmt->channels == 1) + out_fmt->channel_config = SSTH_CHANNEL_CONFIG_MONO; + if (out_fmt->channels == 2) + out_fmt->channel_config = SSTH_CHANNEL_CONFIG_STEREO; + } + +} + +static int hda_sst_get_pipe_widget(struct device *dev, + struct snd_soc_dapm_widget *w, struct ssth_pipe *pipe) +{ + struct ssth_module_config *src_module = NULL; + struct snd_soc_dapm_path *p = NULL; + struct ssth_pipe_module *p_module = NULL; + + dev_dbg(dev, "In%s widget=%s\n", __func__, w->name); + + p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL); + if (!p_module) + return -ENOMEM; + + p_module->w = w; + list_add_tail(&p_module->node, &pipe->w_list); + + list_for_each_entry(p, &w->sinks, list_source) { + if ((p->sink->priv == NULL) + && (is_hda_widget_type(w))) + continue; + + if ((p->sink->priv != NULL) && (p->connect) + && (!is_hda_widget_type(p->sink))) { + src_module = p->sink->priv; + if (pipe->ppl_id == src_module->pipe->ppl_id) { + dev_dbg(dev, "%s: found widget = %s\n", __func__, p->sink->name); + hda_sst_get_pipe_widget(dev, p->sink, pipe); + } + } + } + return 0; +} + +static int hda_init_pipe_modules(struct ssth_lib *ctx, + struct ssth_pipe *pipe, struct hda_platform_info *pinfo) +{ + struct ssth_pipe_module *w_module = NULL; + struct snd_soc_dapm_widget *w = NULL; + struct ssth_module_config *mconfig = NULL; + int ret = 0; + + dev_dbg(ctx->dev, "%s: pipe=%d\n", __func__, pipe->ppl_id); + list_for_each_entry(w_module, &pipe->w_list, node) { + w = w_module->w; + dev_dbg(ctx->dev, "Pipe Module =%s\n", w->name); + mconfig = w->priv; + /*TODO if loadable module, mconfig->is_loadable, load module */ + + ret = ssth_init_module(ctx, mconfig, NULL); + if (ret < 0) + return ret; + } + return 0; +} + +static int hda_bind_unbind_pipe_modules(struct ssth_lib *ctx, + struct ssth_pipe *pipe, bool bind) +{ + struct ssth_pipe_module *w_module = NULL; + struct ssth_module_config *src_module = NULL; + struct ssth_module_config *dst_module = NULL; + int ret = 0; + + dev_dbg(ctx->dev, "%s: pipe=%d\n", __func__, pipe->ppl_id); + list_for_each_entry(w_module, &pipe->w_list, node) { + dst_module = w_module->w->priv; + + if (src_module == NULL) { + src_module = dst_module; + continue; + } + + ret = ssth_bind_unbind_modules(ctx, src_module, dst_module, bind); + if (ret < 0) + return ret; + src_module = dst_module; + } + return 0; +} + +static int hda_unload_pipe_modules(struct ssth_lib *ctx, + struct ssth_pipe *pipe) +{ + struct ssth_pipe_module *w_module = NULL; + struct ssth_module_config *mconfig = NULL; + + dev_dbg(ctx->dev, "%s: pipe=%d\n", __func__, pipe->ppl_id); + list_for_each_entry(w_module, &pipe->w_list, node) { + mconfig = w_module->w->priv; + /*TODO mconfig->is_loadable = 1 , unload module */ + } + return 0; +} + +static int hda_sst_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w, + int w_type, struct ssth_lib *ctx, struct hda_platform_info *pinfo) +{ + int ret = 0; + struct ssth_module_config *mconfig = w->priv; + struct ssth_pipe *s_pipe = mconfig->pipe; + + dev_dbg(ctx->dev, "%s: widget =%s type=%d\n", __func__, w->name, w_type); + + /*check resource available */ + if (!hda_sst_is_pipe_mcps_available(pinfo, ctx, mconfig)) + return -1; + + if (w_type == SSTH_WIDGET_VMIXER || + w_type == SSTH_WIDGET_MIXER) { + + if (!hda_sst_is_pipe_mem_available(pinfo, ctx, mconfig)) + return -ENOMEM; + + ret = ssth_create_pipeline(ctx, mconfig->pipe); + if (ret < 0) + return ret; + if (list_empty(&s_pipe->w_list)) { + ret = hda_sst_get_pipe_widget(ctx->dev, w, s_pipe); + if (ret < 0) + return ret; + } + ret = hda_init_pipe_modules(ctx, s_pipe, pinfo); + if (ret < 0) + return ret; + return hda_bind_unbind_pipe_modules(ctx, s_pipe, true); + } + return 0; +} + +static int hda_sst_dapm_post_pmu_event(struct snd_soc_dapm_widget *w, + int w_type, struct ssth_lib *ctx, struct hda_platform_info *pinfo) +{ + struct ssth_module_config *mconfig = w->priv; + struct ssth_pipeline *ppl, *__ppl; + int ret = 0; + + dev_dbg(ctx->dev, "%s: widget = %s\n", __func__, w->name); + + ret = hda_sst_bind_unbind_pipes(w, ctx, true); + if (ret < 0) + return ret; + if (w_type == SSTH_WIDGET_VMIXER || + w_type == SSTH_WIDGET_MIXER) { + if (mconfig->pipe->conn_type != SSTH_PIPE_CONN_TYPE_FE) { + /*if module is not a FE then add to ppl_start list, + to send the run pipe when be is reached */ + ppl = kzalloc(sizeof(*ppl), GFP_KERNEL); + if (!ppl) + return -ENOMEM; + ppl->pipe = mconfig->pipe; + list_add(&ppl->node, &pinfo->ppl_start_list); + } + } + + if (w_type == SSTH_WIDGET_PGA) { + list_for_each_entry_safe(ppl, __ppl, &pinfo->ppl_start_list, node) { + list_del(&ppl->node); + + dev_dbg(ctx->dev, "%s: set to run pipe_id =%d\n", __func__, ppl->pipe->ppl_id); + ret = ssth_run_pipe(ctx, ppl->pipe); + kfree(ppl); + if (ret < 0) + return ret; + } + } + return ret; +} + +static int hda_sst_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w, + int w_type, struct ssth_lib *ctx, struct hda_platform_info *pinfo) +{ + struct ssth_module_config *mconfig = w->priv; + int ret = 0; + + dev_dbg(ctx->dev, "%s: widget = %s\n", __func__, w->name); + + if (w_type == SSTH_WIDGET_PGA) { + if (mconfig->pipe->conn_type != SSTH_PIPE_CONN_TYPE_FE) { + ret = ssth_stop_pipe(ctx, mconfig->pipe); + if (ret < 0) + return ret; + } + } + return ret; +} + +static int hda_sst_dapm_post_pmd_event(struct snd_soc_dapm_widget *w, + int w_type, struct ssth_lib *ctx, struct hda_platform_info *pinfo) +{ + struct ssth_module_config *mconfig = w->priv; + int ret = 0; + struct ssth_pipe *s_pipe = mconfig->pipe; + + dev_dbg(ctx->dev, "%s: widget = %s\n", __func__, w->name); + + pinfo->resource.mcps -= mconfig->mcps; + + if (w_type == SSTH_WIDGET_VMIXER || + w_type == SSTH_WIDGET_MIXER) { + + ret = hda_bind_unbind_pipe_modules(ctx, s_pipe, false); + if (ret < 0) + return ret; + ret = hda_unload_pipe_modules(ctx, s_pipe); + if (ret < 0) + return ret; + ret = ssth_delete_pipe(ctx, mconfig->pipe); + pinfo->resource.mem -= mconfig->pipe->memory_pages; + } + + return ret; +} + +static int hda_sst_event_handler(struct snd_soc_dapm_widget *w, + int event, int w_type) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct hda_soc_bus *hda = dev_get_drvdata(dapm->dev); + struct hda_platform_info *pinfo = hda->pinfo; + struct ssth_lib *ctx = hda->dsp; + + dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + return hda_sst_dapm_pre_pmu_event(w, w_type, ctx, pinfo); + break; + case SND_SOC_DAPM_POST_PMU: + return hda_sst_dapm_post_pmu_event(w, w_type, ctx, pinfo); + break; + case SND_SOC_DAPM_PRE_PMD: + return hda_sst_dapm_pre_pmd_event(w, w_type, ctx, pinfo); + break; + case SND_SOC_DAPM_POST_PMD: + return hda_sst_dapm_post_pmd_event(w, w_type, ctx, pinfo); + break; + } + + return 0; +} + +static int hda_sst_vmixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + + dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name); + return hda_sst_event_handler(w, event, SSTH_WIDGET_VMIXER); +} + +static int hda_sst_mixer_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + + dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name); + + return hda_sst_event_handler(w, event, SSTH_WIDGET_MIXER); +} + +static int hda_sst_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + + dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name); + + return hda_sst_event_handler(w, event, SSTH_WIDGET_MUX); +} + +static int hda_sst_pga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) + +{ + struct snd_soc_dapm_context *dapm = w->dapm; + + dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name); + return hda_sst_event_handler(w, event, SSTH_WIDGET_PGA); +} + +static struct ssth_module_config *hda_sst_get_module_by_dir( + struct snd_soc_dapm_widget *w, int dir, char *m_type) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_dapm_widget *w1 = NULL; + struct snd_soc_dapm_path *p = NULL; + struct ssth_module_config *mconfig = NULL; + + /* get the source modules dir = 0 source module, dir = 1 sink modules*/ + if (dir == 0) { + dev_dbg(dapm->dev, "Stream name=%s\n", w->name); + if (list_empty(&w->sources)) + return mconfig; + + list_for_each_entry(p, &w->sources, list_sink) { + if (p->connected && !p->connected(w, p->source) && + !is_hda_widget_type(p->source) && + (strstr(p->source->name, m_type) == NULL)) + continue; + + if (p->connect && p->source->priv) { + dev_dbg(dapm->dev, "module widget=%s\n", p->source->name); + return p->source->priv; + } + w1 = p->source; + } + } else { + dev_dbg(dapm->dev, "Stream name=%s\n", w->name); + if (list_empty(&w->sinks)) + return mconfig; + + list_for_each_entry(p, &w->sinks, list_source) { + if (p->connected && !p->connected(w, p->sink) && + !is_hda_widget_type(p->sink) && + (strstr(p->sink->name, m_type) == NULL)) + continue; + + if (p->connect && p->sink->priv) { + dev_dbg(dapm->dev, "module widget=%s\n", p->sink->name); + return p->sink->priv; + } + w1 = p->sink; + } + } + if (w1 != NULL) + mconfig = hda_sst_get_module_by_dir(w1, dir, m_type); + return mconfig; +} + +static struct ssth_module_config *hda_sst_get_module(struct snd_soc_dai *dai, + int stream, bool is_fe, char *m_type) +{ + struct snd_soc_dapm_widget *w; + int dir = 0; + + dev_dbg(dai->dev, "%s: enter, dai-name=%s dir=%d\n", __func__, dai->name, stream); + + /* if FE - Playback, then parse sink list , Capture then source list + if BE - Playback, then parse source list , Capture then sink list + */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + w = dai->playback_widget; + (is_fe) ? (dir = 1) : (dir = 0); + } else { + w = dai->capture_widget; + (is_fe) ? (dir = 0) : (dir = 1); + } + return hda_sst_get_module_by_dir(w, dir, m_type); +} + +static void hda_set_module_params(struct ssth_module_config *mconfig, + struct snd_pcm_hw_params *params, bool is_in_fmt) +{ + struct ssth_module_format *format = NULL; + + if (is_in_fmt) + format = &mconfig->in_fmt; + else + format = &mconfig->out_fmt; + /*set the hw_params */ + format->sampling_freq = params_rate(params); + format->channels = params_channels(params); + format->valid_bit_depth = ssth_get_bit_depth(params); + if (format->valid_bit_depth == SSTH_DEPTH_16BIT) + format->bit_depth = format->valid_bit_depth; + else if (format->valid_bit_depth == SSTH_DEPTH_24BIT) + format->bit_depth = SSTH_DEPTH_32BIT; + if (is_in_fmt) { + mconfig->ibs = (format->sampling_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } else { + mconfig->obs = (format->sampling_freq / 1000) * + (format->channels) * + (format->bit_depth >> 3); + } +} + +void hda_sst_set_copier_hw_params(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, int stream, bool is_fe) +{ + struct ssth_module_config *mconfig = NULL; + bool in_fmt; + + dev_dbg(dai->dev, + "%s: enter, dai-name=%s dir=%d\n", __func__, dai->name, stream); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + in_fmt = true; + else + in_fmt = false; + mconfig = hda_sst_get_module(dai, stream, is_fe, "cpr"); + if (mconfig != NULL) + hda_set_module_params(mconfig, params, in_fmt); +} + +void hda_sst_set_copier_dma_id(struct snd_soc_dai *dai, int dma_id, int stream, + bool is_fe) +{ + struct ssth_module_config *mconfig = NULL; + + dev_dbg(dai->dev, + "%s: enter, dai-name=%s dir=%d\n", __func__, + dai->name, 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*/ +void hda_sst_set_be_copier_caps(struct snd_soc_dai *dai, + struct ssth_specific_config *configs, int stream) +{ + struct ssth_module_config *mconfig = NULL; + + dev_dbg(dai->dev, "%s: enter, dai-name=%s\n", __func__, dai->name); + mconfig = hda_sst_get_module(dai, stream, false, "cpr"); + if (mconfig != NULL && configs->caps_size != 0) { + memcpy(mconfig->formats_config.caps, + configs->caps, + configs->caps_size); + + mconfig->formats_config.caps_size = configs->caps_size; + } +} + +void hda_sst_set_be_dmic_config(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, int stream) +{ + struct ssth_module_config *mconfig = NULL; + u32 outctrl; + + dev_dbg(dai->dev, "%s: enter, dai-name=%s\n", __func__, dai->name); + mconfig = hda_sst_get_module(dai, stream, false, "cpr"); + if (mconfig != NULL && mconfig->formats_config.caps_size != 0) { + /*FIXME need to fix based on the FW dmic interface struct. + the parameter to set here should to set the DMIC mode. + currenltly using timeslot + */ + if (strncmp(dai->name, "DMIC23 Pin", strlen(dai->name)) == 0) { + mconfig->time_slot = 1; + outctrl = mconfig->formats_config.caps[3]; + } else + outctrl = mconfig->formats_config.caps[2]; + + if (ssth_get_bit_depth(params) == SSTH_DEPTH_16BIT) + outctrl &= ~BIT(19); + else if (ssth_get_bit_depth(params) == SSTH_DEPTH_24BIT) + outctrl |= BIT(19); + if (strncmp(dai->name, "DMIC23 Pin", strlen(dai->name)) == 0) + mconfig->formats_config.caps[3] = outctrl; + else + mconfig->formats_config.caps[2] = outctrl; + dev_dbg(dai->dev, "%s: outctrl =%x\n", __func__, outctrl); + hda_set_module_params(mconfig, params, true); + + /*FIXME need to fix based on the FW dmic interface struct. + the parameter to set here should to set the DMIC mode. + currenltly using timeslot + */ + if (strncmp(dai->name, "DMIC23 Pin", strlen(dai->name)) == 0) + mconfig->time_slot = 1; + + } +} + +int hda_sst_set_fe_pipeline_state(struct snd_soc_dai *dai, bool start, + int stream) +{ + struct hda_soc_bus *hda = dev_get_drvdata(dai->dev); + struct ssth_lib *ctx = hda->dsp; + + struct ssth_module_config *mconfig = NULL; + int ret = 0; + + dev_dbg(dai->dev, "%s: enter, dai-name=%s dir=%d\n", __func__, + dai->name, stream); + mconfig = hda_sst_get_module(dai, stream, true, "cpr"); + if (mconfig != NULL) { + if (start) + ret = ssth_run_pipe(ctx, mconfig->pipe); + else + ret = ssth_stop_pipe(ctx, mconfig->pipe); + } + + return ret; +} diff --git a/sound/soc/hda/hda_skl.h b/sound/soc/hda/hda_skl.h index 914ea05da6aa..f6d9c629a8e3 100644 --- a/sound/soc/hda/hda_skl.h +++ b/sound/soc/hda/hda_skl.h @@ -15,7 +15,9 @@ struct hda_soc_bus {
unsigned int init_failed:1; /* delayed init failed */ unsigned int msi:1; + struct ssth_lib *dsp; /* ssth lib ctx */ + struct hda_platform_info *pinfo; /* platform component info*/ };
/*to pass dai dma data */ @@ -24,6 +26,21 @@ struct soc_hda_dma_params { u8 stream_tag; };
+struct ssth_dsp_resource { + u32 max_mcps; + u32 max_mem; + u32 mcps; + u32 mem; +}; + +struct hda_platform_info { + struct hda_soc_bus schip; + struct device *dev; + struct ssth_dsp_resource resource; + struct list_head ppl_list; + struct list_head ppl_start_list; +}; + int azx_get_delay_from_lpib(struct hdac_bus *chip, struct hdac_stream *azx_dev, unsigned int pos); diff --git a/sound/soc/hda/hda_soc_dsp.c b/sound/soc/hda/hda_soc_dsp.c index 5f36ebdc0fba..ce1527602e00 100644 --- a/sound/soc/hda/hda_soc_dsp.c +++ b/sound/soc/hda/hda_soc_dsp.c @@ -652,7 +652,7 @@ int ssth_run_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe) /* * Sets pipe state to PAUSED in FW, stops DMA engines and releases resources */ -int hda_sst_delete_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe) +int ssth_delete_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe) { int ret = 0;
@@ -682,7 +682,7 @@ delete_pipe: return ret; }
-int hda_sst_stop_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe) +int ssth_stop_pipe(struct ssth_lib *ctx, struct ssth_pipe *pipe) { int ret = 0;
On Sun, Apr 19, 2015 at 02:27:31AM +0530, Vinod Koul wrote:
+static int is_hda_widget_type(struct snd_soc_dapm_widget *w) +{
- return ((w->id == snd_soc_dapm_dai_link) ||
(w->id == snd_soc_dapm_dai_in) ||
(w->id == snd_soc_dapm_aif_in) ||
(w->id == snd_soc_dapm_aif_out) ||
(w->id == snd_soc_dapm_dai_out)) ? 1 : 0;
Please use switch statements, and again no need to convert logic values into logic values.
+static int hda_sst_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
- int w_type, struct ssth_lib *ctx, struct hda_platform_info *pinfo)
+{
- int ret = 0;
- struct ssth_module_config *mconfig = w->priv;
- struct ssth_pipe *s_pipe = mconfig->pipe;
- dev_dbg(ctx->dev, "%s: widget =%s type=%d\n", __func__, w->name, w_type);
- /*check resource available */
- if (!hda_sst_is_pipe_mcps_available(pinfo, ctx, mconfig))
return -1;
Proper error code please.
- if (w_type == SSTH_WIDGET_VMIXER ||
w_type == SSTH_WIDGET_MIXER) {
switch.
if (!hda_sst_is_pipe_mem_available(pinfo, ctx, mconfig))
return -ENOMEM;
ret = ssth_create_pipeline(ctx, mconfig->pipe);
if (ret < 0)
return ret;
if (list_empty(&s_pipe->w_list)) {
ret = hda_sst_get_pipe_widget(ctx->dev, w, s_pipe);
if (ret < 0)
return ret;
}
ret = hda_init_pipe_modules(ctx, s_pipe, pinfo);
if (ret < 0)
return ret;
The error handling here appears to be a bit incomplete, we don't unwind anything we did.
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
return hda_sst_dapm_pre_pmu_event(w, w_type, ctx, pinfo);
- break;
Please follow the kernel coding style.
+static int hda_sst_vmixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
+{
- struct snd_soc_dapm_context *dapm = w->dapm;
- dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name);
- return hda_sst_event_handler(w, event, SSTH_WIDGET_VMIXER);
+}
+static int hda_sst_mixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
+{
- struct snd_soc_dapm_context *dapm = w->dapm;
- dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name);
- return hda_sst_event_handler(w, event, SSTH_WIDGET_MIXER);
+}
Lots of indirection and wrapping going on here which seems to make things more confusing. Can you try writing out the event handling directly and having it call common functions to perform the shared operations rather than having a single event handler with per type cases? Or alternatively use the DAPM widget type rather than your own ones.
- /* if FE - Playback, then parse sink list , Capture then source list
- if BE - Playback, then parse source list , Capture then sink list
- */
Coding style on comments too BTW.
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
w = dai->playback_widget;
(is_fe) ? (dir = 1) : (dir = 0);
This looks like a very complicated way of writing dir = is_fe.
On Fri, Apr 24, 2015 at 06:51:15PM +0100, Mark Brown wrote:
On Sun, Apr 19, 2015 at 02:27:31AM +0530, Vinod Koul wrote:
+static int is_hda_widget_type(struct snd_soc_dapm_widget *w) +{
- return ((w->id == snd_soc_dapm_dai_link) ||
(w->id == snd_soc_dapm_dai_in) ||
(w->id == snd_soc_dapm_aif_in) ||
(w->id == snd_soc_dapm_aif_out) ||
(w->id == snd_soc_dapm_dai_out)) ? 1 : 0;
Please use switch statements, and again no need to convert logic values into logic values.
OK
+static int hda_sst_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
- int w_type, struct ssth_lib *ctx, struct hda_platform_info *pinfo)
+{
- int ret = 0;
- struct ssth_module_config *mconfig = w->priv;
- struct ssth_pipe *s_pipe = mconfig->pipe;
- dev_dbg(ctx->dev, "%s: widget =%s type=%d\n", __func__, w->name, w_type);
- /*check resource available */
- if (!hda_sst_is_pipe_mcps_available(pinfo, ctx, mconfig))
return -1;
Proper error code please.
Sure
- if (w_type == SSTH_WIDGET_VMIXER ||
w_type == SSTH_WIDGET_MIXER) {
switch.
Since this is checking for these two types only, I think if maybe fine, but dont mind swicth too.
if (!hda_sst_is_pipe_mem_available(pinfo, ctx, mconfig))
return -ENOMEM;
ret = ssth_create_pipeline(ctx, mconfig->pipe);
if (ret < 0)
return ret;
if (list_empty(&s_pipe->w_list)) {
ret = hda_sst_get_pipe_widget(ctx->dev, w, s_pipe);
if (ret < 0)
return ret;
}
ret = hda_init_pipe_modules(ctx, s_pipe, pinfo);
if (ret < 0)
return ret;
The error handling here appears to be a bit incomplete, we don't unwind anything we did.
Yes let me check that.
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
return hda_sst_dapm_pre_pmu_event(w, w_type, ctx, pinfo);
- break;
Please follow the kernel coding style.
oops, will fix
+static int hda_sst_vmixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
+{
- struct snd_soc_dapm_context *dapm = w->dapm;
- dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name);
- return hda_sst_event_handler(w, event, SSTH_WIDGET_VMIXER);
+}
+static int hda_sst_mixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
+{
- struct snd_soc_dapm_context *dapm = w->dapm;
- dev_dbg(dapm->dev, "%s: widget = %s\n", __func__, w->name);
- return hda_sst_event_handler(w, event, SSTH_WIDGET_MIXER);
+}
Lots of indirection and wrapping going on here which seems to make things more confusing. Can you try writing out the event handling directly and having it call common functions to perform the shared operations rather than having a single event handler with per type cases? Or alternatively use the DAPM widget type rather than your own ones.
Okay let me cleanup this code is next series
- /* if FE - Playback, then parse sink list , Capture then source list
- if BE - Playback, then parse source list , Capture then sink list
- */
Coding style on comments too BTW.
will fix
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
w = dai->playback_widget;
(is_fe) ? (dir = 1) : (dir = 0);
This looks like a very complicated way of writing dir = is_fe.
Yes for sure :)
From: Jeeja KP jeeja.kp@intel.com
SSP registers needs to be updated for the SSP copier module. Add support to update the registers based on dai configuration.
Signed-off-by: Hardik T Shah hardik.t.shah@intel.com Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/hda_dsp_ssp_config.c | 285 ++++++++++++++++++++++++ sound/soc/hda/hda_dsp_ssp_config.h | 423 ++++++++++++++++++++++++++++++++++++ 2 files changed, 708 insertions(+) create mode 100644 sound/soc/hda/hda_dsp_ssp_config.c create mode 100644 sound/soc/hda/hda_dsp_ssp_config.h
diff --git a/sound/soc/hda/hda_dsp_ssp_config.c b/sound/soc/hda/hda_dsp_ssp_config.c new file mode 100644 index 000000000000..8b79777fb8be --- /dev/null +++ b/sound/soc/hda/hda_dsp_ssp_config.c @@ -0,0 +1,285 @@ +/* + * hda_dsp_ssp_config.c - HDA DSP SSP register configuration functionality + * + * Copyright (C) 2015 Intel Corp + * Author: Hardik Shah hardik.t.shah@intel.com + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include "hda_dsp_ssp_config.h" +#include "hda_skl.h" + +#define HDA_SSP_MAX_FREQ_192 19200000 + +struct hda_ssp_regs { + u32 hda_ssc0; + u32 hda_ssc1; + u32 hda_ssto; + u32 hda_sspsp; + u32 hda_sstsa; + u32 hda_ssrsa; + u32 hda_ssc2; + u32 hda_sspsp2; + u32 hda_ssc3; + u32 hda_ssioc; +}; + + +static void azx_set_default_ssp_regs_v1(struct hda_ssp_regs *regs) +{ + regs->hda_ssc0 = 0; + regs->hda_ssc0 = (HDA_SSC0_FRF_DEF_V1 << HDA_SSC0_FRF_SHIFT) | + (HDA_SSC0_ECS_DEF_V1 << HDA_SSC0_ECS_SHIFT) | + (HDA_SSC0_SSE_DEF_V1 << HDA_SSC0_SSE_SHIFT) | + (HDA_SSC0_NCS_DEF_V1 << HDA_SSC0_NCS_SHIFT) | + (HDA_SSC0_RIM_DEF_V1 << HDA_SSC0_RIM_SHIFT) | + (HDA_SSC0_ACS_DEF_V1 << HDA_SSC0_ACS_SHIFT) | + (HDA_SSC0_MOD_DEF_V1 << HDA_SSC0_MOD_SHIFT); + + regs->hda_ssc1 = 0; + regs->hda_ssc1 = (HDA_SSC1_RIE_DEF_V1 << HDA_SSC1_RIE_SHIFT) | + (HDA_SSC1_TIE_DEF_V1 << HDA_SSC1_TIE_SHIFT) | + (HDA_SSC1_LBM_DEF_V1 << HDA_SSC1_LBM_SHIFT) | + (HDA_SSC1_SPO_DEF_V1 << HDA_SSC1_SPO_SHIFT) | + (HDA_SSC1_SPH_DEF_V1 << HDA_SSC1_SPH_SHIFT) | + (HDA_SSC1_MWDS_DEF_V1 << HDA_SSC1_MWDS_SHIFT) | + (HDA_SSC1_EFWR_DEF_V1 << HDA_SSC1_EFWR_SHIFT) | + (HDA_SSC1_STRF_DEF_V1 << HDA_SSC1_STRF_SHIFT) | + (HDA_SSC1_IFS_DEF_V1 << HDA_SSC1_IFS_SHIFT) | + (HDA_SSC1_PINTE_DEF_V1 << HDA_SSC1_PINTE_SHIFT) | + (HDA_SSC1_TINTE_DEF_V1 << HDA_SSC1_TINTE_SHIFT) | + (HDA_SSC1_RSRE_DEF_V1 << HDA_SSC1_RSRE_SHIFT) | + (HDA_SSC1_TSRE_DEF_V1 << HDA_SSC1_TSRE_SHIFT) | + (HDA_SSC1_TRAIL_DEF_V1 << HDA_SSC1_TRAIL_SHIFT) | + (HDA_SSC1_RWOT_DEF_V1 << HDA_SSC1_RWOT_SHIFT) | + (HDA_SSC1_ECRB_DEF_V1 << HDA_SSC1_ECRB_SHIFT) | + (HDA_SSC1_ECRA_DEF_V1 << HDA_SSC1_ECRA_SHIFT) | + (HDA_SSC1_SCFR_DEF_V1 << HDA_SSC1_SCFR_SHIFT) | + (HDA_SSC1_EBCEI_DEF_V1 << HDA_SSC1_EBCEI_SHIFT) | + (HDA_SSC1_TTE_DEF_V1 << HDA_SSC1_TTE_SHIFT) | + (HDA_SSC1_TTELP_DEF_V1 << HDA_SSC1_TTELP_SHIFT); + + regs->hda_ssto = 0; + regs->hda_ssto = HDA_SST0_TIMEOUT_DEF_V1 << HDA_SST0_TIMEOUT_SHIFT; + + regs->hda_sspsp = 0; + regs->hda_sspsp = (HDA_SSPSP_ETDS_DEF_V1 << HDA_SSPSP_ETDS_SHIFT) | + (HDA_SSPSP_STRTDLY_DEF_V1 << HDA_SSPSP_STRTDLY_SHIFT) | + (HDA_SSPSP_DMYSTRT_DEF_V1 << HDA_SSPSP_DMYSTRT_SHIFT) | + (HDA_SSPSP_SFRMDLY_DEF_V1 << HDA_SSPSP_SFRMDLY_SHIFT) | + (HDA_SSPSP_DMYSTOP_DEF_V1 << HDA_SSPSP_DMYSTOP_SHIFT) | + (HDA_SSPSP_EDMYSTOP_DEF_V1 << HDA_SSPSP_EDMYSTOP_SHIFT); + + regs->hda_sstsa = 0; + regs->hda_sstsa = (HDA_SSTSA_TXEN_DEF_V1 << HDA_SSTSA_TXEN_SHIFT) | + (HDA_SSTSA_TTSA_DEF_V1 << HDA_SSTSA_TTSA_SHIFT); + + regs->hda_ssrsa = 0; + regs->hda_ssrsa = (HDA_SSRSA_RXEN_DEF_V1 << HDA_SSRSA_RXEN_SHIFT) | + (HDA_SSTSA_RTSA_DEF_V1 << HDA_SSRSA_RTSA_SHIFT); + + regs->hda_ssc2 = 0; + regs->hda_ssc2 = (HDA_SSC2_TURM0_DEF_V1 << HDA_SSC2_TURM0_SHIFT) | + (HDA_SSC2_TURM1_DEF_V1 << HDA_SSC2_TURM1_SHIFT) | + (HDA_SSC2_PSPMB2BFMFD_DEF_V1 << HDA_SSC2_PSPMB2BFMFD_SHIFT) | + (HDA_SSC2_PSPSRWFDFD_DEF_V1 << HDA_SSC2_PSPSRWFDFD_SHIFT) | + (HDA_SSC2_PSPSTWFDFD_DEF_V1 << HDA_SSC2_PSPSTWFDFD_SHIFT) | + (HDA_SSC2_PSPMFSRTPFD_DEF_V1 << HDA_SSC2_PSPMFSRTPFD_SHIFT) | + (HDA_SSC2_ROFD_DEF_V1 << HDA_SSC2_ROFD_SHIFT) | + (HDA_SSC2_C2DFFD_DEF_V1 << HDA_SSC2_C2DFFD_SHIFT) | + (HDA_SSC2_TSAFD_DEF_V1 << HDA_SSC2_TSAFD_SHIFT) | + (HDA_SSC2_RSAFD_DEF_V1 << HDA_SSC2_RSAFD_SHIFT) | + (HDA_SSC2_RMFGFD_DEF_V1 << HDA_SSC2_RMFGFD_SHIFT) | + (HDA_SSC2_TURFD_DEF_V1 << HDA_SSC2_TURFD_SHIFT) | + (HDA_SSC2_TUDFD_DEF_V1 << HDA_SSC2_TUDFD_SHIFT) | + (HDA_SSC2_BCEFD_DEF_V1 << HDA_SSC2_BCEFD_SHIFT) | + (HDA_SSC2_SDFD_DEF_V1 << HDA_SSC2_SDFD_SHIFT) | + (HDA_SSC2_SDHFD_DEF_V1 << HDA_SSC2_SDHFD_SHIFT) | + (HDA_SSC2_SDPM_DEF_V1 << HDA_SSC2_SDPM_SHIFT) | + (HDA_SSC2_LJDFD_DEF_V1 << HDA_SSC2_LJDFD_SHIFT) | + (HDA_SSC2_MMRATF_DEF_V1 << HDA_SSC2_MMRATF_SHIFT) | + (HDA_SSC2_SMTATF_DEF_V1 << HDA_SSC2_SMTATF_SHIFT); + + regs->hda_ssc3 = 0; + regs->hda_ssc3 = (HDA_SSC3_TFT_DEF_V1 << HDA_SSC3_TFT_SHIFT) | + (HDA_SSC3_RFT_DEF_V1 << HDA_SSC3_RFT_SHIFT); + + regs->hda_ssioc = 0; + regs->hda_ssioc = (HDA_SSIOC_RXDPDEB_DEF_V1 << HDA_SSIOC_RXDPDEB_SHIFT) | + (HDA_SSIOC_TXDPDEB_DEF_V1 << HDA_SSIOC_TXDPDEB_SHIFT) | + (HDA_SSIOC_SFRMPDEB_DEF_V1 << HDA_SSIOC_SFRMPDEB_SHIFT) | + (HDA_SSIOC_SCLKPDEB_DEF_V1 << HDA_SSIOC_SCLKPDEB_SHIFT) | + (HDA_SSIOC_SFCR_DEF_V1 << HDA_SSIOC_SFCR_SHIFT) | + (HDA_SSIOC_SCOE_DEF_V1 << HDA_SSIOC_SCOE_SHIFT); + + regs->hda_sspsp2 = 0; + +} + +static void azx_print_ssp_regs(struct sst_dsp_ctx *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); + dev_dbg(ctx->dev, "sst0\t\t %x\n", regs->hda_ssto); + dev_dbg(ctx->dev, "sspsp\t\t %x\n", regs->hda_sspsp); + dev_dbg(ctx->dev, "sstsa\t\t %x\n", regs->hda_sstsa); + dev_dbg(ctx->dev, "ssrsa\t\t %x\n", regs->hda_ssrsa); + dev_dbg(ctx->dev, "ssc2\t\t %x\n", regs->hda_ssc2); + dev_dbg(ctx->dev, "sspsp2\t\t %x\n", regs->hda_sspsp2); + dev_dbg(ctx->dev, "ssc3\t\t %x\n", regs->hda_ssc3); + 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, + int slots, int s_fmt, int *div, int *dummy_bits) +{ + int divisor, mod; + int found = 0; + int req_bclk; + int dummy = 0; + + dev_dbg(ctx->dev, "fs = %d, slots = %d s_fmt = %d\n", fs, slots, s_fmt); + req_bclk = fs * slots * s_fmt; + if (req_bclk == 0) { + dev_err(ctx->dev, "Required bit clock = 0\n"); + return -EINVAL; + } + if (req_bclk > HDA_SSP_MAX_FREQ_192) { + dev_err(ctx->dev, "Bit clock is %d, greater than max SSP freq\n", req_bclk); + return -EINVAL; + } + dummy = 0; + /* Find the lowest bit clock possible for particular configuration */ + do { + req_bclk = ((s_fmt * fs * slots) + (fs * dummy)); + mod = HDA_SSP_MAX_FREQ_192 % req_bclk; + divisor = HDA_SSP_MAX_FREQ_192 / req_bclk; + if (!mod) { + found = 1; + break; + } + /* Dont add odd number of dummy bits, since I2S requires + * dummy bit after each slot/channel + */ + dummy += 2; + if (dummy > (HDA_SSPSP2_FEP_MASK >> HDA_SSPSP2_FEP_SHIFT)) { + dev_err(ctx->dev, "Dummy bit greater than what SSP can support\n"); + return -EINVAL; + } + + } while (divisor > 0); + if (found) { + *div = divisor; + *dummy_bits = dummy; + return 0; + } + dev_err(ctx->dev, "Suitable divisor not found for SSP configuration\n"); + + return -EINVAL; +} +int azx_calculate_ssp_regs(struct sst_dsp_ctx *ctx, struct azx_dai_config *cfg, + void *ssp_regs) +{ + struct hda_ssp_regs regs; + int ret = 0; + int div, dummy; + int dss, edss; + int edmystop, dmystop; + struct azx_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"); + azx_print_ssp_regs(ctx, ®s); + + if (ssp_cfg->fs_slave) + regs.hda_ssc1 |= HDA_SSC1_SFRMDIR_MASK; + if (ssp_cfg->bclk_slave) + regs.hda_ssc1 |= HDA_SSC1_SCLKDIR_MASK; + if (!ssp_cfg->fs_invert) + regs.hda_sspsp |= ((0x1 << HDA_SSPSP_SFRMP_SHIFT) & + HDA_SSPSP_SFRMP_MASK); + if (ssp_cfg->bclk_invert) + regs.hda_sspsp |= ((0x1 << HDA_SSPSP_SCMODE_SHIFT) & + HDA_SSPSP_SCMODE_MASK); + + if (ssp_cfg->ssp_mode == HDA_SSP_MODE_I2S) + /* I2S mode, means 2 slots */ + ssp_cfg->slots = 2; + + ret = azx_find_ssp_clk_divisor(ctx, cfg->sampling_rate, ssp_cfg->slots, + cfg->s_fmt, &div, &dummy); + if (ret) { + dev_err(ctx->dev, "Clock disior not possible\n"); + return -EINVAL; + } + /* Program dividier */ + regs.hda_ssc0 |= ((div - 1) << HDA_SSC0_SRC_SHIFT) & HDA_SSC0_SCR_MASK; + + + if (ssp_cfg->slot_width != cfg->s_fmt) { + dev_err(ctx->dev, "slot_width must be equal to format bits\n"); + return -EINVAL; + } + + if (cfg->s_fmt > 16) { + edss = 1; + dss = cfg->s_fmt - 16; + } else { + edss = 0; + dss = cfg->s_fmt; + } + dss -= 1; + regs.hda_ssc0 |= ((dss << HDA_SSC0_DSS_SHIFT) & HDA_SSC0_DSS_MASK); + regs.hda_ssc0 |= ((edss << HDA_SSC0_EDSS_SHIFT) & HDA_SSC0_EDSS_MASK); + + if (ssp_cfg->ssp_mode != HDA_SSP_MODE_I2S) { + regs.hda_sspsp2 |= (dummy << HDA_SSPSP2_FEP_SHIFT) & + HDA_SSPSP2_FEP_MASK; + regs.hda_sspsp |= (0x1 << HDA_SSPSP_SFRMWDTH_SHIFT) & + HDA_SSPSP_SFRMWDTH_MASK; + if (ssp_cfg->ssp_mode == HDA_SSP_MODE_DSP_A) + regs.hda_sspsp |= (0x1 << HDA_SSPSP_FSRT_SHIFT) & + HDA_SSPSP_FSRT_MASK; + else + regs.hda_sspsp &= ~(HDA_SSPSP_FSRT_MASK); + } else { + /* Divide dummy bits / 2 */ + regs.hda_sspsp |= (0x1 << HDA_SSPSP_FSRT_SHIFT) & + HDA_SSPSP_FSRT_MASK; + dummy = dummy >> 1; + dmystop = dummy & 0x3; + edmystop = (dummy >> 2); + regs.hda_sspsp |= (dmystop << HDA_SSPSP_DMYSTOP_SHIFT) & + HDA_SSPSP_DMYSTOP_MASK; + regs.hda_sspsp |= (edmystop << HDA_SSPSP_EDMYSTOP_SHIFT) & + HDA_SSPSP_EDMYSTOP_MASK; + regs.hda_sspsp |= ((cfg->s_fmt + dummy) << HDA_SSPSP_SFRMWDTH_SHIFT) & + HDA_SSPSP_SFRMWDTH_MASK; + ssp_cfg->slots = 2; + } + if (ssp_cfg->slots > HDA_SSP_MAX_SLOTS) { + dev_err(ctx->dev, "Max slots = %d\n", HDA_SSP_MAX_SLOTS); + return -EINVAL; + } + regs.hda_ssc0 |= ((ssp_cfg->slots - 1) << HDA_SSC0_FRDC_SHIFT) & + HDA_SSC0_FRDC_MASK; + + memcpy(ssp_regs, ®s, sizeof(struct hda_ssp_regs)); + dev_dbg(ctx->dev, "Registers updated to\n"); + azx_print_ssp_regs(ctx, ®s); + return 0; + +} diff --git a/sound/soc/hda/hda_dsp_ssp_config.h b/sound/soc/hda/hda_dsp_ssp_config.h new file mode 100644 index 000000000000..3d056d2da1ca --- /dev/null +++ b/sound/soc/hda/hda_dsp_ssp_config.h @@ -0,0 +1,423 @@ +/* + * hda_dsp_ssp_config.h - HDA DSP SSP register definition file. + * + * Copyright (C) 2015 Intel Corp + * Author: Hardik Shah hardik.t.shah@intel.com + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + + +#ifndef _INTEL_SSP_CONFIG_H_ +#define _INTEL_SSP_CONFIG_H_ + +#define HDA_SSC0_DSS_MASK 0xF +#define HDA_SSC0_DSS_SHIFT 0 +#define HDA_SSC0_FRF_MASK 0x30 +#define HDA_SSC0_FRF_SHIFT 4 +#define HDA_SSC0_ECS_MASK 0x40 +#define HDA_SSC0_ECS_SHIFT 6 +#define HDA_SSC0_SSE_MASK 0x80 +#define HDA_SSC0_SSE_SHIFT 7 +#define HDA_SSC0_SCR_MASK 0xFFF00 +#define HDA_SSC0_SRC_SHIFT 8 +#define HDA_SSC0_EDSS_MASK 0x100000 +#define HDA_SSC0_EDSS_SHIFT 20 +#define HDA_SSC0_NCS_MASK 0x200000 +#define HDA_SSC0_NCS_SHIFT 21 +#define HDA_SSC0_RIM_MASK 0x400000 +#define HDA_SSC0_RIM_SHIFT 22 +#define HDA_SSC0_TIM_MASK 0x800000 +#define HDA_SSC0_TIM_SHIFT 23 +#define HDA_SSC0_FRDC_MASK 0x7000000 +#define HDA_SSC0_FRDC_SHIFT 24 +#define HDA_SSC0_ACS_MASK 0x40000000 +#define HDA_SSC0_ACS_SHIFT 30 +#define HDA_SSC0_MOD_MASK 0x80000000 +#define HDA_SSC0_MOD_SHIFT 31 + +#define HDA_SSC1_RIE_MASK 0x1 +#define HDA_SSC1_RIE_SHIFT 0 +#define HDA_SSC1_TIE_MASK 0x2 +#define HDA_SSC1_TIE_SHIFT 1 +#define HDA_SSC1_LBM_MASK 0x4 +#define HDA_SSC1_LBM_SHIFT 2 +#define HDA_SSC1_SPO_MASK 0x8 +#define HDA_SSC1_SPO_SHIFT 3 +#define HDA_SSC1_SPH_MASK 0x10 +#define HDA_SSC1_SPH_SHIFT 4 +#define HDA_SSC1_MWDS_MASK 0x20 +#define HDA_SSC1_MWDS_SHIFT 5 +#define HDA_SSC1_EFWR_MASK 0x4000 +#define HDA_SSC1_EFWR_SHIFT 14 +#define HDA_SSC1_STRF_MASK 0x8000 +#define HDA_SSC1_STRF_SHIFT 15 +#define HDA_SSC1_IFS_MASK 0x10000 +#define HDA_SSC1_IFS_SHIFT 16 +#define HDA_SSC1_PINTE_MASK 0x40000 +#define HDA_SSC1_PINTE_SHIFT 18 +#define HDA_SSC1_TINTE_MASK 0x80000 +#define HDA_SSC1_TINTE_SHIFT 19 +#define HDA_SSC1_RSRE_MASK 0x100000 +#define HDA_SSC1_RSRE_SHIFT 20 +#define HDA_SSC1_TSRE_MASK 0x200000 +#define HDA_SSC1_TSRE_SHIFT 21 +#define HDA_SSC1_TRAIL_MASK 0x400000 +#define HDA_SSC1_TRAIL_SHIFT 22 +#define HDA_SSC1_RWOT_MASK 0x800000 +#define HDA_SSC1_RWOT_SHIFT 23 +#define HDA_SSC1_SFRMDIR_MASK 0x1000000 +#define HDA_SSC1_SFRMDIR_SHIFT 24 +#define HDA_SSC1_SCLKDIR_MASK 0x2000000 +#define HDA_SSC1_SCLKDIR_SHIFT 25 +#define HDA_SSC1_ECRB_MASK 0x4000000 +#define HDA_SSC1_ECRB_SHIFT 26 +#define HDA_SSC1_ECRA_MASK 0x8000000 +#define HDA_SSC1_ECRA_SHIFT 27 +#define HDA_SSC1_SCFR_MASK 0x10000000 +#define HDA_SSC1_SCFR_SHIFT 28 +#define HDA_SSC1_EBCEI_MASK 0x20000000 +#define HDA_SSC1_EBCEI_SHIFT 29 +#define HDA_SSC1_TTE_MASK 0x40000000 +#define HDA_SSC1_TTE_SHIFT 30 +#define HDA_SSC1_TTELP_MASK 0x80000000 +#define HDA_SSC1_TTELP_SHIFT 31 + +#define HDA_SST0_TIMEOUT_MASK 0xFFFFFF +#define HDA_SST0_TIMEOUT_SHIFT 0 + +#define HDA_SSPSP_SCMODE_MASK 0x3 +#define HDA_SSPSP_SCMODE_SHIFT 0 +#define HDA_SSPSP_SFRMP_MASK 0x4 +#define HDA_SSPSP_SFRMP_SHIFT 2 +#define HDA_SSPSP_ETDS_MASK 0x8 +#define HDA_SSPSP_ETDS_SHIFT 3 +#define HDA_SSPSP_STRTDLY_MASK 0x70 +#define HDA_SSPSP_STRTDLY_SHIFT 4 +#define HDA_SSPSP_DMYSTRT_MASK 0x180 +#define HDA_SSPSP_DMYSTRT_SHIFT 7 +#define HDA_SSPSP_SFRMDLY_MASK 0xFE00 +#define HDA_SSPSP_SFRMDLY_SHIFT 9 +#define HDA_SSPSP_SFRMWDTH_MASK 0x3F0000 +#define HDA_SSPSP_SFRMWDTH_SHIFT 16 +#define HDA_SSPSP_DMYSTOP_MASK 0x1800000 +#define HDA_SSPSP_DMYSTOP_SHIFT 23 +#define HDA_SSPSP_FSRT_MASK 0x2000000 +#define HDA_SSPSP_FSRT_SHIFT 25 +#define HDA_SSPSP_EDMYSTOP_MASK 0x1C000000 +#define HDA_SSPSP_EDMYSTOP_SHIFT 26 + +#define HDA_SSTSA_TTSA_MASK 0xFF +#define HDA_SSTSA_TTSA_SHIFT 0 +#define HDA_SSTSA_TXEN_MASK 0x100 +#define HDA_SSTSA_TXEN_SHIFT 8 + +#define HDA_SSRSA_RTSA_MASK 0xFF +#define HDA_SSRSA_RTSA_SHIFT 0 +#define HDA_SSRSA_RXEN_MASK 0x100 +#define HDA_SSRSA_RXEN_SHIFT 8 + +#define HDA_SSC2_TURM0_MASK 0x1 +#define HDA_SSC2_TURM0_SHIFT 0 +#define HDA_SSC2_TURM1_MASK 0x2 +#define HDA_SSC2_TURM1_SHIFT 1 +#define HDA_SSC2_PSPMB2BFMFD_MASK 0x4 +#define HDA_SSC2_PSPMB2BFMFD_SHIFT 2 +#define HDA_SSC2_PSPSRWFDFD_MASK 0x8 +#define HDA_SSC2_PSPSRWFDFD_SHIFT 3 +#define HDA_SSC2_PSPSTWFDFD_MASK 0x10 +#define HDA_SSC2_PSPSTWFDFD_SHIFT 4 +#define HDA_SSC2_PSPMFSRTPFD_MASK 0x20 +#define HDA_SSC2_PSPMFSRTPFD_SHIFT 5 +#define HDA_SSC2_ROFD_MASK 0x40 +#define HDA_SSC2_ROFD_SHIFT 6 +#define HDA_SSC2_C2DFFD_MASK 0x80 +#define HDA_SSC2_C2DFFD_SHIFT 7 +#define HDA_SSC2_TSAFD_MASK 0x100 +#define HDA_SSC2_TSAFD_SHIFT 8 +#define HDA_SSC2_RSAFD_MASK 0x200 +#define HDA_SSC2_RSAFD_SHIFT 9 +#define HDA_SSC2_RMFGFD_MASK 0x400 +#define HDA_SSC2_RMFGFD_SHIFT 10 +#define HDA_SSC2_TURFD_MASK 0x800 +#define HDA_SSC2_TURFD_SHIFT 11 +#define HDA_SSC2_TUDFD_MASK 0x1000 +#define HDA_SSC2_TUDFD_SHIFT 12 +#define HDA_SSC2_BCEFD_MASK 0x2000 +#define HDA_SSC2_BCEFD_SHIFT 13 + +#define HDA_SSC2_SDFD_MASK 0x4000 +#define HDA_SSC2_SDFD_SHIFT 14 +#define HDA_SSC2_SDHFD_MASK 0x8000 +#define HDA_SSC2_SDHFD_SHIFT 15 +#define HDA_SSC2_SDPM_MASK 0x10000 +#define HDA_SSC2_SDPM_SHIFT 16 +#define HDA_SSC2_LJDFD_MASK 0x20000 +#define HDA_SSC2_LJDFD_SHIFT 17 +#define HDA_SSC2_MMRATF_MASK 0x40000 +#define HDA_SSC2_MMRATF_SHIFT 18 +#define HDA_SSC2_SMTATF_MASK 0x80000 +#define HDA_SSC2_SMTATF_SHIFT 19 + +#define HDA_SSPSP2_FEP_MASK 0xFF +#define HDA_SSPSP2_FEP_SHIFT 0 + +#define HDA_SSC3_TFL_MASK 0x3F +#define HDA_SSC3_TFL_SHIFT 0 +#define HDA_SSC3_RFL_MASK 0x3F00 +#define HDA_SSC3_RFL_SHIFT 8 +#define HDA_SSC3_TFT_MASK 0x3F0000 +#define HDA_SSC3_TFT_SHIFT 16 +#define HDA_SSC3_RFT_MASK 0x3F000000 +#define HDA_SSC3_RFT_SHIFT 24 + +#define HDA_SSIOC_RXDPDEB_MASK 0x1 +#define HDA_SSIOC_RXDPDEB_SHIFT 0 +#define HDA_SSIOC_TXDPDEB_MASK 0x2 +#define HDA_SSIOC_TXDPDEB_SHIFT 1 +#define HDA_SSIOC_SFRMPDEB_MASK 0x4 +#define HDA_SSIOC_SFRMPDEB_SHIFT 2 +#define HDA_SSIOC_SCLKPDEB_MASK 0x8 +#define HDA_SSIOC_SCLKPDEB_SHIFT 3 +#define HDA_SSIOC_SFCR_MASK 0x10 +#define HDA_SSIOC_SFCR_SHIFT 4 +#define HDA_SSIOC_SCOE_MASK 0x20 +#define HDA_SSIOC_SCOE_SHIFT 5 + + + + + + +/* Default Frame format is PSP */ +#define HDA_SSC0_FRF_DEF_V1 0x3 +/* On-chip clock used to derive SSP clock */ +#define HDA_SSC0_ECS_DEF_V1 0x0 +/* Modified by firmware */ +#define HDA_SSC0_SSE_DEF_V1 0x0 +/* Local clock use for bclk, instead of network clock */ +#define HDA_SSC0_NCS_DEF_V1 0x0 +/* Interrupt not required by firmware */ +#define HDA_SSC0_RIM_DEF_V1 0x0 +/* Interrupt not required by firmware */ +#define HDA_SSC0_TIM_DEF_V1 0x0 +/* Clock selectiion by ECS and NCS bit */ +#define HDA_SSC0_ACS_DEF_V1 0x0 +/* SSP in network mode. */ +#define HDA_SSC0_MOD_DEF_V1 0x1 + +/* Receive FIFO level interrupt is disabled, F/W int not req. */ +#define HDA_SSC1_RIE_DEF_V1 0x0 +/* Transmit FIFO level interrupt is disabled, F/W int not req. */ +#define HDA_SSC1_TIE_DEF_V1 0x0 +/* Normal serial port operation enable */ +#define HDA_SSC1_LBM_DEF_V1 0x0 +/* The inactive or idle state of SCLK is low */ +#define HDA_SSC1_SPO_DEF_V1 0x0 +/* SCLK is inactive one cycle at the start of a + * frame and 1/2 cycle at the end of a frame + */ +#define HDA_SSC1_SPH_DEF_V1 0x0 +/* 8 bit command words are transmitted */ +#define HDA_SSC1_MWDS_DEF_V1 0x0 +/* FIFO write / read special function is disabled + *(normal SSP operational mode) + */ +#define HDA_SSC1_EFWR_DEF_V1 0x0 +/* Transmit FIFO is selected for both writes and reads + * through the SSP Data register + */ +#define HDA_SSC1_STRF_DEF_V1 0x0 +/* Frame polarity is determined by SSP format and PSP polarity */ +#define HDA_SSC1_IFS_DEF_V1 0x0 +/* Peripheral Trailing Byte Interrupts are disabled, F/W int not req. */ +#define HDA_SSC1_PINTE_DEF_V1 0x0 +/* Receiver Time-out interrupts are disabled, F/W int not req. */ +#define HDA_SSC1_TINTE_DEF_V1 0x0 +/* Modified by firmware */ +#define HDA_SSC1_RSRE_DEF_V1 0x1 +/* Modified by firmware */ +#define HDA_SSC1_TSRE_DEF_V1 0x1 +/* DMA based, trailing bytes are handled by DMA. */ +#define HDA_SSC1_TRAIL_DEF_V1 0x1 +/* Transmit / Receive mode. */ +#define HDA_SSC1_RWOT_DEF_V1 0x0 +/* Clock request from other SSP is disabled */ +#define HDA_SSC1_ECRB_DEF_V1 0x0 +/* Clock request from other SSP is disabled */ +#define HDA_SSC1_ECRA_DEF_V1 0x0 +/* Clock input to SCLK is continuously running. (SSP slave mode) */ +#define HDA_SSC1_SCFR_DEF_V1 0x0 +/* Interrupt due to a bit count error is disabled, F/W int not req. */ +#define HDA_SSC1_EBCEI_DEF_V1 0x0 +/* TXD line will be tri-stated when not transmitting data */ +#define HDA_SSC1_TTE_DEF_V1 0x1 +/* TXD line will be tri-stated 1/2 clock edge after TXD is to be flopped */ +#define HDA_SSC1_TTELP_DEF_V1 0x1 + +/* Value defines the time-out interval given by TIMEOUT/peripheral + * clock frequency. + */ +#define HDA_SST0_TIMEOUT_DEF_V1 0x0 + +/* End of Transfer Data State 0: Low 1: Last value */ +#define HDA_SSPSP_ETDS_DEF_V1 0x0 +/* Programmed value sets start delay that is used to set the idle time + * of SCLK between transfers + */ +#define HDA_SSPSP_STRTDLY_DEF_V1 0x0 +/* Programmed value sets the number of SCLK after STRTDLY is complete + * that precede the transmit / receive data. + */ +#define HDA_SSPSP_DMYSTRT_DEF_V1 0x0 +/* Programmed value sets the number of half SCLK cycle from TXD / RXD + * being driven to SFRM being asserted + */ +#define HDA_SSPSP_SFRMDLY_DEF_V1 0x0 +/* With EDMYSTOP as higher order bits, the concatenated programmed value + * sets the number of SCLK cycles that follow the transmitted data. + * Concatenated dummy stop value of 0 . 31 is allowed. + */ +#define HDA_SSPSP_DMYSTOP_DEF_V1 0x0 +/* With EDMYSTOP as higher order bits, the concatenated programmed value + * sets the number of SCLK cycles that follow the transmitted data. + * Concatenated dummy stop value of 0 . 31 is allowed. + */ +#define HDA_SSPSP_EDMYSTOP_DEF_V1 0x0 + +/* Firmware controls this at correct time */ +#define HDA_SSTSA_TXEN_DEF_V1 0x0 +/* SSP TX Time Slot Active */ +#define HDA_SSTSA_TTSA_DEF_V1 0xf + +/* Firmware controls this at correct time */ +#define HDA_SSRSA_RXEN_DEF_V1 0x0 +/* SSP RX Time Slot Active */ +#define HDA_SSTSA_RTSA_DEF_V1 0xf + + +/* Mode 0 of transmit underrun fix. In this mode, new data will always + * start at slot 0. This function is only applied to SSP and PSP modes. + * 0: Disable mode 0. + * 1: Enable mode 0. + */ +#define HDA_SSC2_TURM0_DEF_V1 0x0 +/* Mode 1 of transmit underrun fix. In this mode, new data will always + * start on the underrun slot. This function is only applied to SSP + * and PSP modes. + * 0: Disable mode 1. + * 1: Enable mode 1. + * If both underrun modes are enabled, mode 1 has higher priority. + */ +#define HDA_SSC2_TURM1_DEF_V1 0x1 +/* Set to 1 to disable the fix for PSP master mode back to back frame + * assertion masking in network mode. + */ +#define HDA_SSC2_PSPMB2BFMFD_DEF_V1 0x0 +/* Set to 1 to disable the fix for PSP slave mode RXD wait for frame + * de-assertion before starting the second channel. + */ +#define HDA_SSC2_PSPSRWFDFD_DEF_V1 0x0 +/* Set to 1 to disable the fix for PSP slave mode TXD wait for frame + * de-assertion before starting the second channel. + */ +#define HDA_SSC2_PSPSTWFDFD_DEF_V1 0x0 +/* Set to 1 to disable the fix for PSP master mode FSRT with dummy + * stop & frame end padding capability. + */ +#define HDA_SSC2_PSPMFSRTPFD_DEF_V1 0x0 +/* When set to 1, new data will always start on the overflow slot when + * RX FIFO becomes free again. When cleared to 0, new data may start + * on any slot when RX FIFO becomes free again. + * This function is only applied to SSP and PSP modes. + */ +#define HDA_SSC2_ROFD_DEF_V1 0x0 +/* Set to 1 to disable the fix for combi -> double flop clock crossing. */ +#define HDA_SSC2_C2DFFD_DEF_V1 0x0 +/* When cleared to 0, TX slot defined in SSTSA.TTSA is only enabled in the + * next frame after TX Enable (TXEN) bit set, with the enabled slot. + * When set to 1, TX Enable (TXEN) bit has no effect. + */ +#define HDA_SSC2_TSAFD_DEF_V1 0x0 +/* When cleared to 0, RX slot defined in SSRSA.RTSA is only enabled in the + * next frame after RX Enable (RXEN) bit set. When set to 1, + * RX Enable (RXEN) bit has no effect. + */ +#define HDA_SSC2_RSAFD_DEF_V1 0x0 +/* When cleared to 0, SSP starts driving SFRM in master mode if RXEN + * is set, to allow RX only half duplex operation in master mode. + * When set to 1, RX Enable (RXEN) bit has no effect on the master mode + * SFRM generation. + */ +#define HDA_SSC2_RMFGFD_DEF_V1 0x0 +/* When cleared to 0, SSP will report an underrun if new data is written + * to an empty TX FIFO within 2 link clocks from the transmission of + * 1st data bit. Set to 1 to disable fix. + */ +#define HDA_SSC2_TURFD_DEF_V1 0x0 +/* When cleared to 0, TX data will be masked to 0 when SSP underrun in the + * case of new data written to empty TX FIFO within 2 link clocks from the + * transmission of 1st data bit. Set to 1 to disable fix. + */ +#define HDA_SSC2_TUDFD_DEF_V1 0x0 +/* Set to 1 to disable fix for bit count error bug at the beginning of + * SSP enabling. + */ +#define HDA_SSC2_BCEFD_DEF_V1 0x0 +/* Disable DMA finish function for SSP. */ +#define HDA_SSC2_SDFD_DEF_V1 0x1 +/* For DMA HW Handshake fix. + * 1 . use original DMA request logic + * 0 . enable HW HS + */ +#define HDA_SSC2_SDHFD_DEF_V1 0x0 +/* When 1, SSP can be accessed by PIO else DMAC. */ +#define HDA_SSC2_SDPM_DEF_V1 0x0 +/* Set to 1 to disable the fix for left justified I2S/PCM. */ +#define HDA_SSC2_LJDFD_DEF_V1 0x0 +/* Set to 1 for receive data to be sampled at the opposite clock edge + * specified in SSPSP.SCMODE[1:0] + */ +#define HDA_SSC2_MMRATF_DEF_V1 0x0 +/* Set to 1 for transmit data to be driven at the opposite clock edge + * specified in SSPSP.SCMODE[1:0] + */ +#define HDA_SSC2_SMTATF_DEF_V1 0x0 + + +/* Sets threshold level at which transmit FIFO asserts interrupt. + * This level should be set to the desired threshold value minus 1. + */ +#define HDA_SSC3_TFT_DEF_V1 0x7 +/* Sets threshold level at which receive FIFO asserts interrupt. + * This level should be set to the desired threshold value minus 1. + */ +#define HDA_SSC3_RFT_DEF_V1 0x7 + + +/* Clear to 0 to enable pull down on the SSPxRXD I/O buffer. */ +#define HDA_SSIOC_RXDPDEB_DEF_V1 0x0 +/* Clear to 0 to enable pull down on the SSPxTXD I/O buffer. */ +#define HDA_SSIOC_TXDPDEB_DEF_V1 0x0 +/* Clear to 0 to enable pull down on the SSPxSFRM I/O buffer. */ +#define HDA_SSIOC_SFRMPDEB_DEF_V1 0x0 +/* Clear to 0 to enable pull down on the SSPxSCLK I/O buffer. */ +#define HDA_SSIOC_SCLKPDEB_DEF_V1 0x0 +/* External Enable to force SSPSCLK to keep running. */ +#define HDA_SSIOC_SFCR_DEF_V1 0x0 +/* Enable the SSP SCLK I/O buffer output */ +#define HDA_SSIOC_SCOE_DEF_V1 0x1 + + +#endif /*_SOC_HDA_SSP_CONFIG_H_*/
On Sun, Apr 19, 2015 at 02:27:32AM +0530, Vinod Koul wrote:
- req_bclk = fs * slots * s_fmt;
We have a helper for this.
- dummy = 0;
- /* Find the lowest bit clock possible for particular configuration */
- do {
req_bclk = ((s_fmt * fs * slots) + (fs * dummy));
mod = HDA_SSP_MAX_FREQ_192 % req_bclk;
divisor = HDA_SSP_MAX_FREQ_192 / req_bclk;
if (!mod) {
found = 1;
break;
}
/* Dont add odd number of dummy bits, since I2S requires
* dummy bit after each slot/channel
*/
It does?
dummy += 2;
if (dummy > (HDA_SSPSP2_FEP_MASK >> HDA_SSPSP2_FEP_SHIFT)) {
dev_err(ctx->dev, "Dummy bit greater than what SSP can support\n");
return -EINVAL;
}
- } while (divisor > 0);
Aside from the limits this all looks like it could be generic?
On Fri, Apr 24, 2015 at 06:55:32PM +0100, Mark Brown wrote:
On Sun, Apr 19, 2015 at 02:27:32AM +0530, Vinod Koul wrote:
- req_bclk = fs * slots * s_fmt;
We have a helper for this.
will use
- dummy = 0;
- /* Find the lowest bit clock possible for particular configuration */
- do {
req_bclk = ((s_fmt * fs * slots) + (fs * dummy));
mod = HDA_SSP_MAX_FREQ_192 % req_bclk;
divisor = HDA_SSP_MAX_FREQ_192 / req_bclk;
if (!mod) {
found = 1;
break;
}
/* Dont add odd number of dummy bits, since I2S requires
* dummy bit after each slot/channel
*/
It does?
For us unfortuntely yes. We send 24 bit audio to codec and clock divider doesn't give us 48clocks per frame, so we have to add dummy clocks in each slot and send 25 clocks per slot
dummy += 2;
if (dummy > (HDA_SSPSP2_FEP_MASK >> HDA_SSPSP2_FEP_SHIFT)) {
dev_err(ctx->dev, "Dummy bit greater than what SSP can support\n");
return -EINVAL;
}
- } while (divisor > 0);
Aside from the limits this all looks like it could be generic?
sure will try to do that.
Thanks
On Sun, Apr 26, 2015 at 07:48:31PM +0530, Vinod Koul wrote:
On Fri, Apr 24, 2015 at 06:55:32PM +0100, Mark Brown wrote:
/* Dont add odd number of dummy bits, since I2S requires
* dummy bit after each slot/channel
*/
It does?
For us unfortuntely yes. We send 24 bit audio to codec and clock divider doesn't give us 48clocks per frame, so we have to add dummy clocks in each slot and send 25 clocks per slot
So it's the hardware rather than I2S itself :)
On 4/27/15 9:15 AM, Mark Brown wrote:
On Sun, Apr 26, 2015 at 07:48:31PM +0530, Vinod Koul wrote:
On Fri, Apr 24, 2015 at 06:55:32PM +0100, Mark Brown wrote:
/* Dont add odd number of dummy bits, since I2S requires
* dummy bit after each slot/channel
*/
It does?
For us unfortuntely yes. We send 24 bit audio to codec and clock divider doesn't give us 48clocks per frame, so we have to add dummy clocks in each slot and send 25 clocks per slot
So it's the hardware rather than I2S itself :)
It depends on the clock reference used to drive the SSP. With a 19.2 reference we typically divide by 50 and pad with a trailing bit.
That said I am not sure how this code would work on SKL. Vinod, isn't this for BXT only? how do you get 19.2 on SKL, shouldn't you guys use a 24 MHz root frequency to find the divider?
/* Dont add odd number of dummy bits, since I2S requires
* dummy bit after each slot/channel
*/
It does?
For us unfortuntely yes. We send 24 bit audio to codec and clock divider doesn't give us 48clocks per frame, so we have to add dummy clocks in each slot and send 25 clocks per slot
So it's the hardware rather than I2S itself :)
It depends on the clock reference used to drive the SSP. With a 19.2 reference we typically divide by 50 and pad with a trailing bit.
That said I am not sure how this code would work on SKL. Vinod, isn't this for BXT only? how do you get 19.2 on SKL, shouldn't you guys use a 24 MHz root frequency to find the divider?
And regardless you should make sure that the actual blck does not exceed the maximum serial bit-rate supported by the SOC (AC timing).
On Wed, Apr 29, 2015 at 06:50:14PM -0500, Pierre-Louis Bossart wrote:
/* Dont add odd number of dummy bits, since I2S requires
* dummy bit after each slot/channel
*/
It does?
For us unfortuntely yes. We send 24 bit audio to codec and clock divider doesn't give us 48clocks per frame, so we have to add dummy clocks in each slot and send 25 clocks per slot
So it's the hardware rather than I2S itself :)
It depends on the clock reference used to drive the SSP. With a 19.2 reference we typically divide by 50 and pad with a trailing bit.
That said I am not sure how this code would work on SKL. Vinod, isn't this for BXT only? how do you get 19.2 on SKL, shouldn't you guys use a 24 MHz root frequency to find the divider?
And regardless you should make sure that the actual blck does not exceed the maximum serial bit-rate supported by the SOC (AC timing).
Yes botha re valid points. But I do rember one of the platforms has 10.2 and another has 25, so we need to be agnostic here and do compare, or use ACPI blobs :)
On Thu, Apr 30, 2015 at 10:09:41AM +0530, Vinod Koul wrote:
On Wed, Apr 29, 2015 at 06:50:14PM -0500, Pierre-Louis Bossart wrote:
That said I am not sure how this code would work on SKL. Vinod, isn't this for BXT only? how do you get 19.2 on SKL, shouldn't you guys use a 24 MHz root frequency to find the divider?
And regardless you should make sure that the actual blck does not exceed the maximum serial bit-rate supported by the SOC (AC timing).
Yes botha re valid points. But I do rember one of the platforms has 10.2 and another has 25, so we need to be agnostic here and do compare, or use ACPI blobs :)
If this is under the control of the system integrators then I'd suggest you're going to see the configuration being used.
On 4/30/15 9:36 AM, Mark Brown wrote:
On Thu, Apr 30, 2015 at 10:09:41AM +0530, Vinod Koul wrote:
On Wed, Apr 29, 2015 at 06:50:14PM -0500, Pierre-Louis Bossart wrote:
That said I am not sure how this code would work on SKL. Vinod, isn't this for BXT only? how do you get 19.2 on SKL, shouldn't you guys use a 24 MHz root frequency to find the divider?
And regardless you should make sure that the actual blck does not exceed the maximum serial bit-rate supported by the SOC (AC timing).
Yes botha re valid points. But I do rember one of the platforms has 10.2 and another has 25, so we need to be agnostic here and do compare, or use ACPI blobs :)
If this is under the control of the system integrators then I'd suggest you're going to see the configuration being used.
It's an SOC/chipset parameter that can't be changed, not sure why it would come from ACPI - it's really frozen in silicon. The code can remain generic but needs to have an initialization for the root frequency that depends on the SOC/chipset. Rethinking on the bclock check, I withdraw my comment, it's impractical since there can be all sorts of 'optimizations' or limitations that can't be modeled here in this generic code. This check should be done elsewhere with additional platform information.
From: Jeeja KP jeeja.kp@intel.com
DSP widgets configuration needs to be changed based on hw_params or hw_params_fixup. Add support where dai configuration is store in private data and apply to widget based on fixup.
Signed-off-by: Hardik T Shah hardik.t.shah@intel.com Signed-off-by: Jeeja KP jeeja.kp@intel.com Signed-off-by: Vinod Koul vinod.koul@intel.com --- sound/soc/hda/Makefile | 2 +- sound/soc/hda/hda_dsp_controls.c | 213 +++++++++++++++++++++++++++++++++++- sound/soc/hda/hda_dsp_ssp_config.c | 15 ++- sound/soc/hda/hda_skl.h | 75 +++++++++++++ 4 files changed, 298 insertions(+), 7 deletions(-)
diff --git a/sound/soc/hda/Makefile b/sound/soc/hda/Makefile index 668c831b196e..c8be01816157 100644 --- a/sound/soc/hda/Makefile +++ b/sound/soc/hda/Makefile @@ -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
diff --git a/sound/soc/hda/hda_dsp_controls.c b/sound/soc/hda/hda_dsp_controls.c index ee53227fa751..3c23fdc3e14b 100644 --- a/sound/soc/hda/hda_dsp_controls.c +++ b/sound/soc/hda/hda_dsp_controls.c @@ -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*/ diff --git a/sound/soc/hda/hda_dsp_ssp_config.c b/sound/soc/hda/hda_dsp_ssp_config.c index 8b79777fb8be..aa9d5542dc59 100644 --- a/sound/soc/hda/hda_dsp_ssp_config.c +++ b/sound/soc/hda/hda_dsp_ssp_config.c @@ -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"); diff --git a/sound/soc/hda/hda_skl.h b/sound/soc/hda/hda_skl.h index f6d9c629a8e3..fd09bb855af6 100644 --- a/sound/soc/hda/hda_skl.h +++ b/sound/soc/hda/hda_skl.h @@ -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 */
participants (3)
-
Mark Brown
-
Pierre-Louis Bossart
-
Vinod Koul