* [PATCH v7] i2c: Add support for NXP PCA984x family.
From: Adrian Fiergolski @ 2017-12-25 21:26 UTC (permalink / raw)
To: linux-i2c-u79uwXL29TY76Z2rM5mHXA
Cc: peda-koto5C5qi+TLoDKTGw+V6w, devicetree-u79uwXL29TY76Z2rM5mHXA,
robh-DgEjT+Ai2ygdnm+yROfE0A, Adrian Fiergolski
In-Reply-To: <20171220182745.lo6i27ucfhjekmjp@rob-hp-laptop>
This patch extends the current i2c-mux-pca954x driver and adds support for
a newer PCA984x family of the I2C switches and multiplexers from NXP.
Signed-off-by: Adrian Fiergolski <adrian.fiergolski-vJEk5272eHo@public.gmane.org>
---
Following Rob's and Peter's remarks, bindings contain now one valid
combination of compatibles per line.
.../devicetree/bindings/i2c/i2c-mux-pca954x.txt | 13 ++++++--
drivers/i2c/muxes/Kconfig | 6 ++--
drivers/i2c/muxes/i2c-mux-pca954x.c | 38 +++++++++++++++++++---
3 files changed, 48 insertions(+), 9 deletions(-)
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
index aa097045a10e..34d91501342e 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
@@ -1,10 +1,19 @@
* NXP PCA954x I2C bus switch
+The driver supports NXP PCA954x and PCA984x I2C mux/switch devices.
+
Required Properties:
- compatible: Must contain one of the following.
- "nxp,pca9540", "nxp,pca9542", "nxp,pca9543", "nxp,pca9544",
- "nxp,pca9545", "nxp,pca9546", "nxp,pca9547", "nxp,pca9548"
+ "nxp,pca9540",
+ "nxp,pca9542",
+ "nxp,pca9543",
+ "nxp,pca9544",
+ "nxp,pca9545",
+ "nxp,pca9546", "nxp,pca9846",
+ "nxp,pca9547", "nxp,pca9847",
+ "nxp,pca9548", "nxp,pca9848",
+ "nxp,pca9849"
- reg: The I2C address of the device.
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 0f5c8fc36625..52a4a922e7e6 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -64,11 +64,11 @@ config I2C_MUX_PCA9541
will be called i2c-mux-pca9541.
config I2C_MUX_PCA954x
- tristate "Philips PCA954x I2C Mux/switches"
+ tristate "NXP PCA954x and PCA984x I2C Mux/switches"
depends on GPIOLIB || COMPILE_TEST
help
- If you say yes here you get support for the Philips PCA954x
- I2C mux/switch devices.
+ If you say yes here you get support for the NXP PCA954x
+ and PCA984x I2C mux/switch devices.
This driver can also be built as a module. If so, the module
will be called i2c-mux-pca954x.
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 2ca068d8b92d..fbb84c7ef282 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -4,11 +4,11 @@
* Copyright (c) 2008-2009 Rodolfo Giometti <giometti-k2GhghHVRtY@public.gmane.org>
* Copyright (c) 2008-2009 Eurotech S.p.A. <info-ymFC7zkAqBI1GQ1Ptb7lUw@public.gmane.org>
*
- * This module supports the PCA954x series of I2C multiplexer/switch chips
- * made by Philips Semiconductors.
+ * This module supports the PCA954x and PCA954x series of I2C multiplexer/switch
+ * chips made by NXP Semiconductors.
* This includes the:
- * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547
- * and PCA9548.
+ * PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, PCA9547,
+ * PCA9548, PCA9846, PCA9847, PCA9848 and PCA9849.
*
* These chips are all controlled via the I2C bus itself, and all have a
* single 8-bit register. The upstream "parent" bus fans out to two,
@@ -63,6 +63,10 @@ enum pca_type {
pca_9546,
pca_9547,
pca_9548,
+ pca_9846,
+ pca_9847,
+ pca_9848,
+ pca_9849,
};
struct chip_desc {
@@ -129,6 +133,24 @@ static const struct chip_desc chips[] = {
.nchans = 8,
.muxtype = pca954x_isswi,
},
+ [pca_9846] = {
+ .nchans = 4,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9847] = {
+ .nchans = 8,
+ .enable = 0x8,
+ .muxtype = pca954x_ismux,
+ },
+ [pca_9848] = {
+ .nchans = 8,
+ .muxtype = pca954x_isswi,
+ },
+ [pca_9849] = {
+ .nchans = 4,
+ .enable = 0x4,
+ .muxtype = pca954x_ismux,
+ },
};
static const struct i2c_device_id pca954x_id[] = {
@@ -140,6 +162,10 @@ static const struct i2c_device_id pca954x_id[] = {
{ "pca9546", pca_9546 },
{ "pca9547", pca_9547 },
{ "pca9548", pca_9548 },
+ { "pca9846", pca_9846 },
+ { "pca9847", pca_9847 },
+ { "pca9848", pca_9848 },
+ { "pca9849", pca_9849 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pca954x_id);
@@ -154,6 +180,10 @@ static const struct of_device_id pca954x_of_match[] = {
{ .compatible = "nxp,pca9546", .data = &chips[pca_9546] },
{ .compatible = "nxp,pca9547", .data = &chips[pca_9547] },
{ .compatible = "nxp,pca9548", .data = &chips[pca_9548] },
+ { .compatible = "nxp,pca9846", .data = &chips[pca_9846] },
+ { .compatible = "nxp,pca9847", .data = &chips[pca_9847] },
+ { .compatible = "nxp,pca9848", .data = &chips[pca_9848] },
+ { .compatible = "nxp,pca9849", .data = &chips[pca_9849] },
{}
};
MODULE_DEVICE_TABLE(of, pca954x_of_match);
--
2.14.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH v3] ARM: dts: bcm283x: Fix pin function of JTAG pins
From: Florian Fainelli @ 2017-12-25 20:30 UTC (permalink / raw)
To: henryzhang62-Re5JQEeQqe8AvxtiuMwx3w,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, rjui-dY08KVG/lbpWk0Htik3J/w,
sbranden-dY08KVG/lbpWk0Htik3J/w,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
eric-WhKQ6XTQaPysTnJN9+BGXg,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
stefan.wahren-eS4NqCHxEME
In-Reply-To: <1514231010-19554-1-git-send-email-henryzhang62-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Le 12/25/17 à 11:43, henryzhang62-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org a écrit :
> From: Henry Zhang <henryzhang62-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> BCM2835 ARM Peripherals doc shows gpio pins 4, 5, 6, 12 and 13
> carry altenate function, ALT5 for ARM JTAG
>
> Fixes: 21ff843931b2 ("ARM: dts: bcm283x: Define standard pinctrl
> groups in the gpio node.")
>
Almost there, please don't break the Fixes: line onto multiple lines,
this may, or may not require resending depending on what Eric says.
Thank you!
> Signed-off-by: Henry Zhang <henryzhang62-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> Acked-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
> ---
> arch/arm/boot/dts/bcm283x.dtsi | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
> index dcde93c..64971e4 100644
> --- a/arch/arm/boot/dts/bcm283x.dtsi
> +++ b/arch/arm/boot/dts/bcm283x.dtsi
> @@ -251,7 +251,7 @@
>
> jtag_gpio4: jtag_gpio4 {
> brcm,pins = <4 5 6 12 13>;
> - brcm,function = <BCM2835_FSEL_ALT4>;
> + brcm,function = <BCM2835_FSEL_ALT5>;
> };
> jtag_gpio22: jtag_gpio22 {
> brcm,pins = <22 23 24 25 26 27>;
>
--
Florian
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v3] ARM: dts: bcm283x: Fix pin function of JTAG pins
From: henryzhang62-Re5JQEeQqe8AvxtiuMwx3w @ 2017-12-25 19:43 UTC (permalink / raw)
To: devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-I+IVW8TIWO2tmTQ+vhA3Yw, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
rjui-dY08KVG/lbpWk0Htik3J/w, sbranden-dY08KVG/lbpWk0Htik3J/w,
bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
eric-WhKQ6XTQaPysTnJN9+BGXg,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
stefan.wahren-eS4NqCHxEME, Henry Zhang
From: Henry Zhang <henryzhang62-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
BCM2835 ARM Peripherals doc shows gpio pins 4, 5, 6, 12 and 13
carry altenate function, ALT5 for ARM JTAG
Fixes: 21ff843931b2 ("ARM: dts: bcm283x: Define standard pinctrl
groups in the gpio node.")
Signed-off-by: Henry Zhang <henryzhang62-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Acked-by: Stefan Wahren <stefan.wahren-eS4NqCHxEME@public.gmane.org>
---
arch/arm/boot/dts/bcm283x.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
index dcde93c..64971e4 100644
--- a/arch/arm/boot/dts/bcm283x.dtsi
+++ b/arch/arm/boot/dts/bcm283x.dtsi
@@ -251,7 +251,7 @@
jtag_gpio4: jtag_gpio4 {
brcm,pins = <4 5 6 12 13>;
- brcm,function = <BCM2835_FSEL_ALT4>;
+ brcm,function = <BCM2835_FSEL_ALT5>;
};
jtag_gpio22: jtag_gpio22 {
brcm,pins = <22 23 24 25 26 27>;
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH] ARM: make ARCH_S3C24XX select USE_OF and clean-up boot/dts/Makefile
From: Krzysztof Kozlowski @ 2017-12-25 17:39 UTC (permalink / raw)
To: Masahiro Yamada
Cc: arm-soc, Olof Johansson, Arnd Bergmann,
devicetree-u79uwXL29TY76Z2rM5mHXA, Linux Kernel Mailing List,
Rob Herring, Mark Rutland, Russell King, linux-arm-kernel
In-Reply-To: <CAK7LNASWV7F+rjEq-RN+QGwx1fbnaL1oeWmu178A_a-Q=5f95g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Sun, Dec 24, 2017 at 6:19 PM, Masahiro Yamada
<yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org> wrote:
> 2017-12-22 21:41 GMT+09:00 Krzysztof Kozlowski <krzk-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>:
>> On Mon, Nov 27, 2017 at 3:19 AM, Masahiro Yamada
>> <yamada.masahiro-uWyLwvC0a2jby3iVrkZq2A@public.gmane.org> wrote:
>>> ARCH_S3C24XX is a very exceptional platform that some DT files in
>>> arch/arm/boot/dts/, but does not select USE_OF.
>>
>> Not entirely. The platform does select USE_OF - when MACH_S3C2416_DT
>> is chosen. For other boards USE_OF is not necessary because they do
>> not use DT. Why you need to select it for entire arch?
>>
>> Best regards,
>> Krzysztof
>>
>
>
> The reason is simple - to avoid compile error.
>
>
> If I simply drop ifeq ($(CONFIG_OF),y)
> but do not select ARCH_S3C24XX,
> s3c2410_defconfig failed to build.
>
>
> $ make -s ARCH=arm mrproper
> $ make -s ARCH=arm s3c2410_defconfig
> $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
> ...
>
> DTC arch/arm/boot/dts/s3c2416-smdk2416.dtb
> /bin/sh: 1: ./scripts/dtc/dtc: not found
> scripts/Makefile.lib:310: recipe for target
> 'arch/arm/boot/dts/s3c2416-smdk2416.dtb' failed
> make[1]: *** [arch/arm/boot/dts/s3c2416-smdk2416.dtb] Error 127
> arch/arm/Makefile:349: recipe for target 'dtbs' failed
> make: *** [dtbs] Error 2
>
>
> Another solution would be to enable dtb by CONFIG_MACH_S3C2416_DT.
>
>
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index d0381e9..950b5dd 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -800,7 +798,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
> rk3288-veyron-pinky.dtb \
> rk3288-veyron-speedy.dtb \
> rk3288-vyasa.dtb
> -dtb-$(CONFIG_ARCH_S3C24XX) += \
> +dtb-$(CONFIG_MACH_S3C2416_DT) += \
> s3c2416-smdk2416.dtb
> dtb-$(CONFIG_ARCH_S3C64XX) += \
> s3c6410-mini6410.dtb \
>
>
>
> If you migrate S3C24XX platform to DT,
> per-board CONFIG option will go away.
>
> So, I think dtb-$(CONFIG_ARCH_S3C24XX) is OK.
I think this second solution - using CONFIG_MACH_S3C2416_DT - makes
more sense because:
1, S3C24xx will not be converted to DT. This is a legacy platform.
2. DT version supports only part of boards and peripherals so most of
existing platforms will use non-DT boardfiles. Enabling OF on all of
them is not useful for them.
3. The same error and solution probably applies to MACH_S3C64XX.
Best regards,
Krzysztof
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v2] ASoC: max98373: Added Amplifier Driver
From: Ryan Lee @ 2017-12-25 15:10 UTC (permalink / raw)
To: lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k, arnd-r2nGTMty4D4,
afd-l0cyMroinI0, robert.jarzmik-GANU6spQydw,
supercraig0719-Re5JQEeQqe8AvxtiuMwx3w,
ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR,
jbrunet-rdvid1DuHRBWk0Htik3J/w, dannenberg-l0cyMroinI0,
romain.perier-ZGY8ohtN/8qB+jHODAdFcQ,
bryce.ferguson-lFk7bPDcGtkY5TsXZYaR1UEOCMrvLtNR,
kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ,
m-stecklein-l0cyMroinI0, alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
Cc: ryan.lee.maxim-Re5JQEeQqe8AvxtiuMwx3w
Signed-off-by: Ryan Lee <ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>
---
Changes since v1:
* Removed 'codec' from 'max98373_priv' structure
: Now 'max98373_set_clock' function use 'dai->codec.dev' instead of using 'max98373->codec.dev'.
* Removed 'max98373_dai_set_sysclk' function
: This function is not necessary. Removed 'sysclk' from 'max98373_priv' as well.
* Removed 'iface' from 'max98373_priv' structure
: There is no function who refer max98373->iface variable.
* Added SPDX-License-Identifier
.../devicetree/bindings/sound/max98373.txt | 43 +
sound/soc/codecs/Kconfig | 5 +
sound/soc/codecs/Makefile | 2 +
sound/soc/codecs/max98373.c | 974 +++++++++++++++++++++
sound/soc/codecs/max98373.h | 212 +++++
5 files changed, 1236 insertions(+)
create mode 100644 Documentation/devicetree/bindings/sound/max98373.txt
create mode 100644 sound/soc/codecs/max98373.c
create mode 100644 sound/soc/codecs/max98373.h
diff --git a/Documentation/devicetree/bindings/sound/max98373.txt b/Documentation/devicetree/bindings/sound/max98373.txt
new file mode 100644
index 0000000..22cd259
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/max98373.txt
@@ -0,0 +1,43 @@
+Maxim Integrated MAX98373 Speaker Amplifier
+
+This device supports I2C.
+
+Required properties:
+
+ - compatible : should be one of the following
+ - "maxim,max98373"
+
+ - reg : the I2C address of the device.
+
+Optional properties:
+
+ - maxim,vmon-slot-no : slot number used to send voltage information
+ or in inteleave mode this will be used as
+ interleave slot.
+ slot range : 0 ~ 15, Default : 0
+
+ - maxim,imon-slot-no : slot number used to send current information
+ slot range : 0 ~ 15, Default : 0
+
+ - maxim,spkfb-slot-no : slot number used to send speaker feedback information
+ slot range : 0 ~ 15, Default : 0
+
+ - maxim,interleave-mode : When using two MAX98373 in a system it is
+ possible to create ADC data that that will
+ overflow the frame size. Digital Audio Interleave
+ mode provides a means to output VMON and IMON data
+ from two devices on a single DOUT line when running
+ smaller frames sizes such as 32 BCLKS per LRCLK or
+ 48 BCLKS per LRCLK.
+ Range : 0 (off), 1 (on), Default : 0
+
+Example:
+
+codec: max98373@31 {
+ compatible = "maxim,max98373";
+ reg = <0x31>;
+ maxim,vmon-slot-no = <0>;
+ maxim,imon-slot-no = <1>;
+ maxim,spkfb-slot-no = <2>;
+ maxim,interleave-mode = <0>;
+};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8b02bc8..9af2588 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -95,6 +95,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX98925 if I2C
select SND_SOC_MAX98926 if I2C
select SND_SOC_MAX98927 if I2C
+ select SND_SOC_MAX98373 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9860 if I2C
select SND_SOC_MAX9768 if I2C
@@ -626,6 +627,10 @@ config SND_SOC_MAX98927
tristate "Maxim Integrated MAX98927 Speaker Amplifier"
depends on I2C
+config SND_SOC_MAX98373
+ tristate "Maxim Integrated MAX98373 Speaker Amplifier"
+ depends on I2C
+
config SND_SOC_MAX9850
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0977349..49db8e9 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -90,6 +90,7 @@ snd-soc-max9867-objs := max9867.o
snd-soc-max98925-objs := max98925.o
snd-soc-max98926-objs := max98926.o
snd-soc-max98927-objs := max98927.o
+snd-soc-max98373-objs := max98373.o
snd-soc-max9850-objs := max9850.o
snd-soc-max9860-objs := max9860.o
snd-soc-mc13783-objs := mc13783.o
@@ -334,6 +335,7 @@ obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o
obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o
+obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c
new file mode 100644
index 0000000..93d6dc7
--- /dev/null
+++ b/sound/soc/codecs/max98373.c
@@ -0,0 +1,974 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2017, Maxim Integrated */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98373.h"
+
+static struct reg_default max98373_reg[] = {
+ {MAX98373_R2000_SW_RESET, 0x00},
+ {MAX98373_R2001_INT_RAW1, 0x00},
+ {MAX98373_R2002_INT_RAW2, 0x00},
+ {MAX98373_R2003_INT_RAW3, 0x00},
+ {MAX98373_R2004_INT_STATE1, 0x00},
+ {MAX98373_R2005_INT_STATE2, 0x00},
+ {MAX98373_R2006_INT_STATE3, 0x00},
+ {MAX98373_R2007_INT_FLAG1, 0x00},
+ {MAX98373_R2008_INT_FLAG2, 0x00},
+ {MAX98373_R2009_INT_FLAG3, 0x00},
+ {MAX98373_R200A_INT_EN1, 0x00},
+ {MAX98373_R200B_INT_EN2, 0x00},
+ {MAX98373_R200C_INT_EN3, 0x00},
+ {MAX98373_R200D_INT_FLAG_CLR1, 0x00},
+ {MAX98373_R200E_INT_FLAG_CLR2, 0x00},
+ {MAX98373_R200F_INT_FLAG_CLR3, 0x00},
+ {MAX98373_R2010_IRQ_CTRL, 0x00},
+ {MAX98373_R2014_THERM_WARN_THRESH, 0x10},
+ {MAX98373_R2015_THERM_SHDN_THRESH, 0x27},
+ {MAX98373_R2016_THERM_HYSTERESIS, 0x01},
+ {MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0},
+ {MAX98373_R2018_THERM_FOLDBACK_EN, 0x00},
+ {MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55},
+ {MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE},
+ {MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2022_PCM_TX_SRC_1, 0x00},
+ {MAX98373_R2023_PCM_TX_SRC_2, 0x00},
+ {MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0},
+ {MAX98373_R2025_AUDIO_IF_MODE, 0x00},
+ {MAX98373_R2026_PCM_CLOCK_RATIO, 0x04},
+ {MAX98373_R2027_PCM_SR_SETUP_1, 0x08},
+ {MAX98373_R2028_PCM_SR_SETUP_2, 0x88},
+ {MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00},
+ {MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00},
+ {MAX98373_R202B_PCM_RX_EN, 0x00},
+ {MAX98373_R202C_PCM_TX_EN, 0x00},
+ {MAX98373_R202E_ICC_RX_CH_EN_1, 0x00},
+ {MAX98373_R202F_ICC_RX_CH_EN_2, 0x00},
+ {MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF},
+ {MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF},
+ {MAX98373_R2032_ICC_LINK_EN_CFG, 0x30},
+ {MAX98373_R2034_ICC_TX_CNTL, 0x00},
+ {MAX98373_R2035_ICC_TX_EN, 0x00},
+ {MAX98373_R2036_SOUNDWIRE_CTRL, 0x05},
+ {MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00},
+ {MAX98373_R203E_AMP_PATH_GAIN, 0x08},
+ {MAX98373_R203F_AMP_DSP_CFG, 0x02},
+ {MAX98373_R2040_TONE_GEN_CFG, 0x00},
+ {MAX98373_R2041_AMP_CFG, 0x03},
+ {MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00},
+ {MAX98373_R2043_AMP_EN, 0x00},
+ {MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04},
+ {MAX98373_R2047_IV_SENSE_ADC_EN, 0x00},
+ {MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00},
+ {MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00},
+ {MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00},
+ {MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00},
+ {MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00},
+ {MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00},
+ {MAX98373_R2090_BDE_LVL_HOLD, 0x00},
+ {MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00},
+ {MAX98373_R2092_BDE_CLIPPER_MODE, 0x00},
+ {MAX98373_R2097_BDE_L1_THRESH, 0x00},
+ {MAX98373_R2098_BDE_L2_THRESH, 0x00},
+ {MAX98373_R2099_BDE_L3_THRESH, 0x00},
+ {MAX98373_R209A_BDE_L4_THRESH, 0x00},
+ {MAX98373_R209B_BDE_THRESH_HYST, 0x00},
+ {MAX98373_R20A8_BDE_L1_CFG_1, 0x00},
+ {MAX98373_R20A9_BDE_L1_CFG_2, 0x00},
+ {MAX98373_R20AA_BDE_L1_CFG_3, 0x00},
+ {MAX98373_R20AB_BDE_L2_CFG_1, 0x00},
+ {MAX98373_R20AC_BDE_L2_CFG_2, 0x00},
+ {MAX98373_R20AD_BDE_L2_CFG_3, 0x00},
+ {MAX98373_R20AE_BDE_L3_CFG_1, 0x00},
+ {MAX98373_R20AF_BDE_L3_CFG_2, 0x00},
+ {MAX98373_R20B0_BDE_L3_CFG_3, 0x00},
+ {MAX98373_R20B1_BDE_L4_CFG_1, 0x00},
+ {MAX98373_R20B2_BDE_L4_CFG_2, 0x00},
+ {MAX98373_R20B3_BDE_L4_CFG_3, 0x00},
+ {MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00},
+ {MAX98373_R20B5_BDE_EN, 0x00},
+ {MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00},
+ {MAX98373_R20D1_DHT_CFG, 0x01},
+ {MAX98373_R20D2_DHT_ATTACK_CFG, 0x02},
+ {MAX98373_R20D3_DHT_RELEASE_CFG, 0x03},
+ {MAX98373_R20D4_DHT_EN, 0x00},
+ {MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00},
+ {MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00},
+ {MAX98373_R20E2_LIMITER_EN, 0x00},
+ {MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00},
+ {MAX98373_R20FF_GLOBAL_SHDN, 0x00},
+ {MAX98373_R21FF_REV_ID, 0x42},
+};
+
+static int max98373_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+ unsigned int format = 0;
+ unsigned int invert = 0;
+
+ dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(codec->dev, "DAI invert mode unsupported\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ format = MAX98373_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ format = MAX98373_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ format = MAX98373_PCM_FORMAT_TDM_MODE1;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ format = MAX98373_PCM_FORMAT_TDM_MODE0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98373_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ return 0;
+}
+
+/* BCLKs per LRCLK */
+static const int bclk_sel_table[] = {
+ 32, 48, 64, 96, 128, 192, 256, 384, 512, 320,
+};
+
+static int max98373_get_bclk_sel(int bclk)
+{
+ int i;
+ /* match BCLKs per LRCLK */
+ for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) {
+ if (bclk_sel_table[i] == bclk)
+ return i + 2;
+ }
+ return 0;
+}
+static int max98373_set_clock(struct snd_soc_codec *codec,
+ struct snd_pcm_hw_params *params)
+{
+ struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98373->ch_size;
+ int value;
+
+ if (!max98373->tdm_mode) {
+ /* BCLK configuration */
+ value = max98373_get_bclk_sel(blr_clk_ratio);
+ if (!value) {
+ dev_err(codec->dev, "format unsupported %d\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ }
+ return 0;
+}
+
+static int max98373_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(codec->dev, "format unsupported %d\n",
+ params_format(params));
+ goto err;
+ }
+
+ max98373->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(codec->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98373_PCM_SR_SET1_SR_48000;
+ break;
+ default:
+ dev_err(codec->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2027_PCM_SR_SETUP_1,
+ MAX98373_PCM_SR_SET1_SR_MASK,
+ sampling_rate);
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT);
+
+ /* set sampling rate of IV */
+ if (max98373->interleave_mode &&
+ sampling_rate > MAX98373_PCM_SR_SET1_SR_16000)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate - 3);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2028_PCM_SR_SETUP_2,
+ MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
+
+ return max98373_set_clock(codec, params);
+err:
+ return -EINVAL;
+}
+
+static int max98373_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+ int bsel = 0;
+ unsigned int chan_sz = 0;
+ unsigned int mask;
+ int x, slot_found;
+
+ max98373->tdm_mode = true;
+
+ /* BCLK configuration */
+ bsel = max98373_get_bclk_sel(slots * slot_width);
+ if (bsel == 0) {
+ dev_err(codec->dev, "BCLK %d not supported\n",
+ slots * slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2026_PCM_CLOCK_RATIO,
+ MAX98373_PCM_CLK_SETUP_BSEL_MASK,
+ bsel);
+
+ /* Channel size configuration */
+ switch (slot_width) {
+ case 16:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(codec->dev, "format unsupported %d\n",
+ slot_width);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ /* Rx slot configuration */
+ slot_found = 0;
+ mask = rx_mask;
+ for (x = 0 ; x < 16 ; x++, mask >>= 1) {
+ if (mask & 0x1) {
+ if (slot_found == 0)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ MAX98373_PCM_TO_SPK_CH0_SRC_MASK, x);
+ else
+ regmap_write(max98373->regmap,
+ MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+ x);
+ slot_found++;
+ if (slot_found > 1)
+ break;
+ }
+ }
+
+ /* Tx slot Hi-Z configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ ~tx_mask & 0xFF);
+ regmap_write(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ (~tx_mask & 0xFF00) >> 8);
+
+ return 0;
+}
+
+#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000
+
+#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops max98373_dai_ops = {
+ .set_fmt = max98373_dai_set_fmt,
+ .hw_params = max98373_dai_hw_params,
+ .set_tdm_slot = max98373_dai_tdm_slot,
+};
+
+static int max98373_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R20FF_GLOBAL_SHDN,
+ MAX98373_GLOBAL_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R20FF_GLOBAL_SHDN,
+ MAX98373_GLOBAL_EN_MASK, 0);
+ max98373->tdm_mode = 0;
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98373_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+ 3, max98373_switch_text);
+
+static const struct snd_kcontrol_new max98373_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_kcontrol_new max98373_vi_control =
+ SOC_DAPM_SINGLE("Switch", MAX98373_R202C_PCM_TX_EN, 0, 1, 0);
+
+static const struct snd_kcontrol_new max98373_spkfb_control =
+ SOC_DAPM_SINGLE("Switch", MAX98373_R2043_AMP_EN, 1, 1, 0);
+
+static const struct snd_soc_dapm_widget max98373_dapm_widgets[] = {
+SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
+ MAX98373_R202B_PCM_RX_EN, 0, 0, max98373_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98373_dai_controls),
+SND_SOC_DAPM_OUTPUT("BE_OUT"),
+SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
+ MAX98373_R2047_IV_SENSE_ADC_EN, 0, 0),
+SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
+ MAX98373_R2047_IV_SENSE_ADC_EN, 1, 0),
+SND_SOC_DAPM_AIF_OUT("Speaker FB Sense", "HiFi Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
+ &max98373_vi_control),
+SND_SOC_DAPM_SWITCH("SpkFB Sense", SND_SOC_NOPM, 0, 0,
+ &max98373_spkfb_control),
+SND_SOC_DAPM_SIGGEN("VMON"),
+SND_SOC_DAPM_SIGGEN("IMON"),
+SND_SOC_DAPM_SIGGEN("FBMON"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, 0, -50, 0);
+static const DECLARE_TLV_DB_RANGE(max98373_spk_tlv,
+ 0, 8, TLV_DB_SCALE_ITEM(0, 50, 0),
+ 9, 10, TLV_DB_SCALE_ITEM(500, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_spkgain_max_tlv,
+ 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_step_size_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(25, 25, 0),
+ 2, 4, TLV_DB_SCALE_ITEM(100, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_spkgain_min_tlv,
+ 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_dht_rotation_point_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0),
+ 2, 7, TLV_DB_SCALE_ITEM(-200, -100, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(-1000, -200, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(-1500, -300, 0),
+ 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0),
+ 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0),
+);
+static const DECLARE_TLV_DB_RANGE(max98373_limiter_thresh_tlv,
+ 0, 15, TLV_DB_SCALE_ITEM(0, -100, 0),
+);
+
+static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv,
+ 0, 60, TLV_DB_SCALE_ITEM(0, -25, 0),
+);
+
+static bool max98373_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R2001_INT_RAW1 ... MAX98373_R200C_INT_EN3:
+ case MAX98373_R2010_IRQ_CTRL:
+ case MAX98373_R2014_THERM_WARN_THRESH
+ ... MAX98373_R2018_THERM_FOLDBACK_EN:
+ case MAX98373_R201E_PIN_DRIVE_STRENGTH
+ ... MAX98373_R2036_SOUNDWIRE_CTRL:
+ case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN:
+ case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG
+ ... MAX98373_R2047_IV_SENSE_ADC_EN:
+ case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE
+ ... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN:
+ case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE:
+ case MAX98373_R2097_BDE_L1_THRESH
+ ... MAX98373_R209B_BDE_THRESH_HYST:
+ case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3:
+ case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN:
+ case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN:
+ case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG
+ ... MAX98373_R20FF_GLOBAL_SHDN:
+ case MAX98373_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98373_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3:
+ case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK:
+ case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK:
+ case MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+ case MAX98373_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const max98373_output_voltage_lvl_text[] = {
+ "5.43V", "6.09V", "6.83V", "7.67V", "8.60V",
+ "9.65V", "10.83V", "12.15V", "13.63V", "15.29V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_out_volt_enum,
+ MAX98373_R203E_AMP_PATH_GAIN, 0,
+ max98373_output_voltage_lvl_text);
+
+static const char * const max98373_dht_attack_rate_text[] = {
+ "17.5us", "35us", "70us", "140us",
+ "280us", "560us", "1120us", "2240us"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_dht_attack_rate_enum,
+ MAX98373_R20D2_DHT_ATTACK_CFG, 0,
+ max98373_dht_attack_rate_text);
+
+static const char * const max98373_dht_release_rate_text[] = {
+ "45ms", "225ms", "450ms", "1150ms",
+ "2250ms", "3100ms", "4500ms", "6750ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_dht_release_rate_enum,
+ MAX98373_R20D3_DHT_RELEASE_CFG, 0,
+ max98373_dht_release_rate_text);
+
+static const char * const max98373_limiter_attack_rate_text[] = {
+ "10us", "20us", "40us", "80us",
+ "160us", "320us", "640us", "1.28ms",
+ "2.56ms", "5.12ms", "10.24ms", "20.48ms",
+ "40.96ms", "81.92ms", "16.384ms", "32.768ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_limiter_attack_rate_enum,
+ MAX98373_R20E1_LIMITER_ATK_REL_RATES, 4,
+ max98373_limiter_attack_rate_text);
+
+static const char * const max98373_limiter_release_rate_text[] = {
+ "40us", "80us", "160us", "320us",
+ "640us", "1.28ms", "2.56ms", "5.120ms",
+ "10.24ms", "20.48ms", "40.96ms", "81.92ms",
+ "163.84ms", "327.68ms", "655.36ms", "1310.72ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_limiter_release_rate_enum,
+ MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0,
+ max98373_limiter_release_rate_text);
+
+static const char * const max98373_ADC_samplerate_text[] = {
+ "333kHz", "192kHz", "64kHz", "48kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum,
+ MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0,
+ max98373_ADC_samplerate_text);
+
+static const struct snd_kcontrol_new max98373_snd_controls[] = {
+SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
+SOC_SINGLE("Volume Location Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Up Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT, 1, 0),
+SOC_SINGLE("Ramp Down Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT, 1, 0),
+SOC_SINGLE("CLK Monitor Switch", MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG,
+ MAX98373_CLOCK_MON_SHIFT, 1, 0),
+SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_DITH_SHIFT, 1, 0),
+SOC_SINGLE("DC Blocker Switch", MAX98373_R203F_AMP_DSP_CFG,
+ MAX98373_AMP_DSP_CFG_DCBLK_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Digital Volume", MAX98373_R203D_AMP_DIG_VOL_CTRL,
+ 0, 0x7F, 0, max98373_digital_tlv),
+SOC_SINGLE_TLV("Speaker Volume", MAX98373_R203E_AMP_PATH_GAIN,
+ MAX98373_SPK_DIGI_GAIN_SHIFT, 10, 0, max98373_spk_tlv),
+SOC_SINGLE_TLV("FS Max Volume", MAX98373_R203E_AMP_PATH_GAIN,
+ MAX98373_FS_GAIN_MAX_SHIFT, 9, 0, max98373_spkgain_max_tlv),
+SOC_ENUM("Output Voltage", max98373_out_volt_enum),
+/* Dynamic Headroom Tracking */
+SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN,
+ MAX98373_DHT_EN_SHIFT, 1, 0),
+SOC_SINGLE_TLV("DHT Gain Min", MAX98373_R20D1_DHT_CFG,
+ MAX98373_DHT_SPK_GAIN_MIN_SHIFT, 9, 0, max98373_dht_spkgain_min_tlv),
+SOC_SINGLE_TLV("DHT Rot Pnt", MAX98373_R20D1_DHT_CFG,
+ MAX98373_DHT_ROT_PNT_SHIFT, 15, 0, max98373_dht_rotation_point_tlv),
+SOC_SINGLE_TLV("DHT Attack Step", MAX98373_R20D2_DHT_ATTACK_CFG,
+ MAX98373_DHT_ATTACK_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
+SOC_SINGLE_TLV("DHT Release Step", MAX98373_R20D3_DHT_RELEASE_CFG,
+ MAX98373_DHT_RELEASE_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv),
+SOC_ENUM("DHT Attack Rate", max98373_dht_attack_rate_enum),
+SOC_ENUM("DHT Release Rate", max98373_dht_release_rate_enum),
+/* ADC configuration */
+SOC_SINGLE("ADC PVDD CH Switch", MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0, 1, 0),
+SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
+ MAX98373_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
+ MAX98373_FLT_EN_SHIFT, 1, 0),
+SOC_SINGLE("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0),
+SOC_SINGLE("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0),
+SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
+ 0, 0x3, 0),
+SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
+ 0, 0x3, 0),
+SOC_ENUM("ADC SampleRate", max98373_adc_samplerate_enum),
+/* Brownout Detection Engine */
+SOC_SINGLE("BDE Switch", MAX98373_R20B5_BDE_EN, MAX98373_BDE_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL4 Mute Switch", MAX98373_R20B2_BDE_L4_CFG_2,
+ MAX98373_LVL4_MUTE_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL4 Hold Switch", MAX98373_R20B2_BDE_L4_CFG_2,
+ MAX98373_LVL4_HOLD_EN_SHIFT, 1, 0),
+SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0),
+SOC_SINGLE("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0),
+SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0),
+SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0),
+SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0),
+SOC_SINGLE("BDE Attack Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 4, 0xF, 0),
+SOC_SINGLE("BDE Release Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0, 0xF, 0),
+SOC_SINGLE_TLV("BDE LVL1 Clip Thresh", MAX98373_R20A9_BDE_L1_CFG_2,
+ 0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Clip Thresh", MAX98373_R20AC_BDE_L2_CFG_2,
+ 0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Clip Thresh", MAX98373_R20AF_BDE_L3_CFG_2,
+ 0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Clip Thresh", MAX98373_R20B2_BDE_L4_CFG_2,
+ 0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL1 Clip Gain Reduct", MAX98373_R20AA_BDE_L1_CFG_3,
+ 0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Clip Gain Reduct", MAX98373_R20AD_BDE_L2_CFG_3,
+ 0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Clip Gain Reduct", MAX98373_R20B0_BDE_L3_CFG_3,
+ 0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Clip Gain Reduct", MAX98373_R20B3_BDE_L4_CFG_3,
+ 0, 0x3C, 0, max98373_bde_gain_tlv),
+SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh", MAX98373_R20A8_BDE_L1_CFG_1,
+ 0, 0xF, 0, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh", MAX98373_R20AB_BDE_L2_CFG_1,
+ 0, 0xF, 0, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh", MAX98373_R20AE_BDE_L3_CFG_1,
+ 0, 0xF, 0, max98373_limiter_thresh_tlv),
+SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh", MAX98373_R20B1_BDE_L4_CFG_1,
+ 0, 0xF, 0, max98373_limiter_thresh_tlv),
+/* Limiter */
+SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN,
+ MAX98373_LIMITER_EN_SHIFT, 1, 0),
+SOC_SINGLE("Limiter Src Switch", MAX98373_R20E0_LIMITER_THRESH_CFG,
+ MAX98373_LIMITER_THRESH_SRC_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Limiter Thresh", MAX98373_R20E0_LIMITER_THRESH_CFG,
+ MAX98373_LIMITER_THRESH_SHIFT, 15, 0, max98373_limiter_thresh_tlv),
+SOC_ENUM("Limiter Attack Rate", max98373_limiter_attack_rate_enum),
+SOC_ENUM("Limiter Release Rate", max98373_limiter_release_rate_enum),
+};
+
+static const struct snd_soc_dapm_route max98373_audio_map[] = {
+ /* Plabyack */
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+ /* Capture */
+ { "VI Sense", "Switch", "VMON" },
+ { "VI Sense", "Switch", "IMON" },
+ { "SpkFB Sense", "Switch", "FBMON" },
+ { "Voltage Sense", NULL, "VI Sense" },
+ { "Current Sense", NULL, "VI Sense" },
+ { "Speaker FB Sense", NULL, "SpkFB Sense" },
+};
+
+static struct snd_soc_dai_driver max98373_dai[] = {
+ {
+ .name = "max98373-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98373_RATES,
+ .formats = MAX98373_FORMATS,
+ },
+ .ops = &max98373_dai_ops,
+ }
+};
+
+static int max98373_probe(struct snd_soc_codec *codec)
+{
+ struct max98373_priv *max98373 = snd_soc_codec_get_drvdata(codec);
+
+ codec->control_data = max98373->regmap;
+
+ /* Software Reset */
+ regmap_write(max98373->regmap,
+ MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET);
+
+ /* IV default slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 0xFF);
+ regmap_write(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 0xFF);
+ /* L/R mix configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+ 0x80);
+ regmap_write(max98373->regmap,
+ MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+ 0x1);
+ /* Set inital volume (0dB) */
+ regmap_write(max98373->regmap,
+ MAX98373_R203D_AMP_DIG_VOL_CTRL,
+ 0x00);
+ regmap_write(max98373->regmap,
+ MAX98373_R203E_AMP_PATH_GAIN,
+ 0x00);
+ /* Enable DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R203F_AMP_DSP_CFG,
+ 0x3);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98373->regmap,
+ MAX98373_R2046_IV_SENSE_ADC_DSP_CFG,
+ 0x7);
+ /* voltage, current slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2022_PCM_TX_SRC_1,
+ (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT |
+ max98373->v_slot) & 0xFF);
+ if (max98373->v_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->v_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->v_slot - 8), 0);
+
+ if (max98373->i_slot < 8)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2020_PCM_TX_HIZ_EN_1,
+ 1 << max98373->i_slot, 0);
+ else
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2021_PCM_TX_HIZ_EN_2,
+ 1 << (max98373->i_slot - 8), 0);
+
+ /* speaker feedback slot configuration */
+ regmap_write(max98373->regmap,
+ MAX98373_R2023_PCM_TX_SRC_2,
+ max98373->spkfb_slot & 0xFF);
+
+ /* Set interleave mode */
+ if (max98373->interleave_mode)
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2024_PCM_DATA_FMT_CFG,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98373_PCM_TX_CH_INTERLEAVE_MASK);
+
+ /* Speaker enable */
+ regmap_update_bits(max98373->regmap,
+ MAX98373_R2043_AMP_EN,
+ MAX98373_SPK_EN_MASK, 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max98373_suspend(struct device *dev)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98373->regmap, true);
+ regcache_mark_dirty(max98373->regmap);
+ return 0;
+}
+static int max98373_resume(struct device *dev)
+{
+ struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+ regmap_write(max98373->regmap,
+ MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET);
+ regcache_cache_only(max98373->regmap, false);
+ regcache_sync(max98373->regmap);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max98373_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+};
+
+static const struct snd_soc_codec_driver soc_codec_dev_max98373 = {
+ .probe = max98373_probe,
+ .component_driver = {
+ .controls = max98373_snd_controls,
+ .num_controls = ARRAY_SIZE(max98373_snd_controls),
+ .dapm_widgets = max98373_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98373_dapm_widgets),
+ .dapm_routes = max98373_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98373_audio_map),
+ },
+};
+
+static const struct regmap_config max98373_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98373_R21FF_REV_ID,
+ .reg_defaults = max98373_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98373_reg),
+ .readable_reg = max98373_readable_register,
+ .volatile_reg = max98373_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98373_slot_config(struct i2c_client *i2c,
+ struct max98373_priv *max98373)
+{
+ int value;
+ struct device *dev = &i2c->dev;
+
+ if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
+ max98373->v_slot = value & 0xF;
+ else
+ max98373->v_slot = 0;
+
+ if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value))
+ max98373->i_slot = value & 0xF;
+ else
+ max98373->i_slot = 1;
+
+ if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value))
+ max98373->spkfb_slot = value & 0xF;
+ else
+ max98373->spkfb_slot = 2;
+}
+
+static int max98373_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+
+ int ret = 0, value;
+ int reg = 0;
+ struct max98373_priv *max98373 = NULL;
+
+ max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL);
+
+ if (!max98373) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98373);
+
+ /* update interleave mode info */
+ if (!device_property_read_u32(&i2c->dev,
+ "maxim,interleave_mode", &value)) {
+ if (value > 0)
+ max98373->interleave_mode = 1;
+ else
+ max98373->interleave_mode = 0;
+ } else
+ max98373->interleave_mode = 0;
+
+ /* regmap initialization */
+ max98373->regmap
+ = devm_regmap_init_i2c(i2c, &max98373_regmap);
+ if (IS_ERR(max98373->regmap)) {
+ ret = PTR_ERR(max98373->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98373->regmap,
+ MAX98373_R21FF_REV_ID, ®);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98373_R21FF_REV_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98373 revisionID: 0x%02X\n", reg);
+
+ /* voltage/current slot configuration */
+ max98373_slot_config(i2c, max98373);
+
+ /* codec registeration */
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98373,
+ max98373_dai, ARRAY_SIZE(max98373_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int max98373_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id max98373_i2c_id[] = {
+ { "max98373", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98373_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98373_of_match[] = {
+ { .compatible = "maxim,max98373", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98373_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98373_acpi_match[] = {
+ { "MX98373", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98373_acpi_match);
+#endif
+
+static struct i2c_driver max98373_i2c_driver = {
+ .driver = {
+ .name = "max98373",
+ .of_match_table = of_match_ptr(max98373_of_match),
+ .acpi_match_table = ACPI_PTR(max98373_acpi_match),
+ .pm = &max98373_pm,
+ },
+ .probe = max98373_i2c_probe,
+ .remove = max98373_i2c_remove,
+ .id_table = max98373_i2c_id,
+};
+
+module_i2c_driver(max98373_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98373 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h
new file mode 100644
index 0000000..d0b359d
--- /dev/null
+++ b/sound/soc/codecs/max98373.h
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2017, Maxim Integrated */
+#ifndef _MAX98373_H
+#define _MAX98373_H
+
+#define MAX98373_R2000_SW_RESET 0x2000
+#define MAX98373_R2001_INT_RAW1 0x2001
+#define MAX98373_R2002_INT_RAW2 0x2002
+#define MAX98373_R2003_INT_RAW3 0x2003
+#define MAX98373_R2004_INT_STATE1 0x2004
+#define MAX98373_R2005_INT_STATE2 0x2005
+#define MAX98373_R2006_INT_STATE3 0x2006
+#define MAX98373_R2007_INT_FLAG1 0x2007
+#define MAX98373_R2008_INT_FLAG2 0x2008
+#define MAX98373_R2009_INT_FLAG3 0x2009
+#define MAX98373_R200A_INT_EN1 0x200A
+#define MAX98373_R200B_INT_EN2 0x200B
+#define MAX98373_R200C_INT_EN3 0x200C
+#define MAX98373_R200D_INT_FLAG_CLR1 0x200D
+#define MAX98373_R200E_INT_FLAG_CLR2 0x200E
+#define MAX98373_R200F_INT_FLAG_CLR3 0x200F
+#define MAX98373_R2010_IRQ_CTRL 0x2010
+#define MAX98373_R2014_THERM_WARN_THRESH 0x2014
+#define MAX98373_R2015_THERM_SHDN_THRESH 0x2015
+#define MAX98373_R2016_THERM_HYSTERESIS 0x2016
+#define MAX98373_R2017_THERM_FOLDBACK_SET 0x2017
+#define MAX98373_R2018_THERM_FOLDBACK_EN 0x2018
+#define MAX98373_R201E_PIN_DRIVE_STRENGTH 0x201E
+#define MAX98373_R2020_PCM_TX_HIZ_EN_1 0x2020
+#define MAX98373_R2021_PCM_TX_HIZ_EN_2 0x2021
+#define MAX98373_R2022_PCM_TX_SRC_1 0x2022
+#define MAX98373_R2023_PCM_TX_SRC_2 0x2023
+#define MAX98373_R2024_PCM_DATA_FMT_CFG 0x2024
+#define MAX98373_R2025_AUDIO_IF_MODE 0x2025
+#define MAX98373_R2026_PCM_CLOCK_RATIO 0x2026
+#define MAX98373_R2027_PCM_SR_SETUP_1 0x2027
+#define MAX98373_R2028_PCM_SR_SETUP_2 0x2028
+#define MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 0x2029
+#define MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2 0x202A
+#define MAX98373_R202B_PCM_RX_EN 0x202B
+#define MAX98373_R202C_PCM_TX_EN 0x202C
+#define MAX98373_R202E_ICC_RX_CH_EN_1 0x202E
+#define MAX98373_R202F_ICC_RX_CH_EN_2 0x202F
+#define MAX98373_R2030_ICC_TX_HIZ_EN_1 0x2030
+#define MAX98373_R2031_ICC_TX_HIZ_EN_2 0x2031
+#define MAX98373_R2032_ICC_LINK_EN_CFG 0x2032
+#define MAX98373_R2034_ICC_TX_CNTL 0x2034
+#define MAX98373_R2035_ICC_TX_EN 0x2035
+#define MAX98373_R2036_SOUNDWIRE_CTRL 0x2036
+#define MAX98373_R203D_AMP_DIG_VOL_CTRL 0x203D
+#define MAX98373_R203E_AMP_PATH_GAIN 0x203E
+#define MAX98373_R203F_AMP_DSP_CFG 0x203F
+#define MAX98373_R2040_TONE_GEN_CFG 0x2040
+#define MAX98373_R2041_AMP_CFG 0x2041
+#define MAX98373_R2042_AMP_EDGE_RATE_CFG 0x2042
+#define MAX98373_R2043_AMP_EN 0x2043
+#define MAX98373_R2046_IV_SENSE_ADC_DSP_CFG 0x2046
+#define MAX98373_R2047_IV_SENSE_ADC_EN 0x2047
+#define MAX98373_R2051_MEAS_ADC_SAMPLING_RATE 0x2051
+#define MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG 0x2052
+#define MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG 0x2053
+#define MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK 0x2054
+#define MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK 0x2055
+#define MAX98373_R2056_MEAS_ADC_PVDD_CH_EN 0x2056
+#define MAX98373_R2090_BDE_LVL_HOLD 0x2090
+#define MAX98373_R2091_BDE_GAIN_ATK_REL_RATE 0x2091
+#define MAX98373_R2092_BDE_CLIPPER_MODE 0x2092
+#define MAX98373_R2097_BDE_L1_THRESH 0x2097
+#define MAX98373_R2098_BDE_L2_THRESH 0x2098
+#define MAX98373_R2099_BDE_L3_THRESH 0x2099
+#define MAX98373_R209A_BDE_L4_THRESH 0x209A
+#define MAX98373_R209B_BDE_THRESH_HYST 0x209B
+#define MAX98373_R20A8_BDE_L1_CFG_1 0x20A8
+#define MAX98373_R20A9_BDE_L1_CFG_2 0x20A9
+#define MAX98373_R20AA_BDE_L1_CFG_3 0x20AA
+#define MAX98373_R20AB_BDE_L2_CFG_1 0x20AB
+#define MAX98373_R20AC_BDE_L2_CFG_2 0x20AC
+#define MAX98373_R20AD_BDE_L2_CFG_3 0x20AD
+#define MAX98373_R20AE_BDE_L3_CFG_1 0x20AE
+#define MAX98373_R20AF_BDE_L3_CFG_2 0x20AF
+#define MAX98373_R20B0_BDE_L3_CFG_3 0x20B0
+#define MAX98373_R20B1_BDE_L4_CFG_1 0x20B1
+#define MAX98373_R20B2_BDE_L4_CFG_2 0x20B2
+#define MAX98373_R20B3_BDE_L4_CFG_3 0x20B3
+#define MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE 0x20B4
+#define MAX98373_R20B5_BDE_EN 0x20B5
+#define MAX98373_R20B6_BDE_CUR_STATE_READBACK 0x20B6
+#define MAX98373_R20D1_DHT_CFG 0x20D1
+#define MAX98373_R20D2_DHT_ATTACK_CFG 0x20D2
+#define MAX98373_R20D3_DHT_RELEASE_CFG 0x20D3
+#define MAX98373_R20D4_DHT_EN 0x20D4
+#define MAX98373_R20E0_LIMITER_THRESH_CFG 0x20E0
+#define MAX98373_R20E1_LIMITER_ATK_REL_RATES 0x20E1
+#define MAX98373_R20E2_LIMITER_EN 0x20E2
+#define MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG 0x20FE
+#define MAX98373_R20FF_GLOBAL_SHDN 0x20FF
+#define MAX98373_R21FF_REV_ID 0x21FF
+
+/* MAX98373_R2022_PCM_TX_SRC_1 */
+#define MAX98373_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98373_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98373_R2024_PCM_DATA_FMT_CFG */
+#define MAX98373_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98373_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98373_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2)
+#define MAX98373_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98373_PCM_FORMAT_LJ (0x1 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE0 (0x3 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE1 (0x4 << 0)
+#define MAX98373_PCM_FORMAT_TDM_MODE2 (0x5 << 0)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98373_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98373_R2026_PCM_CLOCK_RATIO */
+#define MAX98373_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4)
+#define MAX98373_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98373_R2027_PCM_SR_SETUP_1 */
+#define MAX98373_PCM_SR_SET1_SR_MASK (0xF << 0)
+#define MAX98373_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98373_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98373_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98373_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98373_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98373_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98373_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98373_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98373_PCM_SR_SET1_SR_48000 (0x8 << 0)
+
+/* MAX98373_R2028_PCM_SR_SETUP_2 */
+#define MAX98373_PCM_SR_SET2_SR_MASK (0xF << 4)
+#define MAX98373_PCM_SR_SET2_SR_SHIFT (4)
+#define MAX98373_PCM_SR_SET2_IVADC_SR_MASK (0xF << 0)
+
+/* MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1 */
+#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98373_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+#define MAX98373_PCM_TO_SPK_CH0_SRC_MASK (0xF << 0)
+
+/* MAX98373_R203E_AMP_PATH_GAIN */
+#define MAX98373_SPK_DIGI_GAIN_MASK (0xF << 4)
+#define MAX98373_SPK_DIGI_GAIN_SHIFT (4)
+#define MAX98373_FS_GAIN_MAX_MASK (0xF << 0)
+#define MAX98373_FS_GAIN_MAX_SHIFT (0)
+
+/* MAX98373_R203F_AMP_DSP_CFG */
+#define MAX98373_AMP_DSP_CFG_DCBLK_SHIFT (0)
+#define MAX98373_AMP_DSP_CFG_DITH_SHIFT (1)
+#define MAX98373_AMP_DSP_CFG_RMP_UP_SHIFT (2)
+#define MAX98373_AMP_DSP_CFG_RMP_DN_SHIFT (3)
+#define MAX98373_AMP_DSP_CFG_DAC_INV_SHIFT (5)
+#define MAX98373_AMP_VOL_SEL_SHIFT (7)
+
+/* MAX98373_R2043_AMP_EN */
+#define MAX98373_SPKFB_EN_MASK (0x1 << 1)
+#define MAX98373_SPK_EN_MASK (0x1 << 0)
+#define MAX98373_SPKFB_EN_SHIFT (1)
+
+/*MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG */
+#define MAX98373_FLT_EN_SHIFT (4)
+
+/* MAX98373_R20B2_BDE_L4_CFG_2 */
+#define MAX98373_LVL4_MUTE_EN_SHIFT (7)
+#define MAX98373_LVL4_HOLD_EN_SHIFT (6)
+
+/* MAX98373_R20B5_BDE_EN */
+#define MAX98373_BDE_EN_SHIFT (0)
+
+/* MAX98373_R20D1_DHT_CFG */
+#define MAX98373_DHT_SPK_GAIN_MIN_SHIFT (4)
+#define MAX98373_DHT_ROT_PNT_SHIFT (0)
+
+/* MAX98373_R20D2_DHT_ATTACK_CFG */
+#define MAX98373_DHT_ATTACK_STEP_SHIFT (3)
+#define MAX98373_DHT_ATTACK_RATE_SHIFT (0)
+
+/* MAX98373_R20D3_DHT_RELEASE_CFG */
+#define MAX98373_DHT_RELEASE_STEP_SHIFT (3)
+#define MAX98373_DHT_RELEASE_RATE_SHIFT (0)
+
+/* MAX98373_R20D4_DHT_EN */
+#define MAX98373_DHT_EN_SHIFT (0)
+
+/* MAX98373_R20E0_LIMITER_THRESH_CFG */
+#define MAX98373_LIMITER_THRESH_SHIFT (2)
+#define MAX98373_LIMITER_THRESH_SRC_SHIFT (0)
+
+/* MAX98373_R20E2_LIMITER_EN */
+#define MAX98373_LIMITER_EN_SHIFT (0)
+
+/* MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG */
+#define MAX98373_CLOCK_MON_SHIFT (0)
+
+/* MAX98373_R20FF_GLOBAL_SHDN */
+#define MAX98373_GLOBAL_EN_MASK (0x1 << 0)
+
+/* MAX98373_R2000_SW_RESET */
+#define MAX98373_SOFT_RESET (0x1 << 0)
+
+struct max98373_priv {
+ struct regmap *regmap;
+ unsigned int v_slot;
+ unsigned int i_slot;
+ unsigned int spkfb_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ bool tdm_mode;
+};
+#endif
--
2.7.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* RE: [PATCH] ASoC: max98373: Added Amplifier Driver
From: Ryan Lee @ 2017-12-25 14:30 UTC (permalink / raw)
To: Philippe Ombredanne
Cc: Liam Girdwood, Mark, Rob Herring, Mark Rutland, Jaroslav Kysela,
Takashi Iwai, Arnd Bergmann, afd-l0cyMroinI0@public.gmane.org,
robert.jarzmik-GANU6spQydw@public.gmane.org,
supercraig0719-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
jbrunet-rdvid1DuHRBWk0Htik3J/w@public.gmane.org,
dannenberg-l0cyMroinI0@public.gmane.org, Romain Perier,
bryce.ferguson-lFk7bPDcGtkY5TsXZYaR1UEOCMrvLtNR@public.gmane.org,
kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org,
m-stecklein-l0cyMroinI0@public.gmane.org, ALSA, OPEN
In-Reply-To: <CAOFm3uH3+UsSZ7DgDk9AYRb8K7GjKUNwchGW3_hxD3DMOidjpg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi Philppe Ombredanne,
>-----Original Message-----
>From: Philippe Ombredanne [mailto:pombredanne@nexb.com]
>Sent: Friday, December 22, 2017 12:39 AM
>To: Ryan Lee <RyanS.Lee@maximintegrated.com>
>Cc: Liam Girdwood <lgirdwood@gmail.com>; Mark <broonie@kernel.org>;
>Rob Herring <robh+dt@kernel.org>; Mark Rutland <mark.rutland@arm.com>;
>Jaroslav Kysela <perex@perex.cz>; Takashi Iwai <tiwai@suse.com>; Arnd
>Bergmann <arnd@arndb.de>; afd@ti.com; robert.jarzmik@free.fr;
>supercraig0719@gmail.com; jbrunet@baylibre.com; dannenberg@ti.com;
>Romain Perier <romain.perier@collabora.com>;
>bryce.ferguson@rockwellcollins.com; kuninori.morimoto.gx@renesas.com; m-
>stecklein@ti.com; ALSA <alsa-devel@alsa-project.org>; open list:OPEN
>FIRMWARE AND FLATTENED DEVICE TREE BINDINGS
><devicetree@vger.kernel.org>; LKML <linux-kernel@vger.kernel.org>;
>ryan.lee.maxim@gmail.com
>Subject: Re: [PATCH] ASoC: max98373: Added Amplifier Driver
>
>EXTERNAL EMAIL
>
>
>
>Ryan,
>
>On Fri, Dec 22, 2017 at 2:43 AM, Ryan Lee <ryans.lee@maximintegrated.com>
>wrote:
>> Signed-off-by: Ryan Lee <ryans.lee@maximintegrated.com>
>> ---
>>
>> Created max98373 amplifier driver.
>>
>
><snip>
>
>> --- /dev/null
>> +++ b/sound/soc/codecs/max98373.c
>> @@ -0,0 +1,996 @@
>> +/*
>> + * max98373.c -- MAX98373 ALSA Soc Audio driver
>> + *
>> + * Copyright (C) 2017 Maxim Integrated Products
>> + * Author: Ryan Lee <ryans.lee@maximintegrated.com>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> +modify it
>> + * under the terms of the GNU General Public License as published
>> +by the
>> + * Free Software Foundation; either version 2 of the License, or
>> +(at your
>> + * option) any later version.
>> + */
>
>Have you considered using the new SPDX tags instead of this fine but long
>legalese?
>
>And if other contributors in your team could follow suit and you could spread
>the word that would be even better!
>
>See Thomas doc patches [1] for details.
>Thanks!
>
>[1]
>https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Flkml.org%
>2Flkml%2F2017%2F12%2F4%2F934&data=01%7C01%7Cryans.lee%40maximint
>egrated.com%7Cd26ec4859b2240fb0a6008d549178c4f%7Cfbd909dfea69478
>8a554f24b7854ad03%7C0&sdata=dTVH044XPox7EPR0WU1IGMlLxaWBDl80%
>2B%2FVYhwu51y0%3D&reserved=0
>
Thanks for your feedback. I will follow this.
>--
>Cordially
>Philippe Ombredanne
^ permalink raw reply
* RE: [PATCH] ASoC: max98373: Added Amplifier Driver
From: Ryan Lee @ 2017-12-25 14:30 UTC (permalink / raw)
To: Kuninori Morimoto
Cc: lgirdwood-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
mark.rutland-5wv7dgnIgG8@public.gmane.org,
perex-/Fr2/VpizcU@public.gmane.org,
tiwai-IBi9RG/b67k@public.gmane.org,
arnd-r2nGTMty4D4@public.gmane.org,
afd-l0cyMroinI0@public.gmane.org,
robert.jarzmik-GANU6spQydw@public.gmane.org,
supercraig0719-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
jbrunet-rdvid1DuHRBWk0Htik3J/w@public.gmane.org,
dannenberg-l0cyMroinI0@public.gmane.org,
romain.perier-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org,
bryce.ferguson-lFk7bPDcGtkY5TsXZYaR1UEOCMrvLtNR@public.gmane.org,
m-stecklein-l0cyMroinI0@public.gmane.org,
alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org,
devicetree-u79uwXL29TbrhsbdSgBK9A
In-Reply-To: <87a7ybo6em.wl%kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org>
Hi Kuninori Miromoto,
>-----Original Message-----
>From: Kuninori Morimoto [mailto:kuninori.morimoto.gx-zM6kxYcvzFBBDgjK7y7TUQ@public.gmane.org]
>Sent: Thursday, December 21, 2017 6:24 PM
>To: Ryan Lee <RyanS.Lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>
>Cc: lgirdwood-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org; broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org; robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org;
>mark.rutland-5wv7dgnIgG8@public.gmane.org; perex-/Fr2/VpizcU@public.gmane.org; tiwai-IBi9RG/b67k@public.gmane.org; arnd-r2nGTMty4D4@public.gmane.org;
>afd-l0cyMroinI0@public.gmane.org; robert.jarzmik-GANU6spQydw@public.gmane.org; supercraig0719-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org;
>jbrunet-rdvid1DuHRBWk0Htik3J/w@public.gmane.org; dannenberg-l0cyMroinI0@public.gmane.org; romain.perier-ZGY8ohtN/8qB+jHODAdFcQ@public.gmane.org;
>bryce.ferguson-lFk7bPDcGtkY5TsXZYaR1UEOCMrvLtNR@public.gmane.org; m-stecklein-l0cyMroinI0@public.gmane.org; alsa-devel@alsa-
>project.org; devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
>ryan.lee.maxim-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
>Subject: Re: [PATCH] ASoC: max98373: Added Amplifier Driver
>
>EXTERNAL EMAIL
>
>
>
>Hi Ryan
>
>> Signed-off-by: Ryan Lee <ryans.lee-zxKO94PEStzToO697jQleEEOCMrvLtNR@public.gmane.org>
>> ---
>>
>> Created max98373 amplifier driver.
>>
>> .../devicetree/bindings/sound/max98373.txt | 43 +
>> sound/soc/codecs/Kconfig | 5 +
>> sound/soc/codecs/Makefile | 2 +
>> sound/soc/codecs/max98373.c | 996
>+++++++++++++++++++++
>> sound/soc/codecs/max98373.h | 225 +++++
>> 5 files changed, 1271 insertions(+)
>> create mode 100644
>> Documentation/devicetree/bindings/sound/max98373.txt
>> create mode 100644 sound/soc/codecs/max98373.c create mode 100644
>> sound/soc/codecs/max98373.h
>(snip)
>> +struct max98373_priv {
>> + struct regmap *regmap;
>> + struct snd_soc_codec *codec;
>> + unsigned int sysclk;
>> + unsigned int v_slot;
>> + unsigned int i_slot;
>> + unsigned int spkfb_slot;
>> + bool interleave_mode;
>> + unsigned int ch_size;
>> + unsigned int iface;
>> + bool tdm_mode;
>> +};
>
>About this max98373->codec.
>This user is only max98373_set_clock(), and it is called from
>max98373_dai_hw_params().
>You are getting *codec from dai->codec in this function, and max98373 is
>came from it.
>This means, we can remove max98373->codec ?
Thanks for your feedback.
I will remove max98373->codec and change related things.
>
>About max98373->regmap.
>You are using devm_regmap_init_i2c(), and keeping it on max98373.
>Can you check snd_soc_component_add() which is called from
>snd_soc_add_component().
>It will set component->regmap if you called devm_regmap_init_i2c().
>I think you can use snd_soc_component_read/write instead of
>regmap_read/write.
>Then, we can remove max98373->regmap too ?
>Ahh, you want to check Revision ID.
>then, snd_soc_component_init_regmap() can help you ?
I'm sorry but I don't fully understand the benefit of this.
Keeping regmap information in private driver data is very common and I can see many ASoC drivers are using it.
I was able to see only a few driver use ' snd_soc_component_read'.
I would like to keep regmap_read/write if it is still acceptable.
>
>Best regards
>---
>Kuninori Morimoto
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v3] ARM: sun8i: h2+: add support for Banana Pi M2 Zero board
From: Philippe Ombredanne @ 2017-12-25 14:25 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Maxime Ripard, Chen-Yu Tsai,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE, LKML,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20171224054029.12278-1-icenowy-h8G6r0blFSE@public.gmane.org>
On Sun, Dec 24, 2017 at 6:40 AM, Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org> wrote:
> Banana Pi M2 Zero board is a H2+-based board by Sinovoip, with a form
> factor and GPIO holes similar to Raspberry Pi Zero.
>
> It features:
> - Allwinner H2+ SoC
> - Single-chip (16-bit) 512MiB DDR3 DRAM
> - Ampak AP6212 Wi-Fi/Bluetooth module
> - MicroSD slot
> - Two MicroUSB Type-B ports (one can only be used to power the board and
> the other features OTG functionality)
> - Two keys, a reset and a GPIO-connected key.
> - HDMI Type-C (miniHDMI) connector connected to the HDMI part of H2+.
> - CSI connector to connect the camera sensor provided by Sinovoip.
>
> Signed-off-by: Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
> ---
> Changes in v3:
> - Add comments about Vbus problem in &usbphy node.
>
> Changes in v2:
> - Use high active SD card detect on the production batch.
>
> arch/arm/boot/dts/Makefile | 1 +
> .../boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts | 162 +++++++++++++++++++++
> 2 files changed, 163 insertions(+)
> create mode 100644 arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts
>
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index 0bb8db33704a..937a8768671f 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -962,6 +962,7 @@ dtb-$(CONFIG_MACH_SUN8I) += \
> sun8i-a83t-cubietruck-plus.dtb \
> sun8i-a83t-tbs-a711.dtb \
> sun8i-h2-plus-orangepi-r1.dtb \
> + sun8i-h2-plus-bananapi-m2-zero.dtb \
> sun8i-h2-plus-orangepi-zero.dtb \
> sun8i-h3-bananapi-m2-plus.dtb \
> sun8i-h3-beelink-x2.dtb \
> diff --git a/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts b/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts
> new file mode 100644
> index 000000000000..5bc182ddc5f7
> --- /dev/null
> +++ b/arch/arm/boot/dts/sun8i-h2-plus-bananapi-m2-zero.dts
> @@ -0,0 +1,162 @@
> +/*
> + * Copyright (C) 2017 Icenowy Zheng <icenowy-h8G6r0blFSE@public.gmane.org>
> + *
> + * Based on sun8i-h3-bananapi-m2-plus.dts, which is:
> + * Copyright (C) 2016 Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + * a) This file is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License as
> + * published by the Free Software Foundation; either version 2 of the
> + * License, or (at your option) any later version.
> + *
> + * This file is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * Or, alternatively,
> + *
> + * b) Permission is hereby granted, free of charge, to any person
> + * obtaining a copy of this software and associated documentation
> + * files (the "Software"), to deal in the Software without
> + * restriction, including without limitation the rights to use,
> + * copy, modify, merge, publish, distribute, sublicense, and/or
> + * sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following
> + * conditions:
> + *
> + * The above copyright notice and this permission notice shall be
> + * included in all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + * OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
Could you use SPDX tags instead of this long legalese?
--
Cordially
Philippe Ombredanne
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 4/4] [media] dt/bindings: Add bindings for OV2685
From: Shunqian Zheng @ 2017-12-25 14:11 UTC (permalink / raw)
To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng
In-Reply-To: <1514211086-13440-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Add device tree binding documentation for the OV2685 sensor.
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
.../devicetree/bindings/media/i2c/ov2685.txt | 35 ++++++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ov2685.txt
diff --git a/Documentation/devicetree/bindings/media/i2c/ov2685.txt b/Documentation/devicetree/bindings/media/i2c/ov2685.txt
new file mode 100644
index 0000000..c62db9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov2685.txt
@@ -0,0 +1,35 @@
+* Omnivision OV2685 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: should be "ovti,ov2685"
+- clocks: reference to the 24M xvclk input clock.
+- clock-names: should be "xvclk".
+- avdd-supply: Analog voltage supply, 2.8 volts
+- dvdd-supply: Digital core voltage supply, 1.2 volts
+- reset-gpios: Low active reset gpio
+
+The device node must contain one 'port' child node for its digital output
+video port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+ ucam: ov2680@3c {
+ compatible = "ovti,ov2685";
+ reg = <0x3c>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&clk_24m_cam &ucam_rst>;
+
+ clocks = <&cru SCLK_TESTCLKOUT1>;
+ clock-names = "xvclk";
+
+ avdd-supply = <&pp2800_cam>;
+ dovdd-supply = <&pp1800>;
+ reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
+
+ port {
+ ucam_out: endpoint {
+ remote-endpoint = <&mipi_in_ucam>;
+ data-lanes = <1>;
+ };
+ };
+ };
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 3/4] media: ov2685: add support for OV2685 sensor
From: Shunqian Zheng @ 2017-12-25 14:11 UTC (permalink / raw)
To: mchehab, robh+dt, mark.rutland
Cc: linux-media, devicetree, ddl, tfiga, Shunqian Zheng
In-Reply-To: <1514211086-13440-1-git-send-email-zhengsq@rock-chips.com>
This patch adds driver for Omnivision's ov2685 sensor.
Though the ov2685 can output yuv data, this driver only
supports the raw bayer format, including the following features:
- output 1600x1200 at 30fps
- test patterns
- manual exposure/gain control
- vblank and hblank
- media controller
- runtime pm
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
---
drivers/media/i2c/Kconfig | 12 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov2685.c | 834 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 847 insertions(+)
create mode 100644 drivers/media/i2c/ov2685.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 55b37c8..63a175d 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -586,6 +586,18 @@ config VIDEO_OV2659
To compile this driver as a module, choose M here: the
module will be called ov2659.
+config VIDEO_OV2685
+ tristate "OmniVision OV2685 sensor support"
+ depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
+ depends on MEDIA_CAMERA_SUPPORT
+ select V4L2_FWNODE
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV2685 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov2685.
+
config VIDEO_OV5640
tristate "OmniVision OV5640 sensor support"
depends on OF
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index a063030..3054c69 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
+obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c
new file mode 100644
index 0000000..60b0794
--- /dev/null
+++ b/drivers/media/i2c/ov2685.c
@@ -0,0 +1,834 @@
+/*
+ * ov2685 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define CHIP_ID 0x2685
+#define OV2685_REG_CHIP_ID 0x300a
+
+#define REG_SC_CTRL_MODE 0x0100
+#define SC_CTRL_MODE_STANDBY 0x0
+#define SC_CTRL_MODE_STREAMING BIT(0)
+
+#define OV2685_REG_EXPOSURE 0x3500
+#define OV2685_EXPOSURE_MIN 4
+#define OV2685_EXPOSURE_STEP 1
+
+#define OV2685_REG_VTS 0x380e
+#define OV2685_VTS_MAX 0x7fff
+#define OV2685_VBLANK_MIN 0x0
+
+#define OV2685_REG_GAIN 0x350a
+#define OV2685_GAIN_MIN 0
+#define OV2685_GAIN_MAX 0x07ff
+/* 4 least significant bits of expsoure are fractional part */
+#define OV2685_GAIN_STEP 0x10
+#define OV2685_GAIN_DEFAULT 0x0036
+
+#define OV2685_REG_TEST_PATTERN 0x5080
+#define OV2685_TEST_PATTERN_DISABLED 0x00
+#define OV2685_TEST_PATTERN_COLOR_BAR 0x80
+#define OV2685_TEST_PATTERN_RND 0x81
+#define OV2685_TEST_PATTERN_COLOR_BAR_FADE 0x88
+#define OV2685_TEST_PATTERN_BW_SQUARE 0x92
+#define OV2685_TEST_PATTERN_COLOR_SQUARE 0x82
+
+#define REG_NULL 0xFFFF
+
+#define OV2685_REG_VALUE_08BIT 1
+#define OV2685_REG_VALUE_16BIT 2
+#define OV2685_REG_VALUE_24BIT 3
+
+#define OV2685_LANES 1
+#define OV2685_BITS_PER_SAMPLE 10
+
+struct regval {
+ u16 addr;
+ u8 val;
+};
+
+struct ov2685_mode {
+ u32 width;
+ u32 height;
+ u32 exp_def;
+ u32 hts_def;
+ u32 vts_def;
+ const struct regval *reg_list;
+};
+
+struct ov2685 {
+ struct i2c_client *client;
+ struct clk *xvclk;
+ struct regulator *avdd_regulator; /* Analog power */
+ struct regulator *dovdd_regulator; /* Digital I/O power */
+ /* use internal DVDD power */
+ struct gpio_desc *reset_gpio;
+
+ bool streaming;
+ struct mutex mutex;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_ctrl *anal_gain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *test_pattern;
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ const struct ov2685_mode *cur_mode;
+};
+#define to_ov2685(sd) container_of(sd, struct ov2685, subdev)
+
+/* PLL settings bases on 24M xvclk */
+static struct regval ov2685_1600x1200_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x3002, 0x00},
+ {0x3016, 0x1c},
+ {0x3018, 0x44},
+ {0x301d, 0xf0},
+ {0x3020, 0x00},
+ {0x3082, 0x37},
+ {0x3083, 0x03},
+ {0x3084, 0x09},
+ {0x3085, 0x04},
+ {0x3086, 0x00},
+ {0x3087, 0x00},
+ {0x3501, 0x4e},
+ {0x3502, 0xe0},
+ {0x3503, 0x00},
+ {0x350b, 0x36},
+ {0x3600, 0xb4},
+ {0x3603, 0x35},
+ {0x3604, 0x24},
+ {0x3605, 0x00},
+ {0x3620, 0x24},
+ {0x3621, 0x34},
+ {0x3622, 0x03},
+ {0x3628, 0x10},
+ {0x3705, 0x3c},
+ {0x370a, 0x21},
+ {0x370c, 0x50},
+ {0x370d, 0xc0},
+ {0x3717, 0x58},
+ {0x3718, 0x80},
+ {0x3720, 0x00},
+ {0x3721, 0x09},
+ {0x3722, 0x06},
+ {0x3723, 0x59},
+ {0x3738, 0x99},
+ {0x3781, 0x80},
+ {0x3784, 0x0c},
+ {0x3789, 0x60},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x06},
+ {0x3805, 0x4f},
+ {0x3806, 0x04},
+ {0x3807, 0xbf},
+ {0x3808, 0x06},
+ {0x3809, 0x40},
+ {0x380a, 0x04},
+ {0x380b, 0xb0},
+ {0x380c, 0x06},
+ {0x380d, 0xa4},
+ {0x380e, 0x05},
+ {0x380f, 0x0e},
+ {0x3810, 0x00},
+ {0x3811, 0x08},
+ {0x3812, 0x00},
+ {0x3813, 0x08},
+ {0x3814, 0x11},
+ {0x3815, 0x11},
+ {0x3819, 0x04},
+ {0x3820, 0xc0},
+ {0x3821, 0x00},
+ {0x3a06, 0x01},
+ {0x3a07, 0x84},
+ {0x3a08, 0x01},
+ {0x3a09, 0x43},
+ {0x3a0a, 0x24},
+ {0x3a0b, 0x60},
+ {0x3a0c, 0x28},
+ {0x3a0d, 0x60},
+ {0x3a0e, 0x04},
+ {0x3a0f, 0x8c},
+ {0x3a10, 0x05},
+ {0x3a11, 0x0c},
+ {0x4000, 0x81},
+ {0x4001, 0x40},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x4300, 0x00},
+ {0x430e, 0x00},
+ {0x4602, 0x02},
+ {0x481b, 0x40},
+ {0x481f, 0x40},
+ {0x4837, 0x18},
+ {0x5000, 0x1f},
+ {0x5001, 0x05},
+ {0x5002, 0x30},
+ {0x5003, 0x04},
+ {0x5004, 0x00},
+ {0x5005, 0x0c},
+ {REG_NULL, 0x00}
+};
+
+#define OV2685_LINK_FREQ_330MHZ 330000000
+static const s64 link_freq_menu_items[] = {
+ OV2685_LINK_FREQ_330MHZ
+};
+
+static const char * const ov2685_test_pattern_menu[] = {
+ "Disabled",
+ "Color Bar",
+ "RND PATTERN",
+ "Color Bar FADE",
+ "BW SQUARE",
+ "COLOR SQUARE"
+};
+
+static const int ov2685_test_pattern_val[] = {
+ OV2685_TEST_PATTERN_DISABLED,
+ OV2685_TEST_PATTERN_COLOR_BAR,
+ OV2685_TEST_PATTERN_RND,
+ OV2685_TEST_PATTERN_COLOR_BAR_FADE,
+ OV2685_TEST_PATTERN_BW_SQUARE,
+ OV2685_TEST_PATTERN_COLOR_SQUARE,
+};
+
+static const struct ov2685_mode supported_modes[] = {
+ {
+ .width = 1600,
+ .height = 1200,
+ .exp_def = 0x04ee,
+ .hts_def = 0x06a4,
+ .vts_def = 0x050e,
+ .reg_list = ov2685_1600x1200_regs,
+ },
+};
+
+/* Write registers up to 4 at a time */
+static int ov2685_write_reg(struct i2c_client *client, u16 reg,
+ unsigned int len, u32 val)
+{
+ int buf_i;
+ int val_i;
+ u8 buf[6];
+ u8 *val_p;
+
+ if (len > 4)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ val = cpu_to_be32(val);
+ val_p = (u8 *)&val;
+ buf_i = 2;
+ val_i = 4 - len;
+
+ while (val_i < 4)
+ buf[buf_i++] = val_p[val_i++];
+
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int ov2685_write_array(struct i2c_client *client,
+ const struct regval *regs)
+{
+ int i, ret = 0;
+
+ for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+ ret = ov2685_write_reg(client, regs[i].addr,
+ OV2685_REG_VALUE_08BIT, regs[i].val);
+
+ return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov2685_read_reg(struct i2c_client *client, u16 reg,
+ unsigned int len, u32 *val)
+{
+ struct i2c_msg msgs[2];
+ u8 *data_be_p;
+ u32 data_be = 0;
+ u16 reg_addr_be = cpu_to_be16(reg);
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ data_be_p = (u8 *)&data_be;
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)®_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+static void ov2685_fill_fmt(struct ov2685 *ov2685,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->width = ov2685->cur_mode->width;
+ fmt->height = ov2685->cur_mode->height;
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+static int ov2685_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+
+ ov2685_fill_fmt(ov2685, mbus_fmt);
+
+ return 0;
+}
+
+static int ov2685_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+
+ ov2685_fill_fmt(ov2685, mbus_fmt);
+
+ return 0;
+}
+
+static int ov2685_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ return 0;
+}
+
+static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ int index = fse->index;
+
+ if (index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ fse->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ fse->min_width = supported_modes[index].width;
+ fse->max_width = supported_modes[index].width;
+ fse->max_height = supported_modes[index].height;
+ fse->min_height = supported_modes[index].height;
+
+ return 0;
+}
+
+static inline void ov2685_set_exposure(struct ov2685 *ov2685, s32 val)
+{
+ ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE,
+ OV2685_REG_VALUE_24BIT, val << 4);
+}
+
+static inline void ov2685_set_gain(struct ov2685 *ov2685, s32 val)
+{
+ ov2685_write_reg(ov2685->client, OV2685_REG_GAIN,
+ OV2685_REG_VALUE_16BIT, val & OV2685_GAIN_MAX);
+}
+
+static inline void ov2685_set_vts(struct ov2685 *ov2685, s32 val)
+{
+ val += ov2685->cur_mode->height;
+ ov2685_write_reg(ov2685->client, OV2685_REG_VTS,
+ OV2685_REG_VALUE_16BIT, val);
+}
+
+static inline void ov2685_enable_test_pattern(struct ov2685 *ov2685, u32 pat)
+{
+ ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN,
+ OV2685_REG_VALUE_08BIT, ov2685_test_pattern_val[pat]);
+}
+
+static int __ov2685_power_on(struct ov2685 *ov2685)
+{
+ int ret;
+ struct device *dev = &ov2685->client->dev;
+
+ ret = clk_prepare_enable(ov2685->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable xvclk\n");
+ return ret;
+ }
+ clk_set_rate(ov2685->xvclk, 24000000);
+
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
+ /* AVDD and DOVDD may rise in any order */
+ ret = regulator_enable(ov2685->avdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable AVDD regulator\n");
+ goto disable_xvclk;
+ }
+ ret = regulator_enable(ov2685->dovdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable DOVDD regulator\n");
+ goto disable_avdd;
+ }
+ /* The minimum delay between AVDD and reset rising can be 0 */
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 0);
+ /* 8192 xvclk cycles prior to the first SCCB transaction.
+ * NOTE: An additional 1ms must be added to wait for
+ * SCCB to become stable when using internal DVDD.
+ */
+ usleep_range(1350, 1500);
+
+ /* HACK: ov2685 would output messy data after reset(R0103),
+ * writing register before .s_stream() as a workaround
+ */
+ ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list);
+
+ return ret;
+disable_avdd:
+ regulator_disable(ov2685->avdd_regulator);
+disable_xvclk:
+ clk_disable_unprepare(ov2685->xvclk);
+
+ return ret;
+}
+
+static void __ov2685_power_off(struct ov2685 *ov2685)
+{
+ /* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */
+ usleep_range(30, 50);
+ clk_disable_unprepare(ov2685->xvclk);
+ gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
+ regulator_disable(ov2685->dovdd_regulator);
+ regulator_disable(ov2685->avdd_regulator);
+}
+
+static int ov2685_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ int ret = 0;
+
+ mutex_lock(&ov2685->mutex);
+
+ if (on)
+ ret = pm_runtime_get_sync(&ov2685->client->dev);
+ else
+ ret = pm_runtime_put(&ov2685->client->dev);
+
+ mutex_unlock(&ov2685->mutex);
+
+ return ret;
+}
+
+static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct i2c_client *client = ov2685->client;
+ int ret = 0;
+
+ mutex_lock(&ov2685->mutex);
+
+ on = !!on;
+ if (on == ov2685->streaming)
+ goto unlock_and_return;
+
+ if (on) {
+ /* In case these controls are set before streaming */
+ ov2685_set_exposure(ov2685, ov2685->exposure->val);
+ ov2685_set_gain(ov2685, ov2685->anal_gain->val);
+ ov2685_set_vts(ov2685, ov2685->vblank->val);
+ ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val);
+
+ ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
+ OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
+ if (ret)
+ goto unlock_and_return;
+ } else {
+ ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
+ OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
+ if (ret)
+ goto unlock_and_return;
+ }
+
+ ov2685->streaming = on;
+
+unlock_and_return:
+ mutex_unlock(&ov2685->mutex);
+ return ret;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ struct v4l2_mbus_framefmt *try_fmt;
+
+ mutex_lock(&ov2685->mutex);
+
+ try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+ /* Initialize try_fmt */
+ ov2685_fill_fmt(ov2685, try_fmt);
+
+ mutex_unlock(&ov2685->mutex);
+
+ return 0;
+}
+#endif
+
+static int ov2685_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov2685 *ov2685 = to_ov2685(sd);
+ int ret;
+
+ ret = __ov2685_power_on(ov2685);
+ if (ret)
+ return ret;
+
+ if (ov2685->streaming) {
+ ret = ov2685_s_stream(sd, 1);
+ if (ret)
+ __ov2685_power_off(ov2685);
+ }
+
+ return ret;
+}
+
+static int ov2685_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov2685 *ov2685 = to_ov2685(sd);
+
+ __ov2685_power_off(ov2685);
+
+ return 0;
+}
+
+static const struct dev_pm_ops ov2685_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov2685_runtime_suspend,
+ ov2685_runtime_resume, NULL)
+};
+
+static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov2685 *ov2685 = container_of(ctrl->handler,
+ struct ov2685, ctrl_handler);
+ struct i2c_client *client = ov2685->client;
+ s64 max;
+ int ret = 0;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max = ov2685->cur_mode->height + ctrl->val - 4;
+ __v4l2_ctrl_modify_range(ov2685->exposure,
+ ov2685->exposure->minimum, max,
+ ov2685->exposure->step,
+ ov2685->exposure->default_value);
+ break;
+ }
+
+ pm_runtime_get_sync(&client->dev);
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ov2685_set_exposure(ov2685, ctrl->val);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ov2685_set_gain(ov2685, ctrl->val);
+ break;
+ case V4L2_CID_VBLANK:
+ ov2685_set_vts(ov2685, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ov2685_enable_test_pattern(ov2685, ctrl->val);
+ break;
+ default:
+ dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+ __func__, ctrl->id, ctrl->val);
+ break;
+ };
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static struct v4l2_subdev_core_ops ov2685_core_ops = {
+ .s_power = ov2685_s_power,
+};
+
+static struct v4l2_subdev_video_ops ov2685_video_ops = {
+ .s_stream = ov2685_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov2685_pad_ops = {
+ .enum_mbus_code = ov2685_enum_mbus_code,
+ .enum_frame_size = ov2685_enum_frame_sizes,
+ .get_fmt = ov2685_get_fmt,
+ .set_fmt = ov2685_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov2685_subdev_ops = {
+ .core = &ov2685_core_ops,
+ .video = &ov2685_video_ops,
+ .pad = &ov2685_pad_ops,
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov2685_internal_ops = {
+ .open = ov2685_open,
+};
+#endif
+
+static const struct v4l2_ctrl_ops ov2685_ctrl_ops = {
+ .s_ctrl = ov2685_set_ctrl,
+};
+
+static int ov2685_initialize_controls(struct ov2685 *ov2685)
+{
+ const struct ov2685_mode *mode;
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl *ctrl;
+ u64 exposure_max;
+ u32 pixel_rate, h_blank;
+ int ret;
+
+ handler = &ov2685->ctrl_handler;
+ mode = ov2685->cur_mode;
+ ret = v4l2_ctrl_handler_init(handler, 1);
+ if (ret)
+ return ret;
+ handler->lock = &ov2685->mutex;
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ pixel_rate = (link_freq_menu_items[0] * 2 * OV2685_LANES) /
+ OV2685_BITS_PER_SAMPLE;
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, pixel_rate, 1, pixel_rate);
+
+ h_blank = mode->hts_def - mode->width;
+ ov2685->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+ h_blank, h_blank, 1, h_blank);
+ if (ov2685->hblank)
+ ov2685->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ov2685->vblank = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_VBLANK, OV2685_VBLANK_MIN,
+ OV2685_VTS_MAX - mode->height, 1,
+ mode->vts_def - mode->height);
+
+ exposure_max = mode->vts_def - 4;
+ ov2685->exposure = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_EXPOSURE, OV2685_EXPOSURE_MIN,
+ exposure_max, OV2685_EXPOSURE_STEP,
+ mode->exp_def);
+
+ ov2685->anal_gain = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, OV2685_GAIN_MIN,
+ OV2685_GAIN_MAX, OV2685_GAIN_STEP,
+ OV2685_GAIN_DEFAULT);
+
+ ov2685->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+ &ov2685_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov2685_test_pattern_menu) - 1,
+ 0, 0, ov2685_test_pattern_menu);
+
+ if (handler->error) {
+ v4l2_ctrl_handler_free(handler);
+ return handler->error;
+ }
+
+ ov2685->subdev.ctrl_handler = handler;
+
+ return 0;
+}
+
+static int ov2685_check_sensor_id(struct ov2685 *ov2685,
+ struct i2c_client *client)
+{
+ struct device *dev = &ov2685->client->dev;
+ int id, ret;
+
+ ret = __ov2685_power_on(ov2685);
+ if (ret)
+ return ret;
+ ov2685_read_reg(client, OV2685_REG_CHIP_ID,
+ OV2685_REG_VALUE_16BIT, &id);
+ __ov2685_power_off(ov2685);
+
+ if (id != CHIP_ID) {
+ dev_err(dev, "Wrong camera sensor id(%04x)\n", id);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID);
+
+ return 0;
+}
+
+static int ov2685_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ov2685 *ov2685;
+ int ret;
+
+ ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
+ if (!ov2685)
+ return -ENOMEM;
+
+ ov2685->client = client;
+ ov2685->cur_mode = &supported_modes[0];
+
+ ov2685->xvclk = devm_clk_get(dev, "xvclk");
+ if (IS_ERR(ov2685->xvclk)) {
+ dev_err(dev, "Failed to get xvclk\n");
+ return -EINVAL;
+ }
+
+ ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ov2685->reset_gpio)) {
+ dev_err(dev, "Failed to get reset-gpios\n");
+ return -EINVAL;
+ }
+
+ ov2685->avdd_regulator = devm_regulator_get(dev, "avdd");
+ if (IS_ERR(ov2685->avdd_regulator)) {
+ dev_err(dev, "Failed to get avdd-supply\n");
+ return -EINVAL;
+ }
+ ov2685->dovdd_regulator = devm_regulator_get(dev, "dovdd");
+ if (IS_ERR(ov2685->dovdd_regulator)) {
+ dev_err(dev, "Failed to get dovdd-supply\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&ov2685->mutex);
+ v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
+ ret = ov2685_initialize_controls(ov2685);
+ if (ret)
+ goto destroy_mutex;
+
+ ret = ov2685_check_sensor_id(ov2685, client);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ ov2685->subdev.internal_ops = &ov2685_internal_ops;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
+ if (ret < 0)
+ goto free_ctrl_handler;
+#endif
+
+ ret = v4l2_async_register_subdev(&ov2685->subdev);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto clean_entity;
+ }
+
+ pm_runtime_enable(dev);
+ return 0;
+
+clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&ov2685->subdev.entity);
+#endif
+free_ctrl_handler:
+ v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
+destroy_mutex:
+ mutex_destroy(&ov2685->mutex);
+
+ return ret;
+}
+
+static int ov2685_remove(struct i2c_client *client)
+{
+ struct ov2685 *ov2685 = i2c_get_clientdata(client);
+
+ __ov2685_power_off(ov2685);
+ v4l2_async_unregister_subdev(&ov2685->subdev);
+ media_entity_cleanup(&ov2685->subdev.entity);
+ v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
+ mutex_destroy(&ov2685->mutex);
+
+ return 0;
+}
+
+static const struct of_device_id ov2685_of_match[] = {
+ { .compatible = "ovti,ov2685" },
+ {},
+};
+
+static struct i2c_driver ov2685_i2c_driver = {
+ .driver = {
+ .name = "ov2685",
+ .owner = THIS_MODULE,
+ .pm = &ov2685_pm_ops,
+ .of_match_table = ov2685_of_match
+ },
+ .probe = &ov2685_probe,
+ .remove = &ov2685_remove,
+};
+
+module_i2c_driver(ov2685_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov2685 sensor driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* [PATCH 2/4] [media] dt/bindings: Add bindings for OV5695
From: Shunqian Zheng @ 2017-12-25 14:11 UTC (permalink / raw)
To: mchehab, robh+dt, mark.rutland
Cc: linux-media, devicetree, ddl, tfiga, Shunqian Zheng
In-Reply-To: <1514211086-13440-1-git-send-email-zhengsq@rock-chips.com>
Add device tree binding documentation for the OV5695 sensor.
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
---
.../devicetree/bindings/media/i2c/ov5695.txt | 38 ++++++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5695.txt
diff --git a/Documentation/devicetree/bindings/media/i2c/ov5695.txt b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
new file mode 100644
index 0000000..ed27eb1
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
@@ -0,0 +1,38 @@
+* Omnivision OV5695 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: should be "ovti,ov5695"
+- clocks: reference to the 24M xvclk input clock.
+- clock-names: should be "xvclk".
+- dovdd-supply: Digital I/O voltage supply, 1.8 volts
+- avdd-supply: Analog voltage supply, 2.8 volts
+- dvdd-supply: Digital core voltage supply, 1.2 volts
+- reset-gpios: Low active reset gpio
+
+The device node must contain one 'port' child node for its digital output
+video port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+&i2c1 {
+ compatible = "ovti,ov5695";
+ reg = <0x36>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&clk_24m_cam &wcam_rst>;
+
+ clocks = <&cru SCLK_TESTCLKOUT1>;
+ clock-names = "xvclk";
+
+ avdd-supply = <&pp2800_cam>;
+ dvdd-supply = <&pp1250_cam>;
+ dovdd-supply = <&pp1800>;
+
+ reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
+
+ port {
+ wcam_out: endpoint {
+ remote-endpoint = <&mipi_in_wcam>;
+ data-lanes = <1 2>;
+ };
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH 1/4] media: ov5695: add support for OV5695 sensor
From: Shunqian Zheng @ 2017-12-25 14:11 UTC (permalink / raw)
To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
mark.rutland-5wv7dgnIgG8
Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng
This patch adds driver for Omnivision's ov5695 sensor,
the driver supports following features:
- supported resolutions
+ 2592x1944 at 30fps
+ 1920x1080 at 30fps
+ 1296x972 at 60fps
+ 1280x720 at 30fps
+ 640x480 at 120fps
- test patterns
- manual exposure/gain(analog and digital) control
- vblank and hblank
- media controller
- runtime pm
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
drivers/media/i2c/Kconfig | 11 +
drivers/media/i2c/Makefile | 1 +
drivers/media/i2c/ov5695.c | 1396 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1408 insertions(+)
create mode 100644 drivers/media/i2c/ov5695.c
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 3c6d642..55b37c8 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -645,6 +645,17 @@ config VIDEO_OV5670
To compile this driver as a module, choose M here: the
module will be called ov5670.
+config VIDEO_OV5695
+ tristate "OmniVision OV5695 sensor support"
+ depends on I2C && VIDEO_V4L2
+ depends on MEDIA_CAMERA_SUPPORT
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the OmniVision
+ OV5695 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ov5695.
+
config VIDEO_OV7640
tristate "OmniVision OV7640 sensor support"
depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 548a9ef..a063030 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
+obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
new file mode 100644
index 0000000..5d546c8
--- /dev/null
+++ b/drivers/media/i2c/ov5695.c
@@ -0,0 +1,1396 @@
+/*
+ * ov5695 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN
+#endif
+
+/* 45Mhz * 4 Binning */
+#define OV5695_PIXEL_RATE (45 * 1000 * 1000 * 4)
+#define CHIP_ID 0x005695
+#define OV5695_REG_CHIP_ID 0x300a
+
+#define OV5695_REG_CTRL_MODE 0x0100
+#define OV5695_MODE_SW_STANDBY 0x0
+#define OV5695_MODE_STREAMING BIT(0)
+
+#define OV5695_REG_EXPOSURE 0x3500
+#define OV5695_EXPOSURE_MIN 4
+#define OV5695_EXPOSURE_STEP 1
+#define OV5695_VTS_MAX 0x7fff
+#define OV5695_VBLANK_MIN 0x0
+
+#define OV5695_REG_ANALOG_GAIN 0x3509
+#define ANALOG_GAIN_MIN 0x10
+#define ANALOG_GAIN_MAX 0xf8
+#define ANALOG_GAIN_STEP 1
+#define ANALOG_GAIN_DEFAULT 0xf8
+
+#define OV5695_REG_DIGI_GAIN_H 0x350a
+#define OV5695_REG_DIGI_GAIN_L 0x350b
+#define OV5695_DIGI_GAIN_L_MASK 0x3f
+#define OV5695_DIGI_GAIN_H_SHIFT 6
+#define OV5695_DIGI_GAIN_MIN 0
+#define OV5695_DIGI_GAIN_MAX (0x4000 - 1)
+#define OV5695_DIGI_GAIN_STEP 1
+#define OV5695_DIGI_GAIN_DEFAULT 1024
+
+#define OV5695_REG_TEST_PATTERN 0x4503
+#define OV5695_TEST_PATTERN_ENABLE 0x80
+#define OV5695_TEST_PATTERN_DISABLE 0x0
+
+#define OV5695_REG_VTS 0x380e
+
+#define REG_NULL 0xFFFF
+
+#define OV5695_REG_VALUE_08BIT 1
+#define OV5695_REG_VALUE_16BIT 2
+#define OV5695_REG_VALUE_24BIT 3
+
+#define OV5695_LANES 2
+#define OV5695_BITS_PER_SAMPLE 10
+
+struct regval {
+ u16 addr;
+ u8 val;
+};
+
+struct ov5695_mode {
+ u32 width;
+ u32 height;
+ u32 max_fps;
+ u32 hts_def;
+ u32 vts_def;
+ u32 exp_def;
+ const struct regval *reg_list;
+};
+
+struct ov5695 {
+ struct i2c_client *client;
+ struct clk *xvclk;
+ struct regulator *avdd_regulator;
+ struct regulator *dovdd_regulator;
+ struct regulator *dvdd_regulator;
+ struct gpio_desc *reset_gpio;
+
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *anal_gain;
+ struct v4l2_ctrl *digi_gain;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *test_pattern;
+ struct mutex mutex;
+ bool streaming;
+ const struct ov5695_mode *cur_mode;
+};
+#define to_ov5695(sd) container_of(sd, struct ov5695, subdev)
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1296
+ * grabwindow_height 972
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_global_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x0300, 0x04},
+ {0x0301, 0x00},
+ {0x0302, 0x69},
+ {0x0303, 0x00},
+ {0x0304, 0x00},
+ {0x0305, 0x01},
+ {0x0307, 0x00},
+ {0x030b, 0x00},
+ {0x030c, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x04},
+ {0x030f, 0x03},
+ {0x0312, 0x01},
+ {0x3000, 0x00},
+ {0x3002, 0xa1},
+ {0x0308, 0x00},
+ {0x0310, 0x00},
+ {0x3022, 0x51},
+ {0x3106, 0x15},
+ {0x3107, 0x01},
+ {0x3108, 0x05},
+ {0x3500, 0x00},
+ {0x3501, 0x45},
+ {0x3502, 0x00},
+ {0x3503, 0x08},
+ {0x3504, 0x03},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0x10},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0x55},
+ {0x3602, 0x58},
+ {0x3614, 0x30},
+ {0x3615, 0x77},
+ {0x3621, 0x08},
+ {0x3624, 0x40},
+ {0x3633, 0x0c},
+ {0x3634, 0x0c},
+ {0x3635, 0x0c},
+ {0x3636, 0x0c},
+ {0x3638, 0x00},
+ {0x3639, 0x00},
+ {0x363a, 0x00},
+ {0x363b, 0x00},
+ {0x363c, 0xff},
+ {0x363d, 0xfa},
+ {0x3650, 0x44},
+ {0x3651, 0x44},
+ {0x3652, 0x44},
+ {0x3653, 0x44},
+ {0x3654, 0x44},
+ {0x3655, 0x44},
+ {0x3656, 0x44},
+ {0x3657, 0x44},
+ {0x3660, 0x00},
+ {0x3661, 0x00},
+ {0x3662, 0x00},
+ {0x366a, 0x00},
+ {0x366e, 0x0c},
+ {0x3673, 0x04},
+ {0x3700, 0x14},
+ {0x3703, 0x0c},
+ {0x3715, 0x01},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x373f, 0xa0},
+ {0x3765, 0x20},
+ {0x37a1, 0x1d},
+ {0x37a8, 0x26},
+ {0x37ab, 0x14},
+ {0x37c2, 0x04},
+ {0x37cb, 0x09},
+ {0x37cc, 0x13},
+ {0x37cd, 0x1f},
+ {0x37ce, 0x1f},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x10},
+ {0x380a, 0x03},
+ {0x380b, 0xcc},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3810, 0x00},
+ {0x3811, 0x06},
+ {0x3812, 0x00},
+ {0x3813, 0x06},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x381a, 0x00},
+ {0x381b, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x3c80, 0x08},
+ {0x3c82, 0x00},
+ {0x3c83, 0x00},
+ {0x3c88, 0x00},
+ {0x3d85, 0x14},
+ {0x3f02, 0x08},
+ {0x3f03, 0x10},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x404e, 0x20},
+ {0x4501, 0x00},
+ {0x4502, 0x10},
+ {0x4800, 0x00},
+ {0x481f, 0x2a},
+ {0x4837, 0x13},
+ {0x5000, 0x17},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x5b00, 0x00},
+ {0x5b01, 0x1c},
+ {0x5b02, 0x00},
+ {0x5b03, 0x7f},
+ {0x5b05, 0x6c},
+ {0x5e10, 0xfc},
+ {0x4010, 0xf1},
+ {0x3503, 0x08},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0xf8},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 740(0x2e4)
+ * framelength 2024(0x7e8)
+ * grabwindow_width 2592
+ * grabwindow_height 1944
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_2592x1944_regs[] = {
+ {0x3501, 0x7e},
+ {0x366e, 0x18},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x04},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xab},
+ {0x3808, 0x0a},
+ {0x3809, 0x20},
+ {0x380a, 0x07},
+ {0x380b, 0x98},
+ {0x380c, 0x02},
+ {0x380d, 0xe4},
+ {0x380e, 0x07},
+ {0x380f, 0xe8},
+ {0x3811, 0x06},
+ {0x3813, 0x08},
+ {0x3814, 0x01},
+ {0x3816, 0x01},
+ {0x3817, 0x01},
+ {0x3820, 0x88},
+ {0x3821, 0x00},
+ {0x4501, 0x00},
+ {0x4008, 0x04},
+ {0x4009, 0x13},
+ {REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1920
+ * grabwindow_height 1080
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1920x1080_regs[] = {
+ {0x3501, 0x45},
+ {0x366e, 0x18},
+ {0x3800, 0x01},
+ {0x3801, 0x50},
+ {0x3802, 0x01},
+ {0x3803, 0xb8},
+ {0x3804, 0x08},
+ {0x3805, 0xef},
+ {0x3806, 0x05},
+ {0x3807, 0xf7},
+ {0x3808, 0x07},
+ {0x3809, 0x80},
+ {0x380a, 0x04},
+ {0x380b, 0x38},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3811, 0x06},
+ {0x3813, 0x04},
+ {0x3814, 0x01},
+ {0x3816, 0x01},
+ {0x3817, 0x01},
+ {0x3820, 0x88},
+ {0x3821, 0x00},
+ {0x4501, 0x00},
+ {0x4008, 0x04},
+ {0x4009, 0x13},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 740(0x02e4)
+ * framelength 1012(0x03f4)
+ * grabwindow_width 1296
+ * grabwindow_height 972
+ * max_framerate 60fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1296x972_regs[] = {
+ {0x0103, 0x01},
+ {0x0100, 0x00},
+ {0x0300, 0x04},
+ {0x0301, 0x00},
+ {0x0302, 0x69},
+ {0x0303, 0x00},
+ {0x0304, 0x00},
+ {0x0305, 0x01},
+ {0x0307, 0x00},
+ {0x030b, 0x00},
+ {0x030c, 0x00},
+ {0x030d, 0x1e},
+ {0x030e, 0x04},
+ {0x030f, 0x03},
+ {0x0312, 0x01},
+ {0x3000, 0x00},
+ {0x3002, 0x21},
+ {0x3016, 0x32},
+ {0x3022, 0x51},
+ {0x3106, 0x15},
+ {0x3107, 0x01},
+ {0x3108, 0x05},
+ {0x3500, 0x00},
+ {0x3501, 0x3e},
+ {0x3502, 0x00},
+ {0x3503, 0x08},
+ {0x3504, 0x03},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0x10},
+ {0x350c, 0x00},
+ {0x350d, 0x80},
+ {0x3510, 0x00},
+ {0x3511, 0x02},
+ {0x3512, 0x00},
+ {0x3601, 0x55},
+ {0x3602, 0x58},
+ {0x3611, 0x58},
+ {0x3614, 0x30},
+ {0x3615, 0x77},
+ {0x3621, 0x08},
+ {0x3624, 0x40},
+ {0x3633, 0x0c},
+ {0x3634, 0x0c},
+ {0x3635, 0x0c},
+ {0x3636, 0x0c},
+ {0x3638, 0x00},
+ {0x3639, 0x00},
+ {0x363a, 0x00},
+ {0x363b, 0x00},
+ {0x363c, 0xff},
+ {0x363d, 0xfa},
+ {0x3650, 0x44},
+ {0x3651, 0x44},
+ {0x3652, 0x44},
+ {0x3653, 0x44},
+ {0x3654, 0x44},
+ {0x3655, 0x44},
+ {0x3656, 0x44},
+ {0x3657, 0x44},
+ {0x3660, 0x00},
+ {0x3661, 0x00},
+ {0x3662, 0x00},
+ {0x366a, 0x00},
+ {0x366e, 0x0c},
+ {0x3673, 0x04},
+ {0x3700, 0x14},
+ {0x3703, 0x0c},
+ {0x3706, 0x24},
+ {0x3714, 0x27},
+ {0x3715, 0x01},
+ {0x3716, 0x00},
+ {0x3717, 0x02},
+ {0x3733, 0x10},
+ {0x3734, 0x40},
+ {0x373f, 0xa0},
+ {0x3765, 0x20},
+ {0x37a1, 0x1d},
+ {0x37a8, 0x26},
+ {0x37ab, 0x14},
+ {0x37c2, 0x04},
+ {0x37c3, 0xf0},
+ {0x37cb, 0x09},
+ {0x37cc, 0x13},
+ {0x37cd, 0x1f},
+ {0x37ce, 0x1f},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x10},
+ {0x380a, 0x03},
+ {0x380b, 0xcc},
+ {0x380c, 0x02},
+ {0x380d, 0xe4},
+ {0x380e, 0x03},
+ {0x380f, 0xf4},
+ {0x3810, 0x00},
+ {0x3811, 0x00},
+ {0x3812, 0x00},
+ {0x3813, 0x06},
+ {0x3814, 0x03},
+ {0x3815, 0x01},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3818, 0x00},
+ {0x3819, 0x00},
+ {0x381a, 0x00},
+ {0x381b, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x3c80, 0x08},
+ {0x3c82, 0x00},
+ {0x3c83, 0x00},
+ {0x3c88, 0x00},
+ {0x3d85, 0x14},
+ {0x3f02, 0x08},
+ {0x3f03, 0x10},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {0x404e, 0x20},
+ {0x4501, 0x00},
+ {0x4502, 0x10},
+ {0x4800, 0x00},
+ {0x481f, 0x2a},
+ {0x4837, 0x13},
+ {0x5000, 0x13},
+ {0x5780, 0x3e},
+ {0x5781, 0x0f},
+ {0x5782, 0x44},
+ {0x5783, 0x02},
+ {0x5784, 0x01},
+ {0x5785, 0x01},
+ {0x5786, 0x00},
+ {0x5787, 0x04},
+ {0x5788, 0x02},
+ {0x5789, 0x0f},
+ {0x578a, 0xfd},
+ {0x578b, 0xf5},
+ {0x578c, 0xf5},
+ {0x578d, 0x03},
+ {0x578e, 0x08},
+ {0x578f, 0x0c},
+ {0x5790, 0x08},
+ {0x5791, 0x06},
+ {0x5792, 0x00},
+ {0x5793, 0x52},
+ {0x5794, 0xa3},
+ {0x5b00, 0x00},
+ {0x5b01, 0x1c},
+ {0x5b02, 0x00},
+ {0x5b03, 0x7f},
+ {0x5b05, 0x6c},
+ {0x5e10, 0xfc},
+ {0x4010, 0xf1},
+ {0x3503, 0x08},
+ {0x3505, 0x8c},
+ {0x3507, 0x03},
+ {0x3508, 0x00},
+ {0x3509, 0xf8},
+ {0x0100, 0x01},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1280
+ * grabwindow_height 720
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1280x720_regs[] = {
+ {0x3501, 0x45},
+ {0x366e, 0x0c},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x01},
+ {0x3803, 0x00},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x06},
+ {0x3807, 0xaf},
+ {0x3808, 0x05},
+ {0x3809, 0x00},
+ {0x380a, 0x02},
+ {0x380b, 0xd0},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x08},
+ {0x380f, 0xb8},
+ {0x3811, 0x06},
+ {0x3813, 0x02},
+ {0x3814, 0x03},
+ {0x3816, 0x03},
+ {0x3817, 0x01},
+ {0x3820, 0x8b},
+ {0x3821, 0x01},
+ {0x4501, 0x00},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 558(0x22e)
+ * grabwindow_width 640
+ * grabwindow_height 480
+ * max_framerate 120fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_640x480_regs[] = {
+ {0x3501, 0x22},
+ {0x366e, 0x0c},
+ {0x3800, 0x00},
+ {0x3801, 0x00},
+ {0x3802, 0x00},
+ {0x3803, 0x08},
+ {0x3804, 0x0a},
+ {0x3805, 0x3f},
+ {0x3806, 0x07},
+ {0x3807, 0xa7},
+ {0x3808, 0x02},
+ {0x3809, 0x80},
+ {0x380a, 0x01},
+ {0x380b, 0xe0},
+ {0x380c, 0x02},
+ {0x380d, 0xa0},
+ {0x380e, 0x02},
+ {0x380f, 0x2e},
+ {0x3811, 0x06},
+ {0x3813, 0x04},
+ {0x3814, 0x07},
+ {0x3816, 0x05},
+ {0x3817, 0x03},
+ {0x3820, 0x8d},
+ {0x3821, 0x01},
+ {0x4501, 0x00},
+ {0x4008, 0x02},
+ {0x4009, 0x09},
+ {REG_NULL, 0x00}
+};
+
+static const struct ov5695_mode supported_modes[] = {
+ {
+ .width = 2592,
+ .height = 1944,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02e4 * 4,
+ .vts_def = 0x07e8,
+ .reg_list = ov5695_2592x1944_regs,
+ },
+ {
+ .width = 1920,
+ .height = 1080,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x08b8,
+ .reg_list = ov5695_1920x1080_regs,
+ },
+ {
+ .width = 1296,
+ .height = 972,
+ .max_fps = 60,
+ .exp_def = 0x03e0,
+ .hts_def = 0x02e4 * 4,
+ .vts_def = 0x03f4,
+ .reg_list = ov5695_1296x972_regs,
+ },
+ {
+ .width = 1280,
+ .height = 720,
+ .max_fps = 30,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x08b8,
+ .reg_list = ov5695_1280x720_regs,
+ },
+ {
+ .width = 640,
+ .height = 480,
+ .max_fps = 120,
+ .exp_def = 0x0450,
+ .hts_def = 0x02a0 * 4,
+ .vts_def = 0x022e,
+ .reg_list = ov5695_640x480_regs,
+ },
+};
+
+#define OV5695_LINK_FREQ_420MHZ 420000000
+static const s64 link_freq_menu_items[] = {
+ OV5695_LINK_FREQ_420MHZ
+};
+
+static const char * const ov5695_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+ "Vertical Color Bar Type 2",
+ "Vertical Color Bar Type 3",
+ "Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int ov5695_write_reg(struct i2c_client *client, u16 reg,
+ unsigned int len, u32 val)
+{
+ int buf_i;
+ int val_i;
+ u8 buf[6];
+ u8 *val_p;
+
+ if (len > 4)
+ return -EINVAL;
+
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
+
+ val = cpu_to_be32(val);
+ val_p = (u8 *)&val;
+ buf_i = 2;
+ val_i = 4 - len;
+
+ while (val_i < 4)
+ buf[buf_i++] = val_p[val_i++];
+
+ if (i2c_master_send(client, buf, len + 2) != len + 2)
+ return -EIO;
+
+ return 0;
+}
+
+static int ov5695_write_array(struct i2c_client *client,
+ const struct regval *regs)
+{
+ int i, ret = 0;
+
+ for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+ ret = ov5695_write_reg(client, regs[i].addr,
+ OV5695_REG_VALUE_08BIT, regs[i].val);
+
+ return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov5695_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+ u32 *val)
+{
+ struct i2c_msg msgs[2];
+ u8 *data_be_p;
+ u32 data_be = 0;
+ u16 reg_addr_be = cpu_to_be16(reg);
+ int ret;
+
+ if (len > 4)
+ return -EINVAL;
+
+ data_be_p = (u8 *)&data_be;
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 2;
+ msgs[0].buf = (u8 *)®_addr_be;
+
+ /* Read data from register */
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = &data_be_p[4 - len];
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *val = be32_to_cpu(data_be);
+
+ return 0;
+}
+
+static int ov5695_get_reso_dist(const struct ov5695_mode *mode,
+ struct v4l2_mbus_framefmt *framefmt)
+{
+ return abs(mode->width - framefmt->width) +
+ abs(mode->height - framefmt->height);
+}
+
+static const struct ov5695_mode *ov5695_find_best_fit(
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+ int dist;
+ int cur_best_fit = 0;
+ int cur_best_fit_dist = -1;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+ dist = ov5695_get_reso_dist(&supported_modes[i], framefmt);
+ if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+ cur_best_fit_dist = dist;
+ cur_best_fit = i;
+ }
+ }
+
+ return &supported_modes[cur_best_fit];
+}
+
+static int ov5695_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ const struct ov5695_mode *mode;
+ s64 h_blank, vblank_def;
+
+ mutex_lock(&ov5695->mutex);
+
+ mode = ov5695_find_best_fit(fmt);
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+ mutex_unlock(&ov5695->mutex);
+ return -ENOTTY;
+#endif
+ } else {
+ ov5695->cur_mode = mode;
+ h_blank = mode->hts_def - mode->width;
+ __v4l2_ctrl_modify_range(ov5695->hblank, h_blank,
+ h_blank, 1, h_blank);
+ vblank_def = mode->vts_def - mode->height;
+ __v4l2_ctrl_modify_range(ov5695->vblank, OV5695_VBLANK_MIN,
+ OV5695_VTS_MAX - mode->height,
+ vblank_def, vblank_def);
+ }
+
+ mutex_unlock(&ov5695->mutex);
+ return 0;
+}
+
+static int ov5695_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ const struct ov5695_mode *mode = ov5695->cur_mode;
+
+ mutex_lock(&ov5695->mutex);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+ mutex_unlock(&ov5695->mutex);
+ return -ENOTTY;
+#endif
+ } else {
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.field = V4L2_FIELD_NONE;
+ }
+ mutex_unlock(&ov5695->mutex);
+ return 0;
+}
+
+static int ov5695_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+ code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+ return 0;
+}
+
+static int ov5695_enum_frame_sizes(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index > ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+ fse->max_width = supported_modes[fse->index].width;
+ fse->max_height = supported_modes[fse->index].height;
+ fse->min_height = supported_modes[fse->index].height;
+
+ return 0;
+}
+
+static int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern)
+{
+ u32 val;
+
+ if (pattern)
+ val = (pattern - 1) | OV5695_TEST_PATTERN_ENABLE;
+ else
+ val = OV5695_TEST_PATTERN_DISABLE;
+
+ return ov5695_write_reg(ov5695->client, OV5695_REG_TEST_PATTERN,
+ OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_exposure(struct ov5695 *ov5695, s32 val)
+{
+ /* 4 least significant bits of expsoure are fractional part */
+ ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE,
+ OV5695_REG_VALUE_24BIT, val << 4);
+}
+
+static void ov5695_set_analog_gain(struct ov5695 *ov5695, s32 val)
+{
+ ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN,
+ OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_digi_gain(struct ov5695 *ov5695, s32 val)
+{
+ ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L,
+ OV5695_REG_VALUE_08BIT, val & OV5695_DIGI_GAIN_L_MASK);
+ val >>= OV5695_DIGI_GAIN_H_SHIFT;
+ ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H,
+ OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_vts(struct ov5695 *ov5695, s32 val)
+{
+ val += ov5695->cur_mode->height;
+
+ ov5695_write_reg(ov5695->client, OV5695_REG_VTS,
+ OV5695_REG_VALUE_16BIT, val);
+}
+
+static int __ov5695_start_stream(struct ov5695 *ov5695)
+{
+ int ret;
+
+ ret = ov5695_write_array(ov5695->client, ov5695_global_regs);
+ if (ret)
+ return ret;
+ ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list);
+ if (ret)
+ return ret;
+
+ /* In case these controls are set before streaming */
+ ov5695_set_exposure(ov5695, ov5695->exposure->val);
+ ov5695_set_analog_gain(ov5695, ov5695->anal_gain->val);
+ ov5695_set_digi_gain(ov5695, ov5695->digi_gain->val);
+ ov5695_set_vts(ov5695, ov5695->vblank->val);
+ ov5695_enable_test_pattern(ov5695, ov5695->test_pattern->val);
+
+ ret = ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
+ OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING);
+ return ret;
+}
+
+static int __ov5695_stop_stream(struct ov5695 *ov5695)
+{
+ return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
+ OV5695_REG_VALUE_08BIT, OV5695_MODE_SW_STANDBY);
+}
+
+static int ov5695_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ struct i2c_client *client = ov5695->client;
+ int ret = 0;
+
+ mutex_lock(&ov5695->mutex);
+ on = !!on;
+ if (on == ov5695->streaming)
+ goto unlock_and_return;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&client->dev);
+ if (ret)
+ goto unlock_and_return;
+
+ ret = __ov5695_start_stream(ov5695);
+ if (ret) {
+ v4l2_err(sd, "start stream failed while write regs\n");
+ pm_runtime_put(&client->dev);
+ }
+ } else {
+ ret = __ov5695_stop_stream(ov5695);
+ if (ret)
+ goto unlock_and_return;
+ pm_runtime_put(&client->dev);
+ }
+
+ ov5695->streaming = on;
+
+unlock_and_return:
+ mutex_unlock(&ov5695->mutex);
+ return ret;
+}
+
+static int __ov5695_power_on(struct ov5695 *ov5695)
+{
+ int ret;
+ struct device *dev = &ov5695->client->dev;
+
+ ret = clk_prepare_enable(ov5695->xvclk);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable xvclk\n");
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
+
+ /* AVDD and DOVDD may rise in any order */
+ ret = regulator_enable(ov5695->avdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulator\n");
+ goto disable_clk;
+ }
+ ret = regulator_enable(ov5695->dovdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable DOVDD regulator\n");
+ goto disable_avdd;
+ }
+ /* DVDD must rise after AVDD and DOVDD */
+ ret = regulator_enable(ov5695->dvdd_regulator);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable DVDD regulator\n");
+ goto disable_dovdd;
+ }
+
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
+ /* 8192 cycles prior to first SCCB transaction */
+ usleep_range(1000, 1500);
+
+ return 0;
+
+disable_dovdd:
+ regulator_disable(ov5695->dovdd_regulator);
+disable_avdd:
+ regulator_disable(ov5695->avdd_regulator);
+disable_clk:
+ clk_disable_unprepare(ov5695->xvclk);
+
+ return ret;
+}
+
+static void __ov5695_power_off(struct ov5695 *ov5695)
+{
+ clk_disable_unprepare(ov5695->xvclk);
+ gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
+ regulator_disable(ov5695->dvdd_regulator);
+ regulator_disable(ov5695->dovdd_regulator);
+ regulator_disable(ov5695->avdd_regulator);
+}
+
+static int ov5695_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ int ret;
+
+ ret = __ov5695_power_on(ov5695);
+ if (ret)
+ return ret;
+
+ if (ov5695->streaming) {
+ ret = __ov5695_start_stream(ov5695);
+ if (ret)
+ __ov5695_power_off(ov5695);
+ }
+
+ return ret;
+}
+
+static int ov5695_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct ov5695 *ov5695 = to_ov5695(sd);
+
+ __ov5695_power_off(ov5695);
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct ov5695 *ov5695 = to_ov5695(sd);
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+ mutex_lock(&ov5695->mutex);
+ /* Initialize try_fmt */
+ try_fmt->width = ov5695->cur_mode->width;
+ try_fmt->height = ov5695->cur_mode->height;
+ try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ try_fmt->field = V4L2_FIELD_NONE;
+
+ mutex_unlock(&ov5695->mutex);
+ /* No crop or compose */
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops ov5695_pm_ops = {
+ SET_RUNTIME_PM_OPS(ov5695_runtime_suspend,
+ ov5695_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov5695_internal_ops = {
+ .open = ov5695_open,
+};
+#endif
+
+static struct v4l2_subdev_video_ops ov5695_video_ops = {
+ .s_stream = ov5695_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5695_pad_ops = {
+ .enum_mbus_code = ov5695_enum_mbus_code,
+ .enum_frame_size = ov5695_enum_frame_sizes,
+ .get_fmt = ov5695_get_fmt,
+ .set_fmt = ov5695_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov5695_subdev_ops = {
+ .video = &ov5695_video_ops,
+ .pad = &ov5695_pad_ops,
+};
+
+static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ov5695 *ov5695 = container_of(ctrl->handler,
+ struct ov5695, ctrl_handler);
+ struct i2c_client *client = ov5695->client;
+ s64 max;
+ int ret = 0;
+
+ /* Propagate change of current control to all related controls */
+ switch (ctrl->id) {
+ case V4L2_CID_VBLANK:
+ /* Update max exposure while meeting expected vblanking */
+ max = ov5695->cur_mode->height + ctrl->val - 4;
+ __v4l2_ctrl_modify_range(ov5695->exposure,
+ ov5695->exposure->minimum, max,
+ ov5695->exposure->step,
+ ov5695->exposure->default_value);
+ break;
+ }
+
+ pm_runtime_get_sync(&client->dev);
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ ov5695_set_exposure(ov5695, ctrl->val);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ ov5695_set_analog_gain(ov5695, ctrl->val);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ov5695_set_digi_gain(ov5695, ctrl->val);
+ break;
+ case V4L2_CID_VBLANK:
+ ov5695_set_vts(ov5695, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ ret = ov5695_enable_test_pattern(ov5695, ctrl->val);
+ break;
+ default:
+ dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+ __func__, ctrl->id, ctrl->val);
+ break;
+ };
+ pm_runtime_put(&client->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops ov5695_ctrl_ops = {
+ .s_ctrl = ov5695_set_ctrl,
+};
+
+static int ov5695_initialize_controls(struct ov5695 *ov5695)
+{
+ const struct ov5695_mode *mode;
+ struct v4l2_ctrl_handler *handler;
+ struct v4l2_ctrl *ctrl;
+ s64 exposure_max, vblank_def;
+ u32 h_blank;
+ int ret;
+
+ handler = &ov5695->ctrl_handler;
+ mode = ov5695->cur_mode;
+ ret = v4l2_ctrl_handler_init(handler, 1);
+ if (ret)
+ return ret;
+ handler->lock = &ov5695->mutex;
+
+ ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+ 0, OV5695_PIXEL_RATE, 1, OV5695_PIXEL_RATE);
+
+ h_blank = mode->hts_def - mode->width;
+ ov5695->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+ h_blank, h_blank, 1, h_blank);
+ if (ov5695->hblank)
+ ov5695->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ vblank_def = mode->vts_def - mode->height;
+ ov5695->vblank = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_VBLANK, OV5695_VBLANK_MIN,
+ OV5695_VTS_MAX - mode->height,
+ vblank_def, vblank_def);
+
+ exposure_max = mode->vts_def - 4;
+ ov5695->exposure = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_EXPOSURE, OV5695_EXPOSURE_MIN,
+ exposure_max, OV5695_EXPOSURE_STEP,
+ mode->exp_def);
+
+ ov5695->anal_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN,
+ ANALOG_GAIN_MAX, ANALOG_GAIN_STEP,
+ ANALOG_GAIN_DEFAULT);
+
+ /* Digital gain */
+ ov5695->digi_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+ V4L2_CID_DIGITAL_GAIN, OV5695_DIGI_GAIN_MIN,
+ OV5695_DIGI_GAIN_MAX, OV5695_DIGI_GAIN_STEP,
+ OV5695_DIGI_GAIN_DEFAULT);
+
+ ov5695->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+ &ov5695_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov5695_test_pattern_menu) - 1,
+ 0, 0, ov5695_test_pattern_menu);
+
+ if (handler->error) {
+ v4l2_ctrl_handler_free(handler);
+ return handler->error;
+ }
+
+ ov5695->subdev.ctrl_handler = handler;
+
+ return 0;
+}
+
+static int ov5695_check_sensor_id(struct ov5695 *ov5695,
+ struct i2c_client *client)
+{
+ struct device *dev = &ov5695->client->dev;
+ u32 id;
+ int ret;
+
+ ret = __ov5695_power_on(ov5695);
+ if (ret)
+ return ret;
+ ov5695_read_reg(client, OV5695_REG_CHIP_ID,
+ OV5695_REG_VALUE_24BIT, &id);
+ __ov5695_power_off(ov5695);
+
+ if (id != CHIP_ID) {
+ dev_err(dev, "Wrong camera sensor id(%06x)\n", id);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+ return 0;
+}
+
+static int ov5695_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ov5695 *ov5695;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL);
+ if (!ov5695)
+ return -ENOMEM;
+
+ ov5695->client = client;
+ ov5695->cur_mode = &supported_modes[0];
+
+ ov5695->xvclk = devm_clk_get(dev, "xvclk");
+ if (IS_ERR(ov5695->xvclk)) {
+ dev_err(dev, "Failed to get xvclk\n");
+ return -EINVAL;
+ }
+ ret = clk_set_rate(ov5695->xvclk, 24000000);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set xvclk rate (24M)\n");
+ return ret;
+ }
+
+ ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ov5695->reset_gpio)) {
+ dev_err(dev, "Failed to get reset-gpios\n");
+ return -EINVAL;
+ }
+
+ ov5695->avdd_regulator = devm_regulator_get(dev, "avdd");
+ if (IS_ERR(ov5695->avdd_regulator)) {
+ dev_err(dev, "Failed to get avdd-supply\n");
+ return -EINVAL;
+ }
+ ov5695->dovdd_regulator = devm_regulator_get(dev, "dovdd");
+ if (IS_ERR(ov5695->dovdd_regulator)) {
+ dev_err(dev, "Failed to get dovdd-supply\n");
+ return -EINVAL;
+ }
+ ov5695->dvdd_regulator = devm_regulator_get(dev, "dvdd");
+ if (IS_ERR(ov5695->dvdd_regulator)) {
+ dev_err(dev, "Failed to get dvdd-supply\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&ov5695->mutex);
+
+ sd = &ov5695->subdev;
+ v4l2_i2c_subdev_init(sd, client, &ov5695_subdev_ops);
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+ sd->internal_ops = &ov5695_internal_ops;
+#endif
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ ret = ov5695_initialize_controls(ov5695);
+ if (ret)
+ return ret;
+ ret = ov5695_check_sensor_id(ov5695, client);
+ if (ret)
+ return ret;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ ov5695->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad);
+ if (ret < 0)
+ return ret;
+#endif
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret) {
+ dev_err(dev, "v4l2 async register subdev failed\n");
+ goto clean_entity;
+ }
+
+ pm_runtime_enable(dev);
+ dev_info(dev, "Probe successfully\n");
+
+ return 0;
+
+clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ media_entity_cleanup(&sd->entity);
+#endif
+
+ return ret;
+}
+
+static int ov5695_remove(struct i2c_client *client)
+{
+ struct ov5695 *ov5695 = i2c_get_clientdata(client);
+
+ v4l2_async_unregister_subdev(&ov5695->subdev);
+
+ pm_runtime_disable(&client->dev);
+ return 0;
+}
+
+static const struct of_device_id ov5695_of_match[] = {
+ { .compatible = "ovti,ov5695" },
+ {},
+};
+
+static struct i2c_driver ov5695_i2c_driver = {
+ .driver = {
+ .name = "ov5695",
+ .owner = THIS_MODULE,
+ .pm = &ov5695_pm_ops,
+ .of_match_table = ov5695_of_match
+ },
+ .probe = &ov5695_probe,
+ .remove = &ov5695_remove,
+};
+
+module_i2c_driver(ov5695_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov5695 sensor driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [patch v15 4/4] Documentation: jtag: Add ABI documentation
From: Oleksandr Shamray @ 2017-12-25 11:53 UTC (permalink / raw)
To: gregkh, arnd
Cc: system-sw-low-level, devicetree, jiri, vadimp, linux-api, openbmc,
linux-kernel, openocd-devel-owner, robh+dt, joel, linux-serial,
Oleksandr Shamray, tklauser, mchehab, davem, linux-arm-kernel
In-Reply-To: <1514202808-29747-1-git-send-email-oleksandrs@mellanox.com>
Added document that describe the ABI for JTAG class drivrer
Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
v14->v15
v13->v14
v12->v13
v11->v12
Tobias Klauser <tklauser@distanz.ch>
- rename /Documentation/ABI/testing/jatg-dev -> jtag-dev
- Typo: s/interfase/interface
v10->v11
v9->v10
Fixes added by Oleksandr:
- change jtag-cdev to jtag-dev in documentation
- update Kernel Version and Date in jtag-dev documentation;
v8->v9
v7->v8
v6->v7
Comments pointed by Pavel Machek <pavel@ucw.cz>
- Added jtag-cdev documentation to Documentation/ABI/testing folder
---
Documentation/ABI/testing/jtag-dev | 27 +++++++++++++++++++++++++++
1 files changed, 27 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/jtag-dev
diff --git a/Documentation/ABI/testing/jtag-dev b/Documentation/ABI/testing/jtag-dev
new file mode 100644
index 0000000..cab867d
--- /dev/null
+++ b/Documentation/ABI/testing/jtag-dev
@@ -0,0 +1,27 @@
+What: /dev/jtag[0-9]+
+Date: October 2017
+KernelVersion: 4.15
+Contact: oleksandrs@mellanox.com
+Description:
+ The misc device files /dev/jtag* are the interface
+ between JTAG master interface and userspace.
+
+ The ioctl(2)-based ABI is defined and documented in
+ [include/uapi]<linux/jtag.h>.
+
+ The following file operations are supported:
+
+ open(2)
+ The argument flag currently support only one access
+ mode O_RDWR.
+
+ ioctl(2)
+ Initiate various actions.
+ See the inline documentation in [include/uapi]<linux/jtag.h>
+ for descriptions of all ioctls.
+
+ close(2)
+ Stops and free up the I/O contexts that was associated
+ with the file descriptor.
+
+Users: TBD
\ No newline at end of file
--
1.7.1
^ permalink raw reply related
* [patch v15 3/4] Documentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2017-12-25 11:53 UTC (permalink / raw)
To: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r, arnd-r2nGTMty4D4
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-u79uwXL29TY76Z2rM5mHXA, openbmc-uLR06cmDAlY/bJ5BZ2RsiQ,
joel-U3u1mxZcP9KHXe+LvDLADg, jiri-rHqAuBHg3fBzbRFIqnYvSA,
tklauser-93Khv+1bN0NyDzI6CaY1VQ,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
vadimp-VPRAkNaXOzVWk0Htik3J/w,
system-sw-low-level-VPRAkNaXOzVWk0Htik3J/w,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
openocd-devel-owner-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linux-api-u79uwXL29TY76Z2rM5mHXA, davem-fT/PcQaiUtIeIZ0/mPfg9Q,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, Oleksandr Shamray, Jiri Pirko
In-Reply-To: <1514202808-29747-1-git-send-email-oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
It has been tested on Mellanox system with BMC equipped with
Aspeed 2520 SoC for programming CPLD devices.
Signed-off-by: Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Signed-off-by: Jiri Pirko <jiri-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
v14->v15
v13->v14
v12->v13
v11->v12
v10->v11
v9->v10
v8->v9
v7->v8
Comments pointed by pointed by Joel Stanley <joel.stan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
- Change compatible string to ast2400 and ast2000
V6->v7
Comments pointed by Tobias Klauser <tklauser-93Khv+1bN0NyDzI6CaY1VQ@public.gmane.org>
- Fix spell "Doccumentation" -> "Documentation"
v5->v6
Comments pointed by Tobias Klauser <tklauser-93Khv+1bN0NyDzI6CaY1VQ@public.gmane.org>
- Small nit: s/documentation/Documentation/
v4->v5
V3->v4
Comments pointed by Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
- delete unnecessary "status" and "reg-shift" descriptions in
bndings file
v2->v3
Comments pointed by Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
- split Aspeed jtag driver and binding to sepatrate patches
- delete unnecessary "status" and "reg-shift" descriptions in
bndings file
---
.../devicetree/bindings/jtag/aspeed-jtag.txt | 18 ++++++++++++++++++
1 files changed, 18 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
diff --git a/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
new file mode 100644
index 0000000..8cfc610
--- /dev/null
+++ b/Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
@@ -0,0 +1,18 @@
+Aspeed JTAG driver for ast2400 and ast2500 SoC
+
+Required properties:
+- compatible: Should be one of
+ - "aspeed,ast2400-jtag"
+ - "aspeed,ast2500-jtag"
+- reg contains the offset and length of the JTAG memory
+ region
+- clocks root clock of bus, should reference the APB clock
+- interrupts should contain JTAG controller interrupt
+
+Example:
+jtag: jtag@1e6e4000 {
+ compatible = "aspeed,ast2500-jtag";
+ reg = <0x1e6e4000 0x1c>;
+ clocks = <&clk_apb>;
+ interrupts = <43>;
+};
--
1.7.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [patch v15 2/4] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2017-12-25 11:53 UTC (permalink / raw)
To: gregkh, arnd
Cc: linux-kernel, linux-arm-kernel, devicetree, openbmc, joel, jiri,
tklauser, linux-serial, vadimp, system-sw-low-level, robh+dt,
openocd-devel-owner, linux-api, davem, mchehab, Oleksandr Shamray,
Jiri Pirko
In-Reply-To: <1514202808-29747-1-git-send-email-oleksandrs@mellanox.com>
Driver adds support of Aspeed 2500/2400 series SOC JTAG master controller.
Driver implements the following jtag ops:
- freq_get;
- freq_set;
- status_get;
- idle;
- xfer;
It has been tested on Mellanox system with BMC equipped with
Aspeed 2520 SoC for programming CPLD devices.
Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
---
v14->v15
Comments pointed by Joel Stanley <joel.stan@gmail.com>
- Add ARCH_ASPEED || COMPILE_TEST to Kconfig
- remove unused offset variable
- remove "aspeed_jtag" from dev_err and dev_dbg messages
- change clk_prepare_enable initialisation order
v13->v14
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change style of head block comment from /**/ to //
v12->v13
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change jtag-aspeed.c licence type to
SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
and reorder line with license in description
Comments pointed by Kun Yi <kunyi@google.com>
- Changed capability check for aspeed,ast2400-jtag/ast200-jtag
v11->v12
Comments pointed by Chip Bilbrey <chip@bilbrey.org>
- Remove access mode from xfer and idle transactions
- Add new ioctl JTAG_SIOCMODE for set hw mode
v10->v11
v9->v10
V8->v9
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- add *data parameter to xfer function prototype
v7->v8
Comments pointed by Joel Stanley <joel.stan@gmail.com>
- aspeed_jtag_init replace goto to return;
- change input variables type from __u32 to u32
in functios freq_get, freq_set, status_get
- change sm_ variables type from char to u8
- in jatg_init add disable clocks on error case
- remove release_mem_region on error case
- remove devm_free_irq on jtag_deinit
- Fix misspelling Disabe/Disable
- Change compatible string to ast2400 and ast2000
v6->v7
Notifications from kbuild test robot <lkp@intel.com>
- Add include <linux/types.h> to jtag-asapeed.c
v5->v6
v4->v5
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Added HAS_IOMEM dependence in Kconfig to avoid
"undefined reference to `devm_ioremap_resource'" error,
because in some arch this not supported
v3->v4
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- change transaction pointer tdio type to __u64
- change internal status type from enum to __u32
v2->v3
v1->v2
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- change license type from GPLv2/BSD to GPLv2
Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
- Add clk_prepare_enable/clk_disable_unprepare in clock init/deinit
- Change .compatible to soc-specific compatible names
aspeed,aspeed4000-jtag/aspeed5000-jtag
- Added dt-bindings
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Reorder functions and removed the forward declarations
- Add static const qualifier to state machine states transitions
- Change .compatible to soc-specific compatible names
aspeed,aspeed4000-jtag/aspeed5000-jtag
- Add dt-bindings
Comments pointed by Randy Dunlap <rdunlap@infradead.org>
- Change module name jtag-aspeed in description in Kconfig
Comments pointed by kbuild test robot <lkp@intel.com>
- Remove invalid include <asm/mach-types.h>
- add resource_size instead of calculation
---
drivers/jtag/Kconfig | 14 +
drivers/jtag/Makefile | 1 +
drivers/jtag/jtag-aspeed.c | 774 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 789 insertions(+), 0 deletions(-)
create mode 100644 drivers/jtag/jtag-aspeed.c
diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
index 0fad1a3..63ddf1f 100644
--- a/drivers/jtag/Kconfig
+++ b/drivers/jtag/Kconfig
@@ -14,3 +14,17 @@ menuconfig JTAG
To compile this driver as a module, choose M here: the module will
be called jtag.
+
+menuconfig JTAG_ASPEED
+ tristate "Aspeed SoC JTAG controller support"
+ depends on JTAG && HAS_IOMEM
+ depends on ARCH_ASPEED || COMPILE_TEST
+ ---help---
+ This provides a support for Aspeed JTAG device, equipped on
+ Aspeed SoC 24xx and 25xx families. Drivers allows programming
+ of hardware devices, connected to SoC through the JTAG interface.
+
+ If you want this support, you should say Y here.
+
+ To compile this driver as a module, choose M here: the module will
+ be called jtag-aspeed.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
index af37493..04a855e 100644
--- a/drivers/jtag/Makefile
+++ b/drivers/jtag/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_JTAG) += jtag.o
+obj-$(CONFIG_JTAG_ASPEED) += jtag-aspeed.o
diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
new file mode 100644
index 0000000..db496a4
--- /dev/null
+++ b/drivers/jtag/jtag-aspeed.c
@@ -0,0 +1,774 @@
+// SPDX-License-Identifier: GPL-2.0
+// drivers/jtag/aspeed-jtag.c
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <uapi/linux/jtag.h>
+
+#define ASPEED_JTAG_DATA 0x00
+#define ASPEED_JTAG_INST 0x04
+#define ASPEED_JTAG_CTRL 0x08
+#define ASPEED_JTAG_ISR 0x0C
+#define ASPEED_JTAG_SW 0x10
+#define ASPEED_JTAG_TCK 0x14
+#define ASPEED_JTAG_EC 0x18
+
+#define ASPEED_JTAG_DATA_MSB 0x01
+#define ASPEED_JTAG_DATA_CHUNK_SIZE 0x20
+
+/* ASPEED_JTAG_CTRL: Engine Control */
+#define ASPEED_JTAG_CTL_ENG_EN BIT(31)
+#define ASPEED_JTAG_CTL_ENG_OUT_EN BIT(30)
+#define ASPEED_JTAG_CTL_FORCE_TMS BIT(29)
+#define ASPEED_JTAG_CTL_INST_LEN(x) ((x) << 20)
+#define ASPEED_JTAG_CTL_LASPEED_INST BIT(17)
+#define ASPEED_JTAG_CTL_INST_EN BIT(16)
+#define ASPEED_JTAG_CTL_DR_UPDATE BIT(10)
+#define ASPEED_JTAG_CTL_DATA_LEN(x) ((x) << 4)
+#define ASPEED_JTAG_CTL_LASPEED_DATA BIT(1)
+#define ASPEED_JTAG_CTL_DATA_EN BIT(0)
+
+/* ASPEED_JTAG_ISR : Interrupt status and enable */
+#define ASPEED_JTAG_ISR_INST_PAUSE BIT(19)
+#define ASPEED_JTAG_ISR_INST_COMPLETE BIT(18)
+#define ASPEED_JTAG_ISR_DATA_PAUSE BIT(17)
+#define ASPEED_JTAG_ISR_DATA_COMPLETE BIT(16)
+#define ASPEED_JTAG_ISR_INST_PAUSE_EN BIT(3)
+#define ASPEED_JTAG_ISR_INST_COMPLETE_EN BIT(2)
+#define ASPEED_JTAG_ISR_DATA_PAUSE_EN BIT(1)
+#define ASPEED_JTAG_ISR_DATA_COMPLETE_EN BIT(0)
+#define ASPEED_JTAG_ISR_INT_EN_MASK GENMASK(3, 0)
+#define ASPEED_JTAG_ISR_INT_MASK GENMASK(19, 16)
+
+/* ASPEED_JTAG_SW : Software Mode and Status */
+#define ASPEED_JTAG_SW_MODE_EN BIT(19)
+#define ASPEED_JTAG_SW_MODE_TCK BIT(18)
+#define ASPEED_JTAG_SW_MODE_TMS BIT(17)
+#define ASPEED_JTAG_SW_MODE_TDIO BIT(16)
+
+/* ASPEED_JTAG_TCK : TCK Control */
+#define ASPEED_JTAG_TCK_DIVISOR_MASK GENMASK(10, 0)
+#define ASPEED_JTAG_TCK_GET_DIV(x) ((x) & ASPEED_JTAG_TCK_DIVISOR_MASK)
+
+/* ASPEED_JTAG_EC : Controller set for go to IDLE */
+#define ASPEED_JTAG_EC_GO_IDLE BIT(0)
+
+#define ASPEED_JTAG_IOUT_LEN(len) (ASPEED_JTAG_CTL_ENG_EN |\
+ ASPEED_JTAG_CTL_ENG_OUT_EN |\
+ ASPEED_JTAG_CTL_INST_LEN(len))
+
+#define ASPEED_JTAG_DOUT_LEN(len) (ASPEED_JTAG_CTL_ENG_EN |\
+ ASPEED_JTAG_CTL_ENG_OUT_EN |\
+ ASPEED_JTAG_CTL_DATA_LEN(len))
+
+#define ASPEED_JTAG_TCK_WAIT 10
+#define ASPEED_JTAG_RESET_CNTR 10
+
+#define ASPEED_JTAG_NAME "jtag-aspeed"
+
+struct aspeed_jtag {
+ void __iomem *reg_base;
+ struct device *dev;
+ struct clk *pclk;
+ enum jtag_endstate status;
+ int irq;
+ u32 flag;
+ wait_queue_head_t jtag_wq;
+ u32 mode;
+};
+
+static char *end_status_str[] = {"idle", "ir pause", "drpause"};
+
+static u32 aspeed_jtag_read(struct aspeed_jtag *aspeed_jtag, u32 reg)
+{
+ return readl(aspeed_jtag->reg_base + reg);
+}
+
+static void
+aspeed_jtag_write(struct aspeed_jtag *aspeed_jtag, u32 val, u32 reg)
+{
+ writel(val, aspeed_jtag->reg_base + reg);
+}
+
+static int aspeed_jtag_freq_set(struct jtag *jtag, u32 freq)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ unsigned long apb_frq;
+ u32 tck_val;
+ u16 div;
+
+ apb_frq = clk_get_rate(aspeed_jtag->pclk);
+ div = (apb_frq % freq == 0) ? (apb_frq / freq) - 1 : (apb_frq / freq);
+ tck_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
+ aspeed_jtag_write(aspeed_jtag,
+ (tck_val & ASPEED_JTAG_TCK_DIVISOR_MASK) | div,
+ ASPEED_JTAG_TCK);
+ return 0;
+}
+
+static int aspeed_jtag_freq_get(struct jtag *jtag, u32 *frq)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ u32 pclk;
+ u32 tck;
+
+ pclk = clk_get_rate(aspeed_jtag->pclk);
+ tck = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
+ *frq = pclk / (ASPEED_JTAG_TCK_GET_DIV(tck) + 1);
+
+ return 0;
+}
+
+static int aspeed_jtag_mode_set(struct jtag *jtag, u32 mode)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+ aspeed_jtag->mode = mode;
+ return 0;
+}
+
+static void aspeed_jtag_sw_delay(struct aspeed_jtag *aspeed_jtag, int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW);
+}
+
+static char aspeed_jtag_tck_cycle(struct aspeed_jtag *aspeed_jtag,
+ u8 tms, u8 tdi)
+{
+ char tdo = 0;
+
+ /* TCK = 0 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+
+ aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT);
+
+ /* TCK = 1 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TCK |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+
+ if (aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW) &
+ ASPEED_JTAG_SW_MODE_TDIO)
+ tdo = 1;
+
+ aspeed_jtag_sw_delay(aspeed_jtag, ASPEED_JTAG_TCK_WAIT);
+
+ /* TCK = 0 */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ (tms * ASPEED_JTAG_SW_MODE_TMS) |
+ (tdi * ASPEED_JTAG_SW_MODE_TDIO), ASPEED_JTAG_SW);
+ return tdo;
+}
+
+static void aspeed_jtag_wait_instruction_pause(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_INST_PAUSE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE;
+}
+
+static void
+aspeed_jtag_wait_instruction_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_INST_COMPLETE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_COMPLETE;
+}
+
+static void
+aspeed_jtag_wait_data_pause_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_DATA_PAUSE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_PAUSE;
+}
+
+static void aspeed_jtag_wait_data_complete(struct aspeed_jtag *aspeed_jtag)
+{
+ wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
+ ASPEED_JTAG_ISR_DATA_COMPLETE);
+ aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_DATA_COMPLETE;
+}
+
+static void aspeed_jtag_sm_cycle(struct aspeed_jtag *aspeed_jtag, const u8 *tms,
+ int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, tms[i], 0);
+}
+
+static void aspeed_jtag_run_test_idle_sw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_run_test_idle *runtest)
+{
+ static const u8 sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0};
+ static const u8 sm_pause_drpause[] = {1, 1, 1, 0, 1, 0};
+ static const u8 sm_idle_irpause[] = {1, 1, 0, 1, 0};
+ static const u8 sm_idle_drpause[] = {1, 0, 1, 0};
+ static const u8 sm_pause_idle[] = {1, 1, 0};
+ int i;
+
+ /* SW mode from idle/pause-> to pause/idle */
+ if (runtest->reset) {
+ for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0);
+ }
+
+ switch (aspeed_jtag->status) {
+ case JTAG_STATE_IDLE:
+ switch (runtest->endstate) {
+ case JTAG_STATE_PAUSEIR:
+ /* ->DRSCan->IRSCan->IRCap->IRExit1->PauseIR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_irpause,
+ sizeof(sm_idle_irpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEIR;
+ break;
+ case JTAG_STATE_PAUSEDR:
+ /* ->DRSCan->DRCap->DRExit1->PauseDR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_idle_drpause,
+ sizeof(sm_idle_drpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEDR;
+ break;
+ case JTAG_STATE_IDLE:
+ /* IDLE */
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case JTAG_STATE_PAUSEIR:
+ /* Fall-through */
+ case JTAG_STATE_PAUSEDR:
+ /* From IR/DR Pause */
+ switch (runtest->endstate) {
+ case JTAG_STATE_PAUSEIR:
+ /*
+ * to Exit2 IR/DR->Updt IR/DR->DRSCan->IRSCan->IRCap->
+ * IRExit1->PauseIR
+ */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_irpause,
+ sizeof(sm_pause_irpause));
+
+ aspeed_jtag->status = JTAG_STATE_PAUSEIR;
+ break;
+ case JTAG_STATE_PAUSEDR:
+ /*
+ * to Exit2 IR/DR->Updt IR/DR->DRSCan->DRCap->
+ * DRExit1->PauseDR
+ */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_drpause,
+ sizeof(sm_pause_drpause));
+ aspeed_jtag->status = JTAG_STATE_PAUSEDR;
+ break;
+ case JTAG_STATE_IDLE:
+ /* to Exit2 IR/DR->Updt IR/DR->IDLE */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle,
+ sizeof(sm_pause_idle));
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ dev_err(aspeed_jtag->dev, "aspeed_jtag_run_test_idle error\n");
+ break;
+ }
+
+ /* Stay on IDLE for at least TCK cycle */
+ for (i = 0; i < runtest->tck; i++)
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+}
+
+/**
+ * aspeed_jtag_run_test_idle:
+ * JTAG reset: generates at least 9 TMS high and 1 TMS low to force
+ * devices into Run-Test/Idle State.
+ */
+static int aspeed_jtag_idle(struct jtag *jtag,
+ struct jtag_run_test_idle *runtest)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+ dev_dbg(aspeed_jtag->dev, "runtest, status:%d, mode:%s, state:%s, reset:%d, tck:%d\n",
+ aspeed_jtag->status,
+ aspeed_jtag->mode & JTAG_XFER_HW_MODE ? "HW" : "SW",
+ end_status_str[runtest->endstate], runtest->reset,
+ runtest->tck);
+
+ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
+ aspeed_jtag_run_test_idle_sw(aspeed_jtag, runtest);
+ return 0;
+ }
+
+ /* Disable sw mode */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
+ /* x TMS high + 1 TMS low */
+ if (runtest->reset)
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
+ ASPEED_JTAG_CTL_ENG_OUT_EN |
+ ASPEED_JTAG_CTL_FORCE_TMS, ASPEED_JTAG_CTRL);
+ else
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_EC_GO_IDLE,
+ ASPEED_JTAG_EC);
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ aspeed_jtag->status = JTAG_STATE_IDLE;
+ return 0;
+}
+
+static void aspeed_jtag_xfer_sw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_xfer *xfer, unsigned long *data)
+{
+ unsigned long remain_xfer = xfer->length;
+ unsigned long shift_bits = 0;
+ unsigned long index = 0;
+ unsigned long tdi;
+ char tdo;
+
+ if (xfer->direction == JTAG_READ_XFER)
+ tdi = UINT_MAX;
+ else
+ tdi = data[index];
+
+ while (remain_xfer > 1) {
+ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 0,
+ tdi & ASPEED_JTAG_DATA_MSB);
+ data[index] |= tdo << (shift_bits %
+ ASPEED_JTAG_DATA_CHUNK_SIZE);
+
+ tdi >>= 1;
+ shift_bits++;
+ remain_xfer--;
+
+ if (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE == 0) {
+ dev_dbg(aspeed_jtag->dev, "R/W data[%lu]:%lx\n",
+ index, data[index]);
+
+ tdo = 0;
+ index++;
+
+ if (xfer->direction == JTAG_READ_XFER)
+ tdi = UINT_MAX;
+ else
+ tdi = data[index];
+ }
+ }
+
+ tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1, tdi & ASPEED_JTAG_DATA_MSB);
+ data[index] |= tdo << (shift_bits % ASPEED_JTAG_DATA_CHUNK_SIZE);
+}
+
+static void aspeed_jtag_xfer_push_data(struct aspeed_jtag *aspeed_jtag,
+ enum jtag_xfer_type type, u32 bits_len)
+{
+ dev_dbg(aspeed_jtag->dev, "shift bits %d\n", bits_len);
+
+ if (type == JTAG_SIR_XFER) {
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_IOUT_LEN(bits_len),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
+ ASPEED_JTAG_CTL_INST_EN, ASPEED_JTAG_CTRL);
+ } else {
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_DOUT_LEN(bits_len) |
+ ASPEED_JTAG_CTL_DATA_EN, ASPEED_JTAG_CTRL);
+ }
+}
+
+static void aspeed_jtag_xfer_push_data_last(struct aspeed_jtag *aspeed_jtag,
+ enum jtag_xfer_type type,
+ u32 shift_bits,
+ enum jtag_endstate endstate)
+{
+ if (endstate != JTAG_STATE_IDLE) {
+ if (type == JTAG_SIR_XFER) {
+ dev_dbg(aspeed_jtag->dev, "IR Keep Pause\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits),
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_INST_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_instruction_pause(aspeed_jtag);
+ } else {
+ dev_dbg(aspeed_jtag->dev, "DR Keep Pause\n");
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_DR_UPDATE,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_DR_UPDATE |
+ ASPEED_JTAG_CTL_DATA_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_data_pause_complete(aspeed_jtag);
+ }
+ } else {
+ if (type == JTAG_SIR_XFER) {
+ dev_dbg(aspeed_jtag->dev, "IR go IDLE\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_INST,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_IOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_INST |
+ ASPEED_JTAG_CTL_INST_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_instruction_complete(aspeed_jtag);
+ } else {
+ dev_dbg(aspeed_jtag->dev, "DR go IDLE\n");
+
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_DATA,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag,
+ ASPEED_JTAG_DOUT_LEN(shift_bits) |
+ ASPEED_JTAG_CTL_LASPEED_DATA |
+ ASPEED_JTAG_CTL_DATA_EN,
+ ASPEED_JTAG_CTRL);
+ aspeed_jtag_wait_data_complete(aspeed_jtag);
+ }
+ }
+}
+
+static void aspeed_jtag_xfer_hw(struct aspeed_jtag *aspeed_jtag,
+ struct jtag_xfer *xfer, unsigned long *data)
+{
+ unsigned long remain_xfer = xfer->length;
+ unsigned long index = 0;
+ char shift_bits;
+ u32 data_reg;
+
+ data_reg = xfer->type == JTAG_SIR_XFER ?
+ ASPEED_JTAG_INST : ASPEED_JTAG_DATA;
+ while (remain_xfer) {
+ if (xfer->direction == JTAG_WRITE_XFER) {
+ dev_dbg(aspeed_jtag->dev, "W dr->dr_data[%lu]:%lx\n",
+ index, data[index]);
+
+ aspeed_jtag_write(aspeed_jtag, data[index], data_reg);
+ } else {
+ aspeed_jtag_write(aspeed_jtag, 0, data_reg);
+ }
+
+ if (remain_xfer > ASPEED_JTAG_DATA_CHUNK_SIZE) {
+ shift_bits = ASPEED_JTAG_DATA_CHUNK_SIZE;
+
+ /*
+ * Read bytes were not equals to column length
+ * and go to Pause-DR
+ */
+ aspeed_jtag_xfer_push_data(aspeed_jtag, xfer->type,
+ shift_bits);
+ } else {
+ /*
+ * Read bytes equals to column length =>
+ * Update-DR
+ */
+ shift_bits = remain_xfer;
+ aspeed_jtag_xfer_push_data_last(aspeed_jtag, xfer->type,
+ shift_bits,
+ xfer->endstate);
+ }
+
+ if (xfer->direction == JTAG_READ_XFER) {
+ if (shift_bits < ASPEED_JTAG_DATA_CHUNK_SIZE) {
+ data[index] = aspeed_jtag_read(aspeed_jtag,
+ data_reg);
+
+ data[index] >>= ASPEED_JTAG_DATA_CHUNK_SIZE -
+ shift_bits;
+ } else {
+ data[index] = aspeed_jtag_read(aspeed_jtag,
+ data_reg);
+ }
+ dev_dbg(aspeed_jtag->dev, "R dr->dr_data[%lu]:%lx\n",
+ index, data[index]);
+ }
+
+ remain_xfer = remain_xfer - shift_bits;
+ index++;
+ dev_dbg(aspeed_jtag->dev, "remain_xfer %lu\n", remain_xfer);
+ }
+}
+
+static int aspeed_jtag_xfer(struct jtag *jtag, struct jtag_xfer *xfer,
+ u8 *xfer_data)
+{
+ static const u8 sm_update_shiftir[] = {1, 1, 0, 0};
+ static const u8 sm_update_shiftdr[] = {1, 0, 0};
+ static const u8 sm_pause_idle[] = {1, 1, 0};
+ static const u8 sm_pause_update[] = {1, 1};
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+ unsigned long *data = (unsigned long *)xfer_data;
+ unsigned long remain_xfer = xfer->length;
+ char dbg_str[256];
+ int pos = 0;
+ int i;
+
+ for (i = 0; i <= xfer->length / BITS_PER_BYTE; i++) {
+ pos += snprintf(&dbg_str[pos], sizeof(dbg_str) - pos,
+ "0x%02x ", xfer_data[i]);
+ }
+
+ dev_dbg(aspeed_jtag->dev, " %s %s xfer, mode:%s, END:%d, len:%lu, TDI[%s]\n",
+ xfer->type == JTAG_SIR_XFER ? "SIR" : "SDR",
+ xfer->direction == JTAG_READ_XFER ? "READ" : "WRITE",
+ aspeed_jtag->mode & JTAG_XFER_HW_MODE ? "HW" : "SW",
+ xfer->endstate, remain_xfer, dbg_str);
+
+ if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
+ /* SW mode */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ if (aspeed_jtag->status != JTAG_STATE_IDLE) {
+ /*IR/DR Pause->Exit2 IR / DR->Update IR /DR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_update,
+ sizeof(sm_pause_update));
+ }
+
+ if (xfer->type == JTAG_SIR_XFER)
+ /* ->IRSCan->CapIR->ShiftIR */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftir,
+ sizeof(sm_update_shiftir));
+ else
+ /* ->DRScan->DRCap->DRShift */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_update_shiftdr,
+ sizeof(sm_update_shiftdr));
+
+ aspeed_jtag_xfer_sw(aspeed_jtag, xfer, data);
+
+ /* DIPause/DRPause */
+ aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0);
+
+ if (xfer->endstate == JTAG_STATE_IDLE) {
+ /* ->DRExit2->DRUpdate->IDLE */
+ aspeed_jtag_sm_cycle(aspeed_jtag, sm_pause_idle,
+ sizeof(sm_pause_idle));
+ }
+ } else {
+ /* hw mode */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
+ aspeed_jtag_xfer_hw(aspeed_jtag, xfer, data);
+ }
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+ aspeed_jtag->status = xfer->endstate;
+ return 0;
+}
+
+static int aspeed_jtag_status_get(struct jtag *jtag, u32 *status)
+{
+ struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
+
+ *status = aspeed_jtag->status;
+ return 0;
+}
+
+static irqreturn_t aspeed_jtag_interrupt(s32 this_irq, void *dev_id)
+{
+ struct aspeed_jtag *aspeed_jtag = dev_id;
+ irqreturn_t ret;
+ u32 status;
+
+ status = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_ISR);
+ dev_dbg(aspeed_jtag->dev, "status %x\n", status);
+
+ if (status & ASPEED_JTAG_ISR_INT_MASK) {
+ aspeed_jtag_write(aspeed_jtag,
+ (status & ASPEED_JTAG_ISR_INT_MASK)
+ | (status & ASPEED_JTAG_ISR_INT_EN_MASK),
+ ASPEED_JTAG_ISR);
+ aspeed_jtag->flag |= status & ASPEED_JTAG_ISR_INT_MASK;
+ }
+
+ if (aspeed_jtag->flag) {
+ wake_up_interruptible(&aspeed_jtag->jtag_wq);
+ ret = IRQ_HANDLED;
+ } else {
+ dev_err(aspeed_jtag->dev, "irq status:%x\n",
+ status);
+ ret = IRQ_NONE;
+ }
+ return ret;
+}
+
+int aspeed_jtag_init(struct platform_device *pdev,
+ struct aspeed_jtag *aspeed_jtag)
+{
+ struct resource *res;
+ int err;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ aspeed_jtag->reg_base = devm_ioremap_resource(aspeed_jtag->dev, res);
+ if (IS_ERR(aspeed_jtag->reg_base))
+ return -ENOMEM;
+
+ aspeed_jtag->pclk = devm_clk_get(aspeed_jtag->dev, NULL);
+ if (IS_ERR(aspeed_jtag->pclk)) {
+ dev_err(aspeed_jtag->dev, "devm_clk_get failed\n");
+ return PTR_ERR(aspeed_jtag->pclk);
+ }
+
+ aspeed_jtag->irq = platform_get_irq(pdev, 0);
+ if (aspeed_jtag->irq < 0) {
+ dev_err(aspeed_jtag->dev, "no irq specified\n");
+ return -ENOENT;
+ }
+
+ clk_prepare_enable(aspeed_jtag->pclk);
+
+ /* Enable clock */
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
+ ASPEED_JTAG_CTL_ENG_OUT_EN, ASPEED_JTAG_CTRL);
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
+ ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
+
+ err = devm_request_irq(aspeed_jtag->dev, aspeed_jtag->irq,
+ aspeed_jtag_interrupt, 0,
+ "aspeed-jtag", aspeed_jtag);
+ if (err) {
+ dev_err(aspeed_jtag->dev, "unable to get IRQ");
+ goto clk_unprep;
+ }
+ dev_dbg(&pdev->dev, "IRQ %d.\n", aspeed_jtag->irq);
+
+ aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_ISR_INST_PAUSE |
+ ASPEED_JTAG_ISR_INST_COMPLETE |
+ ASPEED_JTAG_ISR_DATA_PAUSE |
+ ASPEED_JTAG_ISR_DATA_COMPLETE |
+ ASPEED_JTAG_ISR_INST_PAUSE_EN |
+ ASPEED_JTAG_ISR_INST_COMPLETE_EN |
+ ASPEED_JTAG_ISR_DATA_PAUSE_EN |
+ ASPEED_JTAG_ISR_DATA_COMPLETE_EN,
+ ASPEED_JTAG_ISR);
+
+ aspeed_jtag->flag = 0;
+ aspeed_jtag->mode = 0;
+ init_waitqueue_head(&aspeed_jtag->jtag_wq);
+ return 0;
+
+clk_unprep:
+ clk_disable_unprepare(aspeed_jtag->pclk);
+ return err;
+}
+
+int aspeed_jtag_deinit(struct platform_device *pdev,
+ struct aspeed_jtag *aspeed_jtag)
+{
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_ISR);
+ /* Disable clock */
+ aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_CTRL);
+ clk_disable_unprepare(aspeed_jtag->pclk);
+ return 0;
+}
+
+static const struct jtag_ops aspeed_jtag_ops = {
+ .freq_get = aspeed_jtag_freq_get,
+ .freq_set = aspeed_jtag_freq_set,
+ .status_get = aspeed_jtag_status_get,
+ .idle = aspeed_jtag_idle,
+ .xfer = aspeed_jtag_xfer,
+ .mode_set = aspeed_jtag_mode_set
+};
+
+static int aspeed_jtag_probe(struct platform_device *pdev)
+{
+ struct aspeed_jtag *aspeed_jtag;
+ struct device *dev;
+ struct jtag *jtag;
+ int err;
+
+ dev = &pdev->dev;
+ jtag = jtag_alloc(sizeof(*aspeed_jtag), &aspeed_jtag_ops);
+ if (!jtag)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, jtag);
+ aspeed_jtag = jtag_priv(jtag);
+ aspeed_jtag->dev = &pdev->dev;
+
+ /* Initialize device*/
+ err = aspeed_jtag_init(pdev, aspeed_jtag);
+ if (err)
+ goto err_jtag_init;
+
+ /* Initialize JTAG core structure*/
+ err = jtag_register(jtag);
+ if (err)
+ goto err_jtag_register;
+
+ return 0;
+
+err_jtag_register:
+ aspeed_jtag_deinit(pdev, aspeed_jtag);
+err_jtag_init:
+ jtag_free(jtag);
+ return err;
+}
+
+static int aspeed_jtag_remove(struct platform_device *pdev)
+{
+ struct jtag *jtag;
+
+ jtag = platform_get_drvdata(pdev);
+ aspeed_jtag_deinit(pdev, jtag_priv(jtag));
+ jtag_unregister(jtag);
+ jtag_free(jtag);
+ return 0;
+}
+
+static const struct of_device_id aspeed_jtag_of_match[] = {
+ { .compatible = "aspeed,ast2400-jtag", },
+ { .compatible = "aspeed,ast2500-jtag", },
+ {}
+};
+
+static struct platform_driver aspeed_jtag_driver = {
+ .probe = aspeed_jtag_probe,
+ .remove = aspeed_jtag_remove,
+ .driver = {
+ .name = ASPEED_JTAG_NAME,
+ .of_match_table = aspeed_jtag_of_match,
+ },
+};
+module_platform_driver(aspeed_jtag_driver);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
+MODULE_DESCRIPTION("ASPEED JTAG driver");
+MODULE_LICENSE("GPL v2");
--
1.7.1
^ permalink raw reply related
* [patch v15 1/4] drivers: jtag: Add JTAG core driver
From: Oleksandr Shamray @ 2017-12-25 11:53 UTC (permalink / raw)
To: gregkh, arnd
Cc: linux-kernel, linux-arm-kernel, devicetree, openbmc, joel, jiri,
tklauser, linux-serial, vadimp, system-sw-low-level, robh+dt,
openocd-devel-owner, linux-api, davem, mchehab, Oleksandr Shamray,
Jiri Pirko
In-Reply-To: <1514202808-29747-1-git-send-email-oleksandrs@mellanox.com>
Initial patch for JTAG driver
JTAG class driver provide infrastructure to support hardware/software
JTAG platform drivers. It provide user layer API interface for flashing
and debugging external devices which equipped with JTAG interface
using standard transactions.
Driver exposes set of IOCTL to user space for:
- XFER:
- SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
- SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
number of clocks).
- SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
Driver core provides set of internal APIs for allocation and
registration:
- jtag_register;
- jtag_unregister;
- jtag_alloc;
- jtag_free;
Platform driver on registration with jtag-core creates the next
entry in dev folder:
/dev/jtagX
Signed-off-by: Oleksandr Shamray <oleksandrs@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Acked-by: Philippe Ombredanne <pombredanne@nexb.com>
---
v14->v15
v13->v14
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change style of head block comment from /**/ to //
v12->v13
Comments pointed by Philippe Ombredanne <pombredanne@nexb.com>
- Change jtag.c licence type to
SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
and reorder line with license in description
v11->v12
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- Change jtag.h licence type to
SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
and reorder line with license in description
Chip Bilbrey <chip@bilbrey.org>
- Remove Apeed reference from uapi jtag.h header
- Remove access mode from xfer and idle transactions
- Add new ioctl JTAG_SIOCMODE for set hw mode
- Add only one open per JTAG port blocking with mutex blocking
v10->v11
Notifications from kbuild test robot <lkp@intel.com>
- include types.h headeri to jtag.h
- fix incompatible type of xfer callback
- remove rdundant class defination
- Fix return order in case of xfer error
V9->v10
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- remove unnecessary alignment for pirv data
- move jtag_copy_to_user and jtag_copy_from_user code just to ioctl
- move int jtag_run_test_idle_op and jtag_xfer_op code
just to ioctl
- change return error codes to more applicable
- add missing error checks
- fix error check order in ioctl
- remove unnecessary blank lines
- add param validation to ioctl
- remove compat_ioctl
- remove only one open per JTAG port blocking.
User will care about this.
- Fix idr memory leak on jtag_exit
- change cdev device type to misc
V8->v9
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- use get_user() instead of __get_user().
- change jtag->open type from int to atomic_t
- remove spinlock on jtg_open
- remove mutex on jtag_register
- add unregister_chrdev_region on jtag_init err
- add unregister_chrdev_region on jtag_exit
- remove unnecessary pointer casts
- add *data parameter to xfer function prototype
v7->v8
Comments pointed by Moritz Fischer <moritz.fischer@ettus.com>
- Fix misspelling s/friver/driver
v6->v7
Notifications from kbuild test robot <lkp@intel.com>
- Remove include asm/types.h from jtag.h
- Add include <linux/types.h> to jtag.c
v5->v6
v4->v5
v3->v4
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- change transaction pointer tdio type to __u64
- change internal status type from enum to __u32
- reorder jtag_xfer members to avoid the implied padding
- add __packed attribute to jtag_xfer and jtag_run_test_idle
v2->v3
Notifications from kbuild test robot <lkp@intel.com>
- Change include path to <linux/types.h> in jtag.h
v1->v2
Comments pointed by Greg KH <gregkh@linuxfoundation.org>
- Change license type from GPLv2/BSD to GPLv2
- Change type of variables which crossed user/kernel to __type
- Remove "default n" from Kconfig
Comments pointed by Andrew Lunn <andrew@lunn.ch>
- Change list_add_tail in jtag_unregister to list_del
Comments pointed by Neil Armstrong <narmstrong@baylibre.com>
- Add SPDX-License-Identifier instead of license text
Comments pointed by Arnd Bergmann <arnd@arndb.de>
- Change __copy_to_user to memdup_user
- Change __put_user to put_user
- Change type of variables to __type for compatible 32 and 64-bit systems
- Add check for maximum xfer data size
- Change lookup data mechanism to get jtag data from inode
- Add .compat_ioctl to file ops
- Add mem alignment for jtag priv data
Comments pointed by Tobias Klauser <tklauser@distanz.ch>
- Change function names to avoid match with variable types
- Fix description for jtag_ru_test_idle in uapi jtag.h
- Fix misprints IDEL/IDLE, trough/through
---
Documentation/ioctl/ioctl-number.txt | 2 +
MAINTAINERS | 10 ++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/jtag/Kconfig | 16 ++
drivers/jtag/Makefile | 1 +
drivers/jtag/jtag.c | 288 ++++++++++++++++++++++++++++++++++
include/linux/jtag.h | 45 ++++++
include/uapi/linux/jtag.h | 104 ++++++++++++
9 files changed, 469 insertions(+), 0 deletions(-)
create mode 100644 drivers/jtag/Kconfig
create mode 100644 drivers/jtag/Makefile
create mode 100644 drivers/jtag/jtag.c
create mode 100644 include/linux/jtag.h
create mode 100644 include/uapi/linux/jtag.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 3e3fdae..1af2508 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -321,6 +321,8 @@ Code Seq#(hex) Include File Comments
0xB0 all RATIO devices in development:
<mailto:vgo@ratio.de>
0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
+0xB2 00-0f linux/jtag.h JTAG driver
+ <mailto:oleksandrs@mellanox.com>
0xB3 00 linux/mmc/ioctl.h
0xB4 00-0F linux/gpio.h <mailto:linux-gpio@vger.kernel.org>
0xB5 00-0F uapi/linux/rpmsg.h <mailto:linux-remoteproc@vger.kernel.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 205d397..dfcf49c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7292,6 +7292,16 @@ L: linux-serial@vger.kernel.org
S: Maintained
F: drivers/tty/serial/jsm/
+JTAG SUBSYSTEM
+M: Oleksandr Shamray <oleksandrs@mellanox.com>
+M: Vadim Pasternak <vadimp@mellanox.com>
+S: Maintained
+F: include/linux/jtag.h
+F: include/uapi/linux/jtag.h
+F: drivers/jtag/
+F: Documentation/devicetree/bindings/jtag/
+F: Documentation/ABI/testing/jtag-cdev
+
K10TEMP HARDWARE MONITORING DRIVER
M: Clemens Ladisch <clemens@ladisch.de>
L: linux-hwmon@vger.kernel.org
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 505c676..2214678 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -208,4 +208,6 @@ source "drivers/tee/Kconfig"
source "drivers/mux/Kconfig"
+source "drivers/jtag/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index dfdcda0..6a2059b 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -182,3 +182,4 @@ obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/
obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MULTIPLEXER) += mux/
+obj-$(CONFIG_JTAG) += jtag/
diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
new file mode 100644
index 0000000..0fad1a3
--- /dev/null
+++ b/drivers/jtag/Kconfig
@@ -0,0 +1,16 @@
+menuconfig JTAG
+ tristate "JTAG support"
+ ---help---
+ This provides basic core functionality support for jtag class devices
+ Hardware equipped with JTAG microcontroller which can be built
+ on top of this drivers. Driver exposes the set of IOCTL to the
+ user space for:
+ SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
+ SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
+ RUNTEST (Forces IEEE 1149.1 bus to a run state for specified
+ number of clocks).
+
+ If you want this support, you should say Y here.
+
+ To compile this driver as a module, choose M here: the module will
+ be called jtag.
diff --git a/drivers/jtag/Makefile b/drivers/jtag/Makefile
new file mode 100644
index 0000000..af37493
--- /dev/null
+++ b/drivers/jtag/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_JTAG) += jtag.o
diff --git a/drivers/jtag/jtag.c b/drivers/jtag/jtag.c
new file mode 100644
index 0000000..39cbce9
--- /dev/null
+++ b/drivers/jtag/jtag.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+// drivers/jtag/jtag.c
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/jtag.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <uapi/linux/jtag.h>
+
+#define JTAG_NAME "jtag0"
+#define MAX_JTAG_NAME_LEN (sizeof("jtag") + 5)
+
+struct jtag {
+ struct miscdevice miscdev;
+ struct device *dev;
+ const struct jtag_ops *ops;
+ int id;
+ bool opened;
+ struct mutex open_lock;
+ unsigned long priv[0];
+};
+
+static DEFINE_IDA(jtag_ida);
+
+void *jtag_priv(struct jtag *jtag)
+{
+ return jtag->priv;
+}
+EXPORT_SYMBOL_GPL(jtag_priv);
+
+static long jtag_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct jtag *jtag = file->private_data;
+ struct jtag_run_test_idle idle;
+ struct jtag_xfer xfer;
+ u8 *xfer_data;
+ u32 data_size;
+ u32 value;
+ int err;
+
+ if (!arg)
+ return -EINVAL;
+
+ switch (cmd) {
+ case JTAG_GIOCFREQ:
+
+ if (jtag->ops->freq_get)
+ err = jtag->ops->freq_get(jtag, &value);
+ else
+ err = -EOPNOTSUPP;
+ if (err)
+ break;
+
+ if (put_user(value, (__u32 *)arg))
+ err = -EFAULT;
+ break;
+
+ case JTAG_SIOCFREQ:
+ if (get_user(value, (__u32 *)arg))
+ return -EFAULT;
+ if (value == 0)
+ return -EINVAL;
+
+ if (jtag->ops->freq_set)
+ err = jtag->ops->freq_set(jtag, value);
+ else
+ err = -EOPNOTSUPP;
+ break;
+
+ case JTAG_IOCRUNTEST:
+ if (copy_from_user(&idle, (void *)arg,
+ sizeof(struct jtag_run_test_idle)))
+ return -EFAULT;
+
+ if (idle.endstate > JTAG_STATE_PAUSEDR)
+ return -EINVAL;
+
+ if (jtag->ops->idle)
+ err = jtag->ops->idle(jtag, &idle);
+ else
+ err = -EOPNOTSUPP;
+ break;
+
+ case JTAG_IOCXFER:
+ if (copy_from_user(&xfer, (void *)arg,
+ sizeof(struct jtag_xfer)))
+ return -EFAULT;
+
+ if (xfer.length >= JTAG_MAX_XFER_DATA_LEN)
+ return -EINVAL;
+
+ if (xfer.type > JTAG_SDR_XFER)
+ return -EINVAL;
+
+ if (xfer.direction > JTAG_WRITE_XFER)
+ return -EINVAL;
+
+ if (xfer.endstate > JTAG_STATE_PAUSEDR)
+ return -EINVAL;
+
+ data_size = DIV_ROUND_UP(xfer.length, BITS_PER_BYTE);
+ xfer_data = memdup_user(u64_to_user_ptr(xfer.tdio), data_size);
+
+ if (!xfer_data)
+ return -EFAULT;
+
+ if (jtag->ops->xfer) {
+ err = jtag->ops->xfer(jtag, &xfer, xfer_data);
+ } else {
+ kfree(xfer_data);
+ return -EOPNOTSUPP;
+ }
+
+ if (err) {
+ kfree(xfer_data);
+ return -EFAULT;
+ }
+
+ err = copy_to_user(u64_to_user_ptr(xfer.tdio),
+ (void *)(xfer_data), data_size);
+
+ if (err) {
+ kfree(xfer_data);
+ return -EFAULT;
+ }
+
+ kfree(xfer_data);
+ if (copy_to_user((void *)arg, &xfer, sizeof(struct jtag_xfer)))
+ return -EFAULT;
+ break;
+
+ case JTAG_GIOCSTATUS:
+ if (jtag->ops->status_get)
+ err = jtag->ops->status_get(jtag, &value);
+ else
+ err = -EOPNOTSUPP;
+ if (err)
+ break;
+
+ err = put_user(value, (__u32 *)arg);
+ if (err)
+ err = -EFAULT;
+ break;
+ case JTAG_SIOCMODE:
+ if (get_user(value, (__u32 *)arg))
+ return -EFAULT;
+ if (value == 0)
+ return -EINVAL;
+
+ if (jtag->ops->mode_set)
+ err = jtag->ops->mode_set(jtag, value);
+ else
+ err = -EOPNOTSUPP;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return err;
+}
+
+static int jtag_open(struct inode *inode, struct file *file)
+{
+ struct jtag *jtag = container_of(file->private_data, struct jtag,
+ miscdev);
+
+ if (mutex_lock_interruptible(&jtag->open_lock))
+ return -ERESTARTSYS;
+
+ if (jtag->opened) {
+ mutex_unlock(&jtag->open_lock);
+ return -EINVAL;
+ }
+
+ nonseekable_open(inode, file);
+ file->private_data = jtag;
+ jtag->opened = true;
+ mutex_unlock(&jtag->open_lock);
+ return 0;
+}
+
+static int jtag_release(struct inode *inode, struct file *file)
+{
+ struct jtag *jtag = file->private_data;
+
+ mutex_lock(&jtag->open_lock);
+ jtag->opened = false;
+ mutex_unlock(&jtag->open_lock);
+ return 0;
+}
+
+static const struct file_operations jtag_fops = {
+ .owner = THIS_MODULE,
+ .open = jtag_open,
+ .release = jtag_release,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = jtag_ioctl,
+};
+
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops)
+{
+ struct jtag *jtag;
+
+ jtag = kzalloc(sizeof(*jtag) + round_up(priv_size, ARCH_DMA_MINALIGN),
+ GFP_KERNEL);
+ if (!jtag)
+ return NULL;
+
+ jtag->ops = ops;
+ return jtag;
+}
+EXPORT_SYMBOL_GPL(jtag_alloc);
+
+void jtag_free(struct jtag *jtag)
+{
+ kfree(jtag);
+}
+EXPORT_SYMBOL_GPL(jtag_free);
+
+int jtag_register(struct jtag *jtag)
+{
+ char *name;
+ int err;
+ int id;
+
+ id = ida_simple_get(&jtag_ida, 0, 0, GFP_KERNEL);
+ if (id < 0)
+ return id;
+
+ jtag->id = id;
+
+ name = kzalloc(MAX_JTAG_NAME_LEN, GFP_KERNEL);
+ if (!name) {
+ err = -ENOMEM;
+ goto err_jtag_alloc;
+ }
+
+ err = snprintf(name, MAX_JTAG_NAME_LEN, "jtag%d", id);
+ if (err < 0)
+ goto err_jtag_name;
+
+ mutex_init(&jtag->open_lock);
+ jtag->miscdev.fops = &jtag_fops;
+ jtag->miscdev.minor = MISC_DYNAMIC_MINOR;
+ jtag->miscdev.name = name;
+
+ err = misc_register(&jtag->miscdev);
+ if (err)
+ dev_err(jtag->dev, "Unable to register device\n");
+ else
+ return 0;
+ jtag->opened = false;
+
+err_jtag_name:
+ kfree(name);
+err_jtag_alloc:
+ ida_simple_remove(&jtag_ida, id);
+ return err;
+}
+EXPORT_SYMBOL_GPL(jtag_register);
+
+void jtag_unregister(struct jtag *jtag)
+{
+ misc_deregister(&jtag->miscdev);
+ kfree(jtag->miscdev.name);
+ ida_simple_remove(&jtag_ida, jtag->id);
+}
+EXPORT_SYMBOL_GPL(jtag_unregister);
+
+static void __exit jtag_exit(void)
+{
+ ida_destroy(&jtag_ida);
+}
+
+module_exit(jtag_exit);
+
+MODULE_AUTHOR("Oleksandr Shamray <oleksandrs@mellanox.com>");
+MODULE_DESCRIPTION("Generic jtag support");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/jtag.h b/include/linux/jtag.h
new file mode 100644
index 0000000..312c641
--- /dev/null
+++ b/include/linux/jtag.h
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+// include/linux/jtag.h - JTAG class driver
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#ifndef __JTAG_H
+#define __JTAG_H
+
+#include <uapi/linux/jtag.h>
+
+#ifndef ARCH_DMA_MINALIGN
+#define ARCH_DMA_MINALIGN 1
+#endif
+
+#define jtag_u64_to_ptr(arg) ((void *)(uintptr_t)arg)
+
+#define JTAG_MAX_XFER_DATA_LEN 65535
+
+struct jtag;
+/**
+ * struct jtag_ops - callbacks for jtag control functions:
+ *
+ * @freq_get: get frequency function. Filled by device driver
+ * @freq_set: set frequency function. Filled by device driver
+ * @status_get: set status function. Filled by device driver
+ * @idle: set JTAG to idle state function. Filled by device driver
+ * @xfer: send JTAG xfer function. Filled by device driver
+ */
+struct jtag_ops {
+ int (*freq_get)(struct jtag *jtag, u32 *freq);
+ int (*freq_set)(struct jtag *jtag, u32 freq);
+ int (*status_get)(struct jtag *jtag, u32 *state);
+ int (*idle)(struct jtag *jtag, struct jtag_run_test_idle *idle);
+ int (*xfer)(struct jtag *jtag, struct jtag_xfer *xfer, u8 *xfer_data);
+ int (*mode_set)(struct jtag *jtag, u32 mode_mask);
+};
+
+void *jtag_priv(struct jtag *jtag);
+int jtag_register(struct jtag *jtag);
+void jtag_unregister(struct jtag *jtag);
+struct jtag *jtag_alloc(size_t priv_size, const struct jtag_ops *ops);
+void jtag_free(struct jtag *jtag);
+
+#endif /* __JTAG_H */
diff --git a/include/uapi/linux/jtag.h b/include/uapi/linux/jtag.h
new file mode 100644
index 0000000..cda2520
--- /dev/null
+++ b/include/uapi/linux/jtag.h
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+// include/uapi/linux/jtag.h - JTAG class driver uapi
+//
+// Copyright (c) 2017 Mellanox Technologies. All rights reserved.
+// Copyright (c) 2017 Oleksandr Shamray <oleksandrs@mellanox.com>
+
+#ifndef __UAPI_LINUX_JTAG_H
+#define __UAPI_LINUX_JTAG_H
+
+#include <linux/types.h>
+/*
+ * JTAG_XFER_HW_MODE: JTAG hardware mode. Used to set HW drived or bitbang
+ * mode. This is bitmask param of ioctl JTAG_SIOCMODE command
+ */
+#define JTAG_XFER_HW_MODE 1
+
+/**
+ * enum jtag_endstate:
+ *
+ * @JTAG_STATE_IDLE: JTAG state machine IDLE state
+ * @JTAG_STATE_PAUSEIR: JTAG state machine PAUSE_IR state
+ * @JTAG_STATE_PAUSEDR: JTAG state machine PAUSE_DR state
+ */
+enum jtag_endstate {
+ JTAG_STATE_IDLE,
+ JTAG_STATE_PAUSEIR,
+ JTAG_STATE_PAUSEDR,
+};
+
+/**
+ * enum jtag_xfer_type:
+ *
+ * @JTAG_SIR_XFER: SIR transfer
+ * @JTAG_SDR_XFER: SDR transfer
+ */
+enum jtag_xfer_type {
+ JTAG_SIR_XFER,
+ JTAG_SDR_XFER,
+};
+
+/**
+ * enum jtag_xfer_direction:
+ *
+ * @JTAG_READ_XFER: read transfer
+ * @JTAG_WRITE_XFER: write transfer
+ */
+enum jtag_xfer_direction {
+ JTAG_READ_XFER,
+ JTAG_WRITE_XFER,
+};
+
+/**
+ * struct jtag_run_test_idle - forces JTAG state machine to
+ * RUN_TEST/IDLE state
+ *
+ * @reset: 0 - run IDLE/PAUSE from current state
+ * 1 - go through TEST_LOGIC/RESET state before IDLE/PAUSE
+ * @end: completion flag
+ * @tck: clock counter
+ *
+ * Structure represents interface to JTAG device for jtag idle
+ * execution.
+ */
+struct jtag_run_test_idle {
+ __u8 reset;
+ __u8 endstate;
+ __u8 tck;
+};
+
+/**
+ * struct jtag_xfer - jtag xfer:
+ *
+ * @type: transfer type
+ * @direction: xfer direction
+ * @length: xfer bits len
+ * @tdio : xfer data array
+ * @endir: xfer end state
+ *
+ * Structure represents interface to JTAG device for jtag sdr xfer
+ * execution.
+ */
+struct jtag_xfer {
+ __u8 type;
+ __u8 direction;
+ __u8 endstate;
+ __u32 length;
+ __u64 tdio;
+};
+
+/* ioctl interface */
+#define __JTAG_IOCTL_MAGIC 0xb2
+
+#define JTAG_IOCRUNTEST _IOW(__JTAG_IOCTL_MAGIC, 0,\
+ struct jtag_run_test_idle)
+#define JTAG_SIOCFREQ _IOW(__JTAG_IOCTL_MAGIC, 1, unsigned int)
+#define JTAG_GIOCFREQ _IOR(__JTAG_IOCTL_MAGIC, 2, unsigned int)
+#define JTAG_IOCXFER _IOWR(__JTAG_IOCTL_MAGIC, 3, struct jtag_xfer)
+#define JTAG_GIOCSTATUS _IOWR(__JTAG_IOCTL_MAGIC, 4, enum jtag_endstate)
+#define JTAG_SIOCMODE _IOW(__JTAG_IOCTL_MAGIC, 5, unsigned int)
+
+#define JTAG_FIRST_MINOR 0
+#define JTAG_MAX_DEVICES 32
+
+#endif /* __UAPI_LINUX_JTAG_H */
--
1.7.1
^ permalink raw reply related
* [patch v15 0/4] JTAG driver introduction
From: Oleksandr Shamray @ 2017-12-25 11:53 UTC (permalink / raw)
To: gregkh, arnd
Cc: linux-kernel, linux-arm-kernel, devicetree, openbmc, joel, jiri,
tklauser, linux-serial, vadimp, system-sw-low-level, robh+dt,
openocd-devel-owner, linux-api, davem, mchehab, Oleksandr Shamray
When a need raise up to use JTAG interface for system's devices
programming or CPU debugging, usually the user layer
application implements jtag protocol by bit-bang or using a
proprietary connection to vendor hardware.
This method can be slow and not generic.
We propose to implement general JTAG interface and infrastructure
to communicate with user layer application. In such way, we can
have the standard JTAG interface core part and separation from
specific HW implementation.
This allow new capability to debug the CPU or program system's
device via BMC without additional devices nor cost.
This patch purpose is to add JTAG master core infrastructure by
defining new JTAG class and provide generic JTAG interface
to allow hardware specific drivers to connect this interface.
This will enable all JTAG drivers to use the common interface
part and will have separate for hardware implementation.
The JTAG (Joint Test Action Group) core driver provides minimal generic
JTAG interface, which can be used by hardware specific JTAG master
controllers. By providing common interface for the JTAG controllers,
user space device programing is hardware independent.
Modern SoC which in use for embedded system' equipped with
internal JTAG master interface.
This interface is used for programming and debugging system's
hardware components, like CPLD, FPGA, CPU, voltage and
industrial controllers.
Firmware for such devices can be upgraded through JTAG interface during
Runtime. The JTAG standard support for multiple devices programming,
is in case their lines are daisy-chained together.
For example, systems which equipped with host CPU, BMC SoC or/and
number of programmable devices are capable to connect a pin and
select system components dynamically for programming and debugging,
This is using by the BMC which is equipped with internal SoC master
controller.
For example:
BMC JTAG master --> pin selected to CPLDs chain for programming (filed
upgrade, production)
BMC JTAG master --> pin selected to voltage monitors for programming
(field upgrade, production)
BMC JTAG master --> pin selected to host CPU (on-site debugging
and developers debugging)
For example, we can have application in user space which using calls
to JTAG driver executes CPLD programming directly from SVF file
The JTAG standard (IEEE 1149.1) defines the next connector pins:
- TDI (Test Data In);
- TDO (Test Data Out);
- TCK (Test Clock);
- TMS (Test Mode Select);
- TRST (Test Reset) (Optional);
The SoC equipped with JTAG master controller, performs
device programming on command or vector level. For example
a file in a standard SVF (Serial Vector Format) that contains
boundary scan vectors, can be used by sending each vector
to the JTAG interface and the JTAG controller will execute
the programming.
Initial version provides the system calls set for:
- SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
- SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
number of clocks.
SoC which are not equipped with JTAG master interface, can be built
on top of JTAG core driver infrastructure, by applying bit-banging of
TDI, TDO, TCK and TMS pins within the hardware specific driver.
Oleksandr Shamray (4):
drivers: jtag: Add JTAG core driver
drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master
driver
Documentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx
families JTAG master driver
Documentation: jtag: Add ABI documentation
Documentation/ABI/testing/jtag-dev | 27 +
.../devicetree/bindings/jtag/aspeed-jtag.txt | 18 +
Documentation/ioctl/ioctl-number.txt | 2 +
MAINTAINERS | 10 +
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/jtag/Kconfig | 30 +
drivers/jtag/Makefile | 2 +
drivers/jtag/jtag-aspeed.c | 774 ++++++++++++++++++++
drivers/jtag/jtag.c | 288 ++++++++
include/linux/jtag.h | 45 ++
include/uapi/linux/jtag.h | 104 +++
12 files changed, 1303 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/jtag-dev
create mode 100644 Documentation/devicetree/bindings/jtag/aspeed-jtag.txt
create mode 100644 drivers/jtag/Kconfig
create mode 100644 drivers/jtag/Makefile
create mode 100644 drivers/jtag/jtag-aspeed.c
create mode 100644 drivers/jtag/jtag.c
create mode 100644 include/linux/jtag.h
create mode 100644 include/uapi/linux/jtag.h
^ permalink raw reply
* [RFC PATCH v11 5/5] arm64: dts: rockchip: Move PCIe WAKE# irq to pcie port for Gru
From: Jeffy Chen @ 2017-12-25 11:47 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
Matthias Kaehlcke, devicetree, Enric Balletbo i Serra,
Heiko Stuebner, linux-rockchip, Rob Herring, linux-arm-kernel,
Will Deacon, Mark Rutland, Caesar Wang, Catalin Marinas
In-Reply-To: <20171225114742.18920-1-jeffy.chen@rock-chips.com>
Currently we are handling PCIe WAKE# irq in mrvl wifi driver.
Move it to rockchip pcie port since we are going to handle it in the
pci core.
Also avoid this irq been considered as the PCI interrupt pin in the
of_irq_parse_pci().
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v11:
Move to pcie port as Brian suggested.
Changes in v10: None
Changes in v9:
Rewrite the commit message.
Changes in v8:
Rewrite the commit message.
Changes in v7: None
Changes in v6: None
Changes in v5:
Use "wakeup" instead of "wake"
Changes in v3: None
Changes in v2: None
arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
index 03f195025390..be41d363efd8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi
@@ -719,15 +719,16 @@ ap_i2c_audio: &i2c8 {
#size-cells = <2>;
ranges;
+ interrupts-extended = <&pcie0 1>, <&gpio0 8 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "pci", "wakeup";
+ pinctrl-names = "default";
+ pinctrl-0 = <&wlan_host_wake_l>;
+ wakeup-source;
+
mvl_wifi: wifi@0,0 {
compatible = "pci1b4b,2b42";
reg = <0x83010000 0x0 0x00000000 0x0 0x00100000
0x83010000 0x0 0x00100000 0x0 0x00100000>;
- interrupt-parent = <&gpio0>;
- interrupts = <8 IRQ_TYPE_LEVEL_LOW>;
- pinctrl-names = "default";
- pinctrl-0 = <&wlan_host_wake_l>;
- wakeup-source;
};
};
};
--
2.11.0
^ permalink raw reply related
* [RFC PATCH v11 4/5] PCI / PM: Add support for the PCIe WAKE# signal for OF
From: Jeffy Chen @ 2017-12-25 11:47 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
devicetree, linux-pci, Rob Herring, Frank Rowand
In-Reply-To: <20171225114742.18920-1-jeffy.chen@rock-chips.com>
Add of_pci_setup_wake_irq() and of_pci_teardown_wake_irq() to handle
the PCIe WAKE# interrupt.
Also use the dedicated wakeirq infrastructure to simplify it.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v11:
Only support 1-per-device PCIe WAKE# pin as suggested.
Changes in v10:
Use device_set_wakeup_capable() instead of device_set_wakeup_enable(),
since dedicated wakeirq will be lost in device_set_wakeup_enable(false).
Changes in v9:
Fix check error in .cleanup().
Move dedicated wakeirq setup to setup() callback and use
device_set_wakeup_enable() to enable/disable.
Changes in v8:
Add pci-of.c and use platform_pm_ops to handle the PCIe WAKE# signal.
Changes in v7:
Move PCIE_WAKE handling into pci core.
Changes in v6:
Fix device_init_wake error handling, and add some comments.
Changes in v5:
Rebase.
Changes in v3:
Fix error handling.
Changes in v2:
Use dev_pm_set_dedicated_wake_irq.
drivers/of/of_pci_irq.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++
drivers/pci/pci-driver.c | 10 ++++++++++
include/linux/of_pci.h | 9 +++++++++
3 files changed, 68 insertions(+)
diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c
index d39565d5477b..def884c1a37a 100644
--- a/drivers/of/of_pci_irq.c
+++ b/drivers/of/of_pci_irq.c
@@ -1,8 +1,57 @@
#include <linux/kernel.h>
#include <linux/of_pci.h>
#include <linux/of_irq.h>
+#include <linux/pm_wakeirq.h>
#include <linux/export.h>
+int of_pci_setup_wake_irq(struct pci_dev *pdev)
+{
+ struct pci_dev *ppdev;
+ struct device_node *dn;
+ int ret, irq;
+
+ /* Get the pci_dev of our parent. Hopefully it's a port. */
+ ppdev = pdev->bus->self;
+ /* Nope, it's a host bridge. */
+ if (!ppdev)
+ return 0;
+
+ dn = pci_device_to_OF_node(ppdev);
+ if (!dn)
+ return 0;
+
+ irq = of_irq_get_byname(dn, "wakeup");
+ if (irq == -EPROBE_DEFER)
+ return irq;
+ /* Ignore other errors, since a missing wakeup is non-fatal. */
+ else if (irq < 0) {
+ dev_info(&pdev->dev, "cannot get wakeup interrupt: %d\n", irq);
+ return 0;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+
+ ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, irq);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to set wake IRQ: %d\n", ret);
+ device_init_wakeup(&pdev->dev, false);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_pci_setup_wake_irq);
+
+void of_pci_teardown_wake_irq(struct pci_dev *pdev)
+{
+ if (!pdev->dev.power.wakeirq)
+ return;
+
+ dev_pm_clear_wake_irq(&pdev->dev);
+ device_init_wakeup(&pdev->dev, false);
+}
+EXPORT_SYMBOL_GPL(of_pci_teardown_wake_irq);
+
/**
* of_irq_parse_pci - Resolve the interrupt for a PCI device
* @pdev: the device whose interrupt is to be resolved
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d79dbc377b9c..b4475ff35d97 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/cpu.h>
+#include <linux/of_pci.h>
#include <linux/pm_runtime.h>
#include <linux/suspend.h>
#include <linux/kexec.h>
@@ -421,10 +422,17 @@ static int pci_device_probe(struct device *dev)
if (error < 0)
return error;
+ error = of_pci_setup_wake_irq(pci_dev);
+ if (error < 0) {
+ pcibios_free_irq(pci_dev);
+ return error;
+ }
+
pci_dev_get(pci_dev);
if (pci_device_can_probe(pci_dev)) {
error = __pci_device_probe(drv, pci_dev);
if (error) {
+ of_pci_teardown_wake_irq(pci_dev);
pcibios_free_irq(pci_dev);
pci_dev_put(pci_dev);
}
@@ -438,6 +446,8 @@ static int pci_device_remove(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *drv = pci_dev->driver;
+ of_pci_teardown_wake_irq(pci_dev);
+
if (drv) {
if (drv->remove) {
pm_runtime_get_sync(dev);
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index bf588a05d0d0..80fe8ae3393e 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -10,6 +10,8 @@ struct of_phandle_args;
struct device_node;
#ifdef CONFIG_OF_PCI
+int of_pci_setup_wake_irq(struct pci_dev *pdev);
+void of_pci_teardown_wake_irq(struct pci_dev *pdev);
int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq);
struct device_node *of_pci_find_child_device(struct device_node *parent,
unsigned int devfn);
@@ -23,6 +25,13 @@ int of_pci_map_rid(struct device_node *np, u32 rid,
const char *map_name, const char *map_mask_name,
struct device_node **target, u32 *id_out);
#else
+static inline int of_pci_setup_wake_irq(struct pci_dev *pdev)
+{
+ return 0;
+}
+
+static void of_pci_teardown_wake_irq(struct pci_dev *pdev) { };
+
static inline int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq)
{
return 0;
--
2.11.0
^ permalink raw reply related
* [RFC PATCH v11 2/5] of/irq: Adjust of_pci_irq parsing for multiple interrupts
From: Jeffy Chen @ 2017-12-25 11:47 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
Frank Rowand, devicetree, Rob Herring
In-Reply-To: <20171225114742.18920-1-jeffy.chen@rock-chips.com>
Currently we are considering the first irq as the PCI interrupt pin,
but a PCI device may have multiple interrupts(e.g. PCIe WAKE# pin).
Only parse the PCI interrupt pin when the irq is unnamed or named as
"pci".
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v11:
Address Brian's comments.
Changes in v10: None
Changes in v9: None
Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v3: None
Changes in v2: None
drivers/of/of_pci_irq.c | 22 +++++++++++++++++++---
1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c
index 3a05568f65df..d39565d5477b 100644
--- a/drivers/of/of_pci_irq.c
+++ b/drivers/of/of_pci_irq.c
@@ -27,9 +27,25 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
*/
dn = pci_device_to_OF_node(pdev);
if (dn) {
- rc = of_irq_parse_one(dn, 0, out_irq);
- if (!rc)
- return rc;
+ struct property *prop;
+ const char *name;
+ int index = 0;
+
+ of_property_for_each_string(dn, "interrupt-names", prop, name) {
+ if (!strcmp(name, "pci"))
+ break;
+ index++;
+ }
+
+ /*
+ * Only parse from DT if we have no "interrupt-names",
+ * or if we found an interrupt named "pci".
+ */
+ if (index == 0 || name) {
+ rc = of_irq_parse_one(dn, index, out_irq);
+ if (!rc)
+ return rc;
+ }
}
/* Ok, we don't, time to have fun. Let's start by building up an
--
2.11.0
^ permalink raw reply related
* [RFC PATCH v11 1/5] dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq
From: Jeffy Chen @ 2017-12-25 11:47 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: linux-pm, tony, shawn.lin, briannorris, rjw, dianders, Jeffy Chen,
devicetree, linux-pci, Rob Herring, Mark Rutland
In-Reply-To: <20171225114742.18920-1-jeffy.chen@rock-chips.com>
We are going to handle PCIe WAKE# pin for PCI devices in the pci core,
so add definitions of the optional PCIe WAKE# pin for PCI devices.
Also add an definition of the optional PCI interrupt pin for PCI
devices to distinguish it from the PCIe WAKE# pin.
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v11:
Only add irq definitions for PCI devices and rewrite the commit message.
Changes in v10: None
Changes in v9:
Add section for PCI devices and rewrite the commit message.
Changes in v8:
Add optional "pci", and rewrite commit message.
Changes in v7: None
Changes in v6: None
Changes in v5:
Move to pci.txt
Changes in v3: None
Changes in v2: None
Documentation/devicetree/bindings/pci/pci.txt | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/Documentation/devicetree/bindings/pci/pci.txt b/Documentation/devicetree/bindings/pci/pci.txt
index c77981c5dd18..3045ac452f27 100644
--- a/Documentation/devicetree/bindings/pci/pci.txt
+++ b/Documentation/devicetree/bindings/pci/pci.txt
@@ -24,3 +24,13 @@ driver implementation may support the following properties:
unsupported link speed, for instance, trying to do training for
unsupported link speed, etc. Must be '4' for gen4, '3' for gen3, '2'
for gen2, and '1' for gen1. Any other values are invalid.
+
+PCI devices may support the following properties:
+
+- interrupts: Interrupt specifier for each name in interrupt-names.
+- interrupt-names:
+ May contain "wakeup" for PCIe WAKE# interrupt and "pci" for PCI interrupt.
+ The PCI devices may optionally include an 'interrupts' property that
+ represents the legacy PCI interrupt. And when we try to specify the PCIe
+ WAKE# pin, a corresponding 'interrupt-names' property is required to
+ distinguish them.
--
2.11.0
^ permalink raw reply related
* [RFC PATCH v11 0/5] PCI: rockchip: Move PCIe WAKE# handling into pci core
From: Jeffy Chen @ 2017-12-25 11:47 UTC (permalink / raw)
To: linux-kernel, bhelgaas
Cc: Mark Rutland, linux-wireless, Heiko Stuebner, tony, linux-pci,
shawn.lin, Will Deacon, Amitkumar Karwar, Frank Rowand,
briannorris, linux-rockchip, Matthias Kaehlcke, linux-arm-kernel,
Catalin Marinas, Caesar Wang, devicetree, Xinming Hu, linux-pm,
Jeffy Chen, Nishant Sarmukadam, Rob Herring, Kalle Valo,
Ganapathi Bhat, netdev, rjw, dianders, Enric Balletbo i Serra
Currently we are handling wake irq in mrvl wifi driver. Move it into
pci core.
Tested on my chromebook bob(with cros 4.4 kernel and mrvl wifi).
Changes in v11:
Only add irq definitions for PCI devices and rewrite the commit message.
Address Brian's comments.
Only support 1-per-device PCIe WAKE# pin as suggested.
Move to pcie port as Brian suggested.
Changes in v10:
Use device_set_wakeup_capable() instead of device_set_wakeup_enable(),
since dedicated wakeirq will be lost in device_set_wakeup_enable(false).
Changes in v9:
Add section for PCI devices and rewrite the commit message.
Fix check error in .cleanup().
Move dedicated wakeirq setup to setup() callback and use
device_set_wakeup_enable() to enable/disable.
Rewrite the commit message.
Changes in v8:
Add optional "pci", and rewrite commit message.
Add pci-of.c and use platform_pm_ops to handle the PCIe WAKE# signal.
Rewrite the commit message.
Changes in v7:
Move PCIE_WAKE handling into pci core.
Changes in v6:
Fix device_init_wake error handling, and add some comments.
Changes in v5:
Move to pci.txt
Rebase.
Use "wakeup" instead of "wake"
Changes in v3:
Fix error handling.
Changes in v2:
Use dev_pm_set_dedicated_wake_irq.
Jeffy Chen (5):
dt-bindings: PCI: Add definition of PCIe WAKE# irq and PCI irq
of/irq: Adjust of_pci_irq parsing for multiple interrupts
mwifiex: Disable wakeup irq handling for pcie
PCI / PM: Add support for the PCIe WAKE# signal for OF
arm64: dts: rockchip: Move PCIe WAKE# irq to pcie port for Gru
Documentation/devicetree/bindings/pci/pci.txt | 10 ++++
arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 11 +++--
drivers/net/wireless/marvell/mwifiex/main.c | 4 ++
drivers/of/of_pci_irq.c | 71 +++++++++++++++++++++++++--
drivers/pci/pci-driver.c | 10 ++++
include/linux/of_pci.h | 9 ++++
6 files changed, 107 insertions(+), 8 deletions(-)
--
2.11.0
^ permalink raw reply
* Re: [PATCH v4 2/2] PCI: mediatek: Set up class type and vendor ID for MT7622
From: Ryder Lee @ 2017-12-25 10:27 UTC (permalink / raw)
To: honghui.zhang
Cc: bhelgaas, matthias.bgg, linux-arm-kernel, linux-mediatek,
linux-pci, linux-kernel, devicetree, yingjoe.chen, eddie.huang,
lorenzo.pieralisi, hongkun.cao, youlin.pei, yong.wu, yt.shen,
sean.wang, xinping.qian
In-Reply-To: <1513921178-16148-3-git-send-email-honghui.zhang@mediatek.com>
On Fri, 2017-12-22 at 13:39 +0800, honghui.zhang@mediatek.com wrote:
> From: Honghui Zhang <honghui.zhang@mediatek.com>
>
> The hardware default value of IDs and class type is not correct,
> fix that by setup the correct values before start up.
>
> Signed-off-by: Honghui Zhang <honghui.zhang@mediatek.com>
> ---
> drivers/pci/host/pcie-mediatek.c | 12 ++++++++++++
> include/linux/pci_ids.h | 3 +++
> 2 files changed, 15 insertions(+)
>
> diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
> index fc29a9a..0ef33e4 100644
> --- a/drivers/pci/host/pcie-mediatek.c
> +++ b/drivers/pci/host/pcie-mediatek.c
> @@ -74,6 +74,10 @@
>
> /* PCIe V2 per-port registers */
> #define PCIE_MSI_VECTOR 0x0c0
> +
> +#define PCIE_CONF_ID 0x100
> +#define PCIE_CONF_CLASS 0x104
> +
> #define PCIE_INT_MASK 0x420
> #define INTX_MASK GENMASK(19, 16)
> #define INTX_SHIFT 16
> @@ -393,6 +397,14 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
> val |= PCIE_CSR_LTSSM_EN(port->slot) |
> PCIE_CSR_ASPM_L1_EN(port->slot);
> writel(val, pcie->base + PCIE_SYS_CFG_V2);
> +
> + /* Set up vendor ID and device ID for MT7622*/
> + val = PCI_VENDOR_ID_MEDIATEK | (PCI_DEVICE_ID_MT7622 << 16);
> + writel(val, port->base + PCIE_CONF_ID);
IMHO, this is a general function so you can ignore "device ID for
MT7622" here, but just make sure class code/vendor ID correct.
> + /* Set up class code for MT7622 */
> + val = PCI_CLASS_BRIDGE_PCI << 16;
> + writel(val, port->base + PCIE_CONF_CLASS);
> }
>
> /* Assert all reset signals */
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index ab20dc5..000c5df 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2113,6 +2113,9 @@
>
> #define PCI_VENDOR_ID_MYRICOM 0x14c1
>
> +#define PCI_VENDOR_ID_MEDIATEK 0x14c3
> +#define PCI_DEVICE_ID_MT7622 0x5396
> +
> #define PCI_VENDOR_ID_TITAN 0x14D2
> #define PCI_DEVICE_ID_TITAN_010L 0x8001
> #define PCI_DEVICE_ID_TITAN_100L 0x8010
^ permalink raw reply
* Re: [PATCH v4 0/2] Initial Allwinner V3s CSI Support
From: 'Ondřej Jirman' via linux-sunxi @ 2017-12-25 8:58 UTC (permalink / raw)
To: Yong
Cc: Maxime Ripard, Mauro Carvalho Chehab, Rob Herring, Mark Rutland,
Chen-Yu Tsai, David S. Miller, Greg Kroah-Hartman, Randy Dunlap,
Hans Verkuil, Stanimir Varbanov, Hugues Fruchet, Yannick Fertre,
Philipp Zabel, Arnd Bergmann, Benjamin Gaignard,
Ramesh Shanmugasundaram, Sakari Ailus, Rick Chang,
linux-media-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY
In-Reply-To: <20171225111526.4663f997f5d6bfc6cf157f10-+3dxTMOEIRNWk0Htik3J/w@public.gmane.org>
[-- Attachment #1: Type: text/plain, Size: 3253 bytes --]
Hello,
On Mon, Dec 25, 2017 at 11:15:26AM +0800, Yong wrote:
> Hi,
>
> On Fri, 22 Dec 2017 14:46:48 +0100
> Ondřej Jirman <megous-5qf/QAjKc83QT0dZR+AlfA@public.gmane.org> wrote:
>
> > Hello,
> >
> > Yong Deng píše v Pá 22. 12. 2017 v 17:32 +0800:
> > >
> > > Test input 0:
> > >
> > > Control ioctls:
> > > test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
> > > test VIDIOC_QUERYCTRL: OK (Not Supported)
> > > test VIDIOC_G/S_CTRL: OK (Not Supported)
> > > test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
> > > test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
> > > test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
> > > Standard Controls: 0 Private Controls: 0
> >
> > I'm not sure if your driver passes control queries to the subdev. It
> > did not originally, and I'm not sure you picked up the change from my
> > version of the driver. "Not supported" here seems to indicate that it
> > does not.
> >
> > I'd be interested what's the recommended practice here. It sure helps
> > with some apps that expect to be able to modify various input controls
> > directly on the /dev/video# device. These are then supported out of the
> > box.
> >
> > It's a one-line change. See:
> >
> > https://www.kernel.org/doc/html/latest/media/kapi/v4l2-controls.html#in
> > heriting-controls
>
> I think this is a feature and not affect the driver's main function.
> I just focused on making the CSI main function to work properly in
> the initial version. Is this feature mandatory or most commonly used?
I grepped the platform/ code and it seems, that inheriting controls
from subdevs is pretty common for input drivers. (there are varying
approaches though, some inherit by hand in the link function, some
just register and empty ctrl_handler on the v4l2_dev and leave the
rest to the core).
Practically, I haven't found a common app that would allow me to enter
both /dev/video0 and /dev/v4l-subdevX. I'm sure anyone can write one
themselves, but it would be better if current controls were available
at the /dev/video0 device automatically.
It's much simpler for the userspace apps than the alternative, which
is trying to identify the correct subdev that is currently
associated with the CSI driver at runtime, which is not exactly
straightforward and requires much more code, than a few lines in
the kernel, that are required to inherit controls:
ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 0);
if (ret) {
dev_err(csi->dev,
"V4L2 controls handler init failed (%d)\n",
ret);
goto handle_error;
}
csi->v4l2_dev.ctrl_handler = &csi->ctrl_handler;
See: https://github.com/megous/linux/blob/linux-tbs/drivers/media/platform/sun6i-csi/sun6i_csi.c#L1005
regards,
o.j.
> Thanks,
> Yong
--
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/d/optout.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply
* [PATCH 4/4] ASoC: mediatek: update clock properties for mt2701
From: Ryder Lee @ 2017-12-25 8:28 UTC (permalink / raw)
To: Mark Brown
Cc: linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw, Garlic Tseng, Ryder Lee
In-Reply-To: <18b7f0ec484300af795b1873efab96e51393459c.1514190169.git.ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
Add 'assigned-clocks*' properties which are used to initialize default
domain sources of audio system. we could configure different sets of
input clocks through DTS now - depends on the platform design. Hence
driver no longer cares about that.
Also we change some 'clock-names' to make them more generic so that
other chips can reuse gracefully.
Signed-off-by: Ryder Lee <ryder.lee-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
Tested-by: Garlic Tseng <garlic.tseng-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
.../devicetree/bindings/sound/mt2701-afe-pcm.txt | 199 ++++++++++-----------
1 file changed, 93 insertions(+), 106 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
index 77a57f8..dd99990 100644
--- a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
+++ b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
@@ -6,51 +6,48 @@ Required properties:
- interrupts: should contain AFE and ASYS interrupts
- interrupt-names: should be "afe" and "asys"
- power-domains: should define the power domain
+- clocks: Must contain an entry for each entry in clock-names
+ See ../clocks/clock-bindings.txt for details
- clock-names: should have these clock names:
"infra_sys_audio_clk",
"top_audio_mux1_sel",
"top_audio_mux2_sel",
- "top_audio_mux1_div",
- "top_audio_mux2_div",
- "top_audio_48k_timing",
- "top_audio_44k_timing",
- "top_audpll_mux_sel",
- "top_apll_sel",
- "top_aud1_pll_98M",
- "top_aud2_pll_90M",
- "top_hadds2_pll_98M",
- "top_hadds2_pll_294M",
- "top_audpll",
- "top_audpll_d4",
- "top_audpll_d8",
- "top_audpll_d16",
- "top_audpll_d24",
+ "top_audio_a1sys_hp",
+ "top_audio_a2sys_hp",
"top_audintbus_sel",
- "clk_26m",
- "top_syspll1_d4",
- "top_aud_k1_src_sel",
- "top_aud_k2_src_sel",
- "top_aud_k3_src_sel",
- "top_aud_k4_src_sel",
- "top_aud_k5_src_sel",
- "top_aud_k6_src_sel",
- "top_aud_k1_src_div",
- "top_aud_k2_src_div",
- "top_aud_k3_src_div",
- "top_aud_k4_src_div",
- "top_aud_k5_src_div",
- "top_aud_k6_src_div",
- "top_aud_i2s1_mclk",
- "top_aud_i2s2_mclk",
- "top_aud_i2s3_mclk",
- "top_aud_i2s4_mclk",
- "top_aud_i2s5_mclk",
- "top_aud_i2s6_mclk",
- "top_asm_m_sel",
- "top_asm_h_sel",
- "top_univpll2_d4",
- "top_univpll2_d2",
- "top_syspll_d5";
+ "i2s0_src_sel",
+ "i2s1_src_sel",
+ "i2s2_src_sel",
+ "i2s3_src_sel",
+ "i2s0_src_div",
+ "i2s1_src_div",
+ "i2s2_src_div",
+ "i2s3_src_div",
+ "i2s0_mclk_en",
+ "i2s1_mclk_en",
+ "i2s2_mclk_en",
+ "i2s3_mclk_en",
+ "i2so0_hop_ck",
+ "i2so1_hop_ck",
+ "i2so2_hop_ck",
+ "i2so3_hop_ck",
+ "i2si0_hop_ck",
+ "i2si1_hop_ck",
+ "i2si2_hop_ck",
+ "i2si3_hop_ck",
+ "asrc0_out_ck",
+ "asrc1_out_ck",
+ "asrc2_out_ck",
+ "asrc3_out_ck",
+ "audio_afe_pd",
+ "audio_afe_conn_pd",
+ "audio_a1sys_pd",
+ "audio_a2sys_pd",
+ "audio_mrgif_pd";
+- assigned-clocks: list of input clocks and dividers for the audio system.
+ See ../clocks/clock-bindings.txt for details.
+- assigned-clocks-parents: parent of input clocks of assigned clocks.
+- assigned-clock-rates: list of clock frequencies of assigned clocks.
Example:
@@ -65,90 +62,80 @@ Example:
clocks = <&infracfg CLK_INFRA_AUDIO>,
<&topckgen CLK_TOP_AUD_MUX1_SEL>,
<&topckgen CLK_TOP_AUD_MUX2_SEL>,
- <&topckgen CLK_TOP_AUD_MUX1_DIV>,
- <&topckgen CLK_TOP_AUD_MUX2_DIV>,
<&topckgen CLK_TOP_AUD_48K_TIMING>,
<&topckgen CLK_TOP_AUD_44K_TIMING>,
- <&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
- <&topckgen CLK_TOP_APLL_SEL>,
- <&topckgen CLK_TOP_AUD1PLL_98M>,
- <&topckgen CLK_TOP_AUD2PLL_90M>,
- <&topckgen CLK_TOP_HADDS2PLL_98M>,
- <&topckgen CLK_TOP_HADDS2PLL_294M>,
- <&topckgen CLK_TOP_AUDPLL>,
- <&topckgen CLK_TOP_AUDPLL_D4>,
- <&topckgen CLK_TOP_AUDPLL_D8>,
- <&topckgen CLK_TOP_AUDPLL_D16>,
- <&topckgen CLK_TOP_AUDPLL_D24>,
<&topckgen CLK_TOP_AUDINTBUS_SEL>,
- <&clk26m>,
- <&topckgen CLK_TOP_SYSPLL1_D4>,
<&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
- <&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
- <&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
<&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
<&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
<&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
<&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
- <&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
- <&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
<&topckgen CLK_TOP_AUD_I2S1_MCLK>,
<&topckgen CLK_TOP_AUD_I2S2_MCLK>,
<&topckgen CLK_TOP_AUD_I2S3_MCLK>,
<&topckgen CLK_TOP_AUD_I2S4_MCLK>,
- <&topckgen CLK_TOP_AUD_I2S5_MCLK>,
- <&topckgen CLK_TOP_AUD_I2S6_MCLK>,
- <&topckgen CLK_TOP_ASM_M_SEL>,
- <&topckgen CLK_TOP_ASM_H_SEL>,
- <&topckgen CLK_TOP_UNIVPLL2_D4>,
- <&topckgen CLK_TOP_UNIVPLL2_D2>,
- <&topckgen CLK_TOP_SYSPLL_D5>;
+ <&audiosys CLK_AUD_I2SO1>,
+ <&audiosys CLK_AUD_I2SO2>,
+ <&audiosys CLK_AUD_I2SO3>,
+ <&audiosys CLK_AUD_I2SO4>,
+ <&audiosys CLK_AUD_I2SIN1>,
+ <&audiosys CLK_AUD_I2SIN2>,
+ <&audiosys CLK_AUD_I2SIN3>,
+ <&audiosys CLK_AUD_I2SIN4>,
+ <&audiosys CLK_AUD_ASRCO1>,
+ <&audiosys CLK_AUD_ASRCO2>,
+ <&audiosys CLK_AUD_ASRCO3>,
+ <&audiosys CLK_AUD_ASRCO4>,
+ <&audiosys CLK_AUD_AFE>,
+ <&audiosys CLK_AUD_AFE_CONN>,
+ <&audiosys CLK_AUD_A1SYS>,
+ <&audiosys CLK_AUD_A2SYS>,
+ <&audiosys CLK_AUD_AFE_MRGIF>;
clock-names = "infra_sys_audio_clk",
"top_audio_mux1_sel",
"top_audio_mux2_sel",
- "top_audio_mux1_div",
- "top_audio_mux2_div",
- "top_audio_48k_timing",
- "top_audio_44k_timing",
- "top_audpll_mux_sel",
- "top_apll_sel",
- "top_aud1_pll_98M",
- "top_aud2_pll_90M",
- "top_hadds2_pll_98M",
- "top_hadds2_pll_294M",
- "top_audpll",
- "top_audpll_d4",
- "top_audpll_d8",
- "top_audpll_d16",
- "top_audpll_d24",
+ "top_audio_a1sys_hp",
+ "top_audio_a2sys_hp",
"top_audintbus_sel",
- "clk_26m",
- "top_syspll1_d4",
- "top_aud_k1_src_sel",
- "top_aud_k2_src_sel",
- "top_aud_k3_src_sel",
- "top_aud_k4_src_sel",
- "top_aud_k5_src_sel",
- "top_aud_k6_src_sel",
- "top_aud_k1_src_div",
- "top_aud_k2_src_div",
- "top_aud_k3_src_div",
- "top_aud_k4_src_div",
- "top_aud_k5_src_div",
- "top_aud_k6_src_div",
- "top_aud_i2s1_mclk",
- "top_aud_i2s2_mclk",
- "top_aud_i2s3_mclk",
- "top_aud_i2s4_mclk",
- "top_aud_i2s5_mclk",
- "top_aud_i2s6_mclk",
- "top_asm_m_sel",
- "top_asm_h_sel",
- "top_univpll2_d4",
- "top_univpll2_d2",
- "top_syspll_d5";
+ "i2s0_src_sel",
+ "i2s1_src_sel",
+ "i2s2_src_sel",
+ "i2s3_src_sel",
+ "i2s0_src_div",
+ "i2s1_src_div",
+ "i2s2_src_div",
+ "i2s3_src_div",
+ "i2s0_mclk_en",
+ "i2s1_mclk_en",
+ "i2s2_mclk_en",
+ "i2s3_mclk_en",
+ "i2so0_hop_ck",
+ "i2so1_hop_ck",
+ "i2so2_hop_ck",
+ "i2so3_hop_ck",
+ "i2si0_hop_ck",
+ "i2si1_hop_ck",
+ "i2si2_hop_ck",
+ "i2si3_hop_ck",
+ "asrc0_out_ck",
+ "asrc1_out_ck",
+ "asrc2_out_ck",
+ "asrc3_out_ck",
+ "audio_afe_pd",
+ "audio_afe_conn_pd",
+ "audio_a1sys_pd",
+ "audio_a2sys_pd",
+ "audio_mrgif_pd";
+
+ assigned-clocks = <&topckgen CLK_TOP_AUD_MUX1_SEL>,
+ <&topckgen CLK_TOP_AUD_MUX2_SEL>,
+ <&topckgen CLK_TOP_AUD_MUX1_DIV>,
+ <&topckgen CLK_TOP_AUD_MUX2_DIV>;
+ assigned-clock-parents = <&topckgen CLK_TOP_AUD1PLL_98M>,
+ <&topckgen CLK_TOP_AUD2PLL_90M>;
+ assigned-clock-rates = <0>, <0>, <49152000>, <45158400>;
};
--
1.9.1
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox