Alsa-devel
Threads by month
- ----- 2025 -----
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
February 2023
- 148 participants
- 275 discussions
The deciding factor for when a/an should be used is the sound
that begins the word which follows these indefinite articles,
rather than the letter which does. Use "an SPI" (SPI begins
with the consonant letter S, but the S is pronounced with its
letter name, "es.").
Used sed to do the replacement:
find . -type f -exec sed -i "s/ a SPI/ an SPI/g" {} \;
Signed-off-by: Tudor Ambarus <tudor.ambarus(a)linaro.org>
---
Generated on top of v6.2-rc6.
.../bindings/display/himax,hx8357d.txt | 2 +-
.../bindings/display/ilitek,ili9225.txt | 2 +-
.../bindings/display/multi-inno,mi0283qt.txt | 2 +-
.../bindings/display/panel/lg,lg4573.yaml | 2 +-
.../display/panel/samsung,lms397kf04.yaml | 2 +-
.../devicetree/bindings/display/repaper.txt | 2 +-
.../bindings/display/sitronix,st7586.txt | 2 +-
.../bindings/iio/accel/adi,adis16240.yaml | 2 +-
.../bindings/iio/accel/adi,adxl313.yaml | 2 +-
.../bindings/iio/accel/adi,adxl345.yaml | 2 +-
.../devicetree/bindings/iio/accel/lis302.txt | 2 +-
.../bindings/iio/accel/nxp,fxls8962af.yaml | 2 +-
.../bindings/iio/adc/microchip,mcp3201.yaml | 2 +-
.../bindings/input/touchscreen/ads7846.txt | 2 +-
.../devicetree/bindings/leds/leds-lp8860.txt | 2 +-
.../bindings/media/renesas,drif.yaml | 2 +-
.../memory-controllers/renesas,rpc-if.yaml | 2 +-
.../devicetree/bindings/mfd/atmel-flexcom.txt | 2 +-
.../bindings/misc/olpc,xo1.75-ec.yaml | 2 +-
.../devicetree/bindings/mmc/mmc-spi-slot.yaml | 2 +-
.../bindings/net/dsa/vitesse,vsc73xx.txt | 2 +-
.../bindings/net/microchip,enc28j60.txt | 2 +-
.../devicetree/bindings/net/wiznet,w5x00.txt | 2 +-
.../bindings/spi/brcm,spi-bcm-qspi.yaml | 4 ++--
.../bindings/spi/marvell,mmp2-ssp.yaml | 2 +-
.../bindings/spi/mxicy,mx25f0a-spi.yaml | 2 +-
.../bindings/spi/snps,dw-apb-ssi.yaml | 2 +-
.../devicetree/bindings/spi/spi-mux.yaml | 2 +-
.../bindings/spi/spi-peripheral-props.yaml | 2 +-
Documentation/driver-api/mtd/spi-nor.rst | 4 ++--
.../firmware-guide/acpi/enumeration.rst | 2 +-
.../firmware-guide/acpi/gpio-properties.rst | 2 +-
.../sound/designs/compress-offload.rst | 2 +-
Documentation/spi/spi-lm70llp.rst | 4 ++--
Documentation/spi/spidev.rst | 4 ++--
arch/arm/boot/dts/imx7d-flex-concentrator.dts | 2 +-
.../cavium-octeon/executive/cvmx-helper-spi.c | 6 +++---
arch/mips/cavium-octeon/executive/cvmx-spi.c | 16 ++++++++--------
arch/mips/include/asm/octeon/cvmx-helper-spi.h | 4 ++--
arch/mips/include/asm/octeon/cvmx-spi.h | 18 +++++++++---------
arch/mips/include/asm/octeon/cvmx-wqe.h | 2 +-
drivers/auxdisplay/lcd2s.c | 2 +-
drivers/gpu/drm/solomon/ssd130x-spi.c | 2 +-
drivers/gpu/drm/tiny/ili9486.c | 2 +-
drivers/input/misc/Kconfig | 4 ++--
drivers/input/rmi4/Kconfig | 2 +-
drivers/input/touchscreen/Kconfig | 2 +-
drivers/mfd/Kconfig | 2 +-
drivers/mfd/ocelot.h | 2 +-
drivers/mmc/host/Kconfig | 2 +-
drivers/mtd/spi-nor/controllers/Kconfig | 2 +-
drivers/mtd/spi-nor/core.c | 14 +++++++-------
drivers/mtd/spi-nor/core.h | 4 ++--
drivers/mtd/spi-nor/sfdp.c | 6 +++---
.../net/dsa/sja1105/sja1105_dynamic_config.c | 4 ++--
drivers/net/ethernet/microchip/enc28j60.c | 2 +-
.../net/wireless/microchip/wilc1000/Kconfig | 2 +-
drivers/net/wireless/st/cw1200/Kconfig | 2 +-
drivers/platform/chrome/Kconfig | 2 +-
drivers/platform/chrome/cros_ec_spi.c | 2 +-
drivers/rtc/rtc-ds1305.c | 2 +-
drivers/spi/Kconfig | 8 ++++----
drivers/spi/spi-mem.c | 10 +++++-----
drivers/spi/spi-mpc52xx-psc.c | 2 +-
drivers/spi/spi.c | 4 ++--
drivers/staging/fbtft/fbtft.h | 2 +-
drivers/staging/iio/meter/Kconfig | 2 +-
drivers/staging/octeon/ethernet-spi.c | 2 +-
drivers/usb/host/max3421-hcd.c | 2 +-
include/linux/mtd/spinand.h | 14 +++++++-------
include/linux/rmi.h | 2 +-
include/linux/spi/spi-mem.h | 18 +++++++++---------
include/linux/spi/spi.h | 8 ++++----
73 files changed, 131 insertions(+), 131 deletions(-)
diff --git a/Documentation/devicetree/bindings/display/himax,hx8357d.txt b/Documentation/devicetree/bindings/display/himax,hx8357d.txt
index e641f664763d..ed7aebbd3c69 100644
--- a/Documentation/devicetree/bindings/display/himax,hx8357d.txt
+++ b/Documentation/devicetree/bindings/display/himax,hx8357d.txt
@@ -8,7 +8,7 @@ Required properties:
- dc-gpios: D/C pin
- reg: address of the panel on the SPI bus
-The node for this driver must be a child node of a SPI controller, hence
+The node for this driver must be a child node of an SPI controller, hence
all mandatory properties described in ../spi/spi-bus.txt must be specified.
Optional properties:
diff --git a/Documentation/devicetree/bindings/display/ilitek,ili9225.txt b/Documentation/devicetree/bindings/display/ilitek,ili9225.txt
index a59feb52015b..a62c896926d3 100644
--- a/Documentation/devicetree/bindings/display/ilitek,ili9225.txt
+++ b/Documentation/devicetree/bindings/display/ilitek,ili9225.txt
@@ -8,7 +8,7 @@ Required properties:
- rs-gpios: Register select signal
- reset-gpios: Reset pin
-The node for this driver must be a child node of a SPI controller, hence
+The node for this driver must be a child node of an SPI controller, hence
all mandatory properties described in ../spi/spi-bus.txt must be specified.
Optional properties:
diff --git a/Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt b/Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt
index eed48c3d4875..3b9b3053a438 100644
--- a/Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt
+++ b/Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt
@@ -3,7 +3,7 @@ Multi-Inno MI0283QT display panel
Required properties:
- compatible: "multi-inno,mi0283qt".
-The node for this driver must be a child node of a SPI controller, hence
+The node for this driver must be a child node of an SPI controller, hence
all mandatory properties described in ../spi/spi-bus.txt must be specified.
Optional properties:
diff --git a/Documentation/devicetree/bindings/display/panel/lg,lg4573.yaml b/Documentation/devicetree/bindings/display/panel/lg,lg4573.yaml
index ee357e139ac0..39cae0e07741 100644
--- a/Documentation/devicetree/bindings/display/panel/lg,lg4573.yaml
+++ b/Documentation/devicetree/bindings/display/panel/lg,lg4573.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: LG LG4573 TFT Liquid Crystal Display with SPI control bus
description: |
- The panel must obey the rules for a SPI slave device as specified in
+ The panel must obey the rules for an SPI slave device as specified in
spi/spi-controller.yaml
maintainers:
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml b/Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml
index 5e77cee93f83..dfaf1c63f0a7 100644
--- a/Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml
+++ b/Documentation/devicetree/bindings/display/panel/samsung,lms397kf04.yaml
@@ -39,7 +39,7 @@ properties:
spi-cpol: true
spi-max-frequency:
- description: inherited as a SPI client node, the datasheet specifies
+ description: inherited as an SPI client node, the datasheet specifies
maximum 300 ns minimum cycle which gives around 3 MHz max frequency
maximum: 3000000
diff --git a/Documentation/devicetree/bindings/display/repaper.txt b/Documentation/devicetree/bindings/display/repaper.txt
index f5f9f9cf6a25..707cb50d7fc1 100644
--- a/Documentation/devicetree/bindings/display/repaper.txt
+++ b/Documentation/devicetree/bindings/display/repaper.txt
@@ -14,7 +14,7 @@ Required properties:
Required property for e2271cs021:
- border-gpios: Border control
-The node for this driver must be a child node of a SPI controller, hence
+The node for this driver must be a child node of an SPI controller, hence
all mandatory properties described in ../spi/spi-bus.txt must be specified.
Optional property:
diff --git a/Documentation/devicetree/bindings/display/sitronix,st7586.txt b/Documentation/devicetree/bindings/display/sitronix,st7586.txt
index 1d0dad1210d3..b2f287d64a37 100644
--- a/Documentation/devicetree/bindings/display/sitronix,st7586.txt
+++ b/Documentation/devicetree/bindings/display/sitronix,st7586.txt
@@ -6,7 +6,7 @@ Required properties:
the pin labeled D1 on the controller, not the pin labeled A0)
- reset-gpios: Reset pin
-The node for this driver must be a child node of a SPI controller, hence
+The node for this driver must be a child node of an SPI controller, hence
all mandatory properties described in ../spi/spi-bus.txt must be specified.
Optional properties:
diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml
index f6f97164c2ca..e60f3c3f1b27 100644
--- a/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml
+++ b/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml
@@ -43,7 +43,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- /* Example for a SPI device node */
+ /* Example for an SPI device node */
accelerometer@0 {
compatible = "adi,adis16240";
reg = <0>;
diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml
index 185b68ffb536..dc70d5b8964b 100644
--- a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml
+++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml
@@ -79,7 +79,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- /* Example for a SPI device node */
+ /* Example for an SPI device node */
accelerometer@0 {
compatible = "adi,adxl313";
reg = <0>;
diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
index 346abfb13a3a..d2214170f5ae 100644
--- a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
+++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml
@@ -68,7 +68,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- /* Example for a SPI device node */
+ /* Example for an SPI device node */
accelerometer@0 {
compatible = "adi,adxl345";
reg = <0>;
diff --git a/Documentation/devicetree/bindings/iio/accel/lis302.txt b/Documentation/devicetree/bindings/iio/accel/lis302.txt
index 764e28ec1a0a..03d8a5c6c9aa 100644
--- a/Documentation/devicetree/bindings/iio/accel/lis302.txt
+++ b/Documentation/devicetree/bindings/iio/accel/lis302.txt
@@ -62,7 +62,7 @@ Optional properties for all bus drivers:
(used by self-test)
-Example for a SPI device node:
+Example for an SPI device node:
accelerometer@0 {
compatible = "st,lis302dl-spi";
diff --git a/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml b/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml
index 65ce8ea14b52..9a8ecdc5a056 100644
--- a/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml
+++ b/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml
@@ -69,7 +69,7 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- /* Example for a SPI device node */
+ /* Example for an SPI device node */
accelerometer@0 {
compatible = "nxp,fxls8962af";
reg = <0>;
diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3201.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3201.yaml
index 18108f0f3731..5bc50d350783 100644
--- a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3201.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3201.yaml
@@ -10,7 +10,7 @@ maintainers:
- Oskar Andero <oskar.andero(a)gmail.com>
description: |
- Family of simple ADCs with a SPI interface.
+ Family of simple ADCs with an SPI interface.
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ads7846.txt b/Documentation/devicetree/bindings/input/touchscreen/ads7846.txt
index 81f6bda97d3c..3c3fd7326bdd 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/ads7846.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/ads7846.txt
@@ -1,7 +1,7 @@
Device tree bindings for TI's ADS7843, ADS7845, ADS7846, ADS7873, TSC2046
SPI driven touch screen controllers.
-The node for this driver must be a child node of a SPI controller, hence
+The node for this driver must be a child node of an SPI controller, hence
all mandatory properties described in
Documentation/devicetree/bindings/spi/spi-bus.txt
diff --git a/Documentation/devicetree/bindings/leds/leds-lp8860.txt b/Documentation/devicetree/bindings/leds/leds-lp8860.txt
index 8bb25749a3da..ea9d8bcc7f06 100644
--- a/Documentation/devicetree/bindings/leds/leds-lp8860.txt
+++ b/Documentation/devicetree/bindings/leds/leds-lp8860.txt
@@ -3,7 +3,7 @@
The LP8860-Q1 is an high-efficiency LED
driver with boost controller. It has 4 high-precision
current sinks that can be controlled by a PWM input
-signal, a SPI/I2C master, or both.
+signal, an SPI/I2C master, or both.
Required properties:
- compatible :
diff --git a/Documentation/devicetree/bindings/media/renesas,drif.yaml b/Documentation/devicetree/bindings/media/renesas,drif.yaml
index 9403b235e976..aa30115469f3 100644
--- a/Documentation/devicetree/bindings/media/renesas,drif.yaml
+++ b/Documentation/devicetree/bindings/media/renesas,drif.yaml
@@ -11,7 +11,7 @@ maintainers:
- Fabrizio Castro <fabrizio.castro.jz(a)renesas.com>
description: |
- R-Car Gen3 DRIF is a SPI like receive only slave device. A general
+ R-Car Gen3 DRIF is an SPI like receive only slave device. A general
representation of DRIF interfacing with a master device is shown below.
+---------------------+ +---------------------+
diff --git a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml
index 30a403b1b79a..3b5c56ca2daa 100644
--- a/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml
+++ b/Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml
@@ -10,7 +10,7 @@ maintainers:
- Sergei Shtylyov <sergei.shtylyov(a)gmail.com>
description: |
- Renesas RPC-IF allows a SPI flash or HyperFlash connected to the SoC to
+ Renesas RPC-IF allows an SPI flash or HyperFlash connected to the SoC to
be accessed via the external address space read mode or the manual mode.
The flash chip itself should be represented by a subnode of the RPC-IF node.
diff --git a/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt b/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt
index 9d837535637b..076aa61a01ab 100644
--- a/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt
+++ b/Documentation/devicetree/bindings/mfd/atmel-flexcom.txt
@@ -1,6 +1,6 @@
* Device tree bindings for Atmel Flexcom (Flexible Serial Communication Unit)
-The Atmel Flexcom is just a wrapper which embeds a SPI controller, an I2C
+The Atmel Flexcom is just a wrapper which embeds an SPI controller, an I2C
controller and an USART. Only one function can be used at a time and is chosen
at boot time according to the device tree.
diff --git a/Documentation/devicetree/bindings/misc/olpc,xo1.75-ec.yaml b/Documentation/devicetree/bindings/misc/olpc,xo1.75-ec.yaml
index e99342f268a6..a53087ff80d9 100644
--- a/Documentation/devicetree/bindings/misc/olpc,xo1.75-ec.yaml
+++ b/Documentation/devicetree/bindings/misc/olpc,xo1.75-ec.yaml
@@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: OLPC XO-1.75 Embedded Controller
description: |
- This binding describes the Embedded Controller acting as a SPI bus master
+ This binding describes the Embedded Controller acting as an SPI bus master
on a OLPC XO-1.75 laptop computer.
The embedded controller requires the SPI controller driver to signal
diff --git a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
index c0662ce9946d..897709a51208 100644
--- a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
+++ b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/mmc/mmc-spi-slot.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: MMC/SD/SDIO slot directly connected to a SPI bus
+title: MMC/SD/SDIO slot directly connected to an SPI bus
maintainers:
- Ulf Hansson <ulf.hansson(a)linaro.org>
diff --git a/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt b/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt
index 258bef483673..6a6f4a813620 100644
--- a/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt
+++ b/Documentation/devicetree/bindings/net/dsa/vitesse,vsc73xx.txt
@@ -14,7 +14,7 @@ Vitesse VSC7398 SparX-G8e 8-port Integrated Gigabit Ethernet Switch
This switch could have two different management interface.
If SPI interface is used, the device tree node is an SPI device so it must
-reside inside a SPI bus device tree node, see spi/spi-bus.txt
+reside inside an SPI bus device tree node, see spi/spi-bus.txt
When the chip is connected to a parallel memory bus and work in memory-mapped
I/O mode, a platform device is used to represent the vsc73xx. In this case it
diff --git a/Documentation/devicetree/bindings/net/microchip,enc28j60.txt b/Documentation/devicetree/bindings/net/microchip,enc28j60.txt
index a8275921a896..3cd4774f7de2 100644
--- a/Documentation/devicetree/bindings/net/microchip,enc28j60.txt
+++ b/Documentation/devicetree/bindings/net/microchip,enc28j60.txt
@@ -2,7 +2,7 @@
This is a standalone 10 MBit ethernet controller with SPI interface.
-For each device connected to a SPI bus, define a child node within
+For each device connected to an SPI bus, define a child node within
the SPI master node.
Required properties:
diff --git a/Documentation/devicetree/bindings/net/wiznet,w5x00.txt b/Documentation/devicetree/bindings/net/wiznet,w5x00.txt
index e9665798c4be..a9f979e2e897 100644
--- a/Documentation/devicetree/bindings/net/wiznet,w5x00.txt
+++ b/Documentation/devicetree/bindings/net/wiznet,w5x00.txt
@@ -2,7 +2,7 @@
This is a standalone 10/100 MBit Ethernet controller with SPI interface.
-For each device connected to a SPI bus, define a child node within
+For each device connected to an SPI bus, define a child node within
the SPI master node.
Required properties:
diff --git a/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.yaml b/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.yaml
index ec5873919170..6202aa959a17 100644
--- a/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.yaml
+++ b/Documentation/devicetree/bindings/spi/brcm,spi-bcm-qspi.yaml
@@ -11,10 +11,10 @@ maintainers:
- Rafał Miłecki <rafal(a)milecki.pl>
description: |
- The Broadcom SPI controller is a SPI master found on various SOCs, including
+ The Broadcom SPI controller is an SPI master found on various SOCs, including
BRCMSTB (BCM7XXX), Cygnus, NSP and NS2. The Broadcom Master SPI hw IP consits
of:
- MSPI : SPI master controller can read and write to a SPI slave device
+ MSPI : SPI master controller can read and write to an SPI slave device
BSPI : Broadcom SPI in combination with the MSPI hw IP provides acceleration
for flash reads and be configured to do single, double, quad lane
io with 3-byte and 4-byte addressing support.
diff --git a/Documentation/devicetree/bindings/spi/marvell,mmp2-ssp.yaml b/Documentation/devicetree/bindings/spi/marvell,mmp2-ssp.yaml
index 5f4f6b5615d0..c13c4c8a4e08 100644
--- a/Documentation/devicetree/bindings/spi/marvell,mmp2-ssp.yaml
+++ b/Documentation/devicetree/bindings/spi/marvell,mmp2-ssp.yaml
@@ -28,7 +28,7 @@ properties:
ready-gpios:
description: |
- GPIO used to signal a SPI master that the FIFO is filled and we're
+ GPIO used to signal an SPI master that the FIFO is filled and we're
ready to service a transfer. Only useful in slave mode.
maxItems: 1
diff --git a/Documentation/devicetree/bindings/spi/mxicy,mx25f0a-spi.yaml b/Documentation/devicetree/bindings/spi/mxicy,mx25f0a-spi.yaml
index a3aa5e07c0e4..875f609d09be 100644
--- a/Documentation/devicetree/bindings/spi/mxicy,mx25f0a-spi.yaml
+++ b/Documentation/devicetree/bindings/spi/mxicy,mx25f0a-spi.yaml
@@ -40,7 +40,7 @@ properties:
nand-ecc-engine:
description: NAND ECC engine used by the SPI controller in order to perform
- on-the-fly correction when using a SPI-NAND memory.
+ on-the-fly correction when using an SPI-NAND memory.
$ref: /schemas/types.yaml#/definitions/phandle
required:
diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
index d33b72fabc5d..b9f976d1b5bf 100644
--- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
+++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
@@ -128,7 +128,7 @@ properties:
description: |
Default value of the rx-sample-delay-ns property.
This value will be used if the property is not explicitly defined
- for a SPI slave device.
+ for an SPI slave device.
SPI Rx sample delay offset, unit is nanoseconds.
The delay from the default sample time before the actual sample of the
diff --git a/Documentation/devicetree/bindings/spi/spi-mux.yaml b/Documentation/devicetree/bindings/spi/spi-mux.yaml
index 7ea79f6d33f3..cc65f7d5e1ed 100644
--- a/Documentation/devicetree/bindings/spi/spi-mux.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-mux.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic SPI Multiplexer
description: |
- This binding describes a SPI bus multiplexer to route the SPI chip select
+ This binding describes an SPI bus multiplexer to route the SPI chip select
signals. This can be used when you need more devices than the SPI controller
has chip selects available. An example setup is shown in ASCII art; the actual
setting of the multiplexer to a channel needs to be done by a specific SPI mux
diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
index 9a60c0664bbe..62959906aac3 100644
--- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/spi/spi-peripheral-props.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Peripheral-specific properties for a SPI bus.
+title: Peripheral-specific properties for an SPI bus.
description:
Many SPI controllers need to add properties to peripheral devices. They could
diff --git a/Documentation/driver-api/mtd/spi-nor.rst b/Documentation/driver-api/mtd/spi-nor.rst
index 4a3adca417fd..bd091612dc16 100644
--- a/Documentation/driver-api/mtd/spi-nor.rst
+++ b/Documentation/driver-api/mtd/spi-nor.rst
@@ -12,7 +12,7 @@ arbitrary streams of bytes, but rather are designed specifically for SPI NOR.
In particular, Freescale's QuadSPI controller must know the NOR commands to
find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of
-opcodes, addresses, or data payloads; a SPI controller simply knows to send or
+opcodes, addresses, or data payloads; an SPI controller simply knows to send or
receive bytes (Tx and Rx). Therefore, we must define a new layering scheme under
which the controller driver is aware of the opcodes, addressing, and other
details of the SPI NOR protocol.
@@ -62,7 +62,7 @@ Part III - How can drivers use the framework?
The main API is spi_nor_scan(). Before you call the hook, a driver should
initialize the necessary fields for spi_nor{}. Please see
drivers/mtd/spi-nor/spi-nor.c for detail. Please also refer to spi-fsl-qspi.c
-when you want to write a new driver for a SPI NOR controller.
+when you want to write a new driver for an SPI NOR controller.
Another API is spi_nor_restore(), this is used to restore the status of SPI
flash chip such as addressing mode. Call it whenever detach the driver from
device or reboot the system.
diff --git a/Documentation/firmware-guide/acpi/enumeration.rst b/Documentation/firmware-guide/acpi/enumeration.rst
index b9dc0c603f36..47a5868940c3 100644
--- a/Documentation/firmware-guide/acpi/enumeration.rst
+++ b/Documentation/firmware-guide/acpi/enumeration.rst
@@ -187,7 +187,7 @@ Slave devices behind SPI bus have SpiSerialBus resource attached to them.
This is extracted automatically by the SPI core and the slave devices are
enumerated once spi_register_master() is called by the bus driver.
-Here is what the ACPI namespace for a SPI slave might look like::
+Here is what the ACPI namespace for an SPI slave might look like::
Device (EEP0)
{
diff --git a/Documentation/firmware-guide/acpi/gpio-properties.rst b/Documentation/firmware-guide/acpi/gpio-properties.rst
index eaec732cc77c..a5c865c266ac 100644
--- a/Documentation/firmware-guide/acpi/gpio-properties.rst
+++ b/Documentation/firmware-guide/acpi/gpio-properties.rst
@@ -86,7 +86,7 @@ reprograms them differently.
It is possible to leave holes in the array of GPIOs. This is useful in
cases like with SPI host controllers where some chip selects may be
-implemented as GPIOs and some as native signals. For example a SPI host
+implemented as GPIOs and some as native signals. For example an SPI host
controller can have chip selects 0 and 2 implemented as GPIOs and 1 as
native::
diff --git a/Documentation/sound/designs/compress-offload.rst b/Documentation/sound/designs/compress-offload.rst
index 935f325dbc77..bbd1c040f06a 100644
--- a/Documentation/sound/designs/compress-offload.rst
+++ b/Documentation/sound/designs/compress-offload.rst
@@ -93,7 +93,7 @@ ring buffer cannot be invalidated, except when dropping all buffers.
The Compressed Data API does not make any assumptions on how the data
is transmitted to the audio DSP. DMA transfers from main memory to an
-embedded audio cluster or to a SPI interface for external DSPs are
+embedded audio cluster or to an SPI interface for external DSPs are
possible. As in the ALSA PCM case, a core set of routines is exposed;
each driver implementer will have to write support for a set of
mandatory routines and possibly make use of optional ones.
diff --git a/Documentation/spi/spi-lm70llp.rst b/Documentation/spi/spi-lm70llp.rst
index 07631aef4343..6c8413dba420 100644
--- a/Documentation/spi/spi-lm70llp.rst
+++ b/Documentation/spi/spi-lm70llp.rst
@@ -16,10 +16,10 @@ Description
This driver provides glue code connecting a National Semiconductor LM70 LLP
temperature sensor evaluation board to the kernel's SPI core subsystem.
-This is a SPI master controller driver. It can be used in conjunction with
+This is an SPI master controller driver. It can be used in conjunction with
(layered under) the LM70 logical driver (a "SPI protocol driver").
In effect, this driver turns the parallel port interface on the eval board
-into a SPI bus with a single device, which will be driven by the generic
+into an SPI bus with a single device, which will be driven by the generic
LM70 driver (drivers/hwmon/lm70.c).
diff --git a/Documentation/spi/spidev.rst b/Documentation/spi/spidev.rst
index 369c657ba435..2108cc704b26 100644
--- a/Documentation/spi/spidev.rst
+++ b/Documentation/spi/spidev.rst
@@ -66,12 +66,12 @@ To make the spidev driver bind to such a device, use the following:
echo spidev > /sys/bus/spi/devices/spiB.C/driver_override
echo spiB.C > /sys/bus/spi/drivers/spidev/bind
-When the spidev driver is bound to a SPI device, the sysfs node for the
+When the spidev driver is bound to an SPI device, the sysfs node for the
device will include a child device node with a "dev" attribute that will
be understood by udev or mdev (udev replacement from BusyBox; it's less
featureful, but often enough).
-For a SPI device with chipselect C on bus B, you should see:
+For an SPI device with chipselect C on bus B, you should see:
/dev/spidevB.C ...
character special device, major number 153 with
diff --git a/arch/arm/boot/dts/imx7d-flex-concentrator.dts b/arch/arm/boot/dts/imx7d-flex-concentrator.dts
index bd6b5285aa8d..ac5f6faa2a3f 100644
--- a/arch/arm/boot/dts/imx7d-flex-concentrator.dts
+++ b/arch/arm/boot/dts/imx7d-flex-concentrator.dts
@@ -130,7 +130,7 @@ &ecspi4 {
* ST chip maximum SPI clock frequency is 33 MHz.
*
* TCG specification - Section 6.4.1 Clocking:
- * TPM shall support a SPI clock frequency range of 10-24 MHz.
+ * TPM shall support an SPI clock frequency range of 10-24 MHz.
*/
st33htph: tpm-tis@0 {
compatible = "st,st33htpm-spi", "tcg,tpm_tis-spi";
diff --git a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c
index 525914e9b22d..3886d460cb2a 100644
--- a/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-helper-spi.c
@@ -60,7 +60,7 @@ int __cvmx_helper_spi_enumerate(int interface)
}
/**
- * Probe a SPI interface and determine the number of ports
+ * Probe an SPI interface and determine the number of ports
* connected to it. The SPI interface should still be down after
* this call.
*
@@ -93,7 +93,7 @@ int __cvmx_helper_spi_probe(int interface)
}
/**
- * Bringup and enable a SPI interface. After this call packet I/O
+ * Bringup and enable an SPI interface. After this call packet I/O
* should be fully functional. This is called with IPD enabled but
* PKO disabled.
*
@@ -195,7 +195,7 @@ union cvmx_helper_link_info __cvmx_helper_spi_link_get(int ipd_port)
*/
int __cvmx_helper_spi_link_set(int ipd_port, union cvmx_helper_link_info link_info)
{
- /* Nothing to do. If we have a SPI4000 then the setup was already performed
+ /* Nothing to do. If we have an SPI4000 then the setup was already performed
by cvmx_spi4000_check_speed(). If not then there isn't any link
info */
return 0;
diff --git a/arch/mips/cavium-octeon/executive/cvmx-spi.c b/arch/mips/cavium-octeon/executive/cvmx-spi.c
index eb9333e84a6b..f9814a4be84f 100644
--- a/arch/mips/cavium-octeon/executive/cvmx-spi.c
+++ b/arch/mips/cavium-octeon/executive/cvmx-spi.c
@@ -92,7 +92,7 @@ void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks)
* Initialize and start the SPI interface.
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -138,7 +138,7 @@ int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode, int timeout,
* with its correspondent system.
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -183,7 +183,7 @@ EXPORT_SYMBOL_GPL(cvmx_spi_restart_interface);
* Callback to perform SPI4 reset
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -298,7 +298,7 @@ int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode)
* Callback to setup calendar and miscellaneous settings before clock detection
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -417,7 +417,7 @@ int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode,
* Callback to perform clock detection
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -495,7 +495,7 @@ int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode, int timeout)
* Callback to perform link training
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -564,7 +564,7 @@ int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode, int timeout)
* Callback to perform calendar data synchronization
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -621,7 +621,7 @@ int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode, int timeout)
* Callback to handle interface up
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
diff --git a/arch/mips/include/asm/octeon/cvmx-helper-spi.h b/arch/mips/include/asm/octeon/cvmx-helper-spi.h
index bc8cab9367b8..d2a15c3382ff 100644
--- a/arch/mips/include/asm/octeon/cvmx-helper-spi.h
+++ b/arch/mips/include/asm/octeon/cvmx-helper-spi.h
@@ -33,7 +33,7 @@
#define __CVMX_HELPER_SPI_H__
/**
- * Probe a SPI interface and determine the number of ports
+ * Probe an SPI interface and determine the number of ports
* connected to it. The SPI interface should still be down after
* this call.
*
@@ -45,7 +45,7 @@ extern int __cvmx_helper_spi_probe(int interface);
extern int __cvmx_helper_spi_enumerate(int interface);
/**
- * Bringup and enable a SPI interface. After this call packet I/O
+ * Bringup and enable an SPI interface. After this call packet I/O
* should be fully functional. This is called with IPD enabled but
* PKO disabled.
*
diff --git a/arch/mips/include/asm/octeon/cvmx-spi.h b/arch/mips/include/asm/octeon/cvmx-spi.h
index d5038cc4b475..2d52578262a7 100644
--- a/arch/mips/include/asm/octeon/cvmx-spi.h
+++ b/arch/mips/include/asm/octeon/cvmx-spi.h
@@ -84,7 +84,7 @@ static inline int cvmx_spi_is_spi_interface(int interface)
* Initialize and start the SPI interface.
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -102,7 +102,7 @@ extern int cvmx_spi_start_interface(int interface, cvmx_spi_mode_t mode,
* with its corespondant system.
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -114,7 +114,7 @@ extern int cvmx_spi_restart_interface(int interface, cvmx_spi_mode_t mode,
int timeout);
/**
- * Return non-zero if the SPI interface has a SPI4000 attached
+ * Return non-zero if the SPI interface has an SPI4000 attached
*
* @interface: SPI interface the SPI4000 is connected to
*
@@ -171,7 +171,7 @@ extern void cvmx_spi_set_callbacks(cvmx_spi_callbacks_t *new_callbacks);
* Callback to perform SPI4 reset
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -187,7 +187,7 @@ extern int cvmx_spi_reset_cb(int interface, cvmx_spi_mode_t mode);
* detection
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -204,7 +204,7 @@ extern int cvmx_spi_calendar_setup_cb(int interface, cvmx_spi_mode_t mode,
* Callback to perform clock detection
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -221,7 +221,7 @@ extern int cvmx_spi_clock_detect_cb(int interface, cvmx_spi_mode_t mode,
* Callback to perform link training
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -238,7 +238,7 @@ extern int cvmx_spi_training_cb(int interface, cvmx_spi_mode_t mode,
* Callback to perform calendar data synchronization
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
@@ -255,7 +255,7 @@ extern int cvmx_spi_calendar_sync_cb(int interface, cvmx_spi_mode_t mode,
* Callback to handle interface up
*
* @interface: The identifier of the packet interface to configure and
- * use as a SPI interface.
+ * use as an SPI interface.
* @mode: The operating mode for the SPI interface. The interface
* can operate as a full duplex (both Tx and Rx data paths
* active) or as a halfplex (either the Tx data path is
diff --git a/arch/mips/include/asm/octeon/cvmx-wqe.h b/arch/mips/include/asm/octeon/cvmx-wqe.h
index 9cec2299b81b..4a4c39311a40 100644
--- a/arch/mips/include/asm/octeon/cvmx-wqe.h
+++ b/arch/mips/include/asm/octeon/cvmx-wqe.h
@@ -359,7 +359,7 @@ typedef union {
* RGMII packet had a studder error (data not
* repeated - 10/100M only) or the SPI4 packet
* was sent to an NXA.
- * - 16 = FCS error: a SPI4.2 packet had an FCS error.
+ * - 16 = FCS error: an SPI4.2 packet had an FCS error.
* - 17 = Skip error: a packet was not large enough to
* cover the skipped bytes.
* - 18 = L2 header malformed: the packet is not long
diff --git a/drivers/auxdisplay/lcd2s.c b/drivers/auxdisplay/lcd2s.c
index 135831a16514..7864df3607b1 100644
--- a/drivers/auxdisplay/lcd2s.c
+++ b/drivers/auxdisplay/lcd2s.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Console driver for LCD2S 4x20 character displays connected through i2c.
- * The display also has a SPI interface, but the driver does not support
+ * The display also has an SPI interface, but the driver does not support
* this yet.
*
* This is a driver allowing you to use a LCD2S 4x20 from Modtronix
diff --git a/drivers/gpu/drm/solomon/ssd130x-spi.c b/drivers/gpu/drm/solomon/ssd130x-spi.c
index 19ab4942cb33..f9e1f10c42a3 100644
--- a/drivers/gpu/drm/solomon/ssd130x-spi.c
+++ b/drivers/gpu/drm/solomon/ssd130x-spi.c
@@ -138,7 +138,7 @@ MODULE_DEVICE_TABLE(of, ssd130x_of_match);
* if the device was registered via OF. This means that the module will not be
* auto loaded, unless it contains an alias that matches the MODALIAS reported.
*
- * To workaround this issue, add a SPI device ID table. Even when this should
+ * To workaround this issue, add an SPI device ID table. Even when this should
* not be needed for this driver to match the registered SPI devices.
*/
static const struct spi_device_id ssd130x_spi_table[] = {
diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c
index 1bb847466b10..f059369b1f1d 100644
--- a/drivers/gpu/drm/tiny/ili9486.c
+++ b/drivers/gpu/drm/tiny/ili9486.c
@@ -35,7 +35,7 @@
#define ILI9486_MADCTL_MY BIT(7)
/*
- * The PiScreen/waveshare rpi-lcd-35 has a SPI to 16-bit parallel bus converter
+ * The PiScreen/waveshare rpi-lcd-35 has an SPI to 16-bit parallel bus converter
* in front of the display controller. This means that 8-bit values have to be
* transferred as 16-bit.
*/
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5c2d0c06d2a5..4a3f92a59693 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -68,7 +68,7 @@ config INPUT_AD714X_SPI
depends on INPUT_AD714X && SPI
default y
help
- Say Y here if you have AD7142/AD7147 hooked to a SPI bus.
+ Say Y here if you have AD7142/AD7147 hooked to an SPI bus.
To compile this driver as a module, choose M here: the
module will be called ad714x-spi.
@@ -724,7 +724,7 @@ config INPUT_ADXL34X_SPI
depends on INPUT_ADXL34X && SPI
default y
help
- Say Y here if you have ADXL345/6 hooked to a SPI bus.
+ Say Y here if you have ADXL345/6 hooked to an SPI bus.
To compile this driver as a module, choose M here: the
module will be called adxl34x-spi.
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index c0163b983ce6..055819188b3c 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -26,7 +26,7 @@ config RMI4_SPI
tristate "RMI4 SPI Support"
depends on SPI
help
- Say Y here if you want to support RMI4 devices connected to a SPI
+ Say Y here if you want to support RMI4 devices connected to an SPI
bus.
If unsure, say N.
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 68d99a112e14..1870ab3aed46 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -82,7 +82,7 @@ config TOUCHSCREEN_AD7879_SPI
depends on TOUCHSCREEN_AD7879 && SPI_MASTER
select REGMAP_SPI
help
- Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.
+ Say Y here if you have AD7879-1/AD7889-1 hooked to an SPI bus.
If unsure, say N (but it's safe to say "Y").
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 30db49f31866..413e926ecb3c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -138,7 +138,7 @@ config MFD_ATMEL_FLEXCOM
depends on OF
help
Select this to get support for Atmel Flexcom. This is a wrapper
- which embeds a SPI controller, a I2C controller and a USART. Only
+ which embeds an SPI controller, a I2C controller and a USART. Only
one function can be used at a time. The choice is done at boot time
by the probe function of this MFD driver according to a device tree
property.
diff --git a/drivers/mfd/ocelot.h b/drivers/mfd/ocelot.h
index b8bc2f1486e2..0a6505edba53 100644
--- a/drivers/mfd/ocelot.h
+++ b/drivers/mfd/ocelot.h
@@ -21,7 +21,7 @@ struct resource;
* initialization based on bus speed.
* @dummy_buf: Zero-filled buffer of spi_padding_bytes size. The dummy
* bytes that will be sent out between the address and
- * data of a SPI read operation.
+ * data of an SPI read operation.
*/
struct ocelot_ddata {
struct regmap *gcb_regmap;
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5e19a961c34d..9cc2ae72b957 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -632,7 +632,7 @@ config MMC_SPI
select CRC7
select CRC_ITU_T
help
- Some systems access MMC/SD/SDIO cards using a SPI controller
+ Some systems access MMC/SD/SDIO cards using an SPI controller
instead of using a "native" MMC/SD/SDIO controller. This has a
disadvantage of being relatively high overhead, but a compensating
advantage of working on many systems without dedicated MMC/SD/SDIO
diff --git a/drivers/mtd/spi-nor/controllers/Kconfig b/drivers/mtd/spi-nor/controllers/Kconfig
index ca45dcd3ffe8..250295c8d85c 100644
--- a/drivers/mtd/spi-nor/controllers/Kconfig
+++ b/drivers/mtd/spi-nor/controllers/Kconfig
@@ -14,5 +14,5 @@ config SPI_NXP_SPIFI
Enable support for the NXP LPC SPI Flash Interface controller.
SPIFI is a specialized controller for connecting serial SPI
- Flash. Enable this option if you have a device with a SPIFI
+ Flash. Enable this option if you have a device with an SPIFI
controller and want to access the Flash as a mtd device.
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index d67c926bca8b..9fb03bf0202d 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1138,7 +1138,7 @@ int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
/**
* spi_nor_div_by_erase_size() - calculate remainder and update new dividend
- * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @erase: pointer to a structure that describes an SPI NOR erase type
* @dividend: dividend value
* @remainder: pointer to u32 remainder (will be updated)
*
@@ -1159,7 +1159,7 @@ static u64 spi_nor_div_by_erase_size(const struct spi_nor_erase_type *erase,
* which the address fits is expected to be
* provided.
* @map: the erase map of the SPI NOR
- * @region: pointer to a structure that describes a SPI NOR erase region
+ * @region: pointer to a structure that describes an SPI NOR erase region
* @addr: offset in the serial flash memory
* @len: number of bytes to erase
*
@@ -1217,7 +1217,7 @@ static u64 spi_nor_region_end(const struct spi_nor_erase_region *region)
/**
* spi_nor_region_next() - get the next spi nor region
- * @region: pointer to a structure that describes a SPI NOR erase region
+ * @region: pointer to a structure that describes an SPI NOR erase region
*
* Return: the next spi nor region or NULL if last region.
*/
@@ -1260,8 +1260,8 @@ spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64 addr)
/**
* spi_nor_init_erase_cmd() - initialize an erase command
- * @region: pointer to a structure that describes a SPI NOR erase region
- * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @region: pointer to a structure that describes an SPI NOR erase region
+ * @erase: pointer to a structure that describes an SPI NOR erase type
*
* Return: the pointer to the allocated erase command, ERR_PTR(-errno)
* otherwise.
@@ -2011,8 +2011,8 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps)
}
/**
- * spi_nor_set_erase_type() - set a SPI NOR erase type
- * @erase: pointer to a structure that describes a SPI NOR erase type
+ * spi_nor_set_erase_type() - set an SPI NOR erase type
+ * @erase: pointer to a structure that describes an SPI NOR erase type
* @size: the size of the sector/block erased by the erase type
* @opcode: the SPI command op code to erase the sector/block
*/
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index f03b55cf7e6f..19b2eaaaea0e 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -189,7 +189,7 @@ enum spi_nor_pp_command_index {
};
/**
- * struct spi_nor_erase_type - Structure to describe a SPI NOR erase type
+ * struct spi_nor_erase_type - Structure to describe an SPI NOR erase type
* @size: the size of the sector/block erased by the erase type.
* JEDEC JESD216B imposes erase sizes to be a power of 2.
* @size_shift: @size is a power of 2, the shift is stored in
@@ -228,7 +228,7 @@ struct spi_nor_erase_command {
};
/**
- * struct spi_nor_erase_region - Structure to describe a SPI NOR erase region
+ * struct spi_nor_erase_region - Structure to describe an SPI NOR erase region
* @offset: the offset in the data array of erase region start.
* LSB bits are used as a bitmask encoding flags to
* determine if this region is overlaid, if this region is
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 8434f654eca1..dfead627da6b 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -304,7 +304,7 @@ static const struct sfdp_bfpt_erase sfdp_bfpt_erases[] = {
/**
* spi_nor_set_erase_settings_from_bfpt() - set erase type settings from BFPT
- * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @erase: pointer to a structure that describes an SPI NOR erase type
* @size: the size of the sector/block erased by the erase type
* @opcode: the SPI command op code to erase the sector/block
* @i: erase type index as sorted in the Basic Flash Parameter Table
@@ -775,8 +775,8 @@ static void spi_nor_region_mark_overlay(struct spi_nor_erase_region *region)
/**
* spi_nor_region_check_overlay() - set overlay bit when the region is overlaid
- * @region: pointer to a structure that describes a SPI NOR erase region
- * @erase: pointer to a structure that describes a SPI NOR erase type
+ * @region: pointer to a structure that describes an SPI NOR erase region
+ * @erase: pointer to a structure that describes an SPI NOR erase type
* @erase_type: erase type bitmask
*/
static void
diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c
index 7729d3f8b7f5..4985d3427441 100644
--- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c
+++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c
@@ -71,11 +71,11 @@
* (the switch will read the fields provided in the buffer).
* OP_DEL: Set if the manual says the VALIDENT bit is supported in the
* COMMAND portion of this dynamic config buffer (i.e. the
- * specified entry can be invalidated through a SPI write
+ * specified entry can be invalidated through an SPI write
* command).
* OP_SEARCH: Set if the manual says that the index of an entry can
* be retrieved in the COMMAND portion of the buffer based
- * on its ENTRY portion, as a result of a SPI write command.
+ * on its ENTRY portion, as a result of an SPI write command.
* Only the TCAM-based FDB table on SJA1105 P/Q/R/S supports
* this.
* OP_VALID_ANYWAY: Reading some tables through the dynamic config
diff --git a/drivers/net/ethernet/microchip/enc28j60.c b/drivers/net/ethernet/microchip/enc28j60.c
index 176efbeae127..8133ee248c78 100644
--- a/drivers/net/ethernet/microchip/enc28j60.c
+++ b/drivers/net/ethernet/microchip/enc28j60.c
@@ -346,7 +346,7 @@ static void locked_regw_write(struct enc28j60_net *priv, u8 address, u16 data)
/*
* Buffer memory read
- * Select the starting address and execute a SPI buffer read.
+ * Select the starting address and execute an SPI buffer read.
*/
static void enc28j60_mem_read(struct enc28j60_net *priv, u16 addr, int len,
u8 *data)
diff --git a/drivers/net/wireless/microchip/wilc1000/Kconfig b/drivers/net/wireless/microchip/wilc1000/Kconfig
index 62cfcdc9aacc..8fb10ce1b5ce 100644
--- a/drivers/net/wireless/microchip/wilc1000/Kconfig
+++ b/drivers/net/wireless/microchip/wilc1000/Kconfig
@@ -31,7 +31,7 @@ config WILC1000_SPI
help
This module adds support for the SPI interface of adapters using
WILC1000 chipset. The Atmel WILC1000 has a Serial Peripheral
- Interface (SPI) that operates as a SPI slave. This SPI interface can
+ Interface (SPI) that operates as an SPI slave. This SPI interface can
be used for control and for serial I/O of 802.11 data. The SPI is a
full-duplex slave synchronous serial interface that is available
immediately following reset when pin 9 (SDIO_SPI_CFG) is tied to
diff --git a/drivers/net/wireless/st/cw1200/Kconfig b/drivers/net/wireless/st/cw1200/Kconfig
index 03575e9894bb..f6a1f83e4dd5 100644
--- a/drivers/net/wireless/st/cw1200/Kconfig
+++ b/drivers/net/wireless/st/cw1200/Kconfig
@@ -24,7 +24,7 @@ config CW1200_WLAN_SPI
tristate "Support SPI platforms"
depends on CW1200 && SPI
help
- Enables support for the CW1200 connected via a SPI bus. You will
+ Enables support for the CW1200 connected via an SPI bus. You will
need to add appropriate platform data glue in your board setup
file.
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index c1ca247987d2..ba0d4a2f1096 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -115,7 +115,7 @@ config CROS_EC_SPI
help
If you say Y here, you get support for talking to the ChromeOS EC
- through a SPI bus, using a byte-level protocol. Since the EC's
+ through an SPI bus, using a byte-level protocol. Since the EC's
response time cannot be guaranteed, we support ignoring
'pre-amble' bytes before the response actually starts.
diff --git a/drivers/platform/chrome/cros_ec_spi.c b/drivers/platform/chrome/cros_ec_spi.c
index 21143dba8970..f0e793e9a4ce 100644
--- a/drivers/platform/chrome/cros_ec_spi.c
+++ b/drivers/platform/chrome/cros_ec_spi.c
@@ -61,7 +61,7 @@
#define EC_SPI_RECOVERY_TIME_NS (200 * 1000)
/**
- * struct cros_ec_spi - information about a SPI-connected EC
+ * struct cros_ec_spi - information about an SPI-connected EC
*
* @spi: SPI device we are connected to
* @last_transfer_ns: time that we last finished a transfer.
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index ed9360486953..2c187264b249 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -701,7 +701,7 @@ static int ds1305_probe(struct spi_device *spi)
/* Maybe set up alarm IRQ; be ready to handle it triggering right
* away. NOTE that we don't share this. The signal is active low,
- * and we can't ack it before a SPI message delay. We temporarily
+ * and we can't ack it before an SPI message delay. We temporarily
* disable the IRQ until it's acked, which lets us work with more
* IRQ trigger modes (not all IRQ controllers can do falling edge).
*/
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 3b1c0878bb85..64825495c3e1 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -77,7 +77,7 @@ config SPI_ALTERA_DFL
help
This is a Device Feature List (DFL) bus driver for the
Altera SPI master controller. The SPI master is connected
- to a SPI slave to Avalon bridge in a Intel MAX BMC.
+ to an SPI slave to Avalon bridge in a Intel MAX BMC.
config SPI_AR934X
tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver"
@@ -632,7 +632,7 @@ config SPI_MTK_SNFI
help
This enables support for SPI-NAND mode on the MediaTek NAND
Flash Interface found on MediaTek ARM SoCs. This controller
- is implemented as a SPI-MEM controller with pipelined ECC
+ is implemented as an SPI-MEM controller with pipelined ECC
capcability.
config SPI_WPCM_FIU
@@ -765,7 +765,7 @@ config SPI_PXA2XX
depends on ARCH_PXA || ARCH_MMP || PCI || ACPI || COMPILE_TEST
select PXA_SSP if ARCH_PXA || ARCH_MMP
help
- This enables using a PXA2xx or Sodaville SSP port as a SPI master
+ This enables using a PXA2xx or Sodaville SSP port as an SPI master
controller. The driver can be configured to use any SSP port and
additional documentation can be found a Documentation/spi/pxa2xx.rst.
@@ -1151,7 +1151,7 @@ config SPI_MUX
select MULTIPLEXER
help
This adds support for SPI multiplexers. Each SPI mux will be
- accessible as a SPI controller, the devices behind the mux will appear
+ accessible as an SPI controller, the devices behind the mux will appear
to be chip selects on this controller. It is still necessary to
select one or more specific mux-controller drivers.
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index 0c79193d9697..cfd1c4fff606 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -438,7 +438,7 @@ const char *spi_mem_get_name(struct spi_mem *mem)
EXPORT_SYMBOL_GPL(spi_mem_get_name);
/**
- * spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
+ * spi_mem_adjust_op_size() - Adjust the data size of an SPI mem operation to
* match controller limitations
* @mem: the SPI memory
* @op: the operation to adjust
@@ -886,11 +886,11 @@ static void spi_mem_shutdown(struct spi_device *spi)
}
/**
- * spi_mem_driver_register_with_owner() - Register a SPI memory driver
+ * spi_mem_driver_register_with_owner() - Register an SPI memory driver
* @memdrv: the SPI memory driver to register
* @owner: the owner of this driver
*
- * Registers a SPI memory driver.
+ * Registers an SPI memory driver.
*
* Return: 0 in case of success, a negative error core otherwise.
*/
@@ -907,10 +907,10 @@ int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
/**
- * spi_mem_driver_unregister() - Unregister a SPI memory driver
+ * spi_mem_driver_unregister() - Unregister an SPI memory driver
* @memdrv: the SPI memory driver to unregister
*
- * Unregisters a SPI memory driver.
+ * Unregisters an SPI memory driver.
*/
void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
{
diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c
index 609311231e64..30015b6bffc4 100644
--- a/drivers/spi/spi-mpc52xx-psc.c
+++ b/drivers/spi/spi-mpc52xx-psc.c
@@ -285,7 +285,7 @@ static int mpc52xx_psc_spi_port_config(int psc_id, struct mpc52xx_psc_spi *mps)
out_8(&fifo->rfcntl, 0);
out_8(&psc->mode, MPC52xx_PSC_MODE_FFULL);
- /* Configure 8bit codec mode as a SPI master and use EOF flags */
+ /* Configure 8bit codec mode as an SPI master and use EOF flags */
/* SICR_SIM_CODEC8|SICR_GENCLK|SICR_SPI|SICR_MSTR|SICR_USEEOF */
out_be32(&psc->sicr, 0x0180C800);
out_be16((u16 __iomem *)&psc->ccr, 0x070F); /* default SPI Clk 1MHz */
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 15f174f4e056..95da8dafb652 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -470,7 +470,7 @@ struct bus_type spi_bus_type = {
EXPORT_SYMBOL_GPL(spi_bus_type);
/**
- * __spi_register_driver - register a SPI driver
+ * __spi_register_driver - register an SPI driver
* @owner: owner module of the driver to register
* @sdrv: the driver to register
* Context: can sleep
@@ -3241,7 +3241,7 @@ static void devm_spi_unregister(struct device *dev, void *res)
* spi_alloc_slave()
* Context: can sleep
*
- * Register a SPI device as with spi_register_controller() which will
+ * Register an SPI device as with spi_register_controller() which will
* automatically be unregistered and freed.
*
* Return: zero on success, else a negative error code.
diff --git a/drivers/staging/fbtft/fbtft.h b/drivers/staging/fbtft/fbtft.h
index 2c2b5f1c1df3..21d07b09fd36 100644
--- a/drivers/staging/fbtft/fbtft.h
+++ b/drivers/staging/fbtft/fbtft.h
@@ -152,7 +152,7 @@ struct fbtft_platform_data {
* supported by kernel-doc.
*
*/
-/* @spi: Set if it is a SPI device
+/* @spi: Set if it is an SPI device
* @pdev: Set if it is a platform device
* @info: Pointer to framebuffer fb_info structure
* @pdata: Pointer to platform data
diff --git a/drivers/staging/iio/meter/Kconfig b/drivers/staging/iio/meter/Kconfig
index aa6a3e7f6cdb..316d2589a484 100644
--- a/drivers/staging/iio/meter/Kconfig
+++ b/drivers/staging/iio/meter/Kconfig
@@ -29,7 +29,7 @@ config ADE7854_SPI
depends on ADE7854 && SPI
default y
help
- Say Y here if you have ADE7854/58/68/78 hooked to a SPI bus.
+ Say Y here if you have ADE7854/58/68/78 hooked to an SPI bus.
To compile this driver as a module, choose M here: the
module will be called ade7854-spi.
diff --git a/drivers/staging/octeon/ethernet-spi.c b/drivers/staging/octeon/ethernet-spi.c
index 699c98c5ec13..f5c58b029349 100644
--- a/drivers/staging/octeon/ethernet-spi.c
+++ b/drivers/staging/octeon/ethernet-spi.c
@@ -175,7 +175,7 @@ static void cvm_oct_spi_poll(struct net_device *dev)
if (priv->port == spi4000_port) {
/*
* This function does nothing if it is called on an
- * interface without a SPI4000.
+ * interface without an SPI4000.
*/
cvmx_spi4000_check_speed(interface, priv->port);
/*
diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c
index 352e3ac2b377..6980796f8af2 100644
--- a/drivers/usb/host/max3421-hcd.c
+++ b/drivers/usb/host/max3421-hcd.c
@@ -7,7 +7,7 @@
* (C) Copyright 2014 David Mosberger-Tang <davidm(a)egauge.net>
*
* MAX3421 is a chip implementing a USB 2.0 Full-/Low-Speed host
- * controller on a SPI bus.
+ * controller on an SPI bus.
*
* Based on:
* o MAX3421E datasheet
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 6d3392a7edc6..2564de8b6ed3 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -231,8 +231,8 @@ struct spinand_devid {
/**
* struct manufacurer_ops - SPI NAND manufacturer specific operations
- * @init: initialize a SPI NAND device
- * @cleanup: cleanup a SPI NAND device
+ * @init: initialize an SPI NAND device
+ * @cleanup: cleanup an SPI NAND device
*
* Each SPI NAND manufacturer driver should implement this interface so that
* NAND chips coming from this vendor can be initialized properly.
@@ -293,7 +293,7 @@ struct spinand_op_variants {
}
/**
- * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND
+ * spinand_ecc_info - description of the on-die ECC implemented by an SPI NAND
* chip
* @get_status: get the ECC status. Should return a positive number encoding
* the number of corrected bitflips if correction was possible or
@@ -462,7 +462,7 @@ static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd)
}
/**
- * spinand_to_mtd() - Get the MTD device embedded in a SPI NAND device
+ * spinand_to_mtd() - Get the MTD device embedded in an SPI NAND device
* @spinand: SPI NAND device
*
* Return: the MTD device embedded in @spinand.
@@ -484,7 +484,7 @@ static inline struct spinand_device *nand_to_spinand(struct nand_device *nand)
}
/**
- * spinand_to_nand() - Get the NAND device embedded in a SPI NAND object
+ * spinand_to_nand() - Get the NAND device embedded in an SPI NAND object
* @spinand: SPI NAND device
*
* Return: the NAND device embedded in @spinand.
@@ -496,11 +496,11 @@ spinand_to_nand(struct spinand_device *spinand)
}
/**
- * spinand_set_of_node - Attach a DT node to a SPI NAND device
+ * spinand_set_of_node - Attach a DT node to an SPI NAND device
* @spinand: SPI NAND device
* @np: DT node
*
- * Attach a DT node to a SPI NAND device.
+ * Attach a DT node to an SPI NAND device.
*/
static inline void spinand_set_of_node(struct spinand_device *spinand,
struct device_node *np)
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index ab7eea01ab42..3b437ee3905f 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -173,7 +173,7 @@ struct rmi_f01_power_management {
* SPI mode.
* @split_read_byte_delay_us - the delay between each byte of a read operation
* in V2 mode.
- * @pre_delay_us - the delay before the start of a SPI transaction. This is
+ * @pre_delay_us - the delay before the start of an SPI transaction. This is
* typically useful in conjunction with custom chip select assertions (see
* below).
* @post_delay_us - the delay after the completion of an SPI transaction. This
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index 8e984d75f5b6..2a9a8a7893ac 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -56,7 +56,7 @@
#define SPI_MEM_OP_NO_DATA { }
/**
- * enum spi_mem_data_dir - describes the direction of a SPI memory data
+ * enum spi_mem_data_dir - describes the direction of an SPI memory data
* transfer from the controller perspective
* @SPI_MEM_NO_DATA: no data transferred
* @SPI_MEM_DATA_IN: data coming from the SPI memory
@@ -69,7 +69,7 @@ enum spi_mem_data_dir {
};
/**
- * struct spi_mem_op - describes a SPI memory operation
+ * struct spi_mem_op - describes an SPI memory operation
* @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is
* sent MSB-first.
* @cmd.buswidth: number of IO lines used to transmit the command
@@ -182,7 +182,7 @@ struct spi_mem_dirmap_desc {
};
/**
- * struct spi_mem - describes a SPI memory device
+ * struct spi_mem - describes an SPI memory device
* @spi: the underlying SPI device
* @drvpriv: spi_mem_driver private data
* @name: name of the SPI memory device
@@ -200,7 +200,7 @@ struct spi_mem {
};
/**
- * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
+ * struct spi_mem_set_drvdata() - attach driver private data to an SPI mem
* device
* @mem: memory device
* @data: data to attach to the memory device
@@ -211,7 +211,7 @@ static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
}
/**
- * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
+ * struct spi_mem_get_drvdata() - get driver private data attached to an SPI mem
* device
* @mem: memory device
*
@@ -228,7 +228,7 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
* limitations (can be alignment or max RX/TX size
* limitations)
* @supports_op: check if an operation is supported by the controller
- * @exec_op: execute a SPI memory operation
+ * @exec_op: execute an SPI memory operation
* @get_name: get a custom name for the SPI mem device from the controller.
* This might be needed if the controller driver has been ported
* to use the SPI mem layer and a custom name is used to keep
@@ -302,10 +302,10 @@ struct spi_controller_mem_caps {
/**
* struct spi_mem_driver - SPI memory driver
- * @spidrv: inherit from a SPI driver
- * @probe: probe a SPI memory. Usually where detection/initialization takes
+ * @spidrv: inherit from an SPI driver
+ * @probe: probe an SPI memory. Usually where detection/initialization takes
* place
- * @remove: remove a SPI memory
+ * @remove: remove an SPI memory
* @shutdown: take appropriate action when the system is shutdown
*
* This is just a thin wrapper around a spi_driver. The core takes care of
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 9a32495fbb1f..debe88fdaddb 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -279,7 +279,7 @@ struct spi_message;
* field of this structure.
*
* This represents the kind of device driver that uses SPI messages to
- * interact with the hardware at the other end of a SPI link. It's called
+ * interact with the hardware at the other end of an SPI link. It's called
* a "protocol" driver because it works through messages rather than talking
* directly to SPI hardware (which is what the underlying SPI controller
* driver does to pass those messages). These protocols are defined in the
@@ -323,7 +323,7 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
__spi_register_driver(THIS_MODULE, driver)
/**
- * module_spi_driver() - Helper macro for registering a SPI driver
+ * module_spi_driver() - Helper macro for registering an SPI driver
* @__spi_driver: spi_driver struct
*
* Helper macro for SPI drivers which do not do anything special in module
@@ -839,7 +839,7 @@ int acpi_spi_count_resources(struct acpi_device *adev);
#endif
/*
- * SPI resource management while processing a SPI message
+ * SPI resource management while processing an SPI message
*/
typedef void (*spi_res_release_t)(struct spi_controller *ctlr,
@@ -1454,7 +1454,7 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)
*/
/**
- * struct spi_board_info - board-specific template for a SPI device
+ * struct spi_board_info - board-specific template for an SPI device
* @modalias: Initializes spi_device.modalias; identifies the driver.
* @platform_data: Initializes spi_device.platform_data; the particular
* data stored there is driver-specific.
--
2.34.1
1
0
Modified according to the writing-schema.rst file and tested.
Signed-off-by: Kiseok Jo <kiseok.jo(a)irondevice.com>
---
.../bindings/sound/irondevice,sma1303.yaml | 46 +++++++++++++++++--
1 file changed, 43 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml
index 162c52606635..35d9a046ef75 100644
--- a/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml
+++ b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml
@@ -10,22 +10,62 @@ maintainers:
- Kiseok Jo <kiseok.jo(a)irondevice.com>
description:
- SMA1303 digital class-D audio amplifier with an integrated boost converter.
+ SMA1303 digital class-D audio amplifier
+ with an integrated boost converter.
allOf:
- - $ref: name-prefix.yaml#
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - irondevice,sma1303
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 1
+
+ i2c-retry:
+ description: number of retries for I2C regmap.
+ maximum: 49
+ default: 3
+
+ tdm-slot-rx:
+ description: set the tdm rx start slot.
+ maximum: 7
+ default: 0
+
+ tdm-slot-tx:
+ description: set the tdm tx start slot.
+ maximum: 7
+ default: 0
+
+ sys-clk-id:
+ description: select the using system clock.
+ default: 3
required:
- compatible
- reg
+ - '#sound-dai-cells'
additionalProperties: false
examples:
- |
- i2c_bus {
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
amplifier@1e {
compatible = "irondevice,sma1303";
reg = <0x1e>;
+ #sound-dai-cells = <1>;
+ i2c-retry = <5>;
+ tdm-slot-rx = <0>;
+ tdm-slot-tx = <2>;
+ sys-clk-id = <3>;
};
};
base-commit: e33d4c4f1e2de74cfea556d75eef0886d5b7d472
--
2.20.1
3
5

[PATCH 00/14] Add support for compress offload and gapless playback.
by Mohammad Rafi Shaik 03 Feb '23
by Mohammad Rafi Shaik 03 Feb '23
03 Feb '23
Add support for compress offload and gapless playback in audioreach
platform drivers.
Mohammad Rafi Shaik (14):
ALSA: compress: Update compress set params for gapless playback
ASoC: qcom: SC7280: audioreach: Add sc7280 hardware param fixup
callback
ASoC: q6dsp: audioreach: Add placeholder decoder for compress playback
ASoC: q6dsp: audioreach: Add support for compress offload commands
ASoC: q6dsp: audioreach: Add support to set compress params
ASoC: q6dsp: audioreach: Add support for sending real module ID to
ADSP
ASoC: q6dsp: q6apm-dai: Add async compress write support
ASoC: q6dsp: q6apm-dai: Add open/free compress DAI callbacks
ASoC: q6dsp: q6apm-dai: Add compress DAI and codec caps get callbacks
ASoC: q6dsp: q6apm-dai: Add trigger/pointer compress DAI callbacks
ASoC: q6dsp: q6apm-dai: Add compress set params and metadata DAI
callbacks
ASoC: q6dsp: q6apm-dai: Add mmap and copy compress DAI callbacks
ASoC: qdsp6: audioreach: Add MP3, AAC and FLAC compress format support
ASoC: q6dsp: audioreach: Add gapless feature support
sound/core/compress_offload.c | 12 +-
sound/soc/qcom/qdsp6/audioreach.c | 299 +++++++++++++++++--
sound/soc/qcom/qdsp6/audioreach.h | 56 ++++
sound/soc/qcom/qdsp6/q6apm-dai.c | 464 ++++++++++++++++++++++++++++++
sound/soc/qcom/qdsp6/q6apm.c | 117 ++++++++
sound/soc/qcom/qdsp6/q6apm.h | 8 +
sound/soc/qcom/sc7280.c | 21 +-
7 files changed, 950 insertions(+), 27 deletions(-)
--
2.25.1
2
18

ALSA: emux: Avoid potential array out-of-bound in snd_emux_xg_control()
by Артемий Карасев 03 Feb '23
by Артемий Карасев 03 Feb '23
03 Feb '23
In snd_emux_control() method "snd_emux_port" can be passed with
field chset.midi_mode == SNDRV_MIDI_MODE_XG. The "type" can be
MIDI_CTL_PITCHBEND or MIDI_CTL_CHAN_PRESSURE.
It may lead to accessing 'control' array of size 128 with
MIDI_CTL_PITCHBEND (0x80) or MIDI_CTL_CHAN_PRESSURE (0x82) index.
How can we prevent this? Would it be a good solution to place code like this
before call snd_emux_xg_control()?
if (type == MIDI_CTL_PITCHBEND || type == MIDI_CTL_CHAN_PRESSURE){
return;
}
Found by Linux Verification Center (linuxtesting.org) with SVACE.
1
0
Create tas2781 driver.
Signed-off-by: Shenghao Ding <13916275206(a)139.com>
---
Changes in v3:
- fixed issues reported-by: kernel test robot <lkp(a)intel.com>
- correct all the inconsistent indenting warnings.
- correct uninitialized symbol 'pwr_state'
- correct unsigned 'val' is never less than zero
- correct 'tas_dev' could be null
Changes to be committed:
modified: sound/soc/codecs/Kconfig
modified: sound/soc/codecs/Makefile
new file: sound/soc/codecs/tas2781-dsp.c
new file: sound/soc/codecs/tas2781-dsp.h
new file: sound/soc/codecs/tas2781-i2c.c
new file: sound/soc/codecs/tas2781.h
---
sound/soc/codecs/Kconfig | 12 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/tas2781-dsp.c | 2261 ++++++++++++++++++++++++++++++++
sound/soc/codecs/tas2781-dsp.h | 178 +++
sound/soc/codecs/tas2781-i2c.c | 1913 +++++++++++++++++++++++++++
sound/soc/codecs/tas2781.h | 176 +++
6 files changed, 4542 insertions(+)
create mode 100644 sound/soc/codecs/tas2781-dsp.c
create mode 100644 sound/soc/codecs/tas2781-dsp.h
create mode 100644 sound/soc/codecs/tas2781-i2c.c
create mode 100644 sound/soc/codecs/tas2781.h
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0f9d71490075..9016c4846d95 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -223,6 +223,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS2764
imply SND_SOC_TAS2770
imply SND_SOC_TAS2780
+ imply SND_SOC_TAS2781
imply SND_SOC_TAS5086
imply SND_SOC_TAS571X
imply SND_SOC_TAS5720
@@ -1579,6 +1580,17 @@ config SND_SOC_TAS2780
Enable support for Texas Instruments TAS2780 high-efficiency
digital input mono Class-D audio power amplifiers.
+config SND_SOC_TAS2781
+ tristate "Texas Instruments TAS2781 speaker amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ select CRC8
+ help
+ Enable support for Texas Instruments TAS2781 Smart Amplifier
+ Digital input mono Class-D and DSP-inside audio power amplifiers.
+ Note the TAS2781 driver implements a flexible and configurable
+ algo coff setting, for one, two, even multiple TAS2781 chips.
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 71d3ce5867e4..49f912aea19d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -355,6 +355,7 @@ snd-soc-tas2552-objs := tas2552.o
snd-soc-tas2562-objs := tas2562.o
snd-soc-tas2764-objs := tas2764.o
snd-soc-tas2780-objs := tas2780.o
+snd-soc-tas2781-objs := tas2781-i2c.o tas2781-dsp.o
# Mux
snd-soc-simple-mux-objs := simple-mux.o
@@ -606,6 +607,7 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o
+obj-$(CONFIG_SND_SOC_TAS2781) += snd-soc-tas2781.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
diff --git a/sound/soc/codecs/tas2781-dsp.c b/sound/soc/codecs/tas2781-dsp.c
new file mode 100644
index 000000000000..615f5c24e3b3
--- /dev/null
+++ b/sound/soc/codecs/tas2781-dsp.c
@@ -0,0 +1,2261 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+ *
+ * Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2781 driver implements a flexible and configurable algo coff setting
+ * for one, two, even multiple TAS2781 chips.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/crc8.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+
+#include "tas2781.h"
+
+#define ERROR_PRAM_CRCCHK 0x0000000
+#define ERROR_YRAM_CRCCHK 0x0000001
+#define BINFILE_VER 0
+#define DRV_VER 1
+#define PPC_DRIVER_CRCCHK 0x00000200
+
+#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c)
+#define TAS2781_YRAM_BOOK1 140
+#define TAS2781_YRAM1_PAGE 42
+#define TAS2781_YRAM1_START_REG 88
+
+#define TAS2781_YRAM2_START_PAGE 43
+#define TAS2781_YRAM2_END_PAGE 49
+#define TAS2781_YRAM2_START_REG 8
+#define TAS2781_YRAM2_END_REG 127
+
+#define TAS2781_YRAM3_PAGE 50
+#define TAS2781_YRAM3_START_REG 8
+#define TAS2781_YRAM3_END_REG 27
+
+/*should not include B0_P53_R44-R47 */
+#define TAS2781_YRAM_BOOK2 0
+#define TAS2781_YRAM4_START_PAGE 50
+#define TAS2781_YRAM4_END_PAGE 60
+
+#define TAS2781_YRAM5_PAGE 61
+#define TAS2781_YRAM5_START_REG 8
+#define TAS2781_YRAM5_END_REG 27
+
+#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64
+#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10
+#define MAIN_ALL_DEVICES_1X 0x01
+#define MAIN_DEVICE_A_1X 0x02
+#define MAIN_DEVICE_B_1X 0x03
+#define MAIN_DEVICE_C_1X 0x04
+#define MAIN_DEVICE_D_1X 0x05
+#define COEFF_DEVICE_A_1X 0x12
+#define COEFF_DEVICE_B_1X 0x13
+#define COEFF_DEVICE_C_1X 0x14
+#define COEFF_DEVICE_D_1X 0x15
+#define PRE_DEVICE_A_1X 0x22
+#define PRE_DEVICE_B_1X 0x23
+#define PRE_DEVICE_C_1X 0x24
+#define PRE_DEVICE_D_1X 0x25
+
+struct tas_crc {
+ unsigned char offset;
+ unsigned char len;
+};
+
+const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
+ 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4
+};
+
+static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->type = SMS_HTONL(data[offset],
+ data[offset + 1], data[offset + 2],
+ data[offset + 3]);
+ offset += 4;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mbPChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnPChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->pchksum = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mbYChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnYChkSum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->ychksum = data[offset];
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: blk_size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->blk_size = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->n_subblocks = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ block->data = kzalloc(block->blk_size, GFP_KERNEL);
+ if (block->data == NULL) {
+ offset = -1;
+ goto out;
+ }
+ memcpy(block->data, &data[offset], block->blk_size);
+ offset += block->blk_size;
+out:
+ return offset;
+}
+
+static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = fmw->data;
+ unsigned int n_block;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ img_data->n_blk = SMS_HTONL(data[offset],
+ data[offset + 1], data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ img_data->dev_blks =
+ kcalloc(img_data->n_blk, sizeof(struct tasdev_blk),
+ GFP_KERNEL);
+ if (img_data->dev_blks == NULL) {
+ dev_err(tas_fmw->dev, "%s: FW memory failed!\n", __func__);
+ goto out;
+ }
+
+ for (n_block = 0; n_block < img_data->n_blk; n_block++) {
+ offset = fw_parse_block_data_kernel(tas_fmw,
+ &(img_data->dev_blks[n_block]), fmw, offset);
+ if (offset < 0) {
+ offset = -1;
+ goto out;
+ }
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_program_data_kernel(
+ struct tasdevice_priv *tas_dev, struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned int n_program = 0;
+
+ for (n_program = 0; n_program < tas_fmw->n_programs; n_program++) {
+ program = &(tas_fmw->programs[n_program]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnAppMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPDMI2SMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnISnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnVSnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ //skip 3-byte reserved
+ offset += 3;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPowerLDG error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data_kernel(
+ struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *data = fmw->data;
+ unsigned int n_configs;
+ struct tasdevice_config *config;
+
+ for (n_configs = 0; n_configs < tas_fmw->n_configurations;
+ n_configs++) {
+ config =
+ &(tas_fmw->configs[n_configs]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: orientation error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDevices error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 1;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mProgram error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnSamplingRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrc error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFsRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrcRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ offset = fw_parse_data_kernel(tas_fmw,
+ &(config->dev_data), fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_variable_header_kernel(
+ struct tasdevice_priv *tas_dev, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_prog *program;
+ struct tasdevice_config *config;
+ unsigned int n_program = 0, n_configs = 0;
+ unsigned short max_confs = TASDEVICE_MAXCONFIG_NUM_KERNEL;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONS(buf[offset], buf[offset + 1]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s:not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONS(buf[offset], buf[offset + 1]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev, tas_dev->ndev);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPrograms error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_programs = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+
+ if (tas_fmw->n_programs == 0 || tas_fmw->n_programs >
+ TASDEVICE_MAXPROGRAM_NUM_KERNEL) {
+ dev_err(tas_dev->dev, "%s: mnPrograms is invalid\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpPrograms error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->n_programs,
+ sizeof(struct tasdevice_prog), GFP_KERNEL);
+ if (tas_fmw->programs == NULL) {
+ dev_err(tas_dev->dev, "%s: mpPrograms memory failed!\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ for (n_program = 0; n_program < tas_fmw->n_programs; n_program++) {
+ program = &(tas_fmw->programs[n_program]);
+ program->prog_size = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ }
+ offset += (4 * (5 - n_program));
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_configurations = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ max_confs = (fw_hdr->ndev >= 4) ?
+ TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS :
+ TASDEVICE_MAXCONFIG_NUM_KERNEL;
+ if (tas_fmw->n_configurations == 0 ||
+ tas_fmw->n_configurations > max_confs) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations is invalid\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ if (offset + 4 * max_confs > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpConfigurations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ tas_fmw->configs = kcalloc(tas_fmw->n_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+ if (tas_fmw->configs == NULL) {
+ offset = -1;
+ goto out;
+ }
+
+ for (n_configs = 0; n_configs < tas_fmw->n_programs;
+ n_configs++) {
+ config =
+ &(tas_fmw->configs[n_configs]);
+ config->cfg_size = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ }
+
+ offset += (4 * (max_confs - n_configs));
+out:
+ return offset;
+}
+
+static int tasdevice_load_block_kernel(
+ struct tasdevice_priv *tasdevice, struct tasdev_blk *block)
+{
+ int ret = 0;
+
+ unsigned char *data = block->data;
+ unsigned int i = 0, length = 0;
+ const unsigned int blk_size = block->blk_size;
+ unsigned char dev_idx = 0;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES_1X:
+ dev_idx = 0|0x80;
+ break;
+ case MAIN_DEVICE_A_1X:
+ dev_idx = 1|0x80;
+ break;
+ case COEFF_DEVICE_A_1X:
+ case PRE_DEVICE_A_1X:
+ dev_idx = 1|0xC0;
+ break;
+ case MAIN_DEVICE_B_1X:
+ dev_idx = 2|0x80;
+ break;
+ case COEFF_DEVICE_B_1X:
+ case PRE_DEVICE_B_1X:
+ dev_idx = 2|0xC0;
+ break;
+ case MAIN_DEVICE_C_1X:
+ dev_idx = 3|0x80;
+ break;
+ case COEFF_DEVICE_C_1X:
+ case PRE_DEVICE_C_1X:
+ dev_idx = 3|0xC0;
+ break;
+ case MAIN_DEVICE_D_1X:
+ dev_idx = 4|0x80;
+ break;
+ case COEFF_DEVICE_D_1X:
+ case PRE_DEVICE_D_1X:
+ dev_idx = 4|0xC0;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ } else {
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ dev_idx = 0|0x80;
+ break;
+ case MAIN_DEVICE_A:
+ dev_idx = 1|0x80;
+ break;
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ dev_idx = 1|0xC0;
+ break;
+ case MAIN_DEVICE_B:
+ dev_idx = 2|0x80;
+ break;
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ dev_idx = 2|0xC0;
+ break;
+ case MAIN_DEVICE_C:
+ dev_idx = 3|0x80;
+ break;
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ dev_idx = 3|0xC0;
+ break;
+ case MAIN_DEVICE_D:
+ dev_idx = 4|0x80;
+ break;
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ dev_idx = 4|0xC0;
+ break;
+ default:
+ dev_info(tasdevice->dev,
+ "%s: load block: Other Type = 0x%02x\n",
+ __func__, block->type);
+ break;
+ }
+ }
+
+ for (i = 0; i < block->n_subblocks; i++) {
+ int rc = tasdevice_process_block(tasdevice, data + length,
+ dev_idx, blk_size - length);
+ if (rc < 0) {
+ dev_err(tasdevice->dev,
+ "%s: ERROR:%u %u sublock write error\n",
+ __func__, length, blk_size);
+ break;
+ }
+ length += (unsigned int)rc;
+ if (blk_size < length) {
+ dev_err(tasdevice->dev,
+ "%s: ERROR:%u %u out of memory\n",
+ __func__, length, blk_size);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int fw_parse_variable_header_git(struct tasdevice_priv *tas_dev,
+ const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ int i = strlen((char *)&buf[offset]);
+
+ i++;
+
+ if (offset + i > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += i;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "%s: ndev(%u) in dspbin dismatch ndev(%u) in DTS\n",
+ __func__, fw_hdr->ndev,
+ tas_dev->ndev);
+ offset = -1;
+ }
+
+out:
+ return offset;
+}
+
+static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ const unsigned char *buf = fmw->data;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ int i = strlen((char *)&buf[offset]);
+
+ i++;
+
+ if (offset + i > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += i;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDeviceFamily error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device_family = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device_family != 0) {
+ dev_err(tas_dev->dev, "ERROR:%s: not TAS device\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDevice error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_hdr->device = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE ||
+ fw_hdr->device == 6) {
+ dev_err(tas_dev->dev, "ERROR:%s: not support device %d\n",
+ __func__, fw_hdr->device);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->ndev = deviceNumber[fw_hdr->device];
+ if (fw_hdr->ndev != 1) {
+ dev_err(tas_dev->dev,
+ "%s: calbin must be 1, but currently ndev(%u)\n",
+ __func__, fw_hdr->ndev);
+ offset = -1;
+ }
+
+out:
+ return offset;
+}
+
+static inline void tas2781_clear_calfirmware(struct tasdevice_fw
+ *tas_fmw)
+{
+ int i = 0;
+ unsigned int n_blk = 0;
+
+ if (tas_fmw->calibrations) {
+ struct tasdevice_calibration *calibration;
+
+ for (i = 0; i < tas_fmw->n_calibrations; i++) {
+ calibration = &(tas_fmw->calibrations[i]);
+ if (calibration) {
+ struct tasdevice_data *im =
+ &(calibration->dev_data);
+
+ if (im->dev_blks) {
+ struct tasdev_blk *block;
+
+ for (n_blk = 0; n_blk < im->n_blk;
+ n_blk++) {
+ block = &(im->dev_blks[n_blk]);
+ kfree(block->data);
+ }
+ kfree(im->dev_blks);
+ }
+ }
+ }
+ kfree(tas_fmw->calibrations);
+ }
+ kfree(tas_fmw);
+}
+
+static int fw_parse_block_data(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnType error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->type = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >=
+ PPC_DRIVER_CRCCHK) {
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mbPChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_pchksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnPChkSum error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->pchksum = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mbYChkSumPresent error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->is_ychksum_present = data[offset];
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnYChkSum error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ block->ychksum = data[offset];
+ offset++;
+ } else {
+ block->is_pchksum_present = 0;
+ block->is_ychksum_present = 0;
+ }
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnCommands error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ block->n_cmds = SMS_HTONL(data[offset], data[offset + 1],
+ data[offset + 2], data[offset + 3]);
+ offset += 4;
+
+ n = block->n_cmds * 4;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev,
+ "%s: File Size(%lu) error offset = %d n = %d\n",
+ __func__, (unsigned long)fmw->size, offset, n);
+ offset = -1;
+ goto out;
+ }
+ block->data = kmemdup(&data[offset], n, GFP_KERNEL);
+ if (block->data == NULL) {
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+out:
+ return offset;
+}
+
+static int fw_parse_data(struct tasdevice_fw *tas_fmw,
+ struct tasdevice_data *img_data, const struct firmware *fmw,
+ int offset)
+{
+ const unsigned char *data = (unsigned char *)fmw->data;
+ int n = 0;
+ unsigned int n_block;
+
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mpName error\n", __func__);
+ n = -1;
+ goto out;
+ }
+ memcpy(img_data->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mpDescription error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_fmw->dev, "%s: mnBlocks error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ img_data->n_blk = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ img_data->dev_blks =
+ kcalloc(img_data->n_blk, sizeof(struct tasdev_blk),
+ GFP_KERNEL);
+ if (img_data->dev_blks == NULL) {
+ dev_err(tas_fmw->dev, "%s: FW memory failed!\n", __func__);
+ goto out;
+ }
+ for (n_block = 0; n_block < img_data->n_blk; n_block++) {
+ offset = fw_parse_block_data(tas_fmw,
+ &(img_data->dev_blks[n_block]), fmw, offset);
+ if (offset < 0) {
+ offset = -1;
+ goto out;
+ }
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_calibration_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ unsigned int n = 0;
+ unsigned int n_calibration = 0;
+ struct tasdevice_calibration *calibration = NULL;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnCalibrations error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_calibrations = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->n_calibrations != 1) {
+ dev_err(tas_dev->dev,
+ "%s: only support one calibraiton(%d)!\n",
+ __func__, tas_fmw->n_calibrations);
+ goto out;
+ }
+
+ tas_fmw->calibrations =
+ kcalloc(tas_fmw->n_calibrations,
+ sizeof(struct tasdevice_calibration), GFP_KERNEL);
+ if (tas_fmw->calibrations == NULL) {
+ dev_err(tas_dev->dev, "%s: mpCalibrations memory failed!\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ for (n_calibration = 0; n_calibration < tas_fmw->n_calibrations;
+ n_calibration++) {
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpCalibrations error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ calibration = &(tas_fmw->calibrations[n_calibration]);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += n;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnProgram error, offset = %d\n", __func__,
+ offset);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnConfiguration error, offset = %d\n",
+ __func__,
+ offset);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+
+out:
+
+ return offset;
+}
+
+static int fw_parse_program_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_prog *program;
+ unsigned char *buf = (unsigned char *)fmw->data;
+ int n_program = 0;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_programs = SMS_HTONS(buf[offset], buf[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->n_programs == 0) {
+ dev_err(tas_dev->dev, "%s: mnPrograms is null, maybe calbin\n",
+ __func__);
+ //Do not "offset = -1;", because of calbin
+ goto out;
+ }
+
+ tas_fmw->programs =
+ kcalloc(tas_fmw->n_programs, sizeof(struct tasdevice_prog),
+ GFP_KERNEL);
+ if (tas_fmw->programs == NULL) {
+ offset = -1;
+ goto out;
+ }
+ for (n_program = 0; n_program < tas_fmw->n_programs; n_program++) {
+ int n = 0;
+
+ program = &(tas_fmw->programs[n_program]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ n = strlen((char *)&buf[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += n;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnAppMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPDMI2SMode error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnISnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnVSnsPD error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPowerLDG error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw,
+ offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_configuration_data(
+ struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset)
+{
+ unsigned char *data = (unsigned char *)fmw->data;
+ int n;
+ unsigned int n_configs;
+ struct tasdevice_config *config;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ tas_fmw->n_configurations = SMS_HTONS(data[offset], data[offset + 1]);
+ offset += 2;
+
+ if (tas_fmw->n_configurations == 0) {
+ dev_err(tas_dev->dev, "%s: mnConfigurations is zero\n",
+ __func__);
+ //Do not "offset = -1;", because of calbin
+ goto out;
+ }
+ tas_fmw->configs =
+ kcalloc(tas_fmw->n_configurations,
+ sizeof(struct tasdevice_config), GFP_KERNEL);
+
+ for (n_configs = 0; n_configs < tas_fmw->n_configurations;
+ n_configs++) {
+ config =
+ &(tas_fmw->configs[n_configs]);
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ memcpy(config->name, &data[offset], 64);
+ offset += 64;
+
+ n = strlen((char *)&data[offset]);
+ n++;
+ if (offset + n > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDescription error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+
+ offset += n;
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev,
+ "%s: mnDevice_orientation error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mProgram error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnSamplingRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 1 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrc error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset++;
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPLLSrcRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+
+ if (offset + 2 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFsRate error\n",
+ __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 2;
+
+ offset = fw_parse_data(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ goto out;
+ }
+out:
+ return offset;
+}
+
+static int fw_parse_header(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+ const unsigned char *buf = (unsigned char *)fmw->data;
+ const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 };
+
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ if (memcmp(&buf[offset], magic_number, 4)) {
+ dev_err(tas_dev->dev, "%s: Magic number doesn't match",
+ __func__);
+ offset = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFWSize error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->fwsize = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: File Size error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ if (fw_fixed_hdr->fwsize != fmw->size) {
+ dev_err(tas_dev->dev, "File size not match, %lu %u",
+ (unsigned long)fmw->size, fw_fixed_hdr->fwsize);
+ offset = -1;
+ goto out;
+ }
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnChecksum error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnPPCVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->ppcver = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnFWVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnDriverVersion error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ fw_fixed_hdr->drv_ver = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (offset + 4 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mnTimeStamp error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 4;
+ if (offset + 64 > fmw->size) {
+ dev_err(tas_dev->dev, "%s: mpDDCName error\n", __func__);
+ offset = -1;
+ goto out;
+ }
+ offset += 64;
+
+ out:
+ return offset;
+}
+
+static const unsigned char crc8_lookup_table[CRC8_TABLE_SIZE] = {
+ 0x00, 0x4D, 0x9A, 0xD7, 0x79, 0x34, 0xE3, 0xAE,
+ 0xF2, 0xBF, 0x68, 0x25, 0x8B, 0xC6, 0x11, 0x5C,
+ 0xA9, 0xE4, 0x33, 0x7E, 0xD0, 0x9D, 0x4A, 0x07,
+ 0x5B, 0x16, 0xC1, 0x8C, 0x22, 0x6F, 0xB8, 0xF5,
+ 0x1F, 0x52, 0x85, 0xC8, 0x66, 0x2B, 0xFC, 0xB1,
+ 0xED, 0xA0, 0x77, 0x3A, 0x94, 0xD9, 0x0E, 0x43,
+ 0xB6, 0xFB, 0x2C, 0x61, 0xCF, 0x82, 0x55, 0x18,
+ 0x44, 0x09, 0xDE, 0x93, 0x3D, 0x70, 0xA7, 0xEA,
+ 0x3E, 0x73, 0xA4, 0xE9, 0x47, 0x0A, 0xDD, 0x90,
+ 0xCC, 0x81, 0x56, 0x1B, 0xB5, 0xF8, 0x2F, 0x62,
+ 0x97, 0xDA, 0x0D, 0x40, 0xEE, 0xA3, 0x74, 0x39,
+ 0x65, 0x28, 0xFF, 0xB2, 0x1C, 0x51, 0x86, 0xCB,
+ 0x21, 0x6C, 0xBB, 0xF6, 0x58, 0x15, 0xC2, 0x8F,
+ 0xD3, 0x9E, 0x49, 0x04, 0xAA, 0xE7, 0x30, 0x7D,
+ 0x88, 0xC5, 0x12, 0x5F, 0xF1, 0xBC, 0x6B, 0x26,
+ 0x7A, 0x37, 0xE0, 0xAD, 0x03, 0x4E, 0x99, 0xD4,
+ 0x7C, 0x31, 0xE6, 0xAB, 0x05, 0x48, 0x9F, 0xD2,
+ 0x8E, 0xC3, 0x14, 0x59, 0xF7, 0xBA, 0x6D, 0x20,
+ 0xD5, 0x98, 0x4F, 0x02, 0xAC, 0xE1, 0x36, 0x7B,
+ 0x27, 0x6A, 0xBD, 0xF0, 0x5E, 0x13, 0xC4, 0x89,
+ 0x63, 0x2E, 0xF9, 0xB4, 0x1A, 0x57, 0x80, 0xCD,
+ 0x91, 0xDC, 0x0B, 0x46, 0xE8, 0xA5, 0x72, 0x3F,
+ 0xCA, 0x87, 0x50, 0x1D, 0xB3, 0xFE, 0x29, 0x64,
+ 0x38, 0x75, 0xA2, 0xEF, 0x41, 0x0C, 0xDB, 0x96,
+ 0x42, 0x0F, 0xD8, 0x95, 0x3B, 0x76, 0xA1, 0xEC,
+ 0xB0, 0xFD, 0x2A, 0x67, 0xC9, 0x84, 0x53, 0x1E,
+ 0xEB, 0xA6, 0x71, 0x3C, 0x92, 0xDF, 0x08, 0x45,
+ 0x19, 0x54, 0x83, 0xCE, 0x60, 0x2D, 0xFA, 0xB7,
+ 0x5D, 0x10, 0xC7, 0x8A, 0x24, 0x69, 0xBE, 0xF3,
+ 0xAF, 0xE2, 0x35, 0x78, 0xD6, 0x9B, 0x4C, 0x01,
+ 0xF4, 0xB9, 0x6E, 0x23, 0x8D, 0xC0, 0x17, 0x5A,
+ 0x06, 0x4B, 0x9C, 0xD1, 0x7F, 0x32, 0xE5, 0xA8
+};
+
+static int check_inpage_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret = 0;
+
+ if (book == TAS2781_YRAM_BOOK1) {
+ if (page == TAS2781_YRAM1_PAGE) {
+ if (reg >= TAS2781_YRAM1_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else if ((reg + len) > TAS2781_YRAM1_START_REG) {
+ cd->offset = TAS2781_YRAM1_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM1_START_REG - reg);
+ ret = 1;
+ } else
+ ret = 0;
+ } else if (page == TAS2781_YRAM3_PAGE) {
+ if (reg > TAS2781_YRAM3_END_REG) {
+ ret = 0;
+ } else if (reg >= TAS2781_YRAM3_START_REG) {
+ if ((reg + len) > TAS2781_YRAM3_END_REG) {
+ cd->offset = reg;
+ cd->len =
+ TAS2781_YRAM3_END_REG - reg + 1;
+ ret = 1;
+ } else {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ }
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM3_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM3_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM3_START_REG - reg);
+ ret = 1;
+ }
+ }
+ }
+ } else if (book ==
+ TAS2781_YRAM_BOOK2) {
+ if (page == TAS2781_YRAM5_PAGE) {
+ if (reg > TAS2781_YRAM5_END_REG) {
+ ret = 0;
+ } else if (reg >= TAS2781_YRAM5_START_REG) {
+ if ((reg + len) > TAS2781_YRAM5_END_REG) {
+ cd->offset = reg;
+ cd->len =
+ TAS2781_YRAM5_END_REG - reg + 1;
+ ret = 1;
+ } else {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ }
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM5_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM5_START_REG;
+ cd->len =
+ len - (TAS2781_YRAM5_START_REG - reg);
+ ret = 1;
+ }
+ }
+ }
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static int check_inblock_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret = 0;
+
+ if (book == TAS2781_YRAM_BOOK1) {
+ if (page < TAS2781_YRAM2_START_PAGE)
+ ret = 0;
+ else if (page <= TAS2781_YRAM2_END_PAGE) {
+ if (reg > TAS2781_YRAM2_END_REG)
+ ret = 0;
+ else if (reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else {
+ if ((reg + (len-1)) <
+ TAS2781_YRAM2_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM2_START_REG;
+ cd->len =
+ reg + len - TAS2781_YRAM2_START_REG;
+ ret = 1;
+ }
+ }
+ } else
+ ret = 0;
+ } else if (book ==
+ TAS2781_YRAM_BOOK2) {
+ if (page < TAS2781_YRAM4_START_PAGE)
+ ret = 0;
+ else if (page <= TAS2781_YRAM4_END_PAGE) {
+ if (reg > TAS2781_YRAM2_END_REG)
+ ret = 0;
+ else if (reg >= TAS2781_YRAM2_START_REG) {
+ cd->offset = reg;
+ cd->len = len;
+ ret = 1;
+ } else {
+ if ((reg + (len-1))
+ < TAS2781_YRAM2_START_REG)
+ ret = 0;
+ else {
+ cd->offset =
+ TAS2781_YRAM2_START_REG;
+ cd->len =
+ reg + len - TAS2781_YRAM2_START_REG;
+ ret = 1;
+ }
+ }
+ } else
+ ret = 0;
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static int check_yram(struct tas_crc *cd, unsigned char book,
+ unsigned char page, unsigned char reg, unsigned char len)
+{
+ int ret = 0;
+
+ ret = check_inpage_yram(cd, book, page, reg, len);
+ if (ret == 0)
+ ret = check_inblock_yram(cd, book,
+ page, reg, len);
+
+ return ret;
+}
+
+static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
+ enum channel chl, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned char val)
+{
+ int ret = 0;
+ struct tas_crc crc_data;
+ unsigned int nData1 = 0;
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG) + 4))) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ ret = check_yram(&crc_data, book, page, reg, 1);
+ if (ret == 1) {
+ ret = tasdevice_dev_read(tasdevice, chl,
+ TASDEVICE_REG(book, page, reg), &nData1);
+ if (ret < 0)
+ goto end;
+
+ if (nData1 != val) {
+ dev_err(tasdevice->dev,
+ "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n",
+ book, page, reg,
+ val, nData1);
+ ret = -EAGAIN;
+ tasdevice->tasdevice[chl].err_code |=
+ ERROR_YRAM_CRCCHK;
+ ret = -EAGAIN;
+ goto end;
+ }
+
+ ret = crc8(crc8_lookup_table, &val, 1, 0);
+ }
+
+end:
+ return ret;
+}
+
+static int do_multireg_checksum(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned char book, unsigned char page,
+ unsigned char reg, unsigned int len)
+{
+ int ret = 0, i = 0;
+ unsigned char crc_chksum = 0;
+ unsigned char nBuf1[128] = {0};
+ struct tas_crc crc_data;
+
+ if ((reg + len-1) > 127) {
+ ret = -EINVAL;
+ dev_err(tasdevice->dev, "firmware error\n");
+ goto end;
+ }
+
+ if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG))
+ && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG))
+ && (len == 4)) {
+ /*DSP swap command, pass */
+ ret = 0;
+ goto end;
+ }
+
+ ret = check_yram(&crc_data, book, page, reg, len);
+ dev_info(tasdevice->dev,
+ "isYRAM: Bk 0x%02x, Pg 0x%02x, Rg 0x%02x\n",
+ book, page, reg);
+ dev_info(tasdevice->dev,
+ "isYRAM: crc_data.mnLen 0x%x, len 0x%x, nResult %d\n",
+ crc_data.len, len, ret);
+ dev_info(tasdevice->dev, "crc_data.mnOffset %x\n", crc_data.offset);
+ if (ret == 1) {
+ if (len == 1) {
+ dev_err(tasdevice->dev, "firmware error\n");
+ ret = -EINVAL;
+ goto end;
+ } else {
+ ret = tasdevice_dev_bulk_read(tasdevice, chn,
+ TASDEVICE_REG(book, page, crc_data.offset),
+ nBuf1, crc_data.len);
+ if (ret < 0)
+ goto end;
+
+ for (i = 0; i < crc_data.len; i++) {
+ if ((book == TASDEVICE_BOOK_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && (page == TASDEVICE_PAGE_ID(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ >= TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG))
+ && ((i + crc_data.offset)
+ <= (TASDEVICE_PAGE_REG(
+ TAS2781_SA_COEFF_SWAP_REG)
+ + 4))) {
+ /*DSP swap command, bypass */
+ continue;
+ } else
+ crc_chksum +=
+ crc8(crc8_lookup_table,
+ &nBuf1[i], 1, 0);
+ }
+
+ ret = crc_chksum;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static int tasdevice_load_block(struct tasdevice_priv *tas_dev,
+ struct tasdev_blk *block)
+{
+ int ret = 0;
+ unsigned int n_cmds = 0;
+ unsigned char book = 0;
+ unsigned char page = 0;
+ unsigned char offset = 0;
+ unsigned char val = 0;
+ unsigned int len = 0;
+ unsigned int sleep_dur = 0;
+ unsigned char crc_chksum = 0;
+ unsigned int n_value = 0;
+ int n_retry = 6;
+ unsigned char *data = block->data;
+ int chn = 0, chnend = 0;
+
+ dev_info(tas_dev->dev,
+ "load block: Type = %d, commands = %d\n",
+ block->type, block->n_cmds);
+ switch (block->type) {
+ case MAIN_ALL_DEVICES:
+ chn = 0;
+ chnend = tas_dev->ndev;
+ break;
+ case MAIN_DEVICE_A:
+ case COEFF_DEVICE_A:
+ case PRE_DEVICE_A:
+ chn = 0;
+ chnend = 1;
+ break;
+ case MAIN_DEVICE_B:
+ case COEFF_DEVICE_B:
+ case PRE_DEVICE_B:
+ chn = 1;
+ chnend = 2;
+ break;
+ case MAIN_DEVICE_C:
+ case COEFF_DEVICE_C:
+ case PRE_DEVICE_C:
+ chn = 2;
+ chnend = 3;
+ break;
+ case MAIN_DEVICE_D:
+ case COEFF_DEVICE_D:
+ case PRE_DEVICE_D:
+ chn = 3;
+ chnend = 4;
+ break;
+ default:
+ dev_info(tas_dev->dev,
+ "load block: Other Type = 0x%02x\n",
+ block->type);
+ break;
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_dev->tasdevice[chn].is_loading == false)
+ continue;
+start:
+ if (block->is_pchksum_present) {
+ ret = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_I2CChecksum, 0);
+ if (ret < 0)
+ goto end;
+ }
+
+ if (block->is_ychksum_present)
+ crc_chksum = 0;
+
+ n_cmds = 0;
+
+ while (n_cmds < block->n_cmds) {
+ data = block->data + n_cmds * 4;
+
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ val = data[3];
+
+ n_cmds++;
+
+ if (offset <= 0x7F) {
+ ret = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_REG(book, page, offset),
+ val);
+ if (ret < 0)
+ goto end;
+ if (block->is_ychksum_present) {
+ ret = do_singlereg_checksum(tas_dev,
+ chn, book, page, offset,
+ val);
+ if (ret < 0)
+ goto check;
+ crc_chksum += (unsigned char)ret;
+ }
+ continue;
+ }
+ if (offset == 0x81) {
+ sleep_dur = (book << 8) + page;
+ msleep(sleep_dur);
+ continue;
+ }
+ if (offset == 0x85) {
+ data += 4;
+ len = (book << 8) + page;
+ book = data[0];
+ page = data[1];
+ offset = data[2];
+ if (len > 1) {
+ ret = tasdevice_dev_bulk_write(
+ tas_dev, chn,
+ TASDEVICE_REG(book, page,
+ offset), data + 3, len);
+ if (ret < 0)
+ goto end;
+ if (!block->is_ychksum_present)
+ goto next;
+ ret = do_multireg_checksum(
+ tas_dev, chn, book,
+ page, offset,
+ len);
+ if (ret < 0)
+ goto check;
+ crc_chksum +=
+ (unsigned char)ret;
+ } else {
+ ret = tasdevice_dev_write(tas_dev,
+ chn,
+ TASDEVICE_REG(book, page,
+ offset),
+ data[3]);
+ if (ret < 0)
+ goto end;
+ if (!block->is_ychksum_present)
+ goto next;
+ ret = do_singlereg_checksum(
+ tas_dev, chn, book,
+ page, offset,
+ data[3]);
+ if (ret < 0)
+ goto check;
+ crc_chksum +=
+ (unsigned char)ret;
+ }
+next:
+ n_cmds++;
+ if (len >= 2)
+ n_cmds += ((len - 2) / 4) + 1;
+ }
+ }
+ if (block->is_pchksum_present) {
+ ret = tasdevice_dev_read(tas_dev, chn,
+ TASDEVICE_I2CChecksum, &n_value);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "%s: Channel %d\n",
+ __func__, chn);
+ goto check;
+ }
+ if ((n_value & 0xff) != block->pchksum) {
+ dev_err(tas_dev->dev,
+ "Block PChkSum Channel %d ", chn);
+ dev_err(tas_dev->dev,
+ "FW = 0x%x, Reg = 0x%x\n",
+ block->pchksum,
+ (n_value & 0xff));
+ ret = -EAGAIN;
+ tas_dev->tasdevice[chn].err_code |=
+ ERROR_PRAM_CRCCHK;
+ goto check;
+ }
+ ret = 0;
+ tas_dev->tasdevice[chn].err_code &=
+ ~ERROR_PRAM_CRCCHK;
+ dev_info(tas_dev->dev, "Block[0x%02x] PChkSum match\n",
+ block->type);
+ }
+
+ if (block->is_ychksum_present) {
+ //TBD, open it when FW ready
+ dev_err(tas_dev->dev,
+ "Block YChkSum: FW = 0x%x, YCRC = 0x%x\n",
+ block->ychksum, crc_chksum);
+
+ tas_dev->tasdevice[chn].err_code &=
+ ~ERROR_YRAM_CRCCHK;
+ ret = 0;
+ dev_info(tas_dev->dev,
+ "Block[0x%x] YChkSum match\n", block->type);
+ }
+check:
+ if (ret == -EAGAIN) {
+ n_retry--;
+ if (n_retry > 0)
+ goto start;
+ else {
+ if ((block->type == MAIN_ALL_DEVICES)
+ || (block->type == MAIN_DEVICE_A)
+ || (block->type == MAIN_DEVICE_B)
+ || (block->type == MAIN_DEVICE_C)
+ || (block->type == MAIN_DEVICE_D))
+ tas_dev->tasdevice[chn].cur_prog = -1;
+ else
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ n_retry = 6;
+ }
+ }
+ }
+end:
+ if (ret < 0)
+ dev_err(tas_dev->dev, "Block (%d) load error\n", block->type);
+
+ return ret;
+}
+
+
+static int tasdevice_load_data(struct tasdevice_priv *tas_dev,
+ struct tasdevice_data *dev_data)
+{
+ int ret = 0;
+ unsigned int n_block = 0;
+ struct tasdev_blk *block = NULL;
+
+ dev_info(tas_dev->dev, "%s: TAS2781 load data: %s, Blocks = %d\n",
+ __func__, dev_data->name, dev_data->n_blk);
+
+ for (n_block = 0; n_block < dev_data->n_blk; n_block++) {
+ block = &(dev_data->dev_blks[n_block]);
+ ret = tas_dev->tasdevice_load_block(tas_dev, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static int tasdevice_load_calibrated_data(
+ struct tasdevice_priv *tas_dev, struct tasdevice_data *dev_data)
+{
+ int ret = 0;
+ unsigned int n_block = 0;
+ struct tasdev_blk *block = NULL;
+
+ dev_info(tas_dev->dev, "%s: TAS2781 load data: %s, Blocks = %d\n",
+ __func__, dev_data->name, dev_data->n_blk);
+
+ for (n_block = 0; n_block < dev_data->n_blk; n_block++) {
+ block = &(dev_data->dev_blks[n_block]);
+ ret = tasdevice_load_block(tas_dev, block);
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+int tas2781_load_calibration(void *context,
+ char *file_name, enum channel i)
+{
+ int ret = 0, offset = 0;
+ struct firmware fmw;
+ const struct firmware *fw_entry = NULL;
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)context;
+ struct tasdevice *tasdev = &(tas_dev->tasdevice[i]);
+ struct tasdevice_fw *tas_fmw = NULL;
+
+ ret = request_firmware(&fw_entry, file_name, tas_dev->dev);
+ if (!ret) {
+ if (!fw_entry->size) {
+ dev_err(tas_dev->dev,
+ "%s: file read error: size = %lu\n",
+ __func__, (unsigned long)fw_entry->size);
+ goto out;
+ }
+ fmw.size = fw_entry->size;
+ fmw.data = fw_entry->data;
+ dev_info(tas_dev->dev,
+ "%s: file = %s, file size %zd\n",
+ __func__, file_name, fw_entry->size);
+ } else {
+ dev_info(tas_dev->dev,
+ "%s: Request firmware %s failed\n",
+ __func__, file_name);
+ goto out;
+ }
+
+ tas_fmw = tasdev->cali_data_fmw = kcalloc(1,
+ sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (tasdev->cali_data_fmw == NULL) {
+ dev_err(tas_dev->dev, "%s: FW memory failed!\n", __func__);
+ ret = -1;
+ goto out;
+ }
+ tas_fmw->dev = tas_dev->dev;
+ offset = fw_parse_header(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_header EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_variable_hdr_cal(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev,
+ "%s: fw_parse_variable_header_cal EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_program_data(tas_dev, tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_program_data EXIT!\n",
+ __func__);
+ goto out;
+ }
+ offset = fw_parse_configuration_data(tas_dev, tas_fmw, &fmw,
+ offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev,
+ "%s: fw_parse_configuration_data EXIT!\n", __func__);
+ goto out;
+ }
+ offset = fw_parse_calibration_data(tas_dev,
+ tas_fmw, &fmw, offset);
+ if (offset == -1) {
+ dev_err(tas_dev->dev, "%s: fw_parse_calibration_data EXIT!\n",
+ __func__);
+ goto out;
+ }
+ tasdev->is_calibrated_data_loaded = true;
+out:
+ if (fw_entry) {
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+ }
+ return ret;
+}
+
+int tasdevice_dspfw_ready(const struct firmware *fmw, void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice_fw *tas_fmw = NULL;
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = NULL;
+ int offset = 0, ret = 0;
+
+ if (!fmw || !fmw->data) {
+ dev_err(tas_dev->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_dev->coef_binaryname);
+ ret = -1;
+ goto out;
+ }
+
+ tas_dev->fmw = kcalloc(1,
+ sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (tas_dev->fmw == NULL) {
+ ret = -1;
+ goto out;
+ }
+ tas_fmw = tas_dev->fmw;
+ tas_fmw->dev = tas_dev->dev;
+ offset = fw_parse_header(tas_dev, tas_fmw, fmw, offset);
+
+ if (offset == -1)
+ goto out;
+ fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr);
+ switch (fw_fixed_hdr->drv_ver) {
+ case 0x301:
+ case 0x302:
+ case 0x502:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ break;
+ case 0x202:
+ case 0x400:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ if (fw_fixed_hdr->drv_ver == 0x100) {
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION) {
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data_kernel;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data_kernel;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ tas_dev->fw_parse_calibration_data = NULL;
+ } else {
+ switch (fw_fixed_hdr->ppcver) {
+ case 0x00:
+ tas_dev->fw_parse_variable_header =
+ fw_parse_variable_header_git;
+ tas_dev->fw_parse_program_data =
+ fw_parse_program_data;
+ tas_dev->fw_parse_configuration_data =
+ fw_parse_configuration_data;
+ tas_dev->fw_parse_calibration_data =
+ fw_parse_calibration_data;
+ tas_dev->tasdevice_load_block =
+ tasdevice_load_block;
+ break;
+ default:
+ dev_err(tas_dev->dev,
+ "%s: PPCVersion must be 0x0 or 0x%02x",
+ __func__, PPC3_VERSION);
+ dev_err(tas_dev->dev, " Current:0x%02x\n",
+ fw_fixed_hdr->ppcver);
+ offset = -1;
+ break;
+ }
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: DriverVersion must be 0x0, 0x230 or above 0x230 ",
+ __func__);
+ dev_err(tas_dev->dev, "current is 0x%02x\n",
+ fw_fixed_hdr->drv_ver);
+ offset = -1;
+ }
+ break;
+ }
+
+ offset = tas_dev->fw_parse_variable_header(tas_dev, fmw, offset);
+ if (offset == -1)
+ goto out;
+
+ offset = tas_dev->fw_parse_program_data(tas_dev, tas_fmw, fmw,
+ offset);
+ if (offset < 0) {
+ ret = -1;
+ goto out;
+ }
+ offset = tas_dev->fw_parse_configuration_data(tas_dev,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ ret = -1;
+
+out:
+ return ret;
+}
+
+void tasdevice_calbin_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice *tasdev = NULL;
+ int i = 0;
+
+ if (tas_dev) {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ tasdev = &(tas_dev->tasdevice[i]);
+ if (tasdev->cali_data_fmw) {
+ tas2781_clear_calfirmware(
+ tasdev->cali_data_fmw);
+ tasdev->cali_data_fmw = NULL;
+ }
+ }
+ }
+}
+
+void tasdevice_dsp_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ int i;
+ struct tasdev_blk *blk;
+ unsigned int n_blk;
+
+ if (tas_dev->fmw) {
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+
+ if (tas_fmw->programs) {
+ struct tasdevice_prog *program;
+
+ for (i = 0; i < tas_fmw->n_programs; i++) {
+ program = &(tas_fmw->programs[i]);
+ if (program) {
+ struct tasdevice_data
+ *im =
+ &(program->dev_data);
+
+ if (!im->dev_blks)
+ continue;
+ for (n_blk = 0; n_blk < im->n_blk;
+ n_blk++) {
+ blk =
+ &(im->dev_blks[n_blk]);
+ kfree(blk->data);
+ }
+ kfree(im->dev_blks);
+ }
+ }
+ kfree(tas_fmw->programs);
+ }
+
+ if (tas_fmw->configs) {
+ struct tasdevice_config *config;
+
+ for (i = 0; i < tas_fmw->n_configurations; i++) {
+ config = &(tas_fmw->configs[i]);
+ if (config) {
+ struct tasdevice_data
+ *im = &(config->dev_data);
+
+ if (!im->dev_blks)
+ continue;
+ for (n_blk = 0; n_blk < im->n_blk;
+ n_blk++) {
+ blk =
+ &(im->dev_blks[n_blk]);
+ kfree(blk->data);
+ }
+ kfree(im->dev_blks);
+ }
+ }
+ kfree(tas_fmw->configs);
+ }
+ kfree(tas_fmw);
+ tas_dev->fmw = NULL;
+ }
+}
+
+static int tas2781_set_calibration(void *context, enum channel i,
+ int n_calibration)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ int ret = 0;
+ struct tasdevice *tasdevice = &(tas_dev->tasdevice[i]);
+ struct tasdevice_fw *cal_fmw = tasdevice->cali_data_fmw;
+
+ if ((!tas_dev->fmw->programs)
+ || (!tas_dev->fmw->configs)) {
+ dev_err(tas_dev->dev, "%s, Firmware not loaded\n\r", __func__);
+ ret = 0;
+ goto out;
+ }
+
+ if (n_calibration == 0xFF || (n_calibration == 0x100
+ && tasdevice->is_calibrated_data_loaded == false)) {
+ if (cal_fmw) {
+ tasdevice->is_calibrated_data_loaded = false;
+ tas2781_clear_calfirmware(cal_fmw);
+ cal_fmw = NULL;
+ }
+
+ scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin",
+ tas_dev->dev_name, tas_dev->tasdevice[i].dev_addr);
+ ret = tas2781_load_calibration(tas_dev,
+ tas_dev->cal_binaryname[i], i);
+ if (ret != 0) {
+ dev_err(tas_dev->dev,
+ "%s: load %s error, no-side effect\n",
+ __func__, tas_dev->cal_binaryname[i]);
+ ret = 0;
+ }
+ }
+ tasdevice->is_loading = true;
+ tasdevice->is_loaderr = false;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration *calibration =
+ cal_fmw->calibrations;
+
+ if (calibration)
+ tasdevice_load_calibrated_data(tas_dev,
+ &(calibration->dev_data));
+ } else
+ dev_err(tas_dev->dev,
+ "%s: No calibrated data for device %d\n", __func__, i);
+
+out:
+ return ret;
+}
+
+int tasdevice_select_tuningprm_cfg(void *context, int prm_no,
+ int cfg_no, int rca_conf_no)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ struct tasdevice_config *conf = NULL;
+ struct tasdevice_prog *program = NULL;
+ int i = 0;
+ int status = 0;
+ int prog_status = 0;
+
+ if (tas_fmw == NULL) {
+ dev_err(tas_dev->dev, "%s: Firmware is NULL\n", __func__);
+ goto out;
+ }
+
+ if (cfg_no >= tas_fmw->n_configurations) {
+ dev_err(tas_dev->dev,
+ "%s: cfg(%d) is not in range of conf %u\n",
+ __func__, cfg_no, tas_fmw->n_configurations);
+ goto out;
+ }
+
+ if (prm_no >= tas_fmw->n_programs) {
+ dev_err(tas_dev->dev,
+ "%s: prm(%d) is not in range of Programs %u\n",
+ __func__, prm_no, tas_fmw->n_programs);
+ goto out;
+ }
+
+ if (rca_conf_no > rca->ncfgs || rca_conf_no <= 0 ||
+ cfg_info == NULL) {
+ dev_err(tas_dev->dev,
+ "conf_no:%d should be in range from 0 to %u\n",
+ rca_conf_no, rca->ncfgs-1);
+ goto out;
+ } else {
+ dev_info(tas_dev->dev, "%s: rca_profile_conf_id = %d\n",
+ __func__, rca_conf_no);
+ }
+
+ conf = &(tas_fmw->configs[cfg_no]);
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (cfg_info[rca_conf_no]->active_dev & (1 << i)) {
+ if (tas_dev->tasdevice[i].cur_prog != prm_no) {
+ tas_dev->tasdevice[i].cur_conf = -1;
+ tas_dev->tasdevice[i].is_loading = true;
+ prog_status++;
+ }
+ } else
+ tas_dev->tasdevice[i].is_loading = false;
+ tas_dev->tasdevice[i].is_loaderr = false;
+ }
+
+ if (prog_status) {
+ program = &(tas_fmw->programs[prm_no]);
+ tasdevice_load_data(tas_dev, &(program->dev_data));
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].is_loaderr == true)
+ continue;
+ else if (tas_dev->tasdevice[i].is_loaderr == false
+ && tas_dev->tasdevice[i].is_loading == true) {
+ struct tasdevice_fw *cal_fmw =
+ tas_dev->tasdevice[i].cali_data_fmw;
+
+ if (cal_fmw) {
+ struct tasdevice_calibration
+ *calibration =
+ cal_fmw->calibrations;
+
+ if (calibration)
+ tasdevice_load_calibrated_data(
+ tas_dev,
+ &(calibration->dev_data));
+ }
+ tas_dev->tasdevice[i].cur_prog = prm_no;
+ }
+ }
+ }
+
+ if (tas_dev->is_glb_calibrated_data_loaded == false) {
+ for (i = 0; i < tas_dev->ndev; i++)
+ tas2781_set_calibration(tas_dev, i, 0x100);
+ tas_dev->is_glb_calibrated_data_loaded = true;
+ /* No wise to reload calibrationdata everytime,
+ * this code will work once even if calibrated
+ * data still failed to be got
+ */
+ }
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ dev_info(tas_dev->dev, "%s,fun %d,%d,%d\n", __func__,
+ tas_dev->tasdevice[i].cur_conf,
+ cfg_info[rca_conf_no]->active_dev,
+ tas_dev->tasdevice[i].is_loaderr);
+ if (tas_dev->tasdevice[i].cur_conf != cfg_no
+ && (cfg_info[rca_conf_no]->active_dev & (1 << i))
+ && (tas_dev->tasdevice[i].is_loaderr == false)) {
+ status++;
+ tas_dev->tasdevice[i].is_loading = true;
+ } else
+ tas_dev->tasdevice[i].is_loading = false;
+ }
+
+ if (status) {
+ status = 0;
+ tasdevice_load_data(tas_dev, &(conf->dev_data));
+ for (i = 0; i < tas_dev->ndev; i++) {
+ if (tas_dev->tasdevice[i].is_loaderr == true) {
+ status |= 1 << (i + 4);
+ continue;
+ } else if (tas_dev->tasdevice[i].is_loaderr == false
+ && tas_dev->tasdevice[i].is_loading == true)
+ tas_dev->tasdevice[i].cur_conf = cfg_no;
+ }
+ } else {
+ dev_err(tas_dev->dev,
+ "%s: No device is in active in conf %d\n",
+ __func__, rca_conf_no);
+ }
+
+ status |= cfg_info[rca_conf_no]->active_dev;
+ dev_info(tas_dev->dev, "%s: DSP mode: load status is %08x\n",
+ __func__, status);
+out:
+ return prog_status;
+}
diff --git a/sound/soc/codecs/tas2781-dsp.h b/sound/soc/codecs/tas2781-dsp.h
new file mode 100644
index 000000000000..639e84e0b5b4
--- /dev/null
+++ b/sound/soc/codecs/tas2781-dsp.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+ *
+ * Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2781 driver implements a flexible and configurable algo coff setting
+ * for one, two, even multiple TAS2781 chips.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#ifndef __TASDEVICE_DSP_H__
+#define __TASDEVICE_DSP_H__
+
+#define MAIN_ALL_DEVICES 0x0d
+#define MAIN_DEVICE_A 0x01
+#define MAIN_DEVICE_B 0x08
+#define MAIN_DEVICE_C 0x10
+#define MAIN_DEVICE_D 0x14
+#define COEFF_DEVICE_A 0x03
+#define COEFF_DEVICE_B 0x0a
+#define COEFF_DEVICE_C 0x11
+#define COEFF_DEVICE_D 0x15
+#define PRE_DEVICE_A 0x04
+#define PRE_DEVICE_B 0x0b
+#define PRE_DEVICE_C 0x12
+#define PRE_DEVICE_D 0x16
+
+#define PPC3_VERSION 0x4100
+#define RCA_CONFIGID_BYPASS_ALL 0
+#define TASDEVICE_DEVICE_SUM 8
+#define TASDEVICE_CONFIG_SUM 64
+
+enum channel {
+ top_left_Chn,
+ top_right_chn,
+ bottom_left_Chn,
+ bottom_right_chn,
+ max_chn,
+};
+
+enum tasdevice_dsp_dev_idx {
+ TASDEVICE_DSP_TAS_2555 = 0,
+ TASDEVICE_DSP_TAS_2555_STEREO,
+ TASDEVICE_DSP_TAS_2557_MONO,
+ TASDEVICE_DSP_TAS_2557_DUAL_MONO,
+ TASDEVICE_DSP_TAS_2559,
+ TASDEVICE_DSP_TAS_2563,
+ TASDEVICE_DSP_TAS_2563_DUAL_MONO = 7,
+ TASDEVICE_DSP_TAS_2563_QUAD,
+ TASDEVICE_DSP_TAS_2563_21,
+ TASDEVICE_DSP_TAS_2781,
+ TASDEVICE_DSP_TAS_2781_DUAL_MONO,
+ TASDEVICE_DSP_TAS_2781_21,
+ TASDEVICE_DSP_TAS_2781_QUAD,
+ TASDEVICE_DSP_TAS_MAX_DEVICE
+};
+
+struct tasdevice_fw_fixed_hdr {
+ unsigned int fwsize;
+ unsigned int ppcver;
+ unsigned int drv_ver;
+};
+
+struct tasdevice_dspfw_hdr {
+ struct tasdevice_fw_fixed_hdr fixed_hdr;
+ unsigned short device_family;
+ unsigned short device;
+ unsigned char ndev;
+};
+
+struct tasdev_blk {
+ unsigned int type;
+ unsigned char is_pchksum_present;
+ unsigned char pchksum;
+ unsigned char is_ychksum_present;
+ unsigned char ychksum;
+ unsigned int n_cmds;
+ unsigned int blk_size;
+ unsigned int n_subblocks;
+ unsigned char *data;
+};
+
+struct tasdevice_data {
+ char name[64];
+ unsigned int n_blk;
+ struct tasdev_blk *dev_blks;
+};
+struct tasdevice_prog {
+ unsigned int prog_size;
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_config {
+ unsigned int cfg_size;
+ char name[64];
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_calibration {
+ struct tasdevice_data dev_data;
+};
+
+struct tasdevice_fw {
+ struct tasdevice_dspfw_hdr fw_hdr;
+ unsigned short n_programs;
+ struct tasdevice_prog *programs;
+ unsigned short n_configurations;
+ struct tasdevice_config *configs;
+ unsigned short n_calibrations;
+ struct tasdevice_calibration *calibrations;
+ struct device *dev;
+};
+
+enum tasdevice_dsp_fw_state {
+ TASDEVICE_DSP_FW_NONE = 0,
+ TASDEVICE_DSP_FW_PENDING,
+ TASDEVICE_DSP_FW_FAIL,
+ TASDEVICE_DSP_FW_ALL_OK,
+};
+
+enum tasdevice_bin_blk_type {
+ TASDEVICE_BIN_BLK_COEFF = 1,
+ TASDEVICE_BIN_BLK_POST_POWER_UP,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP,
+ TASDEVICE_BIN_BLK_POST_SHUTDOWN
+};
+
+struct tasdevice_rca_hdr {
+ unsigned int img_sz;
+ unsigned int checksum;
+ unsigned int binary_version_num;
+ unsigned int drv_fw_version;
+ unsigned char plat_type;
+ unsigned char dev_family;
+ unsigned char reserve;
+ unsigned char ndev;
+ unsigned char devs[TASDEVICE_DEVICE_SUM];
+ unsigned int nconfig;
+ unsigned int config_size[TASDEVICE_CONFIG_SUM];
+};
+
+struct tasdev_blk_data {
+ unsigned char dev_idx;
+ unsigned char block_type;
+ unsigned short yram_checksum;
+ unsigned int block_size;
+ unsigned int n_subblks;
+ unsigned char *regdata;
+};
+
+struct tasdevice_config_info {
+ unsigned int nblocks;
+ unsigned int real_nblocks;
+ unsigned char active_dev;
+ struct tasdev_blk_data **blk_data;
+};
+
+struct tasdevice_rca {
+ struct tasdevice_rca_hdr fw_hdr;
+ int ncfgs;
+ struct tasdevice_config_info **cfg_info;
+ int profile_cfg_id;
+};
+
+int tasdevice_dspfw_ready(const struct firmware *fmw, void *context);
+void tasdevice_dsp_remove(void *context);
+void tasdevice_calbin_remove(void *context);
+int tas2781_load_calibration(void *tas_dev, char *filename,
+ enum channel i);
+int tasdevice_select_tuningprm_cfg(void *context, int prm,
+ int cfg_no, int rca_conf_no);
+
+#endif
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
new file mode 100644
index 000000000000..2b2b8367044f
--- /dev/null
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -0,0 +1,1913 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+ *
+ * Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2781 driver implements a flexible and configurable algo coff setting
+ * for one, two, even multiple TAS2781 chips.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/of_gpio.h>
+#include <linux/interrupt.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "tas2781.h"
+
+/* max. length of the alsa mixer control name */
+#define MAX_CONTROL_NAME (48)
+#define TASDEVICE_CLK_DIR_IN (0)
+#define TASDEVICE_CLK_DIR_OUT (1)
+
+#define TASDEVICE_IRQ_DET_TIMEOUT (30000)
+#define TASDEVICE_IRQ_DET_CNT_LIMIT (500)
+#define TAS2781_REG_INT_LTCH0 TASDEVICE_REG(0X0, 0x0, 0x49)
+#define TAS2781_REG_INT_LTCH1 TASDEVICE_REG(0X0, 0x0, 0x4A)
+#define TAS2781_REG_INT_LTCH1_0 TASDEVICE_REG(0X0, 0x0, 0x4B)
+#define TAS2781_REG_INT_LTCH2 TASDEVICE_REG(0X0, 0x0, 0x4F)
+#define TAS2781_REG_INT_LTCH3 TASDEVICE_REG(0X0, 0x0, 0x50)
+#define TAS2781_REG_INT_LTCH4 TASDEVICE_REG(0X0, 0x0, 0x51)
+
+const char *blocktype[5] = {
+ "COEFF",
+ "POST_POWER_UP",
+ "PRE_SHUTDOWN",
+ "PRE_POWER_UP",
+ "POST_SHUTDOWN"
+};
+
+static const char * const dts_dev_addr_tag[] = {
+ "topleft-channel",
+ "topright-channel",
+ "bottomleft-channel",
+ "bottomright-channel"
+};
+
+static const struct i2c_device_id tasdevice_id[] = {
+ { "tas2781", TAS2781 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, tasdevice_id);
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static const struct of_device_id tasdevice_of_match[] = {
+ { .compatible = "ti,tas2781" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, tasdevice_of_match);
+
+int tasdevice_process_block(void *context,
+ unsigned char *data, unsigned char dev_idx, int sublocksize)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *)context;
+ unsigned char subblk_typ = data[1];
+ int subblk_offset = 2;
+ int chn = 0, chnend = 0;
+ int rc = 0;
+ int blktyp = dev_idx & 0xC0, idx = dev_idx & 0x3F;
+ bool is_err = false;
+
+ if (idx) {
+ chn = idx-1;
+ chnend = idx;
+ } else {
+ if (tas_dev->set_global_mode) {
+ chn = tas_dev->ndev;
+ chnend = tas_dev->ndev + 1;
+ } else {
+ chn = 0;
+ chnend = tas_dev->ndev;
+ }
+ }
+
+ for (; chn < chnend; chn++) {
+ if (tas_dev->set_global_mode == NULL &&
+ tas_dev->tasdevice[chn].is_loading == false)
+ continue;
+
+ is_err = false;
+ subblk_offset = 2;
+ switch (subblk_typ) {
+ case TASDEVICE_CMD_SING_W: {
+ int i = 0;
+ unsigned short len = SMS_HTONS(data[2], data[3]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 * len > sublocksize) {
+ dev_err(tas_dev->dev,
+ "process_block: Out of memory\n");
+ is_err = true;
+ break;
+ }
+
+ for (i = 0; i < len; i++) {
+ rc = tasdevice_dev_write(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ data[subblk_offset + 3]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "process_block: single write error\n");
+ }
+ subblk_offset += 4;
+ }
+ }
+ break;
+ case TASDEVICE_CMD_BURST: {
+ unsigned short len =
+ SMS_HTONS(data[2], data[3]);
+
+ subblk_offset += 2;
+ if (subblk_offset + 4 + len > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: BURST Out of memory\n", __func__);
+ is_err = true;
+ break;
+ }
+ if (len % 4) {
+ dev_err(tas_dev->dev,
+ "%s:Bst-len(%u)not div by 4\n",
+ __func__, len);
+ break;
+ }
+
+ rc = tasdevice_dev_bulk_write(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset],
+ data[subblk_offset + 1],
+ data[subblk_offset + 2]),
+ &(data[subblk_offset + 4]),
+ len);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "%s: bulk_write error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += (len + 4);
+ }
+ break;
+ case TASDEVICE_CMD_DELAY: {
+ unsigned short delay_time = 0;
+
+ if (subblk_offset + 2 > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: deley Out of memory\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ delay_time = SMS_HTONS(data[2], data[3]);
+ usleep_range(delay_time*1000, delay_time*1000);
+ subblk_offset += 2;
+ }
+ break;
+ case TASDEVICE_CMD_FIELD_W:
+ if (subblk_offset + 6 > sublocksize) {
+ dev_err(tas_dev->dev,
+ "%s: bit write Out of memory\n",
+ __func__);
+ is_err = true;
+ break;
+ }
+ rc = tasdevice_dev_update_bits(tas_dev, chn,
+ TASDEVICE_REG(data[subblk_offset + 2],
+ data[subblk_offset + 3],
+ data[subblk_offset + 4]),
+ data[subblk_offset + 1],
+ data[subblk_offset + 5]);
+ if (rc < 0) {
+ is_err = true;
+ dev_err(tas_dev->dev,
+ "%s: update_bits error = %d\n",
+ __func__, rc);
+ }
+ subblk_offset += 6;
+ break;
+ default:
+ break;
+ };
+ if (is_err == true && blktyp != 0) {
+ if (blktyp == 0x80) {
+ tas_dev->tasdevice[chn].cur_prog = -1;
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ } else
+ tas_dev->tasdevice[chn].cur_conf = -1;
+ }
+ }
+ return subblk_offset;
+}
+
+static void tasdevice_select_cfg_blk(void *pContext, int conf_no,
+ unsigned char block_type)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) pContext;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ int j = 0, k = 0, chn = 0, chnend = 0;
+
+ if (conf_no >= rca->ncfgs || conf_no < 0 || NULL == cfg_info) {
+ dev_err(tas_dev->dev,
+ "conf_no should be not more than %u\n",
+ rca->ncfgs);
+ goto out;
+ } else
+ dev_info(tas_dev->dev,
+ "select_cfg_blk: profile_conf_id = %d\n",
+ conf_no);
+
+ for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, rc = 0;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(tas_dev->dev,
+ "ERROR!!!block_type should be in range from 2 to 5\n");
+ goto out;
+ }
+ if (block_type != cfg_info[conf_no]->blk_data[j]->block_type)
+ continue;
+ dev_info(tas_dev->dev,
+ "%s: conf %d, block type:%s\t device idx = 0x%02x\n",
+ __func__, conf_no,
+ blocktype[cfg_info[conf_no]->blk_data[j]
+ ->block_type-1], cfg_info[conf_no]->blk_data[j]
+ ->dev_idx);
+
+ for (k = 0; k < (int)cfg_info[conf_no]->blk_data[j]
+ ->n_subblks; k++) {
+ if (cfg_info[conf_no]->blk_data[j]->dev_idx) {
+ chn =
+ cfg_info[conf_no]->blk_data[j]->dev_idx
+ - 1;
+ chnend =
+ cfg_info[conf_no]->blk_data[j]->dev_idx;
+ } else {
+ chn = 0;
+ chnend = tas_dev->ndev;
+ }
+ for (; chn < chnend; chn++)
+ tas_dev->tasdevice[chn].is_loading = true;
+
+ rc = tasdevice_process_block(tas_dev,
+ cfg_info[conf_no]->blk_data[j]->regdata +
+ length,
+ cfg_info[conf_no]->blk_data[j]->dev_idx,
+ cfg_info[conf_no]->blk_data[j]->block_size -
+ length);
+ length += rc;
+ if (cfg_info[conf_no]->blk_data[j]->block_size <
+ length) {
+ dev_err(tas_dev->dev,
+ "%s: ERROR:%u %u out of memory\n",
+ __func__, length,
+ cfg_info[conf_no]->blk_data[j]
+ ->block_size);
+ break;
+ }
+ }
+ if (length != cfg_info[conf_no]->blk_data[j]->block_size)
+ dev_err(tas_dev->dev,
+ "%s: %u %u size is not same\n",
+ __func__, length,
+ cfg_info[conf_no]->blk_data[j]->block_size);
+
+ }
+
+out:
+ return;
+}
+
+static struct tasdevice_config_info *tasdevice_add_config(
+ void *pContext, unsigned char *config_data,
+ unsigned int config_size)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *)pContext;
+ struct tasdevice_config_info *cfg_info = NULL;
+ int config_offset = 0, i = 0;
+
+ cfg_info = kzalloc(
+ sizeof(struct tasdevice_config_info), GFP_KERNEL);
+ if (!cfg_info)
+ goto out;
+
+ if (tas_dev->rcabin.fw_hdr.binary_version_num >= 0x105) {
+ if (config_offset + 64 > (int)config_size) {
+ dev_err(tas_dev->dev,
+ "add config: Out of memory\n");
+ goto out;
+ }
+ config_offset += 64;
+ }
+
+ if (config_offset + 4 > (int)config_size) {
+ dev_err(tas_dev->dev,
+ "add config: Out of memory\n");
+ goto out;
+ }
+ cfg_info->nblocks =
+ SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1],
+ config_data[config_offset + 2], config_data[config_offset + 3]);
+ config_offset += 4;
+
+ cfg_info->blk_data = kcalloc(
+ cfg_info->nblocks, sizeof(struct tasdev_blk_data *),
+ GFP_KERNEL);
+ if (!cfg_info->blk_data) {
+ dev_err(tas_dev->dev,
+ "add config: blk_data alloc failed!\n");
+ goto out;
+ }
+ cfg_info->real_nblocks = 0;
+ for (i = 0; i < (int)cfg_info->nblocks; i++) {
+ if (config_offset + 12 > config_size) {
+ dev_err(tas_dev->dev,
+ "add config: Out of memory: i = %d nblocks = %u!\n",
+ i, cfg_info->nblocks);
+ break;
+ }
+ cfg_info->blk_data[i] = kzalloc(
+ sizeof(struct tasdev_blk_data), GFP_KERNEL);
+ if (!cfg_info->blk_data[i])
+ break;
+
+ cfg_info->blk_data[i]->dev_idx = config_data[config_offset];
+ config_offset++;
+
+ cfg_info->blk_data[i]->block_type = config_data[config_offset];
+ config_offset++;
+
+ if (cfg_info->blk_data[i]->block_type ==
+ TASDEVICE_BIN_BLK_PRE_POWER_UP) {
+ if (cfg_info->blk_data[i]->dev_idx == 0) {
+ cfg_info->active_dev = 1;
+ } else {
+ cfg_info->active_dev =
+ 1 <<
+ (cfg_info->blk_data[i]->dev_idx - 1);
+ }
+ }
+ cfg_info->blk_data[i]->yram_checksum =
+ SMS_HTONS(config_data[config_offset],
+ config_data[config_offset + 1]);
+ config_offset += 2;
+ cfg_info->blk_data[i]->block_size =
+ SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1],
+ config_data[config_offset + 2],
+ config_data[config_offset + 3]);
+ config_offset += 4;
+
+ cfg_info->blk_data[i]->n_subblks =
+ SMS_HTONL(config_data[config_offset],
+ config_data[config_offset + 1],
+ config_data[config_offset + 2],
+ config_data[config_offset + 3]);
+
+ config_offset += 4;
+ cfg_info->blk_data[i]->regdata = kzalloc(
+ cfg_info->blk_data[i]->block_size, GFP_KERNEL);
+ if (cfg_info->blk_data[i]->regdata == 0) {
+ dev_err(tas_dev->dev,
+ "add config: regdata alloc failed!\n");
+ goto out;
+ }
+ if (config_offset + cfg_info->blk_data[i]->block_size
+ > config_size) {
+ dev_err(tas_dev->dev,
+ "%s: block_size Out of memory: i = %d nblocks = %u!\n",
+ __func__, i,
+ cfg_info->nblocks);
+ break;
+ }
+ memcpy(cfg_info->blk_data[i]->regdata,
+ &config_data[config_offset],
+ cfg_info->blk_data[i]->block_size);
+ config_offset += cfg_info->blk_data[i]->block_size;
+ cfg_info->real_nblocks += 1;
+ }
+out:
+ return cfg_info;
+}
+static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int val;
+ int ret = 0;
+
+ /* Read the primary device as the whole */
+ ret = tasdevice_dev_read(tas_dev, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s, get digital vol error\n",
+ __func__);
+ goto out;
+ }
+ val = (val > mc->max) ? mc->max : val;
+ val = mc->invert ? mc->max - val : val;
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+}
+
+static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int val;
+ int i, ret = 0;
+
+ val = ucontrol->value.integer.value[0];
+ val = (val > mc->max) ? mc->max : val;
+ val = mc->invert ? mc->max - val : val;
+ val = (val < 0) ? 0 : val;
+ if (tas_dev->set_global_mode != NULL) {
+ ret = tasdevice_dev_write(tas_dev, tas_dev->ndev,
+ mc->reg, (unsigned int)val);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s, set digital vol error in global mode\n",
+ __func__);
+ } else {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_write(tas_dev, i,
+ mc->reg, (unsigned int)val);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s, set digital vol error in device %d\n",
+ __func__, i);
+ }
+ }
+
+ return ret;
+}
+
+static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int val;
+ unsigned char mask = 0;
+ int ret = 0;
+
+ /* Read the primary device */
+ ret = tasdevice_dev_read(tas_dev, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s, get AMP vol error\n",
+ __func__);
+ goto out;
+ }
+
+ mask = (1 << fls(mc->max)) - 1;
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ val = (val > mc->max) ? mc->max : val;
+ val = mc->invert ? mc->max - val : val;
+ ucontrol->value.integer.value[0] = (int)val;
+
+out:
+ return ret;
+}
+
+static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int val;
+ int i, ret = 0;
+ unsigned char mask = 0;
+
+ mask = (1 << fls(mc->max)) - 1;
+ mask <<= mc->shift;
+ val = ucontrol->value.integer.value[0];
+ val = (val > mc->max) ? mc->max : val;
+ val = mc->invert ? mc->max - val : val;
+ val = (val < 0) ? 0 : val;
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_dev, i,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s, set AMP vol error in device %d\n",
+ __func__, i);
+ }
+
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(dvc_tlv, -10000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(amp_vol_tlv, 1100, 50, 0);
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Amp Gain Volume", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, amp_vol_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Digital Volume Control", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, dvc_tlv),
+};
+
+static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *p_tasdevice =
+ snd_soc_component_get_drvdata(codec);
+
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ p_tasdevice->rcabin.profile_cfg_id =
+ ucontrol->value.integer.value[0];
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+
+ return 0;
+}
+
+static int tasdevice_info_programs(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *p_tasdevice =
+ snd_soc_component_get_drvdata(codec);
+ struct tasdevice_fw *tas_fw = p_tasdevice->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = (int)tas_fw->n_programs;
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+ return 0;
+}
+
+static int tasdevice_info_configurations(
+ struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *p_tasdevice =
+ snd_soc_component_get_drvdata(codec);
+ struct tasdevice_fw *tas_fw = p_tasdevice->fmw;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = (int)tas_fw->n_configurations - 1;
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+
+ return 0;
+}
+
+static int tasdevice_info_profile(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *p_tasdevice =
+ snd_soc_component_get_drvdata(codec);
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ uinfo->count = 1;
+ uinfo->value.integer.min = 1;
+ uinfo->value.integer.max = max(0, p_tasdevice->rcabin.ncfgs);
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+ return 0;
+}
+
+static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *p_tasdevice
+ = snd_soc_component_get_drvdata(codec);
+
+ /* Codec Lock Hold*/
+ mutex_lock(&p_tasdevice->codec_lock);
+ ucontrol->value.integer.value[0] =
+ p_tasdevice->rcabin.profile_cfg_id;
+ /* Codec Lock Release*/
+ mutex_unlock(&p_tasdevice->codec_lock);
+
+ return 0;
+}
+
+static int tasdevice_create_controls(struct tasdevice_priv *tas_dev)
+{
+ int nr_controls = 1, ret = 0, mix_index = 0;
+ char *name = NULL;
+ struct snd_kcontrol_new *tasdevice_profile_controls = NULL;
+
+ tasdevice_profile_controls = devm_kzalloc(tas_dev->dev,
+ nr_controls * sizeof(tasdevice_profile_controls[0]),
+ GFP_KERNEL);
+ if (tasdevice_profile_controls == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Create a mixer item for selecting the active profile */
+ name = devm_kzalloc(tas_dev->dev,
+ MAX_CONTROL_NAME, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ scnprintf(name, MAX_CONTROL_NAME, "TASDEVICE Profile id");
+ tasdevice_profile_controls[mix_index].name = name;
+ tasdevice_profile_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ tasdevice_profile_controls[mix_index].info =
+ tasdevice_info_profile;
+ tasdevice_profile_controls[mix_index].get =
+ tasdevice_get_profile_id;
+ tasdevice_profile_controls[mix_index].put =
+ tasdevice_set_profile_id;
+ mix_index++;
+
+ ret = snd_soc_add_component_controls(tas_dev->codec,
+ tasdevice_profile_controls,
+ nr_controls < mix_index ? nr_controls : mix_index);
+
+ tas_dev->tas_ctrl.nr_controls =
+ nr_controls < mix_index ? nr_controls : mix_index;
+out:
+ return ret;
+}
+
+static int tasdevice_program_get(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2781->codec_lock);
+ pValue->value.integer.value[0] = pTAS2781->cur_prog;
+ mutex_unlock(&pTAS2781->codec_lock);
+ return 0;
+}
+
+static int tasdevice_program_put(struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec);
+ unsigned int nProgram = pValue->value.integer.value[0];
+
+ mutex_lock(&pTAS2781->codec_lock);
+ pTAS2781->cur_prog = nProgram;
+ mutex_unlock(&pTAS2781->codec_lock);
+ return 0;
+}
+
+static int tasdevice_configuration_get(
+ struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tasdevice_priv *pTAS2781 = snd_soc_component_get_drvdata(codec);
+
+ mutex_lock(&pTAS2781->codec_lock);
+ pValue->value.integer.value[0] = pTAS2781->cur_conf;
+ mutex_unlock(&pTAS2781->codec_lock);
+ return 0;
+}
+
+static int tasdevice_configuration_put(
+ struct snd_kcontrol *pKcontrol,
+ struct snd_ctl_elem_value *pValue)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(pKcontrol);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ unsigned int n_configuration = pValue->value.integer.value[0];
+
+ mutex_lock(&tas_dev->codec_lock);
+ tas_dev->cur_conf = n_configuration;
+ mutex_unlock(&tas_dev->codec_lock);
+ return 0;
+}
+
+static int tasdevice_dsp_create_control(
+ struct tasdevice_priv *tas_dev)
+{
+ int nr_controls = 2, ret = 0, mix_index = 0;
+ char *program_name = NULL;
+ char *configuration_name = NULL;
+ struct snd_kcontrol_new *tasdevice_dsp_controls = NULL;
+
+ tasdevice_dsp_controls = devm_kzalloc(tas_dev->dev,
+ nr_controls * sizeof(tasdevice_dsp_controls[0]),
+ GFP_KERNEL);
+ if (tasdevice_dsp_controls == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Create a mixer item for selecting the active profile */
+ program_name = devm_kzalloc(tas_dev->dev,
+ MAX_CONTROL_NAME, GFP_KERNEL);
+ configuration_name = devm_kzalloc(tas_dev->dev,
+ MAX_CONTROL_NAME, GFP_KERNEL);
+ if (!program_name || !configuration_name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ scnprintf(program_name, MAX_CONTROL_NAME, "Program");
+ tasdevice_dsp_controls[mix_index].name = program_name;
+ tasdevice_dsp_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ tasdevice_dsp_controls[mix_index].info =
+ tasdevice_info_programs;
+ tasdevice_dsp_controls[mix_index].get =
+ tasdevice_program_get;
+ tasdevice_dsp_controls[mix_index].put =
+ tasdevice_program_put;
+ mix_index++;
+
+ scnprintf(configuration_name, MAX_CONTROL_NAME, "Configuration");
+ tasdevice_dsp_controls[mix_index].name = configuration_name;
+ tasdevice_dsp_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ tasdevice_dsp_controls[mix_index].info =
+ tasdevice_info_configurations;
+ tasdevice_dsp_controls[mix_index].get =
+ tasdevice_configuration_get;
+ tasdevice_dsp_controls[mix_index].put =
+ tasdevice_configuration_put;
+ mix_index++;
+
+ ret = snd_soc_add_component_controls(tas_dev->codec,
+ tasdevice_dsp_controls,
+ nr_controls < mix_index ? nr_controls : mix_index);
+
+ tas_dev->tas_ctrl.nr_controls += nr_controls;
+out:
+ return ret;
+}
+
+static void tasdevice_rca_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = NULL;
+ struct tasdevice_rca_hdr *fw_hdr = NULL;
+ struct tasdevice_config_info **cfg_info = NULL;
+ const struct firmware *fw_entry = NULL;
+ unsigned char *buf = NULL;
+ int offset = 0, i = 0;
+ unsigned int total_config_sz = 0;
+ int ret = 0;
+
+ mutex_lock(&tas_dev->codec_lock);
+ rca = &(tas_dev->rcabin);
+ fw_hdr = &(rca->fw_hdr);
+ if (unlikely(!fmw) || unlikely(!fmw->data)) {
+ dev_err(tas_dev->dev,
+ "Failed to read %s, no side - effect on driver running\n",
+ tas_dev->rca_binaryname);
+ ret = -1;
+ goto out;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ dev_info(tas_dev->dev, "tasdev: rca_ready start\n");
+ fw_hdr->img_sz = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(tas_dev->dev,
+ "File size not match, %d %u", (int)fmw->size,
+ fw_hdr->img_sz);
+ ret = -1;
+ goto out;
+ }
+
+ fw_hdr->checksum = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ fw_hdr->binary_version_num = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(tas_dev->dev,
+ "File version 0x%04x is too low",
+ fw_hdr->binary_version_num);
+ ret = -1;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 8;
+ fw_hdr->plat_type = buf[offset];
+ offset += 1;
+ fw_hdr->dev_family = buf[offset];
+ offset += 1;
+ fw_hdr->reserve = buf[offset];
+ offset += 1;
+ fw_hdr->ndev = buf[offset];
+ offset += 1;
+ if (fw_hdr->ndev != tas_dev->ndev) {
+ dev_err(tas_dev->dev,
+ "ndev(%u) from rcabin and ndev(%u) from DTS does not match\n",
+ fw_hdr->ndev,
+ tas_dev->ndev);
+ ret = -1;
+ goto out;
+ }
+ if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) {
+ dev_err(tas_dev->dev,
+ "rca_ready: Out of Memory!\n");
+ ret = -1;
+ goto out;
+ }
+
+ for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = SMS_HTONL(buf[offset], buf[offset + 1],
+ buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ dev_info(tas_dev->dev, "nconfig = %u\n", fw_hdr->nconfig);
+ for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = SMS_HTONL(buf[offset],
+ buf[offset + 1], buf[offset + 2], buf[offset + 3]);
+ offset += 4;
+ total_config_sz += fw_hdr->config_size[i];
+ }
+ dev_info(tas_dev->dev,
+ "img_sz = %u total_config_sz = %u offset = %d\n",
+ fw_hdr->img_sz, total_config_sz, offset);
+ if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) {
+ dev_err(tas_dev->dev, "Bin file error!\n");
+ ret = -1;
+ goto out;
+ }
+ cfg_info = kcalloc(fw_hdr->nconfig,
+ sizeof(struct tasdevice_config_info *),
+ GFP_KERNEL);
+
+ if (!cfg_info) {
+ ret = -1;
+ dev_err(tas_dev->dev, "nconfig Memory alloc failed!\n");
+ goto out;
+ }
+ rca->cfg_info = cfg_info;
+ rca->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ cfg_info[i] = tasdevice_add_config(context, &buf[offset],
+ fw_hdr->config_size[i]);
+ if (!cfg_info[i]) {
+ ret = -1;
+ dev_err(tas_dev->dev,
+ "add_config Memory alloc failed!\n");
+ break;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ rca->ncfgs += 1;
+ }
+ tasdevice_create_controls(tas_dev);
+
+ tasdevice_dsp_remove(tas_dev);
+ tasdevice_calbin_remove(tas_dev);
+ tas_dev->fw_state = TASDEVICE_DSP_FW_PENDING;
+ scnprintf(tas_dev->coef_binaryname, 64, "%s_coef.bin",
+ tas_dev->dev_name);
+ ret = request_firmware(&fw_entry, tas_dev->coef_binaryname,
+ tas_dev->dev);
+ if (!ret) {
+ ret = tasdevice_dspfw_ready(fw_entry, tas_dev);
+ release_firmware(fw_entry);
+ fw_entry = NULL;
+ } else {
+ tas_dev->fw_state = TASDEVICE_DSP_FW_FAIL;
+ dev_err(tas_dev->dev, "%s: load %s error\n", __func__,
+ tas_dev->coef_binaryname);
+ goto out;
+ }
+ tasdevice_dsp_create_control(tas_dev);
+
+ tas_dev->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+ tas_dev->is_glb_calibrated_data_loaded = true;
+ for (i = 0; i < tas_dev->ndev; i++) {
+ scnprintf(tas_dev->cal_binaryname[i], 64, "%s_cal_0x%02x.bin",
+ tas_dev->dev_name, tas_dev->tasdevice[i].dev_addr);
+ ret = tas2781_load_calibration(tas_dev,
+ tas_dev->cal_binaryname[i], i);
+ if (ret != 0) {
+ dev_err(tas_dev->dev,
+ "%s: load %s error, no-side effect\n",
+ __func__,
+ tas_dev->cal_binaryname[i]);
+ ret = 0;
+ tas_dev->is_glb_calibrated_data_loaded = false;
+ }
+ }
+
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ if (fmw)
+ release_firmware(fmw);
+ dev_info(tas_dev->dev, "Firmware init complete\n");
+}
+
+static void tasdevice_config_info_remove(void *context)
+{
+ struct tasdevice_priv *tas_dev =
+ (struct tasdevice_priv *) context;
+ struct tasdevice_rca *rca = &(tas_dev->rcabin);
+ struct tasdevice_config_info **cfg_info = rca->cfg_info;
+ int i = 0, j = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (cfg_info) {
+ for (i = 0; i < rca->ncfgs; i++) {
+ if (cfg_info[i]) {
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks;
+ j++) {
+ kfree(
+ cfg_info[i]->blk_data[j]->regdata);
+ kfree(cfg_info[i]->blk_data[j]);
+ }
+ kfree(cfg_info[i]->blk_data);
+ kfree(cfg_info[i]);
+ }
+ }
+ kfree(cfg_info);
+ }
+ mutex_unlock(&tas_dev->dev_lock);
+}
+
+static void tasdevice_enable_irq(
+ struct tasdevice_priv *tas_dev, bool enable)
+{
+ if (enable) {
+ if (tas_dev->irq_info.is_irq_enable)
+ return;
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio))
+ enable_irq(tas_dev->irq_info.irq);
+ tas_dev->irq_info.is_irq_enable = true;
+ } else {
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio))
+ disable_irq_nosync(tas_dev->irq_info.irq);
+ tas_dev->irq_info.is_irq_enable = false;
+ }
+}
+
+static void tasdevice_set_power_state(
+ struct tasdevice_priv *tas_dev, int state)
+{
+ switch (state) {
+ case 0:
+ schedule_delayed_work(&tas_dev->tuning_switch_wrk,
+ msecs_to_jiffies(20));
+ break;
+ default:
+ if (!(tas_dev->pstream || tas_dev->cstream)) {
+ if (tas_dev->irq_work_func)
+ tasdevice_enable_irq(tas_dev, false);
+ tasdevice_select_cfg_blk(tas_dev,
+ tas_dev->rcabin.profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+ }
+ break;
+ }
+}
+
+static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int stream;
+
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+ stream = strcmp(w->name, "SPK") ? SNDRV_PCM_STREAM_PLAYBACK :
+ SNDRV_PCM_STREAM_CAPTURE;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_info(tas_dev->dev, "SND_SOC_DAPM_POST_PMU\n");
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tas_dev->pstream = 1;
+ else
+ tas_dev->cstream = 1;
+ tasdevice_set_power_state(tas_dev, 0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_info(tas_dev->dev, "SND_SOC_DAPM_PRE_PMD\n");
+ /* stop DSP only when both playback and capture streams
+ * are deactivated
+ */
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tas_dev->pstream = 0;
+ else
+ tas_dev->cstream = 0;
+ if (tas_dev->pstream != 0 || tas_dev->cstream != 0)
+ goto out;
+ tasdevice_set_power_state(tas_dev, 1);
+ break;
+ }
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+out:
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM,
+ 0, 0, tasdevice_dapm_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC")
+};
+
+static const struct snd_soc_dapm_route tasdevice_audio_map[] = {
+ {"SPK", NULL, "ASI"},
+ {"OUT", NULL, "SPK"},
+ {"ASI OUT", NULL, "DMIC"}
+};
+
+static int tasdevice_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int ret = 0;
+
+ if (tas_dev->fw_state != TASDEVICE_DSP_FW_ALL_OK) {
+ dev_err(tas_dev->dev, "DSP bin file not loaded\n");
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int tasdevice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct tasdevice_priv *tas_dev = snd_soc_dai_get_drvdata(dai);
+ unsigned int fsrate;
+ unsigned int slot_width;
+ int bclk_rate;
+ int rc = 0;
+
+ dev_info(tas_dev->dev, "%s: %s\n",
+ __func__, substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "Playback":"Capture");
+
+ fsrate = params_rate(params);
+ switch (fsrate) {
+ case 48000:
+ break;
+ case 44100:
+ break;
+ default:
+ dev_err(tas_dev->dev,
+ "%s: incorrect sample rate = %u\n",
+ __func__, fsrate);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ slot_width = params_width(params);
+ switch (slot_width) {
+ case 16:
+ break;
+ case 20:
+ break;
+ case 24:
+ break;
+ case 32:
+ break;
+ default:
+ dev_err(tas_dev->dev,
+ "%s: incorrect slot width = %u\n",
+ __func__, slot_width);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(tas_dev->dev,
+ "%s: incorrect bclk rate = %d\n",
+ __func__, bclk_rate);
+ rc = bclk_rate;
+ goto out;
+ }
+ dev_info(tas_dev->dev,
+ "%s: BCLK rate = %d Channel = %d Sample rate = %u slot width = %u\n",
+ __func__, bclk_rate, params_channels(params),
+ fsrate, slot_width);
+out:
+ return rc;
+}
+
+static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct tasdevice_priv *tas_dev = snd_soc_dai_get_drvdata(codec_dai);
+
+ dev_info(tas_dev->dev,
+ "%s: clk_id = %d, freq = %u, CLK direction %s\n",
+ __func__, clk_id, freq,
+ dir == TASDEVICE_CLK_DIR_OUT ? "OUT":"IN");
+
+ return 0;
+}
+
+static void tuning_switch_routine(struct work_struct *work)
+{
+ struct tasdevice_priv *tas_dev =
+ container_of(work, struct tasdevice_priv,
+ tuning_switch_wrk.work);
+ struct tasdevice_fw *tas_fmw = tas_dev->fmw;
+ int profile_cfg_id = 0;
+ int is_set_glb_mode = 0;
+
+ mutex_lock(&tas_dev->codec_lock);
+
+ if (tas_fmw) {
+ if (tas_dev->cur_prog >= tas_fmw->n_programs)
+ /*bypass all in rca is profile id 0*/
+ profile_cfg_id = RCA_CONFIGID_BYPASS_ALL;
+ else {
+ /*dsp mode or tuning mode*/
+ profile_cfg_id = tas_dev->rcabin.profile_cfg_id;
+ tas_fmw = tas_dev->fmw;
+ dev_info(tas_dev->dev, "%s: %s\n", __func__,
+ tas_fmw->configs[tas_dev->cur_conf].name);
+ is_set_glb_mode =
+ tasdevice_select_tuningprm_cfg(tas_dev,
+ tas_dev->cur_prog,
+ tas_dev->cur_conf,
+ profile_cfg_id);
+ if (tas_dev->set_global_mode != NULL)
+ tas_dev->set_global_mode(tas_dev);
+ }
+ } else
+ profile_cfg_id = RCA_CONFIGID_BYPASS_ALL;
+
+ tasdevice_select_cfg_blk(tas_dev, profile_cfg_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ if (is_set_glb_mode && tas_dev->set_global_mode)
+ tas_dev->set_global_mode(tas_dev);
+ if (tas_dev->irq_work_func)
+ tasdevice_enable_irq(tas_dev, true);
+ mutex_unlock(&tas_dev->codec_lock);
+}
+
+static const struct snd_soc_dai_ops tasdevice_dai_ops = {
+ .startup = tasdevice_startup,
+ .hw_params = tasdevice_hw_params,
+ .set_sysclk = tasdevice_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tasdevice_dai_driver[] = {
+ {
+ .name = "tas2781_codec",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TASDEVICE_RATES,
+ .formats = TASDEVICE_FORMATS,
+ },
+ .ops = &tasdevice_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static void tas2781_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret = 0;
+ int i = 0;
+
+ for (; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_write(tas_dev, i,
+ TAS2871_REG_SWRESET,
+ TAS2871_REG_SWRESET_RESET);
+ if (ret < 0) {
+ dev_err(tas_dev->dev, "%s: chn %d reset fail, %d\n",
+ __func__, i, ret);
+ continue;
+ }
+ }
+ usleep_range(1000, 1050);
+}
+
+static int tasdevice_codec_probe(
+ struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+
+ tas_dev->codec = codec;
+ scnprintf(tas_dev->rca_binaryname, 64, "%s_rca.bin",
+ tas_dev->dev_name);
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ tas_dev->rca_binaryname, tas_dev->dev, GFP_KERNEL, tas_dev,
+ tasdevice_rca_ready);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "%s: request_firmware_nowait error:0x%08x\n",
+ __func__, ret);
+
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+
+ tas2781_reset(tas_dev);
+ if (tas_dev->set_global_mode != NULL)
+ tas_dev->set_global_mode(tas_dev);
+
+ return ret;
+}
+
+static void tasdevice_deinit(void *context)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context;
+
+ tasdevice_config_info_remove(tas_dev);
+ tasdevice_dsp_remove(tas_dev);
+ tasdevice_calbin_remove(tas_dev);
+ tas_dev->fw_state = TASDEVICE_DSP_FW_PENDING;
+}
+
+static void tasdevice_codec_remove(
+ struct snd_soc_component *codec)
+{
+ struct tasdevice_priv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ /* Codec Lock Hold */
+ mutex_lock(&tas_dev->codec_lock);
+ tasdevice_deinit(tas_dev);
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_dev->codec_lock);
+}
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_tasdevice = {
+ .probe = tasdevice_codec_probe,
+ .remove = tasdevice_codec_remove,
+ .controls = tas2781_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2781_snd_controls),
+ .dapm_widgets = tasdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets),
+ .dapm_routes = tasdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static void irq_work_routine(struct work_struct *pWork)
+{
+ struct tasdevice_priv *tas_dev =
+ container_of(pWork, struct tasdevice_priv,
+ irq_info.irq_work.work);
+
+ mutex_lock(&tas_dev->codec_lock);
+ if (tas_dev->runtime_suspend) {
+ dev_info(tas_dev->dev, "%s, Runtime Suspended\n", __func__);
+ goto end;
+ }
+ /*Logical Layer IRQ function, return is ignored*/
+ if (tas_dev->irq_work_func)
+ tas_dev->irq_work_func(tas_dev);
+ else
+ dev_info(tas_dev->dev,
+ "%s, irq_work_func is NULL\n", __func__);
+end:
+ mutex_unlock(&tas_dev->codec_lock);
+}
+
+static void tas2781_irq_work_func(struct tasdevice_priv *tas_dev)
+{
+ int rc = 0;
+ unsigned int reg_val = 0, array_size = 0, i = 0, ndev = 0;
+ unsigned int int_reg_array[] = {
+ TAS2781_REG_INT_LTCH0,
+ TAS2781_REG_INT_LTCH1,
+ TAS2781_REG_INT_LTCH1_0,
+ TAS2781_REG_INT_LTCH2,
+ TAS2781_REG_INT_LTCH3,
+ TAS2781_REG_INT_LTCH4};
+
+ tasdevice_enable_irq(tas_dev, false);
+
+ array_size = ARRAY_SIZE(int_reg_array);
+
+ for (ndev = 0; ndev < tas_dev->ndev; ndev++) {
+ for (i = 0; i < array_size; i++) {
+ rc = tasdevice_dev_read(tas_dev,
+ ndev, int_reg_array[i], ®_val);
+ if (!rc)
+ dev_info(tas_dev->dev,
+ "INT STATUS REG 0x%04x=0x%02x\n",
+ int_reg_array[i], reg_val);
+ else
+ dev_err(tas_dev->dev,
+ "Read Reg 0x%04x error(rc=%d)\n",
+ int_reg_array[i], rc);
+ }
+ }
+
+}
+
+static irqreturn_t tasdevice_irq_handler(int irq,
+ void *dev_id)
+{
+ struct tasdevice_priv *tas_dev = (struct tasdevice_priv *)dev_id;
+
+ /* get IRQ status after 100 ms */
+ schedule_delayed_work(&tas_dev->irq_info.irq_work,
+ msecs_to_jiffies(100));
+ return IRQ_HANDLED;
+}
+
+static int tasdevice_parse_dt(struct tasdevice_priv *tas_dev)
+{
+ struct device_node *np = tas_dev->dev->of_node;
+ int rc = 0, i = 0, ndev = 0;
+ unsigned int dev_addrs[max_chn];
+ struct i2c_client *client = (struct i2c_client *)tas_dev->client;
+
+ ndev = of_property_read_variable_u32_array(np, "ti,audio-slots",
+ dev_addrs, 0, ARRAY_SIZE(dev_addrs));
+ if (ndev <= 0) {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ }
+ for (i = 0; i < ndev; i++) {
+ tas_dev->tasdevice[i].dev_addr = dev_addrs[i];
+ dev_info(tas_dev->dev, "%s=0x%02x",
+ dts_dev_addr_tag[i],
+ tas_dev->tasdevice[i].dev_addr);
+ }
+ if (ndev > 1) {
+ rc = of_property_read_u32(np, "ti,broadcast-addr",
+ &(tas_dev->glb_addr.dev_addr));
+ if (rc) {
+ dev_err(tas_dev->dev,
+ "Looking up node %s failed %d\n",
+ np->full_name, rc);
+ tas_dev->glb_addr.dev_addr = 0;
+ }
+ }
+
+ tas_dev->ndev = ndev;
+
+ tas_dev->rst_gpio = of_get_named_gpio(np, "reset-gpios", i);
+ if (gpio_is_valid(tas_dev->rst_gpio)) {
+ rc = gpio_request(tas_dev->rst_gpio, "reset");
+ if (!rc) {
+ gpio_direction_output(tas_dev->rst_gpio, 1);
+ dev_info(tas_dev->dev, "reset-gpio = %d",
+ tas_dev->rst_gpio);
+ ndev++;
+ } else
+ dev_err(tas_dev->dev,
+ "%s: Failed to request gpio %d\n",
+ __func__, tas_dev->rst_gpio);
+ } else
+ dev_err(tas_dev->dev,
+ "Looking up node %s failed %d\n",
+ np->full_name,
+ tas_dev->rst_gpio);
+
+ strcpy(tas_dev->dev_name, tasdevice_id[tas_dev->chip_id].name);
+
+ tas_dev->irq_info.irq_gpio = of_get_named_gpio(np,
+ "interrupts", 0);
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio)) {
+ dev_dbg(tas_dev->dev, "interrupts gpio = %d",
+ tas_dev->irq_info.irq_gpio);
+ INIT_DELAYED_WORK(&tas_dev->irq_info.irq_work,
+ irq_work_routine);
+
+ rc = gpio_request(tas_dev->irq_info.irq_gpio,
+ "AUDEV-IRQ");
+ if (!rc) {
+ gpio_direction_input(
+ tas_dev->irq_info.irq_gpio);
+
+ tas_dev->irq_info.irq =
+ gpio_to_irq(
+ tas_dev->irq_info.irq_gpio);
+ dev_info(tas_dev->dev,
+ "irq = %d\n",
+ tas_dev->irq_info.irq);
+
+ rc = request_threaded_irq(
+ tas_dev->irq_info.irq,
+ tasdevice_irq_handler,
+ NULL, IRQF_TRIGGER_FALLING|
+ IRQF_ONESHOT,
+ SMARTAMP_MODULE_NAME, tas_dev);
+ if (!rc)
+ disable_irq_nosync(
+ tas_dev->irq_info.irq);
+ else
+ dev_err(tas_dev->dev,
+ "request_irq failed, %d\n",
+ rc);
+ } else
+ dev_err(tas_dev->dev,
+ "%s: GPIO %d request error\n",
+ __func__,
+ tas_dev->irq_info.irq_gpio);
+ } else
+ dev_err(tas_dev->dev,
+ "Looking up irq-gpio property in node %s failed %d\n",
+ np->full_name,
+ tas_dev->irq_info.irq_gpio);
+
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio) && rc == 0) {
+ if (tas_dev->chip_id == TAS2781)
+ tas_dev->irq_work_func = tas2781_irq_work_func;
+ else
+ dev_info(tas_dev->dev, "%s: No match irq_work_func\n",
+ __func__);
+ }
+
+ return 0;
+}
+
+static int tasdevice_change_chn_book(struct tasdevice_priv *tas_dev,
+ enum channel chn, int book)
+{
+ int n_result = 0;
+ struct i2c_client *pClient =
+ (struct i2c_client *)tas_dev->client;
+
+ if (chn < tas_dev->ndev) {
+ if (tas_dev->glb_addr.ref_cnt != 0) {
+ tas_dev->glb_addr.ref_cnt = 0;
+ tas_dev->glb_addr.cur_book = -1;
+ }
+ pClient->addr = tas_dev->tasdevice[chn].dev_addr;
+ if (tas_dev->tasdevice[chn].cur_book != book) {
+ n_result = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_PAGE, 0);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ n_result = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_REG, book);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ tas_dev->tasdevice[chn].cur_book = book;
+ }
+ } else if (chn == tas_dev->ndev) {
+ int i = 0;
+
+ if (tas_dev->glb_addr.ref_cnt == 0)
+ for (i = 0; i < tas_dev->ndev; i++)
+ tas_dev->tasdevice[i].cur_book = -1;
+ pClient->addr = tas_dev->glb_addr.dev_addr;
+ if (tas_dev->glb_addr.cur_book != book) {
+ n_result = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_PAGE, 0);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev,
+ "%s, 0ERROR, E=%d\n",
+ __func__, n_result);
+ goto out;
+ }
+ n_result = regmap_write(tas_dev->regmap,
+ TASDEVICE_BOOKCTL_REG, book);
+ if (n_result < 0) {
+ dev_err(tas_dev->dev,
+ "%s, book%xERROR, E=%d\n",
+ __func__, book, n_result);
+ goto out;
+ }
+ tas_dev->glb_addr.cur_book = book;
+ }
+
+ tas_dev->glb_addr.ref_cnt++;
+ } else
+ dev_err(tas_dev->dev,
+ "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+
+out:
+ return n_result;
+}
+
+int tasdevice_dev_read(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned int *pValue)
+{
+ int n_result = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn < tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = regmap_read(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), pValue);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR,E=%d\n",
+ __func__, n_result);
+ } else {
+
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+ }
+
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+int tasdevice_dev_write(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned int value)
+{
+ int n_result = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn <= tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = regmap_write(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), value);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned char *p_data,
+ unsigned int n_length)
+{
+ int n_result = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn <= tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = regmap_bulk_write(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), p_data, n_length);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_dev,
+ enum channel chn, unsigned int reg, unsigned char *p_data,
+ unsigned int n_length)
+{
+ int n_result = 0;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn < tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+
+ n_result = regmap_bulk_read(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), p_data, n_length);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ } else
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tas_dev, enum channel chn,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int n_result = 0;
+ struct i2c_client *pClient =
+ (struct i2c_client *)tas_dev->client;
+
+ mutex_lock(&tas_dev->dev_lock);
+ if (chn < tas_dev->ndev) {
+ n_result = tasdevice_change_chn_book(tas_dev, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (n_result < 0)
+ goto out;
+ pClient->addr = tas_dev->tasdevice[chn].dev_addr;
+ n_result = regmap_update_bits(tas_dev->regmap,
+ TASDEVICE_PGRG(reg), mask, value);
+ if (n_result < 0)
+ dev_err(tas_dev->dev, "%s, ERROR, E=%d\n",
+ __func__, n_result);
+ } else {
+ dev_err(tas_dev->dev, "%s, ERROR, no such channel(%d)\n",
+ __func__, chn);
+ n_result = -1;
+ }
+
+out:
+ mutex_unlock(&tas_dev->dev_lock);
+ return n_result;
+}
+
+static void tas2781_set_global_mode(struct tasdevice_priv *tas_dev)
+{
+ int i = 0;
+ int ret = 0;
+
+ for (; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_dev, i,
+ TAS2871_MISC_CFG2, TAS2871_GLOBAL_ADDR_MASK,
+ TAS2871_GLOBAL_ADDR_ENABLE);
+ if (ret < 0) {
+ dev_err(tas_dev->dev,
+ "%s: chn %d set global fail, %d\n",
+ __func__, i, ret);
+ continue;
+ }
+ }
+}
+
+static int tasdevice_init(struct tasdevice_priv *tas_dev)
+{
+ int ret = 0, i = 0;
+
+ tas_dev->cur_prog = -1;
+ tas_dev->cur_conf = -1;
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ tas_dev->tasdevice[i].cur_book = -1;
+ tas_dev->tasdevice[i].cur_prog = -1;
+ tas_dev->tasdevice[i].cur_conf = -1;
+ }
+ mutex_init(&tas_dev->dev_lock);
+ if (tas_dev->glb_addr.dev_addr != 0
+ && tas_dev->glb_addr.dev_addr < 0x7F)
+ tas_dev->set_global_mode = tas2781_set_global_mode;
+ dev_set_drvdata(tas_dev->dev, tas_dev);
+
+ INIT_DELAYED_WORK(&tas_dev->tuning_switch_wrk,
+ tuning_switch_routine);
+
+ mutex_init(&tas_dev->codec_lock);
+ ret = devm_snd_soc_register_component(tas_dev->dev,
+ &soc_codec_driver_tasdevice,
+ tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver));
+ if (ret)
+ dev_err(tas_dev->dev, "%s: codec register error:0x%08x\n",
+ __func__, ret);
+
+ INIT_DELAYED_WORK(&tas_dev->irq_info.irq_work, irq_work_routine);
+ tas_dev->irq_info.is_irq_enable = false;
+
+ dev_info(tas_dev->dev, "i2c register success\n");
+
+ return ret;
+}
+
+static void tasdevice_remove(struct tasdevice_priv *tas_dev)
+{
+ if (gpio_is_valid(tas_dev->rst_gpio))
+ gpio_free(tas_dev->rst_gpio);
+
+ if (gpio_is_valid(tas_dev->irq_info.irq_gpio)) {
+ if (delayed_work_pending(&tas_dev->irq_info.irq_work)) {
+ dev_info(tas_dev->dev, "cancel IRQ work\n");
+ cancel_delayed_work(&tas_dev->irq_info.irq_work);
+ }
+ cancel_delayed_work_sync(&tas_dev->irq_info.irq_work);
+ }
+
+ mutex_destroy(&tas_dev->dev_lock);
+ mutex_destroy(&tas_dev->codec_lock);
+ snd_soc_unregister_component(tas_dev->dev);
+}
+
+static int tasdevice_pm_suspend(struct device *dev)
+{
+ struct tasdevice_priv *tas_dev = dev_get_drvdata(dev);
+
+ mutex_lock(&tas_dev->codec_lock);
+ tas_dev->runtime_suspend = true;
+
+ if (tas_dev->irq_work_func) {
+ if (delayed_work_pending(&tas_dev->irq_info.irq_work)) {
+ dev_dbg(tas_dev->dev, "cancel IRQ work\n");
+ cancel_delayed_work_sync(&tas_dev->irq_info.irq_work);
+ }
+ }
+ mutex_unlock(&tas_dev->codec_lock);
+ return 0;
+}
+
+static int tasdevice_pm_resume(struct device *dev)
+{
+ struct tasdevice_priv *tas_dev = dev_get_drvdata(dev);
+
+ mutex_lock(&tas_dev->codec_lock);
+ tas_dev->runtime_suspend = false;
+ mutex_unlock(&tas_dev->codec_lock);
+ return 0;
+}
+
+const struct dev_pm_ops tasdevice_pm_ops = {
+ .suspend = tasdevice_pm_suspend,
+ .resume = tasdevice_pm_resume
+};
+
+static int tasdevice_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct tasdevice_priv *tas_dev = NULL;
+ int ret = 0;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
+ dev_err(&i2c->dev,
+ "%s: I2C check failed\n", __func__);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ tas_dev = devm_kzalloc(&i2c->dev, sizeof(*tas_dev), GFP_KERNEL);
+ if (!tas_dev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ tas_dev->dev = &i2c->dev;
+ tas_dev->client = (void *)i2c;
+ tas_dev->chip_id = id->driver_data;
+
+ if (i2c->dev.of_node)
+ ret = tasdevice_parse_dt(tas_dev);
+ else {
+ dev_err(tas_dev->dev, "No DTS info\n");
+ goto out;
+ }
+
+ tas_dev->regmap = devm_regmap_init_i2c(i2c,
+ &tasdevice_regmap);
+ if (IS_ERR(tas_dev->regmap)) {
+ ret = PTR_ERR(tas_dev->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+ ret = tasdevice_init(tas_dev);
+
+out:
+ if (ret < 0 && tas_dev != NULL)
+ tasdevice_remove(tas_dev);
+ return ret;
+
+}
+
+static void tasdevice_i2c_remove(struct i2c_client *pClient)
+{
+ struct tasdevice_priv *tas_dev = i2c_get_clientdata(pClient);
+
+ if (tas_dev)
+ tasdevice_remove(tas_dev);
+
+}
+
+static struct i2c_driver tasdevice_i2c_driver = {
+ .driver = {
+ .name = "tas2781-codec",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tasdevice_of_match),
+ .pm = &tasdevice_pm_ops,
+ },
+ .probe = tasdevice_i2c_probe,
+ .remove = tasdevice_i2c_remove,
+ .id_table = tasdevice_id,
+};
+
+module_i2c_driver(tasdevice_i2c_driver);
+
+MODULE_AUTHOR("Shenghao Ding <shenghao-ding(a)ti.com>");
+MODULE_AUTHOR("Kevin Lu <kevin-lu(a)ti.com>");
+MODULE_DESCRIPTION("ASoC TAS2781 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781.h b/sound/soc/codecs/tas2781.h
new file mode 100644
index 000000000000..ce9d3d162aa0
--- /dev/null
+++ b/sound/soc/codecs/tas2781.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
+ *
+ * Copyright (C) 2022 - 2023 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2781 driver implements a flexible and configurable algo coff setting
+ * for one, two, even multiple TAS2781 chips.
+ *
+ * Author: Shenghao Ding <shenghao-ding(a)ti.com>
+ * Kevin Lu <kevin-lu(a)ti.com>
+ */
+
+#ifndef __TAS2781_H__
+#define __TAS2781_H__
+
+#include "tas2781-dsp.h"
+
+#define SMARTAMP_MODULE_NAME "tas2781"
+#define TASDEVICE_RETRY_COUNT 3
+#define TASDEVICE_ERROR_FAILED -2
+
+#define TASDEVICE_RATES (SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_88200)
+#define TASDEVICE_MAX_CHANNELS 8
+
+#define TASDEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/*PAGE Control Register (available in page0 of each book) */
+#define TASDEVICE_PAGE_SELECT 0x00
+#define TASDEVICE_BOOKCTL_PAGE 0x00
+#define TASDEVICE_BOOKCTL_REG 127
+#define TASDEVICE_BOOK_ID(reg) (reg / (256 * 128))
+#define TASDEVICE_PAGE_ID(reg) ((reg % (256 * 128)) / 128)
+#define TASDEVICE_PAGE_REG(reg) ((reg % (256 * 128)) % 128)
+#define TASDEVICE_PGRG(reg) ((reg % (256 * 128)))
+#define TASDEVICE_REG(book, page, reg) (((book * 256 * 128) + \
+ (page * 128)) + reg)
+
+ /*Software Reset */
+#define TAS2871_REG_SWRESET TASDEVICE_REG(0x0, 0X0, 0x02)
+#define TAS2871_REG_SWRESET_RESET BIT(0)
+
+ /* Enable Global addresses */
+#define TAS2871_MISC_CFG2 TASDEVICE_REG(0x0, 0X0, 0x07)
+#define TAS2871_GLOBAL_ADDR_MASK BIT(1)
+#define TAS2871_GLOBAL_ADDR_ENABLE BIT(1)
+
+ /*I2C Checksum */
+#define TASDEVICE_I2CChecksum TASDEVICE_REG(0x0, 0x0, 0x7E)
+
+ /* Volume control */
+#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1A)
+#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03)
+#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1)
+
+#define TASDEVICE_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
+enum audio_device {
+ TAS2781 = 0,
+};
+
+#define SMS_HTONS(a, b) ((((a)&0x00FF)<<8) | ((b)&0x00FF))
+#define SMS_HTONL(a, b, c, d) ((((a)&0x000000FF)<<24) | \
+ (((b)&0x000000FF)<<16) | (((c)&0x000000FF)<<8) | \
+ ((d)&0x000000FF))
+
+struct tasdevice {
+ unsigned int dev_addr;
+ unsigned int err_code;
+ short cur_prog;
+ short cur_conf;
+ bool is_loading;
+ bool is_loaderr;
+ bool is_calibrated_data_loaded;
+ unsigned char cur_book;
+ struct tasdevice_fw *cali_data_fmw;
+};
+
+/*
+ * This item is used to store the generic i2c address of
+ * all the tas2781 devices for I2C broadcast during the multi-device
+ * writes, useless in mono case.
+ */
+struct global_addr {
+ unsigned char cur_book;
+ unsigned int dev_addr;
+ int ref_cnt;
+};
+
+struct tas_control {
+ struct snd_kcontrol_new *tasdevice_profile_controls;
+ int nr_controls;
+};
+
+struct tasdevice_irqinfo {
+ int irq_gpio;
+ int irq;
+ struct delayed_work irq_work;
+ bool is_irq_enable;
+};
+
+struct tasdevice_priv {
+ struct device *dev;
+ void *client;
+ struct regmap *regmap;
+ struct mutex codec_lock;
+ struct mutex dev_lock;
+ int rst_gpio;
+ struct tasdevice tasdevice[max_chn];
+ struct tasdevice_fw *fmw;
+ struct tasdevice_rca rcabin;
+ struct tasdevice_irqinfo irq_info;
+ struct tas_control tas_ctrl;
+ struct global_addr glb_addr;
+ int cur_prog;
+ int cur_conf;
+ unsigned int chip_id;
+ void (*set_global_mode)(struct tasdevice_priv *tas_dev);
+ int (*fw_parse_variable_header)(struct tasdevice_priv *tas_dev,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_program_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ int (*tasdevice_load_block)(struct tasdevice_priv *tas_dev,
+ struct tasdev_blk *pBlock);
+ int (*fw_parse_calibration_data)(struct tasdevice_priv *tas_dev,
+ struct tasdevice_fw *tas_fmw,
+ const struct firmware *fmw, int offset);
+ void (*irq_work_func)(struct tasdevice_priv *tas_dev);
+ int fw_state;
+ unsigned int magic_num;
+ unsigned char ndev;
+ unsigned char dev_name[32];
+ unsigned char rca_binaryname[64];
+ unsigned char coef_binaryname[64];
+ unsigned char cal_binaryname[max_chn][64];
+ bool runtime_suspend;
+ struct delayed_work tuning_switch_wrk;
+ void *codec;
+ int sysclk;
+ int pstream;
+ int cstream;
+ bool is_glb_calibrated_data_loaded;
+};
+
+int tasdevice_dev_read(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned int *value);
+int tasdevice_process_block(void *context,
+ unsigned char *data, unsigned char dev_idx, int sublocksize);
+int tasdevice_dev_write(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned int value);
+
+int tasdevice_dev_bulk_write(
+ struct tasdevice_priv *tasdevice, enum channel chn,
+ unsigned int reg, unsigned char *p_data, unsigned int n_length);
+
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tasdevice,
+ enum channel chn, unsigned int reg, unsigned char *p_data,
+ unsigned int n_length);
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tasdevice, enum channel chn,
+ unsigned int reg, unsigned int mask, unsigned int value);
+
+#endif /*__TAS2781_H__ */
--
2.34.1
1
0

03 Feb '23
Hi Mark
This patch-set adds R-Car Gen4 support on Renesas sound driver.
To add it, it needs some cleanups / prepares which makes
easy to review the patch.
Kuninori Morimoto (15):
ASoC: rsnd: check whether playback/capture property exists
ASoC: rsnd: fixup #endif position
ASoC: rsnd: Remove unnecessary rsnd_dbg_dai_call()
ASoC: rsnd: indicate necessary error when clock start failed
ASoC: rsnd: indicate warning once if it can't handle requested rule
ASoC: rsnd: use same debug message format on clkout
ASoC: rsnd: remove unnecessary ADG flags
ASoC: rsnd: rename clk to clkin
ASoC: rsnd: moves clkout_name to top of the file
ASoC: rsnd: use clkin/out_size
ASoC: rsnd: use array for 44.1kHz/48kHz rate handling
ASoC: rsnd: tidyup rsnd_dma_addr()
ASoC: rsnd: dma.c: tidyup rsnd_dma_probe()
ASoC: dt-bindings: renesas: add R8A779G0 V4H
ASoC: rsnd: add R-Car Gen4 Sound support
.../bindings/sound/renesas,rsnd.yaml | 4 +
sound/soc/sh/rcar/adg.c | 169 ++++++++++--------
sound/soc/sh/rcar/core.c | 66 ++++---
sound/soc/sh/rcar/dma.c | 57 +++++-
sound/soc/sh/rcar/gen.c | 70 ++++++++
sound/soc/sh/rcar/rsnd.h | 23 ++-
sound/soc/sh/rcar/ssi.c | 13 +-
sound/soc/sh/rcar/ssiu.c | 15 +-
8 files changed, 289 insertions(+), 128 deletions(-)
--
2.25.1
4
20
I am trying to only use a static /dev. I have opened an issue about this on
GitHub and got it to work only as the root user. This resembles my
experience with xorg. In that case I had to use the suid use flag. Is there
an equivalent in this situation?
2
1

[PATCH] ASoC: mchp-spdifrx: Fix uninitialized use of mr in mchp_spdifrx_hw_params()
by Nathan Chancellor 02 Feb '23
by Nathan Chancellor 02 Feb '23
02 Feb '23
Clang warns:
../sound/soc/atmel/mchp-spdifrx.c:455:3: error: variable 'mr' is uninitialized when used here [-Werror,-Wuninitialized]
mr |= SPDIFRX_MR_ENDIAN_BIG;
^~
../sound/soc/atmel/mchp-spdifrx.c:432:8: note: initialize the variable 'mr' to silence this warning
u32 mr;
^
= 0
1 error generated.
Zero initialize mr so that these bitwise OR and assignment operation
works unconditionally.
Fixes: fa09fa60385a ("ASoC: mchp-spdifrx: fix controls which rely on rsr register")
Link: https://github.com/ClangBuiltLinux/linux/issues/1797
Signed-off-by: Nathan Chancellor <nathan(a)kernel.org>
---
sound/soc/atmel/mchp-spdifrx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c
index 526dd3a17471..eb0c0ef4541e 100644
--- a/sound/soc/atmel/mchp-spdifrx.c
+++ b/sound/soc/atmel/mchp-spdifrx.c
@@ -429,7 +429,7 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
- u32 mr;
+ u32 mr = 0;
int ret;
dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
---
base-commit: 1d78f19d90d1932c83d8c96c6f86fbb06c05e29a
change-id: 20230202-mchp-spdifrx-fix-uninit-mr-80528f2024c9
Best regards,
--
Nathan Chancellor <nathan(a)kernel.org>
3
2

[PATCH] ASoC: SOF: amd: Fix for handling spurious interrupts from DSP
by V sujith kumar Reddy 02 Feb '23
by V sujith kumar Reddy 02 Feb '23
02 Feb '23
As interrupts are Level-triggered,unless and until we deassert the register
the interrupts are generated which causes spurious interrupts unhandled.
Now we deasserted the interrupt at top half which solved the below
"nobody cared" warning.
warning reported in dmesg:
irq 80: nobody cared (try booting with the "irqpoll" option)
CPU: 5 PID: 2735 Comm: irq/80-AudioDSP
Not tainted 5.15.86-15817-g4c19f3e06d49 #1 1bd3fd932cf58caacc95b0504d6ea1e3eab22289
Hardware name: Google Skyrim/Skyrim, BIOS Google_Skyrim.15303.0.0 01/03/2023
Call Trace:
<IRQ>
dump_stack_lvl+0x69/0x97
__report_bad_irq+0x3a/0xae
note_interrupt+0x1a9/0x1e3
handle_irq_event_percpu+0x4b/0x6e
handle_irq_event+0x36/0x5b
handle_fasteoi_irq+0xae/0x171
__common_interrupt+0x48/0xc4
</IRQ>
handlers:
acp_irq_handler [snd_sof_amd_acp] threaded [<000000007e089f34>] acp_irq_thread [snd_sof_amd_acp]
Disabling IRQ #80
Signed-off-by: V sujith kumar Reddy <Vsujithkumar.Reddy(a)amd.com>
---
sound/soc/sof/amd/acp.c | 36 +++++++++++++++---------------------
1 file changed, 15 insertions(+), 21 deletions(-)
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 6bd2888fbb66..d5ccd4d09278 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -318,7 +318,6 @@ static irqreturn_t acp_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
- unsigned int base = desc->dsp_intr_base;
unsigned int val, count = ACP_HW_SEM_RETRY_COUNT;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat);
@@ -328,28 +327,20 @@ static irqreturn_t acp_irq_thread(int irq, void *context)
return IRQ_HANDLED;
}
- val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
- if (val & ACP_DSP_TO_HOST_IRQ) {
- while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) {
- /* Wait until acquired HW Semaphore lock or timeout */
- count--;
- if (!count) {
- dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
- return IRQ_NONE;
- }
+ while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) {
+ /* Wait until acquired HW Semaphore lock or timeout */
+ count--;
+ if (!count) {
+ dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
+ return IRQ_NONE;
}
-
- sof_ops(sdev)->irq_thread(irq, sdev);
- val |= ACP_DSP_TO_HOST_IRQ;
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, val);
-
- /* Unlock or Release HW Semaphore */
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset, 0x0);
-
- return IRQ_HANDLED;
}
- return IRQ_NONE;
+ sof_ops(sdev)->irq_thread(irq, sdev);
+ /* Unlock or Release HW Semaphore */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset, 0x0);
+
+ return IRQ_HANDLED;
};
static irqreturn_t acp_irq_handler(int irq, void *dev_id)
@@ -360,8 +351,11 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id)
unsigned int val;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
- if (val)
+ if (val) {
+ val |= ACP_DSP_TO_HOST_IRQ;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET, val);
return IRQ_WAKE_THREAD;
+ }
return IRQ_NONE;
}
--
2.25.1
2
1
It's been reported that a number of laptops have a low volume
level from the digital microphone compared to Windows.
AMD offers a register that can adjust the gain for PDM which is not
configured at maximum gain by default.
To fix this change the default for all 3 drivers to raise the gain
but also offer a module parameter. The module parameter can be used
for debugging if the gain is too high on a given laptop.
This is intentionally split into multiple patches for default and
parameter so that if the default really does behave better universally
we can bring it back to stable too later.
v2->v3:
* Use clamp and clear properly
Mario Limonciello (6):
ASoC: amd: yc: Adjust the gain for PDM DMIC
ASoC: amd: yc: Add a module parameter to influence pdm_gain
ASoC: amd: renoir: Adjust the gain for PDM DMIC
ASoC: amd: renoir: Add a module parameter to influence pdm_gain
ASoC: amd: ps: Adjust the gain for PDM DMIC
ASoC: amd: ps: Add a module parameter to influence pdm_gain
sound/soc/amd/ps/acp63.h | 2 +-
sound/soc/amd/ps/ps-pdm-dma.c | 8 +++++++-
sound/soc/amd/renoir/acp3x-pdm-dma.c | 8 +++++++-
sound/soc/amd/renoir/rn_acp3x.h | 2 +-
sound/soc/amd/yc/acp6x-pdm-dma.c | 8 +++++++-
sound/soc/amd/yc/acp6x.h | 2 +-
6 files changed, 24 insertions(+), 6 deletions(-)
--
2.34.1
3
11