* [PATCH] crypto/nx: add missing call to of_node_put()
From: Yang Li @ 2021-02-25 8:56 UTC (permalink / raw)
To: herbert; +Cc: linux-kernel, paulus, linux-crypto, Yang Li, linuxppc-dev, davem
In one of the error paths of the for_each_child_of_node() loop,
add missing call to of_node_put().
Fix the following coccicheck warning:
./drivers/crypto/nx/nx-common-powernv.c:927:1-23: WARNING: Function
"for_each_child_of_node" should have of_node_put() before return around
line 936.
Reported-by: Abaci Robot <abaci@linux.alibaba.com>
Signed-off-by: Yang Li <yang.lee@linux.alibaba.com>
---
drivers/crypto/nx/nx-common-powernv.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/crypto/nx/nx-common-powernv.c b/drivers/crypto/nx/nx-common-powernv.c
index 13c65de..b43c457 100644
--- a/drivers/crypto/nx/nx-common-powernv.c
+++ b/drivers/crypto/nx/nx-common-powernv.c
@@ -933,6 +933,7 @@ static int __init nx_powernv_probe_vas(struct device_node *pn)
NX_CT_GZIP, "ibm,p9-nx-gzip", &ct_gzip);
if (ret)
+ of_node_put(dn);
return ret;
}
--
1.8.3.1
^ permalink raw reply related
* Re: [PATCH 1/2] powerpc/perf: Infrastructure to support checking of attr.config*
From: Madhavan Srinivasan @ 2021-02-25 6:39 UTC (permalink / raw)
To: Paul A. Clarke; +Cc: Alexey Kardashevskiy, linuxppc-dev
In-Reply-To: <20210224144758.GA566251@li-24c3614c-2adc-11b2-a85c-85f334518bdb.ibm.com>
On 2/24/21 8:17 PM, Paul A. Clarke wrote:
> On Wed, Feb 24, 2021 at 07:58:39PM +0530, Madhavan Srinivasan wrote:
>> Introduce code to support the checking of attr.config* for
>> values which are reserved for a given platform.
>> Performance Monitoring Unit (PMU) configuration registers
>> have fileds that are reserved and specific values to bit fields
> s/fileds/fields/
>
>> as reserved. Writing a none zero values in these fields
> Should the previous sentences say something like "required values
> for specific bit fields" or "specific bit fields that are reserved"?
>
> s/none zero/non-zero/
>
>> or writing invalid value to bit fields will have unknown
>> behaviours.
>>
>> Patch here add a generic call-back function "check_attr_config"
> s/add/adds/ or "This patch adds ..." or just "Add ...".
Thanks for the review. Will fix it.
>
>> in "struct power_pmu", to be called in event_init to
>> check for attr.config* values for a given platform.
>> "check_attr_config" is valid only for raw event type.
>>
>> Suggested-by: Alexey Kardashevskiy <aik@ozlabs.ru>
>> Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
>> ---
>> arch/powerpc/include/asm/perf_event_server.h | 6 ++++++
>> arch/powerpc/perf/core-book3s.c | 12 ++++++++++++
>> 2 files changed, 18 insertions(+)
>>
>> diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h
>> index 00e7e671bb4b..dde97d7d9253 100644
>> --- a/arch/powerpc/include/asm/perf_event_server.h
>> +++ b/arch/powerpc/include/asm/perf_event_server.h
>> @@ -67,6 +67,12 @@ struct power_pmu {
>> * the pmu supports extended perf regs capability
>> */
>> int capabilities;
>> + /*
>> + * Function to check event code for values which are
>> + * reserved. Function takes struct perf_event as input,
>> + * since event code could be spread in attr.config*
>> + */
>> + int (*check_attr_config)(struct perf_event *ev);
>> };
>>
>> /*
>> diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
>> index 6817331e22ff..679d67506299 100644
>> --- a/arch/powerpc/perf/core-book3s.c
>> +++ b/arch/powerpc/perf/core-book3s.c
>> @@ -1958,6 +1958,18 @@ static int power_pmu_event_init(struct perf_event *event)
>>
>> if (ppmu->blacklist_ev && is_event_blacklisted(ev))
>> return -EINVAL;
>> + /*
>> + * PMU config registers have fileds that are
>> + * reserved and spacific values to bit fileds be reserved.
> s/spacific/specific/
> s/fileds/fields/
> Same comment about "specific values to bit fields be reserved", and
> rewording that to be more clear.
>
>> + * This call-back will check the event code for same.
>> + *
>> + * Event type hardware and hw_cache will not value
>> + * invalid values in the event code which is not true
>> + * for raw event type.
> I confess I don't understand what this means. (But it could be just me!)
My bad. What I wanted to say was, this check is needed only
for raw event type, since tools like fuzzer use it to provide
randomized event code values for test. Will fix the comment
Thanks for the review comments.
>
>> + */
>> + if (ppmu->check_attr_config &&
>> + ppmu->check_attr_config(event))
>> + return -EINVAL;
>> break;
>> default:
>> return -ENOENT;
>> --
> PC
^ permalink raw reply
* [PATCH] selftests/powerpc: Add uaccess flush test
From: Daniel Axtens @ 2021-02-25 6:19 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Thadeu Lima de Souza Cascardo, Daniel Axtens
From: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
Also based on the RFI and entry flush tests, it counts the L1D misses
by doing a syscall that does user access: uname, in this case.
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
[dja: forward port, rename function]
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
This applies on top of Russell's change to use better constants:
https://patchwork.ozlabs.org/project/linuxppc-dev/patch/20210223070227.2916871-1-ruscur@russell.cc/
It's possible that we could share some more code between the tests, but
it hardly seems worth it.
---
.../selftests/powerpc/security/Makefile | 3 +-
.../selftests/powerpc/security/flush_utils.c | 13 ++
.../selftests/powerpc/security/flush_utils.h | 3 +
.../powerpc/security/uaccess_flush.c | 158 ++++++++++++++++++
4 files changed, 176 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/powerpc/security/uaccess_flush.c
diff --git a/tools/testing/selftests/powerpc/security/Makefile b/tools/testing/selftests/powerpc/security/Makefile
index f25e854fe370..844d18cd5f93 100644
--- a/tools/testing/selftests/powerpc/security/Makefile
+++ b/tools/testing/selftests/powerpc/security/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0+
-TEST_GEN_PROGS := rfi_flush entry_flush spectre_v2
+TEST_GEN_PROGS := rfi_flush entry_flush uaccess_flush spectre_v2
top_srcdir = ../../../../..
CFLAGS += -I../../../../../usr/include
@@ -13,3 +13,4 @@ $(OUTPUT)/spectre_v2: CFLAGS += -m64
$(OUTPUT)/spectre_v2: ../pmu/event.c branch_loops.S
$(OUTPUT)/rfi_flush: flush_utils.c
$(OUTPUT)/entry_flush: flush_utils.c
+$(OUTPUT)/uaccess_flush: flush_utils.c
diff --git a/tools/testing/selftests/powerpc/security/flush_utils.c b/tools/testing/selftests/powerpc/security/flush_utils.c
index 0c3c4c40c7fb..4d95965cb751 100644
--- a/tools/testing/selftests/powerpc/security/flush_utils.c
+++ b/tools/testing/selftests/powerpc/security/flush_utils.c
@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
+#include <sys/utsname.h>
#include "utils.h"
#include "flush_utils.h"
@@ -35,6 +36,18 @@ void syscall_loop(char *p, unsigned long iterations,
}
}
+void syscall_loop_uaccess(char *p, unsigned long iterations,
+ unsigned long zero_size)
+{
+ struct utsname utsname;
+
+ for (unsigned long i = 0; i < iterations; i++) {
+ for (unsigned long j = 0; j < zero_size; j += CACHELINE_SIZE)
+ load(p + j);
+ uname(&utsname);
+ }
+}
+
static void sigill_handler(int signr, siginfo_t *info, void *unused)
{
static int warned;
diff --git a/tools/testing/selftests/powerpc/security/flush_utils.h b/tools/testing/selftests/powerpc/security/flush_utils.h
index 7a3d60292916..e1e68281f7ac 100644
--- a/tools/testing/selftests/powerpc/security/flush_utils.h
+++ b/tools/testing/selftests/powerpc/security/flush_utils.h
@@ -16,6 +16,9 @@
void syscall_loop(char *p, unsigned long iterations,
unsigned long zero_size);
+void syscall_loop_uaccess(char *p, unsigned long iterations,
+ unsigned long zero_size);
+
void set_dscr(unsigned long val);
#endif /* _SELFTESTS_POWERPC_SECURITY_FLUSH_UTILS_H */
diff --git a/tools/testing/selftests/powerpc/security/uaccess_flush.c b/tools/testing/selftests/powerpc/security/uaccess_flush.c
new file mode 100644
index 000000000000..cf80f960e38a
--- /dev/null
+++ b/tools/testing/selftests/powerpc/security/uaccess_flush.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2018 IBM Corporation.
+ * Copyright 2020 Canonical Ltd.
+ */
+
+#define __SANE_USERSPACE_TYPES__
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "utils.h"
+#include "flush_utils.h"
+
+int uaccess_flush_test(void)
+{
+ char *p;
+ int repetitions = 10;
+ int fd, passes = 0, iter, rc = 0;
+ struct perf_event_read v;
+ __u64 l1d_misses_total = 0;
+ unsigned long iterations = 100000, zero_size = 24 * 1024;
+ unsigned long l1d_misses_expected;
+ int rfi_flush_orig;
+ int entry_flush_orig;
+ int uaccess_flush, uaccess_flush_orig;
+
+ SKIP_IF(geteuid() != 0);
+
+ // The PMU event we use only works on Power7 or later
+ SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
+
+ if (read_debugfs_file("powerpc/rfi_flush", &rfi_flush_orig) < 0) {
+ perror("Unable to read powerpc/rfi_flush debugfs file");
+ SKIP_IF(1);
+ }
+
+ if (read_debugfs_file("powerpc/entry_flush", &entry_flush_orig) < 0) {
+ perror("Unable to read powerpc/entry_flush debugfs file");
+ SKIP_IF(1);
+ }
+
+ if (read_debugfs_file("powerpc/uaccess_flush", &uaccess_flush_orig) < 0) {
+ perror("Unable to read powerpc/entry_flush debugfs file");
+ SKIP_IF(1);
+ }
+
+ if (rfi_flush_orig != 0) {
+ if (write_debugfs_file("powerpc/rfi_flush", 0) < 0) {
+ perror("error writing to powerpc/rfi_flush debugfs file");
+ FAIL_IF(1);
+ }
+ }
+
+ if (entry_flush_orig != 0) {
+ if (write_debugfs_file("powerpc/entry_flush", 0) < 0) {
+ perror("error writing to powerpc/entry_flush debugfs file");
+ FAIL_IF(1);
+ }
+ }
+
+ uaccess_flush = uaccess_flush_orig;
+
+ fd = perf_event_open_counter(PERF_TYPE_HW_CACHE, PERF_L1D_READ_MISS_CONFIG, -1);
+ FAIL_IF(fd < 0);
+
+ p = (char *)memalign(zero_size, CACHELINE_SIZE);
+
+ FAIL_IF(perf_event_enable(fd));
+
+ // disable L1 prefetching
+ set_dscr(1);
+
+ iter = repetitions;
+
+ /*
+ * We expect to see l1d miss for each cacheline access when entry_flush
+ * is set. Allow a small variation on this.
+ */
+ l1d_misses_expected = iterations * (zero_size / CACHELINE_SIZE - 2);
+
+again:
+ FAIL_IF(perf_event_reset(fd));
+
+ syscall_loop_uaccess(p, iterations, zero_size);
+
+ FAIL_IF(read(fd, &v, sizeof(v)) != sizeof(v));
+
+ if (uaccess_flush && v.l1d_misses >= l1d_misses_expected)
+ passes++;
+ else if (!uaccess_flush && v.l1d_misses < (l1d_misses_expected / 2))
+ passes++;
+
+ l1d_misses_total += v.l1d_misses;
+
+ while (--iter)
+ goto again;
+
+ if (passes < repetitions) {
+ printf("FAIL (L1D misses with uaccess_flush=%d: %llu %c %lu) [%d/%d failures]\n",
+ uaccess_flush, l1d_misses_total, uaccess_flush ? '<' : '>',
+ uaccess_flush ? repetitions * l1d_misses_expected :
+ repetitions * l1d_misses_expected / 2,
+ repetitions - passes, repetitions);
+ rc = 1;
+ } else {
+ printf("PASS (L1D misses with uaccess_flush=%d: %llu %c %lu) [%d/%d pass]\n",
+ uaccess_flush, l1d_misses_total, uaccess_flush ? '>' : '<',
+ uaccess_flush ? repetitions * l1d_misses_expected :
+ repetitions * l1d_misses_expected / 2,
+ passes, repetitions);
+ }
+
+ if (uaccess_flush == uaccess_flush_orig) {
+ uaccess_flush = !uaccess_flush_orig;
+ if (write_debugfs_file("powerpc/uaccess_flush", uaccess_flush) < 0) {
+ perror("error writing to powerpc/uaccess_flush debugfs file");
+ return 1;
+ }
+ iter = repetitions;
+ l1d_misses_total = 0;
+ passes = 0;
+ goto again;
+ }
+
+ perf_event_disable(fd);
+ close(fd);
+
+ set_dscr(0);
+
+ if (write_debugfs_file("powerpc/rfi_flush", rfi_flush_orig) < 0) {
+ perror("unable to restore original value of powerpc/rfi_flush debugfs file");
+ return 1;
+ }
+
+ if (write_debugfs_file("powerpc/entry_flush", entry_flush_orig) < 0) {
+ perror("unable to restore original value of powerpc/entry_flush debugfs file");
+ return 1;
+ }
+
+ if (write_debugfs_file("powerpc/uaccess_flush", uaccess_flush_orig) < 0) {
+ perror("unable to restore original value of powerpc/uaccess_flush debugfs file");
+ return 1;
+ }
+
+ return rc;
+}
+
+int main(int argc, char *argv[])
+{
+ return test_harness(uaccess_flush_test, "uaccess_flush_test");
+}
--
2.27.0
^ permalink raw reply related
* [PATCH] docs: powerpc: Fix tables in syscall64-abi.rst
From: Andrew Donnellan @ 2021-02-25 6:08 UTC (permalink / raw)
To: linuxppc-dev; +Cc: mchehab+huawei, corbet, linux-doc
Commit 209b44c804c ("docs: powerpc: syscall64-abi.rst: fix a malformed
table") attempted to fix the formatting of tables in syscall64-abi.rst, but
inadvertently changed some register names.
Redo the tables with the correct register names, and while we're here,
clean things up to separate the registers into different rows and add
headings.
Fixes: 209b44c804c ("docs: powerpc: syscall64-abi.rst: fix a malformed table")
Signed-off-by: Andrew Donnellan <ajd@linux.ibm.com>
---
Documentation/powerpc/syscall64-abi.rst | 51 ++++++++++++++++---------
1 file changed, 32 insertions(+), 19 deletions(-)
diff --git a/Documentation/powerpc/syscall64-abi.rst b/Documentation/powerpc/syscall64-abi.rst
index cf9b2857c72a..dabee3729e5a 100644
--- a/Documentation/powerpc/syscall64-abi.rst
+++ b/Documentation/powerpc/syscall64-abi.rst
@@ -46,25 +46,38 @@ stack frame LR and CR save fields are not used.
Register preservation rules
---------------------------
-Register preservation rules match the ELF ABI calling sequence with the
-following differences:
-
-+------------------------------------------------------------------------+
-| For the sc instruction, differences with the ELF ABI |
-+--------------+--------------+------------------------------------------+
-| r0 | Volatile | (System call number.) |
-| rr3 | Volatile | (Parameter 1, and return value.) |
-| rr4-r8 | Volatile | (Parameters 2-6.) |
-| rcr0 | Volatile | (cr0.SO is the return error condition.) |
-| rcr1, cr5-7 | Nonvolatile | |
-| rlr | Nonvolatile | |
-+--------------+--------------+------------------------------------------+
-| For the scv 0 instruction, differences with the ELF ABI |
-+--------------+--------------+------------------------------------------+
-| r0 | Volatile | (System call number.) |
-| r3 | Volatile | (Parameter 1, and return value.) |
-| r4-r8 | Volatile | (Parameters 2-6.) |
-+--------------+--------------+------------------------------------------+
+Register preservation rules match the ELF ABI calling sequence with some
+differences.
+
+For the sc instruction, the differences from the ELF ABI are as follows:
+
++--------------+--------------------+-----------------------------------------+
+| Register | Preservation Rules | Purpose |
++==============+====================+=========================================+
+| r0 | Volatile | (System call number.) |
++--------------+--------------------+-----------------------------------------+
+| r3 | Volatile | (Parameter 1, and return value.) |
++--------------+--------------------+-----------------------------------------+
+| r4-r8 | Volatile | (Parameters 2-6.) |
++--------------+--------------------+-----------------------------------------+
+| cr0 | Volatile | (cr0.SO is the return error condition.) |
++--------------+--------------------+-----------------------------------------+
+| cr1, cr5-7 | Nonvolatile | |
++--------------+--------------------+-----------------------------------------+
+| lr | Nonvolatile | |
++--------------+--------------------+-----------------------------------------+
+
+For the scv 0 instruction, the differences from the ELF ABI are as follows:
+
++--------------+--------------------+-----------------------------------------+
+| Register | Preservation Rules | Purpose |
++==============+====================+=========================================+
+| r0 | Volatile | (System call number.) |
++--------------+--------------------+-----------------------------------------+
+| r3 | Volatile | (Parameter 1, and return value.) |
++--------------+--------------------+-----------------------------------------+
+| r4-r8 | Volatile | (Parameters 2-6.) |
++--------------+--------------------+-----------------------------------------+
All floating point and vector data registers as well as control and status
registers are nonvolatile.
--
2.20.1
^ permalink raw reply related
* Re: [PATCH v6 07/10] powerpc/signal64: Replace restore_sigcontext() w/ unsafe_restore_sigcontext()
From: Christopher M. Riedl @ 2021-02-25 3:54 UTC (permalink / raw)
To: Christophe Leroy, linuxppc-dev
In-Reply-To: <67afcec9-6335-2bd5-938b-687ecb61bf3d@csgroup.eu>
On Tue Feb 23, 2021 at 11:36 AM CST, Christophe Leroy wrote:
>
>
> Le 21/02/2021 à 02:23, Christopher M. Riedl a écrit :
> > Previously restore_sigcontext() performed a costly KUAP switch on every
> > uaccess operation. These repeated uaccess switches cause a significant
> > drop in signal handling performance.
> >
> > Rewrite restore_sigcontext() to assume that a userspace read access
> > window is open by replacing all uaccess functions with their 'unsafe'
> > versions. Modify the callers to first open, call
> > unsafe_restore_sigcontext(), and then close the uaccess window.
> >
> > Signed-off-by: Christopher M. Riedl <cmr@codefail.de>
> > ---
> > arch/powerpc/kernel/signal_64.c | 68 ++++++++++++++++++++-------------
> > 1 file changed, 41 insertions(+), 27 deletions(-)
> >
> > diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
> > index 3faaa736ed62..76b525261f61 100644
> > --- a/arch/powerpc/kernel/signal_64.c
> > +++ b/arch/powerpc/kernel/signal_64.c
> > @@ -326,14 +326,14 @@ static long setup_tm_sigcontexts(struct sigcontext __user *sc,
> > /*
> > * Restore the sigcontext from the signal frame.
> > */
> > -
> > -static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
> > - struct sigcontext __user *sc)
> > +#define unsafe_restore_sigcontext(tsk, set, sig, sc, e) \
> > + unsafe_op_wrap(__unsafe_restore_sigcontext(tsk, set, sig, sc), e)
>
> unsafe_op_wrap() was not initially meant to be used outside of uaccess.h
>
> In the begining, it has been copied from include/linux/uaccess.h and was
> used
> for unsafe_put_user(), unsafe_get_user() and unsafe_copy_to_user().
> After other changes, only
> unsafe_get_user() is still using it and I'm going to drop
> unsafe_op_wrap() soon.
>
> I'd prefer if you can do the same as unsafe_save_general_regs() and
> others in signal_32.c
Sounds good, will change this in the next version (and also the wrapper
around unsafe_setup_sigcontext()).
>
> > +static long notrace __unsafe_restore_sigcontext(struct task_struct *tsk, sigset_t *set,
> > + int sig, struct sigcontext __user *sc)
> > {
> > #ifdef CONFIG_ALTIVEC
> > elf_vrreg_t __user *v_regs;
> > #endif
> > - unsigned long err = 0;
> > unsigned long save_r13 = 0;
> > unsigned long msr;
> > struct pt_regs *regs = tsk->thread.regs;
> > @@ -348,27 +348,28 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
> > save_r13 = regs->gpr[13];
> >
> > /* copy the GPRs */
> > - err |= __copy_from_user(regs->gpr, sc->gp_regs, sizeof(regs->gpr));
> > - err |= __get_user(regs->nip, &sc->gp_regs[PT_NIP]);
> > + unsafe_copy_from_user(regs->gpr, sc->gp_regs, sizeof(regs->gpr),
> > + efault_out);
>
> I think it would be better to keep the above on a single line for
> readability.
> Nowadays we tolerate 100 chars lines for cases like this one.
Ok, changed this (and the line you mention further below) in the next
version.
>
> > + unsafe_get_user(regs->nip, &sc->gp_regs[PT_NIP], efault_out);
> > /* get MSR separately, transfer the LE bit if doing signal return */
> > - err |= __get_user(msr, &sc->gp_regs[PT_MSR]);
> > + unsafe_get_user(msr, &sc->gp_regs[PT_MSR], efault_out);
> > if (sig)
> > regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE);
> > - err |= __get_user(regs->orig_gpr3, &sc->gp_regs[PT_ORIG_R3]);
> > - err |= __get_user(regs->ctr, &sc->gp_regs[PT_CTR]);
> > - err |= __get_user(regs->link, &sc->gp_regs[PT_LNK]);
> > - err |= __get_user(regs->xer, &sc->gp_regs[PT_XER]);
> > - err |= __get_user(regs->ccr, &sc->gp_regs[PT_CCR]);
> > + unsafe_get_user(regs->orig_gpr3, &sc->gp_regs[PT_ORIG_R3], efault_out);
> > + unsafe_get_user(regs->ctr, &sc->gp_regs[PT_CTR], efault_out);
> > + unsafe_get_user(regs->link, &sc->gp_regs[PT_LNK], efault_out);
> > + unsafe_get_user(regs->xer, &sc->gp_regs[PT_XER], efault_out);
> > + unsafe_get_user(regs->ccr, &sc->gp_regs[PT_CCR], efault_out);
> > /* Don't allow userspace to set SOFTE */
> > set_trap_norestart(regs);
> > - err |= __get_user(regs->dar, &sc->gp_regs[PT_DAR]);
> > - err |= __get_user(regs->dsisr, &sc->gp_regs[PT_DSISR]);
> > - err |= __get_user(regs->result, &sc->gp_regs[PT_RESULT]);
> > + unsafe_get_user(regs->dar, &sc->gp_regs[PT_DAR], efault_out);
> > + unsafe_get_user(regs->dsisr, &sc->gp_regs[PT_DSISR], efault_out);
> > + unsafe_get_user(regs->result, &sc->gp_regs[PT_RESULT], efault_out);
> >
> > if (!sig)
> > regs->gpr[13] = save_r13;
> > if (set != NULL)
> > - err |= __get_user(set->sig[0], &sc->oldmask);
> > + unsafe_get_user(set->sig[0], &sc->oldmask, efault_out);
> >
> > /*
> > * Force reload of FP/VEC.
> > @@ -378,29 +379,28 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
> > regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC | MSR_VSX);
> >
> > #ifdef CONFIG_ALTIVEC
> > - err |= __get_user(v_regs, &sc->v_regs);
> > - if (err)
> > - return err;
> > + unsafe_get_user(v_regs, &sc->v_regs, efault_out);
> > if (v_regs && !access_ok(v_regs, 34 * sizeof(vector128)))
> > return -EFAULT;
> > /* Copy 33 vec registers (vr0..31 and vscr) from the stack */
> > if (v_regs != NULL && (msr & MSR_VEC) != 0) {
> > - err |= __copy_from_user(&tsk->thread.vr_state, v_regs,
> > - 33 * sizeof(vector128));
> > + unsafe_copy_from_user(&tsk->thread.vr_state, v_regs,
> > + 33 * sizeof(vector128), efault_out);
> > tsk->thread.used_vr = true;
> > } else if (tsk->thread.used_vr) {
> > memset(&tsk->thread.vr_state, 0, 33 * sizeof(vector128));
> > }
> > /* Always get VRSAVE back */
> > if (v_regs != NULL)
> > - err |= __get_user(tsk->thread.vrsave, (u32 __user *)&v_regs[33]);
> > + unsafe_get_user(tsk->thread.vrsave, (u32 __user *)&v_regs[33],
> > + efault_out);
>
> Same, would be better on a single line I think.
>
> > else
> > tsk->thread.vrsave = 0;
> > if (cpu_has_feature(CPU_FTR_ALTIVEC))
> > mtspr(SPRN_VRSAVE, tsk->thread.vrsave);
> > #endif /* CONFIG_ALTIVEC */
> > /* restore floating point */
> > - err |= copy_fpr_from_user(tsk, &sc->fp_regs);
> > + unsafe_copy_fpr_from_user(tsk, &sc->fp_regs, efault_out);
> > #ifdef CONFIG_VSX
> > /*
> > * Get additional VSX data. Update v_regs to point after the
> > @@ -409,14 +409,17 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,
> > */
> > v_regs += ELF_NVRREG;
> > if ((msr & MSR_VSX) != 0) {
> > - err |= copy_vsx_from_user(tsk, v_regs);
> > + unsafe_copy_vsx_from_user(tsk, v_regs, efault_out);
> > tsk->thread.used_vsr = true;
> > } else {
> > for (i = 0; i < 32 ; i++)
> > tsk->thread.fp_state.fpr[i][TS_VSRLOWOFFSET] = 0;
> > }
> > #endif
> > - return err;
> > + return 0;
> > +
> > +efault_out:
> > + return -EFAULT;
> > }
> >
> > #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
> > @@ -707,8 +710,14 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,
> > if (__copy_from_user(&set, &new_ctx->uc_sigmask, sizeof(set)))
> > do_exit(SIGSEGV);
> > set_current_blocked(&set);
> > - if (restore_sigcontext(current, NULL, 0, &new_ctx->uc_mcontext))
> > +
> > + if (!user_read_access_begin(new_ctx, ctx_size))
> > + return -EFAULT;
> > + if (__unsafe_restore_sigcontext(current, NULL, 0, &new_ctx->uc_mcontext)) {
> > + user_read_access_end();
> > do_exit(SIGSEGV);
> > + }
> > + user_read_access_end();
> >
> > /* This returns like rt_sigreturn */
> > set_thread_flag(TIF_RESTOREALL);
> > @@ -811,8 +820,13 @@ SYSCALL_DEFINE0(rt_sigreturn)
> > * causing a TM bad thing.
> > */
> > current->thread.regs->msr &= ~MSR_TS_MASK;
> > - if (restore_sigcontext(current, NULL, 1, &uc->uc_mcontext))
> > + if (!user_read_access_begin(&uc->uc_mcontext, sizeof(uc->uc_mcontext)))
> > + return -EFAULT;
> > + if (__unsafe_restore_sigcontext(current, NULL, 1, &uc->uc_mcontext)) {
> > + user_read_access_end();
> > goto badframe;
> > + }
> > + user_read_access_end();
> > }
> >
> > if (restore_altstack(&uc->uc_stack))
> >
^ permalink raw reply
* [PATCH 3/3] powerpc/sstep: Always test lmw and stmw
From: Jordan Niethe @ 2021-02-25 3:21 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Jordan Niethe
In-Reply-To: <20210225032108.1458352-1-jniethe5@gmail.com>
Load Multiple Word (lmw) and Store Multiple Word (stmw) will raise an
Alignment Exception:
- Little Endian mode: always
- Big Endian mode: address not word aligned
These conditions do not depend on cache inhibited memory. Test the
alignment handler emulation of these instructions regardless of if there
is cache inhibited memory available or not.
Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
---
.../powerpc/alignment/alignment_handler.c | 96 ++++++++++++++++++-
1 file changed, 94 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/powerpc/alignment/alignment_handler.c b/tools/testing/selftests/powerpc/alignment/alignment_handler.c
index f5eb5b85a2cf..c3003f95e043 100644
--- a/tools/testing/selftests/powerpc/alignment/alignment_handler.c
+++ b/tools/testing/selftests/powerpc/alignment/alignment_handler.c
@@ -45,6 +45,7 @@
#include <getopt.h>
#include <setjmp.h>
#include <signal.h>
+#include <errno.h>
#include "utils.h"
#include "instructions.h"
@@ -434,7 +435,6 @@ int test_alignment_handler_integer(void)
LOAD_DFORM_TEST(ldu);
LOAD_XFORM_TEST(ldx);
LOAD_XFORM_TEST(ldux);
- LOAD_DFORM_TEST(lmw);
STORE_DFORM_TEST(stb);
STORE_XFORM_TEST(stbx);
STORE_DFORM_TEST(stbu);
@@ -453,7 +453,6 @@ int test_alignment_handler_integer(void)
STORE_XFORM_TEST(stdx);
STORE_DFORM_TEST(stdu);
STORE_XFORM_TEST(stdux);
- STORE_DFORM_TEST(stmw);
return rc;
}
@@ -599,6 +598,97 @@ int test_alignment_handler_fp_prefix(void)
return rc;
}
+int test_alignment_handler_multiple(void)
+{
+ int offset, width, r, rc = 0;
+ void *src1, *dst1, *src2, *dst2;
+
+ rc = posix_memalign(&src1, bufsize, bufsize);
+ if (rc) {
+ printf("\n");
+ return rc;
+ }
+
+ rc = posix_memalign(&dst1, bufsize, bufsize);
+ if (rc) {
+ printf("\n");
+ free(src1);
+ return rc;
+ }
+
+ src2 = malloc(bufsize);
+ if (!src2) {
+ printf("\n");
+ free(src1);
+ free(dst1);
+ return -ENOMEM;
+ }
+
+ dst2 = malloc(bufsize);
+ if (!dst2) {
+ printf("\n");
+ free(src1);
+ free(dst1);
+ free(src2);
+ return -ENOMEM;
+ }
+
+ /* lmw */
+ width = 4;
+ printf("\tDoing lmw:\t");
+ for (offset = 0; offset < width; offset++) {
+ preload_data(src1, offset, width);
+ preload_data(src2, offset, width);
+
+ asm volatile("lmw 31, 0(%0) ; std 31, 0(%1)"
+ :: "r"(src1 + offset), "r"(dst1 + offset), "r"(0)
+ : "memory", "r31");
+
+ memcpy(dst2 + offset, src1 + offset, width);
+
+ r = test_memcmp(dst1, dst2, width, offset, "test_lmw");
+ if (r && !debug) {
+ printf("FAILED: Wrong Data\n");
+ break;
+ }
+ }
+
+ if (!r)
+ printf("PASSED\n");
+ else
+ rc |= 1;
+
+ /* stmw */
+ width = 4;
+ printf("\tDoing stmw:\t");
+ for (offset = 0; offset < width; offset++) {
+ preload_data(src1, offset, width);
+ preload_data(src2, offset, width);
+
+ asm volatile("ld 31, 0(%0) ; stmw 31, 0(%1)"
+ :: "r"(src1 + offset), "r"(dst1 + offset), "r"(0)
+ : "memory", "r31");
+
+ memcpy(dst2 + offset, src1 + offset, width);
+
+ r = test_memcmp(dst1, dst2, width, offset, "test_stmw");
+ if (r && !debug) {
+ printf("FAILED: Wrong Data\n");
+ break;
+ }
+ }
+ if (!r)
+ printf("PASSED\n");
+ else
+ rc |= 1;
+
+ free(src1);
+ free(src2);
+ free(dst1);
+ free(dst2);
+ return rc;
+}
+
void usage(char *prog)
{
printf("Usage: %s [options] [path [offset]]\n", prog);
@@ -673,5 +763,7 @@ int main(int argc, char *argv[])
"test_alignment_handler_fp_206");
rc |= test_harness(test_alignment_handler_fp_prefix,
"test_alignment_handler_fp_prefix");
+ rc |= test_harness(test_alignment_handler_multiple,
+ "test_alignment_handler_multiple");
return rc;
}
--
2.25.1
^ permalink raw reply related
* [PATCH 2/3] selftests/powerpc: Suggest memtrace instead of /dev/mem for ci memory
From: Jordan Niethe @ 2021-02-25 3:21 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Jordan Niethe
In-Reply-To: <20210225032108.1458352-1-jniethe5@gmail.com>
The suggested alternative for getting cache-inhibited memory with 'mem='
and /dev/mem is pretty hacky. Also, PAPR guests do not allow system
memory to be mapped cache-inhibited so despite /dev/mem being available
this will not work which can cause confusion. Instead recommend using
the memtrace buffers. memtrace is only available on powernv so there
will not be any chance of trying to do this in a guest.
Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
---
.../selftests/powerpc/alignment/alignment_handler.c | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/tools/testing/selftests/powerpc/alignment/alignment_handler.c b/tools/testing/selftests/powerpc/alignment/alignment_handler.c
index cb53a8b777e6..f5eb5b85a2cf 100644
--- a/tools/testing/selftests/powerpc/alignment/alignment_handler.c
+++ b/tools/testing/selftests/powerpc/alignment/alignment_handler.c
@@ -10,16 +10,7 @@
*
* We create two sets of source and destination buffers, one in regular memory,
* the other cache-inhibited (by default we use /dev/fb0 for this, but an
- * alterative path for cache-inhibited memory may be provided).
- *
- * One way to get cache-inhibited memory is to use the "mem" kernel parameter
- * to limit the kernel to less memory than actually exists. Addresses above
- * the limit may still be accessed but will be treated as cache-inhibited. For
- * example, if there is actually 4GB of memory and the parameter "mem=3GB" is
- * used, memory from address 0xC0000000 onwards is treated as cache-inhibited.
- * To access this region /dev/mem is used. The kernel should be configured
- * without CONFIG_STRICT_DEVMEM. In this case use:
- * ./alignment_handler /dev/mem 0xc0000000
+ * alterative path for cache-inhibited memory may be provided, e.g. memtrace).
*
* We initialise the source buffers, then use whichever set of load/store
* instructions is under test to copy bytes from the source buffers to the
--
2.25.1
^ permalink raw reply related
* [PATCH 1/3] powernv/memtrace: Allow mmaping trace buffers
From: Jordan Niethe @ 2021-02-25 3:21 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Jordan Niethe
Let the memory removed from the linear mapping to be used for the trace
buffers be mmaped. This is a useful way of providing cache-inhibited
memory for the alignment_handler selftest.
Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
---
arch/powerpc/platforms/powernv/memtrace.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/platforms/powernv/memtrace.c b/arch/powerpc/platforms/powernv/memtrace.c
index 5fc9408bb0b3..8a1df39305e9 100644
--- a/arch/powerpc/platforms/powernv/memtrace.c
+++ b/arch/powerpc/platforms/powernv/memtrace.c
@@ -45,10 +45,26 @@ static ssize_t memtrace_read(struct file *filp, char __user *ubuf,
return simple_read_from_buffer(ubuf, count, ppos, ent->mem, ent->size);
}
+int memtrace_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+ struct memtrace_entry *ent = filp->private_data;
+
+ if (ent->size < vma->vm_end - vma->vm_start)
+ return -EINVAL;
+
+ if (vma->vm_pgoff << PAGE_SHIFT >= ent->size)
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ return remap_pfn_range(vma, vma->vm_start, PHYS_PFN(ent->start) + vma->vm_pgoff,
+ vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
static const struct file_operations memtrace_fops = {
.llseek = default_llseek,
.read = memtrace_read,
.open = simple_open,
+ .mmap = memtrace_mmap,
};
static void memtrace_clear_range(unsigned long start_pfn,
@@ -158,7 +174,7 @@ static int memtrace_init_debugfs(void)
dir = debugfs_create_dir(ent->name, memtrace_debugfs_dir);
ent->dir = dir;
- debugfs_create_file("trace", 0400, dir, ent, &memtrace_fops);
+ debugfs_create_file_unsafe("trace", 0600, dir, ent, &memtrace_fops);
debugfs_create_x64("start", 0400, dir, &ent->start);
debugfs_create_x64("size", 0400, dir, &ent->size);
}
--
2.25.1
^ permalink raw reply related
* [PATCH] powerpc/sstep: Fix VSX instruction emulation
From: Jordan Niethe @ 2021-02-25 3:19 UTC (permalink / raw)
To: linuxppc-dev; +Cc: ravi.bangoria, Jordan Niethe, bala24
Commit af99da74333b ("powerpc/sstep: Support VSX vector paired storage
access instructions") added loading and storing 32 word long data into
adjacent VSRs. However the calculation used to determine if two VSRs
needed to be loaded/stored inadvertently prevented the load/storing
taking place for instructions with a data length less than 16 words.
This causes the emulation to not function correctly, which can be seen
by the alignment_handler selftest:
$ ./alignment_handler
[snip]
test: test_alignment_handler_vsx_207
tags: git_version:powerpc-5.12-1-0-g82d2c16b350f
VSX: 2.07B
Doing lxsspx: PASSED
Doing lxsiwax: FAILED: Wrong Data
Doing lxsiwzx: PASSED
Doing stxsspx: PASSED
Doing stxsiwx: PASSED
failure: test_alignment_handler_vsx_207
test: test_alignment_handler_vsx_300
tags: git_version:powerpc-5.12-1-0-g82d2c16b350f
VSX: 3.00B
Doing lxsd: PASSED
Doing lxsibzx: PASSED
Doing lxsihzx: PASSED
Doing lxssp: FAILED: Wrong Data
Doing lxv: PASSED
Doing lxvb16x: PASSED
Doing lxvh8x: PASSED
Doing lxvx: PASSED
Doing lxvwsx: FAILED: Wrong Data
Doing lxvl: PASSED
Doing lxvll: PASSED
Doing stxsd: PASSED
Doing stxsibx: PASSED
Doing stxsihx: PASSED
Doing stxssp: PASSED
Doing stxv: PASSED
Doing stxvb16x: PASSED
Doing stxvh8x: PASSED
Doing stxvx: PASSED
Doing stxvl: PASSED
Doing stxvll: PASSED
failure: test_alignment_handler_vsx_300
[snip]
Fix this by making sure all VSX instruction emulation correctly
load/store from the VSRs.
Fixes: af99da74333b ("powerpc/sstep: Support VSX vector paired storage access instructions")
Signed-off-by: Jordan Niethe <jniethe5@gmail.com>
---
arch/powerpc/lib/sstep.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/lib/sstep.c b/arch/powerpc/lib/sstep.c
index 683f7c20f74b..3953e63bbba5 100644
--- a/arch/powerpc/lib/sstep.c
+++ b/arch/powerpc/lib/sstep.c
@@ -902,7 +902,7 @@ static nokprobe_inline int do_vsx_load(struct instruction_op *op,
if (!address_ok(regs, ea, size) || copy_mem_in(mem, ea, size, regs))
return -EFAULT;
- nr_vsx_regs = size / sizeof(__vector128);
+ nr_vsx_regs = max(1ul, size / sizeof(__vector128));
emulate_vsx_load(op, buf, mem, cross_endian);
preempt_disable();
if (reg < 32) {
@@ -949,7 +949,7 @@ static nokprobe_inline int do_vsx_store(struct instruction_op *op,
if (!address_ok(regs, ea, size))
return -EFAULT;
- nr_vsx_regs = size / sizeof(__vector128);
+ nr_vsx_regs = max(1ul, size / sizeof(__vector128));
preempt_disable();
if (reg < 32) {
/* FP regs + extensions */
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v2 3/7] ASoC: dt-bindings: fsl_rpmsg: Add binding doc for rpmsg cpu dai driver
From: Fabio Estevam @ 2021-02-25 3:10 UTC (permalink / raw)
To: Shengjiu Wang
Cc: Rob Herring, Linux-ALSA, Timur Tabi,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Shengjiu Wang, Xiubo Li, Liam Girdwood, linux-kernel,
Nicolin Chen, Mark Brown, Takashi Iwai, linuxppc-dev
In-Reply-To: <CAA+D8APfd8trC8cFsT8k8G54nhrYDrwXxECx9RpDAgw3rco9DQ@mail.gmail.com>
On Thu, Feb 18, 2021 at 4:21 AM Shengjiu Wang <shengjiu.wang@gmail.com> wrote:
> > rpmsg is a protocol. What's the h/w block?
>
> On Linux side this driver is a virtual driver, it is running
> on Arm Cortex-A core. The h/w block is controlled by
> another core (cortex-M core). so this driver actually
> doesn't touch any hardware, it just does configuration
> for rpmsg channel.
> fsl,version: There are maybe different image running on M core, this
> is the image version, different image has different function.
To answer Rob's question: the hardware block that handles these
messages is the Message Unit block.
^ permalink raw reply
* [RFC PATCH 8/8] powerpc/64/asm: don't reassign labels
From: Daniel Axtens @ 2021-02-25 3:10 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Daniel Axtens
In-Reply-To: <20210225031006.1204774-1-dja@axtens.net>
The assembler really does not like us reassigning things to the same
label:
<instantiation>:7:9: error: invalid reassignment of non-absolute variable 'fs_label'
This happens across a bunch of platforms:
https://github.com/ClangBuiltLinux/linux/issues/1043
https://github.com/ClangBuiltLinux/linux/issues/1008
https://github.com/ClangBuiltLinux/linux/issues/920
https://github.com/ClangBuiltLinux/linux/issues/1050
There is no hope of getting this fixed in LLVM, so if we want to build
with LLVM_IAS, we need to hack around it ourselves.
For us the big problem comes from this:
\#define USE_FIXED_SECTION(sname) \
fs_label = start_##sname; \
fs_start = sname##_start; \
use_ftsec sname;
\#define USE_TEXT_SECTION()
fs_label = start_text; \
fs_start = text_start; \
.text
and in particular fs_label.
I have tried to work around it by not setting those 'variables', and
requiring that users of the variables instead track for themselves
what section they are in. This isn't amazing, by any stretch, but it
gets us further in the compilation.
I'm still stuck with the following from head_64.S:
.balign 8
p_end: .8byte _end - copy_to_here
4:
/*
* Now copy the rest of the kernel up to _end, add
* _end - copy_to_here to the copy limit and run again.
*/
addis r8,r26,(ABS_ADDR(p_end, text))@ha
ld r8,(ABS_ADDR(p_end, text))@l(r8)
add r5,r5,r8
5: bl copy_and_flush /* copy the rest */
9: b start_here_multiplatform
Clang does not like this code - in particular it complains about the addis, saying
<unknown>:0: error: expected relocatable expression
I don't know what's special about p_end, because just above we do an
ABS_ADDR(4f, text) and that seems to work just fine.
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
arch/powerpc/include/asm/head-64.h | 12 +++++------
arch/powerpc/kernel/exceptions-64s.S | 31 ++++++++++++++--------------
arch/powerpc/kernel/head_64.S | 16 +++++++-------
3 files changed, 29 insertions(+), 30 deletions(-)
diff --git a/arch/powerpc/include/asm/head-64.h b/arch/powerpc/include/asm/head-64.h
index 7d8ccab47e86..43849a777f91 100644
--- a/arch/powerpc/include/asm/head-64.h
+++ b/arch/powerpc/include/asm/head-64.h
@@ -98,13 +98,9 @@ linker_stub_catch: \
. = sname##_len;
#define USE_FIXED_SECTION(sname) \
- fs_label = start_##sname; \
- fs_start = sname##_start; \
use_ftsec sname;
#define USE_TEXT_SECTION() \
- fs_label = start_text; \
- fs_start = text_start; \
.text
#define CLOSE_FIXED_SECTION(sname) \
@@ -161,13 +157,15 @@ end_##sname:
* - ABS_ADDR is used to find the absolute address of any symbol, from within
* a fixed section.
*/
-#define DEFINE_FIXED_SYMBOL(label) \
- label##_absolute = (label - fs_label + fs_start)
+// define label as being _in_ sname
+#define DEFINE_FIXED_SYMBOL(label, sname) \
+ label##_absolute = (label - start_ ## sname + sname ## _start)
#define FIXED_SYMBOL_ABS_ADDR(label) \
(label##_absolute)
-#define ABS_ADDR(label) (label - fs_label + fs_start)
+// find label from _within_ sname
+#define ABS_ADDR(label, sname) (label - start_ ## sname + sname ## _start)
#endif /* __ASSEMBLY__ */
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 720fb9892745..295d90202665 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -64,7 +64,7 @@
.balign IFETCH_ALIGN_BYTES; \
.global name; \
_ASM_NOKPROBE_SYMBOL(name); \
- DEFINE_FIXED_SYMBOL(name); \
+ DEFINE_FIXED_SYMBOL(name, text); \
name:
#define TRAMP_REAL_BEGIN(name) \
@@ -92,18 +92,18 @@ name:
ld reg,PACAKBASE(r13); /* get high part of &label */ \
ori reg,reg,FIXED_SYMBOL_ABS_ADDR(label)
-#define __LOAD_HANDLER(reg, label) \
+#define __LOAD_HANDLER(reg, label, section) \
ld reg,PACAKBASE(r13); \
- ori reg,reg,(ABS_ADDR(label))@l
+ ori reg,reg,(ABS_ADDR(label, section))@l
/*
* Branches from unrelocated code (e.g., interrupts) to labels outside
* head-y require >64K offsets.
*/
-#define __LOAD_FAR_HANDLER(reg, label) \
+#define __LOAD_FAR_HANDLER(reg, label, section) \
ld reg,PACAKBASE(r13); \
- ori reg,reg,(ABS_ADDR(label))@l; \
- addis reg,reg,(ABS_ADDR(label))@h
+ ori reg,reg,(ABS_ADDR(label, section))@l; \
+ addis reg,reg,(ABS_ADDR(label, section))@h
/*
* Branch to label using its 0xC000 address. This results in instruction
@@ -113,8 +113,9 @@ name:
* This could set the 0xc bits for !RELOCATABLE as an immediate, rather than
* load KBASE for a slight optimisation.
*/
+// only called once
#define BRANCH_TO_C000(reg, label) \
- __LOAD_FAR_HANDLER(reg, label); \
+ __LOAD_FAR_HANDLER(reg, label, real_trampolines); \
mtctr reg; \
bctr
@@ -458,7 +459,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_CFAR)
* This switches to virtual mode and sets MSR[RI].
*/
.macro __GEN_COMMON_ENTRY name
-DEFINE_FIXED_SYMBOL(\name\()_common_real)
+DEFINE_FIXED_SYMBOL(\name\()_common_real, text)
\name\()_common_real:
.if IKVM_REAL
KVMTEST \name
@@ -481,7 +482,7 @@ DEFINE_FIXED_SYMBOL(\name\()_common_real)
.endif
.balign IFETCH_ALIGN_BYTES
-DEFINE_FIXED_SYMBOL(\name\()_common_virt)
+DEFINE_FIXED_SYMBOL(\name\()_common_virt, text)
\name\()_common_virt:
.if IKVM_VIRT
KVMTEST \name
@@ -495,7 +496,7 @@ DEFINE_FIXED_SYMBOL(\name\()_common_virt)
* want to run in real mode.
*/
.macro __GEN_REALMODE_COMMON_ENTRY name
-DEFINE_FIXED_SYMBOL(\name\()_common_real)
+DEFINE_FIXED_SYMBOL(\name\()_common_real, text)
\name\()_common_real:
.if IKVM_REAL
KVMTEST \name
@@ -856,12 +857,12 @@ EXC_VIRT_END(system_call_vectored, 0x3000, 0x1000)
#ifdef CONFIG_RELOCATABLE
TRAMP_VIRT_BEGIN(system_call_vectored_tramp)
- __LOAD_HANDLER(r10, system_call_vectored_common)
+ __LOAD_HANDLER(r10, system_call_vectored_common, virt_trampolines)
mtctr r10
bctr
TRAMP_VIRT_BEGIN(system_call_vectored_sigill_tramp)
- __LOAD_HANDLER(r10, system_call_vectored_sigill)
+ __LOAD_HANDLER(r10, system_call_vectored_sigill, virt_trampolines)
mtctr r10
bctr
#endif
@@ -1968,14 +1969,14 @@ END_FTR_SECTION_IFSET(CPU_FTR_REAL_LE)
HMT_MEDIUM
.if ! \virt
- __LOAD_HANDLER(r10, system_call_common_real)
+ __LOAD_HANDLER(r10, system_call_common_real, real_vectors)
mtctr r10
bctr
.else
li r10,MSR_RI
mtmsrd r10,1 /* Set RI (EE=0) */
#ifdef CONFIG_RELOCATABLE
- __LOAD_HANDLER(r10, system_call_common)
+ __LOAD_HANDLER(r10, system_call_common, virt_vectors)
mtctr r10
bctr
#else
@@ -3075,7 +3076,7 @@ USE_FIXED_SECTION(virt_trampolines)
.align 7
.globl __end_interrupts
__end_interrupts:
-DEFINE_FIXED_SYMBOL(__end_interrupts)
+DEFINE_FIXED_SYMBOL(__end_interrupts, virt_trampolines)
#ifdef CONFIG_PPC_970_NAP
/*
diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S
index ece7f97bafff..3a10e9f55baa 100644
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -156,7 +156,7 @@ __secondary_hold:
/* Tell the master cpu we're here */
/* Relocation is off & we are located at an address less */
/* than 0x100, so only need to grab low order offset. */
- std r24,(ABS_ADDR(__secondary_hold_acknowledge))(0)
+ std r24,(ABS_ADDR(__secondary_hold_acknowledge, first_256B))(0)
sync
li r26,0
@@ -164,7 +164,7 @@ __secondary_hold:
tovirt(r26,r26)
#endif
/* All secondary cpus wait here until told to start. */
-100: ld r12,(ABS_ADDR(__secondary_hold_spinloop))(r26)
+100: ld r12,(ABS_ADDR(__secondary_hold_spinloop, first_256B))(r26)
cmpdi 0,r12,0
beq 100b
@@ -646,15 +646,15 @@ __after_prom_start:
3:
#endif
/* # bytes of memory to copy */
- lis r5,(ABS_ADDR(copy_to_here))@ha
- addi r5,r5,(ABS_ADDR(copy_to_here))@l
+ lis r5,(ABS_ADDR(copy_to_here, text))@ha
+ addi r5,r5,(ABS_ADDR(copy_to_here, text))@l
bl copy_and_flush /* copy the first n bytes */
/* this includes the code being */
/* executed here. */
/* Jump to the copy of this code that we just made */
- addis r8,r3,(ABS_ADDR(4f))@ha
- addi r12,r8,(ABS_ADDR(4f))@l
+ addis r8,r3,(ABS_ADDR(4f, text))@ha
+ addi r12,r8,(ABS_ADDR(4f, text))@l
mtctr r12
bctr
@@ -666,8 +666,8 @@ p_end: .8byte _end - copy_to_here
* Now copy the rest of the kernel up to _end, add
* _end - copy_to_here to the copy limit and run again.
*/
- addis r8,r26,(ABS_ADDR(p_end))@ha
- ld r8,(ABS_ADDR(p_end))@l(r8)
+ addis r8,r26,(ABS_ADDR(p_end, text))@ha
+ ld r8,(ABS_ADDR(p_end, text))@l(r8)
add r5,r5,r8
5: bl copy_and_flush /* copy the rest */
--
2.27.0
^ permalink raw reply related
* [RFC PATCH 7/8] powerpc/purgatory: drop .machine specifier
From: Daniel Axtens @ 2021-02-25 3:10 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Daniel Axtens
In-Reply-To: <20210225031006.1204774-1-dja@axtens.net>
It's ignored by future versions of llvm's integrated assembler (by not -11).
I'm not sure what it does for us in gas.
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
arch/powerpc/purgatory/trampoline_64.S | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/purgatory/trampoline_64.S b/arch/powerpc/purgatory/trampoline_64.S
index d956b8a35fd1..e6a2740a5da0 100644
--- a/arch/powerpc/purgatory/trampoline_64.S
+++ b/arch/powerpc/purgatory/trampoline_64.S
@@ -12,7 +12,7 @@
#include <asm/asm-compat.h>
#include <asm/crashdump-ppc64.h>
- .machine ppc64
+//upgrade clang, gets ignored .machine ppc64
.balign 256
.globl purgatory_start
purgatory_start:
--
2.27.0
^ permalink raw reply related
* [RFC PATCH 6/8] powerpc/mm/book3s64/hash: drop pre 2.06 tlbiel for clang
From: Daniel Axtens @ 2021-02-25 3:10 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Daniel Axtens
In-Reply-To: <20210225031006.1204774-1-dja@axtens.net>
The llvm integrated assembler does not recognise the ISA 2.05 tlbiel
version. Eventually do this more smartly.
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
arch/powerpc/mm/book3s64/hash_native.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/powerpc/mm/book3s64/hash_native.c b/arch/powerpc/mm/book3s64/hash_native.c
index 52e170bd95ae..c5937f69a452 100644
--- a/arch/powerpc/mm/book3s64/hash_native.c
+++ b/arch/powerpc/mm/book3s64/hash_native.c
@@ -267,9 +267,14 @@ static inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize)
va |= ssize << 8;
sllp = get_sllp_encoding(apsize);
va |= sllp << 5;
+#if 0
asm volatile(ASM_FTR_IFSET("tlbiel %0", "tlbiel %0,0", %1)
: : "r" (va), "i" (CPU_FTR_ARCH_206)
: "memory");
+#endif
+ asm volatile("tlbiel %0"
+ : : "r" (va)
+ : "memory");
break;
default:
/* We need 14 to 14 + i bits of va */
@@ -286,9 +291,14 @@ static inline void __tlbiel(unsigned long vpn, int psize, int apsize, int ssize)
*/
va |= (vpn & 0xfe);
va |= 1; /* L */
+#if 0
asm volatile(ASM_FTR_IFSET("tlbiel %0", "tlbiel %0,1", %1)
: : "r" (va), "i" (CPU_FTR_ARCH_206)
: "memory");
+#endif
+ asm volatile("tlbiel %0"
+ : : "r" (va)
+ : "memory");
break;
}
trace_tlbie(0, 1, va, 0, 0, 0, 0);
--
2.27.0
^ permalink raw reply related
* [RFC PATCH 5/8] poweprc/lib/quad: Provide macros for lq/stq
From: Daniel Axtens @ 2021-02-25 3:10 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Daniel Axtens
In-Reply-To: <20210225031006.1204774-1-dja@axtens.net>
For some reason the integrated assembler in clang-11 doesn't recognise
them. Eventually we should fix it there too.
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
arch/powerpc/include/asm/ppc-opcode.h | 4 ++++
arch/powerpc/lib/quad.S | 4 ++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/include/asm/ppc-opcode.h b/arch/powerpc/include/asm/ppc-opcode.h
index ed161ef2b3ca..a5249631cb83 100644
--- a/arch/powerpc/include/asm/ppc-opcode.h
+++ b/arch/powerpc/include/asm/ppc-opcode.h
@@ -339,11 +339,13 @@
#define PPC_RAW_DARN(t, l) (0x7c0005e6 | ___PPC_RT(t) | (((l) & 0x3) << 16))
#define PPC_RAW_DCBAL(a, b) (0x7c2005ec | __PPC_RA(a) | __PPC_RB(b))
#define PPC_RAW_DCBZL(a, b) (0x7c2007ec | __PPC_RA(a) | __PPC_RB(b))
+#define PPC_RAW_LQ(t, a, dq) (0xe0000000 | ___PPC_RT(t) | ___PPC_RA(a) | (((dq) & 0xfff) << 3))
#define PPC_RAW_LQARX(t, a, b, eh) (0x7c000228 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh))
#define PPC_RAW_LDARX(t, a, b, eh) (0x7c0000a8 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh))
#define PPC_RAW_LWARX(t, a, b, eh) (0x7c000028 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | __PPC_EH(eh))
#define PPC_RAW_PHWSYNC (0x7c8004ac)
#define PPC_RAW_PLWSYNC (0x7ca004ac)
+#define PPC_RAW_STQ(t, a, ds) (0xf8000002 | ___PPC_RT(t) | ___PPC_RA(a) | (((ds) & 0xfff) << 3))
#define PPC_RAW_STQCX(t, a, b) (0x7c00016d | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b))
#define PPC_RAW_MADDHD(t, a, b, c) (0x10000030 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c))
#define PPC_RAW_MADDHDU(t, a, b, c) (0x10000031 | ___PPC_RT(t) | ___PPC_RA(a) | ___PPC_RB(b) | ___PPC_RC(c))
@@ -530,9 +532,11 @@
#define PPC_DCBZL(a, b) stringify_in_c(.long PPC_RAW_DCBZL(a, b))
#define PPC_DIVDE(t, a, b) stringify_in_c(.long PPC_RAW_DIVDE(t, a, b))
#define PPC_DIVDEU(t, a, b) stringify_in_c(.long PPC_RAW_DIVDEU(t, a, b))
+#define PPC_LQ(t, a, dq) stringify_in_c(.long PPC_RAW_LQ(t, a, dq))
#define PPC_LQARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LQARX(t, a, b, eh))
#define PPC_LDARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LDARX(t, a, b, eh))
#define PPC_LWARX(t, a, b, eh) stringify_in_c(.long PPC_RAW_LWARX(t, a, b, eh))
+#define PPC_STQ(t, a, ds) stringify_in_c(.long PPC_RAW_STQ(t, a, ds))
#define PPC_STQCX(t, a, b) stringify_in_c(.long PPC_RAW_STQCX(t, a, b))
#define PPC_MADDHD(t, a, b, c) stringify_in_c(.long PPC_RAW_MADDHD(t, a, b, c))
#define PPC_MADDHDU(t, a, b, c) stringify_in_c(.long PPC_RAW_MADDHDU(t, a, b, c))
diff --git a/arch/powerpc/lib/quad.S b/arch/powerpc/lib/quad.S
index da71760e50b5..de802a817992 100644
--- a/arch/powerpc/lib/quad.S
+++ b/arch/powerpc/lib/quad.S
@@ -15,7 +15,7 @@
/* do_lq(unsigned long ea, unsigned long *regs) */
_GLOBAL(do_lq)
-1: lq r6, 0(r3)
+1: PPC_LQ(6, 3, 0)
std r6, 0(r4)
std r7, 8(r4)
li r3, 0
@@ -26,7 +26,7 @@ _GLOBAL(do_lq)
/* do_stq(unsigned long ea, unsigned long val0, unsigned long val1) */
_GLOBAL(do_stq)
-1: stq r4, 0(r3)
+1: PPC_STQ(4, 3, 0)
li r3, 0
blr
2: li r3, -EFAULT
--
2.27.0
^ permalink raw reply related
* [RFC PATCH 4/8] powerpc/ppc_asm: use plain numbers for registers
From: Daniel Axtens @ 2021-02-25 3:10 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Daniel Axtens
In-Reply-To: <20210225031006.1204774-1-dja@axtens.net>
This is dumb but makes the llvm integrated assembler happy.
https://github.com/ClangBuiltLinux/linux/issues/764
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
arch/powerpc/include/asm/ppc_asm.h | 64 +++++++++++++++---------------
1 file changed, 32 insertions(+), 32 deletions(-)
diff --git a/arch/powerpc/include/asm/ppc_asm.h b/arch/powerpc/include/asm/ppc_asm.h
index 3dceb64fc9af..49da2cf4c2d5 100644
--- a/arch/powerpc/include/asm/ppc_asm.h
+++ b/arch/powerpc/include/asm/ppc_asm.h
@@ -509,38 +509,38 @@ END_FTR_SECTION_NESTED(CPU_FTR_CELL_TB_BUG, CPU_FTR_CELL_TB_BUG, 96)
* Use R0-31 only when really nessesary.
*/
-#define r0 %r0
-#define r1 %r1
-#define r2 %r2
-#define r3 %r3
-#define r4 %r4
-#define r5 %r5
-#define r6 %r6
-#define r7 %r7
-#define r8 %r8
-#define r9 %r9
-#define r10 %r10
-#define r11 %r11
-#define r12 %r12
-#define r13 %r13
-#define r14 %r14
-#define r15 %r15
-#define r16 %r16
-#define r17 %r17
-#define r18 %r18
-#define r19 %r19
-#define r20 %r20
-#define r21 %r21
-#define r22 %r22
-#define r23 %r23
-#define r24 %r24
-#define r25 %r25
-#define r26 %r26
-#define r27 %r27
-#define r28 %r28
-#define r29 %r29
-#define r30 %r30
-#define r31 %r31
+#define r0 0
+#define r1 1
+#define r2 2
+#define r3 3
+#define r4 4
+#define r5 5
+#define r6 6
+#define r7 7
+#define r8 8
+#define r9 9
+#define r10 10
+#define r11 11
+#define r12 12
+#define r13 13
+#define r14 14
+#define r15 15
+#define r16 16
+#define r17 17
+#define r18 18
+#define r19 19
+#define r20 20
+#define r21 21
+#define r22 22
+#define r23 23
+#define r24 24
+#define r25 25
+#define r26 26
+#define r27 27
+#define r28 28
+#define r29 29
+#define r30 30
+#define r31 31
/* Floating Point Registers (FPRs) */
--
2.27.0
^ permalink raw reply related
* [RFC PATCH 3/8] powerpc/head-64: do less gas-specific stuff with sections
From: Daniel Axtens @ 2021-02-25 3:10 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Daniel Axtens
In-Reply-To: <20210225031006.1204774-1-dja@axtens.net>
Reopening the section without specifying the same flags breaks
the llvm integrated assembler. Don't do it: just specify all the
flags all the time.
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
arch/powerpc/include/asm/head-64.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/include/asm/head-64.h b/arch/powerpc/include/asm/head-64.h
index 4cb9efa2eb21..7d8ccab47e86 100644
--- a/arch/powerpc/include/asm/head-64.h
+++ b/arch/powerpc/include/asm/head-64.h
@@ -15,10 +15,10 @@
.macro define_data_ftsec name
.section ".head.data.\name\()","a",@progbits
.endm
-.macro use_ftsec name
- .section ".head.text.\name\()"
-.endm
-
+//.macro use_ftsec name
+// .section ".head.text.\name\()"
+//.endm
+#define use_ftsec define_ftsec
/*
* Fixed (location) sections are used by opening fixed sections and emitting
* fixed section entries into them before closing them. Multiple fixed sections
--
2.27.0
^ permalink raw reply related
* [RFC PATCH 2/8] powerpc: check for support for -Wa,-m{power4,any}
From: Daniel Axtens @ 2021-02-25 3:10 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Daniel Axtens
In-Reply-To: <20210225031006.1204774-1-dja@axtens.net>
LLVM's integrated assembler does not like either -Wa,-mpower4
or -Wa,-many. So just don't pass them if they're not supported.
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
arch/powerpc/Makefile | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index 08cf0eade56a..3e2c72d20bb8 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -252,7 +252,9 @@ cpu-as-$(CONFIG_E500) += -Wa,-me500
# When using '-many -mpower4' gas will first try and find a matching power4
# mnemonic and failing that it will allow any valid mnemonic that GAS knows
# about. GCC will pass -many to GAS when assembling, clang does not.
-cpu-as-$(CONFIG_PPC_BOOK3S_64) += -Wa,-mpower4 -Wa,-many
+# LLVM IAS doesn't understand either flag: https://github.com/ClangBuiltLinux/linux/issues/675
+# but LLVM IAS only supports ISA >= 2.06 for Book3S 64 anyway...
+cpu-as-$(CONFIG_PPC_BOOK3S_64) += $(call as-option,-Wa$(comma)-mpower4) $(call as-option,-Wa$(comma)-many)
cpu-as-$(CONFIG_PPC_E500MC) += $(call as-option,-Wa$(comma)-me500mc)
KBUILD_AFLAGS += $(cpu-as-y)
--
2.27.0
^ permalink raw reply related
* [PATCH 1/8] powerpc/64s/exception: Clean up a missed SRR specifier
From: Daniel Axtens @ 2021-02-25 3:09 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Nicholas Piggin, Daniel Axtens
In-Reply-To: <20210225031006.1204774-1-dja@axtens.net>
Nick's patch cleaning up the SRR specifiers in exception-64s.S
missed a single instance of EXC_HV_OR_STD. Clean that up.
Caught by clang's integrated assembler.
Fixes: 3f7fbd97d07d ("powerpc/64s/exception: Clean up SRR specifiers")
Acked-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
arch/powerpc/kernel/exceptions-64s.S | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index a4bd3c114a0a..720fb9892745 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -466,7 +466,7 @@ DEFINE_FIXED_SYMBOL(\name\()_common_real)
ld r10,PACAKMSR(r13) /* get MSR value for kernel */
/* MSR[RI] is clear iff using SRR regs */
- .if IHSRR == EXC_HV_OR_STD
+ .if IHSRR_IF_HVMODE
BEGIN_FTR_SECTION
xori r10,r10,MSR_RI
END_FTR_SECTION_IFCLR(CPU_FTR_HVMODE)
--
2.27.0
^ permalink raw reply related
* [RFC PATCH 0/8] WIP support for the LLVM integrated assembler
From: Daniel Axtens @ 2021-02-25 3:09 UTC (permalink / raw)
To: llvmlinux, linuxppc-dev; +Cc: Daniel Axtens
To support Clang's CFI we need LTO. For LTO, we need to be able to compile
with the LLVM integrated assembler.
Currently, we can't.
This series gets us a bit closer, but I'm still stuck and I'm hoping
someone can point me in the right direction.
Patch 1 is a fix that can be merged at any time.
The rest of this series is pretty rough, but with it, building like this:
make CC=clang-11 LD=ld.lld-11 AR=llvm-ar-11 NM=llvm-nm-11 STRIP=llvm-strip-11 \
OBJCOPY=llvm-objcopy-11 OBJDUMP=llvm-objdump-11 READELF=llvm-readelf-11 \
HOSTCC=clang-11 HOSTCXX=clang++-11 HOSTAR=llvm-ar-11 HOSTLD=ld.lld-11 \
LLVM_IAS=1 vmlinux
on a pseries_le_defconfig without Werror works except for head-64.S,
which still fails as described in the final patch. Help would be
appreciated because it's deep magic all around.
Apart from the very very dodgy change to drop the tlbiel feature
section, none of the de-gas-ing changed the compiled binary for me
under gcc-10.2.0-13ubuntu1.
Daniel Axtens (8):
powerpc/64s/exception: Clean up a missed SRR specifier
powerpc: check for support for -Wa,-m{power4,any}
powerpc/head-64: do less gas-specific stuff with sections
powerpc/ppc_asm: use plain numbers for registers
poweprc/lib/quad: Provide macros for lq/stq
powerpc/mm/book3s64/hash: drop pre 2.06 tlbiel for clang
powerpc/purgatory: drop .machine specifier
powerpc/64/asm: don't reassign labels
arch/powerpc/Makefile | 4 +-
arch/powerpc/include/asm/head-64.h | 20 ++++----
arch/powerpc/include/asm/ppc-opcode.h | 4 ++
arch/powerpc/include/asm/ppc_asm.h | 64 +++++++++++++-------------
arch/powerpc/kernel/exceptions-64s.S | 33 ++++++-------
arch/powerpc/kernel/head_64.S | 16 +++----
arch/powerpc/lib/quad.S | 4 +-
arch/powerpc/mm/book3s64/hash_native.c | 10 ++++
arch/powerpc/purgatory/trampoline_64.S | 2 +-
9 files changed, 86 insertions(+), 71 deletions(-)
--
2.27.0
^ permalink raw reply
* [PATCH v3 7/7] ASoC: dt-bindings: imx-rpmsg: Add binding doc for rpmsg machine driver
From: Shengjiu Wang @ 2021-02-25 2:52 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, alsa-devel, linux-kernel, timur,
nicoleotsuka, Xiubo.Lee, festevam, linuxppc-dev, robh+dt,
devicetree
In-Reply-To: <1614221563-26822-1-git-send-email-shengjiu.wang@nxp.com>
Imx-rpmsg is a new added machine driver for supporting audio on Cortex-M
core. The Cortex-M core will control the audio interface, DMA and audio
codec, setup the pipeline, the audio driver on Cortex-A core side is just
to communitcate with M core, it is a virtual sound card and don't touch
the hardware.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
.../bindings/sound/imx-audio-rpmsg.yaml | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/imx-audio-rpmsg.yaml
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-rpmsg.yaml b/Documentation/devicetree/bindings/sound/imx-audio-rpmsg.yaml
new file mode 100644
index 000000000000..ff9b3fc69dae
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/imx-audio-rpmsg.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/imx-audio-rpmsg.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX audio complex with rpmsg
+
+maintainers:
+ - Shengjiu Wang <shengjiu.wang@nxp.com>
+
+description: |
+ imx-rpmsg is a machine driver for supporting audio by rpmsg.
+ The Cortex-M core will control the audio interface, DMA and audio
+ codec, setup the pipeline, the audio driver on Cortex-A core side
+ is just to communitcate with M core, it is a virtual sound card
+ and don't touch the hardware.
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx-audio-rpmsg
+
+ model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: User specified audio sound card name
+
+ audio-cpu:
+ description: The phandle of an CPU DAI controller
+
+ rpmsg-out:
+ description: |
+ This is a boolean property. If present, the transmitting function
+ will be enabled,
+
+ rpmsg-in:
+ description: |
+ This is a boolean property. If present, the receiving function
+ will be enabled.
+
+required:
+ - compatible
+ - model
+ - audio-cpu
+
+additionalProperties: false
+
+examples:
+ - |
+ sound-rpmsg {
+ compatible = "fsl,imx-audio-rpmsg";
+ model = "ak4497-audio";
+ audio-cpu = <&rpmsg_audio>;
+ rpmsg-out;
+ };
--
2.27.0
^ permalink raw reply related
* [PATCH v3 5/7] ASoC: imx-pcm-rpmsg: Add platform driver for audio base on rpmsg
From: Shengjiu Wang @ 2021-02-25 2:52 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, alsa-devel, linux-kernel, timur,
nicoleotsuka, Xiubo.Lee, festevam, linuxppc-dev, robh+dt,
devicetree
In-Reply-To: <1614221563-26822-1-git-send-email-shengjiu.wang@nxp.com>
platform driver base on rpmsg is the interface for sending and
receiving rpmsg to and from M core. It will tell the Cortex-M core
sound format/rate/channel, where is the data buffer, where is
the period size, when to start, when to stop and when suspend
or resume happen, each this behavior there is defined rpmsg
command.
Especially we designed the low power audio case, that is to
allocate a large buffer and fill the data, then Cortex-A core can go
to sleep mode, Cortex-M core continue to play the sound, when the
buffer is consumed, Cortex-M core will trigger the Cortex-A core to
wakeup.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/Kconfig | 5 +
sound/soc/fsl/Makefile | 1 +
sound/soc/fsl/imx-pcm-rpmsg.c | 919 ++++++++++++++++++++++++++++++++++
sound/soc/fsl/imx-pcm-rpmsg.h | 512 +++++++++++++++++++
4 files changed, 1437 insertions(+)
create mode 100644 sound/soc/fsl/imx-pcm-rpmsg.c
create mode 100644 sound/soc/fsl/imx-pcm-rpmsg.h
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 84d9f0f1f75b..749c44fc0759 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -130,6 +130,11 @@ config SND_SOC_IMX_AUDIO_RPMSG
tristate
depends on RPMSG
+config SND_SOC_IMX_PCM_RPMSG
+ tristate
+ depends on SND_SOC_IMX_AUDIO_RPMSG
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+
config SND_SOC_IMX_AUDMUX
tristate "Digital Audio Mux module support"
help
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index f08f3cd07ff5..ce4f4324c3a2 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
obj-$(CONFIG_SND_SOC_IMX_AUDIO_RPMSG) += imx-audio-rpmsg.o
+obj-$(CONFIG_SND_SOC_IMX_PCM_RPMSG) += imx-pcm-rpmsg.o
# i.MX Machine Support
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c
new file mode 100644
index 000000000000..f05d5d489560
--- /dev/null
+++ b/sound/soc/fsl/imx-pcm-rpmsg.c
@@ -0,0 +1,919 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2021 NXP
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/rpmsg.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+
+#include "imx-pcm.h"
+#include "fsl_rpmsg.h"
+#include "imx-pcm-rpmsg.h"
+
+static struct snd_pcm_hardware imx_rpmsg_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
+ .period_bytes_min = 512,
+ .period_bytes_max = 65536,
+ .periods_min = 2,
+ .periods_max = 6000,
+ .fifo_size = 0,
+};
+
+static int imx_rpmsg_pcm_send_message(struct rpmsg_msg *msg,
+ struct rpmsg_info *info)
+{
+ struct rpmsg_device *rpdev = info->rpdev;
+ int ret = 0;
+
+ mutex_lock(&info->msg_lock);
+ if (!rpdev) {
+ dev_err(info->dev, "rpmsg channel not ready\n");
+ mutex_unlock(&info->msg_lock);
+ return -EINVAL;
+ }
+
+ dev_dbg(&rpdev->dev, "send cmd %d\n", msg->s_msg.header.cmd);
+
+ if (!(msg->s_msg.header.type == MSG_TYPE_C))
+ reinit_completion(&info->cmd_complete);
+
+ ret = rpmsg_send(rpdev->ept, (void *)&msg->s_msg,
+ sizeof(struct rpmsg_s_msg));
+ if (ret) {
+ dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
+ mutex_unlock(&info->msg_lock);
+ return ret;
+ }
+
+ /* No receive msg for TYPE_C command */
+ if (msg->s_msg.header.type == MSG_TYPE_C) {
+ mutex_unlock(&info->msg_lock);
+ return 0;
+ }
+
+ /* wait response from rpmsg */
+ ret = wait_for_completion_timeout(&info->cmd_complete,
+ msecs_to_jiffies(RPMSG_TIMEOUT));
+ if (!ret) {
+ dev_err(&rpdev->dev, "rpmsg_send cmd %d timeout!\n",
+ msg->s_msg.header.cmd);
+ mutex_unlock(&info->msg_lock);
+ return -ETIMEDOUT;
+ }
+
+ memcpy(&msg->r_msg, &info->r_msg, sizeof(struct rpmsg_r_msg));
+ memcpy(&info->msg[msg->r_msg.header.cmd].r_msg,
+ &msg->r_msg, sizeof(struct rpmsg_r_msg));
+
+ /*
+ * Reset the buffer pointer to be zero, actully we have
+ * set the buffer pointer to be zero in imx_rpmsg_terminate_all
+ * But if there is timer task queued in queue, after it is
+ * executed the buffer pointer will be changed, so need to
+ * reset it again with TERMINATE command.
+ */
+ switch (msg->s_msg.header.cmd) {
+ case TX_TERMINATE:
+ info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
+ break;
+ case RX_TERMINATE:
+ info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(&rpdev->dev, "cmd:%d, resp %d\n", msg->s_msg.header.cmd,
+ info->r_msg.param.resp);
+
+ mutex_unlock(&info->msg_lock);
+
+ return 0;
+}
+
+static int imx_rpmsg_insert_workqueue(struct snd_pcm_substream *substream,
+ struct rpmsg_msg *msg,
+ struct rpmsg_info *info)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ /*
+ * Queue the work to workqueue.
+ * If the queue is full, drop the message.
+ */
+ spin_lock_irqsave(&info->wq_lock, flags);
+ if (info->work_write_index != info->work_read_index) {
+ int index = info->work_write_index;
+
+ memcpy(&info->work_list[index].msg, msg,
+ sizeof(struct rpmsg_s_msg));
+
+ queue_work(info->rpmsg_wq, &info->work_list[index].work);
+ info->work_write_index++;
+ info->work_write_index %= WORK_MAX_NUM;
+ } else {
+ info->msg_drop_count[substream->stream]++;
+ ret = -EPIPE;
+ }
+ spin_unlock_irqrestore(&info->wq_lock, flags);
+
+ return ret;
+}
+
+static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct rpmsg_msg *msg;
+ int ret = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_HW_PARAM];
+ msg->s_msg.header.cmd = TX_HW_PARAM;
+ } else {
+ msg = &info->msg[RX_HW_PARAM];
+ msg->s_msg.header.cmd = RX_HW_PARAM;
+ }
+
+ msg->s_msg.param.rate = params_rate(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ msg->s_msg.param.format = RPMSG_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ msg->s_msg.param.format = RPMSG_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ msg->s_msg.param.format = SNDRV_PCM_FORMAT_DSD_U16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ msg->s_msg.param.format = SNDRV_PCM_FORMAT_DSD_U32_LE;
+ break;
+ default:
+ msg->s_msg.param.format = RPMSG_S32_LE;
+ break;
+ }
+
+ switch (params_channels(params)) {
+ case 1:
+ msg->s_msg.param.channels = RPMSG_CH_LEFT;
+ break;
+ case 2:
+ msg->s_msg.param.channels = RPMSG_CH_STEREO;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ info->send_message(msg, info);
+
+ return ret;
+}
+
+static int imx_rpmsg_pcm_hw_free(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+ unsigned int pos = 0;
+ int buffer_tail = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ else
+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+
+ buffer_tail = msg->r_msg.param.buffer_tail;
+ pos = buffer_tail * snd_pcm_lib_period_bytes(substream);
+
+ return bytes_to_frames(substream->runtime, pos);
+}
+
+static void imx_rpmsg_timer_callback(struct timer_list *t)
+{
+ struct stream_timer *stream_timer =
+ from_timer(stream_timer, t, timer);
+ struct snd_pcm_substream *substream = stream_timer->substream;
+ struct rpmsg_info *info = stream_timer->info;
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->s_msg.header.cmd = TX_PERIOD_DONE;
+ } else {
+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->s_msg.header.cmd = RX_PERIOD_DONE;
+ }
+
+ imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_pcm_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+ int ret = 0;
+ int cmd;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_OPEN];
+ msg->s_msg.header.cmd = TX_OPEN;
+
+ /* reinitialize buffer counter*/
+ cmd = TX_PERIOD_DONE + MSG_TYPE_A_NUM;
+ info->msg[cmd].s_msg.param.buffer_tail = 0;
+ info->msg[cmd].r_msg.param.buffer_tail = 0;
+ info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
+
+ } else {
+ msg = &info->msg[RX_OPEN];
+ msg->s_msg.header.cmd = RX_OPEN;
+
+ /* reinitialize buffer counter*/
+ cmd = RX_PERIOD_DONE + MSG_TYPE_A_NUM;
+ info->msg[cmd].s_msg.param.buffer_tail = 0;
+ info->msg[cmd].r_msg.param.buffer_tail = 0;
+ info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
+ }
+
+ info->send_message(msg, info);
+
+ imx_rpmsg_pcm_hardware.period_bytes_max =
+ imx_rpmsg_pcm_hardware.buffer_bytes_max / 2;
+
+ snd_soc_set_runtime_hwparams(substream, &imx_rpmsg_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0)
+ return ret;
+
+ info->msg_drop_count[substream->stream] = 0;
+
+ /* Create timer*/
+ info->stream_timer[substream->stream].info = info;
+ info->stream_timer[substream->stream].substream = substream;
+ timer_setup(&info->stream_timer[substream->stream].timer,
+ imx_rpmsg_timer_callback, 0);
+ return ret;
+}
+
+static int imx_rpmsg_pcm_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+ int ret = 0;
+
+ /* Flush work in workqueue to make TX_CLOSE is the last message */
+ flush_workqueue(info->rpmsg_wq);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_CLOSE];
+ msg->s_msg.header.cmd = TX_CLOSE;
+ } else {
+ msg = &info->msg[RX_CLOSE];
+ msg->s_msg.header.cmd = RX_CLOSE;
+ }
+
+ info->send_message(msg, info);
+
+ del_timer(&info->stream_timer[substream->stream].timer);
+
+ rtd->dai_link->ignore_suspend = 0;
+
+ if (info->msg_drop_count[substream->stream])
+ dev_warn(rtd->dev, "Msg is dropped!, number is %d\n",
+ info->msg_drop_count[substream->stream]);
+
+ return ret;
+}
+
+static int imx_rpmsg_pcm_prepare(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+
+ /*
+ * NON-MMAP mode, NONBLOCK, Version 2, enable lpa in dts
+ * four conditions to determine the lpa is enabled.
+ */
+ if ((runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
+ runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) &&
+ rpmsg->version == API_VERSION_V2 &&
+ rpmsg->enable_lpa) {
+ /*
+ * Ignore suspend operation in low power mode
+ * M core will continue playback music on A core suspend.
+ */
+ rtd->dai_link->ignore_suspend = 1;
+ rpmsg->force_lpa = 1;
+ } else {
+ rpmsg->force_lpa = 0;
+ }
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ return dma_mmap_wc(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static void imx_rpmsg_pcm_dma_complete(void *arg)
+{
+ struct snd_pcm_substream *substream = arg;
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static int imx_rpmsg_prepare_and_submit(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_BUFFER];
+ msg->s_msg.header.cmd = TX_BUFFER;
+ } else {
+ msg = &info->msg[RX_BUFFER];
+ msg->s_msg.header.cmd = RX_BUFFER;
+ }
+
+ /* Send buffer address and buffer size */
+ msg->s_msg.param.buffer_addr = substream->runtime->dma_addr;
+ msg->s_msg.param.buffer_size = snd_pcm_lib_buffer_bytes(substream);
+ msg->s_msg.param.period_size = snd_pcm_lib_period_bytes(substream);
+ msg->s_msg.param.buffer_tail = 0;
+
+ info->num_period[substream->stream] = msg->s_msg.param.buffer_size /
+ msg->s_msg.param.period_size;
+
+ info->callback[substream->stream] = imx_rpmsg_pcm_dma_complete;
+ info->callback_param[substream->stream] = substream;
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_async_issue_pending(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_START];
+ msg->s_msg.header.cmd = TX_START;
+ } else {
+ msg = &info->msg[RX_START];
+ msg->s_msg.header.cmd = RX_START;
+ }
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_restart(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_RESTART];
+ msg->s_msg.header.cmd = TX_RESTART;
+ } else {
+ msg = &info->msg[RX_RESTART];
+ msg->s_msg.header.cmd = RX_RESTART;
+ }
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_pause(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_PAUSE];
+ msg->s_msg.header.cmd = TX_PAUSE;
+ } else {
+ msg = &info->msg[RX_PAUSE];
+ msg->s_msg.header.cmd = RX_PAUSE;
+ }
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_terminate_all(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ struct rpmsg_msg *msg;
+ int cmd;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_TERMINATE];
+ msg->s_msg.header.cmd = TX_TERMINATE;
+ /* Clear buffer count*/
+ cmd = TX_PERIOD_DONE + MSG_TYPE_A_NUM;
+ info->msg[cmd].s_msg.param.buffer_tail = 0;
+ info->msg[cmd].r_msg.param.buffer_tail = 0;
+ info->msg[TX_POINTER].r_msg.param.buffer_offset = 0;
+ } else {
+ msg = &info->msg[RX_TERMINATE];
+ msg->s_msg.header.cmd = RX_TERMINATE;
+ /* Clear buffer count*/
+ cmd = RX_PERIOD_DONE + MSG_TYPE_A_NUM;
+ info->msg[cmd].s_msg.param.buffer_tail = 0;
+ info->msg[cmd].r_msg.param.buffer_tail = 0;
+ info->msg[RX_POINTER].r_msg.param.buffer_offset = 0;
+ }
+
+ del_timer(&info->stream_timer[substream->stream].timer);
+
+ return imx_rpmsg_insert_workqueue(substream, msg, info);
+}
+
+static int imx_rpmsg_pcm_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ ret = imx_rpmsg_prepare_and_submit(component, substream);
+ if (ret)
+ return ret;
+ ret = imx_rpmsg_async_issue_pending(component, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (rpmsg->force_lpa)
+ break;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = imx_rpmsg_restart(component, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (!rpmsg->force_lpa) {
+ if (runtime->info & SNDRV_PCM_INFO_PAUSE)
+ ret = imx_rpmsg_pause(component, substream);
+ else
+ ret = imx_rpmsg_terminate_all(component, substream);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ ret = imx_rpmsg_pause(component, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ ret = imx_rpmsg_terminate_all(component, substream);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/*
+ * imx_rpmsg_pcm_ack
+ *
+ * Send the period index to M core through rpmsg, but not send
+ * all the period index to M core, reduce some unnessesary msg
+ * to reduce the pressure of rpmsg bandwidth.
+ */
+static int imx_rpmsg_pcm_ack(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ struct rpmsg_info *info = dev_get_drvdata(component->dev);
+ snd_pcm_uframes_t period_size = runtime->period_size;
+ snd_pcm_sframes_t avail;
+ struct timer_list *timer;
+ struct rpmsg_msg *msg;
+ unsigned long flags;
+ int buffer_tail = 0;
+ int written_num = 0;
+
+ if (!rpmsg->force_lpa)
+ return 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->s_msg.header.cmd = TX_PERIOD_DONE;
+ } else {
+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+ msg->s_msg.header.cmd = RX_PERIOD_DONE;
+ }
+
+ msg->s_msg.header.type = MSG_TYPE_C;
+
+ buffer_tail = (frames_to_bytes(runtime, runtime->control->appl_ptr) %
+ snd_pcm_lib_buffer_bytes(substream));
+ buffer_tail = buffer_tail / snd_pcm_lib_period_bytes(substream);
+
+ /* There is update for period index */
+ if (buffer_tail != msg->s_msg.param.buffer_tail) {
+ written_num = buffer_tail - msg->s_msg.param.buffer_tail;
+ if (written_num < 0)
+ written_num += runtime->periods;
+
+ msg->s_msg.param.buffer_tail = buffer_tail;
+
+ /* The notification message is updated to latest */
+ spin_lock_irqsave(&info->lock[substream->stream], flags);
+ memcpy(&info->notify[substream->stream], msg,
+ sizeof(struct rpmsg_s_msg));
+ info->notify_updated[substream->stream] = true;
+ spin_unlock_irqrestore(&info->lock[substream->stream], flags);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ avail = snd_pcm_playback_hw_avail(runtime);
+ else
+ avail = snd_pcm_capture_hw_avail(runtime);
+
+ timer = &info->stream_timer[substream->stream].timer;
+ /*
+ * If the data in the buffer is less than one period before
+ * this fill, which means the data may not enough on M
+ * core side, we need to send message immediately to let
+ * M core know the pointer is updated.
+ * if there is more than one period data in the buffer before
+ * this fill, which means the data is enough on M core side,
+ * we can delay one period (using timer) to send the message
+ * for reduce the message number in workqueue, because the
+ * pointer may be updated by ack function later, we can
+ * send latest pointer to M core side.
+ */
+ if ((avail - written_num * period_size) <= period_size) {
+ imx_rpmsg_insert_workqueue(substream, msg, info);
+ } else if (rpmsg->force_lpa && !timer_pending(timer)) {
+ int time_msec;
+
+ time_msec = (int)(runtime->period_size * 1000 / runtime->rate);
+ mod_timer(timer, jiffies + msecs_to_jiffies(time_msec));
+ }
+ }
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+ int stream, int size)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->private_data = NULL;
+ buf->area = dma_alloc_wc(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+
+ buf->bytes = size;
+ return 0;
+}
+
+static void imx_rpmsg_pcm_free_dma_buffers(struct snd_soc_component *component,
+ struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+ int stream;
+
+ for (stream = SNDRV_PCM_STREAM_PLAYBACK;
+ stream < SNDRV_PCM_STREAM_LAST; stream++) {
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ continue;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ continue;
+
+ dma_free_wc(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+}
+
+static int imx_rpmsg_pcm_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ int ret;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ rpmsg->buffer_size);
+ if (ret)
+ goto out;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = imx_rpmsg_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE,
+ rpmsg->buffer_size);
+ if (ret)
+ goto out;
+ }
+
+ imx_rpmsg_pcm_hardware.buffer_bytes_max = rpmsg->buffer_size;
+out:
+ /* free preallocated buffers in case of error */
+ if (ret)
+ imx_rpmsg_pcm_free_dma_buffers(component, pcm);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver imx_rpmsg_soc_component = {
+ .name = IMX_PCM_DRV_NAME,
+ .pcm_construct = imx_rpmsg_pcm_new,
+ .pcm_destruct = imx_rpmsg_pcm_free_dma_buffers,
+ .open = imx_rpmsg_pcm_open,
+ .close = imx_rpmsg_pcm_close,
+ .hw_params = imx_rpmsg_pcm_hw_params,
+ .hw_free = imx_rpmsg_pcm_hw_free,
+ .trigger = imx_rpmsg_pcm_trigger,
+ .pointer = imx_rpmsg_pcm_pointer,
+ .mmap = imx_rpmsg_pcm_mmap,
+ .ack = imx_rpmsg_pcm_ack,
+ .prepare = imx_rpmsg_pcm_prepare,
+};
+
+static void imx_rpmsg_pcm_work(struct work_struct *work)
+{
+ struct work_of_rpmsg *work_of_rpmsg;
+ bool is_notification = false;
+ struct rpmsg_info *info;
+ struct rpmsg_msg msg;
+ unsigned long flags;
+
+ work_of_rpmsg = container_of(work, struct work_of_rpmsg, work);
+ info = work_of_rpmsg->info;
+
+ /*
+ * Every work in the work queue, first we check if there
+ * is update for period is filled, because there may be not
+ * enough data in M core side, need to let M core know
+ * data is updated immediately.
+ */
+ spin_lock_irqsave(&info->lock[TX], flags);
+ if (info->notify_updated[TX]) {
+ memcpy(&msg, &info->notify[TX], sizeof(struct rpmsg_s_msg));
+ info->notify_updated[TX] = false;
+ spin_unlock_irqrestore(&info->lock[TX], flags);
+ info->send_message(&msg, info);
+ } else {
+ spin_unlock_irqrestore(&info->lock[TX], flags);
+ }
+
+ spin_lock_irqsave(&info->lock[RX], flags);
+ if (info->notify_updated[RX]) {
+ memcpy(&msg, &info->notify[RX], sizeof(struct rpmsg_s_msg));
+ info->notify_updated[RX] = false;
+ spin_unlock_irqrestore(&info->lock[RX], flags);
+ info->send_message(&msg, info);
+ } else {
+ spin_unlock_irqrestore(&info->lock[RX], flags);
+ }
+
+ /* Skip the notification message for it has been processed above */
+ if (work_of_rpmsg->msg.s_msg.header.type == MSG_TYPE_C &&
+ (work_of_rpmsg->msg.s_msg.header.cmd == TX_PERIOD_DONE ||
+ work_of_rpmsg->msg.s_msg.header.cmd == RX_PERIOD_DONE))
+ is_notification = true;
+
+ if (!is_notification)
+ info->send_message(&work_of_rpmsg->msg, info);
+
+ /* update read index */
+ spin_lock_irqsave(&info->wq_lock, flags);
+ info->work_read_index++;
+ info->work_read_index %= WORK_MAX_NUM;
+ spin_unlock_irqrestore(&info->wq_lock, flags);
+}
+
+static int imx_rpmsg_pcm_probe(struct platform_device *pdev)
+{
+ struct snd_soc_component *component;
+ struct rpmsg_info *info;
+ int ret, i;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, info);
+
+ info->rpdev = container_of(pdev->dev.parent, struct rpmsg_device, dev);
+ info->dev = &pdev->dev;
+ /* Setup work queue */
+ info->rpmsg_wq = alloc_ordered_workqueue("rpmsg_audio",
+ WQ_HIGHPRI |
+ WQ_UNBOUND |
+ WQ_FREEZABLE);
+ if (!info->rpmsg_wq) {
+ dev_err(&pdev->dev, "workqueue create failed\n");
+ return -ENOMEM;
+ }
+
+ /* Write index initialize 1, make it differ with the read index */
+ info->work_write_index = 1;
+ info->send_message = imx_rpmsg_pcm_send_message;
+
+ for (i = 0; i < WORK_MAX_NUM; i++) {
+ INIT_WORK(&info->work_list[i].work, imx_rpmsg_pcm_work);
+ info->work_list[i].info = info;
+ }
+
+ /* Initialize msg */
+ for (i = 0; i < MSG_MAX_NUM; i++) {
+ info->msg[i].s_msg.header.cate = IMX_RPMSG_AUDIO;
+ info->msg[i].s_msg.header.major = IMX_RMPSG_MAJOR;
+ info->msg[i].s_msg.header.minor = IMX_RMPSG_MINOR;
+ info->msg[i].s_msg.header.type = MSG_TYPE_A;
+ info->msg[i].s_msg.param.audioindex = 0;
+ }
+
+ init_completion(&info->cmd_complete);
+ mutex_init(&info->msg_lock);
+ spin_lock_init(&info->lock[TX]);
+ spin_lock_init(&info->lock[RX]);
+ spin_lock_init(&info->wq_lock);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &imx_rpmsg_soc_component,
+ NULL, 0);
+ if (ret)
+ goto fail;
+
+ component = snd_soc_lookup_component(&pdev->dev, IMX_PCM_DRV_NAME);
+ if (!component) {
+ ret = -EINVAL;
+ goto fail;
+ }
+#ifdef CONFIG_DEBUG_FS
+ component->debugfs_prefix = "rpmsg";
+#endif
+
+ return 0;
+
+fail:
+ if (info->rpmsg_wq)
+ destroy_workqueue(info->rpmsg_wq);
+
+ return ret;
+}
+
+static int imx_rpmsg_pcm_remove(struct platform_device *pdev)
+{
+ struct rpmsg_info *info = platform_get_drvdata(pdev);
+
+ if (info->rpmsg_wq)
+ destroy_workqueue(info->rpmsg_wq);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int imx_rpmsg_pcm_runtime_resume(struct device *dev)
+{
+ struct rpmsg_info *info = dev_get_drvdata(dev);
+
+ cpu_latency_qos_add_request(&info->pm_qos_req, 0);
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_runtime_suspend(struct device *dev)
+{
+ struct rpmsg_info *info = dev_get_drvdata(dev);
+
+ cpu_latency_qos_remove_request(&info->pm_qos_req);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int imx_rpmsg_pcm_suspend(struct device *dev)
+{
+ struct rpmsg_info *info = dev_get_drvdata(dev);
+ struct rpmsg_msg *rpmsg_tx;
+ struct rpmsg_msg *rpmsg_rx;
+
+ rpmsg_tx = &info->msg[TX_SUSPEND];
+ rpmsg_rx = &info->msg[RX_SUSPEND];
+
+ rpmsg_tx->s_msg.header.cmd = TX_SUSPEND;
+ info->send_message(rpmsg_tx, info);
+
+ rpmsg_rx->s_msg.header.cmd = RX_SUSPEND;
+ info->send_message(rpmsg_rx, info);
+
+ return 0;
+}
+
+static int imx_rpmsg_pcm_resume(struct device *dev)
+{
+ struct rpmsg_info *info = dev_get_drvdata(dev);
+ struct rpmsg_msg *rpmsg_tx;
+ struct rpmsg_msg *rpmsg_rx;
+
+ rpmsg_tx = &info->msg[TX_RESUME];
+ rpmsg_rx = &info->msg[RX_RESUME];
+
+ rpmsg_tx->s_msg.header.cmd = TX_RESUME;
+ info->send_message(rpmsg_tx, info);
+
+ rpmsg_rx->s_msg.header.cmd = RX_RESUME;
+ info->send_message(rpmsg_rx, info);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops imx_rpmsg_pcm_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx_rpmsg_pcm_runtime_suspend,
+ imx_rpmsg_pcm_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(imx_rpmsg_pcm_suspend,
+ imx_rpmsg_pcm_resume)
+};
+
+static struct platform_driver imx_pcm_rpmsg_driver = {
+ .probe = imx_rpmsg_pcm_probe,
+ .remove = imx_rpmsg_pcm_remove,
+ .driver = {
+ .name = IMX_PCM_DRV_NAME,
+ .pm = &imx_rpmsg_pcm_pm_ops,
+ },
+};
+module_platform_driver(imx_pcm_rpmsg_driver);
+
+MODULE_DESCRIPTION("Freescale SoC Audio RPMSG PCM interface");
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_ALIAS("platform:" IMX_PCM_DRV_NAME);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.h b/sound/soc/fsl/imx-pcm-rpmsg.h
new file mode 100644
index 000000000000..308d153920a3
--- /dev/null
+++ b/sound/soc/fsl/imx-pcm-rpmsg.h
@@ -0,0 +1,512 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2017-2021 NXP
+ *
+ ******************************************************************************
+ * Communication stack of audio with rpmsg
+ ******************************************************************************
+ * Packet structure:
+ * A SRTM message consists of a 10 bytes header followed by 0~N bytes of data
+ *
+ * +---------------+-------------------------------+
+ * | | Content |
+ * +---------------+-------------------------------+
+ * | Byte Offset | 7 6 5 4 3 2 1 0 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 0 | Category |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 1 ~ 2 | Version |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 3 | Type |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 4 | Command |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 5 | Reserved0 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 6 | Reserved1 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 7 | Reserved2 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 8 | Reserved3 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 9 | Reserved4 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | 10 | DATA 0 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ * : : : : : : : : : : : : :
+ * +---------------+---+---+---+---+---+---+---+---+
+ * | N + 10 - 1 | DATA N-1 |
+ * +---------------+---+---+---+---+---+---+---+---+
+ *
+ * +----------+------------+------------------------------------------------+
+ * | Field | Byte | |
+ * +----------+------------+------------------------------------------------+
+ * | Category | 0 | The destination category. |
+ * +----------+------------+------------------------------------------------+
+ * | Version | 1 ~ 2 | The category version of the sender of the |
+ * | | | packet. |
+ * | | | The first byte represent the major version of |
+ * | | | the packet.The second byte represent the minor |
+ * | | | version of the packet. |
+ * +----------+------------+------------------------------------------------+
+ * | Type | 3 | The message type of current message packet. |
+ * +----------+------------+------------------------------------------------+
+ * | Command | 4 | The command byte sent to remote processor/SoC. |
+ * +----------+------------+------------------------------------------------+
+ * | Reserved | 5 ~ 9 | Reserved field for future extension. |
+ * +----------+------------+------------------------------------------------+
+ * | Data | N | The data payload of the message packet. |
+ * +----------+------------+------------------------------------------------+
+ *
+ * Audio control:
+ * SRTM Audio Control Category Request Command Table:
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | Category | Version | Type | Command | Data | Function |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x00 | Data[0]: Audio Device Index | Open a TX Instance. |
+ * | | | | | Data[1]: format | |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x01 | Data[0]: Audio Device Index | Start a TX Instance. |
+ * | | | | | Same as above command | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x02 | Data[0]: Audio Device Index | Pause a TX Instance. |
+ * | | | | | Same as above command | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x03 | Data[0]: Audio Device Index | Resume a TX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x04 | Data[0]: Audio Device Index | Stop a TX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x05 | Data[0]: Audio Device Index | Close a TX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x06 | Data[0]: Audio Device Index | Set Parameters for |
+ * | | | | | Data[1]: format | a TX Instance. |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x07 | Data[0]: Audio Device Index | Set TX Buffer. |
+ * | | | | | Data[1-6]: reserved | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x08 | Data[0]: Audio Device Index | Suspend a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x09 | Data[0]: Audio Device Index | Resume a TX Instance. |
+ * | | | | | Data[1]: format | |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0A | Data[0]: Audio Device Index | Open a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0B | Data[0]: Audio Device Index | Start a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0C | Data[0]: Audio Device Index | Pause a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0D | Data[0]: Audio Device Index | Resume a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0E | Data[0]: Audio Device Index | Stop a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x0F | Data[0]: Audio Device Index | Close a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x10 | Data[0]: Audio Device Index | Set Parameters for |
+ * | | | | | Data[1]: format | a RX Instance. |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x11 | Data[0]: Audio Device Index | Set RX Buffer. |
+ * | | | | | Data[1-6]: reserved | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x12 | Data[0]: Audio Device Index | Suspend a RX Instance.|
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x13 | Data[0]: Audio Device Index | Resume a RX Instance. |
+ * | | | | | Data[1]: format | |
+ * | | | | | Data[2]: channels | |
+ * | | | | | Data[3-6]: samplerate | |
+ * | | | | | Data[7-10]: buffer_addr | |
+ * | | | | | Data[11-14]: buffer_size | |
+ * | | | | | Data[15-18]: period_size | |
+ * | | | | | Data[19-22]: buffer_tail | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x14 | Data[0]: Audio Device Index | Set register value |
+ * | | | | | Data[1-6]: reserved | to codec |
+ * | | | | | Data[7-10]: register | |
+ * | | | | | Data[11-14]: value | |
+ * | | | | | Data[15-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x00 | 0x15 | Data[0]: Audio Device Index | Get register value |
+ * | | | | | Data[1-6]: reserved | from codec |
+ * | | | | | Data[7-10]: register | |
+ * | | | | | Data[11-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * Note 1: See <List of Sample Format> for available value of
+ * Sample Format;
+ * Note 2: See <List of Audio Channels> for available value of Channels;
+ * Note 3: Sample Rate of Set Parameters for an Audio TX Instance
+ * Command and Set Parameters for an Audio RX Instance Command is
+ * in little-endian format.
+ *
+ * SRTM Audio Control Category Response Command Table:
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | Category | Version | Type | Command | Data | Function |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x00 | Data[0]: Audio Device Index | Reply for Open |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x01 | Data[0]: Audio Device Index | Reply for Start |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x02 | Data[0]: Audio Device Index | Reply for Pause |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x03 | Data[0]: Audio Device Index | Reply for Resume |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x04 | Data[0]: Audio Device Index | Reply for Stop |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x05 | Data[0]: Audio Device Index | Reply for Close |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x06 | Data[0]: Audio Device Index | Reply for Set Param |
+ * | | | | | Data[1]: Return code | for a TX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x07 | Data[0]: Audio Device Index | Reply for Set |
+ * | | | | | Data[1]: Return code | TX Buffer |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x08 | Data[0]: Audio Device Index | Reply for Suspend |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x09 | Data[0]: Audio Device Index | Reply for Resume |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0A | Data[0]: Audio Device Index | Reply for Open |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0B | Data[0]: Audio Device Index | Reply for Start |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0C | Data[0]: Audio Device Index | Reply for Pause |
+ * | | | | | Data[1]: Return code | a TX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0D | Data[0]: Audio Device Index | Reply for Resume |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0E | Data[0]: Audio Device Index | Reply for Stop |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x0F | Data[0]: Audio Device Index | Reply for Close |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x10 | Data[0]: Audio Device Index | Reply for Set Param |
+ * | | | | | Data[1]: Return code | for a RX Instance. |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x11 | Data[0]: Audio Device Index | Reply for Set |
+ * | | | | | Data[1]: Return code | RX Buffer |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x12 | Data[0]: Audio Device Index | Reply for Suspend |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x13 | Data[0]: Audio Device Index | Reply for Resume |
+ * | | | | | Data[1]: Return code | a RX Instance |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x14 | Data[0]: Audio Device Index | Reply for Set codec |
+ * | | | | | Data[1]: Return code | register value |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x01 | 0x15 | Data[0]: Audio Device Index | Reply for Get codec |
+ * | | | | | Data[1]: Return code | register value |
+ * | | | | | Data[2-6]: reserved | |
+ * | | | | | Data[7-10]: register | |
+ * | | | | | Data[11-14]: value | |
+ * | | | | | Data[15-22]: reserved | |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ *
+ * SRTM Audio Control Category Notification Command Table:
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | Category | Version | Type | Command | Data | Function |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x02 | 0x00 | Data[0]: Audio Device Index | Notify one TX period |
+ * | | | | | | is finished |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ * | 0x03 | 0x0100 | 0x02 | 0x01 | Data[0]: Audio Device Index | Notify one RX period |
+ * | | | | | | is finished |
+ * +----------+---------+------+---------+-------------------------------+-----------------------+
+ *
+ * List of Sample Format:
+ * +------------------+-----------------------+
+ * | Sample Format | Description |
+ * +------------------+-----------------------+
+ * | 0x0 | S16_LE |
+ * +------------------+-----------------------+
+ * | 0x1 | S24_LE |
+ * +------------------+-----------------------+
+ *
+ * List of Audio Channels
+ * +------------------+-----------------------+
+ * | Audio Channel | Description |
+ * +------------------+-----------------------+
+ * | 0x0 | Left Channel |
+ * +------------------+-----------------------+
+ * | 0x1 | Right Channel |
+ * +------------------+---------------- ------+
+ * | 0x2 | Left & Right Channel |
+ * +------------------+-----------------------+
+ *
+ */
+
+#ifndef _IMX_PCM_RPMSG_H
+#define _IMX_PCM_RPMSG_H
+
+#include <linux/pm_qos.h>
+#include <linux/interrupt.h>
+#include <sound/dmaengine_pcm.h>
+
+#define RPMSG_TIMEOUT 1000
+
+/* RPMSG Command (TYPE A)*/
+#define TX_OPEN 0x0
+#define TX_START 0x1
+#define TX_PAUSE 0x2
+#define TX_RESTART 0x3
+#define TX_TERMINATE 0x4
+#define TX_CLOSE 0x5
+#define TX_HW_PARAM 0x6
+#define TX_BUFFER 0x7
+#define TX_SUSPEND 0x8
+#define TX_RESUME 0x9
+
+#define RX_OPEN 0xA
+#define RX_START 0xB
+#define RX_PAUSE 0xC
+#define RX_RESTART 0xD
+#define RX_TERMINATE 0xE
+#define RX_CLOSE 0xF
+#define RX_HW_PARAM 0x10
+#define RX_BUFFER 0x11
+#define RX_SUSPEND 0x12
+#define RX_RESUME 0x13
+#define SET_CODEC_VALUE 0x14
+#define GET_CODEC_VALUE 0x15
+#define TX_POINTER 0x16
+#define RX_POINTER 0x17
+/* Total msg numver for type A */
+#define MSG_TYPE_A_NUM 0x18
+
+/* RPMSG Command (TYPE C)*/
+#define TX_PERIOD_DONE 0x0
+#define RX_PERIOD_DONE 0x1
+/* Total msg numver for type C */
+#define MSG_TYPE_C_NUM 0x2
+
+#define MSG_MAX_NUM (MSG_TYPE_A_NUM + MSG_TYPE_C_NUM)
+
+#define MSG_TYPE_A 0x0
+#define MSG_TYPE_B 0x1
+#define MSG_TYPE_C 0x2
+
+#define RESP_NONE 0x0
+#define RESP_NOT_ALLOWED 0x1
+#define RESP_SUCCESS 0x2
+#define RESP_FAILED 0x3
+
+#define RPMSG_S16_LE 0x0
+#define RPMSG_S24_LE 0x1
+#define RPMSG_S32_LE 0x2
+#define RPMSG_DSD_U16_LE 0x3
+#define RPMSG_DSD_U24_LE 0x4
+#define RPMSG_DSD_U32_LE 0x5
+
+#define RPMSG_CH_LEFT 0x0
+#define RPMSG_CH_RIGHT 0x1
+#define RPMSG_CH_STEREO 0x2
+
+#define WORK_MAX_NUM 0x30
+
+/* Category define */
+#define IMX_RMPSG_LIFECYCLE 1
+#define IMX_RPMSG_PMIC 2
+#define IMX_RPMSG_AUDIO 3
+#define IMX_RPMSG_KEY 4
+#define IMX_RPMSG_GPIO 5
+#define IMX_RPMSG_RTC 6
+#define IMX_RPMSG_SENSOR 7
+
+/* rpmsg version */
+#define IMX_RMPSG_MAJOR 1
+#define IMX_RMPSG_MINOR 0
+
+#define TX SNDRV_PCM_STREAM_PLAYBACK
+#define RX SNDRV_PCM_STREAM_CAPTURE
+
+/**
+ * struct rpmsg_head: rpmsg header structure
+ *
+ * @cate: category
+ * @major: major version
+ * @minor: minor version
+ * @type: message type (A/B/C)
+ * @cmd: message command
+ * @reserved: reserved space
+ */
+struct rpmsg_head {
+ u8 cate;
+ u8 major;
+ u8 minor;
+ u8 type;
+ u8 cmd;
+ u8 reserved[5];
+} __packed;
+
+/**
+ * struct param_s: sent rpmsg parameter
+ *
+ * @audioindex: audio instance index
+ * @format: audio format
+ * @channels: audio channel number
+ * @rate: sample rate
+ * @buffer_addr: dma buffer physical address or register for SET_CODEC_VALUE
+ * @buffer_size: dma buffer size or register value for SET_CODEC_VALUE
+ * @period_size: period size
+ * @buffer_tail: current period index
+ */
+struct param_s {
+ unsigned char audioindex;
+ unsigned char format;
+ unsigned char channels;
+ unsigned int rate;
+ unsigned int buffer_addr;
+ unsigned int buffer_size;
+ unsigned int period_size;
+ unsigned int buffer_tail;
+} __packed;
+
+/**
+ * struct param_s: send rpmsg parameter
+ *
+ * @audioindex: audio instance index
+ * @resp: response value
+ * @reserved1: reserved space
+ * @buffer_offset: the consumed offset of buffer
+ * @reg_addr: register addr of codec
+ * @reg_data: register value of codec
+ * @reserved2: reserved space
+ * @buffer_tail: current period index
+ */
+struct param_r {
+ unsigned char audioindex;
+ unsigned char resp;
+ unsigned char reserved1[1];
+ unsigned int buffer_offset;
+ unsigned int reg_addr;
+ unsigned int reg_data;
+ unsigned char reserved2[4];
+ unsigned int buffer_tail;
+} __packed;
+
+/* Struct of sent message */
+struct rpmsg_s_msg {
+ struct rpmsg_head header;
+ struct param_s param;
+};
+
+/* Struct of received message */
+struct rpmsg_r_msg {
+ struct rpmsg_head header;
+ struct param_r param;
+};
+
+/* Struct of rpmsg */
+struct rpmsg_msg {
+ struct rpmsg_s_msg s_msg;
+ struct rpmsg_r_msg r_msg;
+};
+
+/* Struct of rpmsg for workqueue */
+struct work_of_rpmsg {
+ struct rpmsg_info *info;
+ /* Sent msg for each work */
+ struct rpmsg_msg msg;
+ struct work_struct work;
+};
+
+/* Struct of timer */
+struct stream_timer {
+ struct timer_list timer;
+ struct rpmsg_info *info;
+ struct snd_pcm_substream *substream;
+};
+
+typedef void (*dma_callback)(void *arg);
+
+/**
+ * struct rpmsg_info: rpmsg audio information
+ *
+ * @rpdev: pointer of rpmsg_device
+ * @dev: pointer for imx_pcm_rpmsg device
+ * @cmd_complete: command is finished
+ * @pm_qos_req: request of pm qos
+ * @r_msg: received rpmsg
+ * @msg: array of rpmsg
+ * @notify: notification msg (type C) for TX & RX
+ * @notify_updated: notification flag for TX & RX
+ * @rpmsg_wq: rpmsg workqueue
+ * @work_list: array of work list for workqueue
+ * @work_write_index: write index of work list
+ * @work_read_index: read index of work list
+ * @msg_drop_count: counter of dropped msg for TX & RX
+ * @num_period: period number for TX & RX
+ * @callback_param: parameter for period elapse callback for TX & RX
+ * @callback: period elapse callback for TX & RX
+ * @send_message: function pointer for send message
+ * @lock: spin lock for TX & RX
+ * @wq_lock: lock for work queue
+ * @msg_lock: lock for send message
+ * @stream_timer: timer for tigger workqueue
+ */
+struct rpmsg_info {
+ struct rpmsg_device *rpdev;
+ struct device *dev;
+ struct completion cmd_complete;
+ struct pm_qos_request pm_qos_req;
+
+ /* Received msg (global) */
+ struct rpmsg_r_msg r_msg;
+ struct rpmsg_msg msg[MSG_MAX_NUM];
+ /* period done */
+ struct rpmsg_msg notify[2];
+ bool notify_updated[2];
+
+ struct workqueue_struct *rpmsg_wq;
+ struct work_of_rpmsg work_list[WORK_MAX_NUM];
+ int work_write_index;
+ int work_read_index;
+ int msg_drop_count[2];
+ int num_period[2];
+ void *callback_param[2];
+ dma_callback callback[2];
+ int (*send_message)(struct rpmsg_msg *msg, struct rpmsg_info *info);
+ spinlock_t lock[2]; /* spin lock for resource protection */
+ spinlock_t wq_lock; /* spin lock for resource protection */
+ struct mutex msg_lock; /* mutex for resource protection */
+ struct stream_timer stream_timer[2];
+};
+
+#define IMX_PCM_DRV_NAME "imx_pcm_rpmsg"
+
+#endif /* IMX_PCM_RPMSG_H */
--
2.27.0
^ permalink raw reply related
* [PATCH v3 6/7] ASoC: imx-rpmsg: Add machine driver for audio base on rpmsg
From: Shengjiu Wang @ 2021-02-25 2:52 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, alsa-devel, linux-kernel, timur,
nicoleotsuka, Xiubo.Lee, festevam, linuxppc-dev, robh+dt,
devicetree
In-Reply-To: <1614221563-26822-1-git-send-email-shengjiu.wang@nxp.com>
The platform device is not registered by device tree or
cpu dai driver, it is registered by the rpmsg channel,
So add a dedicated machine driver to handle this case.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/Kconfig | 12 ++++
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/imx-rpmsg.c | 148 ++++++++++++++++++++++++++++++++++++++
3 files changed, 162 insertions(+)
create mode 100644 sound/soc/fsl/imx-rpmsg.c
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 749c44fc0759..3557866d3fa2 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -334,6 +334,18 @@ config SND_SOC_IMX_HDMI
Say Y if you want to add support for SoC audio on an i.MX board with
IMX HDMI.
+config SND_SOC_IMX_RPMSG
+ tristate "SoC Audio support for i.MX boards with rpmsg"
+ depends on RPMSG
+ select SND_SOC_IMX_PCM_RPMSG
+ select SND_SOC_IMX_AUDIO_RPMSG
+ select SND_SOC_FSL_RPMSG
+ help
+ SoC Audio support for i.MX boards with rpmsg.
+ There should be rpmsg devices defined in other core (M core)
+ Say Y if you want to add support for SoC audio on an i.MX board with
+ a rpmsg devices.
+
endif # SND_IMX_SOC
endmenu
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index ce4f4324c3a2..f146ce464acd 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -70,6 +70,7 @@ snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-audmix-objs := imx-audmix.o
snd-soc-imx-hdmi-objs := imx-hdmi.o
+snd-soc-imx-rpmsg-objs := imx-rpmsg.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
obj-$(CONFIG_SND_SOC_IMX_ES8328) += snd-soc-imx-es8328.o
@@ -77,3 +78,4 @@ obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_AUDMIX) += snd-soc-imx-audmix.o
obj-$(CONFIG_SND_SOC_IMX_HDMI) += snd-soc-imx-hdmi.o
+obj-$(CONFIG_SND_SOC_IMX_RPMSG) += snd-soc-imx-rpmsg.o
diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
new file mode 100644
index 000000000000..a87dcbce4f36
--- /dev/null
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2020 NXP
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/i2c.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/control.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include "imx-pcm-rpmsg.h"
+
+struct imx_rpmsg {
+ struct snd_soc_dai_link dai;
+ struct snd_soc_card card;
+};
+
+static int imx_rpmsg_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link_component *dlc;
+ struct platform_device *cpu_pdev;
+ struct of_phandle_args args;
+ struct device_node *cpu_np;
+ struct imx_rpmsg *data;
+ int ret;
+
+ dlc = devm_kzalloc(&pdev->dev, 3 * sizeof(*dlc), GFP_KERNEL);
+ if (!dlc)
+ return -ENOMEM;
+
+ cpu_np = of_parse_phandle(pdev->dev.of_node, "audio-cpu", 0);
+ if (!cpu_np) {
+ dev_err(&pdev->dev, "cpu dai phandle missing or invalid\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ cpu_pdev = of_find_device_by_node(cpu_np);
+ if (!cpu_pdev) {
+ dev_err(&pdev->dev, "failed to find rpmsg platform device\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ ret = of_reserved_mem_device_init_by_idx(&pdev->dev, pdev->dev.of_node, 0);
+ if (ret)
+ dev_warn(&pdev->dev, "no reserved DMA memory\n");
+
+ data->dai.cpus = &dlc[0];
+ data->dai.num_cpus = 1;
+ data->dai.platforms = &dlc[1];
+ data->dai.num_platforms = 1;
+ data->dai.codecs = &dlc[2];
+ data->dai.num_codecs = 1;
+
+ data->dai.name = "rpmsg hifi";
+ data->dai.stream_name = "rpmsg hifi";
+ data->dai.dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+
+ /* Optional codec node */
+ ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+ "audio-codec", 0, 0, &args);
+ if (ret) {
+ data->dai.codecs->dai_name = "snd-soc-dummy-dai";
+ data->dai.codecs->name = "snd-soc-dummy";
+ } else {
+ data->dai.codecs->of_node = args.np;
+ ret = snd_soc_get_dai_name(&args, &data->dai.codecs->dai_name);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to get codec_dai_name\n");
+ goto fail;
+ }
+ }
+
+ data->dai.cpus->dai_name = dev_name(&cpu_pdev->dev);
+ data->dai.platforms->name = IMX_PCM_DRV_NAME;
+ data->dai.playback_only = true;
+ data->dai.capture_only = true;
+ data->card.num_links = 1;
+ data->card.dai_link = &data->dai;
+
+ if (of_property_read_bool(pdev->dev.of_node, "rpmsg-out"))
+ data->dai.capture_only = false;
+
+ if (of_property_read_bool(pdev->dev.of_node, "rpmsg-in"))
+ data->dai.playback_only = false;
+
+ if (data->dai.playback_only && data->dai.capture_only) {
+ dev_err(&pdev->dev, "no enabled rpmsg DAI link\n");
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ data->card.dev = &pdev->dev;
+ data->card.owner = THIS_MODULE;
+ ret = snd_soc_of_parse_card_name(&data->card, "model");
+ if (ret)
+ goto fail;
+
+ platform_set_drvdata(pdev, &data->card);
+ snd_soc_card_set_drvdata(&data->card, data);
+ ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ goto fail;
+ }
+
+fail:
+ if (cpu_np)
+ of_node_put(cpu_np);
+ return ret;
+}
+
+static const struct of_device_id imx_rpmsg_dt_ids[] = {
+ { .compatible = "fsl,imx-audio-rpmsg", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_rpmsg_dt_ids);
+
+static struct platform_driver imx_rpmsg_driver = {
+ .driver = {
+ .name = "imx-audio-rpmsg",
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = imx_rpmsg_dt_ids,
+ },
+ .probe = imx_rpmsg_probe,
+};
+module_platform_driver(imx_rpmsg_driver);
+
+MODULE_DESCRIPTION("Freescale SoC Audio RPMSG Machine Driver");
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_ALIAS("platform:imx-rpmsg");
+MODULE_LICENSE("GPL v2");
--
2.27.0
^ permalink raw reply related
* [PATCH v3 4/7] ASoC: imx-audio-rpmsg: Add rpmsg_driver for audio channel
From: Shengjiu Wang @ 2021-02-25 2:52 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, alsa-devel, linux-kernel, timur,
nicoleotsuka, Xiubo.Lee, festevam, linuxppc-dev, robh+dt,
devicetree
In-Reply-To: <1614221563-26822-1-git-send-email-shengjiu.wang@nxp.com>
This driver is used to accept the message from rpmsg audio
channel, and if this driver is probed, it will help to register
the platform driver, the platform driver will use this
audio channel to send and receive message to and from Cortex-M
core.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/Kconfig | 4 +
sound/soc/fsl/Makefile | 1 +
sound/soc/fsl/imx-audio-rpmsg.c | 151 ++++++++++++++++++++++++++++++++
3 files changed, 156 insertions(+)
create mode 100644 sound/soc/fsl/imx-audio-rpmsg.c
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index a688c3c2efbc..84d9f0f1f75b 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -126,6 +126,10 @@ config SND_SOC_IMX_PCM_DMA
tristate
select SND_SOC_GENERIC_DMAENGINE_PCM
+config SND_SOC_IMX_AUDIO_RPMSG
+ tristate
+ depends on RPMSG
+
config SND_SOC_IMX_AUDMUX
tristate "Digital Audio Mux module support"
help
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index b63802f345cc..f08f3cd07ff5 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
+obj-$(CONFIG_SND_SOC_IMX_AUDIO_RPMSG) += imx-audio-rpmsg.o
# i.MX Machine Support
snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
diff --git a/sound/soc/fsl/imx-audio-rpmsg.c b/sound/soc/fsl/imx-audio-rpmsg.c
new file mode 100644
index 000000000000..145edb1492b4
--- /dev/null
+++ b/sound/soc/fsl/imx-audio-rpmsg.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2017-2020 NXP
+
+#include <linux/module.h>
+#include <linux/rpmsg.h>
+#include "imx-pcm-rpmsg.h"
+
+/*
+ * struct imx_audio_rpmsg: private data
+ *
+ * @rpmsg_pdev: pointer of platform device
+ */
+struct imx_audio_rpmsg {
+ struct platform_device *rpmsg_pdev;
+};
+
+static int imx_audio_rpmsg_cb(struct rpmsg_device *rpdev, void *data, int len,
+ void *priv, u32 src)
+{
+ struct imx_audio_rpmsg *rpmsg = dev_get_drvdata(&rpdev->dev);
+ struct rpmsg_info *info = platform_get_drvdata(rpmsg->rpmsg_pdev);
+ struct rpmsg_r_msg *r_msg = (struct rpmsg_r_msg *)data;
+ struct rpmsg_msg *msg;
+ unsigned long flags;
+
+ dev_dbg(&rpdev->dev, "get from%d: cmd:%d. %d\n",
+ src, r_msg->header.cmd, r_msg->param.resp);
+
+ switch (r_msg->header.type) {
+ case MSG_TYPE_C:
+ /* TYPE C is notification from M core */
+ switch (r_msg->header.cmd) {
+ case TX_PERIOD_DONE:
+ spin_lock_irqsave(&info->lock[TX], flags);
+ msg = &info->msg[TX_PERIOD_DONE + MSG_TYPE_A_NUM];
+
+ /*
+ * Low power mode: get the buffer pointer from
+ * receive msg.
+ */
+ if (r_msg->header.major == 1 &&
+ r_msg->header.minor == 2)
+ msg->r_msg.param.buffer_tail =
+ r_msg->param.buffer_tail;
+ else
+ msg->r_msg.param.buffer_tail++;
+
+ msg->r_msg.param.buffer_tail %= info->num_period[TX];
+ spin_unlock_irqrestore(&info->lock[TX], flags);
+ info->callback[TX](info->callback_param[TX]);
+ break;
+ case RX_PERIOD_DONE:
+ spin_lock_irqsave(&info->lock[RX], flags);
+ msg = &info->msg[RX_PERIOD_DONE + MSG_TYPE_A_NUM];
+
+ if (r_msg->header.major == 1 &&
+ r_msg->header.minor == 2)
+ msg->r_msg.param.buffer_tail =
+ r_msg->param.buffer_tail;
+ else
+ msg->r_msg.param.buffer_tail++;
+
+ msg->r_msg.param.buffer_tail %= info->num_period[1];
+ spin_unlock_irqrestore(&info->lock[RX], flags);
+ info->callback[RX](info->callback_param[RX]);
+ break;
+ default:
+ dev_warn(&rpdev->dev, "unknown msg command\n");
+ break;
+ }
+ break;
+ case MSG_TYPE_B:
+ /* TYPE B is response msg */
+ memcpy(&info->r_msg, r_msg, sizeof(struct rpmsg_r_msg));
+ complete(&info->cmd_complete);
+ break;
+ default:
+ dev_warn(&rpdev->dev, "unknown msg type\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int imx_audio_rpmsg_probe(struct rpmsg_device *rpdev)
+{
+ struct imx_audio_rpmsg *data;
+ int ret = 0;
+
+ dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+ rpdev->src, rpdev->dst);
+
+ data = devm_kzalloc(&rpdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ dev_set_drvdata(&rpdev->dev, data);
+
+ /* Register platform driver for rpmsg routine */
+ data->rpmsg_pdev = platform_device_register_data(&rpdev->dev,
+ IMX_PCM_DRV_NAME,
+ PLATFORM_DEVID_NONE,
+ NULL, 0);
+ if (IS_ERR(data->rpmsg_pdev)) {
+ dev_err(&rpdev->dev, "failed to register rpmsg platform.\n");
+ ret = PTR_ERR(data->rpmsg_pdev);
+ }
+
+ return ret;
+}
+
+static void imx_audio_rpmsg_remove(struct rpmsg_device *rpdev)
+{
+ struct imx_audio_rpmsg *data = dev_get_drvdata(&rpdev->dev);
+
+ if (data->rpmsg_pdev)
+ platform_device_unregister(data->rpmsg_pdev);
+
+ dev_info(&rpdev->dev, "audio rpmsg driver is removed\n");
+}
+
+static struct rpmsg_device_id imx_audio_rpmsg_id_table[] = {
+ { .name = "rpmsg-audio-channel" },
+ { },
+};
+
+static struct rpmsg_driver imx_audio_rpmsg_driver = {
+ .drv.name = "imx_audio_rpmsg",
+ .drv.owner = THIS_MODULE,
+ .id_table = imx_audio_rpmsg_id_table,
+ .probe = imx_audio_rpmsg_probe,
+ .callback = imx_audio_rpmsg_cb,
+ .remove = imx_audio_rpmsg_remove,
+};
+
+static int __init imx_audio_rpmsg_init(void)
+{
+ return register_rpmsg_driver(&imx_audio_rpmsg_driver);
+}
+
+static void __exit imx_audio_rpmsg_exit(void)
+{
+ unregister_rpmsg_driver(&imx_audio_rpmsg_driver);
+}
+module_init(imx_audio_rpmsg_init);
+module_exit(imx_audio_rpmsg_exit);
+
+MODULE_DESCRIPTION("Freescale SoC Audio RPMSG interface");
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_ALIAS("platform:imx_audio_rpmsg");
+MODULE_LICENSE("GPL v2");
--
2.27.0
^ permalink raw reply related
* [PATCH v3 3/7] ASoC: dt-bindings: fsl_rpmsg: Add binding doc for rpmsg cpu dai driver
From: Shengjiu Wang @ 2021-02-25 2:52 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, alsa-devel, linux-kernel, timur,
nicoleotsuka, Xiubo.Lee, festevam, linuxppc-dev, robh+dt,
devicetree
In-Reply-To: <1614221563-26822-1-git-send-email-shengjiu.wang@nxp.com>
fsl_rpmsg cpu dai driver is driver for rpmsg audio, which is mainly used
for getting the user's configuration from device tree and configure the
clocks which is used by Cortex-M core. So in this document define the
needed property.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
.../devicetree/bindings/sound/fsl,rpmsg.yaml | 94 +++++++++++++++++++
1 file changed, 94 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
diff --git a/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
new file mode 100644
index 000000000000..ab6d3b8d0d38
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/fsl,rpmsg.yaml
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/fsl,rpmsg.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP Audio RPMSG CPU DAI Controller
+
+maintainers:
+ - Shengjiu Wang <shengjiu.wang@nxp.com>
+
+description: |
+ fsl_rpmsg cpu dai driver is virtual driver for rpmsg audio, which doesn't
+ touch hardware. It is mainly used for getting the user's configuration
+ from device tree and configure the clocks which is used by Cortex-M core.
+ So in this document define the needed property.
+
+properties:
+ compatible:
+ enum:
+ - fsl,imx7ulp-rpmsg
+ - fsl,imx8mn-rpmsg
+ - fsl,imx8mm-rpmsg
+ - fsl,imx8mp-rpmsg
+
+ clocks:
+ items:
+ - description: Peripheral clock for register access
+ - description: Master clock
+ - description: DMA clock for DMA register access
+ - description: Parent clock for multiple of 8kHz sample rates
+ - description: Parent clock for multiple of 11kHz sample rates
+ minItems: 5
+
+ clock-names:
+ items:
+ - const: ipg
+ - const: mclk
+ - const: dma
+ - const: pll8k
+ - const: pll11k
+ minItems: 5
+
+ power-domains:
+ maxItems: 1
+
+ fsl,audioindex:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ default: 0
+ description: Instance index for sound card in
+ M core side, which share one rpmsg
+ channel.
+
+ fsl,version:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [1, 2]
+ default: 2
+ description: The version of M core image, which is
+ to make driver compatible with different image.
+
+ fsl,buffer-size:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: pre allocate dma buffer size
+
+ fsl,enable-lpa:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: enable low power audio path.
+
+ fsl,codec-type:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1, 2]
+ default: 0
+ description: Sometimes the codec is registered by
+ driver not the device tree, this items
+ can be used to distinguish codecs.
+
+required:
+ - compatible
+ - fsl,audioindex
+ - fsl,version
+ - fsl,buffer-size
+
+additionalProperties: false
+
+examples:
+ - |
+ rpmsg_audio: rpmsg_audio {
+ compatible = "fsl,imx8mn-rpmsg";
+ fsl,audioindex = <0> ;
+ fsl,version = <2>;
+ fsl,buffer-size = <0x6000000>;
+ fsl,enable-lpa;
+ };
--
2.27.0
^ permalink raw reply related
* [PATCH v3 2/7] ASoC: fsl_rpmsg: Add CPU DAI driver for audio base on rpmsg
From: Shengjiu Wang @ 2021-02-25 2:52 UTC (permalink / raw)
To: lgirdwood, broonie, perex, tiwai, alsa-devel, linux-kernel, timur,
nicoleotsuka, Xiubo.Lee, festevam, linuxppc-dev, robh+dt,
devicetree
In-Reply-To: <1614221563-26822-1-git-send-email-shengjiu.wang@nxp.com>
This is a cpu dai driver for rpmsg audio use case,
which is mainly used for getting the user's configuration
from devicetree and configure the clocks which is used by
Cortex-M core.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/Kconfig | 7 +
sound/soc/fsl/Makefile | 2 +
sound/soc/fsl/fsl_rpmsg.c | 261 ++++++++++++++++++++++++++++++++++++++
sound/soc/fsl/fsl_rpmsg.h | 40 ++++++
4 files changed, 310 insertions(+)
create mode 100644 sound/soc/fsl/fsl_rpmsg.c
create mode 100644 sound/soc/fsl/fsl_rpmsg.h
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index d7f30036d434..a688c3c2efbc 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -115,6 +115,13 @@ config SND_SOC_FSL_AUD2HTX
config SND_SOC_FSL_UTILS
tristate
+config SND_SOC_FSL_RPMSG
+ tristate "Audio Base on RPMSG support"
+ help
+ Say Y if you want to add rpmsg audio support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
+
config SND_SOC_IMX_PCM_DMA
tristate
select SND_SOC_GENERIC_DMAENGINE_PCM
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index 8c5fa8a859c0..b63802f345cc 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -27,6 +27,7 @@ snd-soc-fsl-mqs-objs := fsl_mqs.o
snd-soc-fsl-easrc-objs := fsl_easrc.o
snd-soc-fsl-xcvr-objs := fsl_xcvr.o
snd-soc-fsl-aud2htx-objs := fsl_aud2htx.o
+snd-soc-fsl-rpmsg-objs := fsl_rpmsg.o
obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o
obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o
@@ -42,6 +43,7 @@ obj-$(CONFIG_SND_SOC_FSL_EASRC) += snd-soc-fsl-easrc.o
obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
obj-$(CONFIG_SND_SOC_FSL_XCVR) += snd-soc-fsl-xcvr.o
obj-$(CONFIG_SND_SOC_FSL_AUD2HTX) += snd-soc-fsl-aud2htx.o
+obj-$(CONFIG_SND_SOC_FSL_RPMSG) += snd-soc-fsl-rpmsg.o
# MPC5200 Platform Support
obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c
new file mode 100644
index 000000000000..1082a3b3a18d
--- /dev/null
+++ b/sound/soc/fsl/fsl_rpmsg.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018-2021 NXP
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <linux/rpmsg.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_rpmsg.h"
+#include "imx-pcm.h"
+
+#define FSL_RPMSG_RATES (SNDRV_PCM_RATE_8000 | \
+ SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_48000)
+#define FSL_RPMSG_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+
+static const unsigned int fsl_rpmsg_rates[] = {
+ 8000, 11025, 16000, 22050, 44100,
+ 32000, 48000, 96000, 88200, 176400, 192000,
+ 352800, 384000, 705600, 768000, 1411200, 2822400,
+};
+
+static const struct snd_pcm_hw_constraint_list fsl_rpmsg_rate_constraints = {
+ .count = ARRAY_SIZE(fsl_rpmsg_rates),
+ .list = fsl_rpmsg_rates,
+};
+
+static int fsl_rpmsg_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
+ struct clk *p = rpmsg->mclk, *pll = 0, *npll = 0;
+ unsigned int rate = params_rate(params);
+ int ret = 0;
+
+ /* Get current pll parent */
+ while (p && rpmsg->pll8k && rpmsg->pll11k) {
+ struct clk *pp = clk_get_parent(p);
+
+ if (clk_is_match(pp, rpmsg->pll8k) ||
+ clk_is_match(pp, rpmsg->pll11k)) {
+ pll = pp;
+ break;
+ }
+ p = pp;
+ }
+
+ /* Switch to another pll parent if needed. */
+ if (pll) {
+ npll = (do_div(rate, 8000) ? rpmsg->pll11k : rpmsg->pll8k);
+ if (!clk_is_match(pll, npll)) {
+ ret = clk_set_parent(p, npll);
+ if (ret < 0)
+ dev_warn(dai->dev, "failed to set parent %s: %d\n",
+ __clk_get_name(npll), ret);
+ }
+ }
+
+ if (!(rpmsg->mclk_streams & BIT(substream->stream))) {
+ ret = clk_prepare_enable(rpmsg->mclk);
+ if (ret) {
+ dev_err(dai->dev, "failed to enable mclk: %d\n", ret);
+ return ret;
+ }
+
+ rpmsg->mclk_streams |= BIT(substream->stream);
+ }
+
+ return ret;
+}
+
+static int fsl_rpmsg_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_rpmsg *rpmsg = snd_soc_dai_get_drvdata(dai);
+
+ if (rpmsg->mclk_streams & BIT(substream->stream)) {
+ clk_disable_unprepare(rpmsg->mclk);
+ rpmsg->mclk_streams &= ~BIT(substream->stream);
+ }
+
+ return 0;
+}
+
+static int fsl_rpmsg_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ int ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &fsl_rpmsg_rate_constraints);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = {
+ .startup = fsl_rpmsg_startup,
+ .hw_params = fsl_rpmsg_hw_params,
+ .hw_free = fsl_rpmsg_hw_free,
+};
+
+static struct snd_soc_dai_driver fsl_rpmsg_dai = {
+ .playback = {
+ .stream_name = "CPU-Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_RPMSG_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CPU-Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_RPMSG_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .symmetric_channels = 1,
+ .symmetric_sample_bits = 1,
+ .ops = &fsl_rpmsg_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_component = {
+ .name = "fsl-rpmsg",
+};
+
+static const struct of_device_id fsl_rpmsg_ids[] = {
+ { .compatible = "fsl,imx7ulp-rpmsg"},
+ { .compatible = "fsl,imx8mm-rpmsg"},
+ { .compatible = "fsl,imx8mn-rpmsg"},
+ { .compatible = "fsl,imx8mp-rpmsg"},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_rpmsg_ids);
+
+static int fsl_rpmsg_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_rpmsg *rpmsg;
+ int ret;
+
+ rpmsg = devm_kzalloc(&pdev->dev, sizeof(struct fsl_rpmsg), GFP_KERNEL);
+ if (!rpmsg)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(np, "fsl,audioindex", &rpmsg->audioindex);
+ if (ret)
+ rpmsg->audioindex = 0;
+
+ if (of_property_read_u32(np, "fsl,buffer-size", &rpmsg->buffer_size))
+ rpmsg->buffer_size = IMX_DEFAULT_DMABUF_SIZE;
+
+ if (of_property_read_bool(pdev->dev.of_node, "fsl,enable-lpa"))
+ rpmsg->enable_lpa = 1;
+
+ ret = of_property_read_u32(np, "fsl,version", &rpmsg->version);
+ if (ret)
+ rpmsg->version = API_VERSION_V2;
+
+ /*Get the optional clocks */
+ rpmsg->ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(rpmsg->ipg))
+ rpmsg->ipg = NULL;
+
+ rpmsg->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(rpmsg->mclk))
+ rpmsg->mclk = NULL;
+
+ rpmsg->dma = devm_clk_get(&pdev->dev, "dma");
+ if (IS_ERR(rpmsg->dma))
+ rpmsg->dma = NULL;
+
+ rpmsg->pll8k = devm_clk_get(&pdev->dev, "pll8k");
+ if (IS_ERR(rpmsg->pll8k))
+ rpmsg->pll8k = NULL;
+
+ rpmsg->pll11k = devm_clk_get(&pdev->dev, "pll11k");
+ if (IS_ERR(rpmsg->pll11k))
+ rpmsg->pll11k = NULL;
+
+ platform_set_drvdata(pdev, rpmsg);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
+ &fsl_rpmsg_dai, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int fsl_rpmsg_runtime_resume(struct device *dev)
+{
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(rpmsg->ipg);
+ if (ret) {
+ dev_err(dev, "failed to enable ipg clock: %d\n", ret);
+ goto ipg_err;
+ }
+
+ ret = clk_prepare_enable(rpmsg->dma);
+ if (ret) {
+ dev_err(dev, "Failed to enable dma clock %d\n", ret);
+ goto dma_err;
+ }
+
+ return 0;
+
+dma_err:
+ clk_disable_unprepare(rpmsg->ipg);
+ipg_err:
+ return ret;
+}
+
+static int fsl_rpmsg_runtime_suspend(struct device *dev)
+{
+ struct fsl_rpmsg *rpmsg = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(rpmsg->dma);
+ clk_disable_unprepare(rpmsg->ipg);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops fsl_rpmsg_pm_ops = {
+ SET_RUNTIME_PM_OPS(fsl_rpmsg_runtime_suspend,
+ fsl_rpmsg_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver fsl_rpmsg_driver = {
+ .probe = fsl_rpmsg_probe,
+ .driver = {
+ .name = "fsl_rpmsg",
+ .pm = &fsl_rpmsg_pm_ops,
+ .of_match_table = fsl_rpmsg_ids,
+ },
+};
+module_platform_driver(fsl_rpmsg_driver);
+
+MODULE_DESCRIPTION("Freescale SoC Audio PRMSG CPU Interface");
+MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
+MODULE_ALIAS("platform:fsl_rpmsg");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/fsl_rpmsg.h b/sound/soc/fsl/fsl_rpmsg.h
new file mode 100644
index 000000000000..8d380d172b4b
--- /dev/null
+++ b/sound/soc/fsl/fsl_rpmsg.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2017-2021 NXP
+ */
+
+#ifndef __FSL_RPMSG_H
+#define __FSL_RPMSG_H
+
+#define API_VERSION_V1 1
+#define API_VERSION_V2 2
+
+/*
+ * struct fsl_rpmsg - rpmsg private data
+ *
+ * @ipg: ipg clock for cpu dai (SAI)
+ * @mclk: master clock for cpu dai (SAI)
+ * @dma: clock for dma device
+ * @pll8k: parent clock for multiple of 8kHz frequency
+ * @pll11k: parent clock for multiple of 11kHz frequency
+ * @mclk_streams: Active streams that are using baudclk
+ * @force_lpa: force enable low power audio routine if condition satisfy
+ * @enable_lpa: enable low power audio routine according to dts setting
+ * @buffer_size: pre allocated dma buffer size
+ * @audioindex: audio instance index
+ * @version: rpmsg image version
+ */
+struct fsl_rpmsg {
+ struct clk *ipg;
+ struct clk *mclk;
+ struct clk *dma;
+ struct clk *pll8k;
+ struct clk *pll11k;
+ unsigned int mclk_streams;
+ int force_lpa;
+ int enable_lpa;
+ int buffer_size;
+ int audioindex;
+ int version;
+};
+#endif /* __FSL_RPMSG_H */
--
2.27.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox