Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] media: v4l: xilinx: Use SPDX-License-Identifier
From: Laurent Pinchart @ 2017-12-14 18:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513276340.27409.77.camel@perches.com>

Hi Joe,

On Thursday, 14 December 2017 20:32:20 EET Joe Perches wrote:
> On Thu, 2017-12-14 at 20:28 +0200, Laurent Pinchart wrote:
> > On Thursday, 14 December 2017 19:05:27 EET Mauro Carvalho Chehab wrote:
> >> Em Fri,  8 Dec 2017 18:05:37 +0530 Dhaval Shah escreveu:
> >>> SPDX-License-Identifier is used for the Xilinx Video IP and
> >>> related drivers.
> >>> 
> >>> Signed-off-by: Dhaval Shah <dhaval23031987@gmail.com>
> >> 
> >> Hi Dhaval,
> >> 
> >> You're not listed as one of the Xilinx driver maintainers. I'm afraid
> >> that, without their explicit acks, sent to the ML, I can't accept a patch
> >> touching at the driver's license tags.
> > 
> > The patch doesn't change the license, I don't see why it would cause any
> > issue. Greg isn't listed as the maintainer or copyright holder of any of
> > the 10k+ files to which he added an SPDX license header in the last
> > kernel release.
> 
> Adding a comment line that describes an implicit or
> explicit license is different than removing the license
> text itself.

The SPDX license header is meant to be equivalent to the license text. The 
only reason why the large SPDX patch didn't touch the whole kernel in one go 
was that it was easier to split in in multiple chunks. This is no different 
than not including the full GPL license in every header file but only pointing 
to it through its name and reference, as every kernel source file does.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* DT dtc warnings
From: Alexandre Belloni @ 2017-12-14 18:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAL_JsqJKCzjx3zNigu_0T=Ku=_P2SQEjTx=uVbvr_QxJQR7kYg@mail.gmail.com>

Hi Rob,

On 14/12/2017 at 12:21:06 -0600, Rob Herring wrote:
> Hi all,
> 
> Below is a current list of ARM boards with more than 40 dtc warnings
> when building with W=1. There's a treewide patch in flight to fix some
> unit-address warnings[1], so those aren't included here. The list is
> grouped by maintainer. AT91, Exynos, and Allwinner continue to be at
> the top of the list and have been for some time (though having
> multiple boards for an SoC can cause lots of duplicated warnings).
> 
> Please help fix these. More checks are being added[2].
> 

For the at91 ones, IIRC, they are coming from the clock binding and we
will have to break the ABI to fix it. This is not something we wanted to
do before 4.14 but it will happen at some point.


-- 
Alexandre Belloni, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH 02/10] arm64: limit PA size to supported range
From: Marc Zyngier @ 2017-12-14 18:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513184845-8711-3-git-send-email-kristina.martsenko@arm.com>

On 13/12/17 17:07, Kristina Martsenko wrote:
> We currently copy the physical address size from
> ID_AA64MMFR0_EL1.PARange directly into TCR.(I)PS. This will not work for
> 4k and 16k granule kernels on systems that support 52-bit physical
> addresses, since 52-bit addresses are only permitted with the 64k
> granule.
> 
> To fix this, fall back to 48 bits when configuring the PA size when the
> kernel does not support 52-bit PAs. When it does, fall back to 52, to
> avoid similar problems in the future if the PA size is ever increased
> above 52.
> 
> Signed-off-by: Kristina Martsenko <kristina.martsenko@arm.com>
> ---
>  arch/arm64/include/asm/assembler.h | 13 +++++++++++++
>  arch/arm64/include/asm/sysreg.h    |  8 ++++++++
>  arch/arm64/kvm/hyp-init.S          |  6 ++----
>  arch/arm64/kvm/hyp/s2-setup.c      |  2 ++
>  arch/arm64/mm/proc.S               |  6 ++----
>  5 files changed, 27 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
> index aef72d886677..6cddf12a0250 100644
> --- a/arch/arm64/include/asm/assembler.h
> +++ b/arch/arm64/include/asm/assembler.h
> @@ -351,6 +351,19 @@ alternative_endif
>  	.endm
>  
>  /*
> + * tcr_set_pa_size - set TCR.(I)PS to the highest supported
> + * ID_AA64MMFR0_EL1.PARange value

It'd be good to document what are the expected parameters here.

> + */
> +	.macro	tcr_set_pa_size, tcr, pos, tmp0, tmp1

Small nit: "tcr_compute_pa_size" would better describe what this does.

> +	mrs	\tmp0, ID_AA64MMFR0_EL1
> +	ubfx	\tmp0, \tmp0, #ID_AA64MMFR0_PARANGE_SHIFT, #3

It'd be good to have a comment explaining that we narrow the PARange to
fit the PS firld in TCR. Who knows what will happen once (if ever) we
get two other PARange extentions... ;-)

> +	mov	\tmp1, #ID_AA64MMFR0_PARANGE_MAX
> +	cmp	\tmp0, \tmp1
> +	csel	\tmp0, \tmp1, \tmp0, hi
> +	bfi	\tcr, \tmp0, \pos, #3> +	.endm
> +
> +/*
>   * Macro to perform a data cache maintenance for the interval
>   * [kaddr, kaddr + size)
>   *
> diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
> index 08cc88574659..ec144f480b39 100644
> --- a/arch/arm64/include/asm/sysreg.h
> +++ b/arch/arm64/include/asm/sysreg.h
> @@ -471,6 +471,14 @@
>  #define ID_AA64MMFR0_TGRAN64_SUPPORTED	0x0
>  #define ID_AA64MMFR0_TGRAN16_NI		0x0
>  #define ID_AA64MMFR0_TGRAN16_SUPPORTED	0x1
> +#define ID_AA64MMFR0_PARANGE_48		0x5
> +#define ID_AA64MMFR0_PARANGE_52		0x6
> +
> +#ifdef CONFIG_ARM64_PA_BITS_52
> +#define ID_AA64MMFR0_PARANGE_MAX	ID_AA64MMFR0_PARANGE_52
> +#else
> +#define ID_AA64MMFR0_PARANGE_MAX	ID_AA64MMFR0_PARANGE_48
> +#endif
>  
>  /* id_aa64mmfr1 */
>  #define ID_AA64MMFR1_PAN_SHIFT		20
> diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S
> index 3f9615582377..f731a48bd9f1 100644
> --- a/arch/arm64/kvm/hyp-init.S
> +++ b/arch/arm64/kvm/hyp-init.S
> @@ -90,11 +90,9 @@ __do_hyp_init:
>  	bfi	x4, x5, TCR_T0SZ_OFFSET, TCR_TxSZ_WIDTH
>  #endif
>  	/*
> -	 * Read the PARange bits from ID_AA64MMFR0_EL1 and set the PS bits in
> -	 * TCR_EL2.
> +	 * Set the PS bits in TCR_EL2.
>  	 */
> -	mrs	x5, ID_AA64MMFR0_EL1
> -	bfi	x4, x5, #16, #3
> +	tcr_set_pa_size x4, #16, x5, x6
>  
>  	msr	tcr_el2, x4
>  
> diff --git a/arch/arm64/kvm/hyp/s2-setup.c b/arch/arm64/kvm/hyp/s2-setup.c
> index a81f5e10fc8c..603e1ee83e89 100644
> --- a/arch/arm64/kvm/hyp/s2-setup.c
> +++ b/arch/arm64/kvm/hyp/s2-setup.c
> @@ -32,6 +32,8 @@ u32 __hyp_text __init_stage2_translation(void)
>  	 * PS is only 3. Fortunately, bit 19 is RES0 in VTCR_EL2...
>  	 */
>  	parange = read_sysreg(id_aa64mmfr0_el1) & 7;
> +	if (parange > ID_AA64MMFR0_PARANGE_MAX)
> +		parange = ID_AA64MMFR0_PARANGE_MAX;
>  	val |= parange << 16;
>  
>  	/* Compute the actual PARange... */
> diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
> index 95233dfc4c39..c10c6c180961 100644
> --- a/arch/arm64/mm/proc.S
> +++ b/arch/arm64/mm/proc.S
> @@ -228,11 +228,9 @@ ENTRY(__cpu_setup)
>  	tcr_set_idmap_t0sz	x10, x9
>  
>  	/*
> -	 * Read the PARange bits from ID_AA64MMFR0_EL1 and set the IPS bits in
> -	 * TCR_EL1.
> +	 * Set the IPS bits in TCR_EL1.
>  	 */
> -	mrs	x9, ID_AA64MMFR0_EL1
> -	bfi	x10, x9, #32, #3
> +	tcr_set_pa_size x10, #32, x5, x6
>  #ifdef CONFIG_ARM64_HW_AFDBM
>  	/*
>  	 * Hardware update of the Access and Dirty bits.
> 

Other than the nits above:

Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>

	M.
-- 
Jazz is not dead. It just smells funny...

^ permalink raw reply

* [PATCH] media: v4l: xilinx: Use SPDX-License-Identifier
From: Joe Perches @ 2017-12-14 18:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <7339763.I7jApfYMM6@avalon>

On Thu, 2017-12-14 at 20:28 +0200, Laurent Pinchart wrote:
> Hi Mauro,
> 
> On Thursday, 14 December 2017 19:05:27 EET Mauro Carvalho Chehab wrote:
> > Em Fri,  8 Dec 2017 18:05:37 +0530 Dhaval Shah escreveu:
> > > SPDX-License-Identifier is used for the Xilinx Video IP and
> > > related drivers.
> > > 
> > > Signed-off-by: Dhaval Shah <dhaval23031987@gmail.com>
> > 
> > Hi Dhaval,
> > 
> > You're not listed as one of the Xilinx driver maintainers. I'm afraid that,
> > without their explicit acks, sent to the ML, I can't accept a patch
> > touching at the driver's license tags.
> 
> The patch doesn't change the license, I don't see why it would cause any 
> issue. Greg isn't listed as the maintainer or copyright holder of any of the 
> 10k+ files to which he added an SPDX license header in the last kernel 
> release.

Adding a comment line that describes an implicit or
explicit license is different than removing the license
text itself.

^ permalink raw reply

* [PATCH] arm64: dts: Remove leading 0x and 0s from bindings notation
From: Joe Perches @ 2017-12-14 18:29 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <98a7dc30-e736-eaf8-204f-2ac2b3ea79b9@gmail.com>

On Thu, 2017-12-14 at 19:03 +0100, Matthias Brugger wrote:
> 
> On 12/14/2017 05:53 PM, Mathieu Malaterre wrote:
> [...]
> > diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> > index 26396ef53bde..0446b122a6e2 100644
> > --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> > +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> > @@ -249,7 +249,7 @@
> >  			reg = <0 0x10005000 0 0x1000>;
> >  		};
> >  
> > -		pio: pinctrl at 0x10005000 {
> > +		pio: pinctrl at 10005000 {
> >  			compatible = "mediatek,mt8173-pinctrl";
> >  			reg = <0 0x1000b000 0 0x1000>;
> >  			mediatek,pctl-regmap = <&syscfg_pctl_a>;
> 
> Acked-by: Matthias Brugger <matthias.bgg@gmail.com>

Should all of these be fixed?

$ git grep -P "^\s*\w+:\s*[\w\-]+ at 0[xX]" -- "*.dts*" | wc -l
69

Is this a pattern that should be added to checkpatch?

^ permalink raw reply

* [PATCH] media: v4l: xilinx: Use SPDX-License-Identifier
From: Laurent Pinchart @ 2017-12-14 18:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214150527.00dca6cc@vento.lan>

Hi Mauro,

On Thursday, 14 December 2017 19:05:27 EET Mauro Carvalho Chehab wrote:
> Em Fri,  8 Dec 2017 18:05:37 +0530 Dhaval Shah escreveu:
> > SPDX-License-Identifier is used for the Xilinx Video IP and
> > related drivers.
> > 
> > Signed-off-by: Dhaval Shah <dhaval23031987@gmail.com>
> 
> Hi Dhaval,
> 
> You're not listed as one of the Xilinx driver maintainers. I'm afraid that,
> without their explicit acks, sent to the ML, I can't accept a patch
> touching at the driver's license tags.

The patch doesn't change the license, I don't see why it would cause any 
issue. Greg isn't listed as the maintainer or copyright holder of any of the 
10k+ files to which he added an SPDX license header in the last kernel 
release.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply

* DT dtc warnings
From: Rob Herring @ 2017-12-14 18:21 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

Below is a current list of ARM boards with more than 40 dtc warnings
when building with W=1. There's a treewide patch in flight to fix some
unit-address warnings[1], so those aren't included here. The list is
grouped by maintainer. AT91, Exynos, and Allwinner continue to be at
the top of the list and have been for some time (though having
multiple boards for an SoC can cause lots of duplicated warnings).

Please help fix these. More checks are being added[2].

Rob

[1] https://patchwork.kernel.org/patch/10112725/
[2] https://www.spinics.net/lists/devicetree-compiler/msg01620.html


77 - arch/arm/boot/dts/prima2-evb.dts

50 - arch/arm/boot/dts/exynos5250-arndale.dts
50 - arch/arm/boot/dts/exynos5250-smdk5250.dts
50 - arch/arm/boot/dts/exynos5250-snow.dts
50 - arch/arm/boot/dts/exynos5250-snow-rev5.dts
50 - arch/arm/boot/dts/exynos5250-spring.dts
71 - arch/arm/boot/dts/exynos5420-arndale-octa.dts
71 - arch/arm/boot/dts/exynos5420-peach-pit.dts
71 - arch/arm/boot/dts/exynos5420-smdk5420.dts
71 - arch/arm/boot/dts/exynos5422-odroidhc1.dts
71 - arch/arm/boot/dts/exynos5422-odroidxu3.dts
71 - arch/arm/boot/dts/exynos5422-odroidxu3-lite.dts
71 - arch/arm/boot/dts/exynos5422-odroidxu4.dts
71 - arch/arm/boot/dts/exynos5800-peach-pi.dts

42 - arch/arm/boot/dts/sun5i-gr8-evb.dts
44 - arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
49 - arch/arm/boot/dts/sun7i-a20-icnova-swac.dts
50 - arch/arm/boot/dts/sun7i-a20-itead-ibox.dts
50 - arch/arm/boot/dts/sun7i-a20-m3.dts
51 - arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
51 - arch/arm/boot/dts/sun7i-a20-mk808c.dts
51 - arch/arm/boot/dts/sun7i-a20-wits-pro-a20-dkt.dts
53 - arch/arm/boot/dts/sun7i-a20-bananapi.dts
53 - arch/arm/boot/dts/sun7i-a20-bananapi-m1-plus.dts
53 - arch/arm/boot/dts/sun7i-a20-hummingbird.dts
53 - arch/arm/boot/dts/sun7i-a20-i12-tvbox.dts
53 - arch/arm/boot/dts/sun7i-a20-lamobo-r1.dts
53 - arch/arm/boot/dts/sun7i-a20-olinuxino-lime.dts
53 - arch/arm/boot/dts/sun7i-a20-pcduino3-nano.dts
54 - arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts
54 - arch/arm/boot/dts/sun7i-a20-olinuxino-lime2-emmc.dts
55 - arch/arm/boot/dts/sun7i-a20-bananapro.dts
55 - arch/arm/boot/dts/sun7i-a20-cubietruck.dts
55 - arch/arm/boot/dts/sun7i-a20-orangepi.dts
55 - arch/arm/boot/dts/sun7i-a20-pcduino3.dts
55 - arch/arm/boot/dts/sun7i-a20-wexler-tab7200.dts
56 - arch/arm/boot/dts/sun7i-a20-orangepi-mini.dts
61 - arch/arm/boot/dts/sun7i-a20-olimex-som-evb.dts
61 - arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
61 - arch/arm/boot/dts/sun7i-a20-olinuxino-micro-emmc.dts

46 - arch/arm/boot/dts/at91sam9rlek.dts
48 - arch/arm/boot/dts/at91sam9261ek.dts
50 - arch/arm/boot/dts/at91-kizbox.dts
50 - arch/arm/boot/dts/at91-qil_a9260.dts
51 - arch/arm/boot/dts/at91rm9200ek.dts
52 - arch/arm/boot/dts/at91sam9g20ek.dts
52 - arch/arm/boot/dts/at91-sam9_l9260.dts
53 - arch/arm/boot/dts/at91-foxg20.dts
53 - arch/arm/boot/dts/at91sam9260ek.dts
53 - arch/arm/boot/dts/at91sam9g20ek_2mmc.dts
54 - arch/arm/boot/dts/at91sam9263ek.dts
56 - arch/arm/boot/dts/at91sam9n12ek.dts
61 - arch/arm/boot/dts/at91-cosino_mega2560.dts
61 - arch/arm/boot/dts/at91sam9m10g45ek.dts
62 - arch/arm/boot/dts/at91-ariettag25.dts
63 - arch/arm/boot/dts/at91-ariag25.dts
64 - arch/arm/boot/dts/at91-kizboxmini.dts
64 - arch/arm/boot/dts/at91sam9g15ek.dts
65 - arch/arm/boot/dts/at91sam9g25ek.dts
66 - arch/arm/boot/dts/at91sam9g35ek.dts
69 - arch/arm/boot/dts/at91sam9x25ek.dts
70 - arch/arm/boot/dts/at91sam9x35ek.dts
74 - arch/arm/boot/dts/sama5d33ek.dts
75 - arch/arm/boot/dts/at91-sama5d27_som1_ek.dts
75 - arch/arm/boot/dts/at91-sama5d2_xplained.dts
76 - arch/arm/boot/dts/at91-kizbox2.dts
77 - arch/arm/boot/dts/sama5d31ek.dts
80 - arch/arm/boot/dts/sama5d34ek.dts
81 - arch/arm/boot/dts/sama5d35ek.dts
84 - arch/arm/boot/dts/at91-sama5d3_xplained.dts
84 - arch/arm/boot/dts/sama5d36ek_cmp.dts
84 - arch/arm/boot/dts/sama5d36ek.dts
93 - arch/arm/boot/dts/at91-sama5d4ek.dts
93 - arch/arm/boot/dts/at91-sama5d4_xplained.dts
93 - arch/arm/boot/dts/at91-vinco.dts
94 - arch/arm/boot/dts/at91-sama5d4_ma5d4evk.dts
77 - arch/arm/boot/dts/at91-tse850-3.dts

40 - arch/arm/boot/dts/stih410-b2260.dts
43 - arch/arm/boot/dts/stih410-b2120.dts

52 - arch/arm/boot/dts/keystone-k2e-evm.dts
73 - arch/arm/boot/dts/keystone-k2l-evm.dts
97 - arch/arm/boot/dts/keystone-k2hk-evm.dts

Boards with no maintainer:
192 - arch/arm/boot/dts/atlas7-evb.dts
50 - arch/arm/boot/dts/aks-cdu.dts
50 - arch/arm/boot/dts/animeo_ip.dts
50 - arch/arm/boot/dts/ethernut5.dts
50 - arch/arm/boot/dts/evk-pro3.dts
50 - arch/arm/boot/dts/tny_a9260.dts
50 - arch/arm/boot/dts/tny_a9g20.dts
50 - arch/arm/boot/dts/usb_a9260.dts
50 - arch/arm/boot/dts/usb_a9g20.dts
50 - arch/arm/boot/dts/usb_a9g20_lpw.dts
51 - arch/arm/boot/dts/mpa1600.dts
54 - arch/arm/boot/dts/tny_a9263.dts
54 - arch/arm/boot/dts/usb_a9263.dts
60 - arch/arm/boot/dts/pm9g45.dts
75 - arch/arm/boot/dts/ste-nomadik-nhk15.dts
75 - arch/arm/boot/dts/ste-nomadik-s8815.dts
77 - arch/arm/boot/dts/atlas6-evb.dts

^ permalink raw reply

* [patch v14 2/4] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver
From: Philippe Ombredanne @ 2017-12-14 18:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513268971-13518-3-git-send-email-oleksandrs@mellanox.com>

Oleksandr,

On Thu, Dec 14, 2017 at 5:29 PM, Oleksandr Shamray
<oleksandrs@mellanox.com> wrote:
> Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller.
>
> Driver implements the following jtag ops:
> - freq_get;
> - freq_set;
> - status_get;
> - idle;
> - xfer;
>
> It has been tested on Mellanox system with BMC equipped with
> Aspeed 2520 SoC for programming CPLD devices.
>
> Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
> Signed-off-by: Jiri Pirko <jiri@mellanox.com>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> ---
> v13->v14
> Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
> - Change style of head block comment from /**/ to //
>
> v12->v13
> Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
> - Change jtag-aspeed.c licence type to
>   SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
>   and reorder line with license in description

Thanks for the SPDX tags! for this part you have my ack:

Acked-by: Philippe Ombredanne <pombredanne@nexB.com?

--
Cordially
Philippe Ombredanne

^ permalink raw reply

* [patch v14 1/4] drivers: jtag: Add JTAG core driver
From: Philippe Ombredanne @ 2017-12-14 18:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513268971-13518-2-git-send-email-oleksandrs@mellanox.com>

Oleksandr,

On Thu, Dec 14, 2017 at 5:29 PM, Oleksandr Shamray
<oleksandrs@mellanox.com> wrote:
> Initial patch for JTAG driver
> JTAG class driver provide infrastructure to support hardware/software
> JTAG platform drivers. It provide user layer API interface for flashing
> and debugging external devices which equipped with JTAG interface
> using standard transactions.
>
> Driver exposes set of IOCTL to user space for:
> - XFER:
> - SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
> - SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
> - RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
>   number of clocks).
> - SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
>
> Driver core provides set of internal APIs for allocation and
> registration:
> - jtag_register;
> - jtag_unregister;
> - jtag_alloc;
> - jtag_free;
>
> Platform driver on registration with jtag-core creates the next
> entry in dev folder:
> /dev/jtagX
>
> Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
> Signed-off-by: Jiri Pirko <jiri@mellanox.com>
> ---
> v13->v14
> Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
> - Change style of head block comment from /**/ to //
>
> v12->v13
> Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
> - Change jtag.c licence type to
>   SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
>   and reorder line with license in description
> v11->v12
> Comments pointed by Greg KH <gregkh@linuxfoundation.org>
> - Change jtag.h licence type to
>   SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
>   and reorder line with license in description

Thanks for the SPDX bits: for this part you have my ack:

Acked-by: Philippe Ombredanne <pombredanne@nexB.com?

-- 
Cordially
Philippe Ombredanne

^ permalink raw reply

* arm64: unhandled level 0 translation fault
From: Geert Uytterhoeven @ 2017-12-14 18:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214152431.GC12608@e103592.cambridge.arm.com>

Hi Dave,

On Thu, Dec 14, 2017 at 4:24 PM, Dave P Martin <Dave.Martin@arm.com> wrote:
> On Thu, Dec 14, 2017 at 02:34:50PM +0000, Geert Uytterhoeven wrote:
>> On Tue, Dec 12, 2017 at 11:20 AM, Geert Uytterhoeven
>> <geert@linux-m68k.org> wrote:
>> > During userspace (Debian jessie NFS root) boot on arm64:
>> >
>> > rpcbind[1083]: unhandled level 0 translation fault (11) at 0x00000008,
>> > esr 0x92000004, in dash[aaaaadf77000+1a000]
>> > CPU: 0 PID: 1083 Comm: rpcbind Not tainted
>> > 4.15.0-rc3-arm64-renesas-02176-g14f9a1826e48e355 #51
>> > Hardware name: Renesas Salvator-X 2nd version board based on r8a7795 ES2.0+ (DT)
>>
>> This is a quad Cortex A57.
>>
>> > pstate: 80000000 (Nzcv daif -PAN -UAO)
>> > pc : 0xaaaaadf8a51c
>> > lr : 0xaaaaadf8ac08
>> > sp : 0000ffffcffeac00
>> > x29: 0000ffffcffeac00 x28: 0000aaaaadfa1000
>> > x27: 0000ffffcffebf7c x26: 0000ffffcffead20
>> > x25: 0000aaaacea1c5f0 x24: 0000000000000000
>> > x23: 0000aaaaadfa1000 x22: 0000aaaaadfa1000
>> > x21: 0000000000000000 x20: 0000000000000008
>> > x19: 0000000000000000 x18: 0000ffffcffeb500
>> > x17: 0000ffffa22babfc x16: 0000aaaaadfa1ae8
>> > x15: 0000ffffa2363588 x14: ffffffffffffffff
>> > x13: 0000000000000020 x12: 0000000000000010
>> > x11: 0101010101010101 x10: 0000aaaaadfa1000
>> > x9 : 00000000ffffff81 x8 : 0000aaaaadfa2000
>> > x7 : 0000000000000000 x6 : 0000000000000000
>> > x5 : 0000aaaaadfa2338 x4 : 0000aaaaadfa2000
>> > x3 : 0000aaaaadfa2338 x2 : 0000000000000000
>> > x1 : 0000aaaaadfa28b0 x0 : 0000aaaaadfa4c30
>> >
>> > Sometimes it happens with other processes, but the main address, esr, and
>> > pstate values are always the same.
>> >
>> > I regularly run arm64/for-next/core (through bi-weekly renesas-drivers
>> > releases, so the last time was two weeks ago), but never saw the issue
>> > before until today, so probably v4.15-rc1 is OK.
>> > Unfortunately it doesn't happen during every boot, which makes it
>> > cumbersome to bisect.
>> >
>> > My first guess was UNMAP_KERNEL_AT_EL0, but even after disabling that,
>> > and even without today's arm64/for-next/core merged in, I still managed to
>> > reproduce the issue, so I believe it was introduced in v4.15-rc2 or
>> > v4.15-rc3.
>> >
>> > Once, when the kernel message above wasn't shown, I got an error from
>> > userspace, which may be related:
>> > *** Error in `/bin/sh': free(): invalid pointer: 0x0000aaaadd970988 ***
>>
>> With more boots (10 instead of 6) to declare a kernel good, I bisected this
>> to commit 9de52a755cfb6da5 ("arm64: fpsimd: Fix failure to restore FPSIMD
>> state after signals").
>>
>> Reverting that commit on top of v4.15-rc3 fixed the issue for me.
>
> Good work on the bisect -- I'll need to have a think about this...
>
> That patch fixes a genuine problem so we can't just revert it.
>
> What if you revert _just this function_ back to what it was in v4.14?

With fpsimd_update_current_state() reverted to v4.14, and

-               __this_cpu_write(fpsimd_last_state, st);
+               __this_cpu_write(fpsimd_last_state.st, st);

to make it build, the problem seems to be fixed, too.

Thanks!

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* [PATCH] arm64: dts: Remove leading 0x and 0s from bindings notation
From: Matthias Brugger @ 2017-12-14 18:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214165352.27902-1-malat@debian.org>



On 12/14/2017 05:53 PM, Mathieu Malaterre wrote:
[...]
> diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> index 26396ef53bde..0446b122a6e2 100644
> --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> @@ -249,7 +249,7 @@
>  			reg = <0 0x10005000 0 0x1000>;
>  		};
>  
> -		pio: pinctrl at 0x10005000 {
> +		pio: pinctrl at 10005000 {
>  			compatible = "mediatek,mt8173-pinctrl";
>  			reg = <0 0x1000b000 0 0x1000>;
>  			mediatek,pctl-regmap = <&syscfg_pctl_a>;

Acked-by: Matthias Brugger <matthias.bgg@gmail.com>

^ permalink raw reply

* [PATCH] ARM: exynos_defconfig - enable CONFIG_EXYNOS_IOMMU
From: Krzysztof Kozlowski @ 2017-12-14 18:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <d7d38ca9-ca53-8cb1-6c8a-c219ec94f005@samsung.com>

On Wed, Dec 13, 2017 at 10:42:33AM +0100, Marek Szyprowski wrote:
> Hi Shuah,
> 
> On 2017-12-12 22:31, Shuah Khan wrote:
> > EXYNOS_IOMMU is disabled in exynos_defconfig since it is known to cause
> > boot failures on Exynos Chrome-books. The recommendation is for IOMMU to
> > be enabled manually on systems as needed.
> > 
> > A recent exynos_drm change added a warning message when EXYNOS_IOMMU is
> > disabled. It is necessary to enable it to avoid the warning messages.
> > A few initial tests have shown that enabling EXYNOS_IOMMU might be safe
> > on Exynos Chrome-books.
> > 
> > Enable CONFIG_EXYNOS_IOMMU in exynos_defconfig.
> > 
> > Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>
> 
> Due to some other changes in the order of operations during boot process,
> power domains are initialized very early and because of the temporary
> lack of devices (which are not yet added to the system), are turned off.
> 
> This practically stops FIMD for scanning framebuffer very early during
> boot, before IOMMU gets initialized and "solves" the issue, which was
> the reason to disable Exynos IOMMU by default.
> 
> Like I've already said, I've checked Exynos Snow Chromebook boots fine
> with IOMMU support enabled, both with v4.15-rc3 and linux-next.
> 
> Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>

Thanks, applied with rephrased and extended message using Marek's
explanation.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH 00/10] arm64: 52-bit physical address support
From: Bob Picco @ 2017-12-14 17:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513184845-8711-1-git-send-email-kristina.martsenko@arm.com>

Kristina Martsenko wrote:	[Wed Dec 13 2017, 12:07:15PM EST]
> Hi,
> 
> This series adds 52-bit physical address space support to arm64, up from
> the current 48 bits. This is an ARMv8.2 feature (ARMv8.2-LPA).
> 
> The series is based on 4.15-rc3. It has been lightly tested on an ARM
> Fast Model. There's still some cases and areas to think through, as well
> as more testing to do.
> 
> Patches for SMMU 52-bit PA support have been sent separately [1]. A GIC
> ITS patch has already been merged [2]. ARMv8.2 also allows 52-bit IPA,
> but support for that is not part of this series.
> 
> This version mostly addresses various review comments received.
> 
> Changes from RFC:
>  - Split kconfig symbol into two patches, to enable 52-bit PA at the end
>  - Patch #3: Changed phys_to_ttbr to use a macro, added an #include
>  - Patch #4: Changed phys_to_pte to use a macro
>  - Patch #6: Replaced __phys_to_pte with __phys_to_pte_val (same for
>    pmd/pud/pgd)
>  - Patch #6: Changed __phys_to_pte_val, __pte_to_phys, and
>    pgtable-hwdef.h macros
>  - Patches #5, #6: Removed kvm_extended_idmap_pgd, inlined its code,
>    moved the comment
>  - Patch #5: Added pfn_pud definition (to make the kernel build on that
>    commit)
> 
> Thanks,
> Kristina
> 
> [1] https://www.spinics.net/lists/arm-kernel/msg619040.html
> [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=30ae9610d275f8f03f5bf7612ce71d8af6fc400b
> 
> 
> Kristina Martsenko (10):
>   arm64: add kconfig symbol to configure physical address size
>   arm64: limit PA size to supported range
>   arm64: handle 52-bit addresses in TTBR
>   arm64: head.S: handle 52-bit PAs in PTEs in early page table setup
>   arm64: don't open code page table entry creation
>   arm64: handle 52-bit physical addresses in page table entries
>   arm64: increase PHYS_MASK to 52 bits
>   arm64: increase sparsemem MAX_PHYSMEM_BITS to 52
>   arm64: allow ID map to be extended to 52 bits
>   arm64: enable 52-bit physical address support
> 
>  arch/arm/include/asm/kvm_mmu.h         |   7 ++
>  arch/arm64/Kconfig                     |  29 ++++++++
>  arch/arm64/include/asm/assembler.h     |  31 ++++++++-
>  arch/arm64/include/asm/kvm_mmu.h       |  21 +++++-
>  arch/arm64/include/asm/mmu_context.h   |  16 ++++-
>  arch/arm64/include/asm/pgalloc.h       |   6 +-
>  arch/arm64/include/asm/pgtable-hwdef.h |  19 +++++-
>  arch/arm64/include/asm/pgtable.h       |  53 ++++++++++++---
>  arch/arm64/include/asm/sparsemem.h     |   2 +-
>  arch/arm64/include/asm/sysreg.h        |   8 +++
>  arch/arm64/kernel/head.S               | 118 +++++++++++++++++++++------------
>  arch/arm64/kernel/hibernate-asm.S      |  12 ++--
>  arch/arm64/kernel/hibernate.c          |   5 +-
>  arch/arm64/kvm/hyp-init.S              |  26 ++++----
>  arch/arm64/kvm/hyp/s2-setup.c          |   2 +
>  arch/arm64/mm/mmu.c                    |  15 +++--
>  arch/arm64/mm/pgd.c                    |   8 +++
>  arch/arm64/mm/proc.S                   |  19 +++---
>  virt/kvm/arm/arm.c                     |   2 +-
>  virt/kvm/arm/mmu.c                     |  12 ++--
>  20 files changed, 302 insertions(+), 109 deletions(-)
> 
> -- 
> 2.1.4
Hi Kristina,

I boot tested but on VM and had a couple issues. I will examine and share
should the issues be of value.
Tested-by: Bob Picco <bob.picco@oracle.com>

I reviewed and thank you. Your effort caused me to return to some code
examined/learned during the last few months.
Reviewed-by: Bob Picco <bob.picco@oracle.com>

bob
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH] media: v4l: xilinx: Use SPDX-License-Identifier
From: Mauro Carvalho Chehab @ 2017-12-14 17:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513272387.27409.69.camel@perches.com>

Em Thu, 14 Dec 2017 09:26:27 -0800
Joe Perches <joe@perches.com> escreveu:

> On Thu, 2017-12-14 at 15:05 -0200, Mauro Carvalho Chehab wrote:
> > Em Fri,  8 Dec 2017 18:05:37 +0530
> > Dhaval Shah <dhaval23031987@gmail.com> escreveu:
> >   
> > > SPDX-License-Identifier is used for the Xilinx Video IP and
> > > related drivers.
> > > 
> > > Signed-off-by: Dhaval Shah <dhaval23031987@gmail.com>  
> > 
> > Hi Dhaval,
> > 
> > You're not listed as one of the Xilinx driver maintainers. I'm afraid that,
> > without their explicit acks, sent to the ML, I can't accept a patch
> > touching at the driver's license tags.  
> 
> And even a maintainer may not have the sole right
> to modify a license.

Very true. I was actually expecting a patch like that either authored
or explicitly sanctioned by either one of those developers:

Hyun Kwon <hyun.kwon@xilinx.com> (supporter:XILINX VIDEO IP CORES)
Michal Simek <michal.simek@xilinx.com> (supporter:ARM/ZYNQ ARCHITECTURE)

As they own an @xilinx.com, we could assume that an email from
their corporate accounts to be official.

Thanks,
Mauro

^ permalink raw reply

* [RESEND PATCH v2 15/15] arm64: dts: msm8996: db820c: Add sound card support
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds hdmi sound card support to db820c via qdsp.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi |  5 +++++
 arch/arm64/boot/dts/qcom/msm8996.dtsi        | 33 ++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
index 9769053957af..b955769b100d 100644
--- a/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
+++ b/arch/arm64/boot/dts/qcom/apq8096-db820c.dtsi
@@ -190,6 +190,11 @@
 		};
 	};
 
+	snd {
+		compatible = "qcom,apq8096-sndcard";
+		qcom,model = "DB820c";
+		iommus = <&lpass_q6_smmu 1>;
+	};
 
 	gpio_keys {
 		compatible = "gpio-keys";
diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi
index a144cec7bb71..25c43fb8ab49 100644
--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi
@@ -1262,6 +1262,7 @@
 
 				phys = <&hdmi_phy>;
 				phy-names = "hdmi_phy";
+				#sound-dai-cells = <0>;
 
 				ports {
 					#address-cells = <1>;
@@ -1297,6 +1298,33 @@
 					      "ref_clk";
 			};
 		};
+
+	        lpass_q6_smmu: arm,smmu-lpass_q6 at 1600000 {
+			compatible = "qcom,msm8996-smmu-v2";
+	                reg = <0x1600000 0x20000>;
+	                #iommu-cells = <1>;
+                        power-domains = <&gcc HLOS1_VOTE_LPASS_CORE_GDSC>;
+
+			#global-interrupts = <1>;
+		        interrupts = <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 393 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 394 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
+		                <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>;
+
+			clocks = <&gcc GCC_HLOS1_VOTE_LPASS_CORE_SMMU_CLK>,
+				 <&gcc GCC_HLOS1_VOTE_LPASS_ADSP_SMMU_CLK>;
+			clock-names = "iface", "bus";
+                        status = "okay";
+		};
 	};
 
 	adsp-pil {
@@ -1325,6 +1353,11 @@
 			qcom,ipc = <&apcs 16 8>;
 			qcom,smd-edge = <1>;
 			qcom,remote-pid = <2>;
+
+			apr {
+				compatible = "qcom,apr-msm8996";
+				qcom,smd-channels = "apr_audio_svc";
+			};
 		};
 	};
 
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 14/15] ASoC: qcom: apq8096: Add db820c machine driver
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

uThis patch adds support to DB820c machine driver.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/Kconfig   |   9 +++-
 sound/soc/qcom/apq8096.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 132 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/qcom/apq8096.c

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index ecd1e4ba834d..748ccc3edefa 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -72,7 +72,6 @@ config SND_SOC_QDSP6_ASM_DAI
 	tristate
 	default n
 
-
 config SND_SOC_QDSP6
 	tristate "SoC ALSA audio driver for QDSP6"
 	select SND_SOC_QDSP6_AFE
@@ -87,3 +86,11 @@ config SND_SOC_QDSP6
 	 This will enable sound soc platform specific
 	 audio drivers. This includes q6asm, q6adm,
 	 q6afe interfaces to DSP using apr.
+
+config SND_SOC_MSM8996
+	tristate "SoC Machine driver for MSM8996 and APQ8096 boards"
+	select SND_SOC_QDSP6V2
+	default n
+	help
+	 To add support for SoC audio on MSM8996 and APQ8096 boards
+
diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c
new file mode 100644
index 000000000000..5b2036317f71
--- /dev/null
+++ b/sound/soc/qcom/apq8096.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/dma-mapping.h>
+#include <sound/pcm.h>
+
+int msm8996_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					      struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	return 0;
+}
+static struct snd_soc_dai_link msm8996_dai_links[] = {
+	/* FrontEnd DAI Links */
+	{
+		.name		= "MultiMedia1 Playback",
+		.stream_name	= "MultiMedia1",
+		.cpu_dai_name	= "MM_DL1",
+		.platform_name	= "q6asm_dai",
+		.dynamic	= 1,
+		.dpcm_playback	= 1,
+
+		.codec_dai_name	= "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	/* Backend DAI Links */
+	{
+		.name		= "HDMI Playback",
+		.stream_name	= "q6afe_dai",
+		.cpu_dai_name	= "HDMI",
+		.platform_name	= "q6routing",
+		.no_pcm		= 1,
+		.dpcm_playback	= 1,
+		.be_hw_params_fixup = msm8996_be_hw_params_fixup,
+		.codec_dai_name	= "i2s-hifi",
+		.codec_name = "hdmi-audio-codec.0.auto",
+	},
+};
+
+static int apq8096_sbc_parse_of(struct snd_soc_card *card)
+{
+	struct device *dev = card->dev;
+	int ret;
+
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret)
+		dev_err(dev, "Error parsing card name: %d\n", ret);
+
+	return ret;
+}
+
+static int msm_snd_apq8096_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct snd_soc_card *card;
+
+	card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+	if (!card)
+		return -ENOMEM;
+
+	card->dev = &pdev->dev;
+
+	ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+	if (ret)
+		return ret;
+
+	card->dai_link = msm8996_dai_links;
+	card->num_links	= ARRAY_SIZE(msm8996_dai_links);
+
+	ret = apq8096_sbc_parse_of(card);
+	if (ret) {
+		dev_err(&pdev->dev, "Error parsing OF data\n");
+		return ret;
+	}
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret)
+		dev_err(&pdev->dev, "sound card register failed (%d)!\n", ret);
+	else
+		dev_err(&pdev->dev, "sound card register Sucessfull\n");
+
+	return ret;
+}
+
+static const struct of_device_id msm_snd_apq8096_dt_match[] = {
+	{.compatible = "qcom,apq8096-sndcard"},
+	{}
+};
+
+static struct platform_driver msm_snd_apq8096_driver = {
+	.probe  = msm_snd_apq8096_probe,
+	.driver = {
+		.name = "msm-snd-apq8096",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_snd_apq8096_dt_match,
+	},
+};
+module_platform_driver(msm_snd_apq8096_driver);
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("APQ8096 ASoC Machine Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 13/15] dt-bindings: sound: qcom: Add devicetree bindings for apq8096
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

Add devicetree bindings documentation file for Qualcomm apq8096 sound card.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 .../devicetree/bindings/sound/qcom,apq8096.txt     | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/qcom,apq8096.txt

diff --git a/Documentation/devicetree/bindings/sound/qcom,apq8096.txt b/Documentation/devicetree/bindings/sound/qcom,apq8096.txt
new file mode 100644
index 000000000000..27b511dab533
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,apq8096.txt
@@ -0,0 +1,22 @@
+* Qualcomm Technologies APQ8096 ASoC sound card driver
+
+This binding describes the APQ8096 sound card, which uses qdsp for audio.
+
+- compatible:
+	Usage: required
+	Value type: <stringlist>
+	Definition: must be "qcom,apq8096-sndcard"
+
+- qcom,audio-routing:
+	Usage: Optional
+	Value type: <stringlist>
+	Definition:  A list of the connections between audio components.
+		  Each entry is a pair of strings, the first being the
+		  connection's sink, the second being the connection's
+		  source. Valid names could be power supplies, MicBias
+		  of codec and the jacks on the board:
+Example:
+	sound {
+		compatible	= "qcom,snd-apq8096";
+		qcom,model = "DB820c";
+	};
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 12/15] ASoC: qcom: qdsp6: Add support to q6asm dai driver
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to q6asm dai driver which configures Q6ASM streams
to pass pcm data.
Currently the driver only exposes 2 playback streams for hdmi playback
support, it can be easily extended to add all 8 streams.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/Kconfig           |   6 +
 sound/soc/qcom/qdsp6/Makefile    |   1 +
 sound/soc/qcom/qdsp6/q6asm-dai.c | 534 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 541 insertions(+)
 create mode 100644 sound/soc/qcom/qdsp6/q6asm-dai.c

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 003ce182691c..ecd1e4ba834d 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -68,6 +68,11 @@ config SND_SOC_QDSP6_AFE_DAI
 	tristate
 	default n
 
+config SND_SOC_QDSP6_ASM_DAI
+	tristate
+	default n
+
+
 config SND_SOC_QDSP6
 	tristate "SoC ALSA audio driver for QDSP6"
 	select SND_SOC_QDSP6_AFE
@@ -76,6 +81,7 @@ config SND_SOC_QDSP6
 	select SND_SOC_QDSP6_CORE
 	select SND_SOC_QDSP6_ROUTING
 	select SND_SOC_QDSP6_AFE_DAI
+	select SND_SOC_QDSP6_ASM_DAI
 	help
 	 To add support for MSM QDSP6 Soc Audio.
 	 This will enable sound soc platform specific
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index bd8bd02bf09e..03576a442fb5 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
 obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o
 obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o
 obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o
+obj-$(CONFIG_SND_SOC_QDSP6_ASM_DAI) += q6asm-dai.o
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
new file mode 100644
index 000000000000..709c5de230fa
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -0,0 +1,534 @@
+/* SPDX-License-Identifier: GPL-2.0
+* Copyright (c) 2011-2016, The Linux Foundation
+* Copyright (c) 2017, Linaro Limited
+*/
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
+#include <sound/pcm_params.h>
+#include "q6asm.h"
+#include "q6routing.h"
+#include "common.h"
+
+#define PLAYBACK_MIN_NUM_PERIODS    2
+#define PLAYBACK_MAX_NUM_PERIODS   8
+#define PLAYBACK_MAX_PERIOD_SIZE    65536
+#define PLAYBACK_MIN_PERIOD_SIZE    128
+
+enum stream_state {
+	IDLE = 0,
+	STOPPED,
+	RUNNING,
+};
+
+struct q6asm_dai_rtd {
+	struct snd_pcm_substream *substream;
+	dma_addr_t phys;
+	unsigned int pcm_size;
+	unsigned int pcm_count;
+	unsigned int pcm_irq_pos;       /* IRQ position */
+	unsigned int periods;
+	uint16_t bits_per_sample;
+	uint16_t source; /* Encoding source bit mask */
+
+	struct audio_client *audio_client;
+	uint16_t session_id;
+
+	enum stream_state state;
+	bool set_channel_map;
+	char channel_map[8];
+};
+
+struct q6asm_dai_data {
+	u64 sid;
+};
+
+static struct snd_pcm_hardware q6asm_dai_hardware_playback = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	.formats =              (SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE),
+	.rates =                SNDRV_PCM_RATE_8000_192000,
+	.rate_min =             8000,
+	.rate_max =             192000,
+	.channels_min =         1,
+	.channels_max =         8,
+	.buffer_bytes_max =     (PLAYBACK_MAX_NUM_PERIODS *
+				PLAYBACK_MAX_PERIOD_SIZE),
+	.period_bytes_min =	PLAYBACK_MIN_PERIOD_SIZE,
+	.period_bytes_max =     PLAYBACK_MAX_PERIOD_SIZE,
+	.periods_min =          PLAYBACK_MIN_NUM_PERIODS,
+	.periods_max =          PLAYBACK_MAX_NUM_PERIODS,
+	.fifo_size =            0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+	88200, 96000, 176400, 192000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+static void event_handler(uint32_t opcode, uint32_t token,
+			  uint32_t *payload, void *priv)
+{
+	struct q6asm_dai_rtd *prtd = priv;
+	struct snd_pcm_substream *substream = prtd->substream;
+
+	switch (opcode) {
+	case ASM_CLIENT_EVENT_CMD_RUN_DONE:
+		q6asm_write_nolock(prtd->audio_client,
+				   prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+		break;
+	case ASM_CLIENT_EVENT_CMD_EOS_DONE:
+		prtd->state = STOPPED;
+		break;
+	case ASM_CLIENT_EVENT_DATA_WRITE_DONE: {
+		prtd->pcm_irq_pos += prtd->pcm_count;
+		snd_pcm_period_elapsed(substream);
+		if (prtd->state == RUNNING)
+			q6asm_write_nolock(prtd->audio_client,
+					   prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+
+		break;
+		}
+	default:
+		break;
+	}
+}
+
+static int q6asm_dai_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct q6asm_dai_rtd *prtd = runtime->private_data;
+	struct q6asm_dai_data *pdata;
+	int ret;
+
+	pdata = dev_get_drvdata(soc_prtd->platform->dev);
+	if (!pdata)
+		return -EINVAL;
+
+	if (!prtd || !prtd->audio_client) {
+		pr_err("%s: private data null or audio client freed\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_irq_pos = 0;
+	/* rate and channels are sent to audio driver */
+	if (prtd->state) {
+		/* clear the previous setup if any  */
+		q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+		q6asm_unmap_memory_regions(substream->stream,
+					   prtd->audio_client);
+		q6routing_dereg_phy_stream(soc_prtd->dai_link->id,
+					 SNDRV_PCM_STREAM_PLAYBACK);
+	}
+
+	ret = q6asm_map_memory_regions(substream->stream, prtd->audio_client,
+				       prtd->phys,
+				       (prtd->pcm_size / prtd->periods),
+				       prtd->periods);
+
+	if (ret < 0) {
+		pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
+							ret);
+		return -ENOMEM;
+	}
+
+	ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM,
+			       prtd->bits_per_sample);
+	if (ret < 0) {
+		pr_err("%s: q6asm_open_write failed\n", __func__);
+		q6asm_audio_client_free(prtd->audio_client);
+		prtd->audio_client = NULL;
+		return -ENOMEM;
+	}
+
+	prtd->session_id = q6asm_get_session_id(prtd->audio_client);
+	ret = q6routing_reg_phy_stream(soc_prtd->dai_link->id, LEGACY_PCM_MODE,
+				      prtd->session_id, substream->stream);
+	if (ret) {
+		pr_err("%s: stream reg failed ret:%d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = q6asm_media_format_block_multi_ch_pcm(
+			prtd->audio_client, runtime->rate,
+			runtime->channels, !prtd->set_channel_map,
+			prtd->channel_map, prtd->bits_per_sample);
+	if (ret < 0)
+		pr_info("%s: CMD Format block failed\n", __func__);
+
+	prtd->state = RUNNING;
+
+	return 0;
+}
+
+static int q6asm_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct q6asm_dai_rtd *prtd = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		prtd->state = STOPPED;
+		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int q6asm_dai_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct q6asm_dai_rtd *prtd;
+	struct q6asm_dai_data *pdata;
+	struct device *dev = soc_prtd->platform->dev;
+	int ret = 0;
+
+	pdata = dev_get_drvdata(dev);
+	if (!pdata) {
+		pr_err("Platform data not found ..\n");
+		return -EINVAL;
+	}
+
+	prtd = kzalloc(sizeof(struct q6asm_dai_rtd), GFP_KERNEL);
+	if (prtd == NULL)
+		return -ENOMEM;
+
+	prtd->substream = substream;
+	prtd->audio_client = q6asm_audio_client_alloc(dev,
+				(app_cb)event_handler, prtd);
+	if (!prtd->audio_client) {
+		pr_info("%s: Could not allocate memory\n", __func__);
+		kfree(prtd);
+		return -ENOMEM;
+	}
+
+//	prtd->audio_client->dev = dev;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = q6asm_dai_hardware_playback;
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&constraints_sample_rates);
+	if (ret < 0)
+		pr_info("snd_pcm_hw_constraint_list failed\n");
+	/* Ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+			PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE,
+			PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE);
+		if (ret < 0) {
+			pr_err("constraint for buffer bytes min max ret = %d\n",
+									ret);
+		}
+	}
+
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	if (ret < 0) {
+		pr_err("constraint for period bytes step ret = %d\n",
+								ret);
+	}
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+	if (ret < 0) {
+		pr_err("constraint for buffer bytes step ret = %d\n",
+								ret);
+	}
+
+	prtd->set_channel_map = false;
+	runtime->private_data = prtd;
+
+	snd_soc_set_runtime_hwparams(substream, &q6asm_dai_hardware_playback);
+
+	runtime->dma_bytes = q6asm_dai_hardware_playback.buffer_bytes_max;
+
+
+	if (pdata->sid < 0)
+		prtd->phys = substream->dma_buffer.addr;
+	else
+		prtd->phys = substream->dma_buffer.addr | (pdata->sid << 32);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static int q6asm_dai_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct q6asm_dai_rtd *prtd = runtime->private_data;
+
+	if (prtd->audio_client) {
+		q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+		q6asm_unmap_memory_regions(substream->stream,
+					   prtd->audio_client);
+		q6asm_audio_client_free(prtd->audio_client);
+	}
+	q6routing_dereg_phy_stream(soc_prtd->dai_link->id,
+						SNDRV_PCM_STREAM_PLAYBACK);
+	kfree(prtd);
+	return 0;
+}
+
+static snd_pcm_uframes_t q6asm_dai_pointer(struct snd_pcm_substream *substream)
+{
+
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct q6asm_dai_rtd *prtd = runtime->private_data;
+
+	if (prtd->pcm_irq_pos >= prtd->pcm_size)
+		prtd->pcm_irq_pos = 0;
+
+	return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int q6asm_dai_mmap(struct snd_pcm_substream *substream,
+				struct vm_area_struct *vma)
+{
+
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct snd_card *card = soc_prtd->card->snd_card;
+
+	return dma_mmap_coherent(card->dev, vma,
+			runtime->dma_area, runtime->dma_addr,
+			runtime->dma_bytes);
+}
+
+static int q6asm_dai_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct q6asm_dai_rtd *prtd = runtime->private_data;
+
+	prtd->pcm_size = params_buffer_bytes(params);
+	prtd->periods = params_periods(params);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		prtd->bits_per_sample = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		prtd->bits_per_sample = 24;
+		break;
+	}
+
+	return 0;
+}
+
+static struct snd_pcm_ops q6asm_dai_ops = {
+	.open           = q6asm_dai_open,
+	.hw_params	= q6asm_dai_hw_params,
+	.close          = q6asm_dai_close,
+	.ioctl          = snd_pcm_lib_ioctl,
+	.prepare        = q6asm_dai_prepare,
+	.trigger        = q6asm_dai_trigger,
+	.pointer        = q6asm_dai_pointer,
+	.mmap		= q6asm_dai_mmap,
+};
+
+static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	struct snd_pcm_substream *substream;
+	struct snd_card *card = rtd->card->snd_card;
+	struct device *dev = card->dev;
+	struct device_node *node = dev->of_node;
+	struct q6asm_dai_data *pdata = dev_get_drvdata(rtd->platform->dev);
+	struct of_phandle_args args;
+
+	int size, ret = 0;
+
+	ret = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
+	if (ret < 0)
+		pdata->sid = -1;
+	else
+		pdata->sid = args.args[0];
+
+
+
+	substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	size = q6asm_dai_hardware_playback.buffer_bytes_max;
+	ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size,
+				  &substream->dma_buffer);
+	if (ret) {
+		dev_err(dev, "Cannot allocate buffer(s)\n");
+		return ret;
+	}
+
+	return ret;
+}
+
+static void q6asm_dai_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
+		substream = pcm->streams[i].substream;
+		if (substream) {
+			snd_dma_free_pages(&substream->dma_buffer);
+			substream->dma_buffer.area = NULL;
+			substream->dma_buffer.addr = 0;
+		}
+	}
+}
+
+static struct snd_soc_platform_driver q6asm_soc_platform = {
+	.ops		= &q6asm_dai_ops,
+	.pcm_new	= q6asm_dai_pcm_new,
+	.pcm_free	= q6asm_dai_pcm_free,
+
+};
+
+static const struct snd_soc_dapm_route afe_pcm_routes[] = {
+	{"MM_DL1",  NULL, "MultiMedia1 Playback" },
+	{"MM_DL2",  NULL, "MultiMedia2 Playback" },
+
+};
+
+static int fe_dai_probe(struct snd_soc_dai *dai)
+{
+	struct snd_soc_dapm_context *dapm;
+
+	dapm = snd_soc_component_get_dapm(dai->component);
+	snd_soc_dapm_add_routes(dapm, afe_pcm_routes,
+				ARRAY_SIZE(afe_pcm_routes));
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver q6asm_fe_dai_component = {
+	.name		= "q6asm-fe-dai",
+};
+
+static struct snd_soc_dai_driver q6asm_fe_dais[] = {
+	{
+		.playback = {
+			.stream_name = "MultiMedia1 Playback",
+			.rates = (SNDRV_PCM_RATE_8000_192000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	192000,
+		},
+		.name = "MM_DL1",
+		.probe = fe_dai_probe,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA1,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia2 Playback",
+			.rates = (SNDRV_PCM_RATE_8000_192000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	192000,
+		},
+		.name = "MM_DL2",
+		.probe = fe_dai_probe,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+	},
+};
+
+static int q6asm_dai_probe(struct platform_device *pdev)
+{
+	struct q6asm_dai_data *pdata;
+	struct device *dev = &pdev->dev;
+	int rc;
+
+	pdata = devm_kzalloc(dev, sizeof(struct q6asm_dai_data), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+
+	dev_set_drvdata(dev, pdata);
+
+	rc = snd_soc_register_platform(dev,  &q6asm_soc_platform);
+	if (rc) {
+		dev_err(&pdev->dev, "err_dai_platform\n");
+		return rc;
+	}
+
+	rc = snd_soc_register_component(dev, &q6asm_fe_dai_component,
+					q6asm_fe_dais,
+					ARRAY_SIZE(q6asm_fe_dais));
+	if (rc)
+		dev_err(dev, "err_dai_component\n");
+
+	return rc;
+
+}
+
+static int q6asm_dai_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver q6asm_dai_driver = {
+	.driver = {
+		.name = "q6asm_dai",
+		.owner = THIS_MODULE,
+	},
+	.probe = q6asm_dai_probe,
+	.remove = q6asm_dai_remove,
+};
+
+module_platform_driver(q6asm_dai_driver);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 11/15] ASoC: qcom: qdsp6: Add support to q6afe dai driver
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to q6afe backend dais driver.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/Kconfig           |   5 +
 sound/soc/qcom/qdsp6/Makefile    |   1 +
 sound/soc/qcom/qdsp6/q6afe-dai.c | 241 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 247 insertions(+)
 create mode 100644 sound/soc/qcom/qdsp6/q6afe-dai.c

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index dd8fb0cde614..003ce182691c 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -64,6 +64,10 @@ config SND_SOC_QDSP6_ROUTING
 	tristate
 	default n
 
+config SND_SOC_QDSP6_AFE_DAI
+	tristate
+	default n
+
 config SND_SOC_QDSP6
 	tristate "SoC ALSA audio driver for QDSP6"
 	select SND_SOC_QDSP6_AFE
@@ -71,6 +75,7 @@ config SND_SOC_QDSP6
 	select SND_SOC_QDSP6_ASM
 	select SND_SOC_QDSP6_CORE
 	select SND_SOC_QDSP6_ROUTING
+	select SND_SOC_QDSP6_AFE_DAI
 	help
 	 To add support for MSM QDSP6 Soc Audio.
 	 This will enable sound soc platform specific
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index c1ad060a2341..bd8bd02bf09e 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
 obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
 obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o
 obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o
+obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
new file mode 100644
index 000000000000..e9865c684bcb
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -0,0 +1,241 @@
+/* SPDX-License-Identifier: GPL-2.0
+* Copyright (c) 2011-2016, The Linux Foundation
+* Copyright (c) 2017, Linaro Limited
+*/
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "q6afe.h"
+
+struct q6hdmi_dai_data {
+	struct q6afe_port *port;
+	struct q6afe_hdmi_cfg port_config;
+	bool is_port_started;
+};
+
+static int q6hdmi_format_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct q6hdmi_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+
+	dai_data->port_config.datatype = value;
+
+	return 0;
+}
+
+static int q6hdmi_format_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct q6hdmi_dai_data *dai_data = kcontrol->private_data;
+
+	ucontrol->value.integer.value[0] =
+		dai_data->port_config.datatype;
+
+	return 0;
+}
+
+static const char * const hdmi_format[] = {
+	"LPCM",
+	"Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+	SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+				 q6hdmi_format_get,
+				 q6hdmi_format_put),
+};
+
+static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct q6hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int channels = params_channels(params);
+
+	dai_data->port_config.sample_rate = params_rate(params);
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dai_data->port_config.bit_width = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dai_data->port_config.bit_width = 24;
+		break;
+	}
+
+	/*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
+	switch (channels) {
+	case 2:
+		dai_data->port_config.channel_allocation = 0;
+		break;
+	case 3:
+		dai_data->port_config.channel_allocation = 0x02;
+		break;
+	case 4:
+		dai_data->port_config.channel_allocation = 0x06;
+		break;
+	case 5:
+		dai_data->port_config.channel_allocation = 0x0A;
+		break;
+	case 6:
+		dai_data->port_config.channel_allocation = 0x0B;
+		break;
+	case 7:
+		dai_data->port_config.channel_allocation = 0x12;
+		break;
+	case 8:
+		dai_data->port_config.channel_allocation = 0x13;
+		break;
+	default:
+		dev_err(dai->dev, "invalid Channels = %u\n", channels);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int q6hdmi_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct q6hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->is_port_started = false;
+
+	return 0;
+}
+
+static void q6hdmi_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct q6hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc;
+
+	rc = q6afe_port_stop(dai_data->port);
+	if (rc < 0)
+		dev_err(dai->dev, "fail to close AFE port\n");
+
+	dai_data->is_port_started = false;
+
+}
+
+static int q6hdmi_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct q6hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc;
+
+	if (dai_data->is_port_started) {
+		/* stop the port and restart with new port config */
+		rc = q6afe_port_stop(dai_data->port);
+		if (rc < 0) {
+			dev_err(dai->dev, "fail to close AFE port\n");
+			return rc;
+		}
+	}
+
+	q6afe_hdmi_port_prepare(dai_data->port, &dai_data->port_config);
+	rc = q6afe_port_start(dai_data->port);
+	if (rc < 0) {
+		dev_err(dai->dev, "fail to start AFE port %x\n", dai->id);
+		return rc;
+	}
+	dai_data->is_port_started = true;
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_route hdmi_dapm_routes[] = {
+	{"HDMI Playback", NULL, "HDMI"},
+};
+
+static struct snd_soc_dai_ops q6hdmi_ops = {
+	.prepare	= q6hdmi_prepare,
+	.hw_params	= q6hdmi_hw_params,
+	.shutdown	= q6hdmi_shutdown,
+	.startup	= q6hdmi_startup,
+};
+
+static struct snd_soc_dai_driver q6afe_dai_hdmi_rx = {
+	.playback = {
+		.stream_name = "HDMI Playback",
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.channels_min = 2,
+		.channels_max = 8,
+		.rate_max =     192000,
+		.rate_min =	48000,
+	},
+	.ops = &q6hdmi_ops,
+	.id = AFE_PORT_HDMI_RX,
+	.name = "HDMI",
+};
+
+static const struct snd_soc_dapm_widget hdmi_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_OUTPUT("HDMI-RX"),
+};
+
+static const struct snd_soc_component_driver msm_dai_hdmi_q6_component = {
+	.name		= "msm-dai-q6-hdmi",
+	.dapm_widgets = hdmi_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(hdmi_dapm_widgets),
+	.controls = hdmi_config_controls,
+	.num_controls = ARRAY_SIZE(hdmi_config_controls),
+	.dapm_routes = hdmi_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(hdmi_dapm_routes),
+};
+
+static int q6afe_dai_dev_probe(struct platform_device *pdev)
+{
+	struct q6hdmi_dai_data *dai_data;
+	int rc = 0;
+	struct q6afe_port *port;
+
+	dai_data = devm_kzalloc(&pdev->dev, sizeof(*dai_data), GFP_KERNEL);
+	if (!dai_data)
+		rc = -ENOMEM;
+
+	port = q6afe_port_get_from_id(&pdev->dev, AFE_PORT_HDMI_RX);
+	if (IS_ERR(port)) {
+		dev_err(&pdev->dev, "Unable to get afe port\n");
+		return -EPROBE_DEFER;
+	}
+	dai_data->port = port;
+	dev_set_drvdata(&pdev->dev, dai_data);
+
+	return devm_snd_soc_register_component(&pdev->dev,
+					  &msm_dai_hdmi_q6_component,
+					  &q6afe_dai_hdmi_rx, 1);
+}
+
+static int q6afe_dai_dev_remove(struct platform_device *pdev)
+{
+	struct q6hdmi_dai_data *dai_data = dev_get_drvdata(&pdev->dev);
+
+	q6afe_port_put(dai_data->port);
+
+	return 0;
+}
+
+static struct platform_driver q6afe_dai_driver = {
+	.probe  = q6afe_dai_dev_probe,
+	.remove = q6afe_dai_dev_remove,
+	.driver = {
+		.name = "q6afe_dai",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(q6afe_dai_driver);
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 10/15] ASoC: qcom: qdsp6: Add support to q6routing driver
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to q6 routing driver which configures route
between ASM and AFE module using ADM apis.

This driver uses dapm widgets to setup the matrix between AFE ports and
ASM streams.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/Kconfig           |   5 +
 sound/soc/qcom/qdsp6/Makefile    |   1 +
 sound/soc/qcom/qdsp6/q6routing.c | 386 +++++++++++++++++++++++++++++++++++++++
 sound/soc/qcom/qdsp6/q6routing.h |   9 +
 4 files changed, 401 insertions(+)
 create mode 100644 sound/soc/qcom/qdsp6/q6routing.c
 create mode 100644 sound/soc/qcom/qdsp6/q6routing.h

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 121b9c957024..dd8fb0cde614 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -60,12 +60,17 @@ config SND_SOC_QDSP6_CORE
 	tristate
 	default n
 
+config SND_SOC_QDSP6_ROUTING
+	tristate
+	default n
+
 config SND_SOC_QDSP6
 	tristate "SoC ALSA audio driver for QDSP6"
 	select SND_SOC_QDSP6_AFE
 	select SND_SOC_QDSP6_ADM
 	select SND_SOC_QDSP6_ASM
 	select SND_SOC_QDSP6_CORE
+	select SND_SOC_QDSP6_ROUTING
 	help
 	 To add support for MSM QDSP6 Soc Audio.
 	 This will enable sound soc platform specific
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index ad7f10691e54..c1ad060a2341 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o
 obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
 obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
 obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o
+obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o
diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c
new file mode 100644
index 000000000000..f5f12d61a1ee
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6routing.c
@@ -0,0 +1,386 @@
+/* SPDX-License-Identifier: GPL-2.0
+* Copyright (c) 2011-2016, The Linux Foundation
+* Copyright (c) 2017, Linaro Limited
+*/
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/asound.h>
+#include <sound/pcm_params.h>
+#include "q6afe.h"
+#include "q6asm.h"
+#include "q6adm.h"
+#include "q6routing.h"
+
+struct session_data {
+	int state;
+	int port_id;
+	int path_type;
+	int app_type;
+	int acdb_id;
+	int sample_rate;
+	int bits_per_sample;
+	int channels;
+	int format;
+	int perf_mode;
+	int numcopps;
+	int fedai_id;
+	unsigned long copp_map;
+};
+
+struct msm_routing_data {
+	struct session_data sessions[MAX_SESSIONS];
+	struct device *dev;
+	struct mutex lock;
+};
+
+static struct msm_routing_data *routing_data;
+
+/**
+ * q6routing_reg_phy_stream() - Register a new stream for route setup
+ *
+ * @fedai_id: Frontend dai id.
+ * @perf_mode: Performace mode.
+ * @stream_id: ASM stream id to map.
+ * @stream_type: Direction of stream
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int q6routing_reg_phy_stream(int fedai_id, int perf_mode,
+			   int stream_id, int stream_type)
+{
+	int j, topology, num_copps = 0;
+	struct route_payload payload;
+	int copp_idx;
+	struct session_data *session;
+
+	if (!routing_data) {
+		pr_err("Routing driver not yet ready\n");
+		return -EINVAL;
+	}
+
+	session = &routing_data->sessions[stream_id - 1];
+	mutex_lock(&routing_data->lock);
+	session->fedai_id = fedai_id;
+	payload.num_copps = 0; /* only RX needs to use payload */
+	topology = NULL_COPP_TOPOLOGY;
+	copp_idx = q6adm_open(routing_data->dev, session->port_id,
+			      session->path_type, session->sample_rate,
+			      session->channels, topology, perf_mode,
+			      session->bits_per_sample, 0, 0);
+	if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+		mutex_unlock(&routing_data->lock);
+		return -EINVAL;
+	}
+
+	set_bit(copp_idx, &session->copp_map);
+	for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+		unsigned long copp = session->copp_map;
+
+		if (test_bit(j, &copp)) {
+			payload.port_id[num_copps] = session->port_id;
+			payload.copp_idx[num_copps] = j;
+			num_copps++;
+		}
+	}
+
+	if (num_copps) {
+		payload.num_copps = num_copps;
+		payload.session_id = stream_id;
+		q6adm_matrix_map(routing_data->dev, session->path_type,
+				 payload, perf_mode);
+	}
+	mutex_unlock(&routing_data->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(q6routing_reg_phy_stream);
+
+static struct session_data *routing_get_session(struct msm_routing_data *data,
+						int port_id, int port_type)
+{
+	int i;
+
+	for (i = 0; i < MAX_SESSIONS; i++)
+		if (port_id == data->sessions[i].port_id)
+			return &data->sessions[i];
+
+	return NULL;
+}
+
+static struct session_data *get_session_from_id(struct msm_routing_data *data,
+						int fedai_id)
+{
+	int i;
+
+	for (i = 0; i < MAX_SESSIONS; i++) {
+		if (fedai_id == data->sessions[i].fedai_id)
+			return &data->sessions[i];
+	}
+
+	return NULL;
+}
+/**
+ * q6routing_dereg_phy_stream() - Deregister a stream
+ *
+ * @fedai_id: Frontend dai id.
+ * @stream_type: Direction of stream
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+void q6routing_dereg_phy_stream(int fedai_id, int stream_type)
+{
+	struct session_data *session;
+	int idx;
+
+	session = get_session_from_id(routing_data, fedai_id);
+	if (!session)
+		return;
+
+	for_each_set_bit(idx, &session->copp_map, MAX_COPPS_PER_PORT)
+		q6adm_close(routing_data->dev, session->port_id,
+			    session->perf_mode, idx);
+
+	session->fedai_id = -1;
+	session->copp_map = 0;
+}
+EXPORT_SYMBOL_GPL(q6routing_dereg_phy_stream);
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_context *dapm =
+	    snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct soc_mixer_control *mc =
+	    (struct soc_mixer_control *)kcontrol->private_value;
+	int session_id = mc->shift;
+	struct snd_soc_platform *platform = snd_soc_dapm_to_platform(dapm);
+	struct msm_routing_data *priv = snd_soc_platform_get_drvdata(platform);
+	struct session_data *session = &priv->sessions[session_id];
+
+	if (session->port_id != -1)
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_context *dapm =
+				    snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct snd_soc_platform *platform = snd_soc_dapm_to_platform(dapm);
+	struct msm_routing_data *data = snd_soc_platform_get_drvdata(platform);
+	struct soc_mixer_control *mc =
+		    (struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_dapm_update *update = NULL;
+	int be_id = mc->reg;
+	int session_id = mc->shift;
+	struct session_data *session = &data->sessions[session_id];
+
+	if (ucontrol->value.integer.value[0]) {
+		session->port_id = be_id;
+		snd_soc_dapm_mixer_update_power(dapm, kcontrol, 1, update);
+	} else {
+		session->port_id = -1;
+		snd_soc_dapm_mixer_update_power(dapm, kcontrol, 0, update);
+	}
+
+	return 1;
+}
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", AFE_PORT_HDMI_RX,
+		       MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0,
+		       msm_routing_get_audio_mixer,
+		       msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", AFE_PORT_HDMI_RX,
+		       MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0,
+		       msm_routing_get_audio_mixer,
+		       msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", AFE_PORT_HDMI_RX,
+		       MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0,
+		       msm_routing_get_audio_mixer,
+		       msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", AFE_PORT_HDMI_RX,
+		       MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0,
+		       msm_routing_get_audio_mixer,
+		       msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", AFE_PORT_HDMI_RX,
+		       MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0,
+		       msm_routing_get_audio_mixer,
+		       msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", AFE_PORT_HDMI_RX,
+		       MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0,
+		       msm_routing_get_audio_mixer,
+		       msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", AFE_PORT_HDMI_RX,
+		       MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0,
+		       msm_routing_get_audio_mixer,
+		       msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", AFE_PORT_HDMI_RX,
+		       MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0,
+		       msm_routing_get_audio_mixer,
+		       msm_routing_put_audio_mixer),
+};
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+	/* Frontend AIF */
+	/* Widget name equals to Front-End DAI name<Need confirmation>,
+	 * Stream name must contains substring of front-end dai name
+	 */
+	SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0),
+
+	/* Mixer definitions */
+	SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+			   hdmi_mixer_controls,
+			   ARRAY_SIZE(hdmi_mixer_controls)),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{"HDMI Mixer", "MultiMedia1", "MM_DL1"},
+	{"HDMI Mixer", "MultiMedia2", "MM_DL2"},
+	{"HDMI Mixer", "MultiMedia3", "MM_DL3"},
+	{"HDMI Mixer", "MultiMedia4", "MM_DL4"},
+	{"HDMI Mixer", "MultiMedia5", "MM_DL5"},
+	{"HDMI Mixer", "MultiMedia6", "MM_DL6"},
+	{"HDMI Mixer", "MultiMedia7", "MM_DL7"},
+	{"HDMI Mixer", "MultiMedia8", "MM_DL8"},
+	{"HDMI", NULL, "HDMI Mixer"},
+	{"HDMI-RX", NULL, "HDMI"},
+};
+
+static int routing_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int be_id = rtd->cpu_dai->id;
+	struct snd_soc_platform *platform = rtd->platform;
+	struct msm_routing_data *data = snd_soc_platform_get_drvdata(platform);
+	struct session_data *session;
+	int port_id, port_type, path_type, bits_per_sample;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		path_type = ADM_PATH_PLAYBACK;
+		port_type = MSM_AFE_PORT_TYPE_RX;
+	}
+
+	port_id = be_id;
+
+	session = routing_get_session(data, port_id, port_type);
+
+	if (!session) {
+		pr_err("No session matrix setup yet..\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&data->lock);
+
+	session->path_type = path_type;
+	session->sample_rate = params_rate(params);
+	session->channels = params_channels(params);
+	session->format = params_format(params);
+
+	if (session->format == SNDRV_PCM_FORMAT_S16_LE)
+		session->bits_per_sample = 16;
+	else if (session->format == SNDRV_PCM_FORMAT_S24_LE)
+		bits_per_sample = 24;
+
+	mutex_unlock(&data->lock);
+	return 0;
+}
+
+static int routing_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int routing_prepare(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static struct snd_pcm_ops q6pcm_routing_ops = {
+	.hw_params = routing_hw_params,
+	.close = routing_close,
+	.prepare = routing_prepare,
+};
+
+/* Not used but frame seems to require it */
+static int msm_routing_probe(struct snd_soc_platform *platform)
+{
+	int i;
+
+	for (i = 0; i < MAX_SESSIONS; i++)
+		routing_data->sessions[i].port_id = -1;
+
+	snd_soc_platform_set_drvdata(platform, routing_data);
+
+	return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_routing_platform = {
+	.ops = &q6pcm_routing_ops,
+	.probe = msm_routing_probe,
+	.component_driver = {
+			     .dapm_widgets = msm_qdsp6_widgets,
+			     .num_dapm_widgets = ARRAY_SIZE(msm_qdsp6_widgets),
+			     .dapm_routes = intercon,
+			     .num_dapm_routes = ARRAY_SIZE(intercon),
+			     },
+};
+
+static int q6pcm_routing_probe(struct platform_device *pdev)
+{
+
+	routing_data = devm_kzalloc(&pdev->dev,
+				    sizeof(*routing_data), GFP_KERNEL);
+	if (!routing_data)
+		return -ENOMEM;
+
+	routing_data->dev = &pdev->dev;
+
+	mutex_init(&routing_data->lock);
+	dev_set_drvdata(&pdev->dev, routing_data);
+
+	return devm_snd_soc_register_platform(&pdev->dev,
+					      &msm_soc_routing_platform);
+}
+
+static int q6pcm_routing_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver q6pcm_routing_driver = {
+	.driver = {
+		   .name = "q6routing",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = q6pcm_routing_probe,
+	.remove = q6pcm_routing_remove,
+};
+
+module_platform_driver(q6pcm_routing_driver);
+
+MODULE_DESCRIPTION("Q6 Routing platform");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6routing.h b/sound/soc/qcom/qdsp6/q6routing.h
new file mode 100644
index 000000000000..7f0feb196acc
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6routing.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _Q6_PCM_ROUTING_H
+#define _Q6_PCM_ROUTING_H
+
+int q6routing_reg_phy_stream(int fedai_id, int perf_mode,
+			   int stream_id, int stream_type);
+void q6routing_dereg_phy_stream(int fedai_id, int stream_type);
+
+#endif /*_Q6_PCM_ROUTING_H */
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 09/15] ASoC: qcom: qdsp6: Add support to Q6CORE
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to core apr service, which is used to query
status of other static and dynamic services on the dsp.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/Kconfig        |   5 +
 sound/soc/qcom/qdsp6/Makefile |   1 +
 sound/soc/qcom/qdsp6/q6core.c | 227 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 233 insertions(+)
 create mode 100644 sound/soc/qcom/qdsp6/q6core.c

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 7ebdb879a8a3..121b9c957024 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -56,11 +56,16 @@ config SND_SOC_QDSP6_ASM
 	tristate
 	default n
 
+config SND_SOC_QDSP6_CORE
+	tristate
+	default n
+
 config SND_SOC_QDSP6
 	tristate "SoC ALSA audio driver for QDSP6"
 	select SND_SOC_QDSP6_AFE
 	select SND_SOC_QDSP6_ADM
 	select SND_SOC_QDSP6_ASM
+	select SND_SOC_QDSP6_CORE
 	help
 	 To add support for MSM QDSP6 Soc Audio.
 	 This will enable sound soc platform specific
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index 49dd3ccab27b..ad7f10691e54 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o
 obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
 obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
+obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o
diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c
new file mode 100644
index 000000000000..d4e2dbc62489
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6core.c
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: GPL-2.0
+* Copyright (c) 2017, Linaro Limited
+*/
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/wait.h>
+#include <linux/soc/qcom/apr.h>
+#include <linux/platform_device.h>
+#include <sound/asound.h>
+#include "common.h"
+
+#define ADSP_STATE_READY_TIMEOUT_MS    3000
+#define Q6_READY_TIMEOUT_MS 100
+#define AVCS_CMD_ADSP_EVENT_GET_STATE		0x0001290C
+#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE	0x0001290D
+#define AVCS_GET_VERSIONS       0x00012905
+#define AVCS_GET_VERSIONS_RSP   0x00012906
+
+struct avcs_svc_info {
+	uint32_t service_id;
+	uint32_t version;
+} __packed;
+
+struct q6core {
+	struct apr_device *adev;
+	wait_queue_head_t wait;
+	uint32_t avcs_state;
+	int resp_received;
+	uint32_t num_services;
+	struct avcs_svc_info *svcs_info;
+};
+
+static struct apr_device_id static_services[] = {
+	ADSP_AUDIO_APR_DEV("AFE", APR_SVC_AFE),
+	ADSP_AUDIO_APR_DEV("ASM", APR_SVC_ASM),
+	ADSP_AUDIO_APR_DEV("ADM", APR_SVC_ADM),
+	ADSP_AUDIO_APR_DEV("TEST", APR_SVC_TEST_CLIENT),
+	ADSP_AUDIO_APR_DEV("MVM", APR_SVC_ADSP_MVM),
+	ADSP_AUDIO_APR_DEV("CVS", APR_SVC_ADSP_CVS),
+	ADSP_AUDIO_APR_DEV("CVP", APR_SVC_ADSP_CVP),
+	ADSP_AUDIO_APR_DEV("USM", APR_SVC_USM),
+	ADSP_AUDIO_APR_DEV("VIDC", APR_SVC_VIDC),
+	ADSP_AUDIO_APR_DEV("LSM", APR_SVC_LSM),
+};
+
+static int core_callback(struct apr_device *adev, struct apr_client_data *data)
+{
+	struct q6core *core = dev_get_drvdata(&adev->dev);
+	uint32_t *payload;
+
+	switch (data->opcode) {
+	case AVCS_GET_VERSIONS_RSP:
+		payload = data->payload;
+		core->num_services = payload[1];
+
+		if (!core->svcs_info)
+			core->svcs_info = kcalloc(core->num_services,
+						  sizeof(*core->svcs_info),
+						  GFP_ATOMIC);
+		if (!core->svcs_info)
+			return -ENOMEM;
+
+		/* svc info is after 8 bytes */
+		memcpy(core->svcs_info, payload + 2,
+		       core->num_services * sizeof(*core->svcs_info));
+
+		core->resp_received = 1;
+		wake_up(&core->wait);
+
+		break;
+	case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
+		payload = data->payload;
+		core->avcs_state = payload[0];
+
+		core->resp_received = 1;
+		wake_up(&core->wait);
+		break;
+	default:
+		dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n",
+			data->opcode);
+		break;
+	}
+
+	return 0;
+}
+
+void q6core_add_service(struct device *dev, uint32_t svc_id, uint32_t version)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(static_services); i++) {
+		if (static_services[i].svc_id == svc_id) {
+			static_services[i].svc_version = version;
+			apr_add_device(dev->parent, &static_services[i]);
+			dev_info(dev,
+				"Adding SVC: %s: id 0x%x API Ver 0x%x:0x%x\n",
+				 static_services[i].name, svc_id,
+				 APR_SVC_MAJOR_VERSION(version),
+				 APR_SVC_MINOR_VERSION(version));
+		}
+	}
+}
+
+static void q6core_add_static_services(struct q6core *core)
+{
+	int i;
+	struct apr_device *adev = core->adev;
+	struct avcs_svc_info *svcs_info = core->svcs_info;
+
+	for (i = 0; i < core->num_services; i++)
+		q6core_add_service(&adev->dev, svcs_info[i].service_id,
+				   svcs_info[i].version);
+}
+
+static int q6core_get_svc_versions(struct q6core *core)
+{
+	struct apr_device *adev = core->adev;
+	struct apr_hdr hdr = {0};
+	int rc;
+
+	hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0);
+	hdr.opcode = AVCS_GET_VERSIONS;
+
+	rc = apr_send_pkt(adev, (uint32_t *)&hdr);
+	if (rc < 0)
+		return rc;
+
+	rc = wait_event_timeout(core->wait, (core->resp_received == 1),
+				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
+	if (rc > 0 && core->resp_received) {
+		core->resp_received = 0;
+		return 0;
+	}
+
+	return rc;
+}
+
+static bool q6core_is_adsp_ready(struct q6core *core)
+{
+	struct apr_device *adev = core->adev;
+	struct apr_hdr hdr = {0};
+	int rc;
+
+	hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0);
+	hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
+
+	rc = apr_send_pkt(adev, (uint32_t *)&hdr);
+	if (rc < 0)
+		return false;
+
+	rc = wait_event_timeout(core->wait, (core->resp_received == 1),
+				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
+	if (rc > 0 && core->resp_received) {
+		core->resp_received = 0;
+		if (core->avcs_state == 0x1)
+			return true;
+	}
+
+	return false;
+}
+
+static int q6core_probe(struct apr_device *adev)
+{
+	struct q6core *core;
+	unsigned long  timeout = jiffies +
+				 msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+	int ret = 0;
+
+	core = devm_kzalloc(&adev->dev, sizeof(*core), GFP_KERNEL);
+	if (!core)
+		return -ENOMEM;
+
+	dev_set_drvdata(&adev->dev, core);
+
+	core->adev = adev;
+	init_waitqueue_head(&core->wait);
+
+	do {
+		if (!q6core_is_adsp_ready(core)) {
+			dev_info(&adev->dev, "ADSP Audio isn't ready\n");
+		} else {
+			dev_info(&adev->dev, "ADSP Audio is ready\n");
+
+			ret = q6core_get_svc_versions(core);
+			if (!ret)
+				q6core_add_static_services(core);
+
+			break;
+		}
+	} while (time_after(timeout, jiffies));
+
+	return ret;
+}
+
+static int q6core_exit(struct apr_device *adev)
+{
+	return 0;
+}
+
+static const struct apr_device_id core_id[] = {
+	{"Q6CORE", APR_DOMAIN_ADSP, APR_SVC_ADSP_CORE, APR_CLIENT_AUDIO},
+	{ },
+};
+
+static struct apr_driver qcom_q6core_driver = {
+	.probe = q6core_probe,
+	.remove = q6core_exit,
+	.callback = core_callback,
+	.id_table = core_id,
+	.driver = {
+		   .name = "qcom-q6core",
+		   },
+};
+
+module_apr_driver(qcom_q6core_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
+MODULE_DESCRIPTION("q6 core");
+MODULE_LICENSE("GPL v2");
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 08/15] ASoC: qcom: q6asm: add support to audio stream apis
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to open, write and media format commands
in the q6asm module.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/qdsp6/q6asm.c | 530 ++++++++++++++++++++++++++++++++++++++++++-
 sound/soc/qcom/qdsp6/q6asm.h |  42 ++++
 2 files changed, 571 insertions(+), 1 deletion(-)

diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
index 4be92441f524..dabd6509ef99 100644
--- a/sound/soc/qcom/qdsp6/q6asm.c
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -8,16 +8,34 @@
 #include <linux/soc/qcom/apr.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
+#include <uapi/sound/asound.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include "q6asm.h"
 #include "common.h"
 
+#define ASM_STREAM_CMD_CLOSE			0x00010BCD
+#define ASM_STREAM_CMD_FLUSH			0x00010BCE
+#define ASM_SESSION_CMD_PAUSE			0x00010BD3
+#define ASM_DATA_CMD_EOS			0x00010BDB
+#define DEFAULT_POPP_TOPOLOGY			0x00010BE4
+#define ASM_STREAM_CMD_FLUSH_READBUFS		0x00010C09
 #define ASM_CMD_SHARED_MEM_MAP_REGIONS		0x00010D92
 #define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS	0x00010D93
 #define ASM_CMD_SHARED_MEM_UNMAP_REGIONS	0x00010D94
-
+#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2	0x00010D98
+#define ASM_DATA_EVENT_WRITE_DONE_V2		0x00010D99
+#define ASM_SESSION_CMD_RUN_V2			0x00010DAA
+#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2	0x00010DA5
+#define ASM_DATA_CMD_WRITE_V2			0x00010DAB
+#define ASM_SESSION_CMD_SUSPEND			0x00010DEC
+#define ASM_STREAM_CMD_OPEN_WRITE_V3		0x00010DB3
+
+#define ASM_LEGACY_STREAM_SESSION	0
+#define ASM_END_POINT_DEVICE_MATRIX	0
+#define DEFAULT_APP_TYPE		0
+#define TUN_WRITE_IO_MODE		0x0008	/* tunnel read write mode */
 #define TUN_READ_IO_MODE		0x0004	/* tunnel read write mode */
 #define SYNC_IO_MODE			0x0001
 #define ASYNC_IO_MODE			0x0002
@@ -42,6 +60,49 @@ struct avs_cmd_shared_mem_unmap_regions {
 	u32 mem_map_handle;
 } __packed;
 
+struct asm_data_cmd_media_fmt_update_v2 {
+	u32 fmt_blk_size;
+} __packed;
+
+struct asm_multi_channel_pcm_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+	u16 num_channels;
+	u16 bits_per_sample;
+	u32 sample_rate;
+	u16 is_signed;
+	u16 reserved;
+	u8 channel_mapping[8];
+} __packed;
+
+struct asm_data_cmd_write_v2 {
+	struct apr_hdr hdr;
+	u32 buf_addr_lsw;
+	u32 buf_addr_msw;
+	u32 mem_map_handle;
+	u32 buf_size;
+	u32 seq_id;
+	u32 timestamp_lsw;
+	u32 timestamp_msw;
+	u32 flags;
+} __packed;
+
+struct asm_stream_cmd_open_write_v3 {
+	struct apr_hdr hdr;
+	uint32_t mode_flags;
+	uint16_t sink_endpointype;
+	uint16_t bits_per_sample;
+	uint32_t postprocopo_id;
+	uint32_t dec_fmt_id;
+} __packed;
+
+struct asm_session_cmd_run_v2 {
+	struct apr_hdr hdr;
+	u32 flags;
+	u32 time_lsw;
+	u32 time_msw;
+} __packed;
+
 struct audio_buffer {
 	dma_addr_t phys;
 	uint32_t used;
@@ -408,6 +469,111 @@ static struct audio_client *q6asm_get_audio_client(struct q6asm *a,
 	return NULL;
 }
 
+static int32_t q6asm_callback(struct apr_device *adev,
+			      struct apr_client_data *data, int session_id)
+{
+	struct audio_client *ac;// = (struct audio_client *)priv;
+	uint32_t token;
+	uint32_t *payload;
+	uint32_t wakeup_flag = 1;
+	uint32_t client_event = 0;
+	struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
+
+	if (data == NULL)
+		return -EINVAL;
+
+	ac = q6asm_get_audio_client(q6asm, session_id);
+	if (!q6asm_is_valid_audio_client(ac))
+		return -EINVAL;
+
+	payload = data->payload;
+
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		token = data->token;
+		switch (payload[0]) {
+		case ASM_SESSION_CMD_PAUSE:
+			client_event = ASM_CLIENT_EVENT_CMD_PAUSE_DONE;
+			break;
+		case ASM_SESSION_CMD_SUSPEND:
+			client_event = ASM_CLIENT_EVENT_CMD_SUSPEND_DONE;
+			break;
+		case ASM_DATA_CMD_EOS:
+			client_event = ASM_CLIENT_EVENT_CMD_EOS_DONE;
+			break;
+			break;
+		case ASM_STREAM_CMD_FLUSH:
+			client_event = ASM_CLIENT_EVENT_CMD_FLUSH_DONE;
+			break;
+		case ASM_SESSION_CMD_RUN_V2:
+			client_event = ASM_CLIENT_EVENT_CMD_RUN_DONE;
+			break;
+
+		case ASM_STREAM_CMD_FLUSH_READBUFS:
+			if (token != ac->session) {
+				dev_err(ac->dev, "session invalid\n");
+				return -EINVAL;
+			}
+		case ASM_STREAM_CMD_CLOSE:
+			client_event = ASM_CLIENT_EVENT_CMD_CLOSE_DONE;
+			break;
+		case ASM_STREAM_CMD_OPEN_WRITE_V3:
+		case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+			if (payload[1] != 0) {
+				dev_err(ac->dev,
+					"cmd = 0x%x returned error = 0x%x\n",
+					payload[0], payload[1]);
+				if (wakeup_flag) {
+					ac->cmd_state = payload[1];
+					wake_up(&ac->cmd_wait);
+				}
+				return 0;
+			}
+			break;
+		default:
+			dev_err(ac->dev, "command[0x%x] not expecting rsp\n",
+				payload[0]);
+			break;
+		}
+
+		if (ac->cmd_state && wakeup_flag) {
+			ac->cmd_state = 0;
+			wake_up(&ac->cmd_wait);
+		}
+		if (ac->cb)
+			ac->cb(client_event, data->token,
+			       data->payload, ac->priv);
+
+		return 0;
+	}
+
+	switch (data->opcode) {
+	case ASM_DATA_EVENT_WRITE_DONE_V2:{
+			struct audio_port_data *port =
+			    &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
+
+			client_event = ASM_CLIENT_EVENT_DATA_WRITE_DONE;
+
+			if (ac->io_mode & SYNC_IO_MODE) {
+				dma_addr_t phys = port->buf[data->token].phys;
+
+				if (lower_32_bits(phys) != payload[0] ||
+				    upper_32_bits(phys) != payload[1]) {
+					dev_err(ac->dev, "Expected addr %pa\n",
+						&port->buf[data->token].phys);
+					return -EINVAL;
+				}
+				token = data->token;
+				port->buf[token].used = 1;
+			}
+			break;
+		}
+	}
+	if (ac->cb)
+		ac->cb(client_event, data->token, data->payload, ac->priv);
+
+	return 0;
+}
+
 static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *data)
 {
 	struct q6asm *a, *q6asm = dev_get_drvdata(&adev->dev);
@@ -415,12 +581,16 @@ static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *
 	struct audio_port_data *port;
 	uint32_t dir = 0;
 	uint32_t sid = 0;
+	int dest_port;
 	uint32_t *payload;
 
 	if (!data) {
 		dev_err(&adev->dev, "%s: Invalid CB\n", __func__);
 		return 0;
 	}
+	dest_port = (data->dest_port >> 8) & 0xFF;
+	if (dest_port)
+		return q6asm_callback(adev, data, dest_port);
 
 	payload = data->payload;
 	sid = (data->token >> 8) & 0x0F;
@@ -540,6 +710,364 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc);
 
+static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
+			      uint16_t bits_per_sample, uint32_t stream_id,
+			      bool is_gapless_mode)
+{
+	struct asm_stream_cmd_open_write_v3 open;
+	int rc;
+
+	q6asm_add_hdr(ac, &open.hdr, sizeof(open), true, stream_id);
+	ac->cmd_state = -1;
+
+	open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3;
+	open.mode_flags = 0x00;
+	open.mode_flags |= ASM_LEGACY_STREAM_SESSION;
+	if (is_gapless_mode)
+		open.mode_flags |= 1 << ASM_SHIFT_GAPLESS_MODE_FLAG;
+
+	/* source endpoint : matrix */
+	open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX;
+	open.bits_per_sample = bits_per_sample;
+	open.postprocopo_id = DEFAULT_POPP_TOPOLOGY;
+
+	switch (format) {
+	case FORMAT_LINEAR_PCM:
+		open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+		break;
+	default:
+		dev_err(ac->dev, "Invalid format 0x%x\n", format);
+		return -EINVAL;
+	}
+	rc = apr_send_pkt(ac->adev, (uint32_t *) &open);
+	if (rc < 0)
+		return rc;
+
+	rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ);
+	if (!rc) {
+		dev_err(ac->dev, "timeout on open write\n");
+		return -ETIMEDOUT;
+	}
+
+	if (ac->cmd_state > 0)
+		return adsp_err_get_lnx_err_code(ac->cmd_state);
+
+	ac->io_mode |= TUN_WRITE_IO_MODE;
+
+	return 0;
+}
+
+/**
+ * q6asm_open_write() - Open audio client for writing
+ *
+ * @ac: audio client pointer
+ * @format: audio sample format
+ * @bits_per_sample: bits per sample
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_open_write(struct audio_client *ac, uint32_t format,
+		     uint16_t bits_per_sample)
+{
+	return __q6asm_open_write(ac, format, bits_per_sample,
+				  ac->stream_id, false);
+}
+EXPORT_SYMBOL_GPL(q6asm_open_write);
+
+static int __q6asm_run(struct audio_client *ac, uint32_t flags,
+	      uint32_t msw_ts, uint32_t lsw_ts, bool wait)
+{
+	struct asm_session_cmd_run_v2 run;
+	int rc;
+
+	q6asm_add_hdr(ac, &run.hdr, sizeof(run), true, ac->stream_id);
+	ac->cmd_state = -1;
+
+	run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
+	run.flags = flags;
+	run.time_lsw = lsw_ts;
+	run.time_msw = msw_ts;
+
+	rc = apr_send_pkt(ac->adev, (uint32_t *) &run);
+	if (rc < 0)
+		return rc;
+
+	if (wait) {
+		rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0),
+					5 * HZ);
+		if (!rc) {
+			dev_err(ac->dev, "timeout on run cmd\n");
+			return -ETIMEDOUT;
+		}
+		if (ac->cmd_state > 0)
+			return adsp_err_get_lnx_err_code(ac->cmd_state);
+	}
+
+	return 0;
+}
+
+/**
+ * q6asm_run() - start the audio client
+ *
+ * @ac: audio client pointer
+ * @flags: flags associated with write
+ * @msw_ts: timestamp msw
+ * @lsw_ts: timestamp lsw
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_run(struct audio_client *ac, uint32_t flags,
+	      uint32_t msw_ts, uint32_t lsw_ts)
+{
+	return __q6asm_run(ac, flags, msw_ts, lsw_ts, true);
+}
+EXPORT_SYMBOL_GPL(q6asm_run);
+
+/**
+ * q6asm_run_nowait() - start the audio client withou blocking
+ *
+ * @ac: audio client pointer
+ * @flags: flags associated with write
+ * @msw_ts: timestamp msw
+ * @lsw_ts: timestamp lsw
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+	      uint32_t msw_ts, uint32_t lsw_ts)
+{
+	return __q6asm_run(ac, flags, msw_ts, lsw_ts, false);
+}
+EXPORT_SYMBOL_GPL(q6asm_run_nowait);
+
+/**
+ * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration
+ *
+ * @ac: audio client pointer
+ * @rate: audio sample rate
+ * @channels: number of audio channels.
+ * @use_default_chmap: flag to use default ch map.
+ * @channel_map: channel map pointer
+ * @bits_per_sample: bits per sample
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+					  uint32_t rate, uint32_t channels,
+					  bool use_default_chmap,
+					  char *channel_map,
+					  uint16_t bits_per_sample)
+{
+	struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
+	u8 *channel_mapping;
+	int rc = 0;
+
+	q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), true, ac->stream_id);
+	ac->cmd_state = -1;
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+	    sizeof(fmt.fmt_blk);
+	fmt.num_channels = channels;
+	fmt.bits_per_sample = bits_per_sample;
+	fmt.sample_rate = rate;
+	fmt.is_signed = 1;
+
+	channel_mapping = fmt.channel_mapping;
+
+	if (use_default_chmap) {
+		if (q6dsp_map_channels(channel_mapping, channels)) {
+			dev_err(ac->dev, " map channels failed %d\n", channels);
+			return -EINVAL;
+		}
+	} else {
+		memcpy(channel_mapping, channel_map,
+		       PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->adev, (uint32_t *) &fmt);
+	if (rc < 0)
+		goto fail_cmd;
+
+	rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ);
+	if (!rc) {
+		dev_err(ac->dev, "timeout on format update\n");
+		return -ETIMEDOUT;
+	}
+	if (ac->cmd_state > 0)
+		return adsp_err_get_lnx_err_code(ac->cmd_state);
+
+	return 0;
+fail_cmd:
+	return rc;
+}
+EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm);
+
+/**
+ * q6asm_write_nolock() - non blocking write
+ *
+ * @ac: audio client pointer
+ * @len: lenght in bytes
+ * @msw_ts: timestamp msw
+ * @lsw_ts: timestamp lsw
+ * @flags: flags associated with write
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+		       uint32_t lsw_ts, uint32_t flags)
+{
+	struct asm_data_cmd_write_v2 write;
+	struct audio_port_data *port;
+	struct audio_buffer *ab;
+	int dsp_buf = 0;
+	int rc = 0;
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK];
+		q6asm_add_hdr(ac, &write.hdr, sizeof(write), false,
+			      ac->stream_id);
+
+		dsp_buf = port->dsp_buf;
+		ab = &port->buf[dsp_buf];
+
+		write.hdr.token = port->dsp_buf;
+		write.hdr.opcode = ASM_DATA_CMD_WRITE_V2;
+		write.buf_addr_lsw = lower_32_bits(ab->phys);
+		write.buf_addr_msw = upper_32_bits(ab->phys);
+		write.buf_size = len;
+		write.seq_id = port->dsp_buf;
+		write.timestamp_lsw = lsw_ts;
+		write.timestamp_msw = msw_ts;
+		write.mem_map_handle =
+		    ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle;
+
+		if (flags == NO_TIMESTAMP)
+			write.flags = (flags & 0x800000FF);
+		else
+			write.flags = (0x80000000 | flags);
+
+		port->dsp_buf++;
+
+		if (port->dsp_buf >= port->max_buf_cnt)
+			port->dsp_buf = 0;
+
+		rc = apr_send_pkt(ac->adev, (uint32_t *) &write);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(q6asm_write_nolock);
+
+static void q6asm_reset_buf_state(struct audio_client *ac)
+{
+	int cnt = 0;
+	int loopcnt = 0;
+	int used;
+	struct audio_port_data *port = NULL;
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0);
+		mutex_lock(&ac->cmd_lock);
+		for (loopcnt = 0; loopcnt <= SNDRV_PCM_STREAM_CAPTURE;
+		     loopcnt++) {
+			port = &ac->port[loopcnt];
+			cnt = port->max_buf_cnt - 1;
+			port->dsp_buf = 0;
+			while (cnt >= 0) {
+				if (!port->buf)
+					continue;
+				port->buf[cnt].used = used;
+				cnt--;
+			}
+		}
+		mutex_unlock(&ac->cmd_lock);
+	}
+}
+
+static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait)
+{
+	int stream_id = ac->stream_id;
+	struct apr_hdr hdr;
+	int rc;
+
+	q6asm_add_hdr(ac, &hdr, sizeof(hdr), true, stream_id);
+	ac->cmd_state = -1;
+	switch (cmd) {
+	case CMD_PAUSE:
+		hdr.opcode = ASM_SESSION_CMD_PAUSE;
+		break;
+	case CMD_SUSPEND:
+		hdr.opcode = ASM_SESSION_CMD_SUSPEND;
+		break;
+	case CMD_FLUSH:
+		hdr.opcode = ASM_STREAM_CMD_FLUSH;
+		break;
+	case CMD_OUT_FLUSH:
+		hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS;
+		break;
+	case CMD_EOS:
+		hdr.opcode = ASM_DATA_CMD_EOS;
+		ac->cmd_state = 0;
+		break;
+	case CMD_CLOSE:
+		hdr.opcode = ASM_STREAM_CMD_CLOSE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	rc = apr_send_pkt(ac->adev, (uint32_t *) &hdr);
+	if (rc < 0)
+		return rc;
+
+	if (!wait)
+		return 0;
+
+	rc = wait_event_timeout(ac->cmd_wait, (ac->cmd_state >= 0), 5 * HZ);
+	if (!rc) {
+		dev_err(ac->dev, "timeout response for opcode[0x%x]\n",
+			hdr.opcode);
+		return -ETIMEDOUT;
+	}
+	if (ac->cmd_state > 0)
+		return adsp_err_get_lnx_err_code(ac->cmd_state);
+
+	if (cmd == CMD_FLUSH)
+		q6asm_reset_buf_state(ac);
+
+	return 0;
+}
+
+/**
+ * q6asm_cmd() - run cmd on audio client
+ *
+ * @ac: audio client pointer
+ * @cmd: command to run on audio client.
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_cmd(struct audio_client *ac, int cmd)
+{
+	return __q6asm_cmd(ac, cmd, true);
+}
+EXPORT_SYMBOL_GPL(q6asm_cmd);
+
+/**
+ * q6asm_cmd_nowait() - non blocking, run cmd on audio client
+ *
+ * @ac: audio client pointer
+ * @cmd: command to run on audio client.
+ *
+ * Return: Will be an negative value on error or zero on success
+ */
+int q6asm_cmd_nowait(struct audio_client *ac, int cmd)
+{
+	return __q6asm_cmd(ac, cmd, false);
+}
+EXPORT_SYMBOL_GPL(q6asm_cmd_nowait);
 
 static int q6asm_probe(struct apr_device *adev)
 {
diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h
index e1409c368600..b4896059da79 100644
--- a/sound/soc/qcom/qdsp6/q6asm.h
+++ b/sound/soc/qcom/qdsp6/q6asm.h
@@ -2,7 +2,34 @@
 #ifndef __Q6_ASM_H__
 #define __Q6_ASM_H__
 
+/* ASM client callback events */
+#define CMD_PAUSE			0x0001
+#define ASM_CLIENT_EVENT_CMD_PAUSE_DONE		0x1001
+#define CMD_FLUSH				0x0002
+#define ASM_CLIENT_EVENT_CMD_FLUSH_DONE		0x1002
+#define CMD_EOS				0x0003
+#define ASM_CLIENT_EVENT_CMD_EOS_DONE		0x1003
+#define CMD_CLOSE				0x0004
+#define ASM_CLIENT_EVENT_CMD_CLOSE_DONE		0x1004
+#define CMD_OUT_FLUSH				0x0005
+#define ASM_CLIENT_EVENT_CMD_OUT_FLUSH_DONE	0x1005
+#define CMD_SUSPEND				0x0006
+#define ASM_CLIENT_EVENT_CMD_SUSPEND_DONE	0x1006
+#define ASM_CLIENT_EVENT_CMD_RUN_DONE		0x1008
+#define ASM_CLIENT_EVENT_DATA_WRITE_DONE	0x1009
+
+#define MSM_FRONTEND_DAI_MULTIMEDIA1	0
+#define MSM_FRONTEND_DAI_MULTIMEDIA2	1
+#define	MSM_FRONTEND_DAI_MULTIMEDIA3	2
+#define MSM_FRONTEND_DAI_MULTIMEDIA4	3
+#define MSM_FRONTEND_DAI_MULTIMEDIA5	4
+#define MSM_FRONTEND_DAI_MULTIMEDIA6	5
+#define	MSM_FRONTEND_DAI_MULTIMEDIA7	6
+#define	MSM_FRONTEND_DAI_MULTIMEDIA8	7
+
 #define MAX_SESSIONS	16
+#define NO_TIMESTAMP    0xFF00
+#define FORMAT_LINEAR_PCM   0x0000
 
 typedef void (*app_cb) (uint32_t opcode, uint32_t token,
 			uint32_t *payload, void *priv);
@@ -10,6 +37,21 @@ struct audio_client;
 struct audio_client *q6asm_audio_client_alloc(struct device *dev,
 					      app_cb cb, void *priv);
 void q6asm_audio_client_free(struct audio_client *ac);
+int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+		       uint32_t lsw_ts, uint32_t flags);
+int q6asm_open_write(struct audio_client *ac, uint32_t format,
+		     uint16_t bits_per_sample);
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+					  uint32_t rate, uint32_t channels,
+					  bool use_default_chmap,
+					  char *channel_map,
+					  uint16_t bits_per_sample);
+int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts,
+	      uint32_t lsw_ts);
+int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts,
+		     uint32_t lsw_ts);
+int q6asm_cmd(struct audio_client *ac, int cmd);
+int q6asm_cmd_nowait(struct audio_client *ac, int cmd);
 int q6asm_get_session_id(struct audio_client *ac);
 int q6asm_map_memory_regions(unsigned int dir,
 			     struct audio_client *ac,
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 07/15] ASoC: qcom: q6asm: Add support to memory map and unmap
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to memory map and unmap regions commands in
q6asm module.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/qdsp6/q6asm.c | 343 ++++++++++++++++++++++++++++++++++++++++++-
 sound/soc/qcom/qdsp6/q6asm.h |   5 +
 2 files changed, 347 insertions(+), 1 deletion(-)

diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
index 9cc583afef4d..4be92441f524 100644
--- a/sound/soc/qcom/qdsp6/q6asm.c
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -14,9 +14,46 @@
 #include "q6asm.h"
 #include "common.h"
 
+#define ASM_CMD_SHARED_MEM_MAP_REGIONS		0x00010D92
+#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS	0x00010D93
+#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS	0x00010D94
+
 #define TUN_READ_IO_MODE		0x0004	/* tunnel read write mode */
 #define SYNC_IO_MODE			0x0001
 #define ASYNC_IO_MODE			0x0002
+#define ASM_SHIFT_GAPLESS_MODE_FLAG	31
+#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL	3
+
+struct avs_cmd_shared_mem_map_regions {
+	struct apr_hdr hdr;
+	u16 mem_pool_id;
+	u16 num_regions;
+	u32 property_flag;
+} __packed;
+
+struct avs_shared_map_region_payload {
+	u32 shm_addr_lsw;
+	u32 shm_addr_msw;
+	u32 mem_size_bytes;
+} __packed;
+
+struct avs_cmd_shared_mem_unmap_regions {
+	struct apr_hdr hdr;
+	u32 mem_map_handle;
+} __packed;
+
+struct audio_buffer {
+	dma_addr_t phys;
+	uint32_t used;
+	uint32_t size;		/* size of buffer */
+};
+
+struct audio_port_data {
+	struct audio_buffer *buf;
+	uint32_t max_buf_cnt;
+	uint32_t dsp_buf;
+	uint32_t mem_map_handle;
+};
 
 struct audio_client {
 	int session;
@@ -27,6 +64,8 @@ struct audio_client {
 	uint64_t time_stamp;
 	struct apr_device *adev;
 	struct mutex cmd_lock;
+	/* idx:1 out port, 0: in port */
+	struct audio_port_data port[2];
 	wait_queue_head_t cmd_wait;
 	int perf_mode;
 	int stream_id;
@@ -86,6 +125,260 @@ static void q6asm_session_free(struct audio_client *ac)
 	mutex_unlock(&a->session_lock);
 }
 
+static inline void q6asm_add_mmaphdr(struct audio_client *ac,
+				     struct apr_hdr *hdr, u32 pkt_size,
+				     bool cmd_flg, u32 token)
+{
+	hdr->hdr_field = APR_SEQ_CMD_HDR_FIELD;
+	hdr->src_port = 0;
+	hdr->dest_port = 0;
+	hdr->pkt_size = pkt_size;
+	if (cmd_flg)
+		hdr->token = token;
+}
+
+static inline void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+				 uint32_t pkt_size, bool cmd_flg,
+				 uint32_t stream_id)
+{
+	hdr->hdr_field = APR_SEQ_CMD_HDR_FIELD;
+	hdr->src_svc = ac->adev->svc_id;
+	hdr->src_domain = APR_DOMAIN_APPS;
+	hdr->dest_svc = APR_SVC_ASM;
+	hdr->dest_domain = APR_DOMAIN_ADSP;
+	hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+	hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+	hdr->pkt_size = pkt_size;
+	if (cmd_flg)
+		hdr->token = ac->session;
+}
+
+static int __q6asm_memory_unmap(struct audio_client *ac,
+				phys_addr_t buf_add, int dir)
+{
+	struct avs_cmd_shared_mem_unmap_regions mem_unmap;
+	struct q6asm *a = dev_get_drvdata(ac->dev->parent);
+	int rc;
+
+	if (!a)
+		return -ENODEV;
+
+	q6asm_add_mmaphdr(ac, &mem_unmap.hdr, sizeof(mem_unmap), true,
+			  ((ac->session << 8) | dir));
+	a->mem_state = -1;
+
+	mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
+	mem_unmap.mem_map_handle = ac->port[dir].mem_map_handle;
+
+	if (mem_unmap.mem_map_handle == 0) {
+		dev_err(ac->dev, "invalid mem handle\n");
+		return -EINVAL;
+	}
+
+	rc = apr_send_pkt(a->adev, (uint32_t *) &mem_unmap);
+	if (rc < 0)
+		return rc;
+
+	rc = wait_event_timeout(a->mem_wait, (a->mem_state >= 0),
+				5 * HZ);
+	if (!rc) {
+		dev_err(ac->dev, "CMD timeout for memory_unmap 0x%x\n",
+			mem_unmap.mem_map_handle);
+		return -ETIMEDOUT;
+	} else if (a->mem_state > 0) {
+		return adsp_err_get_lnx_err_code(a->mem_state);
+	}
+	ac->port[dir].mem_map_handle = 0;
+
+	return 0;
+}
+
+/**
+ * q6asm_unmap_memory_regions() - unmap memory regions in the dsp.
+ *
+ * @dir: direction of audio stream
+ * @ac: audio client instanace
+ *
+ * Return: Will be an negative value on failure or zero on success
+ */
+int q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac)
+{
+	struct audio_port_data *port;
+	int cnt = 0;
+	int rc = 0;
+
+	mutex_lock(&ac->cmd_lock);
+	port = &ac->port[dir];
+	if (!port->buf) {
+		mutex_unlock(&ac->cmd_lock);
+		return 0;
+	}
+	cnt = port->max_buf_cnt - 1;
+	if (cnt >= 0) {
+		rc = __q6asm_memory_unmap(ac, port->buf[dir].phys, dir);
+		if (rc < 0) {
+			dev_err(ac->dev, "%s: Memory_unmap_regions failed %d\n",
+				__func__, rc);
+			mutex_unlock(&ac->cmd_lock);
+			return rc;
+		}
+	}
+
+	port->max_buf_cnt = 0;
+	kfree(port->buf);
+	port->buf = NULL;
+	mutex_unlock(&ac->cmd_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(q6asm_unmap_memory_regions);
+
+static int __q6asm_memory_map_regions(struct audio_client *ac, int dir,
+				      uint32_t period_sz, uint32_t periods,
+				      bool is_contiguous)
+{
+	struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+	struct avs_shared_map_region_payload *mregions = NULL;
+	struct q6asm *a = dev_get_drvdata(ac->dev->parent);
+	struct audio_port_data *port = NULL;
+	struct audio_buffer *ab = NULL;
+	void *mmap_region_cmd = NULL;
+	void *payload = NULL;
+	int rc = 0;
+	int i = 0;
+	int cmd_size = 0;
+	uint32_t num_regions;
+	uint32_t buf_sz;
+
+	if (!a)
+		return -ENODEV;
+	num_regions = is_contiguous ? 1 : periods;
+	buf_sz = is_contiguous ? (period_sz * periods) : period_sz;
+	buf_sz = PAGE_ALIGN(buf_sz);
+
+	cmd_size = sizeof(*mmap_regions) + (sizeof(*mregions) * num_regions);
+
+	mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+	if (!mmap_region_cmd)
+		return -ENOMEM;
+
+	mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
+	q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, true,
+			  ((ac->session << 8) | dir));
+	a->mem_state = -1;
+
+	mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;
+	mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+	mmap_regions->num_regions = num_regions;
+	mmap_regions->property_flag = 0x00;
+
+	payload = ((u8 *) mmap_region_cmd +
+		   sizeof(struct avs_cmd_shared_mem_map_regions));
+
+	mregions = (struct avs_shared_map_region_payload *)payload;
+
+	ac->port[dir].mem_map_handle = 0;
+	port = &ac->port[dir];
+
+	for (i = 0; i < num_regions; i++) {
+		ab = &port->buf[i];
+		mregions->shm_addr_lsw = lower_32_bits(ab->phys);
+		mregions->shm_addr_msw = upper_32_bits(ab->phys);
+		mregions->mem_size_bytes = buf_sz;
+		++mregions;
+	}
+
+	rc = apr_send_pkt(a->adev, (uint32_t *) mmap_region_cmd);
+	if (rc < 0)
+		goto fail_cmd;
+
+	rc = wait_event_timeout(a->mem_wait, (a->mem_state >= 0),
+				5 * HZ);
+	if (!rc) {
+		dev_err(ac->dev, "timeout. waited for memory_map\n");
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (a->mem_state > 0) {
+		rc = adsp_err_get_lnx_err_code(a->mem_state);
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	kfree(mmap_region_cmd);
+	return rc;
+}
+
+/**
+ * q6asm_map_memory_regions() - map memory regions in the dsp.
+ *
+ * @dir: direction of audio stream
+ * @ac: audio client instanace
+ * @phys: physcial address that needs mapping.
+ * @period_sz: audio period size
+ * @periods: number of periods
+ *
+ * Return: Will be an negative value on failure or zero on success
+ */
+int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac,
+			     dma_addr_t phys,
+			     unsigned int period_sz, unsigned int periods)
+{
+	struct audio_buffer *buf;
+	int cnt;
+	int rc;
+
+	if (ac->port[dir].buf) {
+		dev_err(ac->dev, "Buffer already allocated\n");
+		return 0;
+	}
+
+	mutex_lock(&ac->cmd_lock);
+
+	buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_KERNEL);
+	if (!buf) {
+		mutex_unlock(&ac->cmd_lock);
+		return -ENOMEM;
+	}
+
+
+	ac->port[dir].buf = buf;
+
+	buf[0].phys = phys;
+	buf[0].used = dir ^ 1;
+	buf[0].size = period_sz;
+	cnt = 1;
+	while (cnt < periods) {
+		if (period_sz > 0) {
+			buf[cnt].phys = buf[0].phys + (cnt * period_sz);
+			buf[cnt].used = dir ^ 1;
+			buf[cnt].size = period_sz;
+		}
+		cnt++;
+	}
+
+	ac->port[dir].max_buf_cnt = periods;
+	mutex_unlock(&ac->cmd_lock);
+
+	rc = __q6asm_memory_map_regions(ac, dir, period_sz, periods, 1);
+	if (rc < 0) {
+		dev_err(ac->dev,
+			"CMD Memory_map_regions failed %d for size %d\n", rc,
+			period_sz);
+
+
+		ac->port[dir].max_buf_cnt = 0;
+		kfree(buf);
+		ac->port[dir].buf = NULL;
+
+		return rc;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(q6asm_map_memory_regions);
+
 /**
  * q6asm_audio_client_free() - Freee allocated audio client
  *
@@ -117,8 +410,10 @@ static struct audio_client *q6asm_get_audio_client(struct q6asm *a,
 
 static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *data)
 {
-	struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
+	struct q6asm *a, *q6asm = dev_get_drvdata(&adev->dev);
 	struct audio_client *ac = NULL;
+	struct audio_port_data *port;
+	uint32_t dir = 0;
 	uint32_t sid = 0;
 	uint32_t *payload;
 
@@ -135,6 +430,52 @@ static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *
 		return 0;
 	}
 
+	a = dev_get_drvdata(ac->dev->parent);
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		switch (payload[0]) {
+		case ASM_CMD_SHARED_MEM_MAP_REGIONS:
+		case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:
+			if (payload[1] != 0) {
+				dev_err(ac->dev,
+					"cmd = 0x%x returned error = 0x%x sid:%d\n",
+					payload[0], payload[1], sid);
+				a->mem_state = payload[1];
+			} else {
+				a->mem_state = 0;
+			}
+
+			wake_up(&a->mem_wait);
+			break;
+		default:
+			dev_err(&adev->dev, "command[0x%x] not expecting rsp\n",
+				 payload[0]);
+			break;
+		}
+		return 0;
+	}
+
+	dir = (data->token & 0x0F);
+	port = &ac->port[dir];
+
+	switch (data->opcode) {
+	case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS:{
+			a->mem_state = 0;
+			ac->port[dir].mem_map_handle = payload[0];
+			wake_up(&a->mem_wait);
+			break;
+		}
+	case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:{
+			a->mem_state = 0;
+			ac->port[dir].mem_map_handle = 0;
+			wake_up(&a->mem_wait);
+
+			break;
+		}
+	default:
+		dev_dbg(&adev->dev, "command[0x%x]success [0x%x]\n",
+			payload[0], payload[1]);
+		break;
+	}
 	if (ac->cb)
 		ac->cb(data->opcode, data->token, data->payload, ac->priv);
 	return 0;
diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h
index 7a8a9039fd89..e1409c368600 100644
--- a/sound/soc/qcom/qdsp6/q6asm.h
+++ b/sound/soc/qcom/qdsp6/q6asm.h
@@ -11,4 +11,9 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev,
 					      app_cb cb, void *priv);
 void q6asm_audio_client_free(struct audio_client *ac);
 int q6asm_get_session_id(struct audio_client *ac);
+int q6asm_map_memory_regions(unsigned int dir,
+			     struct audio_client *ac,
+			     dma_addr_t phys,
+			     unsigned int bufsz, unsigned int bufcnt);
+int q6asm_unmap_memory_regions(unsigned int dir, struct audio_client *ac);
 #endif /* __Q6_ASM_H__ */
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 06/15] ASoC: qcom: qdsp6: Add support to Q6ASM
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds basic support to Q6 ASM (Audio Stream Manager) module on
Q6DSP. ASM supports up to 8 concurrent streams. each stream can be setup
as playback/capture. ASM provides top control functions like
Pause/flush/resume for playback and record. ASM can Create/destroy encoder,
decoder and also provides POPP dynamic services.

This patch adds support to basic features to allow hdmi playback.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/Kconfig        |   5 +
 sound/soc/qcom/qdsp6/Makefile |   1 +
 sound/soc/qcom/qdsp6/q6asm.c  | 250 ++++++++++++++++++++++++++++++++++++++++++
 sound/soc/qcom/qdsp6/q6asm.h  |  14 +++
 4 files changed, 270 insertions(+)
 create mode 100644 sound/soc/qcom/qdsp6/q6asm.c
 create mode 100644 sound/soc/qcom/qdsp6/q6asm.h

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index a307880dc992..7ebdb879a8a3 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -52,10 +52,15 @@ config SND_SOC_QDSP6_ADM
 	tristate
 	default n
 
+config SND_SOC_QDSP6_ASM
+	tristate
+	default n
+
 config SND_SOC_QDSP6
 	tristate "SoC ALSA audio driver for QDSP6"
 	select SND_SOC_QDSP6_AFE
 	select SND_SOC_QDSP6_ADM
+	select SND_SOC_QDSP6_ASM
 	help
 	 To add support for MSM QDSP6 Soc Audio.
 	 This will enable sound soc platform specific
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index 052813ea7062..49dd3ccab27b 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o
 obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
+obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o
diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c
new file mode 100644
index 000000000000..9cc583afef4d
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6asm.c
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: GPL-2.0
+* Copyright (c) 2011-2016, The Linux Foundation
+* Copyright (c) 2017, Linaro Limited
+*/
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/module.h>
+#include <linux/soc/qcom/apr.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include "q6asm.h"
+#include "common.h"
+
+#define TUN_READ_IO_MODE		0x0004	/* tunnel read write mode */
+#define SYNC_IO_MODE			0x0001
+#define ASYNC_IO_MODE			0x0002
+
+struct audio_client {
+	int session;
+	app_cb cb;
+	int cmd_state;
+	void *priv;
+	uint32_t io_mode;
+	uint64_t time_stamp;
+	struct apr_device *adev;
+	struct mutex cmd_lock;
+	wait_queue_head_t cmd_wait;
+	int perf_mode;
+	int stream_id;
+	struct device *dev;
+};
+
+struct q6asm {
+	struct apr_device *adev;
+	int mem_state;
+	struct device *dev;
+	wait_queue_head_t mem_wait;
+	struct mutex	session_lock;
+	struct platform_device *pcmdev;
+	struct audio_client *session[MAX_SESSIONS + 1];
+};
+
+static int q6asm_session_alloc(struct audio_client *ac, struct q6asm *a)
+{
+	int n = -EINVAL;
+
+	mutex_lock(&a->session_lock);
+	for (n = 1; n <= MAX_SESSIONS; n++) {
+		if (!a->session[n]) {
+			a->session[n] = ac;
+			break;
+		}
+	}
+	mutex_unlock(&a->session_lock);
+
+	return n;
+}
+
+static bool q6asm_is_valid_audio_client(struct audio_client *ac)
+{
+	struct q6asm *a = dev_get_drvdata(ac->dev->parent);
+	int n;
+
+	for (n = 1; n <= MAX_SESSIONS; n++) {
+		if (a->session[n] == ac)
+			return 1;
+	}
+
+	return 0;
+}
+
+static void q6asm_session_free(struct audio_client *ac)
+{
+	struct q6asm *a = dev_get_drvdata(ac->dev->parent);
+
+	if (!a)
+		return;
+
+	mutex_lock(&a->session_lock);
+	a->session[ac->session] = 0;
+	ac->session = 0;
+	ac->perf_mode = LEGACY_PCM_MODE;
+	mutex_unlock(&a->session_lock);
+}
+
+/**
+ * q6asm_audio_client_free() - Freee allocated audio client
+ *
+ * @ac: audio client to free
+ */
+void q6asm_audio_client_free(struct audio_client *ac)
+{
+	q6asm_session_free(ac);
+	kfree(ac);
+}
+EXPORT_SYMBOL_GPL(q6asm_audio_client_free);
+
+static struct audio_client *q6asm_get_audio_client(struct q6asm *a,
+						   int session_id)
+{
+	if ((session_id <= 0) || (session_id > MAX_SESSIONS)) {
+		dev_err(a->dev, "invalid session: %d\n", session_id);
+		goto err;
+	}
+
+	if (!a->session[session_id]) {
+		dev_err(a->dev, "session not active: %d\n", session_id);
+		goto err;
+	}
+	return a->session[session_id];
+err:
+	return NULL;
+}
+
+static int q6asm_srvc_callback(struct apr_device *adev, struct apr_client_data *data)
+{
+	struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
+	struct audio_client *ac = NULL;
+	uint32_t sid = 0;
+	uint32_t *payload;
+
+	if (!data) {
+		dev_err(&adev->dev, "%s: Invalid CB\n", __func__);
+		return 0;
+	}
+
+	payload = data->payload;
+	sid = (data->token >> 8) & 0x0F;
+	ac = q6asm_get_audio_client(q6asm, sid);
+	if (!ac) {
+		dev_err(&adev->dev, "Audio Client not active\n");
+		return 0;
+	}
+
+	if (ac->cb)
+		ac->cb(data->opcode, data->token, data->payload, ac->priv);
+	return 0;
+}
+
+/**
+ * q6asm_get_session_id() - get session id for audio client
+ *
+ * @ac: audio client pointer
+ *
+ * Return: Will be an session id of the audio client.
+ */
+int q6asm_get_session_id(struct audio_client *c)
+{
+	return c->session;
+}
+EXPORT_SYMBOL_GPL(q6asm_get_session_id);
+
+/**
+ * q6asm_audio_client_alloc() - Allocate a new audio client
+ *
+ * @dev: Pointer to asm child device.
+ * @cb: event callback.
+ * @priv: private data associated with this client.
+ *
+ * Return: Will be an error pointer on error or a valid audio client
+ * on success.
+ */
+struct audio_client *q6asm_audio_client_alloc(struct device *dev,
+					      app_cb cb, void *priv)
+{
+	struct q6asm *a = dev_get_drvdata(dev->parent);
+	struct audio_client *ac;
+	int n;
+
+	ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL);
+	if (!ac)
+		return NULL;
+
+	n = q6asm_session_alloc(ac, a);
+	if (n <= 0) {
+		dev_err(dev, "ASM Session alloc fail n=%d\n", n);
+		kfree(ac);
+		return NULL;
+	}
+
+	ac->session = n;
+	ac->cb = cb;
+	ac->dev = dev;
+	ac->priv = priv;
+	ac->io_mode = SYNC_IO_MODE;
+	ac->perf_mode = LEGACY_PCM_MODE;
+	/* DSP expects stream id from 1 */
+	ac->stream_id = 1;
+	ac->adev = a->adev;
+
+	init_waitqueue_head(&ac->cmd_wait);
+	mutex_init(&ac->cmd_lock);
+	ac->cmd_state = 0;
+
+	return ac;
+}
+EXPORT_SYMBOL_GPL(q6asm_audio_client_alloc);
+
+
+static int q6asm_probe(struct apr_device *adev)
+{
+	struct q6asm *q6asm;
+
+	q6asm = devm_kzalloc(&adev->dev, sizeof(*q6asm), GFP_KERNEL);
+	if (!q6asm)
+		return -ENOMEM;
+
+	q6asm->dev = &adev->dev;
+	q6asm->adev = adev;
+	q6asm->mem_state = 0;
+	init_waitqueue_head(&q6asm->mem_wait);
+	mutex_init(&q6asm->session_lock);
+	dev_set_drvdata(&adev->dev, q6asm);
+
+	q6asm->pcmdev = platform_device_register_data(&adev->dev,
+						      "q6asm_dai", -1, NULL, 0);
+
+	return 0;
+}
+
+static int q6asm_remove(struct apr_device *adev)
+{
+	struct q6asm *q6asm = dev_get_drvdata(&adev->dev);
+
+	platform_device_unregister(q6asm->pcmdev);
+
+	return 0;
+}
+
+static const struct apr_device_id q6asm_id[] = {
+	{"Q6ASM", APR_DOMAIN_ADSP, APR_SVC_ASM, APR_CLIENT_AUDIO},
+	{}
+};
+
+static struct apr_driver qcom_q6asm_driver = {
+	.probe = q6asm_probe,
+	.remove = q6asm_remove,
+	.callback = q6asm_srvc_callback,
+	.id_table = q6asm_id,
+	.driver = {
+		   .name = "qcom-q6asm",
+		   },
+};
+
+module_apr_driver(qcom_q6asm_driver);
+MODULE_DESCRIPTION("Q6 Audio Stream Manager driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h
new file mode 100644
index 000000000000..7a8a9039fd89
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6asm.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __Q6_ASM_H__
+#define __Q6_ASM_H__
+
+#define MAX_SESSIONS	16
+
+typedef void (*app_cb) (uint32_t opcode, uint32_t token,
+			uint32_t *payload, void *priv);
+struct audio_client;
+struct audio_client *q6asm_audio_client_alloc(struct device *dev,
+					      app_cb cb, void *priv);
+void q6asm_audio_client_free(struct audio_client *ac);
+int q6asm_get_session_id(struct audio_client *ac);
+#endif /* __Q6_ASM_H__ */
-- 
2.15.0

^ permalink raw reply related

* [RESEND PATCH v2 05/15] ASoC: qcom: qdsp6: Add support to Q6ADM
From: srinivas.kandagatla at linaro.org @ 2017-12-14 17:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171214173402.19074-1-srinivas.kandagatla@linaro.org>

From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>

This patch adds support to q6 ADM (Audio Device Manager) module in
q6dsp. ADM performs routing between audio streams and AFE ports.
It does Rate matching for streams going to devices driven by
different clocks, it handles volume ramping, Mixing with channel
and bit-width. ADM creates and destroys dynamic COPP services
for device-related audio processing as needed.

This patch adds basic support to ADM.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 sound/soc/qcom/Kconfig        |   5 +
 sound/soc/qcom/qdsp6/Makefile |   1 +
 sound/soc/qcom/qdsp6/q6adm.c  | 602 ++++++++++++++++++++++++++++++++++++++++++
 sound/soc/qcom/qdsp6/q6adm.h  |  24 ++
 4 files changed, 632 insertions(+)
 create mode 100644 sound/soc/qcom/qdsp6/q6adm.c
 create mode 100644 sound/soc/qcom/qdsp6/q6adm.h

diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index 1db92069a6a0..a307880dc992 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -48,9 +48,14 @@ config SND_SOC_QDSP6_AFE
 	tristate
 	default n
 
+config SND_SOC_QDSP6_ADM
+	tristate
+	default n
+
 config SND_SOC_QDSP6
 	tristate "SoC ALSA audio driver for QDSP6"
 	select SND_SOC_QDSP6_AFE
+	select SND_SOC_QDSP6_ADM
 	help
 	 To add support for MSM QDSP6 Soc Audio.
 	 This will enable sound soc platform specific
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index 313e65f571db..052813ea7062 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o
+obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o
diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c
new file mode 100644
index 000000000000..b9f79a198ea4
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6adm.c
@@ -0,0 +1,602 @@
+/* SPDX-License-Identifier: GPL-2.0
+* Copyright (c) 2011-2016, The Linux Foundation
+* Copyright (c) 2017, Linaro Limited
+*/
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/wait.h>
+#include <linux/soc/qcom/apr.h>
+#include <linux/platform_device.h>
+#include <sound/asound.h>
+#include "q6adm.h"
+#include "q6afe.h"
+#include "common.h"
+
+#define ADM_CMD_DEVICE_OPEN_V5		0x00010326
+#define ADM_CMDRSP_DEVICE_OPEN_V5	0x00010329
+#define ADM_CMD_DEVICE_CLOSE_V5		0x00010327
+#define ADM_CMD_MATRIX_MAP_ROUTINGS_V5	0x00010325
+
+#define TIMEOUT_MS 1000
+#define RESET_COPP_ID 99
+#define INVALID_COPP_ID 0xFF
+/* Definition for a legacy device session. */
+#define ADM_LEGACY_DEVICE_SESSION	0
+#define ADM_MATRIX_ID_AUDIO_RX		0
+
+struct copp {
+	int afe_port;
+	int copp_idx;
+	int id;
+	int cnt;
+	int topology;
+	int mode;
+	int stat;
+	int rate;
+	int bit_width;
+	int channels;
+	int app_type;
+	int acdb_id;
+	wait_queue_head_t wait;
+	struct list_head node;
+	struct q6adm *adm;
+};
+
+struct q6adm {
+	struct apr_device *apr;
+	struct device *dev;
+	unsigned long copp_bitmap[AFE_MAX_PORTS];
+	struct list_head copps_list;
+	spinlock_t copps_list_lock;
+	int matrix_map_stat;
+	struct platform_device *routing_dev;
+
+	wait_queue_head_t matrix_map_wait;
+};
+
+static struct copp *adm_find_copp(struct q6adm *adm, int port_idx, int copp_idx)
+{
+	struct copp *c;
+
+	spin_lock(&adm->copps_list_lock);
+	list_for_each_entry(c, &adm->copps_list, node) {
+		if ((port_idx == c->afe_port) && (copp_idx == c->copp_idx)) {
+			spin_unlock(&adm->copps_list_lock);
+			return c;
+		}
+	}
+
+	spin_unlock(&adm->copps_list_lock);
+	return NULL;
+
+}
+
+static struct copp *adm_find_matching_copp(struct q6adm *adm,
+					   int port_idx, int topology,
+					   int mode, int rate,
+					   int bit_width, int app_type)
+{
+	struct copp *c;
+
+	spin_lock(&adm->copps_list_lock);
+
+	list_for_each_entry(c, &adm->copps_list, node) {
+		if ((port_idx == c->afe_port) && (topology == c->topology) &&
+		    (mode == c->mode) && (rate == c->rate) &&
+		    (bit_width == c->bit_width) && (app_type == c->app_type)) {
+			spin_unlock(&adm->copps_list_lock);
+			return c;
+		}
+	}
+	spin_unlock(&adm->copps_list_lock);
+
+	return NULL;
+
+}
+
+static int adm_callback(struct apr_device *adev, struct apr_client_data *data)
+{
+	uint32_t *payload;
+	int port_idx, copp_idx;
+	struct copp *copp;
+	struct q6adm *adm = dev_get_drvdata(&adev->dev);
+
+	payload = data->payload;
+
+	if (data->payload_size) {
+		copp_idx = (data->token) & 0XFF;
+		port_idx = ((data->token) >> 16) & 0xFF;
+		if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+			dev_err(&adev->dev, "Invalid port idx %d token %d\n",
+			       port_idx, data->token);
+			return 0;
+		}
+		if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+			dev_err(&adev->dev, "Invalid copp idx %d token %d\n",
+				copp_idx, data->token);
+			return 0;
+		}
+
+		if (data->opcode == APR_BASIC_RSP_RESULT) {
+			if (payload[1] != 0) {
+				dev_err(&adev->dev, "cmd = 0x%x returned error = 0x%x\n",
+					payload[0], payload[1]);
+			}
+			switch (payload[0]) {
+			case ADM_CMD_DEVICE_OPEN_V5:
+			case ADM_CMD_DEVICE_CLOSE_V5:
+				copp = adm_find_copp(adm, port_idx, copp_idx);
+				if (IS_ERR_OR_NULL(copp))
+					return 0;
+
+				copp->stat = payload[1];
+				wake_up(&copp->wait);
+				break;
+			case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
+				adm->matrix_map_stat = payload[1];
+				wake_up(&adm->matrix_map_wait);
+				break;
+
+			default:
+				dev_err(&adev->dev, "Unknown Cmd: 0x%x\n",
+					payload[0]);
+				break;
+			}
+			return 0;
+		}
+
+		switch (data->opcode) {
+		case ADM_CMDRSP_DEVICE_OPEN_V5:{
+				struct adm_cmd_rsp_device_open_v5 {
+					u32 status;
+					u16 copp_id;
+					u16 reserved;
+				} __packed * open = data->payload;
+
+				open = data->payload;
+				copp = adm_find_copp(adm, port_idx, copp_idx);
+				if (IS_ERR_OR_NULL(copp))
+					return 0;
+
+				if (open->copp_id == INVALID_COPP_ID) {
+					dev_err(&adev->dev, "Invalid coppid rxed %d\n",
+						open->copp_id);
+					copp->stat = ADSP_EBADPARAM;
+					wake_up(&copp->wait);
+					break;
+				}
+				copp->stat = payload[0];
+				copp->id = open->copp_id;
+				pr_debug("%s: coppid rxed=%d\n", __func__,
+					 open->copp_id);
+				wake_up(&copp->wait);
+
+			}
+			break;
+		default:
+			dev_err(&adev->dev, "Unknown cmd:0x%x\n",
+			       data->opcode);
+			break;
+		}
+	}
+	return 0;
+}
+
+static struct copp *adm_alloc_copp(struct q6adm *adm, int port_idx)
+{
+	struct copp *c;
+	int idx;
+
+	idx = find_first_zero_bit(&adm->copp_bitmap[port_idx],
+				  MAX_COPPS_PER_PORT);
+
+	if (idx > MAX_COPPS_PER_PORT)
+		return ERR_PTR(-EBUSY);
+
+	set_bit(idx, &adm->copp_bitmap[port_idx]);
+
+	c = devm_kzalloc(adm->dev, sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return ERR_PTR(-ENOMEM);
+	c->copp_idx = idx;
+	c->afe_port = port_idx;
+	c->adm = adm;
+
+	init_waitqueue_head(&c->wait);
+
+	spin_lock(&adm->copps_list_lock);
+	list_add_tail(&c->node, &adm->copps_list);
+	spin_unlock(&adm->copps_list_lock);
+
+	return c;
+}
+
+static void adm_free_copp(struct q6adm *adm, struct copp *c, int port_idx)
+{
+	clear_bit(c->copp_idx, &adm->copp_bitmap[port_idx]);
+	spin_lock(&adm->copps_list_lock);
+	list_del(&c->node);
+	spin_unlock(&adm->copps_list_lock);
+}
+/**
+ * q6adm_open() - open adm to get hold of free copp
+ *
+ * @dev: Pointer to adm child device.
+ * @port_id: port id
+ * @path: playback or capture path.
+ * @rate: rate at which copp is required.
+ * @channel_mode: channel mode
+ * @topology: adm topology id
+ * @perf_mode: performace mode.
+ * @bit_width: audio sample bit width
+ * @app_type: Application type.
+ * @acdb_id: ACDB id
+ *
+ * Return: Will be an negative on error or a valid copp index on success.
+ */
+int q6adm_open(struct device *dev, int port_id, int path, int rate,
+	       int channel_mode, int topology, int perf_mode,
+	       uint16_t bit_width, int app_type, int acdb_id)
+{
+	struct adm_cmd_device_open_v5 {
+		struct apr_hdr hdr;
+		u16 flags;
+		u16 mode_of_operation;
+		u16 endpoint_id_1;
+		u16 endpoint_id_2;
+		u32 topology_id;
+		u16 dev_num_channel;
+		u16 bit_width;
+		u32 sample_rate;
+		u8 dev_channel_mapping[8];
+	} __packed open;
+	int ret = 0;
+	int port_idx, flags;
+	int tmp_port = q6afe_get_port_id(port_id);
+	struct copp *copp;
+	struct q6adm *adm = dev_get_drvdata(dev->parent);
+
+	port_idx = port_id;
+	if (port_idx < 0) {
+		dev_err(dev, "Invalid port_id 0x%x\n", port_id);
+		return -EINVAL;
+	}
+
+	flags = ADM_LEGACY_DEVICE_SESSION;
+	copp = adm_find_matching_copp(adm, port_idx, topology, perf_mode,
+				      rate, bit_width, app_type);
+
+	if (!copp) {
+		copp = adm_alloc_copp(adm, port_idx);
+		if (IS_ERR_OR_NULL(copp))
+			return PTR_ERR(copp);
+
+		copp->cnt = 0;
+		copp->topology = topology;
+		copp->mode = perf_mode;
+		copp->rate = rate;
+		copp->channels = channel_mode;
+		copp->bit_width = bit_width;
+		copp->app_type = app_type;
+	}
+
+	/* Create a COPP if port id are not enabled */
+	if (copp->cnt == 0) {
+
+		open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						   APR_HDR_LEN(APR_HDR_SIZE),
+						   APR_PKT_VER);
+		open.hdr.pkt_size = sizeof(open);
+		open.hdr.src_svc = APR_SVC_ADM;
+		open.hdr.src_domain = APR_DOMAIN_APPS;
+		open.hdr.src_port = tmp_port;
+		open.hdr.dest_svc = APR_SVC_ADM;
+		open.hdr.dest_domain = APR_DOMAIN_ADSP;
+		open.hdr.dest_port = tmp_port;
+		open.hdr.token = port_idx << 16 | copp->copp_idx;
+		open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
+		open.flags = flags;
+		open.mode_of_operation = path;
+		open.endpoint_id_1 = tmp_port;
+		open.topology_id = topology;
+		open.dev_num_channel = channel_mode & 0x00FF;
+		open.bit_width = bit_width;
+		open.sample_rate = rate;
+
+		ret = q6dsp_map_channels(&open.dev_channel_mapping[0],
+					 channel_mode);
+
+		if (ret)
+			return ret;
+
+		copp->stat = -1;
+		ret = apr_send_pkt(adm->apr, (uint32_t *)&open);
+		if (ret < 0) {
+			dev_err(dev, "port_id: 0x%x for[0x%x] failed %d\n",
+				tmp_port, port_id, ret);
+			return -EINVAL;
+		}
+		/* Wait for the callback with copp id */
+		ret =
+		    wait_event_timeout(copp->wait, copp->stat >= 0,
+				       msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			dev_err(dev, "ADM timedout port_id: 0x%x for [0x%x]\n",
+			       tmp_port, port_id);
+			return -EINVAL;
+		} else if (copp->stat > 0) {
+			dev_err(dev, "DSP returned error[%s]\n",
+				adsp_err_get_err_str(copp->stat));
+			return adsp_err_get_lnx_err_code(copp->stat);
+		}
+	}
+	copp->cnt++;
+	return copp->copp_idx;
+}
+EXPORT_SYMBOL_GPL(q6adm_open);
+/**
+ * q6adm_matrix_map() - Map asm streams and afe ports using payload
+ *
+ * @dev: Pointer to adm child device.
+ * @path: playback or capture path.
+ * @payload_map: map between session id and afe ports.
+ * @perf_mode: Performace mode.
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int q6adm_matrix_map(struct device *dev, int path,
+		     struct route_payload payload_map, int perf_mode)
+{
+	struct adm_cmd_matrix_map_routings_v5 {
+		struct apr_hdr hdr;
+		u32 matrix_id;
+		u32 num_sessions;
+	} __packed * route;
+
+	struct adm_session_map_node_v5 {
+		u16 session_id;
+		u16 num_copps;
+	} __packed * node;
+	struct q6adm *adm = dev_get_drvdata(dev->parent);
+	uint16_t *copps_list;
+	int cmd_size = 0;
+	int ret = 0, i = 0;
+	void *payload = NULL;
+	void *matrix_map = NULL;
+	int port_idx, copp_idx;
+	struct copp *copp;
+
+	/* Assumes port_ids have already been validated during adm_open */
+	cmd_size = (sizeof(*route) +
+		    sizeof(*node) +
+		    (sizeof(uint32_t) * payload_map.num_copps));
+	matrix_map = kzalloc(cmd_size, GFP_KERNEL);
+	if (!matrix_map)
+		return -ENOMEM;
+
+	route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map;
+	route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	route->hdr.pkt_size = cmd_size;
+	route->hdr.src_svc = 0;
+	route->hdr.src_domain = APR_DOMAIN_APPS;
+	route->hdr.src_port = 0; /* Ignored */
+	route->hdr.dest_svc = APR_SVC_ADM;
+	route->hdr.dest_domain = APR_DOMAIN_ADSP;
+	route->hdr.dest_port = 0; /* Ignored */
+	route->hdr.token = 0;
+	route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
+	route->num_sessions = 1;
+
+	switch (path) {
+	case ADM_PATH_PLAYBACK:
+		route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
+		break;
+	default:
+		dev_err(dev, "Wrong path set[%d]\n", path);
+
+		break;
+	}
+
+	payload = ((u8 *) matrix_map + sizeof(*route));
+	node = (struct adm_session_map_node_v5 *)payload;
+
+	node->session_id = payload_map.session_id;
+	node->num_copps = payload_map.num_copps;
+	payload = (u8 *) node + sizeof(*node);
+	copps_list = (uint16_t *) payload;
+
+	for (i = 0; i < payload_map.num_copps; i++) {
+		port_idx = payload_map.port_id[i];
+		if (port_idx < 0) {
+			dev_err(dev, "Invalid port_id 0x%x\n",
+				payload_map.port_id[i]);
+			return -EINVAL;
+		}
+		copp_idx = payload_map.copp_idx[i];
+
+		copp = adm_find_copp(adm, port_idx, copp_idx);
+		if (IS_ERR_OR_NULL(copp))
+			return -EINVAL;
+
+		copps_list[i] = copp->id;
+	}
+
+	adm->matrix_map_stat = -1;
+
+	ret = apr_send_pkt(adm->apr, (uint32_t *) matrix_map);
+	if (ret < 0) {
+		dev_err(dev, "routing for syream %d failed ret %d\n",
+		       payload_map.session_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(adm->matrix_map_wait,
+				 adm->matrix_map_stat >= 0,
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		dev_err(dev, "routing for syream %d failed\n",
+		       payload_map.session_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	} else if (adm->matrix_map_stat > 0) {
+		dev_err(dev, "DSP returned error[%s]\n",
+		       adsp_err_get_err_str(adm->matrix_map_stat));
+		ret = adsp_err_get_lnx_err_code(adm->matrix_map_stat);
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	kfree(matrix_map);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(q6adm_matrix_map);
+
+static void adm_reset_copp(struct copp *c)
+{
+	c->id = RESET_COPP_ID;
+	c->cnt = 0;
+	c->topology = 0;
+	c->mode = 0;
+	c->stat = -1;
+	c->rate = 0;
+	c->channels = 0;
+	c->bit_width = 0;
+	c->app_type = 0;
+}
+/**
+ * q6adm_close() - Close adm copp
+ *
+ * @dev: Pointer to adm child device.
+ * @port_id: afe port id.
+ * @perf_mode: perf_mode mode
+ * @copp_idx: copp index to close
+ *
+ * Return: Will be an negative on error or a zero on success.
+ */
+int q6adm_close(struct device *dev, int port_id, int perf_mode, int copp_idx)
+{
+	struct apr_hdr close;
+	struct copp *copp;
+
+	int ret = 0, port_idx;
+	int copp_id = RESET_COPP_ID;
+	struct q6adm *adm = dev_get_drvdata(dev->parent);
+
+	port_idx = port_id;
+	if (port_idx < 0) {
+		dev_err(dev, "Invalid port_id 0x%x\n", port_id);
+		return -EINVAL;
+	}
+
+	if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+		dev_err(dev, "Invalid copp idx: %d\n", copp_idx);
+		return -EINVAL;
+	}
+
+	copp = adm_find_copp(adm, port_id, copp_idx);
+	if (IS_ERR_OR_NULL(copp))
+		return -EINVAL;
+
+	copp->cnt--;
+	if (!copp->cnt) {
+		copp_id = copp->id;
+
+		close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+		close.pkt_size = sizeof(close);
+		close.src_svc = APR_SVC_ADM;
+		close.src_domain = APR_DOMAIN_APPS;
+		close.src_port = port_id;
+		close.dest_svc = APR_SVC_ADM;
+		close.dest_domain = APR_DOMAIN_ADSP;
+		close.dest_port = copp_id;
+		close.token = port_idx << 16 | copp_idx;
+		close.opcode = ADM_CMD_DEVICE_CLOSE_V5;
+
+		ret = apr_send_pkt(adm->apr, (uint32_t *) &close);
+		if (ret < 0) {
+			dev_err(dev, "ADM close failed %d\n", ret);
+			return -EINVAL;
+		}
+
+		ret = wait_event_timeout(copp->wait, copp->stat >= 0,
+					 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			dev_err(dev, "ADM cmd Route timedout for port 0x%x\n",
+				port_id);
+			return -EINVAL;
+		} else if (copp->stat > 0) {
+			dev_err(dev, "DSP returned error[%s]\n",
+				adsp_err_get_err_str(copp->stat));
+			return adsp_err_get_lnx_err_code(copp->stat);
+		}
+
+		adm_reset_copp(copp);
+		adm_free_copp(adm, copp, port_id);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(q6adm_close);
+
+static int q6adm_probe(struct apr_device *adev)
+{
+	struct q6adm *adm;
+
+	adm = devm_kzalloc(&adev->dev, sizeof(*adm), GFP_KERNEL);
+	if (!adm)
+		return -ENOMEM;
+
+	adm->apr = adev;
+	dev_set_drvdata(&adev->dev, adm);
+	adm->dev = &adev->dev;
+	adm->matrix_map_stat = 0;
+	init_waitqueue_head(&adm->matrix_map_wait);
+
+	INIT_LIST_HEAD(&adm->copps_list);
+	spin_lock_init(&adm->copps_list_lock);
+
+	adm->routing_dev = platform_device_register_data(&adev->dev,
+							   "q6routing",
+							   -1, NULL, 0);
+
+
+	return 0;
+}
+
+static int q6adm_exit(struct apr_device *adev)
+{
+	struct q6adm *adm = dev_get_drvdata(&adev->dev);
+
+	platform_device_unregister(adm->routing_dev);
+
+	return 0;
+}
+
+static const struct apr_device_id adm_id[] = {
+	{"Q6ADM", APR_DOMAIN_ADSP, APR_SVC_ADM, APR_CLIENT_AUDIO},
+	{}
+};
+
+static struct apr_driver qcom_q6adm_driver = {
+	.probe = q6adm_probe,
+	.remove = q6adm_exit,
+	.callback = adm_callback,
+	.id_table = adm_id,
+	.driver = {
+		   .name = "qcom-q6adm",
+	   },
+};
+
+module_apr_driver(qcom_q6adm_driver);
+MODULE_DESCRIPTION("Q6 Audio Device Manager");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/qdsp6/q6adm.h b/sound/soc/qcom/qdsp6/q6adm.h
new file mode 100644
index 000000000000..aa7b3ba4360b
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6adm.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __Q6_ADM_V2_H__
+#define __Q6_ADM_V2_H__
+
+#define ADM_PATH_PLAYBACK	0x1
+#define MAX_COPPS_PER_PORT	8
+#define NULL_COPP_TOPOLOGY	0x00010312
+
+/* multiple copp per stream. */
+struct route_payload {
+	int num_copps;
+	int session_id;
+	int copp_idx[MAX_COPPS_PER_PORT];
+	int port_id[MAX_COPPS_PER_PORT];
+};
+
+int q6adm_open(struct device *dev, int port_id, int path, int rate,
+	       int channel_mode, int topology, int perf_mode,
+	       uint16_t bit_width, int app_type, int acdb_id);
+int q6adm_close(struct device *dev, int port, int topology, int perf_mode);
+int q6adm_matrix_map(struct device *dev, int path,
+		     struct route_payload payload_map, int perf_mode);
+
+#endif /* __Q6_ADM_V2_H__ */
-- 
2.15.0

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox