struct davinci_mcasp_ruledata {
struct davinci_mcasp *mcasp;
int serializers;
+ int stream;
};
struct davinci_mcasp {
bool missing_audio_param;
/* McASP specific data */
- int tdm_slots;
+ int tdm_slots_tx;
+ int tdm_slots_rx;
u32 tdm_mask[2];
- int slot_width;
+ int slot_width_tx;
+ int slot_width_rx;
u8 op_mode;
u8 dismod;
u8 num_serializer;
u8 *serial_dir;
u8 version;
- u8 bclk_div;
+ u8 bclk_div_tx;
+ u8 bclk_div_rx;
int streams;
u32 irq_request[2];
- int sysclk_freq;
+ unsigned int sysclk_freq_tx;
+ unsigned int sysclk_freq_rx;
bool bclk_master;
- u32 auxclk_fs_ratio;
+ bool async_mode;
+ u32 auxclk_fs_ratio_tx;
+ u32 auxclk_fs_ratio_rx;
unsigned long pdir; /* Pin direction bitfield */
}
}
+static inline void mcasp_set_clk_pdir_stream(struct davinci_mcasp *mcasp,
+ int stream, bool enable)
+{
+ u32 bit, bit_end;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ bit = PIN_BIT_ACLKX;
+ bit_end = PIN_BIT_AFSX + 1;
+ } else {
+ bit = PIN_BIT_ACLKR;
+ bit_end = PIN_BIT_AFSR + 1;
+ }
+
+ for_each_set_bit_from(bit, &mcasp->pdir, bit_end) {
+ if (enable)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ else
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit));
+ }
+}
+
static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable)
{
u32 bit;
}
}
+static inline int mcasp_get_tdm_slots(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->tdm_slots_tx : mcasp->tdm_slots_rx;
+}
+
+static inline int mcasp_get_slot_width(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->slot_width_tx : mcasp->slot_width_rx;
+}
+
+static inline unsigned int mcasp_get_sysclk_freq(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->sysclk_freq_tx : mcasp->sysclk_freq_rx;
+}
+
+static inline unsigned int mcasp_get_bclk_div(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->bclk_div_tx : mcasp->bclk_div_rx;
+}
+
+static inline unsigned int mcasp_get_auxclk_fs_ratio(struct davinci_mcasp *mcasp, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ mcasp->auxclk_fs_ratio_tx : mcasp->auxclk_fs_ratio_rx;
+}
+
static void mcasp_start_rx(struct davinci_mcasp *mcasp)
{
if (mcasp->rxnumevt) { /* enable FIFO */
/*
* When ASYNC == 0 the transmit and receive sections operate
* synchronously from the transmit clock and frame sync. We need to make
- * sure that the TX signlas are enabled when starting reception.
+ * sure that the TX signals are enabled when starting reception,
+ * when the McASP is the producer.
*/
if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) {
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
}
- mcasp_set_clk_pdir(mcasp, true);
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir(mcasp, true);
+ else
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, true);
/* Activate serializer(s) */
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
/* Start clocks */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
- mcasp_set_clk_pdir(mcasp, true);
+ if (mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir(mcasp, true);
+ else
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, true);
/* Activate serializer(s) */
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
/*
* In synchronous mode stop the TX clocks if no other stream is
* running
+ * Otherwise in async mode only stop RX clocks
*/
- if (!mcasp->streams)
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_clk_pdir(mcasp, false);
+ else if (!mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_CAPTURE, false);
+ /*
+ * When McASP is the producer and operating in synchronous mode,
+ * stop the transmit clocks if no other stream is running. As
+ * tx & rx operate synchronously from the transmit clock.
+ */
if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0);
/*
* In synchronous mode keep TX clocks running if the capture stream is
* still running.
+ * Otherwise in async mode only stop TX clocks
*/
if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams)
val = TXHCLKRST | TXCLKRST | TXFSRST;
- if (!mcasp->streams)
+ if (mcasp_is_synchronous(mcasp) && !mcasp->streams)
mcasp_set_clk_pdir(mcasp, false);
+ else if (!mcasp_is_synchronous(mcasp))
+ mcasp_set_clk_pdir_stream(mcasp, SNDRV_PCM_STREAM_PLAYBACK, false);
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
break;
+ case MCASP_CLKDIV_AUXCLK_TXONLY: /* MCLK divider for TX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
+ break;
+
+ case MCASP_CLKDIV_AUXCLK_RXONLY: /* MCLK divider for RX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
+ break;
+
case MCASP_CLKDIV_BCLK: /* BCLK divider */
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
ACLKXDIV(div - 1), ACLKXDIV_MASK);
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
+ ACLKRDIV(div - 1), ACLKRDIV_MASK);
+ if (explicit) {
+ mcasp->bclk_div_tx = div;
+ mcasp->bclk_div_rx = div;
+ }
+ break;
+
+ case MCASP_CLKDIV_BCLK_TXONLY: /* BCLK divider for TX only */
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG,
+ ACLKXDIV(div - 1), ACLKXDIV_MASK);
+ if (explicit)
+ mcasp->bclk_div_tx = div;
+ break;
+
+ case MCASP_CLKDIV_BCLK_RXONLY: /* BCLK divider for RX only */
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
ACLKRDIV(div - 1), ACLKRDIV_MASK);
if (explicit)
- mcasp->bclk_div = div;
+ mcasp->bclk_div_rx = div;
break;
case MCASP_CLKDIV_BCLK_FS_RATIO:
* tdm_slot width by dividing the ratio by the
* number of configured tdm slots.
*/
- mcasp->slot_width = div / mcasp->tdm_slots;
- if (div % mcasp->tdm_slots)
+ mcasp->slot_width_tx = div / mcasp->tdm_slots_tx;
+ if (div % mcasp->tdm_slots_tx)
dev_warn(mcasp->dev,
- "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
- __func__, div, mcasp->tdm_slots);
+ "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots",
+ __func__, div, mcasp->tdm_slots_tx);
+
+ mcasp->slot_width_rx = div / mcasp->tdm_slots_rx;
+ if (div % mcasp->tdm_slots_rx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots",
+ __func__, div, mcasp->tdm_slots_rx);
+ break;
+
+ case MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY:
+ mcasp->slot_width_tx = div / mcasp->tdm_slots_tx;
+ if (div % mcasp->tdm_slots_tx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d tx tdm slots",
+ __func__, div, mcasp->tdm_slots_tx);
+ break;
+
+ case MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY:
+ mcasp->slot_width_rx = div / mcasp->tdm_slots_rx;
+ if (div % mcasp->tdm_slots_rx)
+ dev_warn(mcasp->dev,
+ "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots",
+ __func__, div, mcasp->tdm_slots_rx);
break;
default:
mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRE);
clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ case MCASP_CLK_HCLK_AHCLK_TXONLY:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AHCLK_RXONLY:
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ clear_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
break;
case MCASP_CLK_HCLK_AUXCLK:
mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
AHCLKRE);
set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_TXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_RXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
break;
default:
dev_err(mcasp->dev, "Invalid clk id: %d\n", clk_id);
goto out;
}
} else {
- /* Select AUXCLK as HCLK */
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
- mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
- set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ /* McASP is clock master, select AUXCLK as HCLK */
+ switch (clk_id) {
+ case MCASP_CLK_HCLK_AUXCLK_TXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ break;
+ case MCASP_CLK_HCLK_AUXCLK_RXONLY:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ default:
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG,
+ AHCLKXE);
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG,
+ AHCLKRE);
+ set_bit(PIN_BIT_AHCLKX, &mcasp->pdir);
+ set_bit(PIN_BIT_AHCLKR, &mcasp->pdir);
+ mcasp->sysclk_freq_tx = freq;
+ mcasp->sysclk_freq_rx = freq;
+ break;
+ }
}
/*
* When AHCLK X/R is selected to be output it means that the HCLK is
* the same clock - coming via AUXCLK.
*/
- mcasp->sysclk_freq = freq;
out:
pm_runtime_put(mcasp->dev);
return 0;
{
struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
unsigned int *list = (unsigned int *) cl->list;
- int slots = mcasp->tdm_slots;
+ int slots;
int i, count = 0;
+ slots = mcasp_get_tdm_slots(mcasp, stream);
+
if (mcasp->tdm_mask[stream])
slots = hweight32(mcasp->tdm_mask[stream]);
return -EINVAL;
}
- mcasp->tdm_slots = slots;
+ if (mcasp->async_mode) {
+ if (tx_mask) {
+ mcasp->tdm_slots_tx = slots;
+ mcasp->slot_width_tx = slot_width;
+ }
+ if (rx_mask) {
+ mcasp->tdm_slots_rx = slots;
+ mcasp->slot_width_rx = slot_width;
+ }
+ } else {
+ mcasp->tdm_slots_tx = slots;
+ mcasp->tdm_slots_rx = slots;
+ mcasp->slot_width_tx = slot_width;
+ mcasp->slot_width_rx = slot_width;
+ }
+
mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
- mcasp->slot_width = slot_width;
return davinci_mcasp_set_ch_constraints(mcasp);
}
static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
- int sample_width)
+ int sample_width, int stream)
{
u32 fmt;
u32 tx_rotate, rx_rotate, slot_width;
u32 mask = (1ULL << sample_width) - 1;
- if (mcasp->slot_width)
- slot_width = mcasp->slot_width;
- else if (mcasp->max_format_width)
- slot_width = mcasp->max_format_width;
- else
- slot_width = sample_width;
+ slot_width = mcasp_get_slot_width(mcasp, stream);
+ if (!slot_width) {
+ if (mcasp->max_format_width)
+ slot_width = mcasp->max_format_width;
+ else
+ slot_width = sample_width;
+ }
/*
* TX rotation:
* right aligned formats: rotate w/ slot_width
fmt = (slot_width >> 1) - 1;
if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
- RXSSZ(0x0F));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
- TXSSZ(0x0F));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
- TXROT(7));
- mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
- RXROT(7));
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+ if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt),
+ TXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate),
+ TXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
+ }
+ if (!mcasp->async_mode || stream == SNDRV_PCM_STREAM_CAPTURE) {
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
+ RXSSZ(0x0F));
+ mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate),
+ RXROT(7));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask);
+ }
} else {
/*
+ * DIT mode only use TX serializers
* according to the TRM it should be TXROT=0, this one works:
* 16 bit to 23-8 (TXROT=6, rotate 24 bits)
* 24 bit to 23-0 (TXROT=0, rotate 0 bits)
TXROT(7));
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(15),
TXSSZ(0x0F));
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
}
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask);
-
return 0;
}
int i;
u8 tx_ser = 0;
u8 rx_ser = 0;
- u8 slots = mcasp->tdm_slots;
+ int slots;
u8 max_active_serializers, max_rx_serializers, max_tx_serializers;
int active_serializers, numevt;
u32 reg;
+ slots = mcasp_get_tdm_slots(mcasp, stream);
+
/* In DIT mode we only allow maximum of one serializers for now */
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
max_active_serializers = 1;
u32 mask = 0;
u32 busel = 0;
- total_slots = mcasp->tdm_slots;
+ total_slots = mcasp_get_tdm_slots(mcasp, stream);
/*
* If more than one serializer is needed, then use them with
mask |= (1 << i);
}
- mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+ if (mcasp->async_mode)
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+ else
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
if (!mcasp->dat_port)
busel = TXSEL;
static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp,
unsigned int sysclk_freq,
- unsigned int bclk_freq, bool set)
+ unsigned int bclk_freq,
+ int stream,
+ bool set)
{
- u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG);
int div = sysclk_freq / bclk_freq;
int rem = sysclk_freq % bclk_freq;
int error_ppm;
int aux_div = 1;
+ int bclk_div_id, auxclk_div_id;
+ bool auxclk_enabled;
+
+ if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_CAPTURE) {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG) & AHCLKRE;
+ bclk_div_id = MCASP_CLKDIV_BCLK_RXONLY;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK_RXONLY;
+ } else if (mcasp->async_mode && stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE;
+ bclk_div_id = MCASP_CLKDIV_BCLK_TXONLY;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK_TXONLY;
+ } else {
+ auxclk_enabled = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG) & AHCLKXE;
+ bclk_div_id = MCASP_CLKDIV_BCLK;
+ auxclk_div_id = MCASP_CLKDIV_AUXCLK;
+ }
if (div > (ACLKXDIV_MASK + 1)) {
- if (reg & AHCLKXE) {
+ if (auxclk_enabled) {
aux_div = div / (ACLKXDIV_MASK + 1);
if (div % (ACLKXDIV_MASK + 1))
aux_div++;
dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n",
error_ppm);
- __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0);
- if (reg & AHCLKXE)
- __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK,
- aux_div, 0);
+ __davinci_mcasp_set_clkdiv(mcasp, bclk_div_id, div, false);
+ if (auxclk_enabled)
+ __davinci_mcasp_set_clkdiv(mcasp, auxclk_div_id,
+ aux_div, false);
}
return error_ppm;
int channels = params_channels(params);
int period_size = params_period_size(params);
int ret;
+ unsigned int sysclk_freq = mcasp_get_sysclk_freq(mcasp, substream->stream);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_U8:
* If mcasp is BCLK master, and a BCLK divider was not provided by
* the machine driver, we need to calculate the ratio.
*/
- if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
- int slots = mcasp->tdm_slots;
+ if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 &&
+ sysclk_freq) {
+ int slots, slot_width;
int rate = params_rate(params);
int sbits = params_width(params);
unsigned int bclk_target;
- if (mcasp->slot_width)
- sbits = mcasp->slot_width;
+ slots = mcasp_get_tdm_slots(mcasp, substream->stream);
+
+ slot_width = mcasp_get_slot_width(mcasp, substream->stream);
+ if (slot_width)
+ sbits = slot_width;
if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
bclk_target = rate * sbits * slots;
else
bclk_target = rate * 128;
- davinci_mcasp_calc_clk_div(mcasp, mcasp->sysclk_freq,
- bclk_target, true);
+ davinci_mcasp_calc_clk_div(mcasp, sysclk_freq,
+ bclk_target, substream->stream, true);
}
ret = mcasp_common_hw_param(mcasp, substream->stream,
if (ret)
return ret;
- davinci_config_channel_size(mcasp, word_length);
+ davinci_config_channel_size(mcasp, word_length, substream->stream);
- if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+ /* Channel constraints are disabled for async mode */
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE && !mcasp->async_mode) {
mcasp->channels = channels;
if (!mcasp->max_format_width)
mcasp->max_format_width = word_length;
snd_pcm_format_t i;
snd_mask_none(&nfmt);
- slot_width = rd->mcasp->slot_width;
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
pcm_for_each_format(i) {
if (snd_mask_test_format(fmt, i)) {
struct snd_interval *ri =
hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
int sbits = params_width(params);
- int slots = rd->mcasp->tdm_slots;
+ int slots, slot_width;
struct snd_interval range;
int i;
- if (rd->mcasp->slot_width)
- sbits = rd->mcasp->slot_width;
+ slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream);
+
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
+ if (slot_width)
+ sbits = slot_width;
snd_interval_any(&range);
range.empty = 1;
uint bclk_freq = sbits * slots *
davinci_mcasp_dai_rates[i];
unsigned int sysclk_freq;
+ unsigned int ratio;
int ppm;
- if (rd->mcasp->auxclk_fs_ratio)
- sysclk_freq = davinci_mcasp_dai_rates[i] *
- rd->mcasp->auxclk_fs_ratio;
+ ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream);
+ if (ratio)
+ sysclk_freq = davinci_mcasp_dai_rates[i] * ratio;
else
- sysclk_freq = rd->mcasp->sysclk_freq;
+ sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream);
ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq,
- bclk_freq, false);
+ bclk_freq, rd->stream, false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
if (range.empty) {
range.min = davinci_mcasp_dai_rates[i];
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_mask nfmt;
int rate = params_rate(params);
- int slots = rd->mcasp->tdm_slots;
+ int slots;
int count = 0;
snd_pcm_format_t i;
+ slots = mcasp_get_tdm_slots(rd->mcasp, rd->stream);
+
snd_mask_none(&nfmt);
pcm_for_each_format(i) {
if (snd_mask_test_format(fmt, i)) {
uint sbits = snd_pcm_format_width(i);
unsigned int sysclk_freq;
- int ppm;
+ unsigned int ratio;
+ int ppm, slot_width;
- if (rd->mcasp->auxclk_fs_ratio)
- sysclk_freq = rate *
- rd->mcasp->auxclk_fs_ratio;
+ ratio = mcasp_get_auxclk_fs_ratio(rd->mcasp, rd->stream);
+ if (ratio)
+ sysclk_freq = rate * ratio;
else
- sysclk_freq = rd->mcasp->sysclk_freq;
+ sysclk_freq = mcasp_get_sysclk_freq(rd->mcasp, rd->stream);
- if (rd->mcasp->slot_width)
- sbits = rd->mcasp->slot_width;
+ slot_width = mcasp_get_slot_width(rd->mcasp, rd->stream);
+ if (slot_width)
+ sbits = slot_width;
ppm = davinci_mcasp_calc_clk_div(rd->mcasp, sysclk_freq,
sbits * slots * rate,
- false);
+ rd->stream, false);
if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
snd_mask_set_format(&nfmt, i);
count++;
&mcasp->ruledata[substream->stream];
u32 max_channels = 0;
int i, dir, ret;
- int tdm_slots = mcasp->tdm_slots;
+ int tdm_slots;
u8 *numevt;
/* Do not allow more then one stream per direction */
mcasp->substreams[substream->stream] = substream;
+ tdm_slots = mcasp_get_tdm_slots(mcasp, substream->stream);
+
if (mcasp->tdm_mask[substream->stream])
tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
}
ruledata->serializers = max_channels;
ruledata->mcasp = mcasp;
+ ruledata->stream = substream->stream;
max_channels *= tdm_slots;
/*
* If the already active stream has less channels than the calculated
* is in use we need to use that as a constraint for the second stream.
* Otherwise (first stream or less allowed channels or more than one
* serializer in use) we use the calculated constraint.
+ *
+ * However, in async mode, TX and RX have independent clocks and can
+ * use different configurations, so don't apply the constraint.
*/
if (mcasp->channels && mcasp->channels < max_channels &&
- ruledata->serializers == 1)
+ ruledata->serializers == 1 &&
+ !mcasp->async_mode)
max_channels = mcasp->channels;
/*
* But we can always allow channels upto the amount of
0, SNDRV_PCM_HW_PARAM_CHANNELS,
&mcasp->chconstr[substream->stream]);
- if (mcasp->max_format_width) {
+ if (mcasp->max_format_width && !mcasp->async_mode) {
/*
* Only allow formats which require same amount of bits on the
- * bus as the currently running stream
+ * bus as the currently running stream to ensure sync mode
*/
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (ret)
return ret;
- }
- else if (mcasp->slot_width) {
+ } else if (mcasp_get_slot_width(mcasp, substream->stream)) {
/* Only allow formats require <= slot_width bits on the bus */
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
* If we rely on implicit BCLK divider setting we should
* set constraints based on what we can provide.
*/
- if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
+ if (mcasp->bclk_master && mcasp_get_bclk_div(mcasp, substream->stream) == 0 &&
+ mcasp_get_sysclk_freq(mcasp, substream->stream)) {
ret = snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
davinci_mcasp_hw_rule_rate,
.formats = DAVINCI_MCASP_PCM_FMTS,
},
.ops = &davinci_mcasp_dai_ops,
-
- .symmetric_rate = 1,
},
{
.name = "davinci-mcasp.1",
goto out;
}
+ /* Parse TX-specific TDM slot and use it as default for RX */
if (of_property_read_u32(np, "tdm-slots", &val) == 0) {
if (val < 2 || val > 32) {
- dev_err(&pdev->dev, "tdm-slots must be in rage [2-32]\n");
+ dev_err(&pdev->dev, "tdm-slots must be in range [2-32]\n");
return -EINVAL;
}
- pdata->tdm_slots = val;
+ pdata->tdm_slots_tx = val;
+ pdata->tdm_slots_rx = val;
} else if (pdata->op_mode == DAVINCI_MCASP_IIS_MODE) {
mcasp->missing_audio_param = true;
goto out;
}
+ /* Parse RX-specific TDM slot count if provided */
+ if (of_property_read_u32(np, "tdm-slots-rx", &val) == 0) {
+ if (val < 2 || val > 32) {
+ dev_err(&pdev->dev, "tdm-slots-rx must be in range [2-32]\n");
+ return -EINVAL;
+ }
+
+ pdata->tdm_slots_rx = val;
+ }
+
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE)
+ mcasp->async_mode = of_property_read_bool(np, "ti,async-mode");
+
of_serial_dir32 = of_get_property(np, "serial-dir", &val);
val /= sizeof(u32);
if (of_serial_dir32) {
if (of_property_read_u32(np, "rx-num-evt", &val) == 0)
pdata->rxnumevt = val;
- if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0)
- mcasp->auxclk_fs_ratio = val;
+ /* Parse TX-specific auxclk/fs ratio and use it as default for RX */
+ if (of_property_read_u32(np, "auxclk-fs-ratio", &val) == 0) {
+ mcasp->auxclk_fs_ratio_tx = val;
+ mcasp->auxclk_fs_ratio_rx = val;
+ }
+
+ /* Parse RX-specific auxclk/fs ratio if provided */
+ if (of_property_read_u32(np, "auxclk-fs-ratio-rx", &val) == 0)
+ mcasp->auxclk_fs_ratio_rx = val;
if (of_property_read_u32(np, "dismod", &val) == 0) {
if (val == 0 || val == 2 || val == 3) {
mcasp->op_mode = pdata->op_mode;
/* sanity check for tdm slots parameter */
if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
- if (pdata->tdm_slots < 2) {
- dev_warn(&pdev->dev, "invalid tdm slots: %d\n",
- pdata->tdm_slots);
- mcasp->tdm_slots = 2;
- } else if (pdata->tdm_slots > 32) {
- dev_warn(&pdev->dev, "invalid tdm slots: %d\n",
- pdata->tdm_slots);
- mcasp->tdm_slots = 32;
+ if (pdata->tdm_slots_tx < 2) {
+ dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n",
+ pdata->tdm_slots_tx);
+ mcasp->tdm_slots_tx = 2;
+ } else if (pdata->tdm_slots_tx > 32) {
+ dev_warn(&pdev->dev, "invalid tdm tx slots: %d\n",
+ pdata->tdm_slots_tx);
+ mcasp->tdm_slots_tx = 32;
+ } else {
+ mcasp->tdm_slots_tx = pdata->tdm_slots_tx;
+ }
+
+ if (pdata->tdm_slots_rx < 2) {
+ dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n",
+ pdata->tdm_slots_rx);
+ mcasp->tdm_slots_rx = 2;
+ } else if (pdata->tdm_slots_rx > 32) {
+ dev_warn(&pdev->dev, "invalid tdm rx slots: %d\n",
+ pdata->tdm_slots_rx);
+ mcasp->tdm_slots_rx = 32;
} else {
- mcasp->tdm_slots = pdata->tdm_slots;
+ mcasp->tdm_slots_rx = pdata->tdm_slots_rx;
}
} else {
- mcasp->tdm_slots = 32;
+ mcasp->tdm_slots_tx = 32;
+ mcasp->tdm_slots_rx = 32;
+ }
+
+ /* Different TX/RX slot counts require async mode */
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE &&
+ mcasp->tdm_slots_tx != mcasp->tdm_slots_rx && !mcasp->async_mode) {
+ dev_err(&pdev->dev,
+ "Different TX (%d) and RX (%d) TDM slots require ti,async-mode\n",
+ mcasp->tdm_slots_tx, mcasp->tdm_slots_rx);
+ return -EINVAL;
+ }
+
+ /* Different TX/RX auxclk-fs-ratio require async mode */
+ if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE &&
+ mcasp->auxclk_fs_ratio_tx && mcasp->auxclk_fs_ratio_rx &&
+ mcasp->auxclk_fs_ratio_tx != mcasp->auxclk_fs_ratio_rx && !mcasp->async_mode) {
+ dev_err(&pdev->dev,
+ "Different TX (%d) and RX (%d) auxclk-fs-ratio require ti,async-mode\n",
+ mcasp->auxclk_fs_ratio_tx, mcasp->auxclk_fs_ratio_rx);
+ return -EINVAL;
}
mcasp->num_serializer = pdata->num_serializer;