Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 78 additions & 35 deletions src/drivers/imx/sai.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ static void sai_start(struct dai *dai, int direction)
{
dai_info(dai, "SAI: sai_start");

struct sai_pdata *sai = dai_get_drvdata(dai);
int chan_idx = 0;
uint32_t xcsr = 0U;
int i;
int n;
#ifdef CONFIG_IMX8ULP
int fifo_offset = 0;
#endif
Expand Down Expand Up @@ -80,11 +83,36 @@ static void sai_start(struct dai *dai, int direction)
dai_update_bits(dai, REG_SAI_XCSR(direction),
REG_SAI_CSR_WSF, 1);

/* add one word to FIFO before TRCE is enabled */
if (direction == DAI_DIR_PLAYBACK)
dai_write(dai, REG_SAI_TDR0, 0x0);
else
dai_write(dai, REG_SAI_RDR0, 0x0);
/*
* Add one frame of data to FIFO before TRCE is enabled.
* In FIFO words this equates to tdm_slots/(slots_per_32b_fifo_word). Minimum: one word.
* Not performing this 'priming' can lead to negative effects like shifted
* and / or missing slots.
* TODO: check if that works in all situations
*/

switch (sai->params.tdm_slot_width) {
case 8:
n = sai->params.tdm_slots / 4;
break;
case 16:
n = sai->params.tdm_slots / 2;
break;
default:
n = sai->params.tdm_slots;
break;
}

if (!n)
n = 1;

if (direction == DAI_DIR_PLAYBACK) {
for (i = 0; i < n; i++)
dai_write(dai, REG_SAI_TDR0, 0x0);
} else {
for (i = 0; i < n; i++)
dai_write(dai, REG_SAI_RDR0, 0x0);
}

/* enable DMA requests */
dai_update_bits(dai, REG_SAI_XCSR(direction),
Expand Down Expand Up @@ -200,22 +228,28 @@ static inline int sai_set_config(struct dai *dai, struct ipc_config_dai *common_
const struct sof_ipc_dai_config *config = spec_config;
uint32_t val_cr2 = 0, val_cr4 = 0, val_cr5 = 0;
uint32_t mask_cr2 = 0, mask_cr4 = 0, mask_cr5 = 0;
uint32_t clk_div;
struct sai_pdata *sai = dai_get_drvdata(dai);
/* TODO: this value will be provided by config */
#ifndef CONFIG_IMX8ULP
uint32_t sywd = 32;
uint32_t twm = ~(BIT(0) | BIT(1));
uint32_t clk_div = SAI_CLOCK_DIV;
#else
uint32_t sywd = 16;
uint32_t twm = ~BIT(0);
uint32_t clk_div = config->sai.fsync_rate == 8000 ? SAI_CLOCK_DIV : SAI_CLOCK_DIV_16K;
#endif

sai->config = *config;
sai->params = config->sai;

val_cr4 |= REG_SAI_CR4_MF;
/* bit width of a single slot; FIFO word always 32b */
uint32_t sywd = sai->params.tdm_slot_width;

/*
* Divide the audio main clock to generate the bit clock when
* configured for an internal bit clock.
* The division value is (DIV + 1) * 2.
* If mclk == bclk set the divider bypass bit, REG_SAI_CR2_BYP.
*/

if (config->sai.mclk_rate == config->sai.bclk_rate) {
val_cr2 |= REG_SAI_CR2_BYP;
clk_div = 0;
} else {
clk_div = (config->sai.mclk_rate / config->sai.bclk_rate / 2) - 1;
}

switch (config->format & SOF_DAI_FMT_FORMAT_MASK) {
case SOF_DAI_FMT_I2S:
Expand Down Expand Up @@ -250,15 +284,15 @@ static inline int sai_set_config(struct dai *dai, struct ipc_config_dai *common_
*/
val_cr2 |= REG_SAI_CR2_BCP;
val_cr4 |= REG_SAI_CR4_FSE;
val_cr4 |= REG_SAI_CR4_SYWD(0U);
val_cr4 |= REG_SAI_CR4_SYWD(1U);
break;
case SOF_DAI_FMT_DSP_B:
/*
* Frame high, one bit for frame sync,
* frame sync asserts with the first bit of the frame.
*/
val_cr2 |= REG_SAI_CR2_BCP;
val_cr4 |= REG_SAI_CR4_SYWD(0U);
val_cr4 |= REG_SAI_CR4_SYWD(1U);
break;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand this change. Is this a bugfix? We should add it in a separate patch with proper explanation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a separate commit. Or did you mean put it into a separate pull request?

case SOF_DAI_FMT_PDM:
val_cr2 |= REG_SAI_CR2_BCP;
Expand Down Expand Up @@ -299,7 +333,7 @@ static inline int sai_set_config(struct dai *dai, struct ipc_config_dai *common_
dai_info(dai, "SAI: codec is consumer");
val_cr2 |= REG_SAI_CR2_MSEL_MCLK1;
val_cr2 |= REG_SAI_CR2_BCD_MSTR;
val_cr2 |= clk_div; /* TODO: determine dynamically.*/
val_cr2 |= clk_div;
val_cr4 |= REG_SAI_CR4_FSD_MSTR;
break;
case SOF_DAI_FMT_CBP_CFP:
Expand All @@ -311,30 +345,42 @@ static inline int sai_set_config(struct dai *dai, struct ipc_config_dai *common_
break;
case SOF_DAI_FMT_CBC_CFP:
val_cr2 |= REG_SAI_CR2_BCD_MSTR;
val_cr2 |= clk_div; /* TODO: determine dynamically.*/
val_cr2 |= clk_div;
break;
case SOF_DAI_FMT_CBP_CFC:
val_cr4 |= REG_SAI_CR4_FSD_MSTR;
val_cr2 |= clk_div; /* TODO: determine dynamically.*/
val_cr2 |= clk_div;
break;
default:
return -EINVAL;
}

/* TODO: set number of slots from config */
val_cr4 |= REG_SAI_CR4_FRSZ(SAI_TDM_SLOTS);
switch (sai->params.tdm_slot_width) {
case 8:
val_cr4 |= REG_SAI_CR4_FPACK_8;
break;
case 16:
val_cr4 |= REG_SAI_CR4_FPACK_16;
break;
default:
break;
}

val_cr4 |= REG_SAI_CR4_FRSZ(sai->params.tdm_slots);
val_cr4 |= REG_SAI_CR4_CHMOD;
val_cr4 |= REG_SAI_CR4_MF;

val_cr5 |= REG_SAI_CR5_WNW(sywd) | REG_SAI_CR5_W0W(sywd) |
REG_SAI_CR5_FBT(sywd);

mask_cr2 = REG_SAI_CR2_BCP | REG_SAI_CR2_BCD_MSTR |
REG_SAI_CR2_BYP_MASK |
REG_SAI_CR2_MSEL_MASK | REG_SAI_CR2_DIV_MASK;

mask_cr4 = REG_SAI_CR4_MF | REG_SAI_CR4_FSE |
REG_SAI_CR4_FSP | REG_SAI_CR4_FSD_MSTR |
REG_SAI_CR4_FRSZ_MASK | REG_SAI_CR4_SYWD_MASK |
REG_SAI_CR4_CHMOD_MASK;
REG_SAI_CR4_CHMOD_MASK | REG_SAI_CR4_FPACK_MASK;

mask_cr5 = REG_SAI_CR5_WNW_MASK | REG_SAI_CR5_W0W_MASK |
REG_SAI_CR5_FBT_MASK;
Expand All @@ -344,8 +390,9 @@ static inline int sai_set_config(struct dai *dai, struct ipc_config_dai *common_
dai_update_bits(dai, REG_SAI_XCR2(REG_TX_DIR), mask_cr2, val_cr2);
dai_update_bits(dai, REG_SAI_XCR4(REG_TX_DIR), mask_cr4, val_cr4);
dai_update_bits(dai, REG_SAI_XCR5(REG_TX_DIR), mask_cr5, val_cr5);
/* turn on (set to zero) stereo slot */
dai_update_bits(dai, REG_SAI_XMR(REG_TX_DIR), REG_SAI_XMR_MASK, twm);
/* turn on (set to zero) slots */
dai_update_bits(dai, REG_SAI_XMR(REG_TX_DIR), REG_SAI_XMR_MASK,
~(sai->params.tx_slots));

val_cr2 |= REG_SAI_CR2_SYNC;
mask_cr2 |= REG_SAI_CR2_SYNC_MASK;
Expand All @@ -355,9 +402,9 @@ static inline int sai_set_config(struct dai *dai, struct ipc_config_dai *common_
dai_update_bits(dai, REG_SAI_XCR2(REG_RX_DIR), mask_cr2, val_cr2);
dai_update_bits(dai, REG_SAI_XCR4(REG_RX_DIR), mask_cr4, val_cr4);
dai_update_bits(dai, REG_SAI_XCR5(REG_RX_DIR), mask_cr5, val_cr5);
/* turn on (set to zero) stereo slot */
/* turn on (set to zero) slots */
dai_update_bits(dai, REG_SAI_XMR(REG_RX_DIR), REG_SAI_XMR_MASK,
twm);
~(sai->params.rx_slots));

#if defined(CONFIG_IMX8M) || defined(CONFIG_IMX93_A55)
/*
Expand Down Expand Up @@ -484,14 +531,10 @@ static int sai_get_hw_params(struct dai *dai,
{
struct sai_pdata *sai = dai_get_drvdata(dai);

/* SAI only currently supports these parameters */
params->rate = sai->params.fsync_rate;
#ifdef CONFIG_IMX8ULP
params->channels = 1;
#else
params->channels = 2;
#endif
params->buffer_fmt = 0;
params->channels = sai->params.tdm_slots;
params->buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED;
/* frame_fmt always S32_LE because that is the native width of the fifo registers */
params->frame_fmt = SOF_IPC_FRAME_S32_LE;

return 0;
Expand Down
18 changes: 2 additions & 16 deletions src/include/sof/drivers/sai.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
#define REG_SAI_CR2_BCP BIT(25)
#define REG_SAI_CR2_BCD_MSTR BIT(24)
#define REG_SAI_CR2_BYP BIT(23) /* BCLK bypass */
#define REG_SAI_CR2_BYP_MASK BIT(23)
#define REG_SAI_CR2_DIV_MASK 0xff

/* SAI Transmit and Receive Configuration 3 Register */
Expand All @@ -163,6 +164,7 @@
#define REG_SAI_CR4_FCOMB_MASK MASK(27, 26)
#define REG_SAI_CR4_FPACK_8 (0x2 << 24)
#define REG_SAI_CR4_FPACK_16 (0x3 << 24)
#define REG_SAI_CR4_FPACK_MASK MASK(25, 24)
#define REG_SAI_CR4_FRSZ(x) (((x) - 1) << 16)
#define REG_SAI_CR4_FRSZ_MASK MASK(20, 16)
#define REG_SAI_CR4_SYWD(x) (((x) - 1) << 8)
Expand Down Expand Up @@ -245,22 +247,6 @@
#define SAI_FIFO_WORD_SIZE 64
#endif

/* Divides down the audio main clock to generate the bit clock when
* configured for an internal bit clock.
* The division value is (DIV + 1) * 2.
*/
#ifdef CONFIG_IMX8ULP
/* frame clk is decided by sai config on 8ulp
* 8K --- 0x17, 16K --- 0xB
*/
#define SAI_CLOCK_DIV 0x17
#define SAI_CLOCK_DIV_16K 0xB
#define SAI_TDM_SLOTS 2
#else
#define SAI_CLOCK_DIV 0x7
#define SAI_TDM_SLOTS 2
#endif

extern const struct dai_driver sai_driver;

/* SAI private data */
Expand Down
12 changes: 12 additions & 0 deletions src/platform/imx8m/include/platform/lib/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,21 @@
#define SAI_1_BASE 0x30c10000
#define SAI_1_SIZE 0x00010000

#define SAI_2_BASE 0x30c20000
#define SAI_2_SIZE 0x00010000

#define SAI_3_BASE 0x30c30000
#define SAI_3_SIZE 0x00010000

#define SAI_5_BASE 0x30c50000
#define SAI_5_SIZE 0x00010000

#define SAI_6_BASE 0x30c60000
#define SAI_6_SIZE 0x00010000

#define SAI_7_BASE 0x30c80000
#define SAI_7_SIZE 0x00010000

#define UUID_ENTRY_ELF_BASE 0x1FFFA000
#define UUID_ENTRY_ELF_SIZE 0x6000

Expand Down
10 changes: 7 additions & 3 deletions src/platform/imx8m/include/platform/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ struct timer;
#define PLATFORM_SCHEDULE_IRQ IRQ_NUM_SOFTWARE0
#define PLATFORM_SCHEDULE_IRQ_NAME NULL

/* Platform stream capabilities */
#define PLATFORM_MAX_CHANNELS 4
#define PLATFORM_MAX_STREAMS 5
/*
* Platform stream capabilities
* MAX_CHANNELS only affects 'components' so it is, for example,
* still possible to have multiple SAI peripherals output 8 channels each.
*/
#define PLATFORM_MAX_CHANNELS 8
#define PLATFORM_MAX_STREAMS 8

/* local buffer size of DMA tracing */
#define DMA_TRACE_LOCAL_SIZE HOST_PAGE_SIZE
Expand Down
86 changes: 86 additions & 0 deletions src/platform/imx8m/lib/dai.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,28 @@ static SHARED_DATA struct dai sai[] = {
},
.drv = &sai_driver,
},

{
.index = 2,
.plat_data = {
.base = SAI_2_BASE,
.fifo[SOF_IPC_STREAM_PLAYBACK] = {
.offset = SAI_2_BASE + REG_SAI_TDR0,
.depth = 128, /* in 4 bytes words */
.watermark = 64, /* half the depth */
/* Handshake is SDMA hardware event */
.handshake = 3,
},
.fifo[SOF_IPC_STREAM_CAPTURE] = {
.offset = SAI_2_BASE + REG_SAI_RDR0,
.depth = 128, /* in 4 bytes words */
.watermark = 64, /* half the depth */
.handshake = 2,
},
},
.drv = &sai_driver,
},

{
.index = 3,
.plat_data = {
Expand All @@ -54,6 +76,70 @@ static SHARED_DATA struct dai sai[] = {
},
.drv = &sai_driver,
},

{
.index = 5,
.plat_data = {
.base = SAI_5_BASE,
.fifo[SOF_IPC_STREAM_PLAYBACK] = {
.offset = SAI_5_BASE + REG_SAI_TDR0,
.depth = 128, /* in 4 bytes words */
.watermark = 64, /* half the depth */
/* Handshake is SDMA hardware event */
.handshake = 9,
},
.fifo[SOF_IPC_STREAM_CAPTURE] = {
.offset = SAI_5_BASE + REG_SAI_RDR0,
.depth = 128, /* in 4 bytes words */
.watermark = 64, /* half the depth */
.handshake = 8,
},
},
.drv = &sai_driver,
},

{
.index = 6,
.plat_data = {
.base = SAI_6_BASE,
.fifo[SOF_IPC_STREAM_PLAYBACK] = {
.offset = SAI_6_BASE + REG_SAI_TDR0,
.depth = 128, /* in 4 bytes words */
.watermark = 64, /* half the depth */
/* Handshake is SDMA hardware event */
.handshake = 11,
},
.fifo[SOF_IPC_STREAM_CAPTURE] = {
.offset = SAI_6_BASE + REG_SAI_RDR0,
.depth = 128, /* in 4 bytes words */
.watermark = 64, /* half the depth */
.handshake = 10,
},
},
.drv = &sai_driver,
},

{
.index = 7,
.plat_data = {
.base = SAI_7_BASE,
.fifo[SOF_IPC_STREAM_PLAYBACK] = {
.offset = SAI_7_BASE + REG_SAI_TDR0,
.depth = 128, /* in 4 bytes words */
.watermark = 64, /* half the depth */
/* Handshake is SDMA hardware event */
.handshake = 13,
},
.fifo[SOF_IPC_STREAM_CAPTURE] = {
.offset = SAI_7_BASE + REG_SAI_RDR0,
.depth = 128, /* in 4 bytes words */
.watermark = 64, /* half the depth */
.handshake = 12,
},
},
.drv = &sai_driver,
},

};

const struct dai_type_info dti[] = {
Expand Down
1 change: 1 addition & 0 deletions tools/topology/topology1/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ set(TPLGS
"sof-imx8-wm8960-mixer\;sof-imx8mp-wm8960-mixer\;-DCODEC=wm8960\;-DRATE=48000\;-DSAI_INDEX=3"
"sof-imx8-wm8960-mixer\;sof-imx8mp-wm8962-mixer\;-DCODEC=wm8962\;-DRATE=48000\;-DSAI_INDEX=3"
"sof-imx8mp-wm8960-kwd\;sof-imx8mp-wm8960-kwd"
"sof-imx8mp-btsco-dual-8ch\;sof-imx8mp-btsco-dual-8ch"
"sof-imx8-wm8960\;sof-imx8mp-wm8960\;-DCODEC=wm8960\;-DRATE=48000\;-DPPROC=volume\;-DSAI_INDEX=3"
"sof-imx8-wm8960\;sof-imx8mp-wm8904\;-DCODEC=wm8904\;-DRATE=44100\;-DPPROC=volume\;-DSAI_INDEX=3"
"sof-imx8-wm8960\;sof-imx8mp-wm8962\;-DCODEC=wm8962\;-DRATE=48000\;-DPPROC=volume\;-DSAI_INDEX=3"
Expand Down
Loading