* [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP
@ 2026-01-30 5:10 Sen Wang
2026-01-30 5:10 ` [PATCH 1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode Sen Wang
` (4 more replies)
0 siblings, 5 replies; 13+ messages in thread
From: Sen Wang @ 2026-01-30 5:10 UTC (permalink / raw)
To: peter.ujfalusi, broonie, lgirdwood, perex, tiwai, robh, krzk+dt,
conor+dt
Cc: linux-sound, devicetree, linux-kernel, Sen Wang
This series adds asynchronous mode support to the McASP driver, which
enables independent configuration of bitclocks, frame sync, and audio
configurations between tx(playback) and rx(record). And achieves
simultaneous playback & record using different audio configurations.
It also adds two clean up patches to the McASP driver that disambiguate
and simplifies the logic which avoids the async enhancement from being
too convoluted to review and analyze.
The implementation is based on vendor documentation and patches tested in
both SK-AM62P-LP (sync mode, McASP slave) and AM62D-EVM
(async mode, McASP master, rx & tx has different TDM configs).
Testing verifies async mode functionality while maintaining backward
compatibility with the default sync mode.
Bootlog and Async mode tests on AM62D-EVM: [0]
[0]: https://gist.github.com/SenWang125/f31f9172b186d414695e37c8b9ef127d
Signed-off-by: Sen Wang <sen@ti.com>
Sen Wang (4):
dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode
ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function
ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams
ASoC: ti: davinci-mcasp: Add asynchronous mode support
.../bindings/sound/davinci-mcasp-audio.yaml | 71 ++-
include/linux/platform_data/davinci_asp.h | 3 +-
sound/soc/ti/davinci-mcasp.c | 510 ++++++++++++++----
sound/soc/ti/davinci-mcasp.h | 10 +
4 files changed, 479 insertions(+), 115 deletions(-)
base-commit: dbf8fe85a16a33d6b6bd01f2bc606fc017771465
--
2.43.0
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH 1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode 2026-01-30 5:10 [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Sen Wang @ 2026-01-30 5:10 ` Sen Wang 2026-02-02 12:44 ` Mark Brown 2026-01-30 5:10 ` [PATCH 2/4] ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function Sen Wang ` (3 subsequent siblings) 4 siblings, 1 reply; 13+ messages in thread From: Sen Wang @ 2026-01-30 5:10 UTC (permalink / raw) To: peter.ujfalusi, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel, Sen Wang McASP supports the independent configuration of TX & RX clk and frame sync registers. By default, the driver is configured in synchronous mode where RX clock generator is disabled and it uses transmit clock signals as bit clock and frame sync. Therefore add optional properties needed for asynchronous mode. Add ti,async-mode boolean binding to provide a way to decouple the default behavior and allows for independent TX & RX clocking. Add tdm-slots-rx uint32 binding to provide an alternative hardware specifier stating the number of RX serializers. The existing property tdm-slots will still dictate number of TX serializers, and RX if tdm-slots-rx isn't given for backwards compatibility. Add auxclk-fs-ratio-rx which allows to specify the ratio just for RX. The driver can be supplied with two different ratios (auxclk-fs-ratio and auxclk-fs-ratio-rx in tandem) and achieve two different sampling rates for tx & rx. Signed-off-by: Sen Wang <sen@ti.com> --- .../bindings/sound/davinci-mcasp-audio.yaml | 71 +++++++++++++++++-- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml index beef193aaaeb..87559d0d079a 100644 --- a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml +++ b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml @@ -40,11 +40,33 @@ properties: tdm-slots: $ref: /schemas/types.yaml#/definitions/uint32 description: - number of channels over one serializer - the property is ignored in DIT mode + Number of channels over one serializer. This property + specifies the TX playback TDM slot count, along with default RX slot count + if tdm-slots-rx is not specified. + The property is ignored in DIT mode. minimum: 2 maximum: 32 + tdm-slots-rx: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Number of RX capture channels over one serializer. If specified, + allows independent RX TDM slot count separate from TX. Requires + ti,async-mode to be enabled for independent TX/RX clock rates. + The property is ignored in DIT mode. + minimum: 2 + maximum: 32 + + ti,async-mode: + description: + Specify to allow independent TX & RX clocking, + to enable audio playback & record with different sampling rate, + and different number of bits per frame. + if property is omitted, TX and RX will share same bit clock and frame clock signals, + thus RX need to use same bits per frame and sampling rate as TX in synchronous mode. + the property is ignored in DIT mode (as DIT is TX-only) + type: boolean + serial-dir: description: A list of serializer configuration @@ -125,7 +147,21 @@ properties: auxclk-fs-ratio: $ref: /schemas/types.yaml#/definitions/uint32 - description: ratio of AUCLK and FS rate if applicable + description: + Ratio of AUCLK and FS rate if applicable. This property specifies + the TX ratio, along with default RX ratio if auxclk-fs-ratio-rx + is not specified. + When not specified, the inputted system clock frequency via set_sysclk + callback by the machine driver is used for divider calculation. + + auxclk-fs-ratio-rx: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Ratio of AUCLK and FS rate for RX. If specified, allows + for a different RX ratio. Requires ti,async-mode to be + enabled when the ratio differs from auxclk-fs-ratio. + When not specified, it defaults to the value of auxclk-fs-ratio. + The property is ignored in DIT mode. gpio-controller: true @@ -170,14 +206,38 @@ allOf: - $ref: dai-common.yaml# - if: properties: - opmode: + op-mode: enum: - 0 - then: required: - tdm-slots + - if: + properties: + op-mode: + const: 1 + then: + properties: + tdm-slots: false + tdm-slots-rx: false + ti,async-mode: false + auxclk-fs-ratio-rx: false + + - if: + required: + - tdm-slots-rx + then: + required: + - ti,async-mode + + - if: + required: + - auxclk-fs-ratio-rx + then: + required: + - ti,async-mode + unevaluatedProperties: false examples: @@ -190,6 +250,7 @@ examples: interrupt-names = "tx", "rx"; op-mode = <0>; /* MCASP_IIS_MODE */ tdm-slots = <2>; + ti,async-mode; dmas = <&main_udmap 0xc400>, <&main_udmap 0x4400>; dma-names = "tx", "rx"; serial-dir = < -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode 2026-01-30 5:10 ` [PATCH 1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode Sen Wang @ 2026-02-02 12:44 ` Mark Brown 2026-02-02 20:18 ` Sen Wang 0 siblings, 1 reply; 13+ messages in thread From: Mark Brown @ 2026-02-02 12:44 UTC (permalink / raw) To: Sen Wang Cc: peter.ujfalusi, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt, linux-sound, devicetree, linux-kernel [-- Attachment #1: Type: text/plain, Size: 707 bytes --] On Thu, Jan 29, 2026 at 11:10:41PM -0600, Sen Wang wrote: > McASP supports the independent configuration of TX & RX clk and frame > sync registers. By default, the driver is configured in synchronous mode > where RX clock generator is disabled and it uses transmit clock signals as > bit clock and frame sync. Therefore add optional properties needed for > asynchronous mode. Please submit patches using subject lines reflecting the style for the subsystem, this makes it easier for people to identify relevant patches. Look at what existing commits in the area you're changing are doing and make sure your subject lines visually resemble what they're doing. There's no need to resubmit to fix this alone. [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode 2026-02-02 12:44 ` Mark Brown @ 2026-02-02 20:18 ` Sen Wang 0 siblings, 0 replies; 13+ messages in thread From: Sen Wang @ 2026-02-02 20:18 UTC (permalink / raw) To: Mark Brown Cc: peter.ujfalusi, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt, linux-sound, devicetree, linux-kernel On 2/2/26 06:44, Mark Brown wrote: > On Thu, Jan 29, 2026 at 11:10:41PM -0600, Sen Wang wrote: >> McASP supports the independent configuration of TX & RX clk and frame >> sync registers. By default, the driver is configured in synchronous mode >> where RX clock generator is disabled and it uses transmit clock signals as >> bit clock and frame sync. Therefore add optional properties needed for >> asynchronous mode. > > Please submit patches using subject lines reflecting the style for the > subsystem, this makes it easier for people to identify relevant patches. > Look at what existing commits in the area you're changing are doing and > make sure your subject lines visually resemble what they're doing. > There's no need to resubmit to fix this alone. Understood, my apologies for the malformed subject line. I picked a bad cherry to follow. Will ensure future patches align with the norm. Thank you for the catch. Best, Sen Wang ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 2/4] ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function 2026-01-30 5:10 [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Sen Wang 2026-01-30 5:10 ` [PATCH 1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode Sen Wang @ 2026-01-30 5:10 ` Sen Wang 2026-02-02 16:42 ` Péter Ujfalusi 2026-01-30 5:10 ` [PATCH 3/4] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams Sen Wang ` (2 subsequent siblings) 4 siblings, 1 reply; 13+ messages in thread From: Sen Wang @ 2026-01-30 5:10 UTC (permalink / raw) To: peter.ujfalusi, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel, Sen Wang The current mcasp_is_synchronous() function does more than what it proclaims, it also checks if McASP is a frame producer. Therefore split the original function into two separate ones and replace all occurrences with the new equivalent logic. So the functions can be re-used when checking async/sync status in light of async mode enhancements. Signed-off-by: Sen Wang <sen@ti.com> --- sound/soc/ti/davinci-mcasp.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 621a9d5f9377..aa14fc1c8011 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -179,10 +179,16 @@ static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val) static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) { - u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); - return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; + return !(aclkxctl & TX_ASYNC); +} + +static bool mcasp_is_frame_producer(struct davinci_mcasp *mcasp) +{ + u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); + + return rxfmctl & AFSRE; } static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) @@ -226,7 +232,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) * synchronously from the transmit clock and frame sync. We need to make * sure that the TX signlas are enabled when starting reception. */ - if (mcasp_is_synchronous(mcasp)) { + 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); @@ -239,7 +245,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); /* Release Frame Sync generator */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); - if (mcasp_is_synchronous(mcasp)) + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); /* enable receive IRQs */ @@ -305,7 +311,7 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) * In synchronous mode stop the TX clocks if no other stream is * running */ - if (mcasp_is_synchronous(mcasp) && !mcasp->streams) { + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) { mcasp_set_clk_pdir(mcasp, false); mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); } @@ -332,7 +338,7 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) * In synchronous mode keep TX clocks running if the capture stream is * still running. */ - if (mcasp_is_synchronous(mcasp) && mcasp->streams) + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) val = TXHCLKRST | TXCLKRST | TXFSRST; else mcasp_set_clk_pdir(mcasp, false); @@ -1041,7 +1047,8 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, * not running already we need to configure the TX slots in * order to have correct FSX on the bus */ - if (mcasp_is_synchronous(mcasp) && !mcasp->channels) + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && + !mcasp->channels) mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXMOD(total_slots), FSXMOD(0x1FF)); } -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 2/4] ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function 2026-01-30 5:10 ` [PATCH 2/4] ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function Sen Wang @ 2026-02-02 16:42 ` Péter Ujfalusi 0 siblings, 0 replies; 13+ messages in thread From: Péter Ujfalusi @ 2026-02-02 16:42 UTC (permalink / raw) To: Sen Wang, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel On 30/01/2026 07:10, Sen Wang wrote: > The current mcasp_is_synchronous() function does more than what it > proclaims, it also checks if McASP is a frame producer. True, the naming was not too precise. It is tasked to decide if the TX clock needs to be enabled for RX operation, which precisely when McASP is in synchronous mode _and_ it is clock provider. > Therefore split the original function into two separate ones and > replace all occurrences with the new equivalent logic. So the functions > can be re-used when checking async/sync status in light of async mode > enhancements. Acked-by: Peter Ujfalusi <peter.ujfalusi@gmail.com> > > Signed-off-by: Sen Wang <sen@ti.com> > --- > sound/soc/ti/davinci-mcasp.c | 21 ++++++++++++++------- > 1 file changed, 14 insertions(+), 7 deletions(-) > > diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c > index 621a9d5f9377..aa14fc1c8011 100644 > --- a/sound/soc/ti/davinci-mcasp.c > +++ b/sound/soc/ti/davinci-mcasp.c > @@ -179,10 +179,16 @@ static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val) > > static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) > { > - u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); > u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); > > - return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; > + return !(aclkxctl & TX_ASYNC); > +} > + > +static bool mcasp_is_frame_producer(struct davinci_mcasp *mcasp) > +{ > + u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); > + > + return rxfmctl & AFSRE; > } > > static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) > @@ -226,7 +232,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) > * synchronously from the transmit clock and frame sync. We need to make > * sure that the TX signlas are enabled when starting reception. > */ > - if (mcasp_is_synchronous(mcasp)) { > + 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); > @@ -239,7 +245,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) > mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); > /* Release Frame Sync generator */ > mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); > - if (mcasp_is_synchronous(mcasp)) > + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp)) > mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); > > /* enable receive IRQs */ > @@ -305,7 +311,7 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) > * In synchronous mode stop the TX clocks if no other stream is > * running > */ > - if (mcasp_is_synchronous(mcasp) && !mcasp->streams) { > + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) { > mcasp_set_clk_pdir(mcasp, false); > mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); > } > @@ -332,7 +338,7 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) > * In synchronous mode keep TX clocks running if the capture stream is > * still running. > */ > - if (mcasp_is_synchronous(mcasp) && mcasp->streams) > + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) > val = TXHCLKRST | TXCLKRST | TXFSRST; > else > mcasp_set_clk_pdir(mcasp, false); > @@ -1041,7 +1047,8 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, > * not running already we need to configure the TX slots in > * order to have correct FSX on the bus > */ > - if (mcasp_is_synchronous(mcasp) && !mcasp->channels) > + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && > + !mcasp->channels) > mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, > FSXMOD(total_slots), FSXMOD(0x1FF)); > } -- Péter ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 3/4] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams 2026-01-30 5:10 [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Sen Wang 2026-01-30 5:10 ` [PATCH 1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode Sen Wang 2026-01-30 5:10 ` [PATCH 2/4] ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function Sen Wang @ 2026-01-30 5:10 ` Sen Wang 2026-02-02 16:49 ` Péter Ujfalusi 2026-01-30 5:10 ` [PATCH 4/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support Sen Wang 2026-02-05 11:04 ` [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Mark Brown 4 siblings, 1 reply; 13+ messages in thread From: Sen Wang @ 2026-01-30 5:10 UTC (permalink / raw) To: peter.ujfalusi, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel, Sen Wang Simplify the mcasp_set_clk_pdir caller convention in start/stop stream function, to make it so that set_clk_pdir gets called regardless when stream starts and also disables when stream ends. Functionality-wise, everything remains the same as the previously skipped calls are now either correctly configured (when McASP is SND_SOC_DAIFMT_BP_FC - pdir needs to be enabled) or called with a bitmask of zero (when McASP is SND_SOC_DAIFMT_BC_FC - pdir gets disabled). On brief regarding McASP Clock and Frame sync configurations, refer to [0]. [0]:TRM Section 12.1.1.4.2 https://www.ti.com/lit/ug/sprujd4a/sprujd4a.pdf Signed-off-by: Sen Wang <sen@ti.com> --- sound/soc/ti/davinci-mcasp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index aa14fc1c8011..4f8a2ce6ce78 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -235,8 +235,8 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) 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); } + mcasp_set_clk_pdir(mcasp, true); /* Activate serializer(s) */ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -311,10 +311,10 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) * In synchronous mode stop the TX clocks if no other stream is * running */ - if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) { - mcasp_set_clk_pdir(mcasp, false); + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); - } + if (!mcasp->streams) + mcasp_set_clk_pdir(mcasp, false); mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -340,7 +340,7 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) val = TXHCLKRST | TXCLKRST | TXFSRST; - else + if (!mcasp->streams) mcasp_set_clk_pdir(mcasp, false); -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 3/4] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams 2026-01-30 5:10 ` [PATCH 3/4] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams Sen Wang @ 2026-02-02 16:49 ` Péter Ujfalusi 2026-02-02 21:22 ` Sen Wang 0 siblings, 1 reply; 13+ messages in thread From: Péter Ujfalusi @ 2026-02-02 16:49 UTC (permalink / raw) To: Sen Wang, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel On 30/01/2026 07:10, Sen Wang wrote: > Simplify the mcasp_set_clk_pdir caller convention in start/stop stream > function, to make it so that set_clk_pdir gets called regardless when > stream starts and also disables when stream ends. > > Functionality-wise, everything remains the same as the previously skipped > calls are now either correctly configured > (when McASP is SND_SOC_DAIFMT_BP_FC - pdir needs to be enabled) > or called with a bitmask of zero (when McASP is SND_SOC_DAIFMT_BC_FC - pdir > gets disabled). > > On brief regarding McASP Clock and Frame sync configurations, refer to [0]. > > [0]:TRM Section 12.1.1.4.2 https://www.ti.com/lit/ug/sprujd4a/sprujd4a.pdf > > Signed-off-by: Sen Wang <sen@ti.com> > --- > sound/soc/ti/davinci-mcasp.c | 10 +++++----- > 1 file changed, 5 insertions(+), 5 deletions(-) > > diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c > index aa14fc1c8011..4f8a2ce6ce78 100644 > --- a/sound/soc/ti/davinci-mcasp.c > +++ b/sound/soc/ti/davinci-mcasp.c > @@ -235,8 +235,8 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) > 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); > } > + mcasp_set_clk_pdir(mcasp, true); > > /* Activate serializer(s) */ > mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); > @@ -311,10 +311,10 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) > * In synchronous mode stop the TX clocks if no other stream is > * running > */ > - if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) { > - mcasp_set_clk_pdir(mcasp, false); > + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) > mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); > - } > + if (!mcasp->streams) > + mcasp_set_clk_pdir(mcasp, false); I'm not sure about this, but the sequence should be preserved, PDIR change first. > > mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); > mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); > @@ -340,7 +340,7 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) > */ > if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) > val = TXHCLKRST | TXCLKRST | TXFSRST; > - else > + if (!mcasp->streams) > mcasp_set_clk_pdir(mcasp, false); > > -- Péter ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/4] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams 2026-02-02 16:49 ` Péter Ujfalusi @ 2026-02-02 21:22 ` Sen Wang 0 siblings, 0 replies; 13+ messages in thread From: Sen Wang @ 2026-02-02 21:22 UTC (permalink / raw) To: Péter Ujfalusi, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel On 2/2/26 10:49, Péter Ujfalusi wrote: > > > On 30/01/2026 07:10, Sen Wang wrote: >> Simplify the mcasp_set_clk_pdir caller convention in start/stop stream >> function, to make it so that set_clk_pdir gets called regardless when >> stream starts and also disables when stream ends. >> >> Functionality-wise, everything remains the same as the previously skipped >> calls are now either correctly configured >> (when McASP is SND_SOC_DAIFMT_BP_FC - pdir needs to be enabled) >> or called with a bitmask of zero (when McASP is SND_SOC_DAIFMT_BC_FC - pdir >> gets disabled). >> >> On brief regarding McASP Clock and Frame sync configurations, refer to [0]. >> >> [0]:TRM Section 12.1.1.4.2 https://www.ti.com/lit/ug/sprujd4a/sprujd4a.pdf >> >> Signed-off-by: Sen Wang <sen@ti.com> >> --- >> sound/soc/ti/davinci-mcasp.c | 10 +++++----- >> 1 file changed, 5 insertions(+), 5 deletions(-) >> >> diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c >> index aa14fc1c8011..4f8a2ce6ce78 100644 >> --- a/sound/soc/ti/davinci-mcasp.c >> +++ b/sound/soc/ti/davinci-mcasp.c >> @@ -235,8 +235,8 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) >> 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); >> } >> + mcasp_set_clk_pdir(mcasp, true); >> >> /* Activate serializer(s) */ >> mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); >> @@ -311,10 +311,10 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) >> * In synchronous mode stop the TX clocks if no other stream is >> * running >> */ >> - if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) { >> - mcasp_set_clk_pdir(mcasp, false); >> + if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) >> mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); >> - } >> + if (!mcasp->streams) >> + mcasp_set_clk_pdir(mcasp, false); > > I'm not sure about this, but the sequence should be preserved, PDIR > change first. > Sounds good, I'll enforce the original order of operation in the new patch. >> >> mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); >> mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); >> @@ -340,7 +340,7 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) >> */ >> if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && mcasp->streams) >> val = TXHCLKRST | TXCLKRST | TXFSRST; >> - else >> + if (!mcasp->streams) >> mcasp_set_clk_pdir(mcasp, false); >> >> > Best, Sen Wang ^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 4/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support 2026-01-30 5:10 [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Sen Wang ` (2 preceding siblings ...) 2026-01-30 5:10 ` [PATCH 3/4] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams Sen Wang @ 2026-01-30 5:10 ` Sen Wang 2026-02-02 17:02 ` Péter Ujfalusi 2026-02-05 11:04 ` [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Mark Brown 4 siblings, 1 reply; 13+ messages in thread From: Sen Wang @ 2026-01-30 5:10 UTC (permalink / raw) To: peter.ujfalusi, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel, Sen Wang McASP has dedicated clock & frame sync registers for both transmit and receive. Currently McASP driver only supports synchronous behavior and couples both TX & RX settings. Add logic that enables asynchronous mode via ti,async-mode property. In async mode, playback & record can be done simultaneously with different audio configurations (tdm slots, tdm width, audio bit depth). Note the ability to have different tx/rx DSP formats (i2s, dsp_a, etc.), while possible in hardware, remains to be a gap as it require changes to the corresponding machine driver interface. Existing IIS (sync mode) and DIT mode logic remains mostly unchanged. Exceptions are IIS mode logic that previously assumed sync mode, which has now been made aware of the distinction. And shared logic across all modes also now checks for McASP tx/rx-specific driver attributes. Those attributes have been populated according to the original extent, ensuring no divergence in functionality. Constraints no longer applicable for async mode are skipped. Clock selection options have also been added to include rx/tx-only clk_ids, exposing independent configuration via the machine driver as well. Note that asynchronous mode is not applicable for McASP in DIT mode, which is a transmitter-only mode to interface w/ self-clocking formats. Signed-off-by: Sen Wang <sen@ti.com> --- include/linux/platform_data/davinci_asp.h | 3 +- sound/soc/ti/davinci-mcasp.c | 487 +++++++++++++++++----- sound/soc/ti/davinci-mcasp.h | 10 + 3 files changed, 398 insertions(+), 102 deletions(-) diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index b9c8520b4bd3..509c5592aab0 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -59,7 +59,8 @@ struct davinci_mcasp_pdata { bool i2s_accurate_sck; /* McASP specific fields */ - int tdm_slots; + int tdm_slots_tx; + int tdm_slots_rx; u8 op_mode; u8 dismod; u8 num_serializer; diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 4f8a2ce6ce78..ef7fa23d30bf 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -70,6 +70,7 @@ struct davinci_mcasp_context { struct davinci_mcasp_ruledata { struct davinci_mcasp *mcasp; int serializers; + int stream; }; struct davinci_mcasp { @@ -87,21 +88,27 @@ 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 */ @@ -203,6 +210,27 @@ static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) } } +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; @@ -215,6 +243,36 @@ static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) } } +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 */ @@ -230,13 +288,17 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) /* * 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. + * Else set pin to be output when McASP is the master */ 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); @@ -267,7 +329,10 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) /* 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); @@ -310,11 +375,14 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) /* * In synchronous mode stop the TX clocks if no other stream is * running + * Otherwise in async mode only stop RX clocks */ if (mcasp_is_frame_producer(mcasp) && mcasp_is_synchronous(mcasp) && !mcasp->streams) mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); - 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); mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -337,11 +405,14 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) /* * 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); @@ -353,7 +424,8 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); } - mcasp_set_axr_pdir(mcasp, false); + if (!mcasp->streams) + mcasp_set_axr_pdir(mcasp, false); } static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) @@ -625,13 +697,39 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, 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: @@ -645,11 +743,33 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, * 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 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 tdm slots", - __func__, div, mcasp->tdm_slots); + "%s(): BCLK/LRCLK %d is not divisible by %d rx tdm slots", + __func__, div, mcasp->tdm_slots_rx); break; default: @@ -683,6 +803,20 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, 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, @@ -690,22 +824,56 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, 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; @@ -717,9 +885,11 @@ static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, { 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]); @@ -784,27 +954,42 @@ static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, 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 @@ -827,17 +1012,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, 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) @@ -850,10 +1041,9 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, 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; } @@ -864,11 +1054,13 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, 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; @@ -996,7 +1188,7 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, 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 @@ -1027,7 +1219,10 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, 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; @@ -1126,16 +1321,33 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, 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++; @@ -1165,10 +1377,10 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, 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; @@ -1219,6 +1431,7 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, 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: @@ -1259,22 +1472,26 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, * 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, @@ -1291,9 +1508,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, 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; @@ -1337,7 +1555,7 @@ static int davinci_mcasp_hw_rule_slot_width(struct snd_pcm_hw_params *params, 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)) { @@ -1387,12 +1605,15 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, 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; @@ -1402,16 +1623,17 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, 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]; @@ -1437,30 +1659,34 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, 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++; @@ -1497,7 +1723,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, &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 */ @@ -1506,6 +1732,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, 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]); @@ -1527,6 +1755,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, } 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 @@ -1534,9 +1763,13 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, * 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 @@ -1553,10 +1786,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, 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, @@ -1565,8 +1798,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, 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, @@ -1581,7 +1813,8 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, * 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, @@ -1758,8 +1991,6 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = { .formats = DAVINCI_MCASP_PCM_FMTS, }, .ops = &davinci_mcasp_dai_ops, - - .symmetric_rate = 1, }, { .name = "davinci-mcasp.1", @@ -1921,18 +2152,33 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, 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) { @@ -1958,8 +2204,15 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, 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) { @@ -1988,19 +2241,51 @@ static int davinci_mcasp_get_config(struct davinci_mcasp *mcasp, 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 = pdata->tdm_slots; + 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_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; diff --git a/sound/soc/ti/davinci-mcasp.h b/sound/soc/ti/davinci-mcasp.h index 5de2b8a31061..4eba8c918c5f 100644 --- a/sound/soc/ti/davinci-mcasp.h +++ b/sound/soc/ti/davinci-mcasp.h @@ -298,10 +298,20 @@ /* Source of High-frequency transmit/receive clock */ #define MCASP_CLK_HCLK_AHCLK 0 /* AHCLKX/R */ #define MCASP_CLK_HCLK_AUXCLK 1 /* Internal functional clock */ +#define MCASP_CLK_HCLK_AHCLK_TXONLY 2 /* AHCLKX for TX only */ +#define MCASP_CLK_HCLK_AHCLK_RXONLY 3 /* AHCLKR for RX only */ +#define MCASP_CLK_HCLK_AUXCLK_TXONLY 4 /* AUXCLK for TX only */ +#define MCASP_CLK_HCLK_AUXCLK_RXONLY 5 /* AUXCLK for RX only */ /* clock divider IDs */ #define MCASP_CLKDIV_AUXCLK 0 /* HCLK divider from AUXCLK */ #define MCASP_CLKDIV_BCLK 1 /* BCLK divider from HCLK */ #define MCASP_CLKDIV_BCLK_FS_RATIO 2 /* to set BCLK FS ration */ +#define MCASP_CLKDIV_AUXCLK_TXONLY 3 /* AUXCLK divider for TX only */ +#define MCASP_CLKDIV_AUXCLK_RXONLY 4 /* AUXCLK divider for RX only */ +#define MCASP_CLKDIV_BCLK_TXONLY 5 /* BCLK divider for TX only */ +#define MCASP_CLKDIV_BCLK_RXONLY 6 /* BCLK divider for RX only */ +#define MCASP_CLKDIV_BCLK_FS_RATIO_TXONLY 7 /* BCLK/FS ratio for TX only */ +#define MCASP_CLKDIV_BCLK_FS_RATIO_RXONLY 8 /* BCLK/FS ratio for RX only */ #endif /* DAVINCI_MCASP_H */ -- 2.43.0 ^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support 2026-01-30 5:10 ` [PATCH 4/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support Sen Wang @ 2026-02-02 17:02 ` Péter Ujfalusi 2026-02-02 21:47 ` Sen Wang 0 siblings, 1 reply; 13+ messages in thread From: Péter Ujfalusi @ 2026-02-02 17:02 UTC (permalink / raw) To: Sen Wang, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel On 30/01/2026 07:10, Sen Wang wrote: > McASP has dedicated clock & frame sync registers for both transmit > and receive. Currently McASP driver only supports synchronous behavior and > couples both TX & RX settings. > > Add logic that enables asynchronous mode via ti,async-mode property. In > async mode, playback & record can be done simultaneously with different > audio configurations (tdm slots, tdm width, audio bit depth). > > Note the ability to have different tx/rx DSP formats (i2s, dsp_a, etc.), > while possible in hardware, remains to be a gap as it require changes > to the corresponding machine driver interface. > > Existing IIS (sync mode) and DIT mode logic remains mostly unchanged. > Exceptions are IIS mode logic that previously assumed sync mode, which has > now been made aware of the distinction. And shared logic across all modes > also now checks for McASP tx/rx-specific driver attributes. Those > attributes have been populated according to the original extent, ensuring > no divergence in functionality. > > Constraints no longer applicable for async mode are skipped. > Clock selection options have also been added to include rx/tx-only clk_ids, > exposing independent configuration via the machine driver as well. > > Note that asynchronous mode is not applicable for McASP in DIT mode, > which is a transmitter-only mode to interface w/ self-clocking formats. > > Signed-off-by: Sen Wang <sen@ti.com> > --- > include/linux/platform_data/davinci_asp.h | 3 +- > sound/soc/ti/davinci-mcasp.c | 487 +++++++++++++++++----- > sound/soc/ti/davinci-mcasp.h | 10 + > 3 files changed, 398 insertions(+), 102 deletions(-) > > diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c > static void mcasp_start_rx(struct davinci_mcasp *mcasp) > { > if (mcasp->rxnumevt) { /* enable FIFO */ > @@ -230,13 +288,17 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) > /* > * 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. > + * Else set pin to be output when McASP is the master In new code - while it might not match with old code - use producer instead of master. Otherwise it looks nice, I trust you have tested the sync and DIT mode. With this nitpick addressed: Acked-by: Peter Ujfalusi <peter.ujfalusi@gmail.com> -- Péter ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support 2026-02-02 17:02 ` Péter Ujfalusi @ 2026-02-02 21:47 ` Sen Wang 0 siblings, 0 replies; 13+ messages in thread From: Sen Wang @ 2026-02-02 21:47 UTC (permalink / raw) To: Péter Ujfalusi, broonie, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt Cc: linux-sound, devicetree, linux-kernel On 2/2/26 11:02, Péter Ujfalusi wrote: > > > On 30/01/2026 07:10, Sen Wang wrote: >> McASP has dedicated clock & frame sync registers for both transmit >> and receive. Currently McASP driver only supports synchronous behavior and >> couples both TX & RX settings. >> >> Add logic that enables asynchronous mode via ti,async-mode property. In >> async mode, playback & record can be done simultaneously with different >> audio configurations (tdm slots, tdm width, audio bit depth). >> >> Note the ability to have different tx/rx DSP formats (i2s, dsp_a, etc.), >> while possible in hardware, remains to be a gap as it require changes >> to the corresponding machine driver interface. >> >> Existing IIS (sync mode) and DIT mode logic remains mostly unchanged. >> Exceptions are IIS mode logic that previously assumed sync mode, which has >> now been made aware of the distinction. And shared logic across all modes >> also now checks for McASP tx/rx-specific driver attributes. Those >> attributes have been populated according to the original extent, ensuring >> no divergence in functionality. >> >> Constraints no longer applicable for async mode are skipped. >> Clock selection options have also been added to include rx/tx-only clk_ids, >> exposing independent configuration via the machine driver as well. >> >> Note that asynchronous mode is not applicable for McASP in DIT mode, >> which is a transmitter-only mode to interface w/ self-clocking formats. >> >> Signed-off-by: Sen Wang <sen@ti.com> >> --- >> include/linux/platform_data/davinci_asp.h | 3 +- >> sound/soc/ti/davinci-mcasp.c | 487 +++++++++++++++++----- >> sound/soc/ti/davinci-mcasp.h | 10 + >> 3 files changed, 398 insertions(+), 102 deletions(-) >> >> diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c >> > > static void mcasp_start_rx(struct davinci_mcasp *mcasp) >> { >> if (mcasp->rxnumevt) { /* enable FIFO */ >> @@ -230,13 +288,17 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) >> /* >> * 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. >> + * Else set pin to be output when McASP is the master > > In new code - while it might not match with old code - use producer > instead of master. > > Otherwise it looks nice, I trust you have tested the sync and DIT mode. > > With this nitpick addressed: > Acked-by: Peter Ujfalusi <peter.ujfalusi@gmail.com> > Hi Péter, thanks for your review I'll use inclusive terminology (producer) instead. I've functionally tested IIS sync mode. For DIT mode, since I don't have hardware available, I've only done a register dump in comparison, during idle, playback & shutdown - all registers in effect stay unchanged. Will post a V2 shortly with all the corrections. Best, Sen Wang ^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP 2026-01-30 5:10 [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Sen Wang ` (3 preceding siblings ...) 2026-01-30 5:10 ` [PATCH 4/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support Sen Wang @ 2026-02-05 11:04 ` Mark Brown 4 siblings, 0 replies; 13+ messages in thread From: Mark Brown @ 2026-02-05 11:04 UTC (permalink / raw) To: peter.ujfalusi, lgirdwood, perex, tiwai, robh, krzk+dt, conor+dt, Sen Wang Cc: linux-sound, devicetree, linux-kernel On Thu, 29 Jan 2026 23:10:40 -0600, Sen Wang wrote: > This series adds asynchronous mode support to the McASP driver, which > enables independent configuration of bitclocks, frame sync, and audio > configurations between tx(playback) and rx(record). And achieves > simultaneous playback & record using different audio configurations. > > It also adds two clean up patches to the McASP driver that disambiguate > and simplifies the logic which avoids the async enhancement from being > too convoluted to review and analyze. > > [...] Applied to https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next Thanks! [1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode (no commit info) [2/4] ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function commit: e683cb088fdcbdc86fc30008319312cc0bb80226 [3/4] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams commit: 016efcaa470cdbc658df46d968d875f6a1cf9a78 [4/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support commit: 9db327083f7e0da702e2ec0169f8a34f3576f371 All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark ^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-02-05 11:04 UTC | newest] Thread overview: 13+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-01-30 5:10 [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Sen Wang 2026-01-30 5:10 ` [PATCH 1/4] dt-bindings: sound: davinci-mcasp: Add optional properties for asynchronous mode Sen Wang 2026-02-02 12:44 ` Mark Brown 2026-02-02 20:18 ` Sen Wang 2026-01-30 5:10 ` [PATCH 2/4] ASoC: ti: davinci-mcasp: Disambiguate mcasp_is_synchronous function Sen Wang 2026-02-02 16:42 ` Péter Ujfalusi 2026-01-30 5:10 ` [PATCH 3/4] ASoC: ti: davinci-mcasp: Streamline pdir behavior across rx & tx streams Sen Wang 2026-02-02 16:49 ` Péter Ujfalusi 2026-02-02 21:22 ` Sen Wang 2026-01-30 5:10 ` [PATCH 4/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support Sen Wang 2026-02-02 17:02 ` Péter Ujfalusi 2026-02-02 21:47 ` Sen Wang 2026-02-05 11:04 ` [PATCH 0/4] ASoC: ti: davinci-mcasp: Add asynchronous mode support for McASP Mark Brown
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox