Synopsys DesignWare HDMI Video Capture Controller + PHY
Subsystems (2)
v10 cover Synopsys DesignWare HDMI Video Capture Controller + PHY
The Synopsys DesignWare HDMI RX controller is an HDMI receiver controller that is responsible to process digital data that comes from a phy. The final result is a stream of RAW video data that can then be connected to a video DMA, for example, and transfered into RAM so that it can be displayed.
The controller + phy available in this series natively support all HDMI 1.4 and HDMI 2.0 modes, including deep color. Although, the driver is quite in its initial stage and unfortunatelly only non deep color modes are supported. Also, audio is not yet supported in the driver (the controller has several audio output interfaces).
Version 10 addresses review comments from Hans Verkuil and from Philippe Ombredanne.
This series was tested in a FPGA platform using an embedded platform called ARC AXS101.
Jose Abreu (4):
dt-bindings: media: Document Synopsys DesignWare HDMI RX
MAINTAINERS: Add entry for Synopsys DesignWare HDMI drivers [media] platform: Add Synopsys DesignWare HDMI RX PHY e405 Driver [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
.../devicetree/bindings/media/snps,dw-hdmi-rx.txt | 58 + MAINTAINERS | 7 + drivers/media/platform/Kconfig | 2 + drivers/media/platform/Makefile | 2 + drivers/media/platform/dwc/Kconfig | 23 + drivers/media/platform/dwc/Makefile | 2 + drivers/media/platform/dwc/dw-hdmi-phy-e405.c | 822 +++++++++ drivers/media/platform/dwc/dw-hdmi-phy-e405.h | 41 + drivers/media/platform/dwc/dw-hdmi-rx.c | 1840 ++++++++++++++++++++ drivers/media/platform/dwc/dw-hdmi-rx.h | 419 +++++ include/media/dwc/dw-hdmi-phy-pdata.h | 106 ++ include/media/dwc/dw-hdmi-rx-pdata.h | 48 + 12 files changed, 3370 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.txt create mode 100644 drivers/media/platform/dwc/Kconfig create mode 100644 drivers/media/platform/dwc/Makefile create mode 100644 drivers/media/platform/dwc/dw-hdmi-phy-e405.c create mode 100644 drivers/media/platform/dwc/dw-hdmi-phy-e405.h create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.c create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.h create mode 100644 include/media/dwc/dw-hdmi-phy-pdata.h create mode 100644 include/media/dwc/dw-hdmi-rx-pdata.h
Signature · 1 line
1.9.1
PATCH v10 1/4 dt-bindings: media: Document Synopsys DesignWare HDMI RX
Document the bindings for the Synopsys DesignWare HDMI RX.
---
Changes from v7:
- Remove SoC specific bindings (Rob)
Changes from v6:
- Document which properties are required/optional (Sylwester)
- Drop compatible string for SoC (Sylwester)
- Reword edid-phandle property (Sylwester)
- Typo fixes (Sylwester)
Changes from v4:
- Use "cfg" instead of "cfg-clk" (Rob)
- Change node names (Rob)
Changes from v3:
- Document the new DT bindings suggested by Sylwester
Changes from v2:
- Document edid-phandle property
--- .../devicetree/bindings/media/snps,dw-hdmi-rx.txt | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.txt
Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.txt new file +58 -0
@@ -0,0 +1,58 @@ +Synopsys DesignWare HDMI RX Decoder +=================================== + +This document defines device tree properties for the Synopsys DesignWare HDMI +RX Decoder (DWC HDMI RX). + +The properties bellow belong to the Synopsys DesignWare HDMI RX Decoder node. + +Required properties: + +- compatible: Shall be "snps,dw-hdmi-rx". +- reg: Memory mapped base address and length of the DWC HDMI RX registers. +- interrupts: Reference to the DWC HDMI RX interrupt and the HDMI 5V sense +interrupt. +- clocks: Reference to the config clock. +- clock-names: Shall be "cfg". +- #address-cells: Shall be 1. +- #size-cells: Shall be 0. + +Optional properties: + +- edid-phandle: Reference to the EDID handler block; if this property is not +specified it is assumed that EDID is handled by device described by parent +node of the HDMI RX node. You should not specify this property if your HDMI RX +controller does not have CEC. + +You also have to create a subnode for the PHY device. PHY node properties are +as follows. + +Required properties: + +- compatible: Shall be "snps,dw-hdmi-phy-e405". +- reg: Shall be the JTAG address of the PHY. +- clocks: Reference to the config clock. +- clock-names: Shall be "cfg". + +Example: + +hdmi_rx: hdmi-rx@0 { + compatible = "snps,dw-hdmi-rx"; + reg = <0x0 0x10000>; + interrupts = <1 2>; + edid-phandle = <&dw_hdmi_edid>; + + clocks = <&dw_hdmi_refclk>; + clock-names = "cfg"; + + #address-cells = <1>; + #size-cells = <0>; + + hdmi-phy@fc { + compatible = "snps,dw-hdmi-phy-e405"; + reg = <0xfc>; + + clocks = <&dw_hdmi_refclk>; + clock-names = "cfg"; + }; +};
Signature · 7 lines
1.9.1 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v10 1/4] dt-bindings: media: Document Synopsys DesignWare HDMI RX
Quoted 11 lines · Jose Abreu
Document the bindings for the Synopsys DesignWare HDMI RX.
Acked-by Rob Herring <[email protected]> (v8)Cc Joao Pinto <jpinto-HKixBCOQz3hWk0Htik3J/[email protected]>Cc Rob Herring <[email protected]>Cc Mark Rutland <[email protected]>Cc Mauro Carvalho Chehab <[email protected]>Cc Hans Verkuil <[email protected]>Cc Sylwester Nawrocki <[email protected]>
Regards,
Hans
Quoted 75 lines
---
Changes from v7:
- Remove SoC specific bindings (Rob)
Changes from v6:
- Document which properties are required/optional (Sylwester)
- Drop compatible string for SoC (Sylwester)
- Reword edid-phandle property (Sylwester)
- Typo fixes (Sylwester)
Changes from v4:
- Use "cfg" instead of "cfg-clk" (Rob)
- Change node names (Rob)
Changes from v3:
- Document the new DT bindings suggested by Sylwester
Changes from v2:
- Document edid-phandle property--- .../devicetree/bindings/media/snps,dw-hdmi-rx.txt | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.txtDocumentation/devicetree/bindings/media/snps,dw-hdmi-rx.txt new file +48
-0@@ -0,0 +1,58 @@ +Synopsys DesignWare HDMI RX Decoder +=================================== + +This document defines device tree properties for the Synopsys DesignWare HDMI +RX Decoder (DWC HDMI RX). + +The properties bellow belong to the Synopsys DesignWare HDMI RX Decoder node. + +Required properties: + +- compatible: Shall be "snps,dw-hdmi-rx". +- reg: Memory mapped base address and length of the DWC HDMI RX registers. +- interrupts: Reference to the DWC HDMI RX interrupt and the HDMI 5V sense +interrupt. +- clocks: Reference to the config clock. +- clock-names: Shall be "cfg". +- #address-cells: Shall be 1. +- #size-cells: Shall be 0. + +Optional properties: + +- edid-phandle: Reference to the EDID handler block; if this property is not +specified it is assumed that EDID is handled by device described by parent +node of the HDMI RX node. You should not specify this property if your HDMI RX +controller does not have CEC. + +You also have to create a subnode for the PHY device. PHY node properties are +as follows. + +Required properties: + +- compatible: Shall be "snps,dw-hdmi-phy-e405". +- reg: Shall be the JTAG address of the PHY. +- clocks: Reference to the config clock. +- clock-names: Shall be "cfg". + +Example: + +hdmi_rx: hdmi-rx@0 { + compatible = "snps,dw-hdmi-rx"; + reg = <0x0 0x10000>; + interrupts = <1 2>; + edid-phandle = <&dw_hdmi_edid>; + + clocks = <&dw_hdmi_refclk>; + clock-names = "cfg"; + + #address-cells = <1>;
Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.txt line 58
Quoted 10 lines
Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.txt new file +10
-0+ #size-cells = <0>; + + hdmi-phy@fc { + compatible = "snps,dw-hdmi-phy-e405"; + reg = <0xfc>; + + clocks = <&dw_hdmi_refclk>; + clock-names = "cfg"; + }; +};
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v10 2/4 MAINTAINERS: Add entry for Synopsys DesignWare HDMI drivers
In reply to Jose AbreuAdd an entry for Synopsys DesignWare HDMI Receivers drivers and phys.
--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)
MAINTAINERS +7 -0
@@ -13108,6 +13108,13 @@ L: [email protected] S: Supported F: drivers/net/ethernet/synopsys/ +SYNOPSYS DESIGNWARE HDMI RECEIVERS AND PHY DRIVERS +M: Jose Abreu <[email protected]> +L: [email protected] +S: Maintained +F: drivers/media/platform/dwc/* +F: include/media/dwc/* + SYNOPSYS DESIGNWARE I2C DRIVER M: Jarkko Nikula <[email protected]> R: Andy Shevchenko <[email protected]>
Signature · 1 line
1.9.1
Re: [PATCH v10 2/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI drivers
Quoted 5 lines · Jose Abreu
Add an entry for Synopsys DesignWare HDMI Receivers drivers and phys.
Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>
Regards,
Hans
Quoted 23 lines
--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)MAINTAINERS +7
-0@@ -13108,6 +13108,13 @@ L: [email protected] S: Supported F: drivers/net/ethernet/synopsys/ +SYNOPSYS DESIGNWARE HDMI RECEIVERS AND PHY DRIVERS +M: Jose Abreu <[email protected]> +L: [email protected] +S: Maintained +F: drivers/media/platform/dwc/* +F: include/media/dwc/* + SYNOPSYS DESIGNWARE I2C DRIVER M: Jarkko Nikula <[email protected]> R: Andy Shevchenko <[email protected]>
Re: [PATCH v10 2/4] MAINTAINERS: Add entry for Synopsys DesignWare HDMI drivers
Quoted 8 lines · Hans Verkuil
Quoted 5 lines · Jose Abreu
Add an entry for Synopsys DesignWare HDMI Receivers drivers and phys.
Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>Reviewed-by Hans Verkuil <[email protected]>
Oops, I was a bit too quick here. You're missing this file:
Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.txt
Regards,
Hans
Quoted 31 lines
Regards,
Hans
Quoted 23 lines
--- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+)MAINTAINERS +7
-0@@ -13108,6 +13108,13 @@ L: [email protected] S: Supported F: drivers/net/ethernet/synopsys/ +SYNOPSYS DESIGNWARE HDMI RECEIVERS AND PHY DRIVERS +M: Jose Abreu <[email protected]> +L: [email protected] +S: Maintained +F: drivers/media/platform/dwc/* +F: include/media/dwc/* + SYNOPSYS DESIGNWARE I2C DRIVER M: Jarkko Nikula <[email protected]> R: Andy Shevchenko <[email protected]>
PATCH v10 3/4 platform: Add Synopsys DesignWare HDMI RX PHY e405 Driver
In reply to Jose AbreuThis adds support for the Synopsys DesignWare HDMI RX PHY e405. This phy receives and decodes HDMI video that is delivered to a controller.
Main features included in this driver are:
- Equalizer algorithm that chooses the phy best settings according to the detected HDMI cable characteristics.
- Support for scrambling
- Support for HDMI 2.0 modes up to 6G (HDMI 4k@60Hz).
The driver was implemented as a standalone V4L2 subdevice and the phy interface with the controller was implemented using V4L2 ioctls. I do not know if this is the best option but it is not possible to use the existing API functions directly as we need specific functions that will be called by the controller at specific configuration stages.
There is also a bidirectional communication between controller and phy:
The phy must provide functions that the controller will call (i.e. configuration functions) and the controller must provide read/write callbacks, as well as other specific functions.
---
Changes from v9:
- Use SPDX License ID (Philippe)
Changes from v4:
- Use usleep_range (Sylwester)
- Remove some comments (Sylwester)
- Parse phy version from of_device_id (Sylwester)
- Use "cfg" instead of "cfg-clk" (Sylwester, Rob)
- Change some messages to dev_dbg (Sylwester)
Changes from v3:
- Use v4l2 async API (Sylwester)
- Use clock API (Sylwester)
- Add compatible string (Sylwester)
Changes from RFC:
- Remove a bunch of functions that can be collapsed into a single config() function
- Add comments for the callbacks and structures (Hans)
--- drivers/media/platform/Kconfig | 2 + drivers/media/platform/Makefile | 2 + drivers/media/platform/dwc/Kconfig | 8 + drivers/media/platform/dwc/Makefile | 1 + drivers/media/platform/dwc/dw-hdmi-phy-e405.c | 822 ++++++++++++++++++++++++++ drivers/media/platform/dwc/dw-hdmi-phy-e405.h | 41 ++ include/media/dwc/dw-hdmi-phy-pdata.h | 106 ++++ 7 files changed, 982 insertions(+) create mode 100644 drivers/media/platform/dwc/Kconfig create mode 100644 drivers/media/platform/dwc/Makefile create mode 100644 drivers/media/platform/dwc/dw-hdmi-phy-e405.c create mode 100644 drivers/media/platform/dwc/dw-hdmi-phy-e405.h create mode 100644 include/media/dwc/dw-hdmi-phy-pdata.h
drivers/media/platform/Kconfig +2 -0
@@ -33,6 +33,8 @@ source "drivers/media/platform/omap/Kconfig" source "drivers/media/platform/blackfin/Kconfig" +source "drivers/media/platform/dwc/Kconfig" + config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on MEDIA_CAMERA_SUPPORT
drivers/media/platform/Makefile +2 -0
@@ -97,3 +97,5 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ obj-y += meson/ + +obj-y += dwc/
drivers/media/platform/dwc/Kconfig new file +8 -0
@@ -0,0 +1,8 @@ +config VIDEO_DWC_HDMI_PHY_E405 + tristate "Synopsys Designware HDMI RX PHY e405 driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + help + Support for Synopsys Designware HDMI RX PHY. Version is e405. + + To compile this driver as a module, choose M here. The module + will be called dw-hdmi-phy-e405.
drivers/media/platform/dwc/Makefile new file +1 -0
@@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_DWC_HDMI_PHY_E405) += dw-hdmi-phy-e405.o
drivers/media/platform/dwc/dw-hdmi-phy-e405.c new file +822 -0
822 lines not on this page · raw message
drivers/media/platform/dwc/dw-hdmi-phy-e405.h new file +41 -0
41 lines not on this page · raw message
include/media/dwc/dw-hdmi-phy-pdata.h new file +106 -0
106 lines not on this page · raw message
Signature · 1 line
1.9.1
Re: [PATCH v10 3/4] [media] platform: Add Synopsys DesignWare HDMI RX PHY e405 Driver
Quoted 26 lines · Jose Abreu
This adds support for the Synopsys DesignWare HDMI RX PHY e405. This phy receives and decodes HDMI video that is delivered to a controller.
Main features included in this driver are:
- Equalizer algorithm that chooses the phy best settings according to the detected HDMI cable characteristics.
- Support for scrambling
- Support for HDMI 2.0 modes up to 6G (HDMI 4k@60Hz).The driver was implemented as a standalone V4L2 subdevice and the phy interface with the controller was implemented using V4L2 ioctls. I do not know if this is the best option but it is not possible to use the existing API functions directly as we need specific functions that will be called by the controller at specific configuration stages.
There is also a bidirectional communication between controller and phy:
The phy must provide functions that the controller will call (i.e. configuration functions) and the controller must provide read/write callbacks, as well as other specific functions.Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>Cc Mauro Carvalho Chehab <[email protected]>Cc Hans Verkuil <[email protected]>Cc Sylwester Nawrocki <[email protected]>Cc Philippe Ombredanne <[email protected]>
Quoted 3 lines
---
Changes from v9:
- Use SPDX License ID (Philippe)
Thanks!
Signature · 2 lines
Cordially Philippe Ombredanne
Re: [PATCH v10 3/4] [media] platform: Add Synopsys DesignWare HDMI RX PHY e405 Driver
In reply to Jose AbreuQuoted 20 lines · Jose Abreu
This adds support for the Synopsys DesignWare HDMI RX PHY e405. This phy receives and decodes HDMI video that is delivered to a controller.
Main features included in this driver are:
- Equalizer algorithm that chooses the phy best settings according to the detected HDMI cable characteristics.
- Support for scrambling
- Support for HDMI 2.0 modes up to 6G (HDMI 4k@60Hz).The driver was implemented as a standalone V4L2 subdevice and the phy interface with the controller was implemented using V4L2 ioctls. I do not know if this is the best option but it is not possible to use the existing API functions directly as we need specific functions that will be called by the controller at specific configuration stages.
There is also a bidirectional communication between controller and phy:
The phy must provide functions that the controller will call (i.e. configuration functions) and the controller must provide read/write callbacks, as well as other specific functions.
Quoted 6 lines
Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>Cc Mauro Carvalho Chehab <[email protected]>Cc Hans Verkuil <[email protected]>Cc Sylwester Nawrocki <[email protected]>Cc Philippe Ombredanne <[email protected]>
Regards,
Hans
Quoted 1,064 lines
---
Changes from v9:
- Use SPDX License ID (Philippe)
Changes from v4:
- Useusleep_range(Sylwester)
- Remove some comments (Sylwester)
- Parse phy version fromof_device_id(Sylwester)
- Use "cfg" instead of "cfg-clk" (Sylwester, Rob)
- Change some messages todev_dbg(Sylwester)
Changes from v3:
- Use v4l2 async API (Sylwester)
- Use clock API (Sylwester)
- Add compatible string (Sylwester)
Changes from RFC:
- Remove a bunch of functions that can be collapsed into a singleconfig()function
- Add comments for the callbacks and structures (Hans)--- drivers/media/platform/Kconfig | 2 + drivers/media/platform/Makefile | 2 + drivers/media/platform/dwc/Kconfig | 8 + drivers/media/platform/dwc/Makefile | 1 + drivers/media/platform/dwc/dw-hdmi-phy-e405.c | 822 ++++++++++++++++++++++++++ drivers/media/platform/dwc/dw-hdmi-phy-e405.h | 41 ++ include/media/dwc/dw-hdmi-phy-pdata.h | 106 ++++ 7 files changed, 982 insertions(+) create mode 100644 drivers/media/platform/dwc/Kconfig create mode 100644 drivers/media/platform/dwc/Makefile create mode 100644 drivers/media/platform/dwc/dw-hdmi-phy-e405.c create mode 100644 drivers/media/platform/dwc/dw-hdmi-phy-e405.h create mode 100644 include/media/dwc/dw-hdmi-phy-pdata.hdrivers/media/platform/Kconfig +2
-0@@ -33,6 +33,8 @@ source "drivers/media/platform/omap/Kconfig" source "drivers/media/platform/blackfin/Kconfig" +source "drivers/media/platform/dwc/Kconfig" + config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on MEDIA_CAMERA_SUPPORTdrivers/media/platform/Makefile +2
-0@@ -97,3 +97,5 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss-8x16/ obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/ obj-y += meson/ + +obj-y += dwc/drivers/media/platform/dwc/Kconfig new file +8
-0@@ -0,0 +1,8 @@ +config VIDEO_DWC_HDMI_PHY_E405 + tristate "Synopsys Designware HDMI RX PHY e405 driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + help + Support for Synopsys Designware HDMI RX PHY. Version is e405. + + To compile this driver as a module, choose M here. The module + will be called dw-hdmi-phy-e405.drivers/media/platform/dwc/Makefile new file +1
-0@@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_DWC_HDMI_PHY_E405) += dw-hdmi-phy-e405.odrivers/media/platform/dwc/dw-hdmi-phy-e405.c new file +822
-0@@ -0,0 +1,822 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates. +// Synopsys DesignWare HDMI PHY e405 driver + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <media/v4l2-async.h> +#include <media/v4l2-subdev.h> +#include <media/dwc/dw-hdmi-phy-pdata.h> +#include "dw-hdmi-phy-e405.h" + +MODULE_AUTHOR ("Jose Abreu <[email protected]>" ); +MODULE_DESCRIPTION ("Designware HDMI PHY e405 driver" ); +MODULE_LICENSE ("Dual MIT/GPL" ); + +#define PHY_EQ_WAIT_TIME_START 3 +#define PHY_EQ_SLEEP_TIME_CDR 30 +#define PHY_EQ_SLEEP_TIME_ACQ 1 +#define PHY_EQ_BOUNDSPREAD 20 +#define PHY_EQ_MIN_ACQ_STABLE 3 +#define PHY_EQ_ACC_LIMIT 360 +#define PHY_EQ_ACC_MIN_LIMIT 0 +#define PHY_EQ_MAX_SETTING 13 +#define PHY_EQ_SHORT_CABLE_SETTING 4 +#define PHY_EQ_ERROR_CABLE_SETTING 4 +#define PHY_EQ_MIN_SLOPE 50 +#define PHY_EQ_AVG_ACQ 5 +#define PHY_EQ_MINMAX_NTRIES 3 +#define PHY_EQ_EQUALIZED_COUNTER_VAL 512 +#define PHY_EQ_EQUALIZED_COUNTER_VAL_HDMI20 512 +#define PHY_EQ_MINMAX_MAXDIFF 4 +#define PHY_EQ_MINMAX_MAXDIFF_HDMI20 2 +#define PHY_EQ_FATBIT_MASK 0x0000 +#define PHY_EQ_FATBIT_MASK_4K 0x0c03 +#define PHY_EQ_FATBIT_MASK_HDMI20 0x0e03 + +struct dw_phy_eq_ch { +u16 best_long_setting; +u8 valid_long_setting; +u16 best_short_setting; +u8 valid_short_setting; +u16 best_setting; +u16 acc; +u16 acq; +u16 last_acq; +u16 upper_bound_acq; +u16 lower_bound_acq; +u16 out_bound_acq; +u16 read_acq; +}; + +static const struct dw_phy_mpll_config { +u16 addr; +u16 val; +} dw_phy_e405_mpll_cfg[] = { + {0x27 ,0x1B94 }, + {0x28 ,0x16D2 }, + {0x29 ,0x12D9 }, + {0x2A ,0x3249 }, + {0x2B ,0x3653 }, + {0x2C ,0x3436 }, + {0x2D ,0x124D }, + {0x2E ,0x0001 }, + {0xCE ,0x0505 }, + {0xCF ,0x0505 }, + {0xD0 ,0x0000 }, + {0x00 ,0x0000 }, +}; + +struct dw_phy_dev { +struct device *dev; +struct dw_phy_pdata *config; +unsigned int version; +struct clk *clk; +u16 cfg_clk; +bool phy_enabled; +struct v4l2_subdev sd; +u16 mpll_status; +unsigned char color_depth; +bool hdmi2; +bool scrambling; +struct mutex lock; +}; + +static inline struct dw_phy_dev *to_dw_dev (struct v4l2_subdev *sd) +{ + return container_of(sd, struct dw_phy_dev, sd); +} + +static void phy_write (struct dw_phy_dev *dw_dev,u16 val,u16 addr) +{ +void *arg = dw_dev->config->funcs_arg; + + dw_dev->config->funcs->write (arg, val, addr); +} + +static u16 phy_read (struct dw_phy_dev *dw_dev,u16 addr) +{ +void *arg = dw_dev->config->funcs_arg; + +return dw_dev->config->funcs->read (arg, addr); +} + +static void phy_reset (struct dw_phy_dev *dw_dev,int enable) +{ +void *arg = dw_dev->config->funcs_arg; + + dw_dev->config->funcs->reset (arg, enable); +} + +static void phy_pddq (struct dw_phy_dev *dw_dev,int enable) +{ +void *arg = dw_dev->config->funcs_arg; + + dw_dev->config->funcs->pddq (arg, enable); +} + +static void phy_svsmode (struct dw_phy_dev *dw_dev,int enable) +{ +void *arg = dw_dev->config->funcs_arg; + + dw_dev->config->funcs->svsmode (arg, enable); +} + +static void phy_zcal_reset (struct dw_phy_dev *dw_dev) +{ +void *arg = dw_dev->config->funcs_arg; + + dw_dev->config->funcs->zcal_reset (arg); +} + +static bool phy_zcal_done (struct dw_phy_dev *dw_dev) +{ +void *arg = dw_dev->config->funcs_arg; + +return dw_dev->config->funcs->zcal_done (arg); +} + +static bool phy_tmds_valid (struct dw_phy_dev *dw_dev) +{ +void *arg = dw_dev->config->funcs_arg; + +return dw_dev->config->funcs->tmds_valid (arg); +} + +static int dw_phy_eq_test (struct dw_phy_dev *dw_dev, +u16 *fat_bit_mask,int *min_max_length) +{ +u16 main_fsm_status, val; +int i; + +for (i =0 ; i <PHY_EQ_WAIT_TIME_START ; i++) { + main_fsm_status =phy_read (dw_dev,PHY_MAINFSM_STATUS1 ); +if (main_fsm_status &0x100 ) +break ; +mdelay (PHY_EQ_SLEEP_TIME_CDR ); + } + +if (i ==PHY_EQ_WAIT_TIME_START ) { +dev_dbg (dw_dev->dev,"phy start conditions not achieved\n" ); +return -ETIMEDOUT ; + } + +if (main_fsm_status &0x400 ) { +dev_dbg (dw_dev->dev,"invalid pll rate\n" ); +return -EINVAL ; + } + + val = (phy_read (dw_dev,PHY_CDR_CTRL_CNT ) &0x300 ) >>8 ; +if (val ==0x1 ) { +/* HDMI 2.0 */ + *fat_bit_mask =PHY_EQ_FATBIT_MASK_HDMI20 ; + *min_max_length =PHY_EQ_MINMAX_MAXDIFF_HDMI20 ; +dev_dbg (dw_dev->dev,"[EQUALIZER] using HDMI 2.0 values\n" ); + }else if (!(main_fsm_status &0x600 )) { +/* HDMI 1.4 (pll rate = 0) */ + *fat_bit_mask =PHY_EQ_FATBIT_MASK_4K ; + *min_max_length =PHY_EQ_MINMAX_MAXDIFF ; +dev_dbg (dw_dev->dev,"[EQUALIZER] using HDMI 1.4@4k values\n" ); + }else { +/* HDMI 1.4 */ + *fat_bit_mask =PHY_EQ_FATBIT_MASK ; + *min_max_length =PHY_EQ_MINMAX_MAXDIFF ; +dev_dbg (dw_dev->dev,"[EQUALIZER] using HDMI 1.4 values\n" ); + } + +return 0 ; +} + +static void dw_phy_eq_default (struct dw_phy_dev *dw_dev) +{ +phy_write (dw_dev,0x08A8 ,PHY_CH0_EQ_CTRL1 ); +phy_write (dw_dev,0x0020 ,PHY_CH0_EQ_CTRL2 ); +phy_write (dw_dev,0x08A8 ,PHY_CH1_EQ_CTRL1 ); +phy_write (dw_dev,0x0020 ,PHY_CH1_EQ_CTRL2 ); +phy_write (dw_dev,0x08A8 ,PHY_CH2_EQ_CTRL1 ); +phy_write (dw_dev,0x0020 ,PHY_CH2_EQ_CTRL2 ); +} + +static void dw_phy_eq_single (struct dw_phy_dev *dw_dev) +{ +phy_write (dw_dev,0x0211 ,PHY_CH0_EQ_CTRL1 ); +phy_write (dw_dev,0x0211 ,PHY_CH1_EQ_CTRL1 ); +phy_write (dw_dev,0x0211 ,PHY_CH2_EQ_CTRL1 ); +} + +static void dw_phy_eq_equal_setting (struct dw_phy_dev *dw_dev, +u16 lock_vector) +{ +phy_write (dw_dev, lock_vector,PHY_CH0_EQ_CTRL4 ); +phy_write (dw_dev,0x0024 ,PHY_CH0_EQ_CTRL2 ); +phy_write (dw_dev,0x0026 ,PHY_CH0_EQ_CTRL2 ); +phy_read (dw_dev,PHY_CH0_EQ_STATUS2 ); +phy_write (dw_dev, lock_vector,PHY_CH1_EQ_CTRL4 ); +phy_write (dw_dev,0x0024 ,PHY_CH1_EQ_CTRL2 ); +phy_write (dw_dev,0x0026 ,PHY_CH1_EQ_CTRL2 ); +phy_read (dw_dev,PHY_CH1_EQ_STATUS2 ); +phy_write (dw_dev, lock_vector,PHY_CH2_EQ_CTRL4 ); +phy_write (dw_dev,0x0024 ,PHY_CH2_EQ_CTRL2 ); +phy_write (dw_dev,0x0026 ,PHY_CH2_EQ_CTRL2 ); +phy_read (dw_dev,PHY_CH2_EQ_STATUS2 ); +} + +static void dw_phy_eq_equal_setting_ch0 (struct dw_phy_dev *dw_dev, +u16 lock_vector) +{ +phy_write (dw_dev, lock_vector,PHY_CH0_EQ_CTRL4 ); +phy_write (dw_dev,0x0024 ,PHY_CH0_EQ_CTRL2 ); +phy_write (dw_dev,0x0026 ,PHY_CH0_EQ_CTRL2 ); +phy_read (dw_dev,PHY_CH0_EQ_STATUS2 ); +} + +static void dw_phy_eq_equal_setting_ch1 (struct dw_phy_dev *dw_dev, +u16 lock_vector) +{ +phy_write (dw_dev, lock_vector,PHY_CH1_EQ_CTRL4 ); +phy_write (dw_dev,0x0024 ,PHY_CH1_EQ_CTRL2 ); +phy_write (dw_dev,0x0026 ,PHY_CH1_EQ_CTRL2 ); +phy_read (dw_dev,PHY_CH1_EQ_STATUS2 ); +} + +static void dw_phy_eq_equal_setting_ch2 (struct dw_phy_dev *dw_dev, +u16 lock_vector) +{ +phy_write (dw_dev, lock_vector,PHY_CH2_EQ_CTRL4 ); +phy_write (dw_dev,0x0024 ,PHY_CH2_EQ_CTRL2 ); +phy_write (dw_dev,0x0026 ,PHY_CH2_EQ_CTRL2 ); +phy_read (dw_dev,PHY_CH2_EQ_STATUS2 ); +} + +static void dw_phy_eq_auto_calib (struct dw_phy_dev *dw_dev) +{ +phy_write (dw_dev,0x1809 ,PHY_MAINFSM_CTRL ); +phy_write (dw_dev,0x1819 ,PHY_MAINFSM_CTRL ); +phy_write (dw_dev,0x1809 ,PHY_MAINFSM_CTRL ); +} + +static void dw_phy_eq_init_vars (struct dw_phy_eq_ch *ch) +{ + ch->acc =0 ; + ch->acq =0 ; + ch->last_acq =0 ; + ch->valid_long_setting =0 ; + ch->valid_short_setting =0 ; + ch->best_setting =PHY_EQ_SHORT_CABLE_SETTING ; +} + +static bool dw_phy_eq_acquire_early_cnt (struct dw_phy_dev *dw_dev, +u16 setting,u16 acq,struct dw_phy_eq_ch *ch0, +struct dw_phy_eq_ch *ch1,struct dw_phy_eq_ch *ch2) +{ +u16 lock_vector =0x1 ; +int i; + + lock_vector <<= setting; + ch0->out_bound_acq =0 ; + ch1->out_bound_acq =0 ; + ch2->out_bound_acq =0 ; + ch0->acq =0 ; + ch1->acq =0 ; + ch2->acq =0 ; + +dw_phy_eq_equal_setting (dw_dev, lock_vector); +dw_phy_eq_auto_calib (dw_dev); + +mdelay (PHY_EQ_SLEEP_TIME_CDR ); +if (!phy_tmds_valid (dw_dev)) +dev_dbg (dw_dev->dev,"TMDS is NOT valid\n" ); + + ch0->read_acq =phy_read (dw_dev,PHY_CH0_EQ_STATUS3 ); + ch1->read_acq =phy_read (dw_dev,PHY_CH1_EQ_STATUS3 ); + ch2->read_acq =phy_read (dw_dev,PHY_CH2_EQ_STATUS3 ); + + ch0->acq += ch0->read_acq; + ch1->acq += ch1->read_acq; + ch2->acq += ch2->read_acq; + + ch0->upper_bound_acq = ch0->read_acq +PHY_EQ_BOUNDSPREAD ; + ch0->lower_bound_acq = ch0->read_acq -PHY_EQ_BOUNDSPREAD ; + ch1->upper_bound_acq = ch1->read_acq +PHY_EQ_BOUNDSPREAD ; + ch1->lower_bound_acq = ch1->read_acq -PHY_EQ_BOUNDSPREAD ; + ch2->upper_bound_acq = ch2->read_acq +PHY_EQ_BOUNDSPREAD ; + ch2->lower_bound_acq = ch2->read_acq -PHY_EQ_BOUNDSPREAD ; + +for (i =1 ; i < acq; i++) { +dw_phy_eq_auto_calib (dw_dev); +mdelay (PHY_EQ_SLEEP_TIME_ACQ ); + +if ((ch0->read_acq > ch0->upper_bound_acq) || + (ch0->read_acq < ch0->lower_bound_acq)) + ch0->out_bound_acq++; +if ((ch1->read_acq > ch1->upper_bound_acq) || + (ch1->read_acq < ch1->lower_bound_acq)) + ch1->out_bound_acq++; +if ((ch2->read_acq > ch2->upper_bound_acq) || + (ch2->read_acq < ch1->lower_bound_acq)) + ch2->out_bound_acq++; + +if (i ==PHY_EQ_MIN_ACQ_STABLE ) { +if ((ch0->out_bound_acq ==0 ) && + (ch1->out_bound_acq ==0 ) && + (ch2->out_bound_acq ==0 )) { + acq =3 ; +break ; + } + } + + ch0->read_acq =phy_read (dw_dev,PHY_CH0_EQ_STATUS3 ); + ch1->read_acq =phy_read (dw_dev,PHY_CH1_EQ_STATUS3 ); + ch2->read_acq =phy_read (dw_dev,PHY_CH2_EQ_STATUS3 ); + + ch0->acq += ch0->read_acq; + ch1->acq += ch1->read_acq; + ch2->acq += ch2->read_acq; + } + + ch0->acq = ch0->acq / acq; + ch1->acq = ch1->acq / acq; + ch2->acq = ch2->acq / acq; + +return true; +} + +static int dw_phy_eq_test_type (u16 setting,bool tmds_valid, +struct dw_phy_eq_ch *ch) +{ +u16 step_slope =0 ; + +if ((ch->acq < ch->last_acq) && tmds_valid) { +/* Long cable equalization */ + ch->acc += ch->last_acq - ch->acq; +if ((ch->valid_long_setting ==0 ) && (ch->acq <512 ) && + (ch->acc >0 )) { + ch->best_long_setting = setting; + ch->valid_long_setting =1 ; + } + step_slope = ch->last_acq - ch->acq; + } + +if (tmds_valid && (ch->valid_short_setting ==0 )) { +/* Short cable equalization */ +if ((setting <PHY_EQ_SHORT_CABLE_SETTING ) && + (ch->acq <PHY_EQ_EQUALIZED_COUNTER_VAL )) { + ch->best_short_setting= setting; + ch->valid_short_setting =1 ; + } + +if (setting ==PHY_EQ_SHORT_CABLE_SETTING ) { + ch->best_short_setting =PHY_EQ_SHORT_CABLE_SETTING ; + ch->valid_short_setting =1 ; + } + } + +if (ch->valid_long_setting && (ch->acc >PHY_EQ_ACC_LIMIT )) { + ch->best_setting = ch->best_long_setting; +return 1 ; + } + +if ((setting ==PHY_EQ_MAX_SETTING ) && (ch->acc <PHY_EQ_ACC_LIMIT ) && + ch->valid_short_setting) { + ch->best_setting = ch->best_short_setting; +return 2 ; + } + +if ((setting ==PHY_EQ_MAX_SETTING ) && tmds_valid && + (ch->acc >PHY_EQ_ACC_LIMIT ) && + (step_slope >PHY_EQ_MIN_SLOPE )) { + ch->best_setting =PHY_EQ_MAX_SETTING ; +return 3 ; + } + +if (setting ==PHY_EQ_MAX_SETTING ) { + ch->best_setting =PHY_EQ_ERROR_CABLE_SETTING ; +return 255 ; + } + +return 0 ; +} + +static bool dw_phy_eq_setting_finder (struct dw_phy_dev *dw_dev,u16 acq, +struct dw_phy_eq_ch *ch0,struct dw_phy_eq_ch *ch1, +struct dw_phy_eq_ch *ch2) +{ +u16 act =0 ; +int ret_ch0 =0 , ret_ch1 =0 , ret_ch2 =0 ; +bool tmds_valid = false; + +dw_phy_eq_init_vars (ch0); +dw_phy_eq_init_vars (ch1); +dw_phy_eq_init_vars (ch2); + + tmds_valid =dw_phy_eq_acquire_early_cnt (dw_dev, act, acq, + ch0, ch1, ch2); + +while ((ret_ch0 ==0 ) || (ret_ch1 ==0 ) || (ret_ch2 ==0 )) { + act++; + + ch0->last_acq = ch0->acq; + ch1->last_acq = ch1->acq; + ch2->last_acq = ch2->acq; + + tmds_valid =dw_phy_eq_acquire_early_cnt (dw_dev, act, acq, + ch0, ch1, ch2); + +if (!ret_ch0) + ret_ch0 =dw_phy_eq_test_type (act, tmds_valid, ch0); +if (!ret_ch1) + ret_ch1 =dw_phy_eq_test_type (act, tmds_valid, ch1); +if (!ret_ch2) + ret_ch2 =dw_phy_eq_test_type (act, tmds_valid, ch2); + } + +if ((ret_ch0 ==255 ) || (ret_ch1 ==255 ) || (ret_ch2 ==255 )) +return false; +return true; +} + +static bool dw_phy_eq_maxvsmin (u16 ch0_setting,u16 ch1_setting, +u16 ch2_setting,u16 min_max_length) +{ +u16 min = ch0_setting, max = ch0_setting; + +if (ch1_setting > max) + max = ch1_setting; +if (ch2_setting > max) + max = ch2_setting; +if (ch1_setting < min) + min = ch1_setting; +if (ch2_setting < min) + min = ch2_setting; + +if ((max - min) > min_max_length) +return false; +return true; +} + +static int dw_phy_eq_init (struct dw_phy_dev *dw_dev,u16 acq,bool force) +{ +struct dw_phy_eq_ch ch0, ch1, ch2; +u16 fat_bit_mask, lock_vector =0x1 ; +int min_max_length, i, ret =0 ; +u16 mpll_status; + +if (dw_dev->version <401 ) +return ret; +if (!dw_dev->phy_enabled) +return -EINVAL ; + + mpll_status =phy_read (dw_dev,PHY_CLK_MPLL_STATUS ); +if (mpll_status == dw_dev->mpll_status && !force) +return ret; + dw_dev->mpll_status = mpll_status; + +phy_write (dw_dev,0x00 ,PHY_MAINFSM_OVR2 ); +phy_write (dw_dev,0x00 ,PHY_CH0_EQ_CTRL3 ); +phy_write (dw_dev,0x00 ,PHY_CH1_EQ_CTRL3 ); +phy_write (dw_dev,0x00 ,PHY_CH2_EQ_CTRL3 ); + + ret =dw_phy_eq_test (dw_dev, &fat_bit_mask, &min_max_length); +if (ret) { +if (ret == -EINVAL )/* Means equalizer is not needed */ + ret =0 ; + +/* Do not change values if we don't have clock */ +if (ret != -ETIMEDOUT ) { +dw_phy_eq_default (dw_dev); +phy_pddq (dw_dev,1 ); +phy_pddq (dw_dev,0 ); + } + }else { +dw_phy_eq_single (dw_dev); +dw_phy_eq_equal_setting (dw_dev,0x0001 ); +phy_write (dw_dev, fat_bit_mask,PHY_CH0_EQ_CTRL6 ); +phy_write (dw_dev, fat_bit_mask,PHY_CH1_EQ_CTRL6 ); +phy_write (dw_dev, fat_bit_mask,PHY_CH2_EQ_CTRL6 ); + +for (i =0 ; i <PHY_EQ_MINMAX_NTRIES ; i++) { +if (dw_phy_eq_setting_finder (dw_dev, acq, + &ch0, &ch1, &ch2)) { +if (dw_phy_eq_maxvsmin (ch0.best_setting, + ch1.best_setting, + ch2.best_setting, + min_max_length)) +break ; + } + + ch0.best_setting =PHY_EQ_ERROR_CABLE_SETTING ; + ch1.best_setting =PHY_EQ_ERROR_CABLE_SETTING ; + ch2.best_setting =PHY_EQ_ERROR_CABLE_SETTING ; + } + +dev_dbg (dw_dev->dev,"equalizer settings: " +"ch0=0x%x, ch1=0x%x, ch1=0x%x\n" , + ch0.best_setting, ch1.best_setting, + ch2.best_setting); + +if (i ==PHY_EQ_MINMAX_NTRIES ) + ret = -EINVAL ; + + lock_vector =0x1 ; + lock_vector <<= ch0.best_setting; +dw_phy_eq_equal_setting_ch0 (dw_dev, lock_vector); + + lock_vector =0x1 ; + lock_vector <<= ch1.best_setting; +dw_phy_eq_equal_setting_ch1 (dw_dev, lock_vector); + + lock_vector =0x1 ; + lock_vector <<= ch2.best_setting; +dw_phy_eq_equal_setting_ch2 (dw_dev, lock_vector); + +phy_pddq (dw_dev,1 ); +phy_pddq (dw_dev,0 ); + } + +return ret; +} + +static int dw_phy_config (struct dw_phy_dev *dw_dev,unsigned char color_depth, +bool hdmi2,bool scrambling) +{ +const struct dw_phy_mpll_config *mpll_cfg = dw_phy_e405_mpll_cfg; +struct device *dev = dw_dev->dev; +int timeout =100 ; +u16 val, res_idx; +bool zcal_done; + +dev_dbg (dev,"%s: color_depth=%d, hdmi2=%d, scrambling=%d, cfg_clk=%d\n" , + __func__, color_depth, hdmi2, scrambling, dw_dev->cfg_clk); + +switch (color_depth) { +case 8 : + res_idx =0x0 ; +break ; +case 10 : + res_idx =0x1 ; +break ; +case 12 : + res_idx =0x2 ; +break ; +case 16 : + res_idx =0x3 ; +break ; +default : +return -EINVAL ; + } + +phy_reset (dw_dev,1 ); +phy_pddq (dw_dev,1 ); +phy_svsmode (dw_dev,1 ); + +phy_zcal_reset (dw_dev); +do { +usleep_range (1000 ,1100 ); + zcal_done =phy_zcal_done (dw_dev); + }while (!zcal_done && timeout--); + +if (!zcal_done) { +dev_err (dw_dev->dev,"Zcal calibration failed\n" ); +return -ETIMEDOUT ; + } + +phy_reset (dw_dev,0 ); + +/* CMU */ + val = (0x08 <<10 ) | (0x01 <<9 ); + val |= (dw_dev->cfg_clk *4 ) &GENMASK (8 ,0 ); +phy_write (dw_dev, val,PHY_CMU_CONFIG ); + +/* Color Depth and enable fast switching */ + val =phy_read (dw_dev,PHY_SYSTEM_CONFIG ); + val = (val & ~0x60 ) | (res_idx <<5 ) |BIT (11 ); +phy_write (dw_dev, val,PHY_SYSTEM_CONFIG ); + +/* MPLL */ +for (; mpll_cfg->addr !=0x0 ; mpll_cfg++) +phy_write (dw_dev, mpll_cfg->val, mpll_cfg->addr); + +/* Operation for data rates between 3.4Gbps and 6Gbps */ + val =phy_read (dw_dev,PHY_CDR_CTRL_CNT ); +if (hdmi2) + val |=BIT (8 ); +else + val &= ~BIT (8 ); +phy_write (dw_dev, val,PHY_CDR_CTRL_CNT ); + +/* Scrambling */ + val =phy_read (dw_dev,PHY_OVL_PROT_CTRL ); +if (scrambling) + val |=GENMASK (7 ,6 ); +else + val &= ~GENMASK (7 ,6 ); +phy_write (dw_dev, val,PHY_OVL_PROT_CTRL ); + +/* Enable phy */ +phy_pddq (dw_dev,0 ); + + dw_dev->color_depth = color_depth; + dw_dev->hdmi2 = hdmi2; + dw_dev->scrambling = scrambling; +return 0 ; +} + +static int dw_phy_enable (struct dw_phy_dev *dw_dev,unsigned char color_depth, +bool hdmi2,bool scrambling) +{ +int ret; + +if (dw_dev->phy_enabled && + dw_dev->color_depth == color_depth && + dw_dev->hdmi2 == hdmi2 && + dw_dev->scrambling == scrambling) +return 0 ; + + ret =dw_phy_config (dw_dev, color_depth, hdmi2, scrambling); +if (ret) +return ret; + +phy_reset (dw_dev,0 ); +phy_pddq (dw_dev,0 ); + dw_dev->phy_enabled = true; +return 0 ; +} + +static void dw_phy_disable (struct dw_phy_dev *dw_dev) +{ +if (!dw_dev->phy_enabled) +return ; + +phy_reset (dw_dev,1 ); +phy_pddq (dw_dev,1 ); +phy_svsmode (dw_dev,0 ); + dw_dev->mpll_status =0xFFFF ; + dw_dev->phy_enabled = false; +} + +static long dw_phy_ioctl (struct v4l2_subdev *sd,unsigned int cmd,void *arg) +{ +struct dw_phy_dev *dw_dev =to_dw_dev (sd); +struct dw_phy_config_command *ccmd; +struct dw_phy_eq_command *ecmd; +int ret =0 ; + +dev_dbg (dw_dev->dev,"%s: cmd=%d\n" , __func__, cmd); + +mutex_lock (&dw_dev->lock); +switch (cmd) { +case DW_PHY_IOCTL_EQ_INIT : + ecmd = (struct dw_phy_eq_command *)arg; + ecmd->result =dw_phy_eq_init (dw_dev, ecmd->nacq, ecmd->force); +break ; +case DW_PHY_IOCTL_CONFIG : + ccmd = (struct dw_phy_config_command *)arg; + ccmd->result =dw_phy_enable (dw_dev, ccmd->color_depth, + ccmd->hdmi2, ccmd->scrambling); +break ; +default : + ret = -ENOIOCTLCMD ; +break ; + } +mutex_unlock (&dw_dev->lock); + +return ret; +} + +static int dw_phy_s_power (struct v4l2_subdev *sd,int on) +{ +struct dw_phy_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s: on=%d\n" , __func__, on); + +mutex_lock (&dw_dev->lock); +if (!on) +dw_phy_disable (dw_dev); +mutex_unlock (&dw_dev->lock); +return 0 ; +} + +static const struct v4l2_subdev_core_ops dw_phy_core_ops = { + .ioctl = dw_phy_ioctl, + .s_power = dw_phy_s_power, +}; + +static const struct v4l2_subdev_ops dw_phy_sd_ops = { + .core = &dw_phy_core_ops, +}; + +struct dw_hdmi_phy_data { +const char *name; +unsigned int version; +}; + +static const struct dw_hdmi_phy_data dw_phy_e405_data = { + .name ="e405" , + .version =405 , +}; + +static int dw_phy_probe (struct platform_device *pdev) +{ +struct dw_phy_pdata *pdata = pdev->dev.platform_data; +const struct dw_hdmi_phy_data *of_data; +struct device *dev = &pdev->dev; +struct dw_phy_dev *dw_dev; +struct v4l2_subdev *sd; +int ret; + +dev_dbg (dev,"probe start\n" ); + + dw_dev =devm_kzalloc (dev,sizeof (*dw_dev),GFP_KERNEL ); +if (!dw_dev) +return -ENOMEM ; + +if (!pdata) { +dev_err (dev,"no platform data suplied\n" ); +return -EINVAL ; + } + + of_data =of_device_get_match_data (dev); +if (!of_data) { +dev_err (dev,"no valid phy configuration available\n" ); +return -EINVAL ; + } + + dw_dev->dev = dev; + dw_dev->config = pdata; + dw_dev->version = of_data->version; +mutex_init (&dw_dev->lock); + + dw_dev->clk =devm_clk_get (dev,"cfg" ); +if (IS_ERR (dw_dev->clk)) { +dev_err (dev,"failed to get cfg clock\n" ); +return PTR_ERR (dw_dev->clk); + } + + ret =clk_prepare_enable (dw_dev->clk); +if (ret) { +dev_err (dev,"failed to enable cfg clock\n" ); +return ret; + } + + dw_dev->cfg_clk =clk_get_rate (dw_dev->clk) /1000000U ; +if (!dw_dev->cfg_clk) { +dev_err (dev,"invalid cfg clock frequency\n" ); + ret = -EINVAL ; + goto err_clk; + } + +/* V4L2 initialization */ + sd = &dw_dev->sd; +v4l2_subdev_init (sd, &dw_phy_sd_ops); +strlcpy (sd->name,dev_name (dev),sizeof (sd->name)); + sd->dev = dev; + +/* Force phy disabling */ + dw_dev->phy_enabled = true; +dw_phy_disable (dw_dev); + + ret =v4l2_async_register_subdev (sd); +if (ret) { +dev_err (dev,"failed to register subdev\n" ); + goto err_clk; + } + +dev_set_drvdata (dev, sd); +dev_dbg (dev,"driver probed (name=%s, cfg clock=%d)\n" , + of_data->name, dw_dev->cfg_clk); +return 0 ; + +err_clk: +clk_disable_unprepare (dw_dev->clk); +return ret; +} + +static int dw_phy_remove (struct platform_device *pdev) +{ +struct v4l2_subdev *sd =dev_get_drvdata (&pdev->dev); +struct dw_phy_dev *dw_dev =to_dw_dev (sd); + +v4l2_async_unregister_subdev (sd); +clk_disable_unprepare (dw_dev->clk); +return 0 ; +} + +static const struct of_device_id dw_hdmi_phy_e405_id[] = { + { .compatible ="snps,dw-hdmi-phy-e405" , .data = &dw_phy_e405_data, }, + { }, +}; +MODULE_DEVICE_TABLE (of, dw_hdmi_phy_e405_id); + +static struct platform_driver dw_phy_e405_driver = { + .probe = dw_phy_probe, + .remove = dw_phy_remove, + .driver = { + .name =DW_PHY_E405_DRVNAME , + .of_match_table = dw_hdmi_phy_e405_id, + } +}; +module_platform_driver (dw_phy_e405_driver);drivers/media/platform/dwc/dw-hdmi-phy-e405.h new file +41
-0@@ -0,0 +1,41 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates. +// Synopsys DesignWare HDMI PHY e405 driver + +#ifndef __DW_HDMI_PHY_E405_H__ +#define __DW_HDMI_PHY_E405_H__ + +#define PHY_CMU_CONFIG 0x02 +#define PHY_SYSTEM_CONFIG 0x03 +#define PHY_MAINFSM_CTRL 0x05 +#define PHY_MAINFSM_OVR2 0x08 +#define PHY_MAINFSM_STATUS1 0x09 +#define PHY_OVL_PROT_CTRL 0x0D +#define PHY_CDR_CTRL_CNT 0x0E +#define PHY_CLK_MPLL_STATUS 0x2F +#define PHY_CH0_EQ_CTRL1 0x32 +#define PHY_CH0_EQ_CTRL2 0x33 +#define PHY_CH0_EQ_STATUS 0x34 +#define PHY_CH0_EQ_CTRL3 0x3E +#define PHY_CH0_EQ_CTRL4 0x3F +#define PHY_CH0_EQ_STATUS2 0x40 +#define PHY_CH0_EQ_STATUS3 0x42 +#define PHY_CH0_EQ_CTRL6 0x43 +#define PHY_CH1_EQ_CTRL1 0x52 +#define PHY_CH1_EQ_CTRL2 0x53 +#define PHY_CH1_EQ_STATUS 0x54 +#define PHY_CH1_EQ_CTRL3 0x5E +#define PHY_CH1_EQ_CTRL4 0x5F +#define PHY_CH1_EQ_STATUS2 0x60 +#define PHY_CH1_EQ_STATUS3 0x62 +#define PHY_CH1_EQ_CTRL6 0x63 +#define PHY_CH2_EQ_CTRL1 0x72 +#define PHY_CH2_EQ_CTRL2 0x73 +#define PHY_CH2_EQ_STATUS 0x74 +#define PHY_CH2_EQ_CTRL3 0x7E +#define PHY_CH2_EQ_CTRL4 0x7F +#define PHY_CH2_EQ_STATUS2 0x80 +#define PHY_CH2_EQ_STATUS3 0x82 +#define PHY_CH2_EQ_CTRL6 0x83 + +#endif /* __DW_HDMI_PHY_E405_H__ */ include/media/dwc/dw-hdmi-phy-pdata.h new file +106
-0@@ -0,0 +1,106 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// Copyright (c) 2017 Synopsys, Inc. and/or its affiliates. +// Synopsys DesignWare HDMI PHY platform data + +#ifndef __DW_HDMI_PHY_PDATA_H__ +#define __DW_HDMI_PHY_PDATA_H__ + +#define DW_PHY_E405_DRVNAME "dw-hdmi-phy-e405" + +#define DW_PHY_IOCTL_EQ_INIT _IOW('R',1,int) +#define DW_PHY_IOCTL_CONFIG _IOW('R',2,int) + +/** +* struct dw_phy_eq_command - Command arguments for HDMI PHY equalizer +* algorithm. +* +* @nacq: Number of acquisitions to get. +* +* @force: Force equalizer algorithm even if the MPLL status didn't change +* from previous run. +* +* @result: Result from the equalizer algorithm. Shall be zero if equalizer +* ran with success or didn't run because the video mode does not need +* equalizer (for low clock values). +*/ +struct dw_phy_eq_command { +u16 nacq; +bool force; +int result; +}; + +/** +* struct dw_phy_config_command - Command arguments for HDMI PHY configuration +* function. +* +* @color_depth: Color depth of the video mode being received. +* +* @hdmi2: Must be set to true if the video mode being received has a data +* rate above 3.4Gbps (generally HDMI 2.0 video modes, like 4k@60Hz). +* +* @scrambling: Must be set to true if scrambling is currently enabled. +* Scrambling is enabled by source when SCDC scrambling_en field is set to 1. +* +* @result: Result from the configuration function. Shall be zero if phy was +* configured with success. +*/ +struct dw_phy_config_command { +unsigned char color_depth; +bool hdmi2; +bool scrambling; +int result; +}; + +/** +* struct dw_phy_funcs - Set of callbacks used to communicate between phy +* and hdmi controller. Controller must correctly fill these callbacks +* before probbing the phy driver. +* +* @write: write callback. Write value 'val' into address 'addr' of phy. +* +* @read: read callback. Read address 'addr' and return the value. +* +* @reset: reset callback. Activate phy reset. Active high. +* +* @pddq: pddq callback. Activate phy configuration mode. Active high. +* +* @svsmode: svsmode callback. Activate phy retention mode. Active low. +* +* @zcal_reset: zcal reset callback. Restart the impedance calibration +* procedure. Active high. This is only used in prototyping and not in real +* ASIC. Callback shall be empty (but non NULL) in ASIC cases. +* +* @zcal_done: zcal done callback. Return the current status of impedance +* calibration procedure. This is only used in prototyping and not in real +* ASIC. Shall return always true in ASIC cases. +* +* @tmds_valid: TMDS valid callback. Return the current status of TMDS signal +* that comes from phy and feeds controller. This is read from a controller +* register. +*/ +struct dw_phy_funcs { +void (*write) (void *arg,u16 val,u16 addr); +u16 (*read) (void *arg,u16 addr); +void (*reset) (void *arg,int enable); +void (*pddq) (void *arg,int enable); +void (*svsmode) (void *arg,int enable); +void (*zcal_reset) (void *arg); +bool (*zcal_done) (void *arg); +bool (*tmds_valid) (void *arg); +}; + +/** +* struct dw_phy_pdata - Platform data definition for Synopsys HDMI PHY. +* +* @funcs: set of callbacks that must be correctly filled and supplied to phy. +* See @dw_phy_funcs. +* +* @funcs_arg: parameter that is supplied to callbacks along with the function +* parameters. +*/ +struct dw_phy_pdata { +const struct dw_phy_funcs *funcs; +void *funcs_arg; +}; + +#endif /* __DW_HDMI_PHY_PDATA_H__ */
PATCH v10 4/4 platform: Add Synopsys DesignWare HDMI RX Controller Driver
In reply to Jose AbreuThis is an initial submission for the Synopsys DesignWare HDMI RX Controller Driver. This driver interacts with a phy driver so that a communication between them is created and a video pipeline is configured.
The controller + phy pipeline can then be integrated into a fully featured system that can be able to receive video up to 4k@60Hz with deep color 48bit RGB, depending on the platform. Although, this initial version does not yet handle deep color modes.
This driver was implemented as a standard V4L2 subdevice and its main features are:
- Internal state machine that reconfigures phy until the video is not stable
- JTAG communication with phy
- Inter-module communication with phy driver
- Debug write/read ioctls
Some notes:
- RX sense controller (cable connection/disconnection) must be handled by the platform wrapper as this is not integrated into the controller RTL
- The same goes for EDID ROM's
- ZCAL calibration is needed only in FPGA platforms, in ASIC this is not needed
- The state machine is not an ideal solution as it creates a kthread but it is needed because some sources might not be very stable at sending the video (i.e. we must react accordingly).
---
Changes from v9:
- Use SPDX License ID (Philippe)
- Use LOW_DRIVE CEC error (Hans)
- Fill bt->il_* fields (Hans)
- Fix format->field to NONE (Hans)
- Drop a left-over comment (Hans)
- Use CEC_CAP_DEFAULTS (Hans)
Changes from v8:
- Incorporate Sakari's work on ASYNC subdevs
Changes from v6:
- edid-phandle now also looks for parent node (Sylwester)
- Fix kbuild build warnings
Changes from v5:
- Removed HDCP 1.4 support (Hans)
- Removed some CEC debug messages (Hans)
- Add s_dv_timings callback (Hans)
- Add V4L2_CID_DV_RX_POWER_PRESENT ctrl (Hans)
Changes from v4:
- Add flag V4L2_SUBDEV_FL_HAS_DEVNODE (Sylwester)
- Remove some comments and change some messages to dev_dbg (Sylwester)
- Use v4l2_async_subnotifier_register() (Sylwester)
Changes from v3:
- Use v4l2 async API (Sylwester)
- Do not block waiting for phy
- Do not use busy waiting delays (Sylwester)
- Simplify dw_hdmi_power_on (Sylwester)
- Use clock API (Sylwester)
- Use compatible string (Sylwester)
- Minor fixes (Sylwester)
Changes from v2:
- Address review comments from Hans regarding CEC
- Use CEC notifier
- Enable SCDC
Changes from v1:
- Add support for CEC
- Correct typo errors
- Correctly detect interlaced video modes
- Correct VIC parsing
Changes from RFC:
- Add support for HDCP 1.4
- Fixup HDMI_VIC not being parsed (Hans)
- Send source change signal when powering off (Hans)
- Add a "wait stable delay"
- Detect interlaced video modes (Hans)
- Restrain g/s_register from reading/writing to HDCP regs (Hans)
--- drivers/media/platform/dwc/Kconfig | 15 + drivers/media/platform/dwc/Makefile | 1 + drivers/media/platform/dwc/dw-hdmi-rx.c | 1840 +++++++++++++++++++++++++++++++ drivers/media/platform/dwc/dw-hdmi-rx.h | 419 +++++++ include/media/dwc/dw-hdmi-rx-pdata.h | 48 + 5 files changed, 2323 insertions(+) create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.c create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.h create mode 100644 include/media/dwc/dw-hdmi-rx-pdata.h
drivers/media/platform/dwc/Kconfig +15 -0
@@ -6,3 +6,18 @@ config VIDEO_DWC_HDMI_PHY_E405 To compile this driver as a module, choose M here. The module will be called dw-hdmi-phy-e405. + +config VIDEO_DWC_HDMI_RX + tristate "Synopsys Designware HDMI Receiver driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + help + Support for Synopsys Designware HDMI RX controller. + + To compile this driver as a module, choose M here. The module + will be called dw-hdmi-rx. + +config VIDEO_DWC_HDMI_RX_CEC + bool + depends on VIDEO_DWC_HDMI_RX + select CEC_CORE + select CEC_NOTIFIER
drivers/media/platform/dwc/Makefile +1 -0
@@ -1 +1,2 @@ obj-$(CONFIG_VIDEO_DWC_HDMI_PHY_E405) += dw-hdmi-phy-e405.o +obj-$(CONFIG_VIDEO_DWC_HDMI_RX) += dw-hdmi-rx.o
drivers/media/platform/dwc/dw-hdmi-rx.c new file +1840 -0
1,840 lines not on this page · raw message
drivers/media/platform/dwc/dw-hdmi-rx.h new file +419 -0
419 lines not on this page · raw message
include/media/dwc/dw-hdmi-rx-pdata.h new file +48 -0
48 lines not on this page · raw message
Signature · 1 line
1.9.1
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
Quoted 37 lines · Jose Abreu
This is an initial submission for the Synopsys DesignWare HDMI RX Controller Driver. This driver interacts with a phy driver so that a communication between them is created and a video pipeline is configured.
The controller + phy pipeline can then be integrated into a fully featured system that can be able to receive video up to 4k@60Hz with deep color 48bit RGB, depending on the platform. Although, this initial version does not yet handle deep color modes.
This driver was implemented as a standard V4L2 subdevice and its main features are:
- Internal state machine that reconfigures phy until the video is not stable
- JTAG communication with phy
- Inter-module communication with phy driver
- Debug write/read ioctlsSome notes:
- RX sense controller (cable connection/disconnection) must be handled by the platform wrapper as this is not integrated into the controller RTL
- The same goes for EDID ROM's
- ZCAL calibration is needed only in FPGA platforms, in ASIC this is not needed
- The state machine is not an ideal solution as it creates a kthread but it is needed because some sources might not be very stable at sending the video (i.e. we must react accordingly).Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>Cc Mauro Carvalho Chehab <[email protected]>Cc Hans Verkuil <[email protected]>Cc Sylwester Nawrocki <[email protected]>Cc Sakari Ailus <[email protected]>Cc Philippe Ombredanne <[email protected]>
Quoted 3 lines
---
Changes from v9:
- Use SPDX License ID (Philippe)
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
In reply to Jose AbreuHi Jose,
Some more comments:
Quoted 129 lines · Jose Abreu
This is an initial submission for the Synopsys DesignWare HDMI RX Controller Driver. This driver interacts with a phy driver so that a communication between them is created and a video pipeline is configured.
The controller + phy pipeline can then be integrated into a fully featured system that can be able to receive video up to 4k@60Hz with deep color 48bit RGB, depending on the platform. Although, this initial version does not yet handle deep color modes.
This driver was implemented as a standard V4L2 subdevice and its main features are:
- Internal state machine that reconfigures phy until the video is not stable
- JTAG communication with phy
- Inter-module communication with phy driver
- Debug write/read ioctlsSome notes:
- RX sense controller (cable connection/disconnection) must be handled by the platform wrapper as this is not integrated into the controller RTL
- The same goes for EDID ROM's
- ZCAL calibration is needed only in FPGA platforms, in ASIC this is not needed
- The state machine is not an ideal solution as it creates a kthread but it is needed because some sources might not be very stable at sending the video (i.e. we must react accordingly).Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>Cc Mauro Carvalho Chehab <[email protected]>Cc Hans Verkuil <[email protected]>Cc Sylwester Nawrocki <[email protected]>Cc Sakari Ailus <[email protected]>Cc Philippe Ombredanne <[email protected]>---
Changes from v9:
- Use SPDX License ID (Philippe)
- UseLOW_DRIVECEC error (Hans)
- Fill bt->il_* fields (Hans)
- Fixformat->fieldto NONE (Hans)
- Drop a left-over comment (Hans)
- UseCEC_CAP_DEFAULTS(Hans)
Changes from v8:
- Incorporate Sakari's work on ASYNC subdevs
Changes from v6:
- edid-phandle now also looks for parent node (Sylwester)
- Fix kbuild build warnings
Changes from v5:
- Removed HDCP 1.4 support (Hans)
- Removed some CEC debug messages (Hans)
- Adds_dv_timingscallback (Hans)
- AddV4L2_CID_DV_RX_POWER_PRESENTctrl (Hans)
Changes from v4:
- Add flagV4L2_SUBDEV_FL_HAS_DEVNODE(Sylwester)
- Remove some comments and change some messages todev_dbg(Sylwester)
- Usev4l2_async_subnotifier_register()(Sylwester)
Changes from v3:
- Use v4l2 async API (Sylwester)
- Do not block waiting for phy
- Do not use busy waiting delays (Sylwester)
- Simplifydw_hdmi_power_on(Sylwester)
- Use clock API (Sylwester)
- Use compatible string (Sylwester)
- Minor fixes (Sylwester)
Changes from v2:
- Address review comments from Hans regarding CEC
- Use CEC notifier
- Enable SCDC
Changes from v1:
- Add support for CEC
- Correct typo errors
- Correctly detect interlaced video modes
- Correct VIC parsing
Changes from RFC:
- Add support for HDCP 1.4
- FixupHDMI_VICnot being parsed (Hans)
- Send source change signal when powering off (Hans)
- Add a "wait stable delay"
- Detect interlaced video modes (Hans)
- Restrain g/s_register from reading/writing to HDCP regs (Hans)--- drivers/media/platform/dwc/Kconfig | 15 + drivers/media/platform/dwc/Makefile | 1 + drivers/media/platform/dwc/dw-hdmi-rx.c | 1840 +++++++++++++++++++++++++++++++ drivers/media/platform/dwc/dw-hdmi-rx.h | 419 +++++++ include/media/dwc/dw-hdmi-rx-pdata.h | 48 + 5 files changed, 2323 insertions(+) create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.c create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.h create mode 100644 include/media/dwc/dw-hdmi-rx-pdata.hdrivers/media/platform/dwc/Kconfig +15
-0@@ -6,3 +6,18 @@ config VIDEO_DWC_HDMI_PHY_E405 To compile this driver as a module, choose M here. The module will be called dw-hdmi-phy-e405. + +config VIDEO_DWC_HDMI_RX + tristate "Synopsys Designware HDMI Receiver driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + help + Support for Synopsys Designware HDMI RX controller. + + To compile this driver as a module, choose M here. The module + will be called dw-hdmi-rx. + +config VIDEO_DWC_HDMI_RX_CEC + bool + depends on VIDEO_DWC_HDMI_RX + select CEC_CORE + select CEC_NOTIFIERdrivers/media/platform/dwc/Makefile +1
-0@@ -1 +1,2 @@ obj-$(CONFIG_VIDEO_DWC_HDMI_PHY_E405) += dw-hdmi-phy-e405.o +obj-$(CONFIG_VIDEO_DWC_HDMI_RX) += dw-hdmi-rx.odrivers/media/platform/dwc/dw-hdmi-rx.c new file +0
-0
<snip>
drivers/media/platform/dwc/dw-hdmi-rx.c line 1077
Quoted 7 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +7
-0+static int dw_hdmi_s_routing (struct v4l2_subdev *sd,u32 input,u32 output, +u32 config) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +if (!has_signal (dw_dev, input)) +return -EINVAL ;
Why would this be a reason to reject this? There may be no signal now, but a signal might appear later.
Quoted 100 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +100
-0+ + dw_dev->selected_input = input; +if (input == dw_dev->configured_input) +return 0 ; + +dw_hdmi_power_off (dw_dev); +return dw_hdmi_power_on (dw_dev, input); +} + +static int dw_hdmi_g_input_status (struct v4l2_subdev *sd,u32 *status) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + + *status =0 ; +if (!has_signal (dw_dev, dw_dev->selected_input)) + *status |=V4L2_IN_ST_NO_POWER ; +if (is_off (dw_dev)) + *status |=V4L2_IN_ST_NO_SIGNAL ; + +dev_dbg (dw_dev->dev,"%s: status=0x%x\n" , __func__, *status); +return 0 ; +} + +static int dw_hdmi_g_parm (struct v4l2_subdev *sd,struct v4l2_streamparm *parm) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + +/* TODO: Use helper to compute timeperframe */ + parm->parm.capture.timeperframe.numerator =1 ; + parm->parm.capture.timeperframe.denominator =60 ; +return 0 ; +} + +static int dw_hdmi_s_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); +if (!v4l2_valid_dv_timings (timings, &dw_hdmi_timings_cap,NULL ,NULL )) +return -EINVAL ; +if (v4l2_match_dv_timings (timings, &dw_dev->timings,0 , false)) +return 0 ; + + dw_dev->timings = *timings; +return 0 ; +} + +static int dw_hdmi_g_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + + *timings = dw_dev->timings; +return 0 ; +} + +static int dw_hdmi_query_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); +struct v4l2_bt_timings *bt = &timings->bt; +bool is_hdmi_vic; +u32 htot, hofs; +u32 vtot; +u8 vic; + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + +memset (timings,0 ,sizeof (*timings)); + + timings->type =V4L2_DV_BT_656_1120 ; + bt->width =hdmi_readl (dw_dev,HDMI_MD_HACT_PX ); + bt->height =hdmi_readl (dw_dev,HDMI_MD_VAL ); + bt->interlaced =hdmi_readl (dw_dev,HDMI_MD_STS ) &HDMI_MD_STS_ILACE ? +V4L2_DV_INTERLACED :V4L2_DV_PROGRESSIVE ; + +if (hdmi_readl (dw_dev,HDMI_ISTS ) &HDMI_ISTS_VS_POL_ADJ ) + bt->polarities |=V4L2_DV_VSYNC_POS_POL ; +if (hdmi_readl (dw_dev,HDMI_ISTS ) &HDMI_ISTS_HS_POL_ADJ ) + bt->polarities |=V4L2_DV_HSYNC_POS_POL ; + + bt->pixelclock =dw_hdmi_get_pixelclk (dw_dev); + +/* HTOT = HACT + HFRONT + HSYNC + HBACK */ + htot =hdmi_mask_readl (dw_dev,HDMI_MD_HT1 , +HDMI_MD_HT1_HTOT_PIX_OFFSET , +HDMI_MD_HT1_HTOT_PIX_MASK ); +/* HOFS = HSYNC + HBACK */ + hofs =hdmi_mask_readl (dw_dev,HDMI_MD_HT1 , +HDMI_MD_HT1_HOFS_PIX_OFFSET , +HDMI_MD_HT1_HOFS_PIX_MASK ); + + bt->hfrontporch = htot - hofs - bt->width; + bt->hsync = hdmi_mask_readl(dw_dev, HDMI_MD_HT0, + HDMI_MD_HT0_HS_CLK_OFFSET,
drivers/media/platform/dwc/dw-hdmi-rx.c line 1187
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +10
-0+ HDMI_MD_HT0_HS_CLK_MASK); + bt->hbackporch = hofs - bt->hsync; + +/* VTOT = VACT + VFRONT + VSYNC + VBACK */ + vtot =hdmi_readl (dw_dev,HDMI_MD_VTL ); + +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */
How do you know this waits for 1 field? Or is this: "Wait for at least 1 field"? I don't know exactly how the IP does this, but it looks fishy to me. If it is correct, then it could use a few comments about what is going on here as it is not obvious.
And what happens if the framerate is even slower? You know the pixelclock and total width+height, so you can calculate the framerate from that.
Quoted 23 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +23
-0+ bt->vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL ); + bt->vfrontporch = vtot - bt->height - bt->vsync - bt->vbackporch; + +if (bt->interlaced ==V4L2_DV_INTERLACED ) { +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK ); +msleep (100 );/* Wait for 2 fields */ + + vtot =hdmi_readl (dw_dev,HDMI_MD_VTL ); +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->il_vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + + hdmi_mask_writel(dw_dev, 0x0, HDMI_MD_VCTRL,
drivers/media/platform/dwc/dw-hdmi-rx.c line 1220
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +10
-0+HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , + HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK); +msleep (50 ); + bt->il_vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL ); + bt->il_vfrontporch = vtot - bt->height - bt->il_vsync - + bt->il_vbackporch; + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK );
Same here, I'm not sure this is correct. What is the output of 'v4l2-ctl --query-dv-timings' when you feed it a standard interlaced format?
Quoted 9 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +9
-0+ } + + bt->standards =V4L2_DV_BT_STD_CEA861 ; + + vic =dw_hdmi_get_curr_vic (dw_dev, &is_hdmi_vic); +if (vic) { +if (is_hdmi_vic) { + bt->flags |=V4L2_DV_FL_HAS_HDMI_VIC ; + bt->hdmi_vic = vic;
drivers/media/platform/dwc/dw-hdmi-rx.c line 1239
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +10
-0+ bt->cea861_vic =0 ; + }else { + bt->flags |=V4L2_DV_FL_HAS_CEA861_VIC ; + bt->hdmi_vic =0 ; + bt->cea861_vic = vic; + } + } + +return 0 ; +}
Regards,
Hans
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
Hi Hans,
Quoted 3 lines · Hans Verkuil
Hi Jose,
Some more comments:
Thanks for the review!
Quoted 133 lines · Jose Abreu
Quoted 129 lines
This is an initial submission for the Synopsys DesignWare HDMI RX Controller Driver. This driver interacts with a phy driver so that a communication between them is created and a video pipeline is configured.
The controller + phy pipeline can then be integrated into a fully featured system that can be able to receive video up to 4k@60Hz with deep color 48bit RGB, depending on the platform. Although, this initial version does not yet handle deep color modes.
This driver was implemented as a standard V4L2 subdevice and its main features are:
- Internal state machine that reconfigures phy until the video is not stable
- JTAG communication with phy
- Inter-module communication with phy driver
- Debug write/read ioctlsSome notes:
- RX sense controller (cable connection/disconnection) must be handled by the platform wrapper as this is not integrated into the controller RTL
- The same goes for EDID ROM's
- ZCAL calibration is needed only in FPGA platforms, in ASIC this is not needed
- The state machine is not an ideal solution as it creates a kthread but it is needed because some sources might not be very stable at sending the video (i.e. we must react accordingly).Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>Cc Mauro Carvalho Chehab <[email protected]>Cc Hans Verkuil <[email protected]>Cc Sylwester Nawrocki <[email protected]>Cc Sakari Ailus <[email protected]>Cc Philippe Ombredanne <[email protected]>---
Changes from v9:
- Use SPDX License ID (Philippe)
- UseLOW_DRIVECEC error (Hans)
- Fill bt->il_* fields (Hans)
- Fixformat->fieldto NONE (Hans)
- Drop a left-over comment (Hans)
- UseCEC_CAP_DEFAULTS(Hans)
Changes from v8:
- Incorporate Sakari's work on ASYNC subdevs
Changes from v6:
- edid-phandle now also looks for parent node (Sylwester)
- Fix kbuild build warnings
Changes from v5:
- Removed HDCP 1.4 support (Hans)
- Removed some CEC debug messages (Hans)
- Adds_dv_timingscallback (Hans)
- AddV4L2_CID_DV_RX_POWER_PRESENTctrl (Hans)
Changes from v4:
- Add flagV4L2_SUBDEV_FL_HAS_DEVNODE(Sylwester)
- Remove some comments and change some messages todev_dbg(Sylwester)
- Usev4l2_async_subnotifier_register()(Sylwester)
Changes from v3:
- Use v4l2 async API (Sylwester)
- Do not block waiting for phy
- Do not use busy waiting delays (Sylwester)
- Simplifydw_hdmi_power_on(Sylwester)
- Use clock API (Sylwester)
- Use compatible string (Sylwester)
- Minor fixes (Sylwester)
Changes from v2:
- Address review comments from Hans regarding CEC
- Use CEC notifier
- Enable SCDC
Changes from v1:
- Add support for CEC
- Correct typo errors
- Correctly detect interlaced video modes
- Correct VIC parsing
Changes from RFC:
- Add support for HDCP 1.4
- FixupHDMI_VICnot being parsed (Hans)
- Send source change signal when powering off (Hans)
- Add a "wait stable delay"
- Detect interlaced video modes (Hans)
- Restrain g/s_register from reading/writing to HDCP regs (Hans)--- drivers/media/platform/dwc/Kconfig | 15 + drivers/media/platform/dwc/Makefile | 1 + drivers/media/platform/dwc/dw-hdmi-rx.c | 1840 +++++++++++++++++++++++++++++++ drivers/media/platform/dwc/dw-hdmi-rx.h | 419 +++++++ include/media/dwc/dw-hdmi-rx-pdata.h | 48 + 5 files changed, 2323 insertions(+) create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.c create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.h create mode 100644 include/media/dwc/dw-hdmi-rx-pdata.hdrivers/media/platform/dwc/Kconfig +15
-0@@ -6,3 +6,18 @@ config VIDEO_DWC_HDMI_PHY_E405 To compile this driver as a module, choose M here. The module will be called dw-hdmi-phy-e405. + +config VIDEO_DWC_HDMI_RX + tristate "Synopsys Designware HDMI Receiver driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + help + Support for Synopsys Designware HDMI RX controller. + + To compile this driver as a module, choose M here. The module + will be called dw-hdmi-rx. + +config VIDEO_DWC_HDMI_RX_CEC + bool + depends on VIDEO_DWC_HDMI_RX + select CEC_CORE + select CEC_NOTIFIERdrivers/media/platform/dwc/Makefile +1
-0@@ -1 +1,2 @@ obj-$(CONFIG_VIDEO_DWC_HDMI_PHY_E405) += dw-hdmi-phy-e405.o +obj-$(CONFIG_VIDEO_DWC_HDMI_RX) += dw-hdmi-rx.odrivers/media/platform/dwc/dw-hdmi-rx.c new file +0
-0<snip>
Quoted 9 lines
drivers/media/platform/dwc/dw-hdmi-rx.c line 1077
Quoted 7 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +7
-0+static int dw_hdmi_s_routing (struct v4l2_subdev *sd,u32 input,u32 output, +u32 config) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +if (!has_signal (dw_dev, input)) +return -EINVAL ;Why would this be a reason to reject this? There may be no signal now, but a signal might appear later.
I would expect s_routing to only be called if there is an input connected, otherwise we are just wasting resources by trying to equalize an input that is not present ... I can remove the "if" as there are other safe guards for this though (for example g_fmt will return an error) ...
Quoted 104 lines
Quoted 104 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +103
-0+ + dw_dev->selected_input = input; +if (input == dw_dev->configured_input) +return 0 ; + +dw_hdmi_power_off (dw_dev); +return dw_hdmi_power_on (dw_dev, input); +} + +static int dw_hdmi_g_input_status (struct v4l2_subdev *sd,u32 *status) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + + *status =0 ; +if (!has_signal (dw_dev, dw_dev->selected_input)) + *status |=V4L2_IN_ST_NO_POWER ; +if (is_off (dw_dev)) + *status |=V4L2_IN_ST_NO_SIGNAL ; + +dev_dbg (dw_dev->dev,"%s: status=0x%x\n" , __func__, *status); +return 0 ; +} + +static int dw_hdmi_g_parm (struct v4l2_subdev *sd,struct v4l2_streamparm *parm) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + +/* TODO: Use helper to compute timeperframe */ + parm->parm.capture.timeperframe.numerator =1 ; + parm->parm.capture.timeperframe.denominator =60 ; +return 0 ; +} + +static int dw_hdmi_s_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); +if (!v4l2_valid_dv_timings (timings, &dw_hdmi_timings_cap,NULL ,NULL )) +return -EINVAL ; +if (v4l2_match_dv_timings (timings, &dw_dev->timings,0 , false)) +return 0 ; + + dw_dev->timings = *timings; +return 0 ; +} + +static int dw_hdmi_g_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + + *timings = dw_dev->timings; +return 0 ; +} + +static int dw_hdmi_query_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); +struct v4l2_bt_timings *bt = &timings->bt; +bool is_hdmi_vic; +u32 htot, hofs; +u32 vtot; +u8 vic; + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + +memset (timings,0 ,sizeof (*timings)); + + timings->type =V4L2_DV_BT_656_1120 ; + bt->width =hdmi_readl (dw_dev,HDMI_MD_HACT_PX ); + bt->height =hdmi_readl (dw_dev,HDMI_MD_VAL ); + bt->interlaced =hdmi_readl (dw_dev,HDMI_MD_STS ) &HDMI_MD_STS_ILACE ? +V4L2_DV_INTERLACED :V4L2_DV_PROGRESSIVE ; + +if (hdmi_readl (dw_dev,HDMI_ISTS ) &HDMI_ISTS_VS_POL_ADJ ) + bt->polarities |=V4L2_DV_VSYNC_POS_POL ; +if (hdmi_readl (dw_dev,HDMI_ISTS ) &HDMI_ISTS_HS_POL_ADJ ) + bt->polarities |=V4L2_DV_HSYNC_POS_POL ; + + bt->pixelclock =dw_hdmi_get_pixelclk (dw_dev); + +/* HTOT = HACT + HFRONT + HSYNC + HBACK */ + htot =hdmi_mask_readl (dw_dev,HDMI_MD_HT1 , +HDMI_MD_HT1_HTOT_PIX_OFFSET , +HDMI_MD_HT1_HTOT_PIX_MASK ); +/* HOFS = HSYNC + HBACK */ + hofs =hdmi_mask_readl (dw_dev,HDMI_MD_HT1 , +HDMI_MD_HT1_HOFS_PIX_OFFSET , +HDMI_MD_HT1_HOFS_PIX_MASK ); + + bt->hfrontporch = htot - hofs - bt->width; + bt->hsync =hdmi_mask_readl (dw_dev,HDMI_MD_HT0 , +HDMI_MD_HT0_HS_CLK_OFFSET , +HDMI_MD_HT0_HS_CLK_MASK ); + bt->hbackporch = hofs - bt->hsync; +
Quoted 8 lines
drivers/media/platform/dwc/dw-hdmi-rx.c line 1187
Quoted 7 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +7
-0+/* VTOT = VACT + VFRONT + VSYNC + VBACK */ + vtot =hdmi_readl (dw_dev,HDMI_MD_VTL ); + +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ How do you know this waits for 1 field? Or is this: "Wait for at least 1 field"?
Its wait at least for 1 field. This is over-generous because its assuming the frame rate is 20fps (which in HDMI does not happen).
Quoted 3 lines
I don't know exactly how the IP does this, but it looks fishy to me. If it is correct, then it could use a few comments about what is going on here as it is not obvious.
The IP updates the values at each field but I need to change this register to populate all values in the bt struct.
Quoted 3 lines
And what happens if the framerate is even slower? You know the pixelclock and total width+height, so you can calculate the framerate from that.
Hmm, but then I have to consider pixelclk error, msleep error, ...
Quoted 28 lines
Quoted 28 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +27
-0+ bt->vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL ); + bt->vfrontporch = vtot - bt->height - bt->vsync - bt->vbackporch; + +if (bt->interlaced ==V4L2_DV_INTERLACED ) { +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK ); +msleep (100 );/* Wait for 2 fields */ + + vtot =hdmi_readl (dw_dev,HDMI_MD_VTL ); +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->il_vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 ); + bt->il_vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL );
Quoted 8 lines
drivers/media/platform/dwc/dw-hdmi-rx.c line 1220
Quoted 6 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +6
-0+ bt->il_vfrontporch = vtot - bt->height - bt->il_vsync - + bt->il_vbackporch; + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK );Same here, I'm not sure this is correct. What is the output of 'v4l2-ctl --query-dv-timings' when you feed it a standard interlaced format?
I used v4l2-ctl --log-status with interlaced format and everything seemed correct ...
Thanks and Best Regards,
Jose Miguel Abreu
Quoted 24 lines
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +9
-0+ } + + bt->standards =V4L2_DV_BT_STD_CEA861 ; + + vic =dw_hdmi_get_curr_vic (dw_dev, &is_hdmi_vic); +if (vic) { +if (is_hdmi_vic) { + bt->flags |=V4L2_DV_FL_HAS_HDMI_VIC ; + bt->hdmi_vic = vic;drivers/media/platform/dwc/dw-hdmi-rx.c line 1239
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +10
-0+ bt->cea861_vic =0 ; + }else { + bt->flags |=V4L2_DV_FL_HAS_CEA861_VIC ; + bt->hdmi_vic =0 ; + bt->cea861_vic = vic; + } + } + +return 0 ; +}Regards,
Hans
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
Quoted 152 lines · Jose Abreu
Hi Hans,
Quoted 3 lines · Hans Verkuil
Hi Jose,
Some more comments:
Thanks for the review!
Quoted 133 lines · Jose Abreu
Quoted 129 lines
This is an initial submission for the Synopsys DesignWare HDMI RX Controller Driver. This driver interacts with a phy driver so that a communication between them is created and a video pipeline is configured.
The controller + phy pipeline can then be integrated into a fully featured system that can be able to receive video up to 4k@60Hz with deep color 48bit RGB, depending on the platform. Although, this initial version does not yet handle deep color modes.
This driver was implemented as a standard V4L2 subdevice and its main features are:
- Internal state machine that reconfigures phy until the video is not stable
- JTAG communication with phy
- Inter-module communication with phy driver
- Debug write/read ioctlsSome notes:
- RX sense controller (cable connection/disconnection) must be handled by the platform wrapper as this is not integrated into the controller RTL
- The same goes for EDID ROM's
- ZCAL calibration is needed only in FPGA platforms, in ASIC this is not needed
- The state machine is not an ideal solution as it creates a kthread but it is needed because some sources might not be very stable at sending the video (i.e. we must react accordingly).Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>Cc Mauro Carvalho Chehab <[email protected]>Cc Hans Verkuil <[email protected]>Cc Sylwester Nawrocki <[email protected]>Cc Sakari Ailus <[email protected]>Cc Philippe Ombredanne <[email protected]>---
Changes from v9:
- Use SPDX License ID (Philippe)
- UseLOW_DRIVECEC error (Hans)
- Fill bt->il_* fields (Hans)
- Fixformat->fieldto NONE (Hans)
- Drop a left-over comment (Hans)
- UseCEC_CAP_DEFAULTS(Hans)
Changes from v8:
- Incorporate Sakari's work on ASYNC subdevs
Changes from v6:
- edid-phandle now also looks for parent node (Sylwester)
- Fix kbuild build warnings
Changes from v5:
- Removed HDCP 1.4 support (Hans)
- Removed some CEC debug messages (Hans)
- Adds_dv_timingscallback (Hans)
- AddV4L2_CID_DV_RX_POWER_PRESENTctrl (Hans)
Changes from v4:
- Add flagV4L2_SUBDEV_FL_HAS_DEVNODE(Sylwester)
- Remove some comments and change some messages todev_dbg(Sylwester)
- Usev4l2_async_subnotifier_register()(Sylwester)
Changes from v3:
- Use v4l2 async API (Sylwester)
- Do not block waiting for phy
- Do not use busy waiting delays (Sylwester)
- Simplifydw_hdmi_power_on(Sylwester)
- Use clock API (Sylwester)
- Use compatible string (Sylwester)
- Minor fixes (Sylwester)
Changes from v2:
- Address review comments from Hans regarding CEC
- Use CEC notifier
- Enable SCDC
Changes from v1:
- Add support for CEC
- Correct typo errors
- Correctly detect interlaced video modes
- Correct VIC parsing
Changes from RFC:
- Add support for HDCP 1.4
- FixupHDMI_VICnot being parsed (Hans)
- Send source change signal when powering off (Hans)
- Add a "wait stable delay"
- Detect interlaced video modes (Hans)
- Restrain g/s_register from reading/writing to HDCP regs (Hans)--- drivers/media/platform/dwc/Kconfig | 15 + drivers/media/platform/dwc/Makefile | 1 + drivers/media/platform/dwc/dw-hdmi-rx.c | 1840 +++++++++++++++++++++++++++++++ drivers/media/platform/dwc/dw-hdmi-rx.h | 419 +++++++ include/media/dwc/dw-hdmi-rx-pdata.h | 48 + 5 files changed, 2323 insertions(+) create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.c create mode 100644 drivers/media/platform/dwc/dw-hdmi-rx.h create mode 100644 include/media/dwc/dw-hdmi-rx-pdata.hdrivers/media/platform/dwc/Kconfig +15
-0@@ -6,3 +6,18 @@ config VIDEO_DWC_HDMI_PHY_E405 To compile this driver as a module, choose M here. The module will be called dw-hdmi-phy-e405. + +config VIDEO_DWC_HDMI_RX + tristate "Synopsys Designware HDMI Receiver driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + help + Support for Synopsys Designware HDMI RX controller. + + To compile this driver as a module, choose M here. The module + will be called dw-hdmi-rx. + +config VIDEO_DWC_HDMI_RX_CEC + bool + depends on VIDEO_DWC_HDMI_RX + select CEC_CORE + select CEC_NOTIFIERdrivers/media/platform/dwc/Makefile +1
-0@@ -1 +1,2 @@ obj-$(CONFIG_VIDEO_DWC_HDMI_PHY_E405) += dw-hdmi-phy-e405.o +obj-$(CONFIG_VIDEO_DWC_HDMI_RX) += dw-hdmi-rx.odrivers/media/platform/dwc/dw-hdmi-rx.c new file +0
-0<snip>
Quoted 9 lines
drivers/media/platform/dwc/dw-hdmi-rx.c line 1077
Quoted 7 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +7
-0+static int dw_hdmi_s_routing (struct v4l2_subdev *sd,u32 input,u32 output, +u32 config) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +if (!has_signal (dw_dev, input)) +return -EINVAL ;Why would this be a reason to reject this? There may be no signal now, but a signal might appear later.
Quoted 5 lines
I would expect
s_routingto only be called if there is an input connected, otherwise we are just wasting resources by trying to equalize an input that is not present ... I can remove the "if" as there are other safe guards for this though (for exampleg_fmtwill return an error) ...
No, s_routing is typically called as a result of a VIDIOC_S_INPUT call, and that can come whether or not there is a signal on an input. In fact, initially the first input is always selected anyway, whether or not there is a signal.
g_fmt will just return the current configured format, this is unrelated to whether or not there is a signal.
The only times the driver checks whether or not there is a signal (and what that is) are:
1) g_input_status
2) query_dv_timings
3) when the irq detects a signal change and sends V4L2_EVENT_SOURCE_CHANGE
Quoted 114 lines
Quoted 105 lines
Quoted 105 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +103
-0+ + dw_dev->selected_input = input; +if (input == dw_dev->configured_input) +return 0 ; + +dw_hdmi_power_off (dw_dev); +return dw_hdmi_power_on (dw_dev, input); +} + +static int dw_hdmi_g_input_status (struct v4l2_subdev *sd,u32 *status) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + + *status =0 ; +if (!has_signal (dw_dev, dw_dev->selected_input)) + *status |=V4L2_IN_ST_NO_POWER ; +if (is_off (dw_dev)) + *status |=V4L2_IN_ST_NO_SIGNAL ; + +dev_dbg (dw_dev->dev,"%s: status=0x%x\n" , __func__, *status); +return 0 ; +} + +static int dw_hdmi_g_parm (struct v4l2_subdev *sd,struct v4l2_streamparm *parm) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + +/* TODO: Use helper to compute timeperframe */ + parm->parm.capture.timeperframe.numerator =1 ; + parm->parm.capture.timeperframe.denominator =60 ; +return 0 ; +} + +static int dw_hdmi_s_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); +if (!v4l2_valid_dv_timings (timings, &dw_hdmi_timings_cap,NULL ,NULL )) +return -EINVAL ; +if (v4l2_match_dv_timings (timings, &dw_dev->timings,0 , false)) +return 0 ; + + dw_dev->timings = *timings; +return 0 ; +} + +static int dw_hdmi_g_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + + *timings = dw_dev->timings; +return 0 ; +} + +static int dw_hdmi_query_dv_timings (struct v4l2_subdev *sd, +struct v4l2_dv_timings *timings) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); +struct v4l2_bt_timings *bt = &timings->bt; +bool is_hdmi_vic; +u32 htot, hofs; +u32 vtot; +u8 vic; + +dev_dbg (dw_dev->dev,"%s\n" , __func__); + +memset (timings,0 ,sizeof (*timings)); + + timings->type =V4L2_DV_BT_656_1120 ; + bt->width =hdmi_readl (dw_dev,HDMI_MD_HACT_PX ); + bt->height =hdmi_readl (dw_dev,HDMI_MD_VAL ); + bt->interlaced =hdmi_readl (dw_dev,HDMI_MD_STS ) &HDMI_MD_STS_ILACE ? +V4L2_DV_INTERLACED :V4L2_DV_PROGRESSIVE ; + +if (hdmi_readl (dw_dev,HDMI_ISTS ) &HDMI_ISTS_VS_POL_ADJ ) + bt->polarities |=V4L2_DV_VSYNC_POS_POL ; +if (hdmi_readl (dw_dev,HDMI_ISTS ) &HDMI_ISTS_HS_POL_ADJ ) + bt->polarities |=V4L2_DV_HSYNC_POS_POL ; + + bt->pixelclock =dw_hdmi_get_pixelclk (dw_dev); + +/* HTOT = HACT + HFRONT + HSYNC + HBACK */ + htot =hdmi_mask_readl (dw_dev,HDMI_MD_HT1 , +HDMI_MD_HT1_HTOT_PIX_OFFSET , +HDMI_MD_HT1_HTOT_PIX_MASK ); +/* HOFS = HSYNC + HBACK */ + hofs =hdmi_mask_readl (dw_dev,HDMI_MD_HT1 , +HDMI_MD_HT1_HOFS_PIX_OFFSET , +HDMI_MD_HT1_HOFS_PIX_MASK ); + + bt->hfrontporch = htot - hofs - bt->width; + bt->hsync =hdmi_mask_readl (dw_dev,HDMI_MD_HT0 , +HDMI_MD_HT0_HS_CLK_OFFSET , +HDMI_MD_HT0_HS_CLK_MASK ); + bt->hbackporch = hofs - bt->hsync; +Quoted 8 lines
drivers/media/platform/dwc/dw-hdmi-rx.c line 1187
Quoted 7 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +7
-0+/* VTOT = VACT + VFRONT + VSYNC + VBACK */ + vtot =hdmi_readl (dw_dev,HDMI_MD_VTL ); + +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ How do you know this waits for 1 field? Or is this: "Wait for at least 1 field"?
Quoted 2 lines
Its wait at least for 1 field. This is over-generous because its assuming the frame rate is 20fps (which in HDMI does not happen).
With custom timings it can happen (i.e. a 15 fps stream). Admittedly, it's not common, but people sometimes use it.
Quoted 7 lines
Quoted 4 lines
I don't know exactly how the IP does this, but it looks fishy to me. If it is correct, then it could use a few comments about what is going on here as it is not obvious.
The IP updates the values at each field but I need to change this register to populate all values in the bt struct.
How do you know which field (top or bottom) you've captured? How do you know you didn't miss e.g. the bottom field and instead end up with two top field measurements?
The top and bottom field are almost, but not quite the same. Typically the vertical backporch of the fields differs by 1 where the second field's backporch is larger by 1 line.
Quoted 6 lines
Quoted 4 lines
And what happens if the framerate is even slower? You know the pixelclock and total width+height, so you can calculate the framerate from that.
Hmm, but then I have to consider pixelclk error, msleep error, ...
But you have that now as well.
An alternative is to measure a single field and deduce the backporch values from that.
At least for all the common HDMI interlaced formats il_vbackporch is an even value and vbackporch is il_vbackporch - 1.
So if you get an even backporch, then you found il_vbackporch, and if it is odd, then you found vbackporch.
Quoted 38 lines
Quoted 29 lines
Quoted 29 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +27
-0+ bt->vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL ); + bt->vfrontporch = vtot - bt->height - bt->vsync - bt->vbackporch; + +if (bt->interlaced ==V4L2_DV_INTERLACED ) { +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK ); +msleep (100 );/* Wait for 2 fields */ + + vtot =hdmi_readl (dw_dev,HDMI_MD_VTL ); +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->il_vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 ); + bt->il_vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL );Quoted 8 lines
drivers/media/platform/dwc/dw-hdmi-rx.c line 1220
Quoted 6 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +6
-0+ bt->il_vfrontporch = vtot - bt->height - bt->il_vsync - + bt->il_vbackporch; + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK );Same here, I'm not sure this is correct. What is the output of 'v4l2-ctl --query-dv-timings' when you feed it a standard interlaced format?
Quoted 2 lines
I used v4l2-ctl --log-status with interlaced format and everything seemed correct ...
Can you show a few examples? Is vbackport odd? And is il_vbackporch equal to vbackporch + 1?
Interlaced is tricky :-)
Regards,
Hans
Quoted 29 lines
Thanks and Best Regards,
Jose Miguel AbreuQuoted 24 lines
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +9
-0+ } + + bt->standards =V4L2_DV_BT_STD_CEA861 ; + + vic =dw_hdmi_get_curr_vic (dw_dev, &is_hdmi_vic); +if (vic) { +if (is_hdmi_vic) { + bt->flags |=V4L2_DV_FL_HAS_HDMI_VIC ; + bt->hdmi_vic = vic;drivers/media/platform/dwc/dw-hdmi-rx.c line 1239
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +10
-0+ bt->cea861_vic =0 ; + }else { + bt->flags |=V4L2_DV_FL_HAS_CEA861_VIC ; + bt->hdmi_vic =0 ; + bt->cea861_vic = vic; + } + } + +return 0 ; +}Regards,
Hans
Philippe Ombredanne
Acked-byRe: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
Jose,
On 12/12/17 17:02, Jose Abreu wrote:
<snip>
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Signed-off-by Jose Abreu <[email protected]>Cc Joao Pinto <[email protected]>Cc Mauro Carvalho Chehab <[email protected]>Cc Hans Verkuil <[email protected]>Cc Sylwester Nawrocki <[email protected]>Cc Sakari Ailus <[email protected]>Cc Philippe Ombredanne <[email protected]>---
Changes from v9:
- Use SPDX License ID (Philippe)
For the use of SPDX tags, thanks!
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
In reply to Hans VerkuilHi Hans,
Quoted 24 lines · Hans Verkuil
Quoted 10 lines · Jose Abreu
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c line 1077
Quoted 8 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +7
-0+static int dw_hdmi_s_routing (struct v4l2_subdev *sd,u32 input,u32 output, +u32 config) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +if (!has_signal (dw_dev, input)) +return -EINVAL ;Why would this be a reason to reject this? There may be no signal now, but a signal might appear later.
Quoted 5 lines
I would expect
s_routingto only be called if there is an input connected, otherwise we are just wasting resources by trying to equalize an input that is not present ... I can remove the "if" as there are other safe guards for this though (for exampleg_fmtwill return an error) ...No,
s_routingis typically called as a result of aVIDIOC_S_INPUTcall, and that can come whether or not there is a signal on an input. In fact, initially the first input is always selected anyway, whether or not there is a signal.
g_fmtwill just return the current configured format, this is unrelated to whether or not there is a signal.
Quoted 6 lines
The only times the driver checks whether or not there is a signal (and what that is) are:
1)
g_input_status
2)query_dv_timings
3) when the irq detects a signal change and sendsV4L2_EVENT_SOURCE_CHANGE
Ok, I will remove the checks then.
Quoted 67 lines
Quoted 5 lines
Quoted 3 lines
Review
Quoted 2 lines
(quoted diff) +1
-0+ msleep(50); /* Wait for 1 field */How do you know this waits for 1 field? Or is this: "Wait for at least 1 field"?
Its wait at least for 1 field. This is over-generous because its assuming the frame rate is 20fps (which in HDMI does not happen).
With custom timings it can happen (i.e. a 15 fps stream). Admittedly, it's not common, but people sometimes use it.
Quoted 5 lines
Quoted 3 lines
I don't know exactly how the IP does this, but it looks fishy to me. If it is correct, then it could use a few comments about what is going on here as it is not obvious.
The IP updates the values at each field but I need to change this register to populate all values in the bt struct.
How do you know which field (top or bottom) you've captured? How do you know you didn't miss e.g. the bottom field and instead end up with two top field measurements?
The top and bottom field are almost, but not quite the same. Typically the vertical backporch of the fields differs by 1 where the second field's backporch is larger by 1 line.
Quoted 3 lines
Quoted 2 lines
And what happens if the framerate is even slower? You know the pixelclock and total width+height, so you can calculate the framerate from that.
Hmm, but then I have to consider pixelclk error, msleep error, ...
But you have that now as well.
An alternative is to measure a single field and deduce the backporch values from that.
At least for all the common HDMI interlaced formats
il_vbackporchis an even value and vbackporch isil_vbackporch- 1.So if you get an even backporch, then you found
il_vbackporch, and if it is odd, then you found vbackporch.Quoted 35 lines
Quoted 35 lines
Quoted 23 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +23
-0+ bt->vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL ); + bt->vfrontporch = vtot - bt->height - bt->vsync - bt->vbackporch; + +if (bt->interlaced ==V4L2_DV_INTERLACED ) { +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK ); +msleep (100 );/* Wait for 2 fields */ + + vtot =hdmi_readl (dw_dev,HDMI_MD_VTL ); +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->il_vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + + hdmi_mask_writel(dw_dev, 0x0, HDMI_MD_VCTRL,drivers/media/platform/dwc/dw-hdmi-rx.c line 1220
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +10
-0+HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , + HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK); +msleep (50 ); + bt->il_vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL ); + bt->il_vfrontporch = vtot - bt->height - bt->il_vsync - + bt->il_vbackporch; + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK );Same here, I'm not sure this is correct. What is the output of 'v4l2-ctl --query-dv-timings' when you feed it a standard interlaced format?
Quoted 5 lines
Quoted 2 lines
I used v4l2-ctl --log-status with interlaced format and everything seemed correct ...
Can you show a few examples? Is vbackport odd? And is
il_vbackporchequal to vbackporch + 1?Interlaced is tricky :-)
Indeed. I compared the values with the spec and they are not correct. Even hsync is wrong. I already corrected in the code the hsync but regarding interlace I'm not seeing an easy way to do this without using interrupts in each vsync because the register I was toggling does not behave as I expected (I misunderstood the databook). Maybe we should not detect interlaced modes for now? Or not fill the il_ fields?
Best Regards,
Jose Miguel Abreu
Quoted 5 lines
Regards,
Hans
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
Quoted 109 lines · Jose Abreu
Hi Hans,
Quoted 24 lines · Hans Verkuil
Quoted 10 lines · Jose Abreu
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c line 1077
Quoted 8 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +7
-0+static int dw_hdmi_s_routing (struct v4l2_subdev *sd,u32 input,u32 output, +u32 config) +{ +struct dw_hdmi_dev *dw_dev =to_dw_dev (sd); + +if (!has_signal (dw_dev, input)) +return -EINVAL ;Why would this be a reason to reject this? There may be no signal now, but a signal might appear later.
Quoted 5 lines
I would expect
s_routingto only be called if there is an input connected, otherwise we are just wasting resources by trying to equalize an input that is not present ... I can remove the "if" as there are other safe guards for this though (for exampleg_fmtwill return an error) ...No,
s_routingis typically called as a result of aVIDIOC_S_INPUTcall, and that can come whether or not there is a signal on an input. In fact, initially the first input is always selected anyway, whether or not there is a signal.
g_fmtwill just return the current configured format, this is unrelated to whether or not there is a signal.Quoted 6 lines
The only times the driver checks whether or not there is a signal (and what that is) are:
1)
g_input_status
2)query_dv_timings
3) when the irq detects a signal change and sendsV4L2_EVENT_SOURCE_CHANGEOk, I will remove the checks then.
Quoted 67 lines
Quoted 5 lines
Quoted 3 lines
Review
Quoted 2 lines
(quoted diff) +1
-0+ msleep(50); /* Wait for 1 field */How do you know this waits for 1 field? Or is this: "Wait for at least 1 field"?
Its wait at least for 1 field. This is over-generous because its assuming the frame rate is 20fps (which in HDMI does not happen).
With custom timings it can happen (i.e. a 15 fps stream). Admittedly, it's not common, but people sometimes use it.
Quoted 5 lines
Quoted 3 lines
I don't know exactly how the IP does this, but it looks fishy to me. If it is correct, then it could use a few comments about what is going on here as it is not obvious.
The IP updates the values at each field but I need to change this register to populate all values in the bt struct.
How do you know which field (top or bottom) you've captured? How do you know you didn't miss e.g. the bottom field and instead end up with two top field measurements?
The top and bottom field are almost, but not quite the same. Typically the vertical backporch of the fields differs by 1 where the second field's backporch is larger by 1 line.
Quoted 3 lines
Quoted 2 lines
And what happens if the framerate is even slower? You know the pixelclock and total width+height, so you can calculate the framerate from that.
Hmm, but then I have to consider pixelclk error, msleep error, ...
But you have that now as well.
An alternative is to measure a single field and deduce the backporch values from that.
At least for all the common HDMI interlaced formats
il_vbackporchis an even value and vbackporch isil_vbackporch- 1.So if you get an even backporch, then you found
il_vbackporch, and if it is odd, then you found vbackporch.Quoted 35 lines
Quoted 35 lines
Quoted 23 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +23
-0+ bt->vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL ); + bt->vfrontporch = vtot - bt->height - bt->vsync - bt->vbackporch; + +if (bt->interlaced ==V4L2_DV_INTERLACED ) { +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK ); +msleep (100 );/* Wait for 2 fields */ + + vtot =hdmi_readl (dw_dev,HDMI_MD_VTL ); +hdmi_mask_writel (dw_dev,0x1 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , +HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK ); +msleep (50 );/* Wait for 1 field */ + bt->il_vsync =hdmi_readl (dw_dev,HDMI_MD_VOL ); + + hdmi_mask_writel(dw_dev, 0x0, HDMI_MD_VCTRL,drivers/media/platform/dwc/dw-hdmi-rx.c line 1220
Quoted 10 lines
drivers/media/platform/dwc/dw-hdmi-rx.c new file +10
-0+HDMI_MD_VCTRL_V_OFFS_LIN_MODE_OFFSET , + HDMI_MD_VCTRL_V_OFFS_LIN_MODE_MASK); +msleep (50 ); + bt->il_vbackporch =hdmi_readl (dw_dev,HDMI_MD_VOL ); + bt->il_vfrontporch = vtot - bt->height - bt->il_vsync - + bt->il_vbackporch; + +hdmi_mask_writel (dw_dev,0x0 ,HDMI_MD_VCTRL , +HDMI_MD_VCTRL_V_MODE_OFFSET , +HDMI_MD_VCTRL_V_MODE_MASK );Same here, I'm not sure this is correct. What is the output of 'v4l2-ctl --query-dv-timings' when you feed it a standard interlaced format?
Quoted 5 lines
Quoted 2 lines
I used v4l2-ctl --log-status with interlaced format and everything seemed correct ...
Can you show a few examples? Is vbackport odd? And is
il_vbackporchequal to vbackporch + 1?Interlaced is tricky :-)
Quoted 7 lines
Indeed. I compared the values with the spec and they are not correct. Even hsync is wrong. I already corrected in the code the hsync but regarding interlace I'm not seeing an easy way to do this without using interrupts in each vsync because the register I was toggling does not behave as I expected (I misunderstood the databook). Maybe we should not detect interlaced modes for now? Or not fill the
il_fields?
As I mentioned above you as long as you get a good backporch value you can deduce from whether it is an odd or even number to which field it belongs and fill in the other values. So I think you only need to read these values for one field.
Filling in good values here (at least as far as is possible since not all hardware can give it) will help debugging issues, even if you otherwise do not support interlaced.
Regards,
Hans
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
Hi Hans,
Quoted 9 lines · Hans Verkuil
Quoted 8 lines · Jose Abreu
Indeed. I compared the values with the spec and they are not correct. Even hsync is wrong. I already corrected in the code the hsync but regarding interlace I'm not seeing an easy way to do this without using interrupts in each vsync because the register I was toggling does not behave as I expected (I misunderstood the databook). Maybe we should not detect interlaced modes for now? Or not fill the
il_fields?
Quoted 8 lines
As I mentioned above you as long as you get a good backporch value you can deduce from whether it is an odd or even number to which field it belongs and fill in the other values. So I think you only need to read these values for one field.
Filling in good values here (at least as far as is possible since not all hardware can give it) will help debugging issues, even if you otherwise do not support interlaced.
Ok, I will fill the fields.
Until the end of January I will be quite busy in another project so if you could review the remaining patches of this series I would appreciate very much ... This way when I have the time I can code all the changes and send them at once.
Thanks and Best Regards,
Jose Miguel Abreu
Quoted 4 lines
Regards,
Hans
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
Hi Jose,
I have not seen any updates for this series. What is the status? I thought we were close to merging this...
Regards,
Hans
Quoted 37 lines · Jose Abreu
Hi Hans,
Quoted 9 lines · Hans Verkuil
Quoted 8 lines · Jose Abreu
Indeed. I compared the values with the spec and they are not correct. Even hsync is wrong. I already corrected in the code the hsync but regarding interlace I'm not seeing an easy way to do this without using interrupts in each vsync because the register I was toggling does not behave as I expected (I misunderstood the databook). Maybe we should not detect interlaced modes for now? Or not fill the
il_fields?Quoted 8 lines
As I mentioned above you as long as you get a good backporch value you can deduce from whether it is an odd or even number to which field it belongs and fill in the other values. So I think you only need to read these values for one field.
Filling in good values here (at least as far as is possible since not all hardware can give it) will help debugging issues, even if you otherwise do not support interlaced.
Ok, I will fill the fields.
Until the end of January I will be quite busy in another project so if you could review the remaining patches of this series I would appreciate very much ... This way when I have the time I can code all the changes and send them at once.
Thanks and Best Regards,
Jose Miguel AbreuQuoted 4 lines
Regards,
Hans
Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver
Hi Hans,
Quoted 4 lines · Hans Verkuil
Hi Jose,
I have not seen any updates for this series. What is the status? I thought we were close to merging this...
Good to hear from you! Hope you are doing fine! :)
A colleague of mine is working on this right now. We expect to send a new series with some more features added.
Thanks and Best Regards,
Jose Miguel Abreu
Quoted 39 lines
Regards,
Hans
Quoted 33 lines · Jose Abreu
Hi Hans,
Quoted 8 lines · Hans Verkuil
Quoted 7 lines · Jose Abreu
Indeed. I compared the values with the spec and they are not correct. Even hsync is wrong. I already corrected in the code the hsync but regarding interlace I'm not seeing an easy way to do this without using interrupts in each vsync because the register I was toggling does not behave as I expected (I misunderstood the databook). Maybe we should not detect interlaced modes for now? Or not fill the
il_fields?Quoted 8 lines
As I mentioned above you as long as you get a good backporch value you can deduce from whether it is an odd or even number to which field it belongs and fill in the other values. So I think you only need to read these values for one field.
Filling in good values here (at least as far as is possible since not all hardware can give it) will help debugging issues, even if you otherwise do not support interlaced.
Ok, I will fill the fields.
Until the end of January I will be quite busy in another project so if you could review the remaining patches of this series I would appreciate very much ... This way when I have the time I can code all the changes and send them at once.
Thanks and Best Regards,
Jose Miguel AbreuQuoted 3 lines
Regards,
Hans