From 0aeb0deb2fe5aae0ba884f7508f778601076adbc Mon Sep 17 00:00:00 2001 From: Slawomir Blauciak Date: Thu, 30 Jan 2020 14:09:58 +0100 Subject: [PATCH 1/2] dai: add ability to send multiple dai configs in a single ipc This change extends the existing sof_ipc_dai_config IPC to add an array of DAI-specific configurations, as opposed to having only one. Signed-off-by: Slawomir Blauciak --- src/audio/dai.c | 21 ++--- src/drivers/imx/esai.c | 11 +-- src/drivers/imx/sai.c | 9 ++- src/drivers/intel/baytrail/ssp.c | 66 +++++++++------- src/drivers/intel/cavs/alh.c | 7 +- src/drivers/intel/cavs/dmic.c | 22 +++--- src/drivers/intel/cavs/hda.c | 2 +- src/drivers/intel/cavs/ssp.c | 124 +++++++++++++++++------------- src/drivers/intel/haswell/ssp.c | 44 ++++++----- src/include/ipc/dai.h | 29 ++++--- src/include/sof/audio/component.h | 7 +- src/include/sof/drivers/ipc.h | 3 +- src/include/sof/drivers/ssp.h | 1 - src/include/sof/lib/dai.h | 8 +- src/ipc/handler.c | 7 +- src/ipc/ipc.c | 10 ++- 16 files changed, 210 insertions(+), 161 deletions(-) diff --git a/src/audio/dai.c b/src/audio/dai.c index 35def51f7956..15c713a10f66 100644 --- a/src/audio/dai.c +++ b/src/audio/dai.c @@ -668,7 +668,8 @@ static int dai_position(struct comp_dev *dev, struct sof_ipc_stream_posn *posn) return 0; } -static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) +static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config, + int config_idx) { struct sof_ipc_comp_config *dconfig = COMP_GET_CONFIG(dev); struct dai_data *dd = comp_get_drvdata(dev); @@ -677,7 +678,7 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) int handshake; trace_dai_comp_with_ids(dev, "dai_config() dai %d.%d", - config->type, config->dai_index); + config->params.type, config->params.dai_index); /* cannot configure DAI while active */ if (dev->state == COMP_STATE_ACTIVE) { @@ -685,10 +686,10 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) return -EINVAL; } - switch (config->type) { + switch (config->params.type) { case SOF_DAI_INTEL_SSP: /* set dma burst elems to slot number */ - dd->config.burst_elems = config->ssp.tdm_slots; + dd->config.burst_elems = config->ssp[config_idx].tdm_slots; break; case SOF_DAI_INTEL_DMIC: tracev_dai_comp_with_ids(dev, "dai_config(), config->type = SOF_DAI_INTEL_DMIC"); @@ -697,11 +698,11 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) dd->config.burst_elems = 8; trace_dai_comp_with_ids(dev, "dai_config(), config->dmic.fifo_bits = %u config->dmic.num_pdm_active = %u", - config->dmic.fifo_bits, - config->dmic.num_pdm_active); + config->dmic[config_idx].fifo_bits, + config->dmic[config_idx].num_pdm_active); break; case SOF_DAI_INTEL_HDA: - channel = config->hda.link_dma_ch; + channel = config->hda[config_idx].link_dma_ch; trace_dai_comp_with_ids(dev, "dai_config(), channel = %d", channel); @@ -729,8 +730,8 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) /* As with HDA, the DMA channel is assigned in runtime, * not during topology parsing. */ - channel = config->alh.stream_id; - dd->stream_id = config->alh.stream_id; + channel = config->alh[config_idx].stream_id; + dd->stream_id = config->alh[config_idx].stream_id; trace_dai_comp_with_ids(dev, "dai_config(), channel = %d", channel); break; @@ -778,7 +779,7 @@ static int dai_config(struct comp_dev *dev, struct sof_ipc_dai_config *config) dai_dma_cb); } - return dai_set_config(dd->dai, config); + return dai_set_config(dd->dai, config, config_idx); } static int dai_ts_config(struct comp_dev *dev) diff --git a/src/drivers/imx/esai.c b/src/drivers/imx/esai.c index ff7a87e51b8c..7f563a45315d 100644 --- a/src/drivers/imx/esai.c +++ b/src/drivers/imx/esai.c @@ -89,13 +89,14 @@ static int esai_context_restore(struct dai *dai) } static inline int esai_set_config(struct dai *dai, - struct sof_ipc_dai_config *config) + struct sof_ipc_dai_config *config, + int config_idx) { uint32_t xcr = 0, xccr = 0, mask; tracev_esai("ESAI: set_config format 0x%04x", - config->format); - switch (config->format & SOF_DAI_FMT_FORMAT_MASK) { + config->params.format); + switch (config->params.format & SOF_DAI_FMT_FORMAT_MASK) { case SOF_DAI_FMT_I2S: /* Data on rising edge of bclk, frame low, 1clk before * data @@ -134,7 +135,7 @@ static inline int esai_set_config(struct dai *dai, return -EINVAL; } - switch (config->format & SOF_DAI_FMT_INV_MASK) { + switch (config->params.format & SOF_DAI_FMT_INV_MASK) { case SOF_DAI_FMT_NB_NF: /* Nothing to do for both normal cases */ break; @@ -155,7 +156,7 @@ static inline int esai_set_config(struct dai *dai, return -EINVAL; } - switch (config->format & SOF_DAI_FMT_MASTER_MASK) { + switch (config->params.format & SOF_DAI_FMT_MASTER_MASK) { case SOF_DAI_FMT_CBM_CFM: /* Nothing to do in the registers */ break; diff --git a/src/drivers/imx/sai.c b/src/drivers/imx/sai.c index f07d08721846..7108ac1262f8 100644 --- a/src/drivers/imx/sai.c +++ b/src/drivers/imx/sai.c @@ -95,7 +95,8 @@ static int sai_context_restore(struct dai *dai) } static inline int sai_set_config(struct dai *dai, - struct sof_ipc_dai_config *config) + struct sof_ipc_dai_config *config, + int config_idx) { trace_sai("SAI: sai_set_config"); uint32_t val_cr2 = 0, val_cr4 = 0, val_cr5 = 0; @@ -103,7 +104,7 @@ static inline int sai_set_config(struct dai *dai, /* TODO: this value will be provided by config */ uint32_t sywd = 32; - switch (config->format & SOF_DAI_FMT_FORMAT_MASK) { + switch (config->params.format & SOF_DAI_FMT_FORMAT_MASK) { case SOF_DAI_FMT_I2S: /* * Frame low, 1clk before data, one word length for frame sync, @@ -162,7 +163,7 @@ static inline int sai_set_config(struct dai *dai, } /* DAI clock inversion */ - switch (config->format & SOF_DAI_FMT_INV_MASK) { + switch (config->params.format & SOF_DAI_FMT_INV_MASK) { case SOF_DAI_FMT_IB_IF: /* Invert both clocks */ val_cr2 ^= REG_SAI_CR2_BCP; @@ -184,7 +185,7 @@ static inline int sai_set_config(struct dai *dai, } /* DAI clock master masks */ - switch (config->format & SOF_DAI_FMT_MASTER_MASK) { + switch (config->params.format & SOF_DAI_FMT_MASTER_MASK) { case SOF_DAI_FMT_CBS_CFS: trace_sai("SAI: codec is slave"); val_cr2 |= REG_SAI_CR2_MSEL_MCLK1; diff --git a/src/drivers/intel/baytrail/ssp.c b/src/drivers/intel/baytrail/ssp.c index 72498e02c69f..b9693d48158a 100644 --- a/src/drivers/intel/baytrail/ssp.c +++ b/src/drivers/intel/baytrail/ssp.c @@ -72,7 +72,7 @@ static int ssp_context_restore(struct dai *dai) /* Digital Audio interface formatting */ static int ssp_set_config(struct dai *dai, - struct sof_ipc_dai_config *config) + struct sof_ipc_dai_config *config, int config_idx) { struct ssp_pdata *ssp = dai_get_drvdata(dai); uint32_t sscr0; @@ -106,7 +106,8 @@ static int ssp_set_config(struct dai *dai, goto out; } - trace_ssp("ssp_set_config(), config->format = %d", config->format); + trace_ssp("ssp_set_config(), config->format = %d", + config->params.format); /* reset SSP settings */ /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */ @@ -164,8 +165,7 @@ static int ssp_set_config(struct dai *dai, /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */ sspsp = SSPSP_ETDS; /* last value (bit 0) */ - ssp->config = *config; - ssp->params = config->ssp; + ssp->params = config->ssp[config_idx]; /* clock masters */ /* @@ -174,7 +174,7 @@ static int ssp_set_config(struct dai *dai, */ sscr1 |= SSCR1_SFRMDIR; - switch (config->format & SOF_DAI_FMT_MASTER_MASK) { + switch (config->params.format & SOF_DAI_FMT_MASTER_MASK) { case SOF_DAI_FMT_CBM_CFM: sscr0 |= SSCR0_ECS; /* external clock used */ sscr1 |= SSCR1_SCLKDIR; @@ -219,7 +219,7 @@ static int ssp_set_config(struct dai *dai, } /* clock signal polarity */ - switch (config->format & SOF_DAI_FMT_INV_MASK) { + switch (config->params.format & SOF_DAI_FMT_INV_MASK) { case SOF_DAI_FMT_NB_NF: break; case SOF_DAI_FMT_NB_IF: @@ -281,7 +281,8 @@ static int ssp_set_config(struct dai *dai, } /* BCLK is generated from MCLK - must be divisable */ - if (config->ssp.mclk_rate % config->ssp.bclk_rate) { + if (config->ssp[config_idx].mclk_rate % + config->ssp[config_idx].bclk_rate) { trace_ssp_error("ssp_set_config() error: " "MCLK is not divisable"); ret = -EINVAL; @@ -289,7 +290,8 @@ static int ssp_set_config(struct dai *dai, } /* divisor must be within SCR range */ - mdiv = (config->ssp.mclk_rate / config->ssp.bclk_rate) - 1; + mdiv = (config->ssp[config_idx].mclk_rate / + config->ssp[config_idx].bclk_rate) - 1; if (mdiv > (SSCR0_SCR_MASK >> 8)) { trace_ssp_error("ssp_set_config() error: " "divisor is not within SCR range"); @@ -301,7 +303,8 @@ static int ssp_set_config(struct dai *dai, sscr0 |= SSCR0_SCR(mdiv); /* calc frame width based on BCLK and rate - must be divisable */ - if (config->ssp.bclk_rate % config->ssp.fsync_rate) { + if (config->ssp[config_idx].bclk_rate % + config->ssp[config_idx].fsync_rate) { trace_ssp_error("ssp_set_config() error: " "BLCK is not divisable"); ret = -EINVAL; @@ -309,23 +312,24 @@ static int ssp_set_config(struct dai *dai, } /* must be enouch BCLKs for data */ - bdiv = config->ssp.bclk_rate / config->ssp.fsync_rate; - if (bdiv < config->ssp.tdm_slot_width * - config->ssp.tdm_slots) { + bdiv = config->ssp[config_idx].bclk_rate / + config->ssp[config_idx].fsync_rate; + if (bdiv < config->ssp[config_idx].tdm_slot_width * + config->ssp[config_idx].tdm_slots) { trace_ssp_error("ssp_set_config() error: not enough BCLKs"); ret = -EINVAL; goto out; } /* tdm_slot_width must be <= 38 for SSP */ - if (config->ssp.tdm_slot_width > 38) { + if (config->ssp[config_idx].tdm_slot_width > 38) { trace_ssp_error("ssp_set_config() error: tdm_slot_width > 38"); ret = -EINVAL; goto out; } /* format */ - switch (config->format & SOF_DAI_FMT_FORMAT_MASK) { + switch (config->params.format & SOF_DAI_FMT_FORMAT_MASK) { case SOF_DAI_FMT_I2S: start_delay = 1; @@ -334,7 +338,7 @@ static int ssp_set_config(struct dai *dai, sscr3 |= SSCR3_I2S_MODE_EN | SSCR3_I2S_TX_EN | SSCR3_I2S_RX_EN; /* set asserted frame length */ - frame_len = config->ssp.tdm_slot_width; + frame_len = config->ssp[config_idx].tdm_slot_width; /* handle frame polarity, I2S default is falling/active low */ sspsp |= SSPSP_SFRMP(!inverted_frame); @@ -364,7 +368,7 @@ static int ssp_set_config(struct dai *dai, sscr3 |= SSCR3_I2S_MODE_EN | SSCR3_I2S_TX_EN | SSCR3_I2S_RX_EN; /* set asserted frame length */ - frame_len = config->ssp.tdm_slot_width; + frame_len = config->ssp[config_idx].tdm_slot_width; /* LEFT_J default is rising/active high, opposite of I2S */ sspsp |= SSPSP_SFRMP(inverted_frame); @@ -389,7 +393,8 @@ static int ssp_set_config(struct dai *dai, start_delay = 1; - sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->ssp.tdm_slots); + sscr0 |= SSCR0_MOD | + SSCR0_FRDC(config->ssp[config_idx].tdm_slots); /* set asserted frame length */ frame_len = 1; @@ -406,18 +411,20 @@ static int ssp_set_config(struct dai *dai, * deasserted time of frame) */ if (cbs) - sscr4 |= SSCR4_TOT_FRM_PRD(config->ssp.tdm_slots * - config->ssp.tdm_slot_width); + sscr4 |= SSCR4_TOT_FRM_PRD( + config->ssp[config_idx].tdm_slots * + config->ssp[config_idx].tdm_slot_width); - active_tx_slots = popcount(config->ssp.tx_slots); - active_rx_slots = popcount(config->ssp.rx_slots); + active_tx_slots = popcount(config->ssp[config_idx].tx_slots); + active_rx_slots = popcount(config->ssp[config_idx].rx_slots); break; case SOF_DAI_FMT_DSP_B: start_delay = 0; - sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->ssp.tdm_slots); + sscr0 |= SSCR0_MOD | + SSCR0_FRDC(config->ssp[config_idx].tdm_slots); /* set asserted frame length */ frame_len = 1; @@ -434,11 +441,12 @@ static int ssp_set_config(struct dai *dai, * deasserted time of frame */ if (cbs) - sscr4 |= SSCR4_TOT_FRM_PRD(config->ssp.tdm_slots * - config->ssp.tdm_slot_width); + sscr4 |= SSCR4_TOT_FRM_PRD( + config->ssp[config_idx].tdm_slots * + config->ssp[config_idx].tdm_slot_width); - active_tx_slots = popcount(config->ssp.tx_slots); - active_rx_slots = popcount(config->ssp.rx_slots); + active_tx_slots = popcount(config->ssp[config_idx].tx_slots); + active_rx_slots = popcount(config->ssp[config_idx].rx_slots); break; default: @@ -452,7 +460,7 @@ static int ssp_set_config(struct dai *dai, sspsp |= SSPSP_SFRMWDTH(frame_len); sscr5 |= SSCR5_FRM_ASRT_CLOCKS(frame_len); - data_size = config->ssp.sample_valid_bits; + data_size = config->ssp[config_idx].sample_valid_bits; if (data_size > 16) sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16)); @@ -473,8 +481,8 @@ static int ssp_set_config(struct dai *dai, ssp_write(dai, SSCR5, sscr5); ssp_write(dai, SSPSP, sspsp); ssp_write(dai, SFIFOTT, sfifott); - ssp_write(dai, SSTSA, config->ssp.tx_slots); - ssp_write(dai, SSRSA, config->ssp.rx_slots); + ssp_write(dai, SSTSA, config->ssp[config_idx].tx_slots); + ssp_write(dai, SSRSA, config->ssp[config_idx].rx_slots); ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE; ssp->state[DAI_DIR_CAPTURE] = COMP_STATE_PREPARE; diff --git a/src/drivers/intel/cavs/alh.c b/src/drivers/intel/cavs/alh.c index ea127cccef47..f48bced6e252 100644 --- a/src/drivers/intel/cavs/alh.c +++ b/src/drivers/intel/cavs/alh.c @@ -27,10 +27,11 @@ static int alh_trigger(struct dai *dai, int cmd, int direction) return 0; } -static int alh_set_config(struct dai *dai, struct sof_ipc_dai_config *config) +static int alh_set_config(struct dai *dai, struct sof_ipc_dai_config *config, + int config_idx) { - trace_alh("alh_set_config() config->format = 0x%4x", - config->format); + trace_alh("alh_set_config() config->params.format = 0x%4x", + config->params.format); return 0; } diff --git a/src/drivers/intel/cavs/dmic.c b/src/drivers/intel/cavs/dmic.c index a3a9c3557894..6866a3948b92 100644 --- a/src/drivers/intel/cavs/dmic.c +++ b/src/drivers/intel/cavs/dmic.c @@ -1062,7 +1062,8 @@ static int configure_registers(struct dai *dai, return 0; } -static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) +static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config, + int config_idx) { struct dmic_pdata *dmic = dai_get_drvdata(dai); struct matched_modes modes_ab; @@ -1077,7 +1078,7 @@ static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) trace_dmic_id(dai, "dmic_set_config()"); - if (config->dmic.driver_ipc_version != DMIC_IPC_VERSION) { + if (config->dmic[config_idx].driver_ipc_version != DMIC_IPC_VERSION) { trace_error_dmic_id(dai, "dmic_set_config() error: wrong ipc version"); return -EINVAL; } @@ -1085,8 +1086,8 @@ static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) /* Compute unmute ramp gain update coefficient. Use the value from * topology if it is non-zero, otherwise use default length. */ - if (config->dmic.unmute_ramp_time) - unmute_ramp_time_ms = config->dmic.unmute_ramp_time; + if (config->dmic[config_idx].unmute_ramp_time) + unmute_ramp_time_ms = config->dmic[config_idx].unmute_ramp_time; else unmute_ramp_time_ms = LOGRAMP_TIME_MS; @@ -1102,7 +1103,7 @@ static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) return -EINVAL; } - if (config->dmic.num_pdm_active > DMIC_HW_CONTROLLERS) { + if (config->dmic[config_idx].num_pdm_active > DMIC_HW_CONTROLLERS) { trace_error_dmic_id(dai, "dmic_set_config() error: the requested PDM controllers count exceeds platform capability"); return -EINVAL; } @@ -1136,19 +1137,20 @@ static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) /* Copy the new DMIC params to persistent. The last request * determines the parameters. */ - ret = memcpy_s(dmic_prm[di], sizeof(*dmic_prm[di]), &config->dmic, + ret = memcpy_s(dmic_prm[di], sizeof(*dmic_prm[di]), + &config->dmic[config_idx], sizeof(struct sof_ipc_dai_dmic_params)); assert(!ret); /* copy the pdm controller params from ipc */ for (i = 0; i < DMIC_HW_CONTROLLERS; i++) { dmic_prm[di]->pdm[i].id = i; - for (j = 0; j < config->dmic.num_pdm_active; j++) { + for (j = 0; j < config->dmic[config_idx].num_pdm_active; j++) { /* copy the pdm controller params id the id's match */ - if (dmic_prm[di]->pdm[i].id == config->dmic.pdm[j].id) { + if (dmic_prm[di]->pdm[i].id == config->dmic[config_idx].pdm[j].id) { ret = memcpy_s(&dmic_prm[di]->pdm[i], sizeof(dmic_prm[di]->pdm[i]), - &config->dmic.pdm[j], + &config->dmic[config_idx].pdm[j], sizeof( struct sof_ipc_dai_dmic_pdm_ctrl)); assert(!ret); @@ -1157,7 +1159,7 @@ static int dmic_set_config(struct dai *dai, struct sof_ipc_dai_config *config) } trace_dmic_id(dai, "dmic_set_config(), prm config->dmic.num_pdm_active = %u", - config->dmic.num_pdm_active); + config->dmic[config_idx].num_pdm_active); trace_dmic_id(dai, "dmic_set_config(), prm pdmclk_min = %u, pdmclk_max = %u", dmic_prm[di]->pdmclk_min, dmic_prm[di]->pdmclk_max); trace_dmic_id(dai, "dmic_set_config(), prm duty_min = %u, duty_max = %u", diff --git a/src/drivers/intel/cavs/hda.c b/src/drivers/intel/cavs/hda.c index 201fba4c785c..25ef9b8c013c 100644 --- a/src/drivers/intel/cavs/hda.c +++ b/src/drivers/intel/cavs/hda.c @@ -23,7 +23,7 @@ static int hda_trigger(struct dai *dai, int cmd, int direction) } static int hda_set_config(struct dai *dai, - struct sof_ipc_dai_config *config) + struct sof_ipc_dai_config *config, int config_idx) { return 0; } diff --git a/src/drivers/intel/cavs/ssp.c b/src/drivers/intel/cavs/ssp.c index 2995c1a42500..a640dbf692fc 100644 --- a/src/drivers/intel/cavs/ssp.c +++ b/src/drivers/intel/cavs/ssp.c @@ -92,7 +92,7 @@ static int ssp_context_restore(struct dai *dai) /* Digital Audio interface formatting */ static int ssp_set_config(struct dai *dai, - struct sof_ipc_dai_config *config) + struct sof_ipc_dai_config *config, int config_idx) { struct ssp_pdata *ssp = dai_get_drvdata(dai); uint32_t sscr0; @@ -137,7 +137,8 @@ static int ssp_set_config(struct dai *dai, goto out; } - trace_ssp("ssp_set_config(), config->format = 0x%4x", config->format); + trace_ssp("ssp_set_config(), config->format = 0x%4x", + config->params.format); /* reset SSP settings */ /* sscr0 dynamic settings are DSS, EDSS, SCR, FRDC, ECS */ @@ -159,8 +160,7 @@ static int ssp_set_config(struct dai *dai, /* sspsp dynamic settings are SCMODE, SFRMP, DMYSTRT, SFRMWDTH */ sspsp = 0; - ssp->config = *config; - ssp->params = config->ssp; + ssp->params = config->ssp[config_idx]; /* sspsp2 no dynamic setting */ sspsp2 = 0x0; @@ -172,12 +172,12 @@ static int ssp_set_config(struct dai *dai, ssto = 0x0; /* sstsa dynamic setting is TTSA, default 2 slots */ - sstsa = config->ssp.tx_slots; + sstsa = config->ssp[config_idx].tx_slots; /* ssrsa dynamic setting is RTSA, default 2 slots */ - ssrsa = config->ssp.rx_slots; + ssrsa = config->ssp[config_idx].rx_slots; - switch (config->format & SOF_DAI_FMT_MASTER_MASK) { + switch (config->params.format & SOF_DAI_FMT_MASTER_MASK) { case SOF_DAI_FMT_CBM_CFM: sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; break; @@ -203,7 +203,7 @@ static int ssp_set_config(struct dai *dai, } /* clock signal polarity */ - switch (config->format & SOF_DAI_FMT_INV_MASK) { + switch (config->params.format & SOF_DAI_FMT_INV_MASK) { case SOF_DAI_FMT_NB_NF: break; case SOF_DAI_FMT_NB_IF: @@ -275,39 +275,46 @@ static int ssp_set_config(struct dai *dai, sscr2 |= (ssp->params.quirks & SOF_DAI_INTEL_SSP_QUIRK_PSPSRWFDFD) ? SSCR2_PSPSRWFDFD : 0; - if (!config->ssp.mclk_rate || - config->ssp.mclk_rate > ssp_freq[MAX_SSP_FREQ_INDEX].freq) { + if (!config->ssp[config_idx].mclk_rate || + config->ssp[config_idx].mclk_rate > + ssp_freq[MAX_SSP_FREQ_INDEX].freq) { trace_ssp_error("ssp_set_config() error: " "invalid MCLK = %d Hz (valid < %d)", - config->ssp.mclk_rate, + config->ssp[config_idx].mclk_rate, ssp_freq[MAX_SSP_FREQ_INDEX].freq); ret = -EINVAL; goto out; } - if (!config->ssp.bclk_rate || - config->ssp.bclk_rate > config->ssp.mclk_rate) { + if (!config->ssp[config_idx].bclk_rate || + config->ssp[config_idx].bclk_rate > + config->ssp[config_idx].mclk_rate) { trace_ssp_error("ssp_set_config() error: " "BCLK %d Hz = 0 or > MCLK %d Hz", - config->ssp.bclk_rate, config->ssp.mclk_rate); + config->ssp[config_idx].bclk_rate, + config->ssp[config_idx].mclk_rate); ret = -EINVAL; goto out; } /* MCLK config */ - ret = mn_set_mclk(config->ssp.mclk_id, config->ssp.mclk_rate); + ret = mn_set_mclk(config->ssp[config_idx].mclk_id, + config->ssp[config_idx].mclk_rate); if (ret < 0) { trace_ssp_error("error: invalid mclk_rate = %d for mclk_id = %d", - config->ssp.mclk_id, config->ssp.mclk_rate); + config->ssp[config_idx].mclk_id, + config->ssp[config_idx].mclk_rate); goto out; } /* BCLK config */ - ret = mn_set_bclk(config->dai_index, config->ssp.bclk_rate, + ret = mn_set_bclk(config->params.dai_index, + config->ssp[config_idx].bclk_rate, &mdiv, &need_ecs); if (ret < 0) { trace_ssp_error("error: invalid bclk_rate = %d for dai_index = %d", - config->ssp.bclk_rate, config->dai_index); + config->ssp[config_idx].bclk_rate, + config->params.dai_index); goto out; } @@ -329,35 +336,41 @@ static int ssp_set_config(struct dai *dai, sscr0 |= SSCR0_SCR(mdiv); /* calc frame width based on BCLK and rate - must be divisable */ - if (config->ssp.bclk_rate % config->ssp.fsync_rate) { + if (config->ssp[config_idx].bclk_rate % + config->ssp[config_idx].fsync_rate) { trace_ssp_error("ssp_set_config() error: " "BCLK %d is not divisable by rate %d", - config->ssp.bclk_rate, config->ssp.fsync_rate); + config->ssp[config_idx].bclk_rate, + config->ssp[config_idx].fsync_rate); ret = -EINVAL; goto out; } /* must be enough BCLKs for data */ - bdiv = config->ssp.bclk_rate / config->ssp.fsync_rate; - if (bdiv < config->ssp.tdm_slot_width * config->ssp.tdm_slots) { + bdiv = config->ssp[config_idx].bclk_rate / + config->ssp[config_idx].fsync_rate; + if (bdiv < config->ssp[config_idx].tdm_slot_width * + config->ssp[config_idx].tdm_slots) { trace_ssp_error("ssp_set_config() error: not enough BCLKs " - "need %d", config->ssp.tdm_slot_width * - config->ssp.tdm_slots); + "need %d", + config->ssp[config_idx].tdm_slot_width * + config->ssp[config_idx].tdm_slots); ret = -EINVAL; goto out; } /* tdm_slot_width must be <= 38 for SSP */ - if (config->ssp.tdm_slot_width > 38) { + if (config->ssp[config_idx].tdm_slot_width > 38) { trace_ssp_error("ssp_set_config() error: tdm_slot_width %d > " - "38", config->ssp.tdm_slot_width); + "38", config->ssp[config_idx].tdm_slot_width); ret = -EINVAL; goto out; } - bdiv_min = config->ssp.tdm_slots * - (config->ssp.tdm_per_slot_padding_flag ? - config->ssp.tdm_slot_width : config->ssp.sample_valid_bits); + bdiv_min = config->ssp[config_idx].tdm_slots * + (config->ssp[config_idx].tdm_per_slot_padding_flag ? + config->ssp[config_idx].tdm_slot_width : + config->ssp[config_idx].sample_valid_bits); if (bdiv < bdiv_min) { trace_ssp_error("ssp_set_config() error: bdiv(%d) < " "bdiv_min(%d)", bdiv, bdiv_min); @@ -374,12 +387,12 @@ static int ssp_set_config(struct dai *dai, } /* format */ - switch (config->format & SOF_DAI_FMT_FORMAT_MASK) { + switch (config->params.format & SOF_DAI_FMT_FORMAT_MASK) { case SOF_DAI_FMT_I2S: start_delay = true; - sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); + sscr0 |= SSCR0_FRDC(config->ssp[config_idx].tdm_slots); if (bdiv % 2) { trace_ssp_error("ssp_set_config() error: " @@ -433,7 +446,7 @@ static int ssp_set_config(struct dai *dai, /* default start_delay value is set to false */ - sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); + sscr0 |= SSCR0_FRDC(config->ssp[config_idx].tdm_slots); /* LJDFD enable */ sscr2 &= ~SSCR2_LJDFD; @@ -495,7 +508,8 @@ static int ssp_set_config(struct dai *dai, /* default start_delay value is set to false */ - sscr0 |= SSCR0_MOD | SSCR0_FRDC(config->ssp.tdm_slots); + sscr0 |= SSCR0_MOD | + SSCR0_FRDC(config->ssp[config_idx].tdm_slots); /* set asserted frame length */ frame_len = 1; /* default */ @@ -524,8 +538,8 @@ static int ssp_set_config(struct dai *dai, */ sspsp |= SSPSP_SFRMP(!inverted_frame); - active_tx_slots = popcount(config->ssp.tx_slots); - active_rx_slots = popcount(config->ssp.rx_slots); + active_tx_slots = popcount(config->ssp[config_idx].tx_slots); + active_rx_slots = popcount(config->ssp[config_idx].rx_slots); /* * handle TDM mode, TDM mode has padding at the end of @@ -533,11 +547,13 @@ static int ssp_set_config(struct dai *dai, * subtracting slot width and valid bits per slot. */ if (ssp->params.tdm_per_slot_padding_flag) { - frame_end_padding = bdiv - config->ssp.tdm_slots * - config->ssp.tdm_slot_width; + frame_end_padding = bdiv - + config->ssp[config_idx].tdm_slots * + config->ssp[config_idx].tdm_slot_width; - slot_end_padding = config->ssp.tdm_slot_width - - config->ssp.sample_valid_bits; + slot_end_padding = + config->ssp[config_idx].tdm_slot_width - + config->ssp[config_idx].sample_valid_bits; if (slot_end_padding > SOF_DAI_INTEL_SSP_SLOT_PADDING_MAX) { @@ -561,7 +577,7 @@ static int ssp_set_config(struct dai *dai, break; default: trace_ssp_error("ssp_set_config() error: " - "invalid format 0x%04x", config->format); + "invalid format 0x%04x", config->params.format); ret = -EINVAL; goto out; } @@ -571,7 +587,7 @@ static int ssp_set_config(struct dai *dai, sspsp |= SSPSP_SFRMWDTH(frame_len); - data_size = config->ssp.sample_valid_bits; + data_size = config->ssp[config_idx].sample_valid_bits; if (data_size > 16) sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16)); @@ -579,22 +595,22 @@ static int ssp_set_config(struct dai *dai, sscr0 |= SSCR0_DSIZE(data_size); /* setting TFT and RFT */ - switch (config->ssp.sample_valid_bits) { + switch (config->ssp[config_idx].sample_valid_bits) { case 16: - /* use 2 bytes for each slot */ - sample_width = 2; - break; + /* use 2 bytes for each slot */ + sample_width = 2; + break; case 24: case 32: - /* use 4 bytes for each slot */ - sample_width = 4; - break; + /* use 4 bytes for each slot */ + sample_width = 4; + break; default: - trace_ssp_error("ssp_set_config() error: " - "sample_valid_bits %d", - config->ssp.sample_valid_bits); - ret = -EINVAL; - goto out; + trace_ssp_error("ssp_set_config() error: " + "sample_valid_bits %d", + config->ssp[config_idx].sample_valid_bits); + ret = -EINVAL; + goto out; } tft = MIN(SSP_FIFO_DEPTH - SSP_FIFO_WATERMARK, @@ -790,7 +806,7 @@ static int ssp_remove(struct dai *dai) pm_runtime_put_sync(SSP_CLK, dai->index); - mn_release_mclk(ssp->config.ssp.mclk_id); + mn_release_mclk(ssp->params.mclk_id); mn_release_bclk(dai->index); /* Disable SSP power */ diff --git a/src/drivers/intel/haswell/ssp.c b/src/drivers/intel/haswell/ssp.c index 98b64e9a7ab0..44c2f3002b75 100644 --- a/src/drivers/intel/haswell/ssp.c +++ b/src/drivers/intel/haswell/ssp.c @@ -51,7 +51,7 @@ static int ssp_context_restore(struct dai *dai) /* Digital Audio interface formatting */ static int ssp_set_config(struct dai *dai, - struct sof_ipc_dai_config *config) + struct sof_ipc_dai_config *config, int config_idx) { struct ssp_pdata *ssp = dai_get_drvdata(dai); uint32_t sscr0; @@ -107,10 +107,9 @@ static int ssp_set_config(struct dai *dai, /* sspsp2 no dynamic setting */ sspsp2 = 0x0; - ssp->config = *config; - ssp->params = config->ssp; + ssp->params = config->ssp[config_idx]; - switch (config->format & SOF_DAI_FMT_MASTER_MASK) { + switch (config->params.format & SOF_DAI_FMT_MASTER_MASK) { case SOF_DAI_FMT_CBM_CFM: sscr1 |= SSCR1_SCLKDIR | SSCR1_SFRMDIR; #ifdef ENABLE_SSRCR1_SCFR @@ -136,7 +135,7 @@ static int ssp_set_config(struct dai *dai, } /* clock signal polarity */ - switch (config->format & SOF_DAI_FMT_INV_MASK) { + switch (config->params.format & SOF_DAI_FMT_INV_MASK) { case SOF_DAI_FMT_NB_NF: break; case SOF_DAI_FMT_NB_IF: @@ -197,7 +196,8 @@ static int ssp_set_config(struct dai *dai, SSCR2_PSPSRWFDFD : 0; /* BCLK is generated from MCLK - must be divisable */ - if (config->ssp.mclk_rate % config->ssp.bclk_rate) { + if (config->ssp[config_idx].mclk_rate % + config->ssp[config_idx].bclk_rate) { trace_ssp_error("ssp_set_config() error: " "MCLK is not divisable"); ret = -EINVAL; @@ -205,7 +205,8 @@ static int ssp_set_config(struct dai *dai, } /* divisor must be within SCR range */ - mdiv = (config->ssp.mclk_rate / config->ssp.bclk_rate) - 1; + mdiv = (config->ssp[config_idx].mclk_rate / + config->ssp[config_idx].bclk_rate) - 1; if (mdiv > (SSCR0_SCR_MASK >> 8)) { trace_ssp_error("ssp_set_config() error: " "divisor is not within SCR range"); @@ -217,7 +218,8 @@ static int ssp_set_config(struct dai *dai, sscr0 |= SSCR0_SCR(mdiv); /* calc frame width based on BCLK and rate - must be divisable */ - if (config->ssp.bclk_rate % config->ssp.fsync_rate) { + if (config->ssp[config_idx].bclk_rate % + config->ssp[config_idx].fsync_rate) { trace_ssp_error("ssp_set_config() error: " "BLCK is not divisable"); ret = -EINVAL; @@ -225,22 +227,24 @@ static int ssp_set_config(struct dai *dai, } /* must be enouch BCLKs for data */ - bdiv = config->ssp.bclk_rate / config->ssp.fsync_rate; - if (bdiv < config->ssp.tdm_slot_width * - config->ssp.tdm_slots) { + bdiv = config->ssp[config_idx].bclk_rate / + config->ssp[config_idx].fsync_rate; + if (bdiv < config->ssp[config_idx].tdm_slot_width * + config->ssp[config_idx].tdm_slots) { trace_ssp_error("ssp_set_config() error: not enough BCLKs"); ret = -EINVAL; goto out; } /* tdm_slot_width must be <= 38 for SSP */ - if (config->ssp.tdm_slot_width > 38) { + if (config->ssp[config_idx].tdm_slot_width > 38) { trace_ssp_error("ssp_set_config() error: tdm_slot_width > 38"); ret = -EINVAL; goto out; } - bdiv_min = config->ssp.tdm_slots * config->ssp.sample_valid_bits; + bdiv_min = config->ssp[config_idx].tdm_slots * + config->ssp[config_idx].sample_valid_bits; if (bdiv < bdiv_min) { trace_ssp_error("ssp_set_config() error: bdiv < bdiv_min"); ret = -EINVAL; @@ -256,7 +260,7 @@ static int ssp_set_config(struct dai *dai, } /* format */ - format = config->format & SOF_DAI_FMT_FORMAT_MASK; + format = config->params.format & SOF_DAI_FMT_FORMAT_MASK; switch (format) { case SOF_DAI_FMT_I2S: case SOF_DAI_FMT_LEFT_J: @@ -285,7 +289,7 @@ static int ssp_set_config(struct dai *dai, sspsp |= SSPSP_SFRMP(!inverted_frame); } - sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); + sscr0 |= SSCR0_FRDC(config->ssp[config_idx].tdm_slots); if (bdiv % 2) { trace_ssp_error("ssp_set_config() error: " @@ -328,7 +332,7 @@ static int ssp_set_config(struct dai *dai, start_delay = 1; - sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); + sscr0 |= SSCR0_FRDC(config->ssp[config_idx].tdm_slots); /* set asserted frame length */ frame_len = 1; @@ -342,7 +346,7 @@ static int ssp_set_config(struct dai *dai, start_delay = 0; - sscr0 |= SSCR0_FRDC(config->ssp.tdm_slots); + sscr0 |= SSCR0_FRDC(config->ssp[config_idx].tdm_slots); /* set asserted frame length */ frame_len = 1; @@ -363,7 +367,7 @@ static int ssp_set_config(struct dai *dai, sspsp |= SSPSP_SFRMWDTH(frame_len); - data_size = config->ssp.sample_valid_bits; + data_size = config->ssp[config_idx].sample_valid_bits; if (data_size > 16) sscr0 |= (SSCR0_EDSS | SSCR0_DSIZE(data_size - 16)); @@ -376,8 +380,8 @@ static int ssp_set_config(struct dai *dai, ssp_write(dai, SSCR1, sscr1); ssp_write(dai, SSCR2, sscr2); ssp_write(dai, SSPSP, sspsp); - ssp_write(dai, SSTSA, config->ssp.tx_slots); - ssp_write(dai, SSRSA, config->ssp.rx_slots); + ssp_write(dai, SSTSA, config->ssp[config_idx].tx_slots); + ssp_write(dai, SSRSA, config->ssp[config_idx].rx_slots); ssp_write(dai, SSPSP2, sspsp2); ssp->state[DAI_DIR_PLAYBACK] = COMP_STATE_PREPARE; diff --git a/src/include/ipc/dai.h b/src/include/ipc/dai.h index 50f986f54baa..85751633e056 100644 --- a/src/include/ipc/dai.h +++ b/src/include/ipc/dai.h @@ -16,6 +16,7 @@ #ifndef __IPC_DAI_H__ #define __IPC_DAI_H__ +#include #include #include #include @@ -63,9 +64,7 @@ enum sof_ipc_dai_type { SOF_DAI_IMX_ESAI, /**< i.MX ESAI */ }; -/* general purpose DAI configuration */ -struct sof_ipc_dai_config { - struct sof_ipc_cmd_hdr hdr; +struct sof_ipc_dai_base_params { uint32_t type; /**< DAI type - enum sof_ipc_dai_type */ uint32_t dai_index; /**< index of this type dai */ @@ -74,16 +73,26 @@ struct sof_ipc_dai_config { uint16_t reserved16; /**< alignment */ /* reserved for future use */ - uint32_t reserved[8]; + uint32_t reserved[4]; +} __attribute__((packed)); + +/* general purpose DAI configuration */ +struct sof_ipc_dai_config { + struct sof_ipc_cmd_hdr hdr; + struct sof_ipc_dai_base_params params; + + /* reserved for future use */ + uint32_t reserved[3]; + uint32_t num_configs; /* HW specific data */ union { - struct sof_ipc_dai_ssp_params ssp; - struct sof_ipc_dai_dmic_params dmic; - struct sof_ipc_dai_hda_params hda; - struct sof_ipc_dai_alh_params alh; - struct sof_ipc_dai_esai_params esai; - struct sof_ipc_dai_sai_params sai; + struct sof_ipc_dai_ssp_params ssp[0]; + struct sof_ipc_dai_dmic_params dmic[0]; + struct sof_ipc_dai_hda_params hda[0]; + struct sof_ipc_dai_alh_params alh[0]; + struct sof_ipc_dai_esai_params esai[0]; + struct sof_ipc_dai_sai_params sai[0]; }; } __attribute__((packed)); diff --git a/src/include/sof/audio/component.h b/src/include/sof/audio/component.h index 1d024e46bf6e..55a396ce55ec 100644 --- a/src/include/sof/audio/component.h +++ b/src/include/sof/audio/component.h @@ -197,7 +197,8 @@ struct comp_ops { /** set component audio stream parameters */ int (*dai_config)(struct comp_dev *dev, - struct sof_ipc_dai_config *dai_config); + struct sof_ipc_dai_config *dai_config, + int config_idx); /** used to pass standard and bespoke commands (with optional data) */ int (*cmd)(struct comp_dev *dev, int cmd, void *data, @@ -519,10 +520,10 @@ static inline int comp_reset(struct comp_dev *dev) * @return 0 if succeeded, error code otherwise. */ static inline int comp_dai_config(struct comp_dev *dev, - struct sof_ipc_dai_config *config) + struct sof_ipc_dai_config *config, int config_idx) { if (dev->drv->ops.dai_config) - return dev->drv->ops.dai_config(dev, config); + return dev->drv->ops.dai_config(dev, config, config_idx); return 0; } diff --git a/src/include/sof/drivers/ipc.h b/src/include/sof/drivers/ipc.h index 0002922b17c2..b26fd7df422b 100644 --- a/src/include/sof/drivers/ipc.h +++ b/src/include/sof/drivers/ipc.h @@ -236,7 +236,8 @@ struct ipc_comp_dev *ipc_get_comp_by_ppl_id(struct ipc *ipc, uint16_t type, /* * Configure all DAI components attached to DAI. */ -int ipc_comp_dai_config(struct ipc *ipc, struct sof_ipc_dai_config *config); +int ipc_comp_dai_config(struct ipc *ipc, struct sof_ipc_dai_config *config, + int config_idx); /* send DMA trace host buffer position to host */ int ipc_dma_trace_send_position(void); diff --git a/src/include/sof/drivers/ssp.h b/src/include/sof/drivers/ssp.h index 328cf46a5a9a..d331a3361dac 100644 --- a/src/include/sof/drivers/ssp.h +++ b/src/include/sof/drivers/ssp.h @@ -235,7 +235,6 @@ struct ssp_pdata { uint32_t psp; uint32_t state[2]; /* SSP_STATE_ for each direction */ completion_t drain_complete; - struct sof_ipc_dai_config config; struct sof_ipc_dai_ssp_params params; }; diff --git a/src/include/sof/lib/dai.h b/src/include/sof/lib/dai.h index 1d554eb47167..d6eacec19fd6 100644 --- a/src/include/sof/lib/dai.h +++ b/src/include/sof/lib/dai.h @@ -61,7 +61,8 @@ struct sof_ipc_dai_config; * \brief DAI operations - all optional */ struct dai_ops { - int (*set_config)(struct dai *dai, struct sof_ipc_dai_config *config); + int (*set_config)(struct dai *dai, struct sof_ipc_dai_config *config, + int config_idx); int (*trigger)(struct dai *dai, int cmd, int direction); int (*pm_context_restore)(struct dai *dai); int (*pm_context_store)(struct dai *dai); @@ -212,9 +213,10 @@ void dai_put(struct dai *dai); * \brief Digital Audio interface formatting */ static inline int dai_set_config(struct dai *dai, - struct sof_ipc_dai_config *config) + struct sof_ipc_dai_config *config, + int config_idx) { - int ret = dai->drv->ops.set_config(dai, config); + int ret = dai->drv->ops.set_config(dai, config, config_idx); platform_shared_commit(dai, sizeof(*dai)); diff --git a/src/ipc/handler.c b/src/ipc/handler.c index 8bb3715e2ad3..93397b9154d4 100644 --- a/src/ipc/handler.c +++ b/src/ipc/handler.c @@ -552,12 +552,13 @@ static int ipc_dai_config(uint32_t header) /* copy message with ABI safe method */ IPC_COPY_CMD(config, ipc->comp_data); - trace_ipc("ipc: dai %d.%d -> config ", config.type, - config.dai_index); + trace_ipc("ipc: dai %d.%d -> config ", config.params.type, + config.params.dai_index); /* send params to all DAI components who use that physical DAI */ return ipc_comp_dai_config(ipc, - (struct sof_ipc_dai_config *)ipc->comp_data); + (struct sof_ipc_dai_config *)ipc->comp_data, + 0); } static int ipc_glb_dai_message(uint32_t header) diff --git a/src/ipc/ipc.c b/src/ipc/ipc.c index 4b9b7656ef2e..1be7eee444a7 100644 --- a/src/ipc/ipc.c +++ b/src/ipc/ipc.c @@ -546,7 +546,8 @@ int ipc_pipeline_complete(struct ipc *ipc, uint32_t comp_id) return ret; } -int ipc_comp_dai_config(struct ipc *ipc, struct sof_ipc_dai_config *config) +int ipc_comp_dai_config(struct ipc *ipc, struct sof_ipc_dai_config *config, + int config_idx) { bool comp_on_core[PLATFORM_CORE_COUNT] = { false }; struct sof_ipc_comp_dai *dai; @@ -580,9 +581,10 @@ int ipc_comp_dai_config(struct ipc *ipc, struct sof_ipc_dai_config *config) * set config if comp dai_index matches * config dai_index. */ - if (dai->dai_index == config->dai_index && - dai->type == config->type) { - ret = comp_dai_config(icd->cd, config); + if (dai->dai_index == config->params.dai_index && + dai->type == config->params.type) { + ret = comp_dai_config(icd->cd, config, + config_idx); platform_shared_commit(icd, sizeof(*icd)); if (ret < 0) break; From ddbed059dbdbffe81fa9e73343186ff9c9950e2c Mon Sep 17 00:00:00 2001 From: Slawomir Blauciak Date: Wed, 8 Jan 2020 13:56:15 +0100 Subject: [PATCH 2/2] ipc: introduce multidai abi Introduces configuration IPC ABI for the MultiDAI. MultiDAI is a new logical DAI type responsible for allocation of multiple physical child DAIs and transparently managing them, routing the audio data. Signed-off-by: Slawomir Blauciak --- src/include/ipc/dai.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/include/ipc/dai.h b/src/include/ipc/dai.h index 85751633e056..f3ee9313d1b0 100644 --- a/src/include/ipc/dai.h +++ b/src/include/ipc/dai.h @@ -62,6 +62,7 @@ enum sof_ipc_dai_type { SOF_DAI_INTEL_ALH, /**< Intel ALH */ SOF_DAI_IMX_SAI, /**< i.MX SAI */ SOF_DAI_IMX_ESAI, /**< i.MX ESAI */ + SOF_DAI_MULTIDAI, /**< MultiDAI */ }; struct sof_ipc_dai_base_params { @@ -76,6 +77,16 @@ struct sof_ipc_dai_base_params { uint32_t reserved[4]; } __attribute__((packed)); +struct sof_ipc_multidai_params { + struct sof_ipc_cmd_hdr hdr; + + uint32_t num_dais; + uint32_t reserved[5]; + + struct sof_ipc_stream_map stream_map; + struct sof_ipc_dai_base_params params[0]; +} __attribute__((packed)); + /* general purpose DAI configuration */ struct sof_ipc_dai_config { struct sof_ipc_cmd_hdr hdr; @@ -93,6 +104,7 @@ struct sof_ipc_dai_config { struct sof_ipc_dai_alh_params alh[0]; struct sof_ipc_dai_esai_params esai[0]; struct sof_ipc_dai_sai_params sai[0]; + struct sof_ipc_multidai_params multidai[0]; }; } __attribute__((packed));