Re: [PATCH v10 4/4] [media] platform: Add Synopsys DesignWare HDMI RX Controller Driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Hans,

On 12-12-2017 15:47, Hans Verkuil wrote:
> Hi Jose,
>
> Some more comments:

Thanks for the review!

>
> On 11/12/17 18:41, Jose Abreu wrote:
>> 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 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).
>>
>> Signed-off-by: Jose Abreu <joabreu@xxxxxxxxxxxx>
>> Cc: Joao Pinto <jpinto@xxxxxxxxxxxx>
>> Cc: Mauro Carvalho Chehab <mchehab@xxxxxxxxxx>
>> Cc: Hans Verkuil <hans.verkuil@xxxxxxxxx>
>> Cc: Sylwester Nawrocki <snawrocki@xxxxxxxxxx>
>> Cc: Sakari Ailus <sakari.ailus@xxxxxx>
>> Cc: Philippe Ombredanne <pombredanne@xxxxxxxx>
>> ---
>> 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
>>
>> diff --git a/drivers/media/platform/dwc/Kconfig b/drivers/media/platform/dwc/Kconfig
>> index 361d38d..3ddccde 100644
>> --- a/drivers/media/platform/dwc/Kconfig
>> +++ b/drivers/media/platform/dwc/Kconfig
>> @@ -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
>> diff --git a/drivers/media/platform/dwc/Makefile b/drivers/media/platform/dwc/Makefile
>> index fc3b62c..cd04ca9 100644
>> --- a/drivers/media/platform/dwc/Makefile
>> +++ b/drivers/media/platform/dwc/Makefile
>> @@ -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
>> diff --git a/drivers/media/platform/dwc/dw-hdmi-rx.c b/drivers/media/platform/dwc/dw-hdmi-rx.c
>> new file mode 100644
>> index 0000000..437351e
>> --- /dev/null
>> +++ b/drivers/media/platform/dwc/dw-hdmi-rx.c
> <snip>
>
>> +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) ...

>
>> +
>> +	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;
>> +
>> +	/* 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).

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

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

>
>> +	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);
>> +		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

>
>> +	}
>> +
>> +	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;
>> +			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
>




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux