Devicetree
 help / color / mirror / Atom feed
* [PATCH v3 1/3] dt-bindings: hwrng: Add Samsung Exynos 5250+ True RNG bindings
From: Łukasz Stelmach @ 2017-12-04 12:53 UTC (permalink / raw)
  To: Andrew F . Davis, PrasannaKumar Muralidharan, Rob Herring,
	Matt Mackall, Herbert Xu, Krzysztof Kozlowski, Kukjin Kim,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-crypto-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Łukasz Stelmach, Marek Szyprowski, Bartlomiej Zolnierkiewicz
In-Reply-To: <20171204120429.22892-1-l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

Add binding documentation for the True Random Number Generator
found on Samsung Exynos 5250+ SoCs.

Signed-off-by: Łukasz Stelmach <l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 .../devicetree/bindings/rng/samsung,exynos5250-trng.txt | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt

diff --git a/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt b/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
new file mode 100644
index 000000000000..5a613a4ec780
--- /dev/null
+++ b/Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
@@ -0,0 +1,17 @@
+Exynos True Random Number Generator
+
+Required properties:
+
+- compatible  : Should be "samsung,exynos5250-trng".
+- reg         : Specifies base physical address and size of the registers map.
+- clocks      : Phandle to clock-controller plus clock-specifier pair.
+- clock-names : "secss" as a clock name.
+
+Example:
+
+	rng@10830600 {
+		compatible = "samsung,exynos5250-trng";
+		reg = <0x10830600 0x100>;
+		clocks = <&clock CLK_SSS>;
+		clock-names = "secss";
+	};
-- 
2.11.0

--
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 v3 0/3] True RNG driver for Samsung Exynos 5250+ SoCs
From: Łukasz Stelmach @ 2017-12-04 12:53 UTC (permalink / raw)
  To: Andrew F. Davis, PrasannaKumar Muralidharan, Rob Herring,
	Matt Mackall, Herbert Xu, Krzysztof Kozlowski, Kukjin Kim,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-crypto-u79uwXL29TY76Z2rM5mHXA,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: Łukasz Stelmach, Marek Szyprowski, Bartlomiej Zolnierkiewicz
In-Reply-To: <20171204120429.22892-1-l.stelmach-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="UTF-8", Size: 3154 bytes --]

Hello.

The following patches add support for the true random number generator
found in Samsung Exynos 5250+ SoCs.

Patch #1 adds documentation for devicetree bindings.

Patch #2 introduces the driver and appropriate changes in Makefile and Kconfig.

Patch #3 adds nodes in devicetree files for Exynos SoCs (requires
https://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux.git/commit/?id=cdd745c8c76b02471d88f467c44a3d4eb431aa0b).

Changes in v3:

- Changed node-name in device-tree bindings according to from Krzysztof
  Koz‚³owski's recommendation.
- Fixed name and added EXYNOS_ in EXYNOS_TRNG_CTRL_RNGEN
- Removed unnecessary label and simplifed the abnormal exit path in
  exynos_trng_probe()
- Replaced __raw_{readl,writel}() with {readl,writel}_relaxed() (thanks
  PrasannaKumar Muralidharan)

Changes in v2:
- Fixed indentation in drivers/char/hw_random/Kconfig.
- Defined TRNG_CTRL_RGNEN.
- Removed global variable exynos_trng_dev.
- Removed exynos_trng_{set,get}_reg() functions.
- Used the min_t() macro instead of the ternary operator in
  exynos_trng_do_read().
- Moved trng initialisation to the variable declaration in
  exynos_trng_init().
- Fixed comment formating.
- Removed unnecessary "TODO" comments.
- Return ENOMEM, if devm_kzalloc() devm_kstrdup() fail.
- Rephrased and unified error messages in exynos_trng_probe().
- Removed nullification of trng->mem.
- Added err_pm_get label at the end of exynos_trng_probe().
- Removed double error message at the end of exynos_trng_probe().
- Implemented exynos_trng_remove().

v2 available here:

https://www.spinics.net/lists/linux-samsung-soc/msg61280.html
https://patchwork.kernel.org/patch/10076225/
https://patchwork.kernel.org/patch/10076227/
https://patchwork.kernel.org/patch/10076237/

v1 can be found:

https://www.spinics.net/lists/linux-samsung-soc/msg61253.html
https://patchwork.kernel.org/patch/10072967/
https://patchwork.kernel.org/patch/10072971/
https://patchwork.kernel.org/patch/10072963/

‚£ukasz Stelmach (3):
  dt-bindings: hwrng: Add Samsung Exynos 5250+ True RNG bindings
  hwrng: exynos - add Samsung Exynos True RNG driver
  ARM: dts: exynos: Add nodes for True Random Number Generator

 .../bindings/rng/samsung,exynos5250-trng.txt       |  17 ++
 MAINTAINERS                                        |   7 +
 arch/arm/boot/dts/exynos5.dtsi                     |   5 +
 arch/arm/boot/dts/exynos5250.dtsi                  |   5 +
 arch/arm/boot/dts/exynos5410.dtsi                  |   5 +
 arch/arm/boot/dts/exynos5420.dtsi                  |   5 +
 drivers/char/hw_random/Kconfig                     |  12 +
 drivers/char/hw_random/Makefile                    |   1 +
 drivers/char/hw_random/exynos-trng.c               | 245 +++++++++++++++++++++
 9 files changed, 302 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rng/samsung,exynos5250-trng.txt
 create mode 100644 drivers/char/hw_random/exynos-trng.c

-- 
2.11.0

--
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 v4 3/5] media: i2c: Add TDA1997x HDMI receiver driver
From: Hans Verkuil @ 2017-12-04 12:50 UTC (permalink / raw)
  To: Tim Harvey, linux-media-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	shawnguo-DgEjT+Ai2ygdnm+yROfE0A, Steve Longerbeam, Philipp Zabel,
	Hans Verkuil, Mauro Carvalho Chehab
In-Reply-To: <1511990397-27647-4-git-send-email-tharvey-UMMOYl/HMS+akBO8gow8eQ@public.gmane.org>

Hi Tim,

Found a few more small issues. After that's fixed and you have the Ack for the
bindings this can be merged I think.

On 11/29/2017 10:19 PM, Tim Harvey wrote:
> Add support for the TDA1997x HDMI receivers.
> 
> Cc: Hans Verkuil <hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
> Signed-off-by: Tim Harvey <tharvey-UMMOYl/HMS+akBO8gow8eQ@public.gmane.org>
> ---
> v4:
>  - move include/dt-bindings/media/tda1997x.h to bindings patch
>  - fix typos
>  - fix default quant range for VGA
>  - fix quant range handling and conv matrix
>  - add additional standards and capabilities to timings_cap
> 
> v3:
>  - use V4L2_DV_BT_FRAME_WIDTH/HEIGHT macros
>  - fixed missing break
>  - use only hdmi_infoframe_log for infoframe logging
>  - simplify tda1997x_s_stream error handling
>  - add delayed work proc to handle hotplug enable/disable
>  - fix set_edid (disable HPD before writing, enable after)
>  - remove enabling edid by default
>  - initialize timings
>  - take quant range into account in colorspace conversion
>  - remove vendor/product tracking (we provide this in log_status via infoframes)
>  - add v4l_controls
>  - add more detail to log_status
>  - calculate vhref generator timings
>  - timing detection fixes (rounding errors, hswidth errors)
>  - rename configure_input/configure_conv functions
> 
> v2:
>  - implement dv timings enum/cap
>  - remove deprecated g_mbus_config op
>  - fix dv_query_timings
>  - add EDID get/set handling
>  - remove max-pixel-rate support
>  - add audio codec DAI support
>  - change audio bindings
> ---
>  drivers/media/i2c/Kconfig    |    9 +
>  drivers/media/i2c/Makefile   |    1 +
>  drivers/media/i2c/tda1997x.c | 3516 ++++++++++++++++++++++++++++++++++++++++++
>  include/media/i2c/tda1997x.h |   53 +
>  4 files changed, 3579 insertions(+)
>  create mode 100644 drivers/media/i2c/tda1997x.c
>  create mode 100644 include/media/i2c/tda1997x.h
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 3c6d642..abf24b9 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -56,6 +56,15 @@ config VIDEO_TDA9840
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called tda9840.
>  
> +config VIDEO_TDA1997X
> +	tristate "NXP TDA1997x HDMI receiver"
> +	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
> +	---help---
> +	  V4L2 subdevice driver for the NXP TDA1997x HDMI receivers.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called tda1997x.
> +
>  config VIDEO_TEA6415C
>  	tristate "Philips TEA6415C audio processor"
>  	depends on I2C
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 548a9ef..adfcae9 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
>  obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
>  obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
>  obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
> +obj-$(CONFIG_VIDEO_TDA1997X) += tda1997x.o
>  obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
>  obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
>  obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
> diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
> new file mode 100644
> index 0000000..70e9680
> --- /dev/null
> +++ b/drivers/media/i2c/tda1997x.c
> @@ -0,0 +1,3516 @@
> +/*
> + * Copyright (C) 2017 Gateworks Corporation
> + *
> + * 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/delay.h>
> +#include <linux/hdmi.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_graph.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/types.h>
> +#include <linux/v4l2-dv-timings.h>
> +#include <linux/videodev2.h>
> +
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-device.h>
> +#include <media/v4l2-dv-timings.h>
> +#include <media/v4l2-event.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/i2c/tda1997x.h>
> +
> +#include <sound/core.h>
> +#include <sound/pcm.h>
> +#include <sound/pcm_params.h>
> +#include <sound/soc.h>
> +
> +#include <dt-bindings/media/tda1997x.h>
> +
> +/* debug level */
> +static int debug;
> +module_param(debug, int, 0644);
> +MODULE_PARM_DESC(debug, "debug level (0-2)");
> +
> +/* Page 0x00 - General Control */
> +#define REG_VERSION		0x0000
> +#define REG_INPUT_SEL		0x0001
> +#define REG_SVC_MODE		0x0002
> +#define REG_HPD_MAN_CTRL	0x0003
> +#define REG_RT_MAN_CTRL		0x0004
> +#define REG_STANDBY_SOFT_RST	0x000A
> +#define REG_HDMI_SOFT_RST	0x000B
> +#define REG_HDMI_INFO_RST	0x000C
> +#define REG_INT_FLG_CLR_TOP	0x000E
> +#define REG_INT_FLG_CLR_SUS	0x000F
> +#define REG_INT_FLG_CLR_DDC	0x0010
> +#define REG_INT_FLG_CLR_RATE	0x0011
> +#define REG_INT_FLG_CLR_MODE	0x0012
> +#define REG_INT_FLG_CLR_INFO	0x0013
> +#define REG_INT_FLG_CLR_AUDIO	0x0014
> +#define REG_INT_FLG_CLR_HDCP	0x0015
> +#define REG_INT_FLG_CLR_AFE	0x0016
> +#define REG_INT_MASK_TOP	0x0017
> +#define REG_INT_MASK_SUS	0x0018
> +#define REG_INT_MASK_DDC	0x0019
> +#define REG_INT_MASK_RATE	0x001A
> +#define REG_INT_MASK_MODE	0x001B
> +#define REG_INT_MASK_INFO	0x001C
> +#define REG_INT_MASK_AUDIO	0x001D
> +#define REG_INT_MASK_HDCP	0x001E
> +#define REG_INT_MASK_AFE	0x001F
> +#define REG_DETECT_5V		0x0020
> +#define REG_SUS_STATUS		0x0021
> +#define REG_V_PER		0x0022
> +#define REG_H_PER		0x0025
> +#define REG_HS_WIDTH		0x0027
> +#define REG_FMT_H_TOT		0x0029
> +#define REG_FMT_H_ACT		0x002b
> +#define REG_FMT_H_FRONT		0x002d
> +#define REG_FMT_H_SYNC		0x002f
> +#define REG_FMT_H_BACK		0x0031
> +#define REG_FMT_V_TOT		0x0033
> +#define REG_FMT_V_ACT		0x0035
> +#define REG_FMT_V_FRONT_F1	0x0037
> +#define REG_FMT_V_FRONT_F2	0x0038
> +#define REG_FMT_V_SYNC		0x0039
> +#define REG_FMT_V_BACK_F1	0x003a
> +#define REG_FMT_V_BACK_F2	0x003b
> +#define REG_FMT_DE_ACT		0x003c
> +#define REG_RATE_CTRL		0x0040
> +#define REG_CLK_MIN_RATE	0x0043
> +#define REG_CLK_MAX_RATE	0x0046
> +#define REG_CLK_A_STATUS	0x0049
> +#define REG_CLK_A_RATE		0x004A
> +#define REG_DRIFT_CLK_A_REG	0x004D
> +#define REG_CLK_B_STATUS	0x004E
> +#define REG_CLK_B_RATE		0x004F
> +#define REG_DRIFT_CLK_B_REG	0x0052
> +#define REG_HDCP_CTRL		0x0060
> +#define REG_HDCP_KDS		0x0061
> +#define REG_HDCP_BCAPS		0x0063
> +#define REG_HDCP_KEY_CTRL	0x0064
> +#define REG_INFO_CTRL		0x0076
> +#define REG_INFO_EXCEED		0x0077
> +#define REG_PIX_REPEAT		0x007B
> +#define REG_AUDIO_PATH		0x007C
> +#define REG_AUDCFG		0x007D
> +#define REG_AUDIO_OUT_ENABLE	0x007E
> +#define REG_AUDIO_OUT_HIZ	0x007F
> +#define REG_VDP_CTRL		0x0080
> +#define REG_VDP_MATRIX		0x0081
> +#define REG_VHREF_CTRL		0x00A0
> +#define REG_PXCNT_PR		0x00A2
> +#define REG_PXCNT_NPIX		0x00A4
> +#define REG_LCNT_PR		0x00A6
> +#define REG_LCNT_NLIN		0x00A8
> +#define REG_HREF_S		0x00AA
> +#define REG_HREF_E		0x00AC
> +#define REG_HS_S		0x00AE
> +#define REG_HS_E		0x00B0
> +#define REG_VREF_F1_S		0x00B2
> +#define REG_VREF_F1_WIDTH	0x00B4
> +#define REG_VREF_F2_S		0x00B5
> +#define REG_VREF_F2_WIDTH	0x00B7
> +#define REG_VS_F1_LINE_S	0x00B8
> +#define REG_VS_F1_LINE_WIDTH	0x00BA
> +#define REG_VS_F2_LINE_S	0x00BB
> +#define REG_VS_F2_LINE_WIDTH	0x00BD
> +#define REG_VS_F1_PIX_S		0x00BE
> +#define REG_VS_F1_PIX_E		0x00C0
> +#define REG_VS_F2_PIX_S		0x00C2
> +#define REG_VS_F2_PIX_E		0x00C4
> +#define REG_FREF_F1_S		0x00C6
> +#define REG_FREF_F2_S		0x00C8
> +#define REG_FDW_S		0x00ca
> +#define REG_FDW_E		0x00cc
> +#define REG_BLK_GY		0x00da
> +#define REG_BLK_BU		0x00dc
> +#define REG_BLK_RV		0x00de
> +#define REG_FILTERS_CTRL	0x00e0
> +#define REG_DITHERING_CTRL	0x00E9
> +#define REG_OF			0x00EA
> +#define REG_PCLK		0x00EB
> +#define REG_HS_HREF		0x00EC
> +#define REG_VS_VREF		0x00ED
> +#define REG_DE_FREF		0x00EE
> +#define REG_VP35_32_CTRL	0x00EF
> +#define REG_VP31_28_CTRL	0x00F0
> +#define REG_VP27_24_CTRL	0x00F1
> +#define REG_VP23_20_CTRL	0x00F2
> +#define REG_VP19_16_CTRL	0x00F3
> +#define REG_VP15_12_CTRL	0x00F4
> +#define REG_VP11_08_CTRL	0x00F5
> +#define REG_VP07_04_CTRL	0x00F6
> +#define REG_VP03_00_CTRL	0x00F7
> +#define REG_CURPAGE_00H		0xFF
> +
> +#define MASK_VPER		0x3fffff
> +#define MASK_VHREF		0x3fff
> +#define MASK_HPER		0x0fff
> +#define MASK_HSWIDTH		0x03ff
> +
> +/* HPD Detection */
> +#define DETECT_UTIL		BIT(7)	/* utility of HDMI level */
> +#define DETECT_HPD		BIT(6)	/* HPD of HDMI level */
> +#define DETECT_5V_SEL		BIT(2)	/* 5V present on selected input */
> +#define DETECT_5V_B		BIT(1)	/* 5V present on input B */
> +#define DETECT_5V_A		BIT(0)	/* 5V present on input A */
> +
> +/* Input Select */
> +#define INPUT_SEL_RST_FMT	BIT(7)	/* 1=reset format measurement */
> +#define INPUT_SEL_RST_VDP	BIT(2)	/* 1=reset video data path */
> +#define INPUT_SEL_OUT_MODE	BIT(1)	/* 0=loop 1=bypass */
> +#define INPUT_SEL_B		BIT(0)	/* 0=inputA 1=inputB */
> +
> +/* Service Mode */
> +#define SVC_MODE_CLK2_MASK	0xc0
> +#define SVC_MODE_CLK2_SHIFT	6
> +#define SVC_MODE_CLK2_XTL	0L
> +#define SVC_MODE_CLK2_XTLDIV2	1L
> +#define SVC_MODE_CLK2_HDMIX2	3L
> +#define SVC_MODE_CLK1_MASK	0x30
> +#define SVC_MODE_CLK1_SHIFT	4
> +#define SVC_MODE_CLK1_XTAL	0L
> +#define SVC_MODE_CLK1_XTLDIV2	1L
> +#define SVC_MODE_CLK1_HDMI	3L
> +#define SVC_MODE_RAMP		BIT(3)	/* 0=colorbar 1=ramp */
> +#define SVC_MODE_PAL		BIT(2)	/* 0=NTSC(480i/p) 1=PAL(576i/p) */
> +#define SVC_MODE_INT_PROG	BIT(1)	/* 0=interlaced 1=progressive */
> +#define SVC_MODE_SM_ON		BIT(0)	/* Enable color bars and tone gen */
> +
> +/* HDP Manual Control */
> +#define HPD_MAN_CTRL_HPD_PULSE	BIT(7)	/* HPD Pulse low 110ms */
> +#define HPD_MAN_CTRL_5VEN	BIT(2)	/* Output 5V */
> +#define HPD_MAN_CTRL_HPD_B	BIT(1)	/* Assert HPD High for Input A */
> +#define HPD_MAN_CTRL_HPD_A	BIT(0)	/* Assert HPD High for Input A */
> +
> +/* RT_MAN_CTRL */
> +#define RT_MAN_CTRL_RT_AUTO	BIT(7)
> +#define RT_MAN_CTRL_RT		BIT(6)
> +#define RT_MAN_CTRL_RT_B	BIT(1)	/* enable TMDS pull-up on Input B */
> +#define RT_MAN_CTRL_RT_A	BIT(0)	/* enable TMDS pull-up on Input A */
> +
> +/* VDP_CTRL */
> +#define VDP_CTRL_COMPDEL_BP	BIT(5)	/* bypass compdel */
> +#define VDP_CTRL_FORMATTER_BP	BIT(4)	/* bypass formatter */
> +#define VDP_CTRL_PREFILTER_BP	BIT(1)	/* bypass prefilter */
> +#define VDP_CTRL_MATRIX_BP	BIT(0)	/* bypass matrix conversion */
> +
> +/* REG_VHREF_CTRL */
> +#define VHREF_INT_DET		BIT(7)	/* interlace detect: 1=alt 0=frame */
> +#define VHREF_VSYNC_MASK	0x60
> +#define VHREF_VSYNC_SHIFT	6
> +#define VHREF_VSYNC_AUTO	0L
> +#define VHREF_VSYNC_FDW		1L
> +#define VHREF_VSYNC_EVEN	2L
> +#define VHREF_VSYNC_ODD		3L
> +#define VHREF_STD_DET_MASK	0x18
> +#define VHREF_STD_DET_SHIFT	3
> +#define VHREF_STD_DET_PAL	0L
> +#define VHREF_STD_DET_NTSC	1L
> +#define VHREF_STD_DET_AUTO	2L
> +#define VHREF_STD_DET_OFF	3L
> +#define VHREF_VREF_SRC_STD	BIT(2)	/* 1=from standard 0=manual */
> +#define VHREF_HREF_SRC_STD	BIT(1)	/* 1=from standard 0=manual */
> +#define VHREF_HSYNC_SEL_HS	BIT(0)	/* 1=HS 0=VS */
> +
> +/* AUDIO_OUT_ENABLE */
> +#define AUDIO_OUT_ENABLE_ACLK	BIT(5)
> +#define AUDIO_OUT_ENABLE_WS	BIT(4)
> +#define AUDIO_OUT_ENABLE_AP3	BIT(3)
> +#define AUDIO_OUT_ENABLE_AP2	BIT(2)
> +#define AUDIO_OUT_ENABLE_AP1	BIT(1)
> +#define AUDIO_OUT_ENABLE_AP0	BIT(0)
> +
> +/* Prefilter Control */
> +#define FILTERS_CTRL_BU_MASK	0x0c
> +#define FILTERS_CTRL_BU_SHIFT	2
> +#define FILTERS_CTRL_RV_MASK	0x03
> +#define FILTERS_CTRL_RV_SHIFT	0
> +#define FILTERS_CTRL_OFF	0L	/* off */
> +#define FILTERS_CTRL_2TAP	1L	/* 2 Taps */
> +#define FILTERS_CTRL_7TAP	2L	/* 7 Taps */
> +#define FILTERS_CTRL_2_7TAP	3L	/* 2/7 Taps */
> +
> +/* PCLK Configuration */
> +#define PCLK_DELAY_MASK		0x70
> +#define PCLK_DELAY_SHIFT	4	/* Pixel delay (-8..+7) */
> +#define PCLK_INV_SHIFT		2
> +#define PCLK_SEL_MASK		0x03	/* clock scaler */
> +#define PCLK_SEL_SHIFT		0
> +#define PCLK_SEL_X1		0L
> +#define PCLK_SEL_X2		1L
> +#define PCLK_SEL_DIV2		2L
> +#define PCLK_SEL_DIV4		3L
> +
> +/* Pixel Repeater */
> +#define PIX_REPEAT_MASK_UP_SEL	0x30
> +#define PIX_REPEAT_MASK_REP	0x0f
> +#define PIX_REPEAT_SHIFT	4
> +#define PIX_REPEAT_CHROMA	1
> +
> +/* Page 0x01 - HDMI info and packets */
> +#define REG_HDMI_FLAGS		0x0100
> +#define REG_DEEP_COLOR_MODE	0x0101
> +#define REG_AUDIO_FLAGS		0x0108
> +#define REG_AUDIO_FREQ		0x0109
> +#define REG_ACP_PACKET_TYPE	0x0141
> +#define REG_ISRC1_PACKET_TYPE	0x0161
> +#define REG_ISRC2_PACKET_TYPE	0x0181
> +#define REG_GBD_PACKET_TYPE	0x01a1
> +
> +/* HDMI_FLAGS */
> +#define HDMI_FLAGS_AUDIO	BIT(7)	/* Audio packet in last videoframe */
> +#define HDMI_FLAGS_HDMI		BIT(6)	/* HDMI detected */
> +#define HDMI_FLAGS_EESS		BIT(5)	/* EESS detected */
> +#define HDMI_FLAGS_HDCP		BIT(4)	/* HDCP detected */
> +#define HDMI_FLAGS_AVMUTE	BIT(3)	/* AVMUTE */
> +#define HDMI_FLAGS_AUD_LAYOUT	BIT(2)	/* Layout status Audio sample packet */
> +#define HDMI_FLAGS_AUD_FIFO_OF	BIT(1)	/* FIFO read/write pointers crossed */
> +#define HDMI_FLAGS_AUD_FIFO_LOW	BIT(0)	/* FIFO read ptr within 2 of write */
> +
> +/* Page 0x12 - HDMI Extra control and debug */
> +#define REG_CLK_CFG		0x1200
> +#define REG_CLK_OUT_CFG		0x1201
> +#define REG_CFG1		0x1202
> +#define REG_CFG2		0x1203
> +#define REG_WDL_CFG		0x1210
> +#define REG_DELOCK_DELAY	0x1212
> +#define REG_PON_OVR_EN		0x12A0
> +#define REG_PON_CBIAS		0x12A1
> +#define REG_PON_RESCAL		0x12A2
> +#define REG_PON_RES		0x12A3
> +#define REG_PON_CLK		0x12A4
> +#define REG_PON_PLL		0x12A5
> +#define REG_PON_EQ		0x12A6
> +#define REG_PON_DES		0x12A7
> +#define REG_PON_OUT		0x12A8
> +#define REG_PON_MUX		0x12A9
> +#define REG_MODE_REC_CFG1	0x12F8
> +#define REG_MODE_REC_CFG2	0x12F9
> +#define REG_MODE_REC_STS	0x12FA
> +#define REG_AUDIO_LAYOUT	0x12D0
> +
> +#define PON_EN			1
> +#define PON_DIS			0
> +
> +/* CLK CFG */
> +#define CLK_CFG_INV_OUT_CLK	BIT(7)
> +#define CLK_CFG_INV_BUS_CLK	BIT(6)
> +#define CLK_CFG_SEL_ACLK_EN	BIT(1)
> +#define CLK_CFG_SEL_ACLK	BIT(0)
> +#define CLK_CFG_DIS		0
> +
> +/* Page 0x13 - HDMI Extra control and debug */
> +#define REG_DEEP_COLOR_CTRL	0x1300
> +#define REG_CGU_DBG_SEL		0x1305
> +#define REG_HDCP_DDC_ADDR	0x1310
> +#define REG_HDCP_KIDX		0x1316
> +#define REG_DEEP_PLL7_BYP	0x1347
> +#define REG_HDCP_DE_CTRL	0x1370
> +#define REG_HDCP_EP_FILT_CTRL	0x1371
> +#define REG_HDMI_CTRL		0x1377
> +#define REG_HMTP_CTRL		0x137a
> +#define REG_TIMER_D		0x13CF
> +#define REG_SUS_SET_RGB0	0x13E1
> +#define REG_SUS_SET_RGB1	0x13E2
> +#define REG_SUS_SET_RGB2	0x13E3
> +#define REG_SUS_SET_RGB3	0x13E4
> +#define REG_SUS_SET_RGB4	0x13E5
> +#define REG_MAN_SUS_HDMI_SEL	0x13E8
> +#define REG_MAN_HDMI_SET	0x13E9
> +#define REG_SUS_CLOCK_GOOD	0x13EF
> +
> +/* HDCP DE Control */
> +#define HDCP_DE_MODE_MASK	0xc0	/* DE Measurement mode */
> +#define HDCP_DE_MODE_SHIFT	6
> +#define HDCP_DE_REGEN_EN	BIT(5)	/* enable regen mode */
> +#define HDCP_DE_FILTER_MASK	0x18	/* DE filter sensitivity */
> +#define HDCP_DE_FILTER_SHIFT	3
> +#define HDCP_DE_COMP_MASK	0x07	/* DE Composition mode */
> +#define HDCP_DE_COMP_MIXED	6L
> +#define HDCP_DE_COMP_OR		5L
> +#define HDCP_DE_COMP_AND	4L
> +#define HDCP_DE_COMP_CH3	3L
> +#define HDCP_DE_COMP_CH2	2L
> +#define HDCP_DE_COMP_CH1	1L
> +#define HDCP_DE_COMP_CH0	0L
> +
> +/* HDCP EP Filter Control */
> +#define HDCP_EP_FIL_CTL_MASK	0x30
> +#define HDCP_EP_FIL_CTL_SHIFT	4
> +#define HDCP_EP_FIL_VS_MASK	0x0c
> +#define HDCP_EP_FIL_VS_SHIFT	2
> +#define HDCP_EP_FIL_HS_MASK	0x03
> +#define HDCP_EP_FIL_HS_SHIFT	0
> +
> +/* HDMI_CTRL */
> +#define HDMI_CTRL_MUTE_MASK	0x0c
> +#define HDMI_CTRL_MUTE_SHIFT	2
> +#define HDMI_CTRL_MUTE_AUTO	0L
> +#define HDMI_CTRL_MUTE_OFF	1L
> +#define HDMI_CTRL_MUTE_ON	2L
> +#define HDMI_CTRL_HDCP_MASK	0x03
> +#define HDMI_CTRL_HDCP_SHIFT	0
> +#define HDMI_CTRL_HDCP_EESS	2L
> +#define HDMI_CTRL_HDCP_OESS	1L
> +#define HDMI_CTRL_HDCP_AUTO	0L
> +
> +/* CGU_DBG_SEL bits */
> +#define CGU_DBG_CLK_SEL_MASK	0x18
> +#define CGU_DBG_CLK_SEL_SHIFT	3
> +#define CGU_DBG_XO_FRO_SEL	BIT(2)
> +#define CGU_DBG_VDP_CLK_SEL	BIT(1)
> +#define CGU_DBG_PIX_CLK_SEL	BIT(0)
> +
> +/* REG_MAN_SUS_HDMI_SEL / REG_MAN_HDMI_SET bits */
> +#define MAN_DIS_OUT_BUF		BIT(7)
> +#define MAN_DIS_ANA_PATH	BIT(6)
> +#define MAN_DIS_HDCP		BIT(5)
> +#define MAN_DIS_TMDS_ENC	BIT(4)
> +#define MAN_DIS_TMDS_FLOW	BIT(3)
> +#define MAN_RST_HDCP		BIT(2)
> +#define MAN_RST_TMDS_ENC	BIT(1)
> +#define MAN_RST_TMDS_FLOW	BIT(0)
> +
> +/* Page 0x14 - Audio Extra control and debug */
> +#define REG_FIFO_LATENCY_VAL	0x1403
> +#define REG_AUDIO_CLOCK		0x1411
> +#define REG_TEST_NCTS_CTRL	0x1415
> +#define REG_TEST_AUDIO_FREQ	0x1426
> +#define REG_TEST_MODE		0x1437
> +
> +/* Audio Clock Configuration */
> +#define AUDIO_CLOCK_PLL_PD	BIT(7)	/* powerdown PLL */
> +#define AUDIO_CLOCK_SEL_MASK	0x7f
> +#define AUDIO_CLOCK_SEL_16FS	0L	/* 16*fs */
> +#define AUDIO_CLOCK_SEL_32FS	1L	/* 32*fs */
> +#define AUDIO_CLOCK_SEL_64FS	2L	/* 64*fs */
> +#define AUDIO_CLOCK_SEL_128FS	3L	/* 128*fs */
> +#define AUDIO_CLOCK_SEL_256FS	4L	/* 256*fs */
> +#define AUDIO_CLOCK_SEL_512FS	5L	/* 512*fs */
> +
> +/* Page 0x20: EDID and Hotplug Detect */
> +#define REG_EDID_IN_BYTE0	0x2000 /* EDID base */
> +#define REG_EDID_IN_VERSION	0x2080
> +#define REG_EDID_ENABLE		0x2081
> +#define REG_HPD_POWER		0x2084
> +#define REG_HPD_AUTO_CTRL	0x2085
> +#define REG_HPD_DURATION	0x2086
> +#define REG_RX_HPD_HEAC		0x2087
> +
> +/* EDID_ENABLE */
> +#define EDID_ENABLE_NACK_OFF	BIT(7)
> +#define EDID_ENABLE_EDID_ONLY	BIT(6)
> +#define EDID_ENABLE_B_EN	BIT(1)
> +#define EDID_ENABLE_A_EN	BIT(0)
> +
> +/* HPD Power */
> +#define HPD_POWER_BP_MASK	0x0c
> +#define HPD_POWER_BP_SHIFT	2
> +#define HPD_POWER_BP_LOW	0L
> +#define HPD_POWER_BP_HIGH	1L
> +#define HPD_POWER_EDID_ONLY	BIT(1)
> +
> +/* HPD Auto control */
> +#define HPD_AUTO_READ_EDID	BIT(7)
> +#define HPD_AUTO_HPD_F3TECH	BIT(5)
> +#define HPD_AUTO_HP_OTHER	BIT(4)
> +#define HPD_AUTO_HPD_UNSEL	BIT(3)
> +#define HPD_AUTO_HPD_ALL_CH	BIT(2)
> +#define HPD_AUTO_HPD_PRV_CH	BIT(1)
> +#define HPD_AUTO_HPD_NEW_CH	BIT(0)
> +
> +/* Page 0x21 - EDID content */
> +#define REG_EDID_IN_BYTE128	0x2100 /* CEA Extension block */
> +#define REG_EDID_IN_SPA_SUB	0x2180
> +#define REG_EDID_IN_SPA_AB_A	0x2181
> +#define REG_EDID_IN_SPA_CD_A	0x2182
> +#define REG_EDID_IN_CKSUM_A	0x2183
> +#define REG_EDID_IN_SPA_AB_B	0x2184
> +#define REG_EDID_IN_SPA_CD_B	0x2185
> +#define REG_EDID_IN_CKSUM_B	0x2186
> +
> +/* Page 0x30 - NV Configuration */
> +#define REG_RT_AUTO_CTRL	0x3000
> +#define REG_EQ_MAN_CTRL0	0x3001
> +#define REG_EQ_MAN_CTRL1	0x3002
> +#define REG_OUTPUT_CFG		0x3003
> +#define REG_MUTE_CTRL		0x3004
> +#define REG_SLAVE_ADDR		0x3005
> +#define REG_CMTP_REG6		0x3006
> +#define REG_CMTP_REG7		0x3007
> +#define REG_CMTP_REG8		0x3008
> +#define REG_CMTP_REG9		0x3009
> +#define REG_CMTP_REGA		0x300A
> +#define REG_CMTP_REGB		0x300B
> +#define REG_CMTP_REGC		0x300C
> +#define REG_CMTP_REGD		0x300D
> +#define REG_CMTP_REGE		0x300E
> +#define REG_CMTP_REGF		0x300F
> +#define REG_CMTP_REG10		0x3010
> +#define REG_CMTP_REG11		0x3011
> +
> +/* Page 0x80 - CEC */
> +#define REG_PWR_CONTROL		0x80F4
> +#define REG_OSC_DIVIDER		0x80F5
> +#define REG_EN_OSC_PERIOD_LSB	0x80F8
> +#define REG_CONTROL		0x80FF
> +
> +/* global interrupt flags (INT_FLG_CRL_TOP) */
> +#define INTERRUPT_AFE		BIT(7) /* AFE module */
> +#define INTERRUPT_HDCP		BIT(6) /* HDCP module */
> +#define INTERRUPT_AUDIO		BIT(5) /* Audio module */
> +#define INTERRUPT_INFO		BIT(4) /* Infoframe module */
> +#define INTERRUPT_MODE		BIT(3) /* HDMI mode module */
> +#define INTERRUPT_RATE		BIT(2) /* rate module */
> +#define INTERRUPT_DDC		BIT(1) /* DDC module */
> +#define INTERRUPT_SUS		BIT(0) /* SUS module */
> +
> +/* INT_FLG_CLR_HDCP bits */
> +#define MASK_HDCP_MTP		BIT(7) /* HDCP MTP busy */
> +#define MASK_HDCP_DLMTP		BIT(4) /* HDCP end download MTP to SRAM */
> +#define MASK_HDCP_DLRAM		BIT(3) /* HDCP end download keys from SRAM */
> +#define MASK_HDCP_ENC		BIT(2) /* HDCP ENC */
> +#define MASK_STATE_C5		BIT(1) /* HDCP State C5 reached */
> +#define MASK_AKSV		BIT(0) /* AKSV received (start of auth) */
> +
> +/* INT_FLG_CLR_RATE bits */
> +#define MASK_RATE_B_DRIFT	BIT(7) /* Rate measurement drifted */
> +#define MASK_RATE_B_ST		BIT(6) /* Rate measurement stability change */
> +#define MASK_RATE_B_ACT		BIT(5) /* Rate measurement activity change */
> +#define MASK_RATE_B_PST		BIT(4) /* Rate measreument presence change */
> +#define MASK_RATE_A_DRIFT	BIT(3) /* Rate measurement drifted */
> +#define MASK_RATE_A_ST		BIT(2) /* Rate measurement stability change */
> +#define MASK_RATE_A_ACT		BIT(1) /* Rate measurement presence change */
> +#define MASK_RATE_A_PST		BIT(0) /* Rate measreument presence change */
> +
> +/* INT_FLG_CLR_SUS (Start Up Sequencer) bits */
> +#define MASK_MPT		BIT(7) /* Config MTP end of process */
> +#define MASK_FMT		BIT(5) /* Video format changed */
> +#define MASK_RT_PULSE		BIT(4) /* End of termination resistance pulse */
> +#define MASK_SUS_END		BIT(3) /* SUS last state reached */
> +#define MASK_SUS_ACT		BIT(2) /* Activity of selected input changed */
> +#define MASK_SUS_CH		BIT(1) /* Selected input changed */
> +#define MASK_SUS_ST		BIT(0) /* SUS state changed */
> +
> +/* INT_FLG_CLR_DDC bits */
> +#define MASK_EDID_MTP		BIT(7) /* EDID MTP end of process */
> +#define MASK_DDC_ERR		BIT(6) /* master DDC error */
> +#define MASK_DDC_CMD_DONE	BIT(5) /* master DDC cmd send correct */
> +#define MASK_READ_DONE		BIT(4) /* End of down EDID read */
> +#define MASK_RX_DDC_SW		BIT(3) /* Output DDC switching finished */
> +#define MASK_HDCP_DDC_SW	BIT(2) /* HDCP DDC switching finished */
> +#define MASK_HDP_PULSE_END	BIT(1) /* End of Hot Plug Detect pulse */
> +#define MASK_DET_5V		BIT(0) /* Detection of +5V */
> +
> +/* INT_FLG_CLR_MODE bits */
> +#define MASK_HDMI_FLG		BIT(7) /* HDMI mode/avmute/encrypt/FIFO fail */
> +#define MASK_GAMUT		BIT(6) /* Gamut packet */
> +#define MASK_ISRC2		BIT(5) /* ISRC2 packet */
> +#define MASK_ISRC1		BIT(4) /* ISRC1 packet */
> +#define MASK_ACP		BIT(3) /* Audio Content Protection packet */
> +#define MASK_DC_NO_GCP		BIT(2) /* GCP not received in 5 frames */
> +#define MASK_DC_PHASE		BIT(1) /* deepcolor pixel phase needs update */
> +#define MASK_DC_MODE		BIT(0) /* deepcolor color depth changed */
> +
> +/* INT_FLG_CLR_INFO bits (Infoframe Change Status) */
> +#define MASK_MPS_IF		BIT(6) /* MPEG Source Product */
> +#define MASK_AUD_IF		BIT(5) /* Audio */
> +#define MASK_SPD_IF		BIT(4) /* Source Product Descriptor */
> +#define MASK_AVI_IF		BIT(3) /* Auxiliary Video IF */
> +#define MASK_VS_IF_OTHER_BK2	BIT(2) /* Vendor Specific (bank2) */
> +#define MASK_VS_IF_OTHER_BK1	BIT(1) /* Vendor Specific (bank1) */
> +#define MASK_VS_IF_HDMI		BIT(0) /* Vendor Specific (w/ HDMI LLC code) */
> +
> +/* INT_FLG_CLR_AUDIO bits */
> +#define MASK_AUDIO_FREQ_FLG	BIT(5) /* Audio freq change */
> +#define MASK_AUDIO_FLG		BIT(4) /* DST, OBA, HBR, ASP change */
> +#define MASK_MUTE_FLG		BIT(3) /* Audio Mute */
> +#define MASK_CH_STATE		BIT(2) /* Channel status */
> +#define MASK_UNMUTE_FIFO	BIT(1) /* Audio Unmute */
> +#define MASK_ERROR_FIFO_PT	BIT(0) /* Audio FIFO pointer error */
> +
> +/* INT_FLG_CLR_AFE bits */
> +#define MASK_AFE_WDL_UNLOCKED	BIT(7) /* Wordlocker was unlocked */
> +#define MASK_AFE_GAIN_DONE	BIT(6) /* Gain calibration done */
> +#define MASK_AFE_OFFSET_DONE	BIT(5) /* Offset calibration done */
> +#define MASK_AFE_ACTIVITY_DET	BIT(4) /* Activity detected on data */
> +#define MASK_AFE_PLL_LOCK	BIT(3) /* TMDS PLL is locked */
> +#define MASK_AFE_TRMCAL_DONE	BIT(2) /* Termination calibration done */
> +#define MASK_AFE_ASU_STATE	BIT(1) /* ASU state is reached */
> +#define MASK_AFE_ASU_READY	BIT(0) /* AFE calibration done: TMDS ready */
> +
> +/* Audio Output */
> +#define AUDCFG_CLK_INVERT	BIT(7)	/* invert A_CLK polarity */
> +#define AUDCFG_TEST_TONE	BIT(6)	/* enable test tone generator */
> +#define AUDCFG_BUS_SHIFT	5
> +#define AUDCFG_BUS_I2S		0L
> +#define AUDCFG_BUS_SPDIF	1L
> +#define AUDCFG_I2SW_SHIFT	4
> +#define AUDCFG_I2SW_16		0L
> +#define AUDCFG_I2SW_32		1L
> +#define AUDCFG_AUTO_MUTE_EN	BIT(3)	/* Enable Automatic audio mute */
> +#define AUDCFG_HBR_SHIFT	2
> +#define AUDCFG_HBR_STRAIGHT	0L	/* straight via AP0 */
> +#define AUDCFG_HBR_DEMUX	1L	/* demuxed via AP0:AP3 */
> +#define AUDCFG_TYPE_MASK	0x03
> +#define AUDCFG_TYPE_SHIFT	0
> +#define AUDCFG_TYPE_DST		3L	/* Direct Stream Transfer (DST) */
> +#define AUDCFG_TYPE_OBA		2L	/* One Bit Audio (OBA) */
> +#define AUDCFG_TYPE_HBR		1L	/* High Bit Rate (HBR) */
> +#define AUDCFG_TYPE_PCM		0L	/* Audio samples */
> +
> +/* Video Formatter */
> +#define OF_VP_ENABLE		BIT(7)	/* VP[35:0]/HS/VS/DE/CLK */
> +#define OF_BLK			BIT(4)	/* blanking codes */
> +#define OF_TRC			BIT(3)	/* timing codes (SAV/EAV) */
> +#define OF_FMT_MASK		0x3
> +#define OF_FMT_444		0L	/* RGB444/YUV444 */
> +#define OF_FMT_422_SMPT		1L	/* YUV422 semi-planar */
> +#define OF_FMT_422_CCIR		2L	/* YUV422 CCIR656 */
> +
> +/* HS/HREF output control */
> +#define HS_HREF_DELAY_MASK	0xf0
> +#define HS_HREF_DELAY_SHIFT	4	/* Pixel delay (-8..+7) */
> +#define HS_HREF_PXQ_SHIFT	3	/* Timing codes from HREF */
> +#define HS_HREF_INV_SHIFT	2	/* polarity (1=invert) */
> +#define HS_HREF_SEL_MASK	0x03
> +#define HS_HREF_SEL_SHIFT	0
> +#define HS_HREF_SEL_HS_VHREF	0L	/* HS from VHREF */
> +#define HS_HREF_SEL_HREF_VHREF	1L	/* HREF from VHREF */
> +#define HS_HREF_SEL_HREF_HDMI	2L	/* HREF from HDMI */
> +#define HS_HREF_SEL_NONE	3L	/* not generated */
> +
> +/* VS output control */
> +#define VS_VREF_DELAY_MASK	0xf0
> +#define VS_VREF_DELAY_SHIFT	4	/* Pixel delay (-8..+7) */
> +#define VS_VREF_INV_SHIFT	2	/* polarity (1=invert) */
> +#define VS_VREF_SEL_MASK	0x03
> +#define VS_VREF_SEL_SHIFT	0
> +#define VS_VREF_SEL_VS_VHREF	0L	/* VS from VHREF */
> +#define VS_VREF_SEL_VREF_VHREF	1L	/* VREF from VHREF */
> +#define VS_VREF_SEL_VREF_HDMI	2L	/* VREF from HDMI */
> +#define VS_VREF_SEL_NONE	3L	/* not generated */
> +
> +/* DE/FREF output control */
> +#define DE_FREF_DELAY_MASK	0xf0
> +#define DE_FREF_DELAY_SHIFT	4	/* Pixel delay (-8..+7) */
> +#define DE_FREF_DE_PXQ_SHIFT	3	/* Timing codes from DE */
> +#define DE_FREF_INV_SHIFT	2	/* polarity (1=invert) */
> +#define DE_FREF_SEL_MASK	0x03
> +#define DE_FREF_SEL_SHIFT	0
> +#define DE_FREF_SEL_DE_VHREF	0L	/* DE from VHREF (HREF and not(VREF) */
> +#define DE_FREF_SEL_FREF_VHREF	1L	/* FREF from VHREF */
> +#define DE_FREF_SEL_FREF_HDMI	2L	/* FREF from HDMI */
> +#define DE_FREF_SEL_NONE	3L	/* not generated */
> +
> +/* HDMI_SOFT_RST bits */
> +#define RESET_DC		BIT(7)	/* Reset deep color module */
> +#define RESET_HDCP		BIT(6)	/* Reset HDCP module */
> +#define RESET_KSV		BIT(5)	/* Reset KSV-FIFO */
> +#define RESET_SCFG		BIT(4)	/* Reset HDCP and repeater function */
> +#define RESET_HCFG		BIT(3)	/* Reset HDCP DDC part */
> +#define RESET_PA		BIT(2)	/* Reset polarity adjust */
> +#define RESET_EP		BIT(1)	/* Reset Error protection */
> +#define RESET_TMDS		BIT(0)	/* Reset TMDS (calib, encoding, flow) */
> +
> +/* HDMI_INFO_RST bits */
> +#define NACK_HDCP		BIT(7)	/* No ACK on HDCP request */
> +#define RESET_FIFO		BIT(4)	/* Reset Audio FIFO control */
> +#define RESET_GAMUT		BIT(3)	/* Clear Gamut packet */
> +#define RESET_AI		BIT(2)	/* Clear ACP and ISRC packets */
> +#define RESET_IF		BIT(1)	/* Clear all Audio infoframe packets */
> +#define RESET_AUDIO		BIT(0)	/* Reset Audio FIFO control */
> +
> +/* HDCP_BCAPS bits */
> +#define HDCP_HDMI		BIT(7)	/* HDCP suports HDMI (vs DVI only) */
> +#define HDCP_REPEATER		BIT(6)	/* HDCP supports repeater function */
> +#define HDCP_READY		BIT(5)	/* set by repeater function */
> +#define HDCP_FAST		BIT(4)	/* Up to 400kHz */
> +#define HDCP_11			BIT(1)	/* HDCP 1.1 supported */
> +#define HDCP_FAST_REAUTH	BIT(0)	/* fast reauthentication supported */
> +
> +/* Audio output formatter */
> +#define AUDIO_LAYOUT_SP_FLAG	BIT(2)	/* sp flag used by FIFO */
> +#define AUDIO_LAYOUT_MANUAL	BIT(1)	/* manual layout (vs per pkt) */
> +#define AUDIO_LAYOUT_LAYOUT1	BIT(0)  /* Layout1: AP0-3 vs Layout0:AP0 */
> +
> +/* masks for interrupt status registers */
> +#define MASK_SUS_STATUS		0x1F
> +#define LAST_STATE_REACHED	0x1B
> +#define MASK_CLK_STABLE		0x04
> +#define MASK_CLK_ACTIVE		0x02
> +#define MASK_SUS_STATE		0x10
> +#define MASK_SR_FIFO_FIFO_CTRL	0x30
> +#define MASK_AUDIO_FLAG		0x10
> +
> +/* Rate measurement */
> +#define RATE_REFTIM_ENABLE	0x01
> +#define CLK_MIN_RATE		0x0057e4
> +#define CLK_MAX_RATE		0x0395f8
> +#define WDL_CFG_VAL		0x82
> +#define DC_FILTER_VAL		0x31
> +
> +/* Infoframe */
> +#define VS_HDMI_IF_UPDATE	0x0200
> +#define VS_HDMI_IF		0x0201
> +#define VS_BK1_IF_UPDATE	0x0220
> +#define VS_BK1_IF		0x0221
> +#define VS_BK2_IF_UPDATE	0x0240
> +#define VS_BK2_IF		0x0241
> +#define AVI_IF_UPDATE		0x0260
> +#define AVI_IF			0x0261
> +#define SPD_IF_UPDATE		0x0280
> +#define SPD_IF			0x0281
> +#define AUD_IF_UPDATE		0x02a0
> +#define AUD_IF			0x02a1
> +#define MPS_IF_UPDATE		0x02c0
> +#define MPS_IF			0x02c1
> +
> +/* Audio formats */
> +static const char * const audtype_names[] = {
> +	"PCM",			/* PCM Samples */
> +	"HBR",			/* High Bit Rate Audio */
> +	"OBA",			/* One-Bit Audio */
> +	"DST"			/* Direct Stream Transfer */
> +};
> +
> +/* Audio output port formats */
> +enum audfmt_types {
> +	AUDFMT_TYPE_DISABLED = 0,
> +	AUDFMT_TYPE_I2S,
> +	AUDFMT_TYPE_SPDIF,
> +};
> +static const char * const audfmt_names[] = {
> +	"disabled",

"Disabled"

> +	"I2S",
> +	"SPDIF",
> +};
> +
> +/* Video input formats */
> +static const char * const colorspace_names[] = {
> +	"RGB", "YUV422", "YUV444", "YUV420", "", "", "", "",
> +};
> +static const char * const colorimetry_names[] = {
> +	"", "ITU601", "ITU709", "Extended",
> +};
> +static const char * const rgb_quantization_range_names[] = {
> +	"Automatic",
> +	"limited range (16-235)",

"Limited Range (16-235)"
"Full Range (0-255)"

> +	"full range (0-255)",
> +};
> +
> +/* Video output port formats */
> +static const char * const vidfmt_names[] = {
> +	"RGB444/YUV444",	/* RGB/YUV444 16bit data bus, 8bpp */
> +	"YUV422 semi-planar",	/* YUV422 16bit data base, 8bpp */
> +	"YUV422 CCIR656",	/* BT656 (YUV 8bpp 2 clock per pixel) */
> +	"invalid",

"Invalid"

> +};
> +
> +/*
> + * Video Output formats
> + * There are 24 video output pins on TDA19971 and 36 on TDA19973 supporting
> + * the following output formats:
> + * - RGB444
> + * - YUV444
> + * - YUV422 semi-planar based on ITU-R BT.601
> + * - YUV422 ITU-R BT.656
> + *
> + * TDA19971 can output 3x8bits per pixel
> + * TDA19973 can output 3x8, 3x10, or 3x12bit per pixel
> + *
> + * Deep color modes (3x10 or 3x12 bits) are possible in any case.
> + *
> + * Reference: NXP AN1206 - TDA19971_TDA19973 receiver HW recommendation: 3.3.4
> + */
> +static u32 tda19971_video_formats[] = {
> +	/* 24bit RGB444: 1 pixel in 1x24bit sample: VP[23:0] */
> +	MEDIA_BUS_FMT_RGB888_1X24,
> +	/* 24bit YUV444: 1 pixel in 1x24bit sample: VP[23:0] */
> +	MEDIA_BUS_FMT_YUV8_1X24,
> +	/* 24bit YUV422: 1 pixel in 1x24bit sample: VP[23:12]/VP[11:0] */
> +	MEDIA_BUS_FMT_UYVY12_1X24,
> +	/* 20bit YUV422: 1 pixel in 1x20bit sample: VP[23:14]/VP[11:2] */
> +	MEDIA_BUS_FMT_UYVY10_1X20,
> +	/* 16bit YUV422: 1 pixel in 1x16bit sample: VP[23:16]/VP[15:8] */
> +	MEDIA_BUS_FMT_UYVY8_1X16,
> +	/* 12bit CCIR656: 1 pixel in 2x12bit samples: VP[23:12] */
> +	MEDIA_BUS_FMT_UYVY12_2X12,
> +	/* 10bit CCIR656: 1 pixel in 2x10bit samples: VP[23:14] */
> +	MEDIA_BUS_FMT_UYVY10_2X10,
> +	/* 8bit CCIR656: 1 pixel in 2x8bit samples: VP[23:16] */
> +	MEDIA_BUS_FMT_UYVY8_2X8,
> +};
> +static u32 tda19973_video_formats[] = {
> +	/* 36bit RGB444: 1 pixel in 1x36bit sample on VP[35:0] */
> +	MEDIA_BUS_FMT_RGB121212_1X36,
> +	/* 36bit YUV444 1 pixel in 1x36bit sample on VP[35:0] */
> +	MEDIA_BUS_FMT_YUV12_1X36,
> +	/* 24bit YUV422: 1 pixel in 1x24bit sample on VP[35:24]/VP[11:0]  */
> +	MEDIA_BUS_FMT_UYVY12_1X24,
> +	/* 12bit CCIR656: 1 pixel in 2x12bit samples on VP[11:0] */
> +	MEDIA_BUS_FMT_UYVY12_2X12,
> +};
> +
> +/*
> + * Colorspace conversion matrices
> + */
> +struct color_matrix_coefs {
> +	const char *name;
> +	/* Input offsets */
> +	s16 offint1;
> +	s16 offint2;
> +	s16 offint3;
> +	/* Coeficients */
> +	s16 p11coef;
> +	s16 p12coef;
> +	s16 p13coef;
> +	s16 p21coef;
> +	s16 p22coef;
> +	s16 p23coef;
> +	s16 p31coef;
> +	s16 p32coef;
> +	s16 p33coef;
> +	/* Output offsets */
> +	s16 offout1;
> +	s16 offout2;
> +	s16 offout3;
> +};
> +
> +enum {
> +	ITU709_RGBFULL,
> +	ITU601_RGBFULL,
> +	RGBLIMITED_RGBFULL,
> +	RGBLIMITED_ITU601,
> +	RGBLIMITED_ITU709,
> +	RGBFULL_ITU601,
> +	RGBFULL_ITU709,
> +};
> +
> +/* NB: 4096 is 1.0 using fixed point numbers */
> +static const struct color_matrix_coefs conv_matrix[] = {
> +	{
> +		"YUV709 -> RGB full",
> +		 -256, -2048,  -2048,
> +		 4769, -2183,   -873,
> +		 4769,  7343,      0,
> +		 4769,     0,   8652,
> +		    0,     0,      0,
> +	},
> +	{
> +		"YUV601 -> RGB full",
> +		 -256, -2048,  -2048,
> +		 4769, -3330,  -1602,
> +		 4769,  6538,      0,
> +		 4769,     0,   8264,
> +		  256,   256,    256,
> +	},
> +	{
> +		"RGB limited -> RGB full",
> +		 -256,  -256,   -256,
> +		    0,  4769,      0,
> +		    0,     0,   4769,
> +		 4769,     0,      0,
> +		    0,     0,      0,
> +	},
> +	{
> +		"RGB limited -> ITU601",
> +		 -256,  -256,   -256,
> +		 2404,  1225,    467,
> +		-1754,  2095,   -341,
> +		-1388,  -707,   2095,
> +		  256,  2048,   2048,
> +	},
> +	{
> +		"RGB limited -> ITU709",
> +		 -256,  -256,   -256,
> +		 2918,   867,    295,
> +		-1894,  2087,   -190,
> +		-1607,  -477,   2087,
> +		  256,  2048,   2048,
> +	},
> +	{
> +		"RGB full -> ITU601",
> +		    0,     0,      0,
> +		 2065,  1052,    401,
> +		-1506,  1799,   -293,
> +		-1192,  -607,   1799,
> +		  256,  2048,   2048,
> +	},
> +	{
> +		"RGB full -> ITU709",
> +		    0,     0,      0,
> +		 2506,   745,    253,
> +		-1627,  1792,   -163,
> +		-1380,  -410,   1792,
> +		  256,  2048,   2048,
> +	},
> +};
> +
> +static const struct v4l2_dv_timings_cap tda1997x_dv_timings_cap = {
> +	.type = V4L2_DV_BT_656_1120,
> +	/* keep this initialization for compatibility with GCC < 4.4.6 */
> +	.reserved = { 0 },
> +
> +	V4L2_INIT_BT_TIMINGS(
> +		640, 1920,			/* min/max width */
> +		480, 1080,			/* min/max height */
> +		13000000, 165000000,		/* min/max pixelclock */
> +		/* standards */
> +		V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
> +			V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
> +		/* capabilities */
> +		V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE |
> +			V4L2_DV_BT_CAP_REDUCED_BLANKING |
> +			V4L2_DV_BT_CAP_CUSTOM
> +	)
> +};
> +
> +/*
> + * Video Input formats
> + */
> +static const struct v4l2_dv_timings tda1997x_hdmi_modes[] = {
> +	V4L2_DV_BT_DMT_640X350P85,
> +	V4L2_DV_BT_DMT_640X400P85,
> +	V4L2_DV_BT_DMT_640X480P60,
> +	V4L2_DV_BT_DMT_640X480P72,
> +	V4L2_DV_BT_DMT_640X480P75,
> +	V4L2_DV_BT_DMT_720X400P85,
> +	V4L2_DV_BT_CEA_720X480I59_94,
> +	V4L2_DV_BT_CEA_720X480P59_94,
> +	V4L2_DV_BT_CEA_720X576I50,
> +	V4L2_DV_BT_CEA_720X576P50,
> +	V4L2_DV_BT_DMT_800X600P60,
> +	V4L2_DV_BT_DMT_800X600P72,
> +	V4L2_DV_BT_DMT_800X600P75,
> +	V4L2_DV_BT_DMT_800X600P85,
> +	V4L2_DV_BT_DMT_1024X768P60,
> +	V4L2_DV_BT_DMT_1024X768P70,
> +	V4L2_DV_BT_DMT_1024X768P75,
> +	V4L2_DV_BT_DMT_1024X768P85,
> +	V4L2_DV_BT_CEA_1280X720P24,
> +	V4L2_DV_BT_CEA_1280X720P25,
> +	V4L2_DV_BT_CEA_1280X720P30,
> +	V4L2_DV_BT_CEA_1280X720P50,
> +	V4L2_DV_BT_CEA_1280X720P60,
> +	V4L2_DV_BT_DMT_1280X768P60_RB,
> +	V4L2_DV_BT_DMT_1280X768P75,
> +	V4L2_DV_BT_DMT_1280X768P85,
> +	V4L2_DV_BT_DMT_1280X960P60,
> +	V4L2_DV_BT_DMT_1280X1024P60,
> +	V4L2_DV_BT_DMT_1280X1024P85,
> +	V4L2_DV_BT_DMT_1280X1024P75,
> +	V4L2_DV_BT_DMT_1360X768P60,
> +	V4L2_DV_BT_DMT_1440X900P60,
> +	V4L2_DV_BT_DMT_1400X1050P60_RB,
> +	V4L2_DV_BT_DMT_1400X1050P60,
> +	V4L2_DV_BT_DMT_1600X1200P60,
> +	V4L2_DV_BT_DMT_1680X1050P60_RB,
> +	V4L2_DV_BT_CEA_1920X1080P24,
> +	V4L2_DV_BT_CEA_1920X1080P25,
> +	V4L2_DV_BT_CEA_1920X1080P30,
> +	V4L2_DV_BT_CEA_1920X1080I50,
> +	V4L2_DV_BT_CEA_1920X1080P50,
> +	V4L2_DV_BT_CEA_1920X1080I60,
> +	V4L2_DV_BT_CEA_1920X1080P60,
> +	V4L2_DV_BT_DMT_1920X1200P60_RB,
> +};
> +
> +/* regulator supplies */
> +static const char * const tda1997x_supply_name[] = {
> +	"DOVDD", /* Digital I/O supply */
> +	"DVDD",  /* Digital Core supply */
> +	"AVDD",  /* Analog supply */
> +};
> +
> +#define TDA1997X_NUM_SUPPLIES ARRAY_SIZE(tda1997x_supply_name)
> +
> +enum tda1997x_type {
> +	TDA19971,
> +	TDA19973,
> +};
> +
> +enum tda1997x_hdmi_pads {
> +	TDA1997X_PAD_SOURCE,
> +	TDA1997X_NUM_PADS,
> +};
> +
> +struct tda1997x_chip_info {
> +	enum tda1997x_type type;
> +	const char *name;
> +	const u32 *formats;
> +	unsigned int nformats;
> +};
> +
> +struct tda1997x_state {
> +	const struct tda1997x_chip_info *info;
> +	struct tda1997x_platform_data pdata;
> +	struct i2c_client *client;
> +	struct i2c_client *client_cec;
> +	struct v4l2_subdev sd;
> +	struct regulator_bulk_data supplies[TDA1997X_NUM_SUPPLIES];
> +	struct media_pad pads[TDA1997X_NUM_PADS];
> +	struct mutex lock;
> +	struct mutex page_lock;
> +	char page;
> +
> +	/* detected info from chip */
> +	int chip_revision;
> +	char port_30bit;
> +	char output_2p5;
> +	char tmdsb_clk;
> +	char tmdsb_soc;
> +
> +	/* status info */
> +	char hdmi_status;
> +	char mptrw_in_progress;
> +	char state_c5_reached;
> +	char activity_status;
> +	char input_detect[2];
> +
> +	/* video */
> +	enum hdmi_colorspace colorspace;
> +	enum hdmi_colorimetry colorimetry;
> +	enum hdmi_quantization_range range;
> +	enum hdmi_content_type content;
> +	u32 rgb_quantization_range;
> +	struct v4l2_dv_timings timings;
> +	const struct v4l2_dv_timings *detected_timings;
> +	int fps;
> +	const struct color_matrix_coefs *conv;
> +	u32 code;
> +	enum v4l2_mbus_type bus_type;
> +	u8 vid_fmt;
> +
> +	/* controls */
> +	struct v4l2_ctrl_handler hdl;
> +	struct v4l2_ctrl *detect_tx_5v_ctrl;
> +	struct v4l2_ctrl *rgb_quantization_range_ctrl;
> +
> +	/* audio */
> +	u8  audio_ch_alloc;
> +	int audio_samplerate;
> +	int audio_channels;
> +	int audio_samplesize;
> +	int audio_type;
> +	struct mutex audio_lock;
> +	struct snd_pcm_substream *audio_stream;
> +
> +	/* EDID */
> +	struct {
> +		u8 edid[256];
> +		u32 present;
> +		unsigned int blocks;
> +	} edid;
> +	struct delayed_work delayed_work_enable_hpd;
> +};
> +
> +static const struct v4l2_event tda1997x_ev_fmt = {
> +	.type = V4L2_EVENT_SOURCE_CHANGE,
> +	.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
> +};
> +
> +static const struct tda1997x_chip_info tda1997x_chip_info[] = {
> +	[TDA19971] = {
> +		.type = TDA19971,
> +		.name = "tda19971",
> +		.formats = tda19971_video_formats,
> +		.nformats = ARRAY_SIZE(tda19971_video_formats),
> +	},
> +	[TDA19973] = {
> +		.type = TDA19973,
> +		.name = "tda19973",
> +		.formats = tda19973_video_formats,
> +		.nformats = ARRAY_SIZE(tda19973_video_formats),
> +	},
> +};
> +
> +static inline bool is_sd(int h)
> +{
> +	return ((h == 480) || (h == 576) || (h == 240) || (h == 288));
> +}
> +
> +static inline bool is_hd(int h)
> +{
> +	return ((h == 720) || (h == 1080));
> +}
> +
> +static inline struct tda1997x_state *to_state(struct v4l2_subdev *sd)
> +{
> +	return container_of(sd, struct tda1997x_state, sd);
> +}
> +
> +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
> +{
> +	return &container_of(ctrl->handler, struct tda1997x_state, hdl)->sd;
> +}
> +
> +static int tda1997x_cec_read(struct v4l2_subdev *sd, u8 reg)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	int val;
> +
> +	val = i2c_smbus_read_byte_data(state->client_cec, reg);
> +	if (val < 0) {
> +		v4l_err(state->client, "read reg error: reg=%2x\n", reg);
> +		val = -1;
> +	}
> +
> +	return val;
> +}
> +
> +static int tda1997x_cec_write(struct v4l2_subdev *sd, u8 reg, u8 val)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	int ret = 0;
> +
> +	ret = i2c_smbus_write_byte_data(state->client_cec, reg, val);
> +	if (ret < 0) {
> +		v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n",
> +			reg, val);
> +		ret = -1;
> +	}
> +
> +	return ret;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * I2C transfer
> + */
> +
> +static int tda1997x_setpage(struct v4l2_subdev *sd, u8 page)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	int ret;
> +
> +	if (state->page != page) {
> +		ret = i2c_smbus_write_byte_data(state->client,
> +			REG_CURPAGE_00H, page);
> +		if (ret < 0) {
> +			v4l_err(state->client,
> +				"write reg error:reg=%2x,val=%2x\n",
> +				REG_CURPAGE_00H, page);
> +			return ret;
> +		}
> +		state->page = page;
> +	}
> +	return 0;
> +}
> +
> +static inline int io_read(struct v4l2_subdev *sd, u16 reg)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	int val;
> +
> +	mutex_lock(&state->page_lock);
> +	if (tda1997x_setpage(sd, reg >> 8)) {
> +		val = -1;
> +		goto out;
> +	}
> +
> +	val = i2c_smbus_read_byte_data(state->client, reg&0xff);
> +	if (val < 0) {
> +		v4l_err(state->client, "read reg error: reg=%2x\n", reg & 0xff);
> +		val = -1;
> +		goto out;
> +	}
> +
> +out:
> +	mutex_unlock(&state->page_lock);
> +	return val;
> +}
> +
> +static inline long io_read16(struct v4l2_subdev *sd, u16 reg)
> +{
> +	u8 val;
> +	long lval = 0;
> +
> +	val = io_read(sd, reg);
> +	if (val < 0)
> +		return val;
> +	lval |= (val << 8);
> +	val = io_read(sd, reg + 1);
> +	if (val < 0)
> +		return val;
> +	lval |= val;
> +
> +	return lval;
> +}
> +
> +static inline long io_read24(struct v4l2_subdev *sd, u16 reg)
> +{
> +	u8 val;
> +	long lval = 0;
> +
> +	val = io_read(sd, reg);
> +	if (val < 0)
> +		return val;
> +	lval |= (val << 16);
> +	val = io_read(sd, reg + 1);
> +	if (val < 0)
> +		return val;
> +	lval |= (val << 8);
> +	val = io_read(sd, reg + 2);
> +	if (val < 0)
> +		return val;
> +	lval |= val;
> +
> +	return lval;
> +}
> +
> +static unsigned int io_readn(struct v4l2_subdev *sd, u16 reg, u8 len, u8 *data)
> +{
> +	int i;
> +	int sz = 0;
> +	u8 val;
> +
> +	for (i = 0; i < len; i++) {
> +		val = io_read(sd, reg + i);
> +		if (val < 0)
> +			break;
> +		data[i] = val;
> +		sz++;
> +	}
> +
> +	return sz;
> +}
> +
> +static int io_write(struct v4l2_subdev *sd, u16 reg, u8 val)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	s32 ret = 0;
> +
> +	mutex_lock(&state->page_lock);
> +	if (tda1997x_setpage(sd, reg >> 8)) {
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	ret = i2c_smbus_write_byte_data(state->client, reg & 0xff, val);
> +	if (ret < 0) {
> +		v4l_err(state->client, "write reg error:reg=%2x,val=%2x\n",
> +			reg&0xff, val);
> +		ret = -1;
> +		goto out;
> +	}
> +
> +out:
> +	mutex_unlock(&state->page_lock);
> +	return ret;
> +}
> +
> +static int io_write16(struct v4l2_subdev *sd, u16 reg, u16 val)
> +{
> +	int ret;
> +
> +	ret = io_write(sd, reg, (val >> 8) & 0xff);
> +	if (ret < 0)
> +		return ret;
> +	ret = io_write(sd, reg + 1, val & 0xff);
> +	if (ret < 0)
> +		return ret;
> +	return 0;
> +}
> +
> +static int io_write24(struct v4l2_subdev *sd, u16 reg, u32 val)
> +{
> +	int ret;
> +
> +	ret = io_write(sd, reg, (val >> 16) & 0xff);
> +	if (ret < 0)
> +		return ret;
> +	ret = io_write(sd, reg + 1, (val >> 8) & 0xff);
> +	if (ret < 0)
> +		return ret;
> +	ret = io_write(sd, reg + 2, val & 0xff);
> +	if (ret < 0)
> +		return ret;
> +	return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Hotplug
> + */
> +
> +enum hpd_mode {
> +	HPD_LOW_BP,	/* HPD low and pulse of at least 100ms */
> +	HPD_LOW_OTHER,	/* HPD low and pulse of at least 100ms */
> +	HPD_HIGH_BP,	/* HIGH */
> +	HPD_HIGH_OTHER,
> +	HPD_PULSE,	/* HPD low pulse */
> +};
> +
> +/* manual HPD (Hot Plug Detect) control */
> +static int tda1997x_manual_hpd(struct v4l2_subdev *sd, enum hpd_mode mode)
> +{
> +	u8 hpd_auto, hpd_pwr, hpd_man;
> +
> +	hpd_auto = io_read(sd, REG_HPD_AUTO_CTRL);
> +	hpd_pwr = io_read(sd, REG_HPD_POWER);
> +	hpd_man = io_read(sd, REG_HPD_MAN_CTRL);
> +
> +	/* mask out unused bits */
> +	hpd_man &= (HPD_MAN_CTRL_HPD_PULSE |
> +		    HPD_MAN_CTRL_5VEN |
> +		    HPD_MAN_CTRL_HPD_B |
> +		    HPD_MAN_CTRL_HPD_A);
> +
> +	switch (mode) {
> +	/* HPD low and pulse of at least 100ms */
> +	case HPD_LOW_BP:
> +		/* hpd_bp=0 */
> +		hpd_pwr &= ~HPD_POWER_BP_MASK;
> +		/* disable HPD_A and HPD_B */
> +		hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B);
> +		io_write(sd, REG_HPD_POWER, hpd_pwr);
> +		io_write(sd, REG_HPD_MAN_CTRL, hpd_man);
> +		break;
> +	/* HPD high */
> +	case HPD_HIGH_BP:
> +		/* hpd_bp=1 */
> +		hpd_pwr &= ~HPD_POWER_BP_MASK;
> +		hpd_pwr |= 1 << HPD_POWER_BP_SHIFT;
> +		io_write(sd, REG_HPD_POWER, hpd_pwr);
> +		break;
> +	/* HPD low and pulse of at least 100ms */
> +	case HPD_LOW_OTHER:
> +		/* disable HPD_A and HPD_B */
> +		hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B);
> +		/* hp_other=0 */
> +		hpd_auto &= ~HPD_AUTO_HP_OTHER;
> +		io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto);
> +		io_write(sd, REG_HPD_MAN_CTRL, hpd_man);
> +		break;
> +	/* HPD high */
> +	case HPD_HIGH_OTHER:
> +		hpd_auto |= HPD_AUTO_HP_OTHER;
> +		io_write(sd, REG_HPD_AUTO_CTRL, hpd_auto);
> +		break;
> +	/* HPD low pulse */
> +	case HPD_PULSE:
> +		/* disable HPD_A and HPD_B */
> +		hpd_man &= ~(HPD_MAN_CTRL_HPD_A | HPD_MAN_CTRL_HPD_B);
> +		io_write(sd, REG_HPD_MAN_CTRL, hpd_man);
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static void tda1997x_delayed_work_enable_hpd(struct work_struct *work)
> +{
> +	struct delayed_work *dwork = to_delayed_work(work);
> +	struct tda1997x_state *state = container_of(dwork,
> +						    struct tda1997x_state,
> +						    delayed_work_enable_hpd);
> +	struct v4l2_subdev *sd = &state->sd;
> +
> +	v4l2_dbg(2, debug, sd, "%s:\n", __func__);
> +
> +	/* Set HPD high */
> +	tda1997x_manual_hpd(sd, HPD_HIGH_OTHER);
> +	tda1997x_manual_hpd(sd, HPD_HIGH_BP);
> +
> +	state->edid.present = 1;
> +}
> +
> +static void tda1997x_disable_edid(struct v4l2_subdev *sd)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	v4l2_dbg(1, debug, sd, "%s\n", __func__);
> +	cancel_delayed_work_sync(&state->delayed_work_enable_hpd);
> +
> +	/* Set HPD low */
> +	tda1997x_manual_hpd(sd, HPD_LOW_BP);
> +}
> +
> +static void tda1997x_enable_edid(struct v4l2_subdev *sd)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	v4l2_dbg(1, debug, sd, "%s\n", __func__);
> +
> +	/* Enable hotplug after 100ms */
> +	schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10);
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Signal Control
> + */
> +
> +/*
> + * The color conversion matrix will convert between the colorimetry of the
> + * HDMI input to the desired output format RGB|YUV. RGB output is to be
> + * full-range and YUV is to be limited range.
> + *
> + * RGB full-range uses values from 0 to 255 which is recommended on a monitor
> + * and RGB Limited uses values from 16 to 236 (16=black, 235=white) which is
> + * typically recommended on a TV.
> + */
> +static int
> +tda1997x_configure_csc(struct v4l2_subdev *sd)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	/* Blanking code values depend on output colorspace (RGB or YUV) */
> +	struct blanking_codes {
> +		s16 code_gy;
> +		s16 code_bu;
> +		s16 code_rv;
> +	};
> +	static const struct blanking_codes rgb_blanking = { 64, 64, 64 };
> +	static const struct blanking_codes yuv_blanking = { 64, 512, 512 };
> +	const struct blanking_codes *blanking_codes = NULL;
> +	u8 reg;
> +
> +	v4l_dbg(1, debug, state->client, "input:%s quant:%s output:%s\n",
> +		colorspace_names[state->colorspace],
> +		rgb_quantization_range_names[state->rgb_quantization_range],
> +		vidfmt_names[state->vid_fmt]);
> +	state->conv = NULL;
> +	switch (state->vid_fmt) {
> +	/* RGB output */
> +	case OF_FMT_444:
> +		blanking_codes = &rgb_blanking;
> +		if (state->colorspace == HDMI_COLORSPACE_RGB) {
> +			if (state->range == HDMI_QUANTIZATION_RANGE_LIMITED)
> +				state->conv = &conv_matrix[RGBLIMITED_RGBFULL];
> +		} else {
> +			if (state->colorimetry == HDMI_COLORIMETRY_ITU_709)
> +				state->conv = &conv_matrix[ITU709_RGBFULL];
> +			else if (state->colorimetry == HDMI_COLORIMETRY_ITU_601)
> +				state->conv = &conv_matrix[ITU601_RGBFULL];
> +		}
> +		break;
> +
> +	/* YUV output */
> +	case OF_FMT_422_SMPT: /* semi-planar */
> +	case OF_FMT_422_CCIR: /* CCIR656 */
> +		blanking_codes = &yuv_blanking;
> +		if ((state->colorspace == HDMI_COLORSPACE_RGB) &&
> +		    (state->range == HDMI_QUANTIZATION_RANGE_FULL)) {
> +			if (is_sd(state->timings.bt.height))
> +				state->conv = &conv_matrix[RGBFULL_ITU601];
> +			else
> +				state->conv = &conv_matrix[RGBFULL_ITU709];
> +		} else if ((state->colorspace == HDMI_COLORSPACE_RGB) &&
> +			   (state->range == HDMI_QUANTIZATION_RANGE_LIMITED)) {
> +			if (is_sd(state->timings.bt.height))
> +				state->conv = &conv_matrix[RGBLIMITED_ITU601];
> +			else
> +				state->conv = &conv_matrix[RGBLIMITED_ITU709];
> +		}
> +		break;
> +	}
> +
> +	if (state->conv) {
> +		v4l_dbg(1, debug, state->client, "%s\n",
> +			state->conv->name);
> +		/* enable matrix conversion */
> +		reg = io_read(sd, REG_VDP_CTRL);
> +		reg &= ~VDP_CTRL_MATRIX_BP;
> +		io_write(sd, REG_VDP_CTRL, reg);
> +		/* offset inputs */
> +		io_write16(sd, REG_VDP_MATRIX + 0, state->conv->offint1);
> +		io_write16(sd, REG_VDP_MATRIX + 2, state->conv->offint2);
> +		io_write16(sd, REG_VDP_MATRIX + 4, state->conv->offint3);
> +		/* coefficients */
> +		io_write16(sd, REG_VDP_MATRIX + 6, state->conv->p11coef);
> +		io_write16(sd, REG_VDP_MATRIX + 8, state->conv->p12coef);
> +		io_write16(sd, REG_VDP_MATRIX + 10, state->conv->p13coef);
> +		io_write16(sd, REG_VDP_MATRIX + 12, state->conv->p21coef);
> +		io_write16(sd, REG_VDP_MATRIX + 14, state->conv->p22coef);
> +		io_write16(sd, REG_VDP_MATRIX + 16, state->conv->p23coef);
> +		io_write16(sd, REG_VDP_MATRIX + 18, state->conv->p31coef);
> +		io_write16(sd, REG_VDP_MATRIX + 20, state->conv->p32coef);
> +		io_write16(sd, REG_VDP_MATRIX + 22, state->conv->p33coef);
> +		/* offset outputs */
> +		io_write16(sd, REG_VDP_MATRIX + 24, state->conv->offout1);
> +		io_write16(sd, REG_VDP_MATRIX + 26, state->conv->offout2);
> +		io_write16(sd, REG_VDP_MATRIX + 28, state->conv->offout3);
> +	} else {
> +		/* disable matrix conversion */
> +		reg = io_read(sd, REG_VDP_CTRL);
> +		reg |= VDP_CTRL_MATRIX_BP;
> +		io_write(sd, REG_VDP_CTRL, reg);
> +	}
> +
> +	/* SetBlankingCodes */
> +	if (blanking_codes) {
> +		io_write16(sd, REG_BLK_GY, blanking_codes->code_gy);
> +		io_write16(sd, REG_BLK_BU, blanking_codes->code_bu);
> +		io_write16(sd, REG_BLK_RV, blanking_codes->code_rv);
> +	}
> +
> +	return 0;
> +}
> +
> +/* Configure frame detection window and VHREF timing generator */
> +static int
> +tda1997x_configure_vhref(struct v4l2_subdev *sd)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	const struct v4l2_bt_timings *bt;
> +	int width, lines;
> +	u16 href_start, href_end;
> +	u16 vref_f1_start, vref_f2_start;
> +	u8 vref_f1_width, vref_f2_width;
> +	u8 field_polarity;
> +	u16 fieldref_f1_start, fieldref_f2_start;
> +	u8 reg;
> +
> +	if (!state->detected_timings)
> +		return -EINVAL;
> +	bt = &state->detected_timings->bt;
> +	href_start = bt->hbackporch + bt->hsync + 1;
> +	href_end = href_start + bt->width;
> +	vref_f1_start = bt->height + bt->vbackporch + bt->vsync +
> +			bt->il_vbackporch + bt->il_vsync +
> +			bt->il_vfrontporch;
> +	vref_f1_width = bt->vbackporch + bt->vsync + bt->vfrontporch;
> +	vref_f2_start = 0;
> +	vref_f2_width = 0;
> +	fieldref_f1_start = 0;
> +	fieldref_f2_start = 0;
> +	if (bt->interlaced) {
> +		vref_f2_start = (bt->height / 2) +
> +				(bt->il_vbackporch + bt->il_vsync - 1);
> +		vref_f2_width = bt->il_vbackporch + bt->il_vsync +
> +				bt->il_vfrontporch;
> +		fieldref_f2_start = vref_f2_start + bt->il_vfrontporch +
> +				    fieldref_f1_start;
> +	}
> +	field_polarity = 0;
> +
> +	width = V4L2_DV_BT_FRAME_WIDTH(bt);
> +	lines = V4L2_DV_BT_FRAME_HEIGHT(bt);
> +
> +	/*
> +	 * Configure Frame Detection Window:
> +	 *  horiz area where the VHREF module consider a VSYNC a new frame
> +	 */
> +	io_write16(sd, REG_FDW_S, 0x2ef); /* start position */
> +	io_write16(sd, REG_FDW_E, 0x141); /* end position */
> +
> +	/* Set Pixel And Line Counters */
> +	if (state->chip_revision == 0)
> +		io_write16(sd, REG_PXCNT_PR, 4);
> +	else
> +		io_write16(sd, REG_PXCNT_PR, 1);
> +	io_write16(sd, REG_PXCNT_NPIX, width & MASK_VHREF);
> +	io_write16(sd, REG_LCNT_PR, 1);
> +	io_write16(sd, REG_LCNT_NLIN, lines & MASK_VHREF);
> +
> +	/*
> +	 * Configure the VHRef timing generator responsible for rebuilding all
> +	 * horiz and vert synch and ref signals from its input allowing auto
> +	 * detection algorithms and forcing predefined modes (480i & 576i)
> +	 */
> +	reg = VHREF_STD_DET_OFF << VHREF_STD_DET_SHIFT;
> +	io_write(sd, REG_VHREF_CTRL, reg);
> +
> +	/*
> +	 * Configure the VHRef timing values. In case the VHREF generator has
> +	 * been configured in manual mode, this will allow to manually set all
> +	 * horiz and vert ref values (non-active pixel areas) of the generator
> +	 * and allows setting the frame reference params.
> +	 */
> +	/* horizontal reference start/end */
> +	io_write16(sd, REG_HREF_S, href_start & MASK_VHREF);
> +	io_write16(sd, REG_HREF_E, href_end & MASK_VHREF);
> +	/* vertical reference f1 start/end */
> +	io_write16(sd, REG_VREF_F1_S, vref_f1_start & MASK_VHREF);
> +	io_write(sd, REG_VREF_F1_WIDTH, vref_f1_width);
> +	/* vertical reference f2 start/end */
> +	io_write16(sd, REG_VREF_F2_S, vref_f2_start & MASK_VHREF);
> +	io_write(sd, REG_VREF_F2_WIDTH, vref_f2_width);
> +
> +	/* F1/F2 FREF, field polarity */
> +	reg = fieldref_f1_start & MASK_VHREF;
> +	reg |= field_polarity << 8;
> +	io_write16(sd, REG_FREF_F1_S, reg);
> +	reg = fieldref_f2_start & MASK_VHREF;
> +	io_write16(sd, REG_FREF_F2_S, reg);
> +
> +	return 0;
> +}
> +
> +/* Configure Video Output port signals */
> +static int
> +tda1997x_configure_vidout(struct tda1997x_state *state)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	struct tda1997x_platform_data *pdata = &state->pdata;
> +	u8 prefilter;
> +	u8 reg;
> +
> +	/* Configure pixel clock generator: delay, polarity, rate */
> +	reg = (state->vid_fmt == OF_FMT_422_CCIR) ?
> +	       PCLK_SEL_X2 : PCLK_SEL_X1;
> +	reg |= pdata->vidout_delay_pclk << PCLK_DELAY_SHIFT;
> +	reg |= pdata->vidout_inv_pclk << PCLK_INV_SHIFT;
> +	io_write(sd, REG_PCLK, reg);
> +
> +	/* Configure pre-filter */
> +	prefilter = 0; /* filters off */
> +	/* YUV422 mode requires conversion */
> +	if ((state->vid_fmt == OF_FMT_422_SMPT)
> +	 || (state->vid_fmt == OF_FMT_422_CCIR)) {
> +		/* 2/7taps for Rv and Bu */
> +		prefilter = FILTERS_CTRL_2_7TAP << FILTERS_CTRL_BU_SHIFT |
> +			    FILTERS_CTRL_2_7TAP << FILTERS_CTRL_RV_SHIFT;
> +	}
> +	io_write(sd, REG_FILTERS_CTRL, prefilter);
> +
> +	/* Configure video port */
> +	reg = state->vid_fmt & OF_FMT_MASK;
> +	if (state->vid_fmt == OF_FMT_422_CCIR)
> +		reg |= (OF_BLK | OF_TRC);
> +	reg |= OF_VP_ENABLE;
> +	io_write(sd, REG_OF, reg);
> +
> +	/* Configure formatter and conversions */
> +	reg = io_read(sd, REG_VDP_CTRL);
> +	/* pre-filter is needed unless (REG_FILTERS_CTRL == 0) */
> +	if (!prefilter)
> +		reg |= VDP_CTRL_PREFILTER_BP;
> +	else
> +		reg &= ~VDP_CTRL_PREFILTER_BP;
> +	/* formatter is needed for YUV422 and for trc/blc codes */
> +	if (state->vid_fmt == OF_FMT_444)
> +		reg |= VDP_CTRL_FORMATTER_BP;
> +	/* formatter and compdel needed for timing/blanking codes */
> +	else
> +		reg &= ~(VDP_CTRL_FORMATTER_BP | VDP_CTRL_COMPDEL_BP);
> +	/* activate compdel for small sync delays */
> +	if ((pdata->vidout_delay_vs < 4) || (pdata->vidout_delay_hs < 4))
> +		reg &= ~VDP_CTRL_COMPDEL_BP;
> +	io_write(sd, REG_VDP_CTRL, reg);
> +
> +	/* Configure DE output signal: delay, polarity, and source */
> +	reg = pdata->vidout_delay_de << DE_FREF_DELAY_SHIFT |
> +	      pdata->vidout_inv_de << DE_FREF_INV_SHIFT |
> +	      pdata->vidout_sel_de << DE_FREF_SEL_SHIFT;
> +	io_write(sd, REG_DE_FREF, reg);
> +
> +	/* Configure HS/HREF output signal: delay, polarity, and source */
> +	if (state->vid_fmt != OF_FMT_422_CCIR) {
> +		reg = pdata->vidout_delay_hs << HS_HREF_DELAY_SHIFT |
> +		      pdata->vidout_inv_hs << HS_HREF_INV_SHIFT |
> +		      pdata->vidout_sel_hs << HS_HREF_SEL_SHIFT;
> +	} else
> +		reg = HS_HREF_SEL_NONE << HS_HREF_SEL_SHIFT;
> +	io_write(sd, REG_HS_HREF, reg);
> +
> +	/* Configure VS/VREF output signal: delay, polarity, and source */
> +	if (state->vid_fmt != OF_FMT_422_CCIR) {
> +		reg = pdata->vidout_delay_vs << VS_VREF_DELAY_SHIFT |
> +		      pdata->vidout_inv_vs << VS_VREF_INV_SHIFT |
> +		      pdata->vidout_sel_vs << VS_VREF_SEL_SHIFT;
> +	} else
> +		reg = VS_VREF_SEL_NONE << VS_VREF_SEL_SHIFT;
> +	io_write(sd, REG_VS_VREF, reg);
> +
> +	return 0;
> +}
> +
> +/* Configure Audio output port signals */
> +static int
> +tda1997x_configure_audout(struct v4l2_subdev *sd, u8 channel_assignment)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	struct tda1997x_platform_data *pdata = &state->pdata;
> +	bool sp_used_by_fifo = 1;
> +	u8 reg;
> +
> +	if (!pdata->audout_format)
> +		return 0;
> +
> +	/* channel assignment (CEA-861-D Table 20) */
> +	io_write(sd, REG_AUDIO_PATH, channel_assignment);
> +
> +	/* Audio output configuration */
> +	reg = 0;
> +	switch (pdata->audout_format) {
> +	case AUDFMT_TYPE_I2S:
> +		reg |= AUDCFG_BUS_I2S << AUDCFG_BUS_SHIFT;
> +		break;
> +	case AUDFMT_TYPE_SPDIF:
> +		reg |= AUDCFG_BUS_SPDIF << AUDCFG_BUS_SHIFT;
> +		break;
> +	}
> +	switch (state->audio_type) {
> +	case AUDCFG_TYPE_PCM:
> +		reg |= AUDCFG_TYPE_PCM << AUDCFG_TYPE_SHIFT;
> +		break;
> +	case AUDCFG_TYPE_OBA:
> +		reg |= AUDCFG_TYPE_OBA << AUDCFG_TYPE_SHIFT;
> +		break;
> +	case AUDCFG_TYPE_DST:
> +		reg |= AUDCFG_TYPE_DST << AUDCFG_TYPE_SHIFT;
> +		sp_used_by_fifo = 0;
> +		break;
> +	case AUDCFG_TYPE_HBR:
> +		reg |= AUDCFG_TYPE_HBR << AUDCFG_TYPE_SHIFT;
> +		if (pdata->audout_layout == 1) {
> +			/* demuxed via AP0:AP3 */
> +			reg |= AUDCFG_HBR_DEMUX << AUDCFG_HBR_SHIFT;
> +			if (pdata->audout_format == AUDFMT_TYPE_SPDIF)
> +				sp_used_by_fifo = 0;
> +		} else {
> +			/* straight via AP0 */
> +			reg |= AUDCFG_HBR_STRAIGHT << AUDCFG_HBR_SHIFT;
> +		}
> +		break;
> +	}
> +	if (pdata->audout_width == 32)
> +		reg |= AUDCFG_I2SW_32 << AUDCFG_I2SW_SHIFT;
> +	else
> +		reg |= AUDCFG_I2SW_16 << AUDCFG_I2SW_SHIFT;
> +
> +	/* automatic hardware mute */
> +	if (pdata->audio_auto_mute)
> +		reg |= AUDCFG_AUTO_MUTE_EN;
> +	/* clock polarity */
> +	if (pdata->audout_invert_clk)
> +		reg |= AUDCFG_CLK_INVERT;
> +	io_write(sd, REG_AUDCFG, reg);
> +
> +	/* audio layout */
> +	reg = (pdata->audout_layout) ? AUDIO_LAYOUT_LAYOUT1 : 0;
> +	if (!pdata->audout_layoutauto)
> +		reg |= AUDIO_LAYOUT_MANUAL;
> +	if (sp_used_by_fifo)
> +		reg |= AUDIO_LAYOUT_SP_FLAG;
> +	io_write(sd, REG_AUDIO_LAYOUT, reg);
> +
> +	/* FIFO Latency value */
> +	io_write(sd, REG_FIFO_LATENCY_VAL, 0x80);
> +
> +	/* Audio output port config */
> +	if (sp_used_by_fifo) {
> +		reg = AUDIO_OUT_ENABLE_AP0;
> +		if (channel_assignment >= 0x01)
> +			reg |= AUDIO_OUT_ENABLE_AP1;
> +		if (channel_assignment >= 0x04)
> +			reg |= AUDIO_OUT_ENABLE_AP2;
> +		if (channel_assignment >= 0x0c)
> +			reg |= AUDIO_OUT_ENABLE_AP3;
> +		/* specific cases where AP1 is not used */
> +		if ((channel_assignment == 0x04)
> +		 || (channel_assignment == 0x08)
> +		 || (channel_assignment == 0x0c)
> +		 || (channel_assignment == 0x10)
> +		 || (channel_assignment == 0x14)
> +		 || (channel_assignment == 0x18)
> +		 || (channel_assignment == 0x1c))
> +			reg &= ~AUDIO_OUT_ENABLE_AP1;
> +		/* specific cases where AP2 is not used */
> +		if ((channel_assignment >= 0x14)
> +		 && (channel_assignment <= 0x17))
> +			reg &= ~AUDIO_OUT_ENABLE_AP2;
> +	} else {
> +		reg = AUDIO_OUT_ENABLE_AP3 |
> +		      AUDIO_OUT_ENABLE_AP2 |
> +		      AUDIO_OUT_ENABLE_AP1 |
> +		      AUDIO_OUT_ENABLE_AP0;
> +	}
> +	if (pdata->audout_format == AUDFMT_TYPE_I2S)
> +		reg |= (AUDIO_OUT_ENABLE_ACLK | AUDIO_OUT_ENABLE_WS);
> +	io_write(sd, REG_AUDIO_OUT_ENABLE, reg);
> +
> +	/* reset test mode to normal audio freq auto selection */
> +	io_write(sd, REG_TEST_MODE, 0x00);
> +
> +	return 0;
> +}
> +
> +/* Soft Reset of specific hdmi info */
> +static int
> +tda1997x_hdmi_info_reset(struct v4l2_subdev *sd, u8 info_rst, bool reset_sus)
> +{
> +	u8 reg;
> +
> +	/* reset infoframe engine packets */
> +	reg = io_read(sd, REG_HDMI_INFO_RST);
> +	io_write(sd, REG_HDMI_INFO_RST, info_rst);
> +
> +	/* if infoframe engine has been reset clear INT_FLG_MODE */
> +	if (reg & RESET_IF) {
> +		reg = io_read(sd, REG_INT_FLG_CLR_MODE);
> +		io_write(sd, REG_INT_FLG_CLR_MODE, reg);
> +	}
> +
> +	/* Disable REFTIM to restart start-up-sequencer (SUS) */
> +	reg = io_read(sd, REG_RATE_CTRL);
> +	reg &= ~RATE_REFTIM_ENABLE;
> +	if (!reset_sus)
> +		reg |= RATE_REFTIM_ENABLE;
> +	reg = io_write(sd, REG_RATE_CTRL, reg);
> +
> +	return 0;
> +}
> +
> +static void
> +tda1997x_power_mode(struct tda1997x_state *state, bool enable)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 reg;
> +
> +	if (enable) {
> +		/* Automatic control of TMDS */
> +		io_write(sd, REG_PON_OVR_EN, PON_DIS);
> +		/* Enable current bias unit */
> +		io_write(sd, REG_CFG1, PON_EN);
> +		/* Enable deep color PLL */
> +		io_write(sd, REG_DEEP_PLL7_BYP, PON_DIS);
> +		/* Output buffers active */
> +		reg = io_read(sd, REG_OF);
> +		reg &= ~OF_VP_ENABLE;
> +		io_write(sd, REG_OF, reg);
> +	} else {
> +		/* Power down EDID mode sequence */
> +		/* Output buffers in HiZ */
> +		reg = io_read(sd, REG_OF);
> +		reg |= OF_VP_ENABLE;
> +		io_write(sd, REG_OF, reg);
> +		/* Disable deep color PLL */
> +		io_write(sd, REG_DEEP_PLL7_BYP, PON_EN);
> +		/* Disable current bias unit */
> +		io_write(sd, REG_CFG1, PON_DIS);
> +		/* Manual control of TMDS */
> +		io_write(sd, REG_PON_OVR_EN, PON_EN);
> +	}
> +}
> +
> +static bool
> +tda1997x_detect_tx_5v(struct v4l2_subdev *sd)
> +{
> +	u8 reg = io_read(sd, REG_DETECT_5V);
> +
> +	return ((reg & DETECT_5V_SEL) ? 1 : 0);
> +}
> +
> +static bool
> +tda1997x_detect_tx_hpd(struct v4l2_subdev *sd)
> +{
> +	u8 reg = io_read(sd, REG_DETECT_5V);
> +
> +	return ((reg & DETECT_HPD) ? 1 : 0);
> +}
> +
> +static int
> +tda1997x_detect_std(struct tda1997x_state *state)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u32 vper;
> +	u16 hper;
> +	u16 hsper;
> +	int i;
> +
> +	/*
> +	 * Read the FMT registers
> +	 *   REG_V_PER: Period of a frame (or two fields) in MCLK(27MHz) cycles
> +	 *   REG_H_PER: Period of a line in MCLK(27MHz) cycles
> +	 *   REG_HS_WIDTH: Period of horiz sync pulse in MCLK(27MHz) cycles
> +	 */
> +	vper = io_read24(sd, REG_V_PER) & MASK_VPER;
> +	hper = io_read16(sd, REG_H_PER) & MASK_HPER;
> +	hsper = io_read16(sd, REG_HS_WIDTH) & MASK_HSWIDTH;
> +	if (!vper || !hper || !hsper)
> +		return -ENOLINK;
> +	v4l2_dbg(1, debug, sd, "Signal Timings: %u/%u/%u\n", vper, hper, hsper);
> +
> +	/* look for matching timings */
> +	for (i = 0; i < ARRAY_SIZE(tda1997x_hdmi_modes); i++) {
> +		const struct v4l2_bt_timings *bt = &tda1997x_hdmi_modes[i].bt;
> +		u32 lines, width, _hper, _hsper;
> +		u32 vmin, vmax, hmin, hmax, hsmin, hsmax;
> +		bool vmatch, hmatch, hsmatch;
> +
> +		width = V4L2_DV_BT_FRAME_WIDTH(bt);
> +		lines = V4L2_DV_BT_FRAME_HEIGHT(bt);
> +		_hper = (u32)bt->pixelclock / width;
> +		if (bt->interlaced)
> +			lines /= 2;
> +		/* vper +/- 0.7% */
> +		vmin = ((27000000 / 1000) * 993) / _hper * lines;
> +		vmax = ((27000000 / 1000) * 1007) / _hper * lines;
> +		/* hper +/- 1.0% */
> +		hmin = ((27000000 / 100) * 99) / _hper;
> +		hmax = ((27000000 / 100) * 101) / _hper;
> +		/* hsper +/- 2 (take care to avoid 32bit overflow) */
> +		_hsper = 27000 * bt->hsync / ((u32)bt->pixelclock/1000);
> +		hsmin = _hsper - 2;
> +		hsmax = _hsper + 2;
> +
> +		/* vmatch matches the framerate */
> +		vmatch = ((vper <= vmax) && (vper >= vmin)) ? 1 : 0;
> +		/* hmatch matches the width */
> +		hmatch = ((hper <= hmax) && (hper >= hmin)) ? 1 : 0;
> +		/* hsmatch matches the hswidth */
> +		hsmatch = ((hsper <= hsmax) && (hsper >= hsmin)) ? 1 : 0;
> +		if (hmatch && vmatch && hsmatch) {
> +			state->detected_timings = &tda1997x_hdmi_modes[i];
> +			v4l2_print_dv_timings(sd->name, "Detected format: ",
> +					      state->detected_timings, false);
> +			return 0;
> +		}
> +	}
> +
> +	v4l_err(state->client, "no resolution match for timings: %d/%d/%d\n",
> +		vper, hper, hsper);
> +	return -EINVAL;
> +}
> +
> +/* some sort of errata workaround for chip revision 0 (N1) */
> +static void tda1997x_reset_n1(struct tda1997x_state *state)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 reg;
> +
> +	/* clear HDMI mode flag in BCAPS */
> +	io_write(sd, REG_CLK_CFG, CLK_CFG_SEL_ACLK_EN | CLK_CFG_SEL_ACLK);
> +	io_write(sd, REG_PON_OVR_EN, PON_EN);
> +	io_write(sd, REG_PON_CBIAS, PON_EN);
> +	io_write(sd, REG_PON_PLL, PON_EN);
> +
> +	reg = io_read(sd, REG_MODE_REC_CFG1);
> +	reg &= ~0x06;
> +	reg |= 0x02;
> +	io_write(sd, REG_MODE_REC_CFG1, reg);
> +	io_write(sd, REG_CLK_CFG, CLK_CFG_DIS);
> +	io_write(sd, REG_PON_OVR_EN, PON_DIS);
> +	reg = io_read(sd, REG_MODE_REC_CFG1);
> +	reg &= ~0x06;
> +	io_write(sd, REG_MODE_REC_CFG1, reg);
> +}
> +
> +/*
> + * Activity detection must only be notified when stable_clk_x AND active_x
> + * bits are set to 1. If only stable_clk_x bit is set to 1 but not
> + * active_x, it means that the TMDS clock is not in the defined range
> + * and activity detection must not be notified.
> + */
> +static u8
> +tda1997x_read_activity_status_regs(struct v4l2_subdev *sd)
> +{
> +	u8 reg, status = 0;
> +
> +	/* Read CLK_A_STATUS register */
> +	reg = io_read(sd, REG_CLK_A_STATUS);
> +	/* when stable_clk_x is set to 1, check active_x bit */
> +	if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE))
> +		reg &= ~MASK_CLK_STABLE;
> +	status |= ((reg & MASK_CLK_STABLE) >> 2);
> +
> +	/* Read CLK_B_STATUS register */
> +	reg = io_read(sd, REG_CLK_B_STATUS);
> +	/* when stable_clk_x is set to 1, check active_x bit */
> +	if ((reg & MASK_CLK_STABLE) && !(reg & MASK_CLK_ACTIVE))
> +		reg &= ~MASK_CLK_STABLE;
> +	status |= ((reg & MASK_CLK_STABLE) >> 1);
> +
> +	/* Read the SUS_STATUS register */
> +	reg = io_read(sd, REG_SUS_STATUS);
> +
> +	/* If state = 5 => TMDS is locked */
> +	if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED)
> +		status |= MASK_SUS_STATE;
> +	else
> +		status &= ~MASK_SUS_STATE;
> +
> +	return status;
> +}
> +
> +/* parse an infoframe and do some sanity checks on it */
> +static unsigned int
> +tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	union hdmi_infoframe frame;
> +	u8 buffer[40];
> +	u8 reg;
> +	int len, err;
> +
> +	/* read data */
> +	len = io_readn(sd, addr, sizeof(buffer), buffer);
> +	err = hdmi_infoframe_unpack(&frame, buffer);
> +	if (err) {
> +		v4l_err(state->client,
> +			"failed parsing %d byte infoframe: 0x%04x/0x%02x\n",
> +			len, addr, buffer[0]);
> +		return err;
> +	}
> +	hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame);
> +	switch (frame.any.type) {
> +	/* Audio InfoFrame: see HDMI spec 8.2.2 */
> +	case HDMI_INFOFRAME_TYPE_AUDIO:
> +		/* sample rate */
> +		switch (frame.audio.sample_frequency) {
> +		case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
> +			state->audio_samplerate = 32000;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
> +			state->audio_samplerate = 44100;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
> +			state->audio_samplerate = 48000;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
> +			state->audio_samplerate = 88200;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
> +			state->audio_samplerate = 96000;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_FREQUENCY_176400:
> +			state->audio_samplerate = 176400;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_FREQUENCY_192000:
> +			state->audio_samplerate = 192000;
> +			break;
> +		default:
> +		case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM:
> +			break;
> +		}
> +
> +		/* sample size */
> +		switch (frame.audio.sample_size) {
> +		case HDMI_AUDIO_SAMPLE_SIZE_16:
> +			state->audio_samplesize = 16;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_SIZE_20:
> +			state->audio_samplesize = 20;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_SIZE_24:
> +			state->audio_samplesize = 24;
> +			break;
> +		case HDMI_AUDIO_SAMPLE_SIZE_STREAM:
> +		default:
> +			break;
> +		}
> +
> +		/* Channel Count */
> +		state->audio_channels = frame.audio.channels;
> +		if (frame.audio.channel_allocation &&
> +		    frame.audio.channel_allocation != state->audio_ch_alloc) {
> +			/* use the channel assignment from the infoframe */
> +			state->audio_ch_alloc = frame.audio.channel_allocation;
> +			tda1997x_configure_audout(sd, state->audio_ch_alloc);
> +			/* reset the audio FIFO */
> +			tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false);
> +		}
> +		break;
> +
> +	/* Auxiliary Video information (AVI) InfoFrame: see HDMI spec 8.2.1 */
> +	case HDMI_INFOFRAME_TYPE_AVI:
> +		state->colorspace = frame.avi.colorspace;
> +		state->colorimetry = frame.avi.colorimetry;
> +		state->content = frame.avi.content_type;
> +		/* Quantization Range */
> +		switch (state->rgb_quantization_range) {
> +		case V4L2_DV_RGB_RANGE_AUTO:
> +			state->range = frame.avi.quantization_range;
> +			break;
> +		case V4L2_DV_RGB_RANGE_LIMITED:
> +			state->range = HDMI_QUANTIZATION_RANGE_LIMITED;
> +			break;
> +		case V4L2_DV_RGB_RANGE_FULL:
> +			state->range = HDMI_QUANTIZATION_RANGE_FULL;
> +			break;
> +		}
> +		if (state->range == HDMI_QUANTIZATION_RANGE_DEFAULT) {
> +			if (frame.avi.video_code <= 1)
> +				state->range = HDMI_QUANTIZATION_RANGE_FULL;
> +			else
> +				state->range = HDMI_QUANTIZATION_RANGE_LIMITED;
> +		}
> +		/*
> +		 * If colorimetry not specified, conversion depends on res type:
> +		 *  - SDTV: ITU601 for SD (480/576/240/288 line resolution)
> +		 *  - HDTV: ITU709 for HD (720/1080 line resolution)
> +		 *  -   PC: sRGB
> +		 * see HDMI specification section 6.7
> +		 */
> +		if ((state->colorspace == HDMI_COLORSPACE_YUV422 ||
> +		     state->colorspace == HDMI_COLORSPACE_YUV444) &&
> +		    (state->colorimetry == HDMI_COLORIMETRY_EXTENDED ||
> +		     state->colorimetry == HDMI_COLORIMETRY_NONE)) {
> +			if (is_sd(state->timings.bt.height))
> +				state->colorimetry = HDMI_COLORIMETRY_ITU_601;
> +			else if (is_hd(state->timings.bt.height))
> +				state->colorimetry = HDMI_COLORIMETRY_ITU_709;
> +			else
> +				state->colorimetry = HDMI_COLORIMETRY_NONE;
> +		}
> +		v4l_dbg(1, debug, state->client,
> +			"colorspace=%d colorimetry=%d range=%d content=%d\n",
> +			state->colorspace, state->colorimetry, state->range,
> +			state->content);
> +
> +		/* configure upsampler: 0=bypass 1=repeatchroma 2=interpolate */
> +		reg = io_read(sd, REG_PIX_REPEAT);
> +		reg &= ~PIX_REPEAT_MASK_UP_SEL;
> +		if (state->colorspace == HDMI_COLORSPACE_YUV422)
> +			reg |= (PIX_REPEAT_CHROMA << PIX_REPEAT_SHIFT);
> +		io_write(sd, REG_PIX_REPEAT, reg);
> +
> +		/* ConfigurePixelRepeater: repeat n-times each pixel */
> +		reg = io_read(sd, REG_PIX_REPEAT);
> +		reg &= ~PIX_REPEAT_MASK_REP;
> +		reg |= frame.avi.pixel_repeat;
> +		io_write(sd, REG_PIX_REPEAT, reg);
> +
> +		/* configure the receiver with the new colorspace */
> +		tda1997x_configure_csc(sd);
> +		break;
> +	default:
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static void tda1997x_irq_sus(struct tda1997x_state *state, u8 *flags)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 reg, source;
> +
> +	source = io_read(sd, REG_INT_FLG_CLR_SUS);
> +	io_write(sd, REG_INT_FLG_CLR_SUS, source);
> +
> +	if (source & MASK_MPT) {
> +		/* reset MTP in use flag if set */
> +		if (state->mptrw_in_progress)
> +			state->mptrw_in_progress = 0;
> +	}
> +
> +	if (source & MASK_SUS_END) {
> +		/* reset audio FIFO */
> +		reg = io_read(sd, REG_HDMI_INFO_RST);
> +		reg |= MASK_SR_FIFO_FIFO_CTRL;
> +		io_write(sd, REG_HDMI_INFO_RST, reg);
> +		reg &= ~MASK_SR_FIFO_FIFO_CTRL;
> +		io_write(sd, REG_HDMI_INFO_RST, reg);
> +
> +		/* reset HDMI flags */
> +		state->hdmi_status = 0;
> +	}
> +
> +	/* filter FMT interrupt based on SUS state */
> +	reg = io_read(sd, REG_SUS_STATUS);
> +	if (((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED)
> +	   || (source & MASK_MPT)) {
> +		source &= ~MASK_FMT;
> +	}
> +
> +	if (source & (MASK_FMT | MASK_SUS_END)) {
> +		reg = io_read(sd, REG_SUS_STATUS);
> +		if ((reg & MASK_SUS_STATUS) != LAST_STATE_REACHED) {
> +			v4l_err(state->client, "BAD SUS STATUS\n");
> +			return;
> +		}
> +
> +		/* There is new activity, the status for HDCP repeater state */
> +		state->state_c5_reached = 0;
> +
> +		/* Detect the new resolution */
> +		if (!tda1997x_detect_std(state))
> +			v4l2_subdev_notify_event(&state->sd, &tda1997x_ev_fmt);
> +	}
> +}
> +
> +static void tda1997x_irq_ddc(struct tda1997x_state *state, u8 *flags)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 source;
> +
> +	source = io_read(sd, REG_INT_FLG_CLR_DDC);
> +	io_write(sd, REG_INT_FLG_CLR_DDC, source);
> +	if (source & MASK_EDID_MTP) {
> +		/* reset MTP in use flag if set */
> +		if (state->mptrw_in_progress)
> +			state->mptrw_in_progress = 0;
> +	}
> +
> +	/* Detection of +5V */
> +	if (source & MASK_DET_5V) {
> +		v4l2_ctrl_s_ctrl(state->detect_tx_5v_ctrl,
> +				 tda1997x_detect_tx_5v(sd));
> +	}
> +}
> +
> +static void tda1997x_irq_rate(struct tda1997x_state *state, u8 *flags)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 reg, source;
> +
> +	u8 irq_status, last_irq_status;
> +
> +	source = io_read(sd, REG_INT_FLG_CLR_RATE);
> +	io_write(sd, REG_INT_FLG_CLR_RATE, source);
> +
> +	/* read status regs */
> +	last_irq_status = irq_status = tda1997x_read_activity_status_regs(sd);
> +
> +	/*
> +	 * read clock status reg until INT_FLG_CLR_RATE is still 0
> +	 * after the read to make sure its the last one
> +	 */
> +	reg = source;
> +	while (reg != 0) {
> +		irq_status = tda1997x_read_activity_status_regs(sd);
> +		reg = io_read(sd, REG_INT_FLG_CLR_RATE);
> +		io_write(sd, REG_INT_FLG_CLR_RATE, reg);
> +		source |= reg;
> +	}
> +
> +	/* we only pay attention to stability change events */
> +	if (source & (MASK_RATE_A_ST | MASK_RATE_B_ST)) {
> +		int input = (source & MASK_RATE_A_ST)?0:1;
> +		u8 mask = 1<<input;
> +
> +		/* state change */
> +		if ((irq_status & mask) != (state->activity_status & mask)) {
> +			/* activity lost */
> +			if ((irq_status & mask) == 0) {
> +				v4l_info(state->client,
> +					 "HDMI-%c: Digital Activity Lost\n",
> +					 input+'A');
> +
> +				/* bypass up/down sampler and pixel repeater */
> +				reg = io_read(sd, REG_PIX_REPEAT);
> +				reg &= ~PIX_REPEAT_MASK_UP_SEL;
> +				reg &= ~PIX_REPEAT_MASK_REP;
> +				io_write(sd, REG_PIX_REPEAT, reg);
> +
> +				if (state->chip_revision == 0)
> +					tda1997x_reset_n1(state);
> +
> +				state->detected_timings = NULL;
> +				state->input_detect[input] = 0;
> +				v4l2_subdev_notify_event(sd, &tda1997x_ev_fmt);
> +			}
> +
> +			/* activity detected */
> +			else {
> +				v4l_info(state->client,
> +					 "HDMI-%c: Digital Activity Detected\n",
> +					 input+'A');
> +				state->input_detect[input] = 1;
> +			}
> +
> +			/* hold onto current state */
> +			state->activity_status = (irq_status & mask);
> +		}
> +	}
> +}
> +
> +static void tda1997x_irq_info(struct tda1997x_state *state, u8 *flags)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 source;
> +
> +	source = io_read(sd, REG_INT_FLG_CLR_INFO);
> +	io_write(sd, REG_INT_FLG_CLR_INFO, source);
> +
> +	/* Audio infoframe */
> +	if (source & MASK_AUD_IF) {
> +		tda1997x_parse_infoframe(state, AUD_IF);
> +		source &= ~MASK_AUD_IF;
> +	}
> +
> +	/* Source Product Descriptor infoframe change */
> +	if (source & MASK_SPD_IF) {
> +		tda1997x_parse_infoframe(state, SPD_IF);
> +		source &= ~MASK_SPD_IF;
> +	}
> +
> +	/* Auxiliary Video Information infoframe */
> +	if (source & MASK_AVI_IF) {
> +		tda1997x_parse_infoframe(state, AVI_IF);
> +		source &= ~MASK_AVI_IF;
> +	}
> +}
> +
> +static void tda1997x_irq_audio(struct tda1997x_state *state, u8 *flags)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 reg, source;
> +
> +	source = io_read(sd, REG_INT_FLG_CLR_AUDIO);
> +	io_write(sd, REG_INT_FLG_CLR_AUDIO, source);
> +
> +	/* reset audio FIFO on FIFO pointer error or audio mute */
> +	if (source & MASK_ERROR_FIFO_PT ||
> +	    source & MASK_MUTE_FLG) {
> +		/* audio reset audio FIFO */
> +		reg = io_read(sd, REG_SUS_STATUS);
> +		if ((reg & MASK_SUS_STATUS) == LAST_STATE_REACHED) {
> +			reg = io_read(sd, REG_HDMI_INFO_RST);
> +			reg |= MASK_SR_FIFO_FIFO_CTRL;
> +			io_write(sd, REG_HDMI_INFO_RST, reg);
> +			reg &= ~MASK_SR_FIFO_FIFO_CTRL;
> +			io_write(sd, REG_HDMI_INFO_RST, reg);
> +			/* reset channel status IT if present */
> +			source &= ~(MASK_CH_STATE);
> +		}
> +	}
> +	if (source & MASK_AUDIO_FREQ_FLG) {
> +		static const int freq[] = {
> +			0, 32000, 44100, 48000, 88200, 96000, 176400, 192000
> +		};
> +
> +		reg = io_read(sd, REG_AUDIO_FREQ);
> +		state->audio_samplerate = freq[reg & 7];
> +		v4l_info(state->client, "Audio Frequency Change: %dHz\n",
> +			 state->audio_samplerate);
> +	}
> +	if (source & MASK_AUDIO_FLG) {
> +		reg = io_read(sd, REG_AUDIO_FLAGS);
> +		if (reg & BIT(AUDCFG_TYPE_DST))
> +			state->audio_type = AUDCFG_TYPE_DST;
> +		if (reg & BIT(AUDCFG_TYPE_OBA))
> +			state->audio_type = AUDCFG_TYPE_OBA;
> +		if (reg & BIT(AUDCFG_TYPE_HBR))
> +			state->audio_type = AUDCFG_TYPE_HBR;
> +		if (reg & BIT(AUDCFG_TYPE_PCM))
> +			state->audio_type = AUDCFG_TYPE_PCM;
> +		v4l_info(state->client, "Audio Type: %s\n",
> +			 audtype_names[state->audio_type]);
> +	}
> +}
> +
> +static void tda1997x_irq_hdcp(struct tda1997x_state *state, u8 *flags)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 reg, source;
> +
> +	source = io_read(sd, REG_INT_FLG_CLR_HDCP);
> +	io_write(sd, REG_INT_FLG_CLR_HDCP, source);
> +
> +	/* reset MTP in use flag if set */
> +	if (source & MASK_HDCP_MTP)
> +		state->mptrw_in_progress = 0;
> +	if (source & MASK_STATE_C5) {
> +		/* REPEATER: mask AUDIO and IF irqs to avoid IF during auth */
> +		reg = io_read(sd, REG_INT_MASK_TOP);
> +		reg &= ~(INTERRUPT_AUDIO | INTERRUPT_INFO);
> +		io_write(sd, REG_INT_MASK_TOP, reg);
> +		*flags &= (INTERRUPT_AUDIO | INTERRUPT_INFO);
> +	}
> +}
> +
> +static irqreturn_t tda1997x_isr_thread(int irq, void *d)
> +{
> +	struct tda1997x_state *state = d;
> +	struct v4l2_subdev *sd = &state->sd;
> +	u8 flags;
> +
> +	mutex_lock(&state->lock);
> +	do {
> +		/* read interrupt flags */
> +		flags = io_read(sd, REG_INT_FLG_CLR_TOP);
> +		if (flags == 0)
> +			break;
> +
> +		/* SUS interrupt source (Input activity events) */
> +		if (flags & INTERRUPT_SUS)
> +			tda1997x_irq_sus(state, &flags);
> +		/* DDC interrupt source (Display Data Channel) */
> +		else if (flags & INTERRUPT_DDC)
> +			tda1997x_irq_ddc(state, &flags);
> +		/* RATE interrupt source (Digital Input activity) */
> +		else if (flags & INTERRUPT_RATE)
> +			tda1997x_irq_rate(state, &flags);
> +		/* Infoframe change interrupt */
> +		else if (flags & INTERRUPT_INFO)
> +			tda1997x_irq_info(state, &flags);
> +		/* Audio interrupt source:
> +		 *   freq change, DST,OBA,HBR,ASP flags, mute, FIFO err
> +		 */
> +		else if (flags & INTERRUPT_AUDIO)
> +			tda1997x_irq_audio(state, &flags);
> +		/* HDCP interrupt source (content protection) */
> +		if (flags & INTERRUPT_HDCP)
> +			tda1997x_irq_hdcp(state, &flags);
> +	} while (flags != 0);
> +	mutex_unlock(&state->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static bool tda1997x_check_dv_timings(const struct v4l2_dv_timings *timings,
> +				      void *hdl)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(tda1997x_hdmi_modes); i++) {
> +		if (v4l2_match_dv_timings(timings, &tda1997x_hdmi_modes[i],
> +					  0, false))
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * v4l2_subdev_video_ops
> + */
> +
> +static int
> +tda1997x_g_input_status(struct v4l2_subdev *sd, u32 *status)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	mutex_lock(&state->lock);
> +	if (state->detected_timings)
> +		*status = 0;
> +	else
> +		*status |= V4L2_IN_ST_NO_SIGNAL;
> +	mutex_unlock(&state->lock);
> +
> +	return 0;
> +};
> +
> +static int tda1997x_s_dv_timings(struct v4l2_subdev *sd,
> +				struct v4l2_dv_timings *timings)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	int ret;
> +
> +	v4l_dbg(1, debug, state->client, "%s\n", __func__);
> +	if (!timings)
> +		return -EINVAL;
> +
> +	if (v4l2_match_dv_timings(&state->timings, timings, 0, false))
> +		return 0; /* no changes */
> +
> +	if (!v4l2_valid_dv_timings(timings, &tda1997x_dv_timings_cap,
> +				   tda1997x_check_dv_timings, state))
> +		return -ERANGE;
> +
> +	mutex_lock(&state->lock);
> +	state->timings = *timings;
> +	/* setup frame detection window and VHREF timing generator */
> +	ret = tda1997x_configure_vhref(sd);
> +	if (ret)
> +		goto error;
> +	/* configure colorspace conversion */
> +	ret = tda1997x_configure_csc(sd);
> +	if (ret)
> +		goto error;
> +	mutex_unlock(&state->lock);
> +
> +	return 0;
> +
> +error:
> +	mutex_unlock(&state->lock);
> +	return ret;
> +}
> +
> +static int tda1997x_g_dv_timings(struct v4l2_subdev *sd,
> +				 struct v4l2_dv_timings *timings)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	v4l_dbg(1, debug, state->client, "%s\n", __func__);
> +	if (!timings)
> +		return -EINVAL;
> +
> +	mutex_lock(&state->lock);
> +	*timings = state->timings;
> +	mutex_unlock(&state->lock);
> +
> +	return 0;
> +}
> +
> +static int tda1997x_query_dv_timings(struct v4l2_subdev *sd,
> +				     struct v4l2_dv_timings *timings)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	int ret;
> +
> +	v4l_dbg(1, debug, state->client, "%s\n", __func__);
> +	if (!timings)
> +		return -EINVAL;
> +
> +	memset(timings, 0, sizeof(struct v4l2_dv_timings));
> +	mutex_lock(&state->lock);
> +	ret = tda1997x_detect_std(state);
> +	if (!ret)
> +		*timings = *state->detected_timings;
> +	mutex_unlock(&state->lock);
> +
> +	return ret;
> +}
> +
> +static int tda1997x_s_stream(struct v4l2_subdev *sd, int enable)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	v4l_dbg(1, debug, state->client, "%s %d\n", __func__, enable);
> +	mutex_lock(&state->lock);
> +	if (!state->detected_timings)
> +		v4l_dbg(1, debug, state->client, "Invalid HDMI signal\n");
> +	mutex_unlock(&state->lock);
> +
> +	return 0;
> +}
> +
> +static const struct v4l2_subdev_video_ops tda1997x_video_ops = {
> +	.g_input_status = tda1997x_g_input_status,
> +	.s_dv_timings = tda1997x_s_dv_timings,
> +	.g_dv_timings = tda1997x_g_dv_timings,
> +	.query_dv_timings = tda1997x_query_dv_timings,
> +	.s_stream = tda1997x_s_stream,
> +};
> +
> +
> +/* -----------------------------------------------------------------------------
> + * v4l2_subdev_pad_ops
> + */
> +
> +static int tda1997x_enum_mbus_code(struct v4l2_subdev *sd,
> +				  struct v4l2_subdev_pad_config *cfg,
> +				  struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	if (code->index > 0)
> +		return -EINVAL;
> +
> +	code->code = state->code;
> +
> +	return 0;
> +}
> +
> +static int tda1997x_fill_format(struct tda1997x_state *state,
> +				struct v4l2_mbus_framefmt *format)
> +{
> +	const struct v4l2_bt_timings *bt;
> +
> +	v4l_dbg(1, debug, state->client, "%s\n", __func__);
> +
> +	if (!state->detected_timings)
> +		return -EINVAL;
> +	bt = &state->detected_timings->bt;
> +	memset(format, 0, sizeof(*format));
> +
> +	format->width = bt->width;
> +	format->height = bt->height;
> +	format->field = V4L2_FIELD_NONE;
> +	format->colorspace = V4L2_COLORSPACE_SRGB;
> +	if (bt->flags & V4L2_DV_FL_IS_CE_VIDEO)
> +		format->colorspace = (bt->height <= 576) ?
> +			V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;

Close. What is missing is a check of the AVI InfoFrame: if it has an explicit
colorimetry then use that. E.g. check for HDMI_COLORIMETRY_ITU_601 or ITU_709
and set the colorspace accordingly. Otherwise fall back to what you have here.

Make a note that we ignore HDMI_COLORIMETRY_EXTENDED as we don't properly
support that anyway. For the record: I know that the colorimetry field in the
AVI InfoFrame can change dynamically, so this is just a snapshot in time. We
don't have good support for dynamic colorimetry changes. Or to be precise: we
never got around to implement it.

> +
> +	return 0;
> +}
> +
> +static int tda1997x_get_pad_format(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_pad_config *cfg,
> +				   struct v4l2_subdev_format *format)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	v4l_dbg(1, debug, state->client, "%s\n", __func__);
> +	if (format->pad != TDA1997X_PAD_SOURCE)
> +		return -EINVAL;
> +
> +	tda1997x_fill_format(state, &format->format);
> +
> +	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
> +		struct v4l2_mbus_framefmt *fmt;
> +
> +		fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
> +		format->format.code = format->format.code;
> +	} else
> +		format->format.code = state->code;
> +
> +	return 0;
> +}
> +
> +static int tda1997x_set_pad_format(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_pad_config *cfg,
> +				   struct v4l2_subdev_format *format)
> +{
> +	struct v4l2_mbus_framefmt *fmt;
> +
> +	if (format->pad != TDA1997X_PAD_SOURCE)
> +		return -EINVAL;
> +
> +	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
> +		return tda1997x_get_pad_format(sd, cfg, format);
> +
> +	fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
> +	*fmt = format->format;
> +
> +	return 0;
> +}
> +
> +static int tda1997x_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad);
> +	memset(edid->reserved, 0, sizeof(edid->reserved));
> +
> +	if (!state->edid.present)
> +		return -ENODATA;
> +
> +	if (edid->start_block == 0 && edid->blocks == 0) {
> +		edid->blocks = state->edid.blocks;
> +		return 0;
> +	}
> +
> +	if (edid->start_block >= state->edid.blocks)
> +		return -EINVAL;
> +
> +	if (edid->start_block + edid->blocks > state->edid.blocks)
> +		edid->blocks = state->edid.blocks - edid->start_block;
> +
> +	memcpy(edid->edid, state->edid.edid + edid->start_block * 128,
> +	       edid->blocks * 128);
> +
> +	return 0;
> +}
> +
> +static int tda1997x_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	int i;
> +
> +	v4l_dbg(1, debug, state->client, "%s pad=%d\n", __func__, edid->pad);
> +	memset(edid->reserved, 0, sizeof(edid->reserved));
> +
> +	if (edid->start_block != 0)
> +		return -EINVAL;
> +
> +	if (edid->blocks == 0) {
> +		state->edid.blocks = 0;
> +		state->edid.present = 0;
> +		tda1997x_disable_edid(sd);
> +		return 0;
> +	}
> +
> +	if (edid->blocks > 2) {
> +		edid->blocks = 2;
> +		return -E2BIG;
> +	}
> +
> +	tda1997x_disable_edid(sd);
> +
> +	/* write base EDID */
> +	for (i = 0; i < 128; i++)
> +		io_write(sd, REG_EDID_IN_BYTE0 + i, edid->edid[i]);
> +
> +	/* write CEA Extension */
> +	for (i = 0; i < 128; i++)
> +		io_write(sd, REG_EDID_IN_BYTE128 + i, edid->edid[i+128]);
> +
> +	tda1997x_enable_edid(sd);
> +
> +	return 0;
> +}
> +
> +static int tda1997x_get_dv_timings_cap(struct v4l2_subdev *sd,
> +				       struct v4l2_dv_timings_cap *cap)
> +{
> +	*cap = tda1997x_dv_timings_cap;
> +	return 0;
> +}
> +
> +static int tda1997x_enum_dv_timings(struct v4l2_subdev *sd,
> +				    struct v4l2_enum_dv_timings *timings)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	return v4l2_enum_dv_timings_cap(timings, &tda1997x_dv_timings_cap,
> +					tda1997x_check_dv_timings, state);
> +}
> +
> +static const struct v4l2_subdev_pad_ops tda1997x_pad_ops = {
> +	.enum_mbus_code = tda1997x_enum_mbus_code,
> +	.get_fmt = tda1997x_get_pad_format,
> +	.set_fmt = tda1997x_set_pad_format,
> +	.get_edid = tda1997x_get_edid,
> +	.set_edid = tda1997x_set_edid,
> +	.dv_timings_cap = tda1997x_get_dv_timings_cap,
> +	.enum_dv_timings = tda1997x_enum_dv_timings,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * v4l2_subdev_core_ops
> + */
> +
> +static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	union hdmi_infoframe frame;
> +	u8 buffer[40];
> +	int len, err;
> +
> +	/* read data */
> +	len = io_readn(sd, addr, sizeof(buffer), buffer);
> +	v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len);
> +	err = hdmi_infoframe_unpack(&frame, buffer);
> +	if (err) {
> +		v4l_err(state->client,
> +			"failed parsing %d byte infoframe: 0x%04x/0x%02x\n",
> +			len, addr, buffer[0]);
> +		return err;
> +	}
> +	hdmi_infoframe_log(KERN_INFO, &state->client->dev, &frame);
> +
> +	return 0;
> +}
> +
> +static int tda1997x_log_status(struct v4l2_subdev *sd)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	struct v4l2_dv_timings timings;
> +
> +	v4l2_info(sd, "-----Chip status-----\n");
> +	v4l2_info(sd, "Chip: %s N%d\n", state->info->name,
> +		  state->chip_revision + 1);
> +	v4l2_info(sd, "EDID Enabled: %s\n", state->edid.present ? "yes" : "no");
> +
> +	v4l2_info(sd, "-----Signal status-----\n");
> +	v4l2_info(sd, "Cable detected (+5V power): %s\n",
> +		  tda1997x_detect_tx_5v(sd) ? "yes" : "no");
> +	v4l2_info(sd, "HPD detected: %s\n",
> +		  tda1997x_detect_tx_hpd(sd) ? "yes" : "no");
> +
> +	v4l2_info(sd, "-----Video Timings-----\n");
> +	if (tda1997x_query_dv_timings(sd, &timings))
> +		v4l2_info(sd, "No video detected\n");
> +	else
> +		v4l2_print_dv_timings(sd->name, "Detected format: ",
> +				      &timings, true);
> +	v4l2_print_dv_timings(sd->name, "Configured format: ",
> +			      &state->timings, true);
> +
> +	v4l2_info(sd, "-----Color space-----\n");
> +	v4l2_info(sd, "Input color space: %s %s %s",
> +		  colorspace_names[state->colorspace],
> +		  (state->colorspace == HDMI_COLORSPACE_RGB) ? "" :
> +		  colorimetry_names[state->colorimetry],
> +		  rgb_quantization_range_names[state->range]);
> +	v4l2_info(sd, "Output color space: %s",
> +		  vidfmt_names[state->vid_fmt]);
> +	v4l2_info(sd, "Color space conversion: %s", state->conv ?
> +		  state->conv->name : "None");
> +
> +	v4l2_info(sd, "-----Audio-----\n");
> +	if (state->audio_channels) {
> +		v4l2_info(sd, "audio: %dch %dHz\n", state->audio_channels,
> +			  state->audio_samplerate);
> +	} else {
> +		v4l2_info(sd, "audio: none\n");
> +	}
> +
> +	v4l2_info(sd, "-----Infoframes-----\n");
> +	tda1997x_log_infoframe(sd, AUD_IF);
> +	tda1997x_log_infoframe(sd, SPD_IF);
> +	tda1997x_log_infoframe(sd, AVI_IF);
> +
> +	return 0;
> +}
> +
> +static int tda1997x_subscribe_event(struct v4l2_subdev *sd,
> +				    struct v4l2_fh *fh,
> +				    struct v4l2_event_subscription *sub)
> +{
> +	switch (sub->type) {
> +	case V4L2_EVENT_SOURCE_CHANGE:
> +		return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
> +	case V4L2_EVENT_CTRL:
> +		return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct v4l2_subdev_core_ops tda1997x_core_ops = {
> +	.log_status = tda1997x_log_status,
> +	.subscribe_event = tda1997x_subscribe_event,
> +	.unsubscribe_event = v4l2_event_subdev_unsubscribe,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * v4l2_subdev_ops
> + */
> +
> +static const struct v4l2_subdev_ops tda1997x_subdev_ops = {
> +	.core = &tda1997x_core_ops,
> +	.video = &tda1997x_video_ops,
> +	.pad = &tda1997x_pad_ops,
> +};
> +
> +/* -----------------------------------------------------------------------------
> + * v4l2_controls
> + */
> +
> +static int tda1997x_s_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct v4l2_subdev *sd = to_sd(ctrl);
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	switch (ctrl->id) {
> +	/* allow overriding the default RGB quantization range */
> +	case V4L2_CID_DV_RX_RGB_RANGE:
> +		state->range = ctrl->val;

This should be assigned to state->rgb_quantization_range!

> +		tda1997x_configure_csc(sd);
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +};
> +
> +static int tda1997x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct v4l2_subdev *sd = to_sd(ctrl);
> +	struct tda1997x_state *state = to_state(sd);
> +
> +	if (ctrl->id == V4L2_CID_DV_RX_IT_CONTENT_TYPE) {
> +		ctrl->val = state->content;
> +		return 0;
> +	}
> +	return -EINVAL;
> +};
> +
> +static const struct v4l2_ctrl_ops tda1997x_ctrl_ops = {
> +	.s_ctrl = tda1997x_s_ctrl,
> +	.g_volatile_ctrl = tda1997x_g_volatile_ctrl,
> +};
> +
> +static int tda1997x_core_init(struct v4l2_subdev *sd)
> +{
> +	struct tda1997x_state *state = to_state(sd);
> +	struct tda1997x_platform_data *pdata = &state->pdata;
> +	u8 reg;
> +	int i;
> +
> +	/* disable HPD */
> +	io_write(sd, REG_HPD_AUTO_CTRL, HPD_AUTO_HPD_UNSEL);
> +	if (state->chip_revision == 0) {
> +		io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_DIS_HDCP | MAN_RST_HDCP);
> +		io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT);
> +	}
> +
> +	/* reset infoframe at end of start-up-sequencer */
> +	io_write(sd, REG_SUS_SET_RGB2, 0x06);
> +	io_write(sd, REG_SUS_SET_RGB3, 0x06);
> +
> +	/* Enable TMDS pull-ups */
> +	io_write(sd, REG_RT_MAN_CTRL, RT_MAN_CTRL_RT |
> +		 RT_MAN_CTRL_RT_B | RT_MAN_CTRL_RT_A);
> +
> +	/* enable sync measurement timing */
> +	tda1997x_cec_write(sd, REG_PWR_CONTROL & 0xff, 0x04);
> +	/* adjust CEC clock divider */
> +	tda1997x_cec_write(sd, REG_OSC_DIVIDER & 0xff, 0x03);
> +	tda1997x_cec_write(sd, REG_EN_OSC_PERIOD_LSB & 0xff, 0xa0);
> +	io_write(sd, REG_TIMER_D, 0x54);
> +	/* enable power switch */
> +	reg = tda1997x_cec_read(sd, REG_CONTROL & 0xff);
> +	reg |= 0x20;
> +	tda1997x_cec_write(sd, REG_CONTROL & 0xff, reg);
> +	mdelay(50);
> +
> +	/* read the chip version */
> +	reg = io_read(sd, REG_VERSION);
> +	/* get the chip configuration */
> +	reg = io_read(sd, REG_CMTP_REG10);
> +
> +	/* enable interrupts we care about */
> +	io_write(sd, REG_INT_MASK_TOP,
> +		 INTERRUPT_HDCP | INTERRUPT_AUDIO | INTERRUPT_INFO |
> +		 INTERRUPT_RATE | INTERRUPT_SUS);
> +	/* config_mtp,fmt,sus_end,sus_st */
> +	io_write(sd, REG_INT_MASK_SUS, MASK_MPT | MASK_FMT | MASK_SUS_END);
> +	/* rate stability change for inputs A/B */
> +	io_write(sd, REG_INT_MASK_RATE, MASK_RATE_B_ST | MASK_RATE_A_ST);
> +	/* aud,spd,avi*/
> +	io_write(sd, REG_INT_MASK_INFO,
> +		 MASK_AUD_IF | MASK_SPD_IF | MASK_AVI_IF);
> +	/* audio_freq,audio_flg,mute_flg,fifo_err */
> +	io_write(sd, REG_INT_MASK_AUDIO,
> +		 MASK_AUDIO_FREQ_FLG | MASK_AUDIO_FLG | MASK_MUTE_FLG |
> +		 MASK_ERROR_FIFO_PT);
> +	/* HDCP C5 state reached */
> +	io_write(sd, REG_INT_MASK_HDCP, MASK_STATE_C5);
> +	/* 5V detect and HDP pulse end */
> +	io_write(sd, REG_INT_MASK_DDC, MASK_DET_5V);
> +	/* don't care about AFE/MODE */
> +	io_write(sd, REG_INT_MASK_AFE, 0);
> +	io_write(sd, REG_INT_MASK_MODE, 0);
> +
> +	/* clear all interrupts */
> +	io_write(sd, REG_INT_FLG_CLR_TOP, 0xff);
> +	io_write(sd, REG_INT_FLG_CLR_SUS, 0xff);
> +	io_write(sd, REG_INT_FLG_CLR_DDC, 0xff);
> +	io_write(sd, REG_INT_FLG_CLR_RATE, 0xff);
> +	io_write(sd, REG_INT_FLG_CLR_MODE, 0xff);
> +	io_write(sd, REG_INT_FLG_CLR_INFO, 0xff);
> +	io_write(sd, REG_INT_FLG_CLR_AUDIO, 0xff);
> +	io_write(sd, REG_INT_FLG_CLR_HDCP, 0xff);
> +	io_write(sd, REG_INT_FLG_CLR_AFE, 0xff);
> +
> +	/* init TMDS equalizer */
> +	if (state->chip_revision == 0)
> +		io_write(sd, REG_CGU_DBG_SEL, 1 << CGU_DBG_CLK_SEL_SHIFT);
> +	io_write24(sd, REG_CLK_MIN_RATE, CLK_MIN_RATE);
> +	io_write24(sd, REG_CLK_MAX_RATE, CLK_MAX_RATE);
> +	if (state->chip_revision == 0)
> +		io_write(sd, REG_WDL_CFG, WDL_CFG_VAL);
> +	/* DC filter */
> +	io_write(sd, REG_DEEP_COLOR_CTRL, DC_FILTER_VAL);
> +	/* disable test pattern */
> +	io_write(sd, REG_SVC_MODE, 0x00);
> +	/* update HDMI INFO CTRL */
> +	io_write(sd, REG_INFO_CTRL, 0xff);
> +	/* write HDMI INFO EXCEED value */
> +	io_write(sd, REG_INFO_EXCEED, 3);
> +
> +	if (state->chip_revision == 0)
> +		tda1997x_reset_n1(state);
> +
> +	/*
> +	 * No HDCP acknowledge when HDCP is disabled
> +	 * and reset SUS to force format detection
> +	 */
> +	tda1997x_hdmi_info_reset(sd, NACK_HDCP, true);
> +
> +	/* Set HPD low */
> +	tda1997x_manual_hpd(sd, HPD_LOW_BP);
> +
> +	/* Configure receiver capabilities */
> +	io_write(sd, REG_HDCP_BCAPS, HDCP_HDMI | HDCP_FAST_REAUTH);
> +
> +	/* Configure HDMI: Auto HDCP mode, packet controlled mute */
> +	reg = HDMI_CTRL_MUTE_AUTO << HDMI_CTRL_MUTE_SHIFT;
> +	reg |= HDMI_CTRL_HDCP_AUTO << HDMI_CTRL_HDCP_SHIFT;
> +	io_write(sd, REG_HDMI_CTRL, reg);
> +
> +	/* reset start-up-sequencer to force format detection */
> +	tda1997x_hdmi_info_reset(sd, 0, true);
> +
> +	/* disable matrix conversion */
> +	reg = io_read(sd, REG_VDP_CTRL);
> +	reg |= VDP_CTRL_MATRIX_BP;
> +	io_write(sd, REG_VDP_CTRL, reg);
> +
> +	/* set video output mode */
> +	tda1997x_configure_vidout(state);
> +
> +	/* configure video output port */
> +	for (i = 0; i < 9; i++) {
> +		v4l_dbg(1, debug, state->client, "vidout_cfg[%d]=0x%02x\n", i,
> +			pdata->vidout_port_cfg[i]);
> +		io_write(sd, REG_VP35_32_CTRL + i, pdata->vidout_port_cfg[i]);
> +	}
> +
> +	/* configure audio output port */
> +	tda1997x_configure_audout(sd, 0);
> +
> +	/* configure audio clock freq */
> +	switch (pdata->audout_mclk_fs) {
> +	case 512:
> +		reg = AUDIO_CLOCK_SEL_512FS;
> +		break;
> +	case 256:
> +		reg = AUDIO_CLOCK_SEL_256FS;
> +		break;
> +	case 128:
> +		reg = AUDIO_CLOCK_SEL_128FS;
> +		break;
> +	case 64:
> +		reg = AUDIO_CLOCK_SEL_64FS;
> +		break;
> +	case 32:
> +		reg = AUDIO_CLOCK_SEL_32FS;
> +		break;
> +	default:
> +		reg = AUDIO_CLOCK_SEL_16FS;
> +		break;
> +	}
> +	io_write(sd, REG_AUDIO_CLOCK, reg);
> +
> +	/* reset advanced infoframes (ISRC1/ISRC2/ACP) */
> +	tda1997x_hdmi_info_reset(sd, RESET_AI, false);
> +	/* reset infoframe */
> +	tda1997x_hdmi_info_reset(sd, RESET_IF, false);
> +	/* reset audio infoframes */
> +	tda1997x_hdmi_info_reset(sd, RESET_AUDIO, false);
> +	/* reset gamut */
> +	tda1997x_hdmi_info_reset(sd, RESET_GAMUT, false);
> +
> +	/* get initial HDMI status */
> +	state->hdmi_status = io_read(sd, REG_HDMI_FLAGS);
> +
> +	return 0;
> +}
> +
> +static int tda1997x_set_power(struct tda1997x_state *state, bool on)
> +{
> +	int ret = 0;
> +
> +	if (on) {
> +		ret = regulator_bulk_enable(TDA1997X_NUM_SUPPLIES,
> +					     state->supplies);
> +		msleep(300);
> +	}
> +
> +	ret = regulator_bulk_disable(TDA1997X_NUM_SUPPLIES,
> +			       state->supplies);
> +	return ret;
> +}
> +
> +static const struct i2c_device_id tda1997x_i2c_id[] = {
> +	{"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]},
> +	{"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]},
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id);
> +
> +static const struct of_device_id tda1997x_of_id[] __maybe_unused = {
> +	{ .compatible = "nxp,tda19971", .data = &tda1997x_chip_info[TDA19971] },
> +	{ .compatible = "nxp,tda19973", .data = &tda1997x_chip_info[TDA19973] },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, tda1997x_of_id);
> +
> +static int tda1997x_parse_dt(struct tda1997x_state *state)
> +{
> +	struct tda1997x_platform_data *pdata = &state->pdata;
> +	struct v4l2_fwnode_endpoint bus_cfg;
> +	struct device_node *ep;
> +	struct device_node *np;
> +	unsigned int flags;
> +	const char *str;
> +	int ret;
> +	u32 v;
> +
> +	/*
> +	 * setup default values:
> +	 * - HREF: active high from start to end of row
> +	 * - VS: Vertical Sync active high at beginning of frame
> +	 * - DE: Active high when data valid
> +	 * - A_CLK: 128*Fs
> +	 */
> +	pdata->vidout_sel_hs = HS_HREF_SEL_HREF_VHREF;
> +	pdata->vidout_sel_vs = VS_VREF_SEL_VREF_HDMI;
> +	pdata->vidout_sel_de = DE_FREF_SEL_DE_VHREF;
> +
> +	np = state->client->dev.of_node;
> +	ep = of_graph_get_next_endpoint(np, NULL);
> +	if (!ep)
> +		return -EINVAL;
> +
> +	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
> +	if (ret) {
> +		of_node_put(ep);
> +		return ret;
> +	}
> +	of_node_put(ep);
> +
> +	/* polarity of HS/VS/DE */
> +	flags = bus_cfg.bus.parallel.flags;
> +	if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
> +		pdata->vidout_inv_hs = 1;
> +	if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
> +		pdata->vidout_inv_vs = 1;
> +	if (flags & V4L2_MBUS_DATA_ACTIVE_LOW)
> +		pdata->vidout_inv_de = 1;
> +	pdata->vidout_bus_width = bus_cfg.bus.parallel.bus_width;
> +
> +	/* video output port config */
> +	ret = of_property_count_u32_elems(np, "nxp,vidout-portcfg");
> +	if (ret > 0) {
> +		u32 reg, val, i;
> +
> +		for (i = 0; i < ret / 2 && i < 9; i++) {
> +			of_property_read_u32_index(np, "nxp,vidout-portcfg",
> +						   i * 2, &reg);
> +			of_property_read_u32_index(np, "nxp,vidout-portcfg",
> +						   i * 2 + 1, &val);
> +			if (reg < 9)
> +				pdata->vidout_port_cfg[reg] = val;
> +		}
> +	} else {
> +		v4l_err(state->client, "nxp,vidout-portcfg missing\n");
> +		return -EINVAL;
> +	}
> +
> +	/* default to channel layout dictated by packet header */
> +	pdata->audout_layoutauto = true;
> +
> +	pdata->audout_format = AUDFMT_TYPE_DISABLED;
> +	if (!of_property_read_string(np, "nxp,audout-format", &str)) {
> +		if (strcmp(str, "i2s") == 0)
> +			pdata->audout_format = AUDFMT_TYPE_I2S;
> +		else if (strcmp(str, "spdif") == 0)
> +			pdata->audout_format = AUDFMT_TYPE_SPDIF;
> +		else {
> +			v4l_err(state->client, "nxp,audout-format invalid\n");
> +			return -EINVAL;
> +		}
> +		if (!of_property_read_u32(np, "nxp,audout-layout", &v)) {
> +			switch (v) {
> +			case 0:
> +			case 1:
> +				break;
> +			default:
> +				v4l_err(state->client,
> +					"nxp,audout-layout invalid\n");
> +				return -EINVAL;
> +			}
> +			pdata->audout_layout = v;
> +		}
> +		if (!of_property_read_u32(np, "nxp,audout-width", &v)) {
> +			switch (v) {
> +			case 16:
> +			case 32:
> +				break;
> +			default:
> +				v4l_err(state->client,
> +					"nxp,audout-width invalid\n");
> +				return -EINVAL;
> +			}
> +			pdata->audout_width = v;
> +		}
> +		if (!of_property_read_u32(np, "nxp,audout-mclk-fs", &v)) {
> +			switch (v) {
> +			case 512:
> +			case 256:
> +			case 128:
> +			case 64:
> +			case 32:
> +			case 16:
> +				break;
> +			default:
> +				v4l_err(state->client,
> +					"nxp,audout-mclk-fs invalid\n");
> +				return -EINVAL;
> +			}
> +			pdata->audout_mclk_fs = v;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int tda1997x_get_regulators(struct tda1997x_state *state)
> +{
> +	int i;
> +
> +	for (i = 0; i < TDA1997X_NUM_SUPPLIES; i++)
> +		state->supplies[i].supply = tda1997x_supply_name[i];
> +
> +	return devm_regulator_bulk_get(&state->client->dev,
> +				       TDA1997X_NUM_SUPPLIES,
> +				       state->supplies);
> +}
> +
> +static int tda1997x_identify_module(struct tda1997x_state *state)
> +{
> +	struct v4l2_subdev *sd = &state->sd;
> +	enum tda1997x_type type;
> +	u8 reg;
> +
> +	/* Read chip configuration*/
> +	reg = io_read(sd, REG_CMTP_REG10);
> +	state->tmdsb_clk = (reg >> 6) & 0x01; /* use tmds clock B_inv for B */
> +	state->tmdsb_soc = (reg >> 5) & 0x01; /* tmds of input B */
> +	state->port_30bit = (reg >> 2) & 0x03; /* 30bit vs 24bit */
> +	state->output_2p5 = (reg >> 1) & 0x01; /* output supply 2.5v */
> +	switch ((reg >> 4) & 0x03) {
> +	case 0x00:
> +		type = TDA19971;
> +		break;
> +	case 0x02:
> +	case 0x03:
> +		type = TDA19973;
> +		break;
> +	default:
> +		dev_err(&state->client->dev, "unsupported chip ID\n");
> +		return -EIO;
> +	}
> +	if (state->info->type != type) {
> +		dev_err(&state->client->dev, "chip id mismatch\n");
> +		return -EIO;
> +	}
> +
> +	/* read chip revision */
> +	state->chip_revision = io_read(sd, REG_CMTP_REG11);
> +
> +	return 0;
> +}
> +
> +static const struct media_entity_operations tda1997x_media_ops = {
> +	.link_validate = v4l2_subdev_link_validate,
> +};
> +
> +
> +/* -----------------------------------------------------------------------------
> + * HDMI Audio Codec
> + */
> +
> +/* refine sample-rate based on HDMI source */
> +static int tda1997x_pcm_startup(struct snd_pcm_substream *substream,
> +				struct snd_soc_dai *dai)
> +{
> +	struct tda1997x_state *state = snd_soc_dai_get_drvdata(dai);
> +	struct snd_soc_codec *codec = dai->codec;
> +	struct snd_pcm_runtime *rtd = substream->runtime;
> +	int rate, err;
> +
> +	rate = state->audio_samplerate;
> +	err = snd_pcm_hw_constraint_minmax(rtd, SNDRV_PCM_HW_PARAM_RATE,
> +					   rate, rate);
> +	if (err < 0) {
> +		dev_err(codec->dev, "failed to constrain samplerate to %dHz\n",
> +			rate);
> +		return err;
> +	}
> +	dev_info(codec->dev, "set samplerate constraint to %dHz\n", rate);
> +
> +	return 0;
> +}
> +
> +static const struct snd_soc_dai_ops tda1997x_dai_ops = {
> +	.startup = tda1997x_pcm_startup,
> +};
> +
> +static struct snd_soc_dai_driver tda1997x_audio_dai = {
> +	.name = "tda1997x",
> +	.capture = {
> +		.stream_name = "Capture",
> +		.channels_min = 2,
> +		.channels_max = 8,
> +		.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
> +			 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
> +			 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
> +			 SNDRV_PCM_RATE_192000,
> +	},
> +	.ops = &tda1997x_dai_ops,
> +};
> +
> +static int tda1997x_codec_probe(struct snd_soc_codec *codec)
> +{
> +	return 0;
> +}
> +
> +static int tda1997x_codec_remove(struct snd_soc_codec *codec)
> +{
> +	return 0;
> +}
> +
> +static struct snd_soc_codec_driver tda1997x_codec_driver = {
> +	.probe = tda1997x_codec_probe,
> +	.remove = tda1997x_codec_remove,
> +	.reg_word_size = sizeof(u16),
> +};
> +
> +static int tda1997x_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *id)
> +{
> +	struct tda1997x_state *state;
> +	struct tda1997x_platform_data *pdata;
> +	struct v4l2_subdev *sd;
> +	struct v4l2_ctrl_handler *hdl;
> +	struct v4l2_ctrl *ctrl;
> +	static const struct v4l2_dv_timings cea1920x1080 =
> +		V4L2_DV_BT_CEA_1920X1080P60;
> +	int ret;
> +
> +	/* Check if the adapter supports the needed features */
> +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
> +		return -EIO;
> +
> +	state = kzalloc(sizeof(struct tda1997x_state), GFP_KERNEL);
> +	if (!state)
> +		return -ENOMEM;
> +
> +	state->client = client;
> +	pdata = &state->pdata;
> +	if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) {
> +		const struct of_device_id *oid;
> +
> +		oid = of_match_node(tda1997x_of_id, client->dev.of_node);
> +		state->info = oid->data;
> +
> +		ret = tda1997x_parse_dt(state);
> +		if (ret < 0) {
> +			v4l_err(client, "DT parsing error\n");
> +			goto err_free_state;
> +		}
> +	} else if (client->dev.platform_data) {
> +		struct tda1997x_platform_data *pdata =
> +			client->dev.platform_data;
> +		state->info =
> +			(const struct tda1997x_chip_info *)id->driver_data;
> +		state->pdata = *pdata;
> +	} else {
> +		v4l_err(client, "No platform data\n");
> +		ret = -ENODEV;
> +		goto err_free_state;
> +	}
> +
> +	ret = tda1997x_get_regulators(state);
> +	if (ret)
> +		goto err_free_state;
> +
> +	ret = tda1997x_set_power(state, 1);
> +	if (ret)
> +		goto err_free_state;
> +
> +	mutex_init(&state->page_lock);
> +	mutex_init(&state->lock);
> +	state->page = 0xff;
> +
> +	INIT_DELAYED_WORK(&state->delayed_work_enable_hpd,
> +			  tda1997x_delayed_work_enable_hpd);
> +
> +	/* set video format based on chip and bus width */
> +	ret = tda1997x_identify_module(state);
> +	if (ret)
> +		goto err_free_mutex;
> +
> +	/* initialize subdev */
> +	sd = &state->sd;
> +	v4l2_i2c_subdev_init(sd, client, &tda1997x_subdev_ops);
> +	snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",
> +		 id->name, i2c_adapter_id(client->adapter),
> +		 client->addr);
> +	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
> +	sd->entity.ops = &tda1997x_media_ops;
> +
> +	switch (state->info->type) {
> +	case TDA19973:
> +		switch (pdata->vidout_bus_width) {
> +		case 36: /* 36bit RGB (could also be 36bit YUV) */
> +			state->code = MEDIA_BUS_FMT_YUV12_1X36;
> +			break;
> +		case 24: /* 24bit BT656 (YUV422 semi-planar: 1-cycle) */
> +			state->code = MEDIA_BUS_FMT_UYVY12_1X24;
> +			break;
> +		case 12: /* 12bit BT656 (2-cycle) */
> +			state->code = MEDIA_BUS_FMT_UYVY12_2X12;
> +			break;
> +		}
> +		break;
> +	case TDA19971:
> +		switch (pdata->vidout_bus_width) {
> +		case 24: /* 24bit YUV (could also be RGB or YUV422) */
> +			state->code = MEDIA_BUS_FMT_YUV8_1X24;
> +			break;
> +		case 20: /* 20bit YUV422 */
> +			state->code = MEDIA_BUS_FMT_UYVY10_1X20;
> +			break;
> +		case 16: /* 16bit BT656 (YUV422 semi-planar: 1-cycle) */
> +			state->code = MEDIA_BUS_FMT_UYVY8_1X16;
> +			break;
> +		case 12: /* 12bit BT656 (2-cycle) */
> +			state->code = MEDIA_BUS_FMT_UYVY12_2X12;
> +			break;
> +		case 10: /* 10bit BT656 (2-cycle) */
> +			state->code = MEDIA_BUS_FMT_UYVY10_2X10;
> +			break;
> +		case 8: /* 8bit BT656 (2-cycle) */
> +			state->code = MEDIA_BUS_FMT_UYVY8_2X8;
> +			break;
> +		}
> +	}
> +	switch (state->code) {
> +	case MEDIA_BUS_FMT_RGB121212_1X36:
> +	case MEDIA_BUS_FMT_RGB888_1X24:
> +	case MEDIA_BUS_FMT_YUV12_1X36:
> +	case MEDIA_BUS_FMT_YUV8_1X24:
> +		state->bus_type = V4L2_MBUS_PARALLEL;
> +		state->vid_fmt = OF_FMT_444;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY12_1X24:
> +	case MEDIA_BUS_FMT_UYVY8_1X16:
> +		state->bus_type = V4L2_MBUS_BT656;
> +		state->vid_fmt = OF_FMT_422_SMPT;
> +		break;
> +	case MEDIA_BUS_FMT_UYVY12_2X12:
> +	case MEDIA_BUS_FMT_UYVY10_2X10:
> +	case MEDIA_BUS_FMT_UYVY8_2X8:
> +		state->bus_type = V4L2_MBUS_BT656;
> +		state->vid_fmt = OF_FMT_422_CCIR;
> +		break;
> +	default:
> +		v4l_err(state->client, "incompatible code (%x)\n", state->code);
> +		ret = -EINVAL;
> +		goto err_free_mutex;
> +	}
> +	state->colorspace = HDMI_COLORSPACE_RGB;
> +	state->colorimetry = HDMI_COLORIMETRY_NONE;
> +	state->timings = cea1920x1080;
> +
> +	/* disable/reset HDCP to get correct I2C access to Rx HDMI */
> +	io_write(sd, REG_MAN_SUS_HDMI_SEL, MAN_RST_HDCP | MAN_DIS_HDCP);
> +
> +	/*
> +	 * if N2 version, reset compdel_bp as it may generate some small pixel
> +	 * shifts in case of embedded sync/or delay lower than 4
> +	 */
> +	if (state->chip_revision != 0) {
> +		io_write(sd, REG_MAN_SUS_HDMI_SEL, 0x00);
> +		io_write(sd, REG_VDP_CTRL, 0x1f);
> +	}
> +
> +	v4l_info(client, "NXP %s N%d detected\n", state->info->name,
> +		 state->chip_revision + 1);
> +	v4l_info(client, "video: %dbit %s %s MBUS_FMT=0x%x\n",
> +		pdata->vidout_bus_width,
> +		(state->bus_type == V4L2_MBUS_PARALLEL) ? "parallel" : "BT656",
> +		vidfmt_names[state->vid_fmt],
> +		state->code);
> +	if (pdata->audout_format) {
> +		v4l_info(client, "audio: %dch %s layout%d sysclk=%d*fs\n",
> +			 pdata->audout_layout ? 2 : 8,
> +			 audfmt_names[pdata->audout_format],
> +			 pdata->audout_layout,
> +			 pdata->audout_mclk_fs);
> +	}
> +
> +	ret = 0x34 + ((io_read(sd, REG_SLAVE_ADDR)>>4) & 0x03);
> +	state->client_cec = i2c_new_dummy(client->adapter, ret);
> +	v4l_info(client, "CEC slave address 0x%02x\n", ret);
> +
> +	ret = tda1997x_core_init(sd);
> +	if (ret)
> +		goto err_free_mutex;
> +
> +	/* control handlers */
> +	hdl = &state->hdl;
> +	v4l2_ctrl_handler_init(hdl, 5);
> +	ctrl = v4l2_ctrl_new_std_menu(hdl, &tda1997x_ctrl_ops,
> +			V4L2_CID_DV_RX_IT_CONTENT_TYPE,
> +			V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0,
> +			V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
> +	if (ctrl)
> +		ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
> +	/* custom controls */
> +	state->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
> +			V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0);
> +	state->rgb_quantization_range_ctrl = v4l2_ctrl_new_std_menu(hdl,
> +			&tda1997x_ctrl_ops,
> +			V4L2_CID_DV_RX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, 0,
> +			V4L2_DV_RGB_RANGE_AUTO);
> +
> +	/* initialize source pads */
> +	state->pads[TDA1997X_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
> +	ret = media_entity_pads_init(&sd->entity, TDA1997X_NUM_PADS,
> +		state->pads);
> +	if (ret) {
> +		v4l_err(client, "failed entity_init: %d", ret);
> +		goto err_free_mutex;
> +	}
> +
> +	ret = v4l2_async_register_subdev(sd);
> +	if (ret)
> +		goto err_free_media;
> +
> +	/* register audio DAI */
> +	if (pdata->audout_format) {
> +		u64 formats;
> +
> +		if (pdata->audout_width == 32)
> +			formats = SNDRV_PCM_FMTBIT_S32_LE;
> +		else
> +			formats = SNDRV_PCM_FMTBIT_S16_LE;
> +		tda1997x_audio_dai.capture.formats = formats;
> +		ret = snd_soc_register_codec(&state->client->dev,
> +					     &tda1997x_codec_driver,
> +					     &tda1997x_audio_dai, 1);
> +		if (ret) {
> +			dev_err(&client->dev, "register audio codec failed\n");
> +			goto err_free_media;
> +		}
> +		dev_set_drvdata(&state->client->dev, state);
> +		v4l_info(state->client, "registered audio codec\n");
> +	}
> +
> +	/* request irq */
> +	ret = devm_request_threaded_irq(&client->dev, client->irq,
> +					NULL, tda1997x_isr_thread,
> +					IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> +					KBUILD_MODNAME, state);
> +	if (ret) {
> +		v4l_err(client, "irq%d reg failed: %d\n", client->irq, ret);
> +		goto err_free_media;
> +	}
> +
> +	return 0;
> +
> +err_free_media:
> +	media_entity_cleanup(&sd->entity);
> +err_free_mutex:
> +	cancel_delayed_work(&state->delayed_work_enable_hpd);
> +	mutex_destroy(&state->page_lock);
> +	mutex_destroy(&state->lock);
> +err_free_state:
> +	kfree(state);
> +	dev_err(&client->dev, "%s failed: %d\n", __func__, ret);
> +
> +	return ret;
> +}
> +
> +static int tda1997x_remove(struct i2c_client *client)
> +{
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct tda1997x_state *state = to_state(sd);
> +	struct tda1997x_platform_data *pdata = &state->pdata;
> +
> +	if (pdata->audout_format) {
> +		snd_soc_unregister_codec(&client->dev);
> +		mutex_destroy(&state->audio_lock);
> +	}
> +
> +	disable_irq(state->client->irq);
> +	tda1997x_power_mode(state, 0);
> +
> +	v4l2_async_unregister_subdev(sd);
> +	media_entity_cleanup(&sd->entity);
> +	regulator_bulk_disable(TDA1997X_NUM_SUPPLIES, state->supplies);
> +	i2c_unregister_device(state->client_cec);
> +	cancel_delayed_work(&state->delayed_work_enable_hpd);
> +	mutex_destroy(&state->page_lock);
> +	mutex_destroy(&state->lock);
> +
> +	kfree(state);
> +
> +	return 0;
> +}
> +
> +static struct i2c_driver tda1997x_i2c_driver = {
> +	.driver = {
> +		.name = "tda1997x",
> +		.owner = THIS_MODULE,
> +		.of_match_table = of_match_ptr(tda1997x_of_id),
> +	},
> +	.probe = tda1997x_probe,
> +	.remove = tda1997x_remove,
> +	.id_table = tda1997x_i2c_id,
> +};
> +
> +module_i2c_driver(tda1997x_i2c_driver);
> +
> +MODULE_AUTHOR("Tim Harvey <tharvey-UMMOYl/HMS+akBO8gow8eQ@public.gmane.org>");
> +MODULE_DESCRIPTION("TDA1997X HDMI Receiver driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/media/i2c/tda1997x.h b/include/media/i2c/tda1997x.h
> new file mode 100644
> index 0000000..50cb847
> --- /dev/null
> +++ b/include/media/i2c/tda1997x.h
> @@ -0,0 +1,53 @@
> +/*
> + * tda1997x - NXP HDMI receiver
> + *
> + * Copyright 2017 Tim Harvey <tharvey-UMMOYl/HMS+akBO8gow8eQ@public.gmane.org>
> + *
> + * This program is free software; you may redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + *
> + * 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.
> + *
> + */
> +
> +#ifndef _TDA1997X_
> +#define _TDA1997X_
> +
> +/* Platform Data */
> +struct tda1997x_platform_data {
> +	u32 vidout_bus_width;
> +	u8 vidout_port_cfg[9];
> +	/* pin polarity (1=invert) */
> +	bool vidout_inv_de;
> +	bool vidout_inv_hs;
> +	bool vidout_inv_vs;
> +	bool vidout_inv_pclk;
> +	/* clock delays (0=-8, 1=-7 ... 15=+7 pixels) */
> +	u8 vidout_delay_hs;
> +	u8 vidout_delay_vs;
> +	u8 vidout_delay_de;
> +	u8 vidout_delay_pclk;
> +	/* sync selections (controls how sync pins are derived) */
> +	u8 vidout_sel_hs;
> +	u8 vidout_sel_vs;
> +	u8 vidout_sel_de;
> +
> +	/* Audio Port Output */
> +	int audout_format;
> +	u32 audout_mclk_fs;	/* clock multiplier */
> +	u32 audout_width;	/* 13 or 32 bit */
> +	u32 audout_layout;	/* layout0=AP0 layout1=AP0,AP1,AP2,AP3 */
> +	bool audout_layoutauto;	/* audio layout dictated by pkt header */
> +	bool audout_invert_clk;	/* data valid on rising edge of BCLK */
> +	bool audio_auto_mute;	/* enable hardware audio auto-mute */
> +};
> +
> +#endif
> 

Regards,

	Hans
--
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 v13 2/4] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver
From: Philippe Ombredanne @ 2017-12-04 12:41 UTC (permalink / raw)
  To: Oleksandr Shamray
  Cc: Greg Kroah-Hartman, Arnd Bergmann, LKML,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	OpenBMC Maillist, joel, Jiří Pírko, Tobias Klauser,
	linux-serial, vadimp, system-sw-low-level, Rob Herring,
	openocd-devel-owner, linux-api, David S. Miller,
	Mauro Carvalho Chehab, Jiri Pirko
In-Reply-To: <1512382405-14774-3-git-send-email-oleksandrs@mellanox.com>

Oleksandr,

On Mon, Dec 4, 2017 at 11:13 AM, Oleksandr Shamray
<oleksandrs@mellanox.com> wrote:
[...]
> 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
[...]
> diff --git a/drivers/jtag/jtag-aspeed.c b/drivers/jtag/jtag-aspeed.c
> new file mode 100644
> index 0000000..2ff46b0
> --- /dev/null
> +++ b/drivers/jtag/jtag-aspeed.c
> @@ -0,0 +1,784 @@
> +/* SPDX-License-Identifier: GPL-2.0

Sorry to nit pick on this but the comment style should be // here IMHO
This is what Linus requested, e.g.:

> +// SPDX-License-Identifier: GPL-2.0

For reference, the doc is being written:Thomas (tglx) has already
submitted a first series of doc patches a few weeks ago. And AFAIK he
might be working on posting the updates soon, whenever his real time
clock yields a few cycles away from real time coding work ;)

See also these discussions with Linus [1][2][3], Thomas[4] and Greg[5]
on this and mostly related topics, where you will see Linus rationale
for the C++ comment stylle

[1] https://lkml.org/lkml/2017/11/2/715
[2] https://lkml.org/lkml/2017/11/25/125
[3] https://lkml.org/lkml/2017/11/25/133
[4] https://lkml.org/lkml/2017/11/2/805
[5] https://lkml.org/lkml/2017/10/19/165


-- 
Cordially
Philippe Ombredanne

^ permalink raw reply

* [PATCH v4.1] phylib: Add device reset GPIO support
From: Geert Uytterhoeven @ 2017-12-04 12:35 UTC (permalink / raw)
  To: David S . Miller, Andrew Lunn, Florian Fainelli, Simon Horman,
	Magnus Damm
  Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Nicolas Ferre,
	Richard Leitner, netdev, devicetree, linux-renesas-soc,
	linux-kernel, Geert Uytterhoeven
In-Reply-To: <1512383692-14009-2-git-send-email-geert+renesas@glider.be>

From: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

The PHY devices sometimes do have their reset signal (maybe even power
supply?) tied to some GPIO and sometimes it also does happen that a boot
loader does not leave it deasserted. So far this issue has been attacked
from (as I believe) a wrong angle: by teaching the MAC driver to manipulate
the GPIO in question; that solution, when applied to the device trees, led
to adding the PHY reset GPIO properties to the MAC device node, with one
exception: Cadence MACB driver which could handle the "reset-gpios" prop
in a PHY device subnode. I believe that the correct approach is to teach
the 'phylib' to get the MDIO device reset GPIO from the device tree node
corresponding to this device -- which this patch is doing...

Note that I had to modify the AT803x PHY driver as it would stop working
otherwise -- it made use of the reset GPIO for its own purposes...

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Acked-by: Rob Herring <robh@kernel.org>
[geert: Propagate actual errors from fwnode_get_named_gpiod()]
[geert: Avoid destroying initial setup]
[geert: Consolidate GPIO descriptor acquiring code]
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4.1:
  - Fix uninitialized variable in mdiobus_register_device(),

v4:
  - Remove Florian's Acked-by,
  - Add missing #include <linux/gpio/consumer.h>,
  - Re-add the gpiod check, as the dummy gpiod_set_value() for !GPIOLIB
    does not ignore NULL, and calls WARN_ON(1),
  - Do not reassert the reset signal if {mdio,phy}_probe() or
    phy_device_register() succeeded, as that may destroy initial setup,
  - Do not deassert the reset signal in {mdio,phy}_remove(), as it
    should already be deasserted,
  - Bring the PHY back into reset state in phy_device_remove(),
  - Move/consolidate GPIO descriptor acquiring code from
    of_mdiobus_register_phy() and of_mdiobus_register_device() to
    mdiobus_register_device().
    Note that this changes behavior slightly, in that the reset signal
    is now also asserted when called from of_mdiobus_register_device().

v3:
  - Fix fwnode_get_named_gpiod() call due to added parameters (which
    allowed to eliminate the gpiod_direction_output() call),
  - Undelete one blank line in the AT803x driver,
  - Resolve rejects, refresh patch,
  - Reword/reformat changelog,
  - Take over from Sergei,
  - Add Acked-by,
  - Remove unneeded gpiod check, as gpiod_set_value() handles NULL fine,
  - Handle fwnode_get_named_gpiod() errors correctly:
      - -ENOENT is ignored (the GPIO is optional), and turned into NULL,
        which allowed to remove all later !IS_ERR() checks,
      - Other errors (incl. -EPROBE_DEFER) are propagated [*],

v2:
  - Reformat changelog,
  - Resolve rejects, refresh patch.
---
 Documentation/devicetree/bindings/net/phy.txt |  2 ++
 drivers/net/phy/at803x.c                      | 18 +++------------
 drivers/net/phy/mdio_bus.c                    | 21 ++++++++++++++++++
 drivers/net/phy/mdio_device.c                 | 25 +++++++++++++++++++--
 drivers/net/phy/phy_device.c                  | 32 +++++++++++++++++++++++++--
 include/linux/mdio.h                          |  3 +++
 include/linux/phy.h                           |  5 +++++
 7 files changed, 87 insertions(+), 19 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/phy.txt b/Documentation/devicetree/bindings/net/phy.txt
index 77d0b2a61ffa96fc..c05479f5ac7cc837 100644
--- a/Documentation/devicetree/bindings/net/phy.txt
+++ b/Documentation/devicetree/bindings/net/phy.txt
@@ -53,6 +53,8 @@ Optional Properties:
   to ensure the integrated PHY is used. The absence of this property indicates
   the muxers should be configured so that the external PHY is used.
 
+- reset-gpios: The GPIO phandle and specifier for the PHY reset signal.
+
 Example:
 
 ethernet-phy@0 {
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index de7dd6566df7a364..29da7a3c7a3761c0 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -71,7 +71,6 @@ MODULE_LICENSE("GPL");
 
 struct at803x_priv {
 	bool phy_reset:1;
-	struct gpio_desc *gpiod_reset;
 };
 
 struct at803x_context {
@@ -254,22 +253,11 @@ static int at803x_probe(struct phy_device *phydev)
 {
 	struct device *dev = &phydev->mdio.dev;
 	struct at803x_priv *priv;
-	struct gpio_desc *gpiod_reset;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
-	if (phydev->drv->phy_id != ATH8030_PHY_ID)
-		goto does_not_require_reset_workaround;
-
-	gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(gpiod_reset))
-		return PTR_ERR(gpiod_reset);
-
-	priv->gpiod_reset = gpiod_reset;
-
-does_not_require_reset_workaround:
 	phydev->priv = priv;
 
 	return 0;
@@ -343,14 +331,14 @@ static void at803x_link_change_notify(struct phy_device *phydev)
 	 * cannot recover from by software.
 	 */
 	if (phydev->state == PHY_NOLINK) {
-		if (priv->gpiod_reset && !priv->phy_reset) {
+		if (phydev->mdio.reset && !priv->phy_reset) {
 			struct at803x_context context;
 
 			at803x_context_save(phydev, &context);
 
-			gpiod_set_value(priv->gpiod_reset, 1);
+			phy_device_reset(phydev, 1);
 			msleep(1);
-			gpiod_set_value(priv->gpiod_reset, 0);
+			phy_device_reset(phydev, 0);
 			msleep(1);
 
 			at803x_context_restore(phydev, &context);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 2df7b62c1a36811e..8f8b7747c54bc478 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -38,6 +38,7 @@
 #include <linux/phy.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/gpio/consumer.h>
 
 #include <asm/irq.h>
 
@@ -48,9 +49,26 @@
 
 int mdiobus_register_device(struct mdio_device *mdiodev)
 {
+	struct gpio_desc *gpiod = NULL;
+
 	if (mdiodev->bus->mdio_map[mdiodev->addr])
 		return -EBUSY;
 
+	/* Deassert the optional reset signal */
+	if (mdiodev->dev.of_node)
+		gpiod = fwnode_get_named_gpiod(&mdiodev->dev.of_node->fwnode,
+					       "reset-gpios", 0, GPIOD_OUT_LOW,
+					       "PHY reset");
+	if (PTR_ERR(gpiod) == -ENOENT)
+		gpiod = NULL;
+	else if (IS_ERR(gpiod))
+		return PTR_ERR(gpiod);
+
+	mdiodev->reset = gpiod;
+
+	/* Assert the reset signal again */
+	mdio_device_reset(mdiodev, 1);
+
 	mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
 
 	return 0;
@@ -420,6 +438,9 @@ void mdiobus_unregister(struct mii_bus *bus)
 		if (!mdiodev)
 			continue;
 
+		if (mdiodev->reset)
+			gpiod_put(mdiodev->reset);
+
 		mdiodev->device_remove(mdiodev);
 		mdiodev->device_free(mdiodev);
 	}
diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c
index e24f28924af8953d..75d97dd9fb281704 100644
--- a/drivers/net/phy/mdio_device.c
+++ b/drivers/net/phy/mdio_device.c
@@ -12,6 +12,8 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
@@ -114,6 +116,13 @@ void mdio_device_remove(struct mdio_device *mdiodev)
 }
 EXPORT_SYMBOL(mdio_device_remove);
 
+void mdio_device_reset(struct mdio_device *mdiodev, int value)
+{
+	if (mdiodev->reset)
+		gpiod_set_value(mdiodev->reset, value);
+}
+EXPORT_SYMBOL(mdio_device_reset);
+
 /**
  * mdio_probe - probe an MDIO device
  * @dev: device to probe
@@ -128,8 +137,16 @@ static int mdio_probe(struct device *dev)
 	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
 	int err = 0;
 
-	if (mdiodrv->probe)
+	if (mdiodrv->probe) {
+		/* Deassert the reset signal */
+		mdio_device_reset(mdiodev, 0);
+
 		err = mdiodrv->probe(mdiodev);
+		if (err) {
+			/* Assert the reset signal */
+			mdio_device_reset(mdiodev, 1);
+		}
+	}
 
 	return err;
 }
@@ -140,9 +157,13 @@ static int mdio_remove(struct device *dev)
 	struct device_driver *drv = mdiodev->dev.driver;
 	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
 
-	if (mdiodrv->remove)
+	if (mdiodrv->remove) {
 		mdiodrv->remove(mdiodev);
 
+		/* Assert the reset signal */
+		mdio_device_reset(mdiodev, 1);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 8154fb706751e8ad..1de5e242b8b4cda3 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -632,6 +632,9 @@ int phy_device_register(struct phy_device *phydev)
 	if (err)
 		return err;
 
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
 	/* Run all of the fixups for this PHY */
 	err = phy_scan_fixups(phydev);
 	if (err) {
@@ -650,6 +653,9 @@ int phy_device_register(struct phy_device *phydev)
 	return 0;
 
  out:
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
 	mdiobus_unregister_device(&phydev->mdio);
 	return err;
 }
@@ -666,6 +672,10 @@ EXPORT_SYMBOL(phy_device_register);
 void phy_device_remove(struct phy_device *phydev)
 {
 	device_del(&phydev->mdio.dev);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
 	mdiobus_unregister_device(&phydev->mdio);
 }
 EXPORT_SYMBOL(phy_device_remove);
@@ -849,6 +859,9 @@ int phy_init_hw(struct phy_device *phydev)
 {
 	int ret = 0;
 
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
 	if (!phydev->drv || !phydev->drv->config_init)
 		return 0;
 
@@ -1126,6 +1139,9 @@ void phy_detach(struct phy_device *phydev)
 	put_device(&phydev->mdio.dev);
 	if (ndev_owner != bus->owner)
 		module_put(bus->owner);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
 }
 EXPORT_SYMBOL(phy_detach);
 
@@ -1811,8 +1827,16 @@ static int phy_probe(struct device *dev)
 	/* Set the state to READY by default */
 	phydev->state = PHY_READY;
 
-	if (phydev->drv->probe)
+	if (phydev->drv->probe) {
+		/* Deassert the reset signal */
+		phy_device_reset(phydev, 0);
+
 		err = phydev->drv->probe(phydev);
+		if (err) {
+			/* Assert the reset signal */
+			phy_device_reset(phydev, 1);
+		}
+	}
 
 	mutex_unlock(&phydev->lock);
 
@@ -1829,8 +1853,12 @@ static int phy_remove(struct device *dev)
 	phydev->state = PHY_DOWN;
 	mutex_unlock(&phydev->lock);
 
-	if (phydev->drv && phydev->drv->remove)
+	if (phydev->drv && phydev->drv->remove) {
 		phydev->drv->remove(phydev);
+
+		/* Assert the reset signal */
+		phy_device_reset(phydev, 1);
+	}
 	phydev->drv = NULL;
 
 	return 0;
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index ca08ab16ecdc9b78..92d4e55ffe675637 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -12,6 +12,7 @@
 #include <uapi/linux/mdio.h>
 #include <linux/mod_devicetable.h>
 
+struct gpio_desc;
 struct mii_bus;
 
 /* Multiple levels of nesting are possible. However typically this is
@@ -39,6 +40,7 @@ struct mdio_device {
 	/* Bus address of the MDIO device (0-31) */
 	int addr;
 	int flags;
+	struct gpio_desc *reset;
 };
 #define to_mdio_device(d) container_of(d, struct mdio_device, dev)
 
@@ -71,6 +73,7 @@ void mdio_device_free(struct mdio_device *mdiodev);
 struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
 int mdio_device_register(struct mdio_device *mdiodev);
 void mdio_device_remove(struct mdio_device *mdiodev);
+void mdio_device_reset(struct mdio_device *mdiodev, int value);
 int mdio_driver_register(struct mdio_driver *drv);
 void mdio_driver_unregister(struct mdio_driver *drv);
 int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 4962af37722ac2ea..6106ed915841c97e 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -841,6 +841,11 @@ int phy_aneg_done(struct phy_device *phydev);
 int phy_stop_interrupts(struct phy_device *phydev);
 int phy_restart_aneg(struct phy_device *phydev);
 
+static inline void phy_device_reset(struct phy_device *phydev, int value)
+{
+	mdio_device_reset(&phydev->mdio, value);
+}
+
 #define phydev_err(_phydev, format, args...)	\
 	dev_err(&_phydev->mdio.dev, format, ##args)
 
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH 11/12] ARM: dts: imx6qdl-nitrogen6_max: Remove unneeded unit address
From: Fabio Estevam @ 2017-12-04 11:32 UTC (permalink / raw)
  To: Gary Bisson
  Cc: Shawn Guo,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Fabio Estevam
In-Reply-To: <20171204113120.wbofrsoxl2ekd3ch-gtbVdJvtf7D/PtFMR13I2A@public.gmane.org>

Hi Gary,

On Mon, Dec 4, 2017 at 9:31 AM, Gary Bisson
<gary.bisson-Q5RJGjKts06CY9SHAMCTRUEOCMrvLtNR@public.gmane.org> wrote:

> Yes exactly, I find it clearer, what do you think?

Yes, I will change it like this.

Thanks
--
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 11/12] ARM: dts: imx6qdl-nitrogen6_max: Remove unneeded unit address
From: Gary Bisson @ 2017-12-04 11:31 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: Shawn Guo,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Fabio Estevam
In-Reply-To: <CAOMZO5C77f9mQQWJ5GJH3J+cj4Fxu3H-3yQYeFNDxm3xfpe8Gw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

Hi Fabio,

On Mon, Dec 04, 2017 at 09:28:08AM -0200, Fabio Estevam wrote:
> Hi Gary,
> 
> On Mon, Dec 4, 2017 at 9:19 AM, Gary Bisson
> <gary.bisson-Q5RJGjKts06CY9SHAMCTRUEOCMrvLtNR@public.gmane.org> wrote:
> 
> > What about naming it i2c2mux and i2c3mux so it is clearer those mux
> > happen respectively for i2c2 and i2c3?
> >
> > Then I'd rename the sub-nodes i2cXmux@Y instead of i2cX@Y currently.
> >
> > Let me know your thoughts.
> 
> Is this what you suggest?

Yes exactly, I find it clearer, what do you think?

Regards,
Gary
--
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 11/12] ARM: dts: imx6qdl-nitrogen6_max: Remove unneeded unit address
From: Fabio Estevam @ 2017-12-04 11:28 UTC (permalink / raw)
  To: Gary Bisson
  Cc: Shawn Guo,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Fabio Estevam
In-Reply-To: <20171204111954.v7jexbtkjyaa6mhe-gtbVdJvtf7D/PtFMR13I2A@public.gmane.org>

Hi Gary,

On Mon, Dec 4, 2017 at 9:19 AM, Gary Bisson
<gary.bisson-Q5RJGjKts06CY9SHAMCTRUEOCMrvLtNR@public.gmane.org> wrote:

> What about naming it i2c2mux and i2c3mux so it is clearer those mux
> happen respectively for i2c2 and i2c3?
>
> Then I'd rename the sub-nodes i2cXmux@Y instead of i2cX@Y currently.
>
> Let me know your thoughts.

Is this what you suggest?

--- a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
@@ -172,7 +172,7 @@
                };
        };

-       i2cmux@2 {
+       i2c2mux {
                compatible = "i2c-mux-gpio";
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_i2c2mux>;
@@ -183,20 +183,20 @@
                i2c-parent = <&i2c2>;
                idle-state = <0>;

-               i2c2@1 {
+               i2c2mux@1 {
                        reg = <1>;
                        #address-cells = <1>;
                        #size-cells = <0>;
                };

-               i2c2@2 {
+               i2c2mux@2 {
                        reg = <2>;
                        #address-cells = <1>;
                        #size-cells = <0>;
                };
        };

-       i2cmux@3 {
+       i2c3mux {
                compatible = "i2c-mux-gpio";
                pinctrl-names = "default";
                pinctrl-0 = <&pinctrl_i2c3mux>;
@@ -206,7 +206,7 @@
                i2c-parent = <&i2c3>;
                idle-state = <0>;

-               i2c3@1 {
+               i2c3mux@1 {
                        reg = <1>;
                        #address-cells = <1>;
                        #size-cells = <0>;
--
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 11/12] ARM: dts: imx6qdl-nitrogen6_max: Remove unneeded unit address
From: Gary Bisson @ 2017-12-04 11:19 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: shawnguo-DgEjT+Ai2ygdnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Fabio Estevam
In-Reply-To: <1512156285-18451-11-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Hi Fabio,

On Fri, Dec 01, 2017 at 05:24:44PM -0200, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> 
> Remove the unneeded unit address for the i2cmux nodes in order to fix
> the following build warnings with W=1:
> 
> arch/arm/boot/dts/imx6q-nitrogen6_max.dtb: Warning (unit_address_vs_reg): Node /i2cmux@2 has a unit name, but no reg property
> arch/arm/boot/dts/imx6q-nitrogen6_max.dtb: Warning (unit_address_vs_reg): Node /i2cmux@3 has a unit name, but no reg property
> 
> Cc: Gary Bisson <gary.bisson-Q5RJGjKts06CY9SHAMCTRUEOCMrvLtNR@public.gmane.org>
> Signed-off-by: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> ---
>  arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
> index 3a77f0f..3cdffed 100644
> --- a/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
> +++ b/arch/arm/boot/dts/imx6qdl-nitrogen6_max.dtsi
> @@ -172,7 +172,7 @@
>  		};
>  	};
>  
> -	i2cmux@2 {
> +	i2cmux2 {
>  		compatible = "i2c-mux-gpio";
>  		pinctrl-names = "default";
>  		pinctrl-0 = <&pinctrl_i2c2mux>;
> @@ -196,7 +196,7 @@
>  		};
>  	};
>  
> -	i2cmux@3 {
> +	i2cmux3 {
>  		compatible = "i2c-mux-gpio";
>  		pinctrl-names = "default";
>  		pinctrl-0 = <&pinctrl_i2c3mux>;
> -- 

What about naming it i2c2mux and i2c3mux so it is clearer those mux
happen respectively for i2c2 and i2c3?

Then I'd rename the sub-nodes i2cXmux@Y instead of i2cX@Y currently.

Let me know your thoughts.

Regards,
Gary
--
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 04/12] ARM: dts: imx53-tx53-x03x: Move display node out of 'soc'
From: Lothar Waßmann @ 2017-12-04 11:10 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: shawnguo-DgEjT+Ai2ygdnm+yROfE0A,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Fabio Estevam
In-Reply-To: <1512156285-18451-4-git-send-email-festevam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

Hi,

On Fri,  1 Dec 2017 17:24:37 -0200 Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> 
> Move disp0 node from soc node to root node.
> 
> disp0 node does not have any register properties and thus
> shouldn't be placed on the bus.
> 
> This fixes the following build warnings with W=1:
> 
> arch/arm/boot/dts/imx53-tx53-x03x.dtb: Warning (simple_bus_reg): Node /soc/disp0 missing or empty reg/ranges property
> 
> Cc: Lothar Waßmann <LW-bxm8fMRDkQLDiMYJYoSAnRvVK+yQ3ZXh@public.gmane.org>
> Signed-off-by: Fabio Estevam <fabio.estevam-3arQi8VN3Tc@public.gmane.org>
> ---
>  arch/arm/boot/dts/imx53-tx53-x03x.dts | 232 +++++++++++++++++-----------------
>  1 file changed, 115 insertions(+), 117 deletions(-)
> 
> diff --git a/arch/arm/boot/dts/imx53-tx53-x03x.dts b/arch/arm/boot/dts/imx53-tx53-x03x.dts
> index 7eb53e4..fe15c95 100644
> --- a/arch/arm/boot/dts/imx53-tx53-x03x.dts
> +++ b/arch/arm/boot/dts/imx53-tx53-x03x.dts
> @@ -53,132 +53,130 @@
>  		display = &display;
>  	};
>  
> -	soc {
> -		display: disp0 {
> -			compatible = "fsl,imx-parallel-display";
> -			interface-pix-fmt = "rgb24";
> -			pinctrl-names = "default";
> -			pinctrl-0 = <&pinctrl_rgb24_vga1>;
> -			status = "okay";
> +	display: disp0 {
> +		compatible = "fsl,imx-parallel-display";
> +		interface-pix-fmt = "rgb24";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_rgb24_vga1>;
> +		status = "okay";
>  
> -			port {
> -				display0_in: endpoint {
> -					remote-endpoint = <&ipu_di0_disp0>;
> -				};
> +		port {
> +			display0_in: endpoint {
> +				remote-endpoint = <&ipu_di0_disp0>;
>  			};
> +		};
>  
> -			display-timings {
> -				VGA {
> -					clock-frequency = <25200000>;
> -					hactive = <640>;
> -					vactive = <480>;
> -					hback-porch = <48>;
> -					hsync-len = <96>;
> -					hfront-porch = <16>;
> -					vback-porch = <31>;
> -					vsync-len = <2>;
> -					vfront-porch = <12>;
> -					hsync-active = <0>;
> -					vsync-active = <0>;
> -					de-active = <1>;
> -					pixelclk-active = <0>;
> -				};
> +		display-timings {
> +			VGA {
> +				clock-frequency = <25200000>;
> +				hactive = <640>;
> +				vactive = <480>;
> +				hback-porch = <48>;
> +				hsync-len = <96>;
> +				hfront-porch = <16>;
> +				vback-porch = <31>;
> +				vsync-len = <2>;
> +				vfront-porch = <12>;
> +				hsync-active = <0>;
> +				vsync-active = <0>;
> +				de-active = <1>;
> +				pixelclk-active = <0>;
> +			};
>  
> -				ETV570 {
> -					clock-frequency = <25200000>;
> -					hactive = <640>;
> -					vactive = <480>;
> -					hback-porch = <114>;
> -					hsync-len = <30>;
> -					hfront-porch = <16>;
> -					vback-porch = <32>;
> -					vsync-len = <3>;
> -					vfront-porch = <10>;
> -					hsync-active = <0>;
> -					vsync-active = <0>;
> -					de-active = <1>;
> -					pixelclk-active = <0>;
> -				};
> +			ETV570 {
> +				clock-frequency = <25200000>;
> +				hactive = <640>;
> +				vactive = <480>;
> +				hback-porch = <114>;
> +				hsync-len = <30>;
> +				hfront-porch = <16>;
> +				vback-porch = <32>;
> +				vsync-len = <3>;
> +				vfront-porch = <10>;
> +				hsync-active = <0>;
> +				vsync-active = <0>;
> +				de-active = <1>;
> +				pixelclk-active = <0>;
> +			};
>  
> -				ET0350 {
> -					clock-frequency = <6413760>;
> -					hactive = <320>;
> -					vactive = <240>;
> -					hback-porch = <34>;
> -					hsync-len = <34>;
> -					hfront-porch = <20>;
> -					vback-porch = <15>;
> -					vsync-len = <3>;
> -					vfront-porch = <4>;
> -					hsync-active = <0>;
> -					vsync-active = <0>;
> -					de-active = <1>;
> -					pixelclk-active = <0>;
> -				};
> +			ET0350 {
> +				clock-frequency = <6413760>;
> +				hactive = <320>;
> +				vactive = <240>;
> +				hback-porch = <34>;
> +				hsync-len = <34>;
> +				hfront-porch = <20>;
> +				vback-porch = <15>;
> +				vsync-len = <3>;
> +				vfront-porch = <4>;
> +				hsync-active = <0>;
> +				vsync-active = <0>;
> +				de-active = <1>;
> +				pixelclk-active = <0>;
> +			};
>  
> -				ET0430 {
> -					clock-frequency = <9009000>;
> -					hactive = <480>;
> -					vactive = <272>;
> -					hback-porch = <2>;
> -					hsync-len = <41>;
> -					hfront-porch = <2>;
> -					vback-porch = <2>;
> -					vsync-len = <10>;
> -					vfront-porch = <2>;
> -					hsync-active = <0>;
> -					vsync-active = <0>;
> -					de-active = <1>;
> -					pixelclk-active = <1>;
> -				};
> +			ET0430 {
> +				clock-frequency = <9009000>;
> +				hactive = <480>;
> +				vactive = <272>;
> +				hback-porch = <2>;
> +				hsync-len = <41>;
> +				hfront-porch = <2>;
> +				vback-porch = <2>;
> +				vsync-len = <10>;
> +				vfront-porch = <2>;
> +				hsync-active = <0>;
> +				vsync-active = <0>;
> +				de-active = <1>;
> +				pixelclk-active = <1>;
> +			};
>  
> -				ET0500 {
> -					clock-frequency = <33264000>;
> -					hactive = <800>;
> -					vactive = <480>;
> -					hback-porch = <88>;
> -					hsync-len = <128>;
> -					hfront-porch = <40>;
> -					vback-porch = <33>;
> -					vsync-len = <2>;
> -					vfront-porch = <10>;
> -					hsync-active = <0>;
> -					vsync-active = <0>;
> -					de-active = <1>;
> -					pixelclk-active = <0>;
> -				};
> +			ET0500 {
> +				clock-frequency = <33264000>;
> +				hactive = <800>;
> +				vactive = <480>;
> +				hback-porch = <88>;
> +				hsync-len = <128>;
> +				hfront-porch = <40>;
> +				vback-porch = <33>;
> +				vsync-len = <2>;
> +				vfront-porch = <10>;
> +				hsync-active = <0>;
> +				vsync-active = <0>;
> +				de-active = <1>;
> +				pixelclk-active = <0>;
> +			};
>  
> -				ET0700 { /* same as ET0500 */
> -					clock-frequency = <33264000>;
> -					hactive = <800>;
> -					vactive = <480>;
> -					hback-porch = <88>;
> -					hsync-len = <128>;
> -					hfront-porch = <40>;
> -					vback-porch = <33>;
> -					vsync-len = <2>;
> -					vfront-porch = <10>;
> -					hsync-active = <0>;
> -					vsync-active = <0>;
> -					de-active = <1>;
> -					pixelclk-active = <0>;
> -				};
> +			ET0700 { /* same as ET0500 */
> +				clock-frequency = <33264000>;
> +				hactive = <800>;
> +				vactive = <480>;
> +				hback-porch = <88>;
> +				hsync-len = <128>;
> +				hfront-porch = <40>;
> +				vback-porch = <33>;
> +				vsync-len = <2>;
> +				vfront-porch = <10>;
> +				hsync-active = <0>;
> +				vsync-active = <0>;
> +				de-active = <1>;
> +				pixelclk-active = <0>;
> +			};
>  
> -				ETQ570 {
> -					clock-frequency = <6596040>;
> -					hactive = <320>;
> -					vactive = <240>;
> -					hback-porch = <38>;
> -					hsync-len = <30>;
> -					hfront-porch = <30>;
> -					vback-porch = <16>;
> -					vsync-len = <3>;
> -					vfront-porch = <4>;
> -					hsync-active = <0>;
> -					vsync-active = <0>;
> -					de-active = <1>;
> -					pixelclk-active = <0>;
> -				};
> +			ETQ570 {
> +				clock-frequency = <6596040>;
> +				hactive = <320>;
> +				vactive = <240>;
> +				hback-porch = <38>;
> +				hsync-len = <30>;
> +				hfront-porch = <30>;
> +				vback-porch = <16>;
> +				vsync-len = <3>;
> +				vfront-porch = <4>;
> +				hsync-active = <0>;
> +				vsync-active = <0>;
> +				de-active = <1>;
> +				pixelclk-active = <0>;
>  			};
>  		};
>  	};
Acked-by: Lothar Waßmann <LW-bxm8fMRDkQLDiMYJYoSAnRvVK+yQ3ZXh@public.gmane.org>


Lothar Waßmann
--
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 V7 0/2] clk: qcom: spmi_pmic clock divider support
From: Tirupathi Reddy T @ 2017-12-04 10:48 UTC (permalink / raw)
  To: sboyd-sgV2jX0FEOL9JmXXK+q4OQ, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: mturquette-rdvid1DuHRBWk0Htik3J/w, mark.rutland-5wv7dgnIgG8,
	andy.gross-QSEj5FYQhm4dnm+yROfE0A,
	david.brown-QSEj5FYQhm4dnm+yROfE0A,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA,
	linux-soc-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1511255465-3984-1-git-send-email-tirupath-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>

Hi Stephen,

I have addressed the comments given on previous patch versions. Could 
you please review the patch series ?

Thanks

Tirupathi


On 11/21/2017 2:41 PM, Tirupathi Reddy wrote:
> The SPMI_PMIC clock divider driver configures the clkdiv modules present
> on Qualcomm Technologies, Inc. SPMI PMIC. This driver provides a clock
> interface for each clkdiv module and allows clock operations such as enable,
> disable, set_rate, recalc_rate and round_rate.
>
> Tirupathi Reddy (2):
>    clk: qcom: Add spmi_pmic clock divider support
>    dt-bindings: Add qcom spmi_pmic clock divider bindings
>
>   .../bindings/clock/qcom,spmi-pmic-div.txt          |  59 ++++
>   drivers/clk/qcom/Kconfig                           |   9 +
>   drivers/clk/qcom/Makefile                          |   1 +
>   drivers/clk/qcom/clk-spmi-pmic-div.c               | 302 +++++++++++++++++++++
>   4 files changed, 371 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/clock/qcom,spmi-pmic-div.txt
>   create mode 100644 drivers/clk/qcom/clk-spmi-pmic-div.c
>

--
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] arm: dts: rockchip: fix errors in IOMMU interrupts property cells
From: Heiko Stuebner @ 2017-12-04 10:46 UTC (permalink / raw)
  To: Rob Herring
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20171109223535.17368-1-robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

Am Donnerstag, 9. November 2017, 16:35:35 CET schrieb Rob Herring:
> The interrupts property in the IOMMU node for Rockchip dts file has a
> spurious extra cell causing a dtc warning:
> 
> Warning (interrupts_property): interrupts size is (16), expected multiple of 12 in /iommu@ff900800
> 
> Remove the extra cell.
> 
> Cc: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
> Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
> Signed-off-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> ---
>  arch/arm/boot/dts/rk3288.dtsi            | 2 +-
>  arch/arm64/boot/dts/rockchip/rk3368.dtsi | 2 +-

Arnd fixed the rk3368 variant in a patch already, so I've dropped that
and applied the rk3288 fix as fix for 4.15.


Thanks
Heiko

--
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 v4 4/4] arm64: dts: renesas: ulcb: Add EthernetAVB PHY reset
From: Geert Uytterhoeven @ 2017-12-04 10:34 UTC (permalink / raw)
  To: David S . Miller, Andrew Lunn, Florian Fainelli, Simon Horman,
	Magnus Damm
  Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Nicolas Ferre,
	Richard Leitner, netdev-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-renesas-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
In-Reply-To: <1512383692-14009-1-git-send-email-geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>

Describe the GPIO used to reset the Ethernet PHY for EthernetAVB.
This allows the driver to reset the PHY during probe and after system
resume.

On ULCB, the enable pin of the regulator providing PHY power is always
pulled high, but the driver may still need to reset the PHY if this
wasn't done by the bootloader before.

Inspired by patches in the BSP for the individual Salvator-X/XS boards
by Kazuya Mizuguchi.

Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
Compile-tested only.

v4:
  - No changes,

v3:
  - Extract from series "[PATCH 0/4] ravb: Add PHY reset support", and
    incorporate in this series,
  - Move reset-gpios from the ethernet to the ethernet-phy node.
---
 arch/arm64/boot/dts/renesas/ulcb.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/renesas/ulcb.dtsi b/arch/arm64/boot/dts/renesas/ulcb.dtsi
index 0d85b315ce711c8f..be91016e0b48f196 100644
--- a/arch/arm64/boot/dts/renesas/ulcb.dtsi
+++ b/arch/arm64/boot/dts/renesas/ulcb.dtsi
@@ -154,6 +154,7 @@
 		reg = <0>;
 		interrupt-parent = <&gpio2>;
 		interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+		reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>;
 	};
 };
 
-- 
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

* [PATCH v4 3/4] arm64: dts: renesas: salvator-common: Add EthernetAVB PHY reset
From: Geert Uytterhoeven @ 2017-12-04 10:34 UTC (permalink / raw)
  To: David S . Miller, Andrew Lunn, Florian Fainelli, Simon Horman,
	Magnus Damm
  Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Nicolas Ferre,
	Richard Leitner, netdev, devicetree, linux-renesas-soc,
	linux-kernel, Geert Uytterhoeven
In-Reply-To: <1512383692-14009-1-git-send-email-geert+renesas@glider.be>

Describe the GPIO used to reset the Ethernet PHY for EthernetAVB.
This allows the driver to reset the PHY during probe and after system
resume.

This fixes Ethernet operation after resume from s2ram on Salvator-XS,
where the enable pin of the regulator providing PHY power is connected
to PRESETn, and PSCI powers down the SoC during system suspend.

On Salvator-X, the enable pin is always pulled high, but the driver may
still need to reset the PHY if this wasn't done by the bootloader
before.

Inspired by patches in the BSP for the individual Salvator-X/XS boards
by Kazuya Mizuguchi.

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
For proper PHY reset operation during system resume, this depends on
"[PATCH v4 1/4] phylib: Add device reset GPIO support".
However, this patch can be applied independently.

v4:
  - No changes,

v3:
  - Extract from series "[PATCH 0/4] ravb: Add PHY reset support", and
    incorporate in this series,
  - Move reset-gpios from the ethernet to the ethernet-phy node.
---
 arch/arm64/boot/dts/renesas/salvator-common.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/boot/dts/renesas/salvator-common.dtsi b/arch/arm64/boot/dts/renesas/salvator-common.dtsi
index a298df74ca6c037f..6a9524ff881306cf 100644
--- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi
+++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi
@@ -264,6 +264,7 @@
 		reg = <0>;
 		interrupt-parent = <&gpio2>;
 		interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+		reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>;
 	};
 };
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 2/4] macb: Kill PHY reset code
From: Geert Uytterhoeven @ 2017-12-04 10:34 UTC (permalink / raw)
  To: David S . Miller, Andrew Lunn, Florian Fainelli, Simon Horman,
	Magnus Damm
  Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Nicolas Ferre,
	Richard Leitner, netdev, devicetree, linux-renesas-soc,
	linux-kernel, Geert Uytterhoeven
In-Reply-To: <1512383692-14009-1-git-send-email-geert+renesas@glider.be>

From: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

With the phylib now being aware of the "reset-gpios" PHY node property,
there should be no need to frob the PHY reset in this driver anymore...

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
  - Add Reviewed-by,

v3:
  - Resolve rejects due to the file being renamed, refresh the patch,
  - Add code to reset PHY on probe error,
  - Edit patch description,
  - Take over from Sergei,
  - Add Acked-by,

v2:
  - No changes.
---
 drivers/net/ethernet/cadence/macb.h      |  1 -
 drivers/net/ethernet/cadence/macb_main.c | 21 ---------------------
 2 files changed, 22 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 3165c2ba58f97744..c50c5ec49b1dc585 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -1171,7 +1171,6 @@ struct macb {
 	unsigned int		dma_burst_length;
 
 	phy_interface_t		phy_interface;
-	struct gpio_desc	*reset_gpio;
 
 	/* AT91RM9200 transmit */
 	struct sk_buff *skb;			/* holds skb until xmit interrupt completes */
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index c5fa87cdc6c401a7..3f0dfb20fdbcf936 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -3803,7 +3803,6 @@ static int macb_probe(struct platform_device *pdev)
 					      = macb_config->clk_init;
 	int (*init)(struct platform_device *) = macb_config->init;
 	struct device_node *np = pdev->dev.of_node;
-	struct device_node *phy_node;
 	struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL;
 	unsigned int queue_mask, num_queues;
 	struct macb_platform_data *pdata;
@@ -3909,18 +3908,6 @@ static int macb_probe(struct platform_device *pdev)
 	else
 		macb_get_hwaddr(bp);
 
-	/* Power up the PHY if there is a GPIO reset */
-	phy_node =  of_get_next_available_child(np, NULL);
-	if (phy_node) {
-		int gpio = of_get_named_gpio(phy_node, "reset-gpios", 0);
-
-		if (gpio_is_valid(gpio)) {
-			bp->reset_gpio = gpio_to_desc(gpio);
-			gpiod_direction_output(bp->reset_gpio, 1);
-		}
-	}
-	of_node_put(phy_node);
-
 	err = of_get_phy_mode(np);
 	if (err < 0) {
 		pdata = dev_get_platdata(&pdev->dev);
@@ -3967,10 +3954,6 @@ static int macb_probe(struct platform_device *pdev)
 		of_phy_deregister_fixed_link(np);
 	mdiobus_free(bp->mii_bus);
 
-	/* Shutdown the PHY if there is a GPIO reset */
-	if (bp->reset_gpio)
-		gpiod_set_value(bp->reset_gpio, 0);
-
 err_out_free_netdev:
 	free_netdev(dev);
 
@@ -4001,10 +3984,6 @@ static int macb_remove(struct platform_device *pdev)
 		dev->phydev = NULL;
 		mdiobus_free(bp->mii_bus);
 
-		/* Shutdown the PHY if there is a GPIO reset */
-		if (bp->reset_gpio)
-			gpiod_set_value(bp->reset_gpio, 0);
-
 		unregister_netdev(dev);
 		clk_disable_unprepare(bp->tx_clk);
 		clk_disable_unprepare(bp->hclk);
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 1/4] phylib: Add device reset GPIO support
From: Geert Uytterhoeven @ 2017-12-04 10:34 UTC (permalink / raw)
  To: David S . Miller, Andrew Lunn, Florian Fainelli, Simon Horman,
	Magnus Damm
  Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Nicolas Ferre,
	Richard Leitner, netdev, devicetree, linux-renesas-soc,
	linux-kernel, Geert Uytterhoeven
In-Reply-To: <1512383692-14009-1-git-send-email-geert+renesas@glider.be>

From: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>

The PHY devices sometimes do have their reset signal (maybe even power
supply?) tied to some GPIO and sometimes it also does happen that a boot
loader does not leave it deasserted. So far this issue has been attacked
from (as I believe) a wrong angle: by teaching the MAC driver to manipulate
the GPIO in question; that solution, when applied to the device trees, led
to adding the PHY reset GPIO properties to the MAC device node, with one
exception: Cadence MACB driver which could handle the "reset-gpios" prop
in a PHY device subnode. I believe that the correct approach is to teach
the 'phylib' to get the MDIO device reset GPIO from the device tree node
corresponding to this device -- which this patch is doing...

Note that I had to modify the AT803x PHY driver as it would stop working
otherwise -- it made use of the reset GPIO for its own purposes...

Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
Acked-by: Rob Herring <robh@kernel.org>
[geert: Propagate actual errors from fwnode_get_named_gpiod()]
[geert: Avoid destroying initial setup]
[geert: Consolidate GPIO descriptor acquiring code]
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v4:
  - Remove Florian's Acked-by,
  - Add missing #include <linux/gpio/consumer.h>,
  - Re-add the gpiod check, as the dummy gpiod_set_value() for !GPIOLIB
    does not ignore NULL, and calls WARN_ON(1),
  - Do not reassert the reset signal if {mdio,phy}_probe() or
    phy_device_register() succeeded, as that may destroy initial setup,
  - Do not deassert the reset signal in {mdio,phy}_remove(), as it
    should already be deasserted,
  - Bring the PHY back into reset state in phy_device_remove(),
  - Move/consolidate GPIO descriptor acquiring code from
    of_mdiobus_register_phy() and of_mdiobus_register_device() to
    mdiobus_register_device().
    Note that this changes behavior slightly, in that the reset signal
    is now also asserted when called from of_mdiobus_register_device().

v3:
  - Fix fwnode_get_named_gpiod() call due to added parameters (which
    allowed to eliminate the gpiod_direction_output() call),
  - Undelete one blank line in the AT803x driver,
  - Resolve rejects, refresh patch,
  - Reword/reformat changelog,
  - Take over from Sergei,
  - Add Acked-by,
  - Remove unneeded gpiod check, as gpiod_set_value() handles NULL fine,
  - Handle fwnode_get_named_gpiod() errors correctly:
      - -ENOENT is ignored (the GPIO is optional), and turned into NULL,
	which allowed to remove all later !IS_ERR() checks,
      - Other errors (incl. -EPROBE_DEFER) are propagated [*],

v2:
  - Reformat changelog,
  - Resolve rejects, refresh patch.
---
 Documentation/devicetree/bindings/net/phy.txt |  2 ++
 drivers/net/phy/at803x.c                      | 18 +++------------
 drivers/net/phy/mdio_bus.c                    | 21 ++++++++++++++++++
 drivers/net/phy/mdio_device.c                 | 25 +++++++++++++++++++--
 drivers/net/phy/phy_device.c                  | 32 +++++++++++++++++++++++++--
 include/linux/mdio.h                          |  3 +++
 include/linux/phy.h                           |  5 +++++
 7 files changed, 87 insertions(+), 19 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/phy.txt b/Documentation/devicetree/bindings/net/phy.txt
index 77d0b2a61ffa96fc..c05479f5ac7cc837 100644
--- a/Documentation/devicetree/bindings/net/phy.txt
+++ b/Documentation/devicetree/bindings/net/phy.txt
@@ -53,6 +53,8 @@ Optional Properties:
   to ensure the integrated PHY is used. The absence of this property indicates
   the muxers should be configured so that the external PHY is used.
 
+- reset-gpios: The GPIO phandle and specifier for the PHY reset signal.
+
 Example:
 
 ethernet-phy@0 {
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index de7dd6566df7a364..29da7a3c7a3761c0 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -71,7 +71,6 @@ MODULE_LICENSE("GPL");
 
 struct at803x_priv {
 	bool phy_reset:1;
-	struct gpio_desc *gpiod_reset;
 };
 
 struct at803x_context {
@@ -254,22 +253,11 @@ static int at803x_probe(struct phy_device *phydev)
 {
 	struct device *dev = &phydev->mdio.dev;
 	struct at803x_priv *priv;
-	struct gpio_desc *gpiod_reset;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
-	if (phydev->drv->phy_id != ATH8030_PHY_ID)
-		goto does_not_require_reset_workaround;
-
-	gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(gpiod_reset))
-		return PTR_ERR(gpiod_reset);
-
-	priv->gpiod_reset = gpiod_reset;
-
-does_not_require_reset_workaround:
 	phydev->priv = priv;
 
 	return 0;
@@ -343,14 +331,14 @@ static void at803x_link_change_notify(struct phy_device *phydev)
 	 * cannot recover from by software.
 	 */
 	if (phydev->state == PHY_NOLINK) {
-		if (priv->gpiod_reset && !priv->phy_reset) {
+		if (phydev->mdio.reset && !priv->phy_reset) {
 			struct at803x_context context;
 
 			at803x_context_save(phydev, &context);
 
-			gpiod_set_value(priv->gpiod_reset, 1);
+			phy_device_reset(phydev, 1);
 			msleep(1);
-			gpiod_set_value(priv->gpiod_reset, 0);
+			phy_device_reset(phydev, 0);
 			msleep(1);
 
 			at803x_context_restore(phydev, &context);
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 2df7b62c1a36811e..c97d55cd6679412e 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -38,6 +38,7 @@
 #include <linux/phy.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
+#include <linux/gpio/consumer.h>
 
 #include <asm/irq.h>
 
@@ -48,9 +49,26 @@
 
 int mdiobus_register_device(struct mdio_device *mdiodev)
 {
+	struct gpio_desc *gpiod;
+
 	if (mdiodev->bus->mdio_map[mdiodev->addr])
 		return -EBUSY;
 
+	/* Deassert the optional reset signal */
+	if (mdiodev->dev.of_node)
+		gpiod = fwnode_get_named_gpiod(&mdiodev->dev.of_node->fwnode,
+					       "reset-gpios", 0, GPIOD_OUT_LOW,
+					       "PHY reset");
+	if (PTR_ERR(gpiod) == -ENOENT)
+		gpiod = NULL;
+	else if (IS_ERR(gpiod))
+		return PTR_ERR(gpiod);
+
+	mdiodev->reset = gpiod;
+
+	/* Assert the reset signal again */
+	mdio_device_reset(mdiodev, 1);
+
 	mdiodev->bus->mdio_map[mdiodev->addr] = mdiodev;
 
 	return 0;
@@ -420,6 +438,9 @@ void mdiobus_unregister(struct mii_bus *bus)
 		if (!mdiodev)
 			continue;
 
+		if (mdiodev->reset)
+			gpiod_put(mdiodev->reset);
+
 		mdiodev->device_remove(mdiodev);
 		mdiodev->device_free(mdiodev);
 	}
diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c
index e24f28924af8953d..75d97dd9fb281704 100644
--- a/drivers/net/phy/mdio_device.c
+++ b/drivers/net/phy/mdio_device.c
@@ -12,6 +12,8 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
@@ -114,6 +116,13 @@ void mdio_device_remove(struct mdio_device *mdiodev)
 }
 EXPORT_SYMBOL(mdio_device_remove);
 
+void mdio_device_reset(struct mdio_device *mdiodev, int value)
+{
+	if (mdiodev->reset)
+		gpiod_set_value(mdiodev->reset, value);
+}
+EXPORT_SYMBOL(mdio_device_reset);
+
 /**
  * mdio_probe - probe an MDIO device
  * @dev: device to probe
@@ -128,8 +137,16 @@ static int mdio_probe(struct device *dev)
 	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
 	int err = 0;
 
-	if (mdiodrv->probe)
+	if (mdiodrv->probe) {
+		/* Deassert the reset signal */
+		mdio_device_reset(mdiodev, 0);
+
 		err = mdiodrv->probe(mdiodev);
+		if (err) {
+			/* Assert the reset signal */
+			mdio_device_reset(mdiodev, 1);
+		}
+	}
 
 	return err;
 }
@@ -140,9 +157,13 @@ static int mdio_remove(struct device *dev)
 	struct device_driver *drv = mdiodev->dev.driver;
 	struct mdio_driver *mdiodrv = to_mdio_driver(drv);
 
-	if (mdiodrv->remove)
+	if (mdiodrv->remove) {
 		mdiodrv->remove(mdiodev);
 
+		/* Assert the reset signal */
+		mdio_device_reset(mdiodev, 1);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 8154fb706751e8ad..1de5e242b8b4cda3 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -632,6 +632,9 @@ int phy_device_register(struct phy_device *phydev)
 	if (err)
 		return err;
 
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
 	/* Run all of the fixups for this PHY */
 	err = phy_scan_fixups(phydev);
 	if (err) {
@@ -650,6 +653,9 @@ int phy_device_register(struct phy_device *phydev)
 	return 0;
 
  out:
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
 	mdiobus_unregister_device(&phydev->mdio);
 	return err;
 }
@@ -666,6 +672,10 @@ EXPORT_SYMBOL(phy_device_register);
 void phy_device_remove(struct phy_device *phydev)
 {
 	device_del(&phydev->mdio.dev);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
+
 	mdiobus_unregister_device(&phydev->mdio);
 }
 EXPORT_SYMBOL(phy_device_remove);
@@ -849,6 +859,9 @@ int phy_init_hw(struct phy_device *phydev)
 {
 	int ret = 0;
 
+	/* Deassert the reset signal */
+	phy_device_reset(phydev, 0);
+
 	if (!phydev->drv || !phydev->drv->config_init)
 		return 0;
 
@@ -1126,6 +1139,9 @@ void phy_detach(struct phy_device *phydev)
 	put_device(&phydev->mdio.dev);
 	if (ndev_owner != bus->owner)
 		module_put(bus->owner);
+
+	/* Assert the reset signal */
+	phy_device_reset(phydev, 1);
 }
 EXPORT_SYMBOL(phy_detach);
 
@@ -1811,8 +1827,16 @@ static int phy_probe(struct device *dev)
 	/* Set the state to READY by default */
 	phydev->state = PHY_READY;
 
-	if (phydev->drv->probe)
+	if (phydev->drv->probe) {
+		/* Deassert the reset signal */
+		phy_device_reset(phydev, 0);
+
 		err = phydev->drv->probe(phydev);
+		if (err) {
+			/* Assert the reset signal */
+			phy_device_reset(phydev, 1);
+		}
+	}
 
 	mutex_unlock(&phydev->lock);
 
@@ -1829,8 +1853,12 @@ static int phy_remove(struct device *dev)
 	phydev->state = PHY_DOWN;
 	mutex_unlock(&phydev->lock);
 
-	if (phydev->drv && phydev->drv->remove)
+	if (phydev->drv && phydev->drv->remove) {
 		phydev->drv->remove(phydev);
+
+		/* Assert the reset signal */
+		phy_device_reset(phydev, 1);
+	}
 	phydev->drv = NULL;
 
 	return 0;
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index ca08ab16ecdc9b78..92d4e55ffe675637 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -12,6 +12,7 @@
 #include <uapi/linux/mdio.h>
 #include <linux/mod_devicetable.h>
 
+struct gpio_desc;
 struct mii_bus;
 
 /* Multiple levels of nesting are possible. However typically this is
@@ -39,6 +40,7 @@ struct mdio_device {
 	/* Bus address of the MDIO device (0-31) */
 	int addr;
 	int flags;
+	struct gpio_desc *reset;
 };
 #define to_mdio_device(d) container_of(d, struct mdio_device, dev)
 
@@ -71,6 +73,7 @@ void mdio_device_free(struct mdio_device *mdiodev);
 struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
 int mdio_device_register(struct mdio_device *mdiodev);
 void mdio_device_remove(struct mdio_device *mdiodev);
+void mdio_device_reset(struct mdio_device *mdiodev, int value);
 int mdio_driver_register(struct mdio_driver *drv);
 void mdio_driver_unregister(struct mdio_driver *drv);
 int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 4962af37722ac2ea..6106ed915841c97e 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -841,6 +841,11 @@ int phy_aneg_done(struct phy_device *phydev);
 int phy_stop_interrupts(struct phy_device *phydev);
 int phy_restart_aneg(struct phy_device *phydev);
 
+static inline void phy_device_reset(struct phy_device *phydev, int value)
+{
+	mdio_device_reset(&phydev->mdio, value);
+}
+
 #define phydev_err(_phydev, format, args...)	\
 	dev_err(&_phydev->mdio.dev, format, ##args)
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH v4 0/4] Teach phylib hard-resetting devices
From: Geert Uytterhoeven @ 2017-12-04 10:34 UTC (permalink / raw)
  To: David S . Miller, Andrew Lunn, Florian Fainelli, Simon Horman,
	Magnus Damm
  Cc: Sergei Shtylyov, Rob Herring, Mark Rutland, Nicolas Ferre,
	Richard Leitner, netdev, devicetree, linux-renesas-soc,
	linux-kernel, Geert Uytterhoeven

	Hi David, Andrew, Florian, Simon, Magnus,

This patch series adds optional PHY reset support to phylib.

The first two patches are destined for David's net-next tree. They add
core PHY reset code, and update a driver that currently uses its own
reset code.

The last two patches are destined for Simon's renesas tree.  They add
properties to describe the EthernetAVB PHY reset topology to the common
Salvator-X/XS and ULCB DTS files, which solves two issues:
  1. On Salvator-XS, the enable pin of the regulator providing PHY power
     is connected to PRESETn, and PSCI powers down the SoC during system
     suspend.  Hence a PHY reset is needed to restore network
     functionality after system resume.
  2. Linux should not rely on the boot loader having reset the PHY, but
     should reset the PHY during driver probe.

Changes compared to v3:
  - Remove Florian's Acked-by,
  - Add missing #include <linux/gpio/consumer.h>,
  - Re-add the gpiod check, as the dummy gpiod_set_value() for !GPIOLIB
    does not ignore NULL, and calls WARN_ON(1),
  - Do not reassert the reset signal if {mdio,phy}_probe() or
    phy_device_register() succeeded, as that may destroy initial setup,
  - Do not deassert the reset signal in {mdio,phy}_remove(), as it
    should already be deasserted,
  - Bring the PHY back into reset state in phy_device_remove(),
  - Move/consolidate GPIO descriptor acquiring code from
    of_mdiobus_register_phy() and of_mdiobus_register_device() to
    mdiobus_register_device().
    Note that this changes behavior slightly, in that the reset signal
    is now also asserted when called from of_mdiobus_register_device().
  - Add Reviewed-by,

Changes compared to v2, as sent by Sergei Shtylyov:
  - Fix fwnode_get_named_gpiod() call due to added parameters (which
    allowed to eliminate the gpiod_direction_output() call),
  - Rebased, refreshed, reworded,
  - Take over from Sergei,
  - Add Acked-by,
  - Remove unneeded gpiod check, as gpiod_set_value() handles NULL fine,
  - Handle fwnode_get_named_gpiod() errors correctly:
      - -ENOENT is ignored (the GPIO is optional), and turned into NULL,
	which allowed to remove all later !IS_ERR() checks,
      - Other errors (incl. -EPROBE_DEFER) are propagated,
  - Extract DTS patches from series "[PATCH 0/4] ravb: Add PHY reset
    support" (https://www.spinics.net/lists/netdev/msg457308.html), and
    incorporate in this series, after moving reset-gpios from the
    ethernet to the ethernet-phy node.

Given (1) the new reset-gpios DT property in the PHY node follows
established practises, (2) the DT binding change in the first patch has
been acked by Rob, and (3) the DTS patch does not cause any regressions
if it is applied before the PHY driver patches, the DTS patches can be
applied independently.

Thanks!

Geert Uytterhoeven (2):
  arm64: dts: renesas: salvator-common: Add EthernetAVB PHY reset
  arm64: dts: renesas: ulcb: Add EthernetAVB PHY reset

Sergei Shtylyov (2):
  phylib: Add device reset GPIO support
  macb: Kill PHY reset code

 Documentation/devicetree/bindings/net/phy.txt    |  2 ++
 arch/arm64/boot/dts/renesas/salvator-common.dtsi |  1 +
 arch/arm64/boot/dts/renesas/ulcb.dtsi            |  1 +
 drivers/net/ethernet/cadence/macb.h              |  1 -
 drivers/net/ethernet/cadence/macb_main.c         | 21 ----------------
 drivers/net/phy/at803x.c                         | 18 +++----------
 drivers/net/phy/mdio_bus.c                       | 21 ++++++++++++++++
 drivers/net/phy/mdio_device.c                    | 25 ++++++++++++++++--
 drivers/net/phy/phy_device.c                     | 32 ++++++++++++++++++++++--
 include/linux/mdio.h                             |  3 +++
 include/linux/phy.h                              |  5 ++++
 11 files changed, 89 insertions(+), 41 deletions(-)

-- 
2.7.4

Gr{oetje,eeting}s,

						Geert

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

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

^ permalink raw reply

* Re: [PATCH] iio: accel: bmc150: Add OF device ID table
From: Javier Martinez Canillas @ 2017-12-04 10:24 UTC (permalink / raw)
  To: Jonathan Cameron, Hans de Goede
  Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA, Hartmut Knaack,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, Lars-Peter Clausen,
	Jonathan Cameron, Peter Meerwald-Stadler,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Wolfram Sang
In-Reply-To: <20171204092259.00006250-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>

Hello Jonathan,

On 12/04/2017 10:44 AM, Jonathan Cameron wrote:
> On Mon, 4 Dec 2017 09:29:38 +0100
> Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
> 
>> Hi,
>>
>> On 01-12-17 12:10, Javier Martinez Canillas wrote:
>>> The driver doesn't have a struct of_device_id table but supported devices
>>> are registered via Device Trees. This is working on the assumption that a
>>> I2C device registered via OF will always match a legacy I2C device ID and
>>> that the MODALIAS reported will always be of the form i2c:<device>.
>>>
>>> But this could change in the future so the correct approach is to have an
>>> OF device ID table if the devices are registered via OF.
>>>
>>> The I2C device ID table entries have the .driver_data field set, but they
>>> are not used in the driver so weren't set in the OF device table entries.
>>>
>>> Signed-off-by: Javier Martinez Canillas <javierm-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
>>> ---
>>>
>>>   drivers/iio/accel/bmc150-accel-i2c.c | 12 ++++++++++++
>>>   1 file changed, 12 insertions(+)
>>>
>>> diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c
>>> index f85014fbaa12..8ffc308d5fd0 100644
>>> --- a/drivers/iio/accel/bmc150-accel-i2c.c
>>> +++ b/drivers/iio/accel/bmc150-accel-i2c.c
>>> @@ -81,9 +81,21 @@ static const struct i2c_device_id bmc150_accel_id[] = {
>>>   
>>>   MODULE_DEVICE_TABLE(i2c, bmc150_accel_id);
>>>   
>>> +static const struct of_device_id bmc150_accel_of_match[] = {
>>> +	{ .compatible = "bosch,bmc150_accel" },
>>> +	{ .compatible = "bosch,bmi055_accel" },  
>>
>> These look a bit weird, there is no reason to mirror the i2c_device_ids
> 
> There has been a steady move for a long time to add these IDs with the plan
> that we would stop automatically matching against the manufacturer free
> i2c IDs. Mostly on the basis that was a hack that brought a lot

Matching using OF IDs have been working for some time (since v4.10 AFAICT)
after the following commit:

da10c06a044b ("i2c: Make I2C ID tables non-mandatory for DT'ed devices").

The only remaining problem is with module auto-loading.

> of effectively unreviewed device tree bindings. As I understand it the
> eventual plan is to be able to get rid of that old path entirely...
> +CC Wolfram to see what his view is on this.
>

I don't think we can get rid of the old path entirely since are valid use cases
for it. For example when the I2C devices are registered with the i2c_new_device
interface where the bus and address are declared in a struct i2c_board_info (ie:
old platforms that still use board files or devices with an embedded I2C chip).

What I think though is that drivers should only be required to define the device
table for the firmware interface used to instantiate them. For example, a driver
for a device that's DT-only should only have an OF device ID table just like a
driver for an ACPI-only device only requires to have an ACPI ID table.

Conversely, a driver for a device that's only instantiated using platform data
should only have an I2C device ID table.

If a driver supports both DT and legacy platforms, then it's OK to have both ID
tables defined. What is not correct is to require OF-only drivers to have an I2C
device ID table just as a workaround to have their modules auto-loading working.

Best regards,
-- 
Javier Martinez Canillas
Software Engineer - Desktop Hardware Enablement
Red Hat

^ permalink raw reply

* [patch v13 4/4] Documentation: jtag: Add ABI documentation
From: Oleksandr Shamray @ 2017-12-04 10:13 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
In-Reply-To: <1512382405-14774-1-git-send-email-oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>

Added document that describe the ABI for JTAG class drivrer

Signed-off-by: Oleksandr Shamray <oleksandrs-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org>
Acked-by: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
---
v12->v13
v11->v12
Tobias Klauser <tklauser-93Khv+1bN0NyDzI6CaY1VQ@public.gmane.org>
- 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-+ZI9xUNit7I@public.gmane.org>
- 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-VPRAkNaXOzVWk0Htik3J/w@public.gmane.org
+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

--
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 v13 3/4] Documentation: jtag: Add bindings for Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2017-12-04 10:13 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: <1512382405-14774-1-git-send-email-oleksandrs@mellanox.com>

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: Rob Herring <robh@kernel.org>
---
v12->v13
v11->v12
v10->v11
v9->v10
v8->v9
v7->v8
Comments pointed by pointed by Joel Stanley <joel.stan@gmail.com>
- Change compatible string to ast2400 and ast2000

V6->v7
Comments pointed by Tobias Klauser <tklauser@distanz.ch>
 - Fix spell "Doccumentation" -> "Documentation"

v5->v6
Comments pointed by Tobias Klauser <tklauser@distanz.ch>
- Small nit: s/documentation/Documentation/

v4->v5

V3->v4
Comments pointed by Rob Herring <robh@kernel.org>
- delete unnecessary "status" and "reg-shift" descriptions in
  bndings file

v2->v3
Comments pointed by Rob Herring <robh@kernel.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

^ permalink raw reply related

* [patch v13 2/4] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2017-12-04 10:13 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: <1512382405-14774-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>
---
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       |   13 +
 drivers/jtag/Makefile      |    1 +
 drivers/jtag/jtag-aspeed.c |  784 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 798 insertions(+), 0 deletions(-)
 create mode 100644 drivers/jtag/jtag-aspeed.c

diff --git a/drivers/jtag/Kconfig b/drivers/jtag/Kconfig
index 0fad1a3..098beb0 100644
--- a/drivers/jtag/Kconfig
+++ b/drivers/jtag/Kconfig
@@ -14,3 +14,16 @@ 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
+	---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..2ff46b0
--- /dev/null
+++ b/drivers/jtag/jtag-aspeed.c
@@ -0,0 +1,784 @@
+/* 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, "aspeed_jtag 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;
+	unsigned long offset;
+	char dbg_str[256];
+	int pos = 0;
+	int i;
+
+	for (offset = 0, i = 0; offset < xfer->length;
+			offset += ASPEED_JTAG_DATA_CHUNK_SIZE, i++) {
+		pos += snprintf(&dbg_str[pos], sizeof(dbg_str) - pos,
+				"0x%08lx ", data[i]);
+	}
+
+	dev_dbg(aspeed_jtag->dev, "aspeed_jtag %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, "aspeed_jtag 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);
+	}
+
+	clk_prepare_enable(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");
+		err = -ENOENT;
+		goto clk_unprep;
+	}
+
+	/* 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, "aspeed_jtag unable to get IRQ");
+		goto clk_unprep;
+	}
+	dev_dbg(&pdev->dev, "aspeed_jtag: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;
+	if (!of_device_is_compatible(pdev->dev.of_node,
+				     "aspeed,ast2500-jtag") &&
+	    !of_device_is_compatible(pdev->dev.of_node,
+				     "aspeed,ast2400-jtag"))
+		return -ENODEV;
+
+	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 v13 1/4] drivers: jtag: Add JTAG core driver
From: Oleksandr Shamray @ 2017-12-04 10:13 UTC (permalink / raw)
  To: gregkh, arnd
  Cc: system-sw-low-level, devicetree, jiri, vadimp, linux-api, openbmc,
	linux-kernel, openocd-devel-owner, Jiri Pirko, robh+dt, joel,
	linux-serial, Oleksandr Shamray, tklauser, mchehab, davem,
	linux-arm-kernel
In-Reply-To: <1512382405-14774-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>
---
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                  |  289 ++++++++++++++++++++++++++++++++++
 include/linux/jtag.h                 |   46 ++++++
 include/uapi/linux/jtag.h            |  105 ++++++++++++
 9 files changed, 472 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..42df6e3
--- /dev/null
+++ b/drivers/jtag/jtag.c
@@ -0,0 +1,289 @@
+/* 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..66b6389
--- /dev/null
+++ b/include/linux/jtag.h
@@ -0,0 +1,46 @@
+/* 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..b6c4dde
--- /dev/null
+++ b/include/uapi/linux/jtag.h
@@ -0,0 +1,105 @@
+/* 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 v13 0/4] JTAG driver introduction
From: Oleksandr Shamray @ 2017-12-04 10:13 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

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                               |   29 +
 drivers/jtag/Makefile                              |    2 +
 drivers/jtag/jtag-aspeed.c                         |  784 ++++++++++++++++++++
 drivers/jtag/jtag.c                                |  289 +++++++
 include/linux/jtag.h                               |   46 ++
 include/uapi/linux/jtag.h                          |  105 +++
 12 files changed, 1315 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

* Re: [PATCH 1/3] dt-bindings: Add optional nvmem MAC address bindings to ti,wlink-st
From: Sergei Shtylyov @ 2017-12-04 10:09 UTC (permalink / raw)
  To: David Lechner, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-bluetooth-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Mark Rutland, Marcel Holtmann, Gustavo Padovan,
	Johan Hedberg, netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <2dfb7130-e229-7429-0f30-11d7df553fdf-M4DtvfQ/ZS1MRgGoP+s0PdBPR1lH4CV8@public.gmane.org>

On 12/4/2017 12:54 PM, Sergei Shtylyov wrote:

>> This adds optional nvmem consumer properties to the ti,wlink-st device tree
>> bindings to allow specifying the Bluetooth MAC address.
>>
>> Signed-off-by: David Lechner <david-nq/r/kbU++upp/zk7JDF2g@public.gmane.org>
>> ---
>>   Documentation/devicetree/bindings/net/ti,wilink-st.txt | 4 ++++
>>   1 file changed, 4 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/net/ti,wilink-st.txt 
>> b/Documentation/devicetree/bindings/net/ti,wilink-st.txt
>> index 1649c1f..24eb897 100644
>> --- a/Documentation/devicetree/bindings/net/ti,wilink-st.txt
>> +++ b/Documentation/devicetree/bindings/net/ti,wilink-st.txt
>> @@ -32,6 +32,8 @@ Optional properties:
>>      See ../clocks/clock-bindings.txt for details.
>>    - clock-names : Must include the following entry:
>>      "ext_clock" (External clock provided to the TI combo chip).
>> + - nvmem-cells: phandle to nvmem data cell that contains a MAC address
>> + - nvmem-cell-names: "mac-address" (required when nvmem-cells is specified)
>>   Example:
>> @@ -43,5 +45,7 @@ Example:
>>           enable-gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
>>           clocks = <&clk32k_wl18xx>;
>>           clock-names = "ext_clock";
>> +        nvmem-cells: <&mac_address>
> 
>     s/:/ = /?
> 
>> +        nvmem-cell-names "mac-address"
> 
>     Where's =?

    And ;? :-)

[...]

MBR, Sergei
--
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] ARM64: dts: meson-gxm: fix q200 interrupt number
From: Jerome Brunet @ 2017-12-04 10:05 UTC (permalink / raw)
  To: Kevin Hilman, Martin Blumenstingl
  Cc: Jerome Brunet, linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

Correct the interrupt number assigned to the Realtek PHY in the q200

Fixes: b94d22d94ad2 ("ARM64: dts: meson-gx: add external PHY interrupt on some platforms")
Reported-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
Signed-off-by: Jerome Brunet <jbrunet-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
---

 This change depends on the related series from Martin [0]
 Thanks for catching it Martin !

[0]: https://lkml.kernel.org/r/20171202214037.17017-1-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org

 arch/arm64/boot/dts/amlogic/meson-gxm-q200.dts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-q200.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-q200.dts
index 9847fce443a8..388fac4f2d97 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-q200.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-q200.dts
@@ -111,8 +111,8 @@
 		reg = <0>;
 		max-speed = <1000>;
 		interrupt-parent = <&gpio_intc>;
-		/* MAC_INTR on GPIOH_3 */
-		interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
+		/* MAC_INTR on GPIOZ_15 */
+		interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
 	};
 };
 
-- 
2.14.3

--
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


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