Hi Hans, Thanks for your initial comments and feedback! From: Hans Verkuil <hverkuil-cisco@xxxxxxxxx> Date: qua, jun 02, 2021 at 12:54:38 > Hi Nelson, > > Thank you for this patch. Some initial comments below, I will > need to go over this another time for some of the colorimetry > related functions. > Ok. > BTW, if you are interested in HDCP support, let me know. Cisco worked > on an API for this, but due to various reasons we have not been able > to upstream it. But we do use it in our products. > Good to know! In that case if in the future we think to upstream something related with HDCP we will let you know. :) BTW, which versions of HDCP (1.4, 2.2, 2.3) the API supports? > On 02/06/2021 13:24, Nelson Costa wrote: > > This is an initial submission for the Synopsys DesignWare HDMI RX > > Controller Driver. It is responsible to manage and handle the PHY > > (through the PHY API) and the Controller configurations in order > > to configure the video and audio pipeline. > > > > This driver is implemented as a standard V4L2 subdevice. > > The main features of this module are: > > - Support for scrambling > > - Support for color depth up to 48bpp > > - Support for HDMI 2.0 modes up to 6G (HDMI 4k@60Hz) > > - Support for RGB, YCC444, YCC422 and YCC420 > > - Support for basic audio (LPCM 2ch, 32KHz/44.1KHz/48KHz, 16bit) > > - Support for Aspect Ratio > > - Support for CEC > > - Internal state machine that reconfigures PHY and controller > > - JTAG communication with PHY > > - Inter-module communication with PHY driver: > > * through the PHY API using the PHY reference "hdmi-phy" > > * through the callbacks that PHY DWC driver needs. > > - Debug write/read ioctls. > > > > Signed-off-by: Jose Abreu <jose.abreu@xxxxxxxxxxxx> > > Signed-off-by: Nelson Costa <nelson.costa@xxxxxxxxxxxx> > > --- > > drivers/media/platform/Kconfig | 2 + > > drivers/media/platform/Makefile | 1 + > > drivers/media/platform/dwc/Kconfig | 13 + > > drivers/media/platform/dwc/Makefile | 3 + > > drivers/media/platform/dwc/dw-hdmi-rx.c | 3237 +++++++++++++++++++++++++++++++ > > drivers/media/platform/dwc/dw-hdmi-rx.h | 476 +++++ > > include/media/dwc/dw-hdmi-rx-pdata.h | 126 ++ > > 7 files changed, 3858 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-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/Kconfig b/drivers/media/platform/Kconfig > > index b238a92..20db68d 100644 > > --- a/drivers/media/platform/Kconfig > > +++ b/drivers/media/platform/Kconfig > > @@ -29,6 +29,8 @@ source "drivers/media/platform/cadence/Kconfig" > > > > source "drivers/media/platform/davinci/Kconfig" > > > > +source "drivers/media/platform/dwc/Kconfig" > > + > > source "drivers/media/platform/omap/Kconfig" > > > > config VIDEO_ASPEED > > diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile > > index eedc14a..955aae6 100644 > > --- a/drivers/media/platform/Makefile > > +++ b/drivers/media/platform/Makefile > > @@ -6,6 +6,7 @@ > > obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/ > > obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o > > obj-$(CONFIG_VIDEO_CADENCE) += cadence/ > > +obj-y += dwc/ > > obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o > > obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/ > > obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/ > > diff --git a/drivers/media/platform/dwc/Kconfig b/drivers/media/platform/dwc/Kconfig > > new file mode 100644 > > index 0000000..e915ca6 > > --- /dev/null > > +++ b/drivers/media/platform/dwc/Kconfig > > @@ -0,0 +1,13 @@ > > +# SPDX-License-Identifier: GPL-2.0 > > +config VIDEO_DWC_HDMI_RX > > + tristate "Synopsys DesignWare HDMI Receiver driver" > > + depends on VIDEO_V4L2 > > + select MEDIA_CONTROLLER > > + select VIDEO_V4L2_SUBDEV_API > > + select HDMI > > + help > > + Support for Synopsys DesignWare HDMI RX Controller. > > + This driver supports HDMI 2.0 version. > > + > > + To compile this driver as a module, choose M here. The module > > + will be called dw-hdmi-rx. > > diff --git a/drivers/media/platform/dwc/Makefile b/drivers/media/platform/dwc/Makefile > > new file mode 100644 > > index 0000000..fddd30c > > --- /dev/null > > +++ b/drivers/media/platform/dwc/Makefile > > @@ -0,0 +1,3 @@ > > +# SPDX-License-Identifier: GPL-2.0 > > + > > +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..b20eccc > > --- /dev/null > > +++ b/drivers/media/platform/dwc/dw-hdmi-rx.c > > @@ -0,0 +1,3237 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * Copyright (c) 2018 - present Synopsys, Inc. and/or its affiliates. > > + * Synopsys DesignWare HDMI Receiver controller driver > > + * > > + * Author: Jose Abreu <jose.abreu@xxxxxxxxxxxx> > > + * Author: Nelson Costa <nelson.costa@xxxxxxxxxxxx> > > + */ > > + > > +#include <linux/clk.h> > > +#include <linux/delay.h> > > +#include <linux/dma-mapping.h> > > +#include <linux/interrupt.h> > > +#include <linux/module.h> > > +#include <linux/of.h> > > +#include <linux/of_platform.h> > > +#include <linux/platform_device.h> > > +#include <linux/phy/phy.h> > > +#include <linux/phy/dwc/dw-hdmi-phy-pdata.h> > > +#include <linux/v4l2-dv-timings.h> > > +#include <linux/workqueue.h> > > +#include <linux/rational.h> > > +#include <linux/hdmi.h> > > +#include <media/v4l2-async.h> > > +#include <media/v4l2-ctrls.h> > > +#include <media/v4l2-device.h> > > +#include <media/v4l2-dv-timings.h> > > +#include <media/v4l2-event.h> > > +#include <media/v4l2-subdev.h> > > +#include <media/dwc/dw-hdmi-rx-pdata.h> > > + > > +#include "dw-hdmi-rx.h" > > + > > +#define DW_HDMI_DEFAULT_TIMING V4L2_DV_BT_CEA_640X480P59_94 > > +#define DW_HDMI_JTAG_TAP_ADDR_CMD 0 > > +#define DW_HDMI_JTAG_TAP_WRITE_CMD 1 > > +#define DW_HDMI_JTAG_TAP_READ_CMD 3 > > +#define DW_HDMI_AUDIO_FREQ_RANGE 1000 > > + > > +/* EDID for HDMI RX */ > > +static u32 dw_hdmi_edid[] = { > > + /* V2 Support for */ > > + /* - Video modes up-to 2.97Gbps */ > > + /* - RGB, Ycc444/422/420 */ > > + /* - Audio 2Ch L-PCM */ > > + /* - SCDC CED */ > > + 0x00FFFFFF, 0xFFFFFF00, 0x4F2E4A21, 0x00000000, > > + 0x331E0103, 0x80462878, 0x0A0DC9A0, 0x57479827, > > + 0x12484C20, 0x00000101, 0x01010101, 0x01010101, > > + 0x01010101, 0x0101023A, 0x80187138, 0x2D40582C, > > + 0x450020C2, 0x3100001E, 0x011D0072, 0x51D01E20, > > + 0x6E285500, 0x20C23100, 0x001E0000, 0x00FC0053, > > + 0x4E505320, 0x48444D49, 0x2052580A, 0x000000FD, > > + 0x0017780F, 0x871E000A, 0x20202020, 0x20200160, > > + > > + 0x02035771, 0x83010000, 0x57696867, 0x6463625F, > > + 0x5E5D901F, 0x04131211, 0x0302015A, 0x59585756, > > + 0x55555453, 0x5251504F, 0x4E4D4C4B, 0x4A494847, > > + 0x46454443, 0x42412309, 0x07076E03, 0x0C001000, > > + 0xF83B2000, 0x80010203, 0x0467D85D, 0xC4010080, > > + 0x00E50E66, 0x65616000, 0x00000000, 0x00000000, > > + 0x00000000, 0x00000000, 0x00000000, 0x00000000, > > + 0x00000000, 0x00000000, 0x00000000, 0x000000B6, > > +}; > > This won't work, it's a bad idea to have a default EDID since it is almost > certainly wrong anyway. Userspace has to set the EDID since userspace knows > the capabilities of the device as a whole. I.e., while this receiver supports > up to 2.97 Gbps, there might other contraints that would lower this value. > And the EDID contains vendor and model information, and addition blocks and > capabilities might have to be set. > > So keep the EDID empty and the HPD low at start up, and leave it to userspace > to write the EDID (for testing the v4l2-ctl utility comes with a bunch of > predefined EDIDs). > I agree! This will be addressed in the next patch series. > > + > > +static const struct v4l2_dv_timings_cap dw_hdmi_timings_cap = { > > + .type = V4L2_DV_BT_656_1120, > > + .reserved = { 0 }, > > + V4L2_INIT_BT_TIMINGS(640, 4096, /* min/max width */ > > + 480, 4455, /* min/max height */ > > + 20000000, 600000000, /* min/max pixelclock */ > > + /* standards */ > > + V4L2_DV_BT_STD_CEA861, > > + /* capabilities */ > > + V4L2_DV_BT_CAP_PROGRESSIVE) > > +}; > > + > > +static const struct v4l2_event dw_hdmi_event_fmt = { > > + .type = V4L2_EVENT_SOURCE_CHANGE, > > + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, > > +}; > > + > > +enum dw_hdmi_state { > > + HDMI_STATE_NO_INIT = 0, > > + HDMI_STATE_POWER_OFF, > > + HDMI_STATE_POWER_UP, > > + HDMI_STATE_PHY_CONFIG, > > + HDMI_STATE_HPD, > > + HDMI_STATE_EQUALIZER, > > + HDMI_STATE_DATAPATH, > > + HDMI_STATE_VIDEO_UNSTABLE, > > + HDMI_STATE_AUDIO, > > + HDMI_STATE_POWER_ON, > > +}; > > + > > +struct dw_hdmi_infoframe_cfg { > > + const char *desc; > > + u8 header[3]; > > + u32 header_addr; > > + u32 payload_addr; > > + u32 payload_len; > > + void *frame; > > + unsigned int frame_size; > > +}; > > + > > +struct dw_hdmi_dev { > > + struct device *dev; > > + struct device_node *of_node; > > + void __iomem *regs; > > + struct clk *clk; > > + > > + /* Platform Data configuration */ > > + struct dw_hdmi_rx_pdata *config; > > + > > + /* Phy info */ > > + struct platform_device *phy_pdev; > > + struct dw_phy_pdata phy_config; > > + struct phy *phy; > > + > > + /* > > + * Used to prevent race conditions between multiple > > + * concurrent calls to handle the state machine changes > > + * and pending configurations. > > + */ > > + spinlock_t lock; > > + /* > > + * Used to prevent race conditions between multiple > > + * concurrent calls to notify the audio changes. > > + */ > > + spinlock_t event_lock; > > + > > + /* Work queue to handle the state machine */ > > + struct workqueue_struct *wq; > > + struct work_struct work; > > + > > + /* State machine variables */ > > + enum dw_hdmi_state state; > > + u32 mbus_code; > > + u32 old_mbus; > > + u8 *curr_edid_blocks; > > + u8 current_vic; > > + bool current_vic_is_4k; > > + bool registered; > > + bool pending_config; > > + bool force_off; > > + bool is_hdmi2; > > + bool is_scrambled; > > + bool phy_eq_force; > > + bool phy_eq_on; > > + bool hw_reset_on_hot_plug; > > + bool *input_connected; > > + unsigned int selected_input; > > + unsigned int configured_input; > > + unsigned int input_stat; > > + unsigned int audio_sf; > > + unsigned int tmds_valid_wait_count; > > + unsigned int has_clock_wait_ms; > > + unsigned int video_stable_wait_ms; > > + unsigned int reset_datapath_enable; > > + > > + /* Infoframes */ > > + union hdmi_infoframe aviif; > > + union hdmi_infoframe spdif; > > + union hdmi_infoframe audioif; > > + union hdmi_infoframe vsif; > > + > > + /* v4l2 device */ > > + struct v4l2_subdev sd; > > + struct v4l2_ctrl_handler hdl; > > + struct v4l2_ctrl *detect_tx_5v_ctrl; > > + struct v4l2_dv_timings timings; > > +}; > > + > > +static const char *get_state_name(enum dw_hdmi_state state) > > +{ > > + switch (state) { > > + case HDMI_STATE_NO_INIT: > > + return "NO_INIT"; > > + case HDMI_STATE_POWER_OFF: > > + return "POWER_OFF"; > > + case HDMI_STATE_POWER_UP: > > + return "POWER_UP"; > > + case HDMI_STATE_PHY_CONFIG: > > + return "PHY_CONFIG"; > > + case HDMI_STATE_HPD: > > + return "HPD"; > > + case HDMI_STATE_EQUALIZER: > > + return "EQUALIZER"; > > + case HDMI_STATE_DATAPATH: > > + return "DATAPATH"; > > + case HDMI_STATE_VIDEO_UNSTABLE: > > + return "VIDEO_UNSTABLE"; > > + case HDMI_STATE_AUDIO: > > + return "AUDIO"; > > + case HDMI_STATE_POWER_ON: > > + return "POWER_ON"; > > + default: > > + return "UNKNOWN"; > > + } > > +} > > + > > +static inline void dw_hdmi_set_state(struct dw_hdmi_dev *dw_dev, > > + enum dw_hdmi_state new_state) > > +{ > > + unsigned long flags; > > + > > + spin_lock_irqsave(&dw_dev->lock, flags); > > + dev_dbg(dw_dev->dev, "old_state=%s, new_state=%s\n", > > + get_state_name(dw_dev->state), > > + get_state_name(new_state)); > > + dw_dev->state = new_state; > > + spin_unlock_irqrestore(&dw_dev->lock, flags); > > +} > > + > > +static inline struct dw_hdmi_dev *to_dw_dev(struct v4l2_subdev *sd) > > +{ > > + return container_of(sd, struct dw_hdmi_dev, sd); > > +} > > + > > +static inline void hdmi_writel(struct dw_hdmi_dev *dw_dev, u32 val, int reg) > > +{ > > + writel(val, dw_dev->regs + reg); > > +} > > + > > +static inline u32 hdmi_readl(struct dw_hdmi_dev *dw_dev, int reg) > > +{ > > + return readl(dw_dev->regs + reg); > > +} > > + > > +static void hdmi_modl(struct dw_hdmi_dev *dw_dev, u32 data, u32 mask, int reg) > > +{ > > + u32 val = hdmi_readl(dw_dev, reg) & ~mask; > > + > > + val |= data & mask; > > + hdmi_writel(dw_dev, val, reg); > > +} > > + > > +static void hdmi_mask_writel(struct dw_hdmi_dev *dw_dev, u32 data, int reg, > > + u32 shift, u32 mask) > > +{ > > + hdmi_modl(dw_dev, data << shift, mask, reg); > > +} > > + > > +static u32 hdmi_mask_readl(struct dw_hdmi_dev *dw_dev, int reg, u32 shift, > > + u32 mask) > > +{ > > + return (hdmi_readl(dw_dev, reg) & mask) >> shift; > > +} > > + > > +static bool dw_hdmi_5v_status(struct dw_hdmi_dev *dw_dev, int input) > > +{ > > + void __iomem *arg = dw_dev->config->dw_5v_arg; > > + > > + if (dw_dev->config->dw_5v_status) > > + return dw_dev->config->dw_5v_status(arg, input); > > + > > + return false; > > +} > > + > > +static void dw_hdmi_5v_disable(struct dw_hdmi_dev *dw_dev, int input) > > +{ > > + void __iomem *arg = dw_dev->config->dw_5v_arg; > > + > > + if (!dw_dev->config->dw_5v_disable) > > + return; > > + > > + dw_dev->config->dw_5v_disable(arg, input); > > +} > > + > > +static void dw_hdmi_5v_enable(struct dw_hdmi_dev *dw_dev, int input) > > +{ > > + void __iomem *arg = dw_dev->config->dw_5v_arg; > > + > > + if (!dw_dev->config->dw_5v_enable) > > + return; > > + > > + dw_dev->config->dw_5v_enable(arg, input); > > +} > > + > > +static u32 dw_hdmi_edid_read(struct dw_hdmi_dev *dw_dev, int input, u32 offset) > > +{ > > + void __iomem *arg = dw_dev->config->dw_edid_arg; > > + > > + if (!dw_dev->config->dw_edid_read) > > + return 0x0; > > + > > + return dw_dev->config->dw_edid_read(arg, input, offset); > > +} > > + > > +static int dw_hdmi_edid_write(struct dw_hdmi_dev *dw_dev, int input, u32 *edid, > > + int size) > > +{ > > + void __iomem *arg = dw_dev->config->dw_edid_arg; > > + > > + if (!dw_dev->config->dw_edid_write) > > + return 0; > > + > > + return dw_dev->config->dw_edid_write(arg, input, edid, size); > > +} > > + > > +static u32 dw_hdmi_edid_4blocks_le(struct dw_hdmi_dev *dw_dev) > > +{ > > + void __iomem *arg = dw_dev->config->dw_edid_arg; > > + > > + if (!dw_dev->config->dw_edid_4blocks_le) > > + return 0x0; > > + > > + return dw_dev->config->dw_edid_4blocks_le(arg); > > +} > > + > > +static int dw_hdmi_update_edid(struct dw_hdmi_dev *dw_dev, int input, > > + u8 *edid, int size, u8 invert_bytes) > > +{ > > + unsigned int i, j; > > + u32 *w_edid_srt; > > + int ret; > > + > > + if (invert_bytes) { > > + /* invert the order of bytes to register 32bit */ > > + w_edid_srt = devm_kzalloc(dw_dev->dev, size, GFP_KERNEL); > > + if (!w_edid_srt) { > > + devm_kfree(dw_dev->dev, w_edid_srt); > > + return -ENOMEM; > > + } > > + > > + for (i = 0; i < size / sizeof(u32); i++) { > > + for (j = 0; j < 4; j++) { > > + w_edid_srt[i] |= > > + edid[i * 4 + j] << (8 * (3 - j)); > > + } > > + } > > + > > + ret = dw_hdmi_edid_write(dw_dev, input, (u32 *)w_edid_srt, > > + size / sizeof(u32)); > > + devm_kfree(dw_dev->dev, w_edid_srt); > > + } else { > > + /* no need to invert bytes */ > > + ret = dw_hdmi_edid_write(dw_dev, input, (u32 *)edid, > > + size / sizeof(u32)); > > + } > > + dw_dev->curr_edid_blocks[input] = size / 128; > > + > > + return ret; > > +} > > + > > +static void dw_hdmi_main_reset(struct dw_hdmi_dev *dw_dev) > > +{ > > + void __iomem *arg = dw_dev->config->dw_reset_arg; > > + > > + if (!dw_dev->config->dw_reset_all) > > + return; > > + > > + dev_dbg(dw_dev->dev, "%s: main reset\n", __func__); > > + > > + dw_dev->config->dw_reset_all(arg); > > +} > > + > > +static void dw_hdmi_disable_hpd(struct dw_hdmi_dev *dw_dev); > > + > > +static void dw_hdmi_reset(struct dw_hdmi_dev *dw_dev) > > +{ > > + dev_dbg(dw_dev->dev, "%s: reset\n", __func__); > > + > > + /* perform main reset */ > > + dw_hdmi_main_reset(dw_dev); > > + > > + dw_hdmi_disable_hpd(dw_dev); > > +} > > + > > +static inline bool is_off(struct dw_hdmi_dev *dw_dev) > > +{ > > + return dw_dev->state <= HDMI_STATE_POWER_OFF; > > +} > > + > > +static inline bool is_on(struct dw_hdmi_dev *dw_dev) > > +{ > > + return dw_dev->state == HDMI_STATE_POWER_ON; > > +} > > + > > +static bool has_signal(struct dw_hdmi_dev *dw_dev, unsigned int input) > > +{ > > + return dw_dev->input_connected[input]; > > +} > > + > > +static inline bool is_hdmi2(struct dw_hdmi_dev *dw_dev) > > +{ > > + return hdmi_readl(dw_dev, DW_HDMI_SCDC_REGS0) & > > + DW_HDMI_SCDC_TMDSBITCLKRATIO_MASK; > > +} > > + > > +static inline bool is_scrambled(struct dw_hdmi_dev *dw_dev) > > +{ > > + return hdmi_readl(dw_dev, DW_HDMI_SCDC_REGS0) & > > + DW_HDMI_SCDC_SCRAMBSTATUS_MASK; > > +} > > + > > +static void hdmi_phy_jtag_send_pulse(struct dw_hdmi_dev *dw_dev, u8 tms, u8 tdi) > > +{ > > + u8 val; > > + > > + val = tms ? DW_HDMI_JTAG_TMS : 0; > > + val |= tdi ? DW_HDMI_JTAG_TDI : 0; > > + > > + hdmi_writel(dw_dev, 0, DW_HDMI_JTAG_TAP_TCLK); > > + hdmi_writel(dw_dev, val, DW_HDMI_JTAG_TAP_IN); > > + hdmi_writel(dw_dev, 1, DW_HDMI_JTAG_TAP_TCLK); > > +} > > + > > +static void hdmi_phy_jtag_shift_dr(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_phy_jtag_send_pulse(dw_dev, 1, 0); > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, 0); > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, 0); > > +} > > + > > +static void hdmi_phy_jtag_shift_ir(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_phy_jtag_send_pulse(dw_dev, 1, 0); > > + hdmi_phy_jtag_send_pulse(dw_dev, 1, 0); > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, 0); > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, 0); > > +} > > + > > +static u16 hdmi_phy_jtag_send(struct dw_hdmi_dev *dw_dev, u8 cmd, u16 val) > > +{ > > + u32 in = (cmd << 16) | val; > > + u16 out = 0; > > + u8 i; > > + > > + for (i = 0; i < 16; i++) { > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, in & 0x1); > > + out |= (hdmi_readl(dw_dev, DW_HDMI_JTAG_TAP_OUT) & 0x1) << i; > > + in >>= 1; > > + } > > + > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, in & 0x1); > > + in >>= 1; > > + hdmi_phy_jtag_send_pulse(dw_dev, 1, in & 0x1); > > + > > + out |= (hdmi_readl(dw_dev, DW_HDMI_JTAG_TAP_OUT) & 0x1) << ++i; > > + return out; > > +} > > + > > +static void hdmi_phy_jtag_idle(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_phy_jtag_send_pulse(dw_dev, 1, 0); > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, 0); > > +} > > + > > +static void hdmi_phy_jtag_init(struct dw_hdmi_dev *dw_dev, u8 addr) > > +{ > > + u8 i; > > + > > + hdmi_writel(dw_dev, addr, DW_HDMI_JTAG_ADDR); > > + > > + /* reset */ > > + hdmi_writel(dw_dev, 0x10, DW_HDMI_JTAG_TAP_IN); > > + hdmi_writel(dw_dev, 0x0, DW_HDMI_JTAG_CONF); > > + hdmi_writel(dw_dev, 0x1, DW_HDMI_JTAG_CONF); > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, 0); > > + > > + /* soft reset */ > > + for (i = 0; i < 5; i++) > > + hdmi_phy_jtag_send_pulse(dw_dev, 1, 0); > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, 0); > > + > > + /* set slave address */ > > + hdmi_phy_jtag_shift_ir(dw_dev); > > + for (i = 0; i < 7; i++) { > > + hdmi_phy_jtag_send_pulse(dw_dev, 0, addr & 0x1); > > + addr >>= 1; > > + } > > + hdmi_phy_jtag_send_pulse(dw_dev, 1, addr & 0x1); > > + hdmi_phy_jtag_idle(dw_dev); > > +} > > + > > +static void hdmi_phy_jtag_write(struct dw_hdmi_dev *dw_dev, u16 val, u16 addr) > > +{ > > + hdmi_phy_jtag_shift_dr(dw_dev); > > + hdmi_phy_jtag_send(dw_dev, DW_HDMI_JTAG_TAP_ADDR_CMD, addr << 8); > > + hdmi_phy_jtag_idle(dw_dev); > > + hdmi_phy_jtag_shift_dr(dw_dev); > > + hdmi_phy_jtag_send(dw_dev, DW_HDMI_JTAG_TAP_WRITE_CMD, val); > > + hdmi_phy_jtag_idle(dw_dev); > > +} > > + > > +static u16 hdmi_phy_jtag_read(struct dw_hdmi_dev *dw_dev, u16 addr) > > +{ > > + u16 val; > > + > > + hdmi_phy_jtag_shift_dr(dw_dev); > > + hdmi_phy_jtag_send(dw_dev, DW_HDMI_JTAG_TAP_ADDR_CMD, addr << 8); > > + hdmi_phy_jtag_idle(dw_dev); > > + hdmi_phy_jtag_shift_dr(dw_dev); > > + val = hdmi_phy_jtag_send(dw_dev, DW_HDMI_JTAG_TAP_READ_CMD, 0xFFFF); > > + hdmi_phy_jtag_idle(dw_dev); > > + > > + return val; > > +} > > + > > +static void dw_hdmi_phy_write(void *arg, u16 val, u16 addr) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + u16 rval; > > + > > + hdmi_phy_jtag_init(dw_dev, dw_dev->config->phy->jtag_addr); > > + hdmi_phy_jtag_write(dw_dev, val, addr); > > + rval = hdmi_phy_jtag_read(dw_dev, addr); > > + > > + if (rval != val) { > > + dev_err(dw_dev->dev, > > + "JTAG read-back failed: expected=0x%x, got=0x%x\n", > > + val, rval); > > + } > > +} > > + > > +static u16 dw_hdmi_phy_read(void *arg, u16 addr) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + > > + hdmi_phy_jtag_init(dw_dev, dw_dev->config->phy->jtag_addr); > > + return hdmi_phy_jtag_read(dw_dev, addr); > > +} > > + > > +static void dw_hdmi_phy_reset(void *arg, int enable) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + > > + hdmi_mask_writel(dw_dev, enable, DW_HDMI_PHY_CTRL, > > + DW_HDMI_PHYRESET_OFFSET, > > + DW_HDMI_PHYRESET_MASK); > > +} > > + > > +static void dw_hdmi_phy_pddq(void *arg, int enable) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + > > + hdmi_mask_writel(dw_dev, enable, DW_HDMI_PHY_CTRL, > > + DW_HDMI_PHYPDDQ_OFFSET, > > + DW_HDMI_PHYPDDQ_MASK); > > +} > > + > > +static void dw_hdmi_phy_svsmode(void *arg, int enable) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + > > + hdmi_mask_writel(dw_dev, enable, DW_HDMI_PHY_CTRL, > > + DW_HDMI_PHYSVSRETMODEZ_OFFSET, > > + DW_HDMI_PHYSVSRETMODEZ_MASK); > > +} > > + > > +static void dw_hdmi_zcal_reset(void *arg) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + > > + if (dw_dev->config->dw_zcal_reset) > > + dw_dev->config->dw_zcal_reset(dw_dev->config->dw_zcal_arg); > > +} > > + > > +static bool dw_hdmi_zcal_done(void *arg) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + void __iomem *zcal_arg = dw_dev->config->dw_zcal_arg; > > + > > + if (dw_dev->config->dw_zcal_done) > > + return dw_dev->config->dw_zcal_done(zcal_arg); > > + > > + return false; > > +} > > + > > +static bool dw_hdmi_tmds_valid(void *arg) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + > > + return (hdmi_readl(dw_dev, DW_HDMI_PLL_LCK_STS) & DW_HDMI_PLL_LOCKED); > > +} > > + > > +static bool dw_hdmi_audio_valid(void *arg) > > +{ > > + struct dw_hdmi_dev *dw_dev = arg; > > + > > + return ((hdmi_readl(dw_dev, DW_HDMI_AUD_PLL_CTRL) & > > + DW_HDMI_PLL_LOCK_STABLE_MASK) != 0); > > +} > > + > > +static const struct dw_phy_funcs dw_hdmi_phy_funcs = { > > + .write = dw_hdmi_phy_write, > > + .read = dw_hdmi_phy_read, > > + .reset = dw_hdmi_phy_reset, > > + .pddq = dw_hdmi_phy_pddq, > > + .svsmode = dw_hdmi_phy_svsmode, > > + .zcal_reset = dw_hdmi_zcal_reset, > > + .zcal_done = dw_hdmi_zcal_done, > > + .tmds_valid = dw_hdmi_tmds_valid, > > +}; > > + > > +static const struct of_device_id dw_hdmi_supported_phys[] = { > > + { .compatible = "snps,dw-hdmi-phy-e405", .data = DW_PHY_E40X_DRVNAME, }, > > + { .compatible = "snps,dw-hdmi-phy-e406", .data = DW_PHY_E40X_DRVNAME, }, > > + { }, > > +}; > > + > > +static struct device_node * > > +dw_hdmi_get_phy_of_node(struct dw_hdmi_dev *dw_dev, > > + const struct of_device_id **found_id) > > +{ > > + const struct of_device_id *id = NULL; > > + struct device_node *np = NULL; > > + > > + np = of_find_matching_node_and_match(NULL, dw_hdmi_supported_phys, &id); > > + > > + if (!id) > > + return NULL; > > + > > + if (found_id) > > + *found_id = id; > > + > > + return np; > > +} > > + > > +static bool dw_hdmi_has_dt(struct dw_hdmi_dev *dw_dev) > > +{ > > + const struct of_device_id *of_id; > > + > > + if (!dw_dev->of_node || > > + !dw_hdmi_get_phy_of_node(dw_dev, &of_id) || > > + !of_id || !of_id->data) { > > + return false; > > + } > > + > > + return true; > > +} > > + > > +static const struct dw_hdmi_phy_cfg { > > + const char *drvname; > > + const struct dw_phy_funcs *funcs; > > + int (*extra_init)(struct dw_hdmi_dev *dw_dev); > > +} dw_hdmi_phys[] = { > > + { > > + .drvname = DW_PHY_E40X_DRVNAME, > > + .funcs = &dw_hdmi_phy_funcs, > > + }, > > +}; > > + > > +static int dw_hdmi_phy_init_dt(struct dw_hdmi_dev *dw_dev) > > +{ > > + const struct dw_hdmi_phy_cfg *phy_config = NULL; > > + struct dw_phy_pdata *phy = &dw_dev->phy_config; > > + struct of_dev_auxdata lookup = { }; > > + const struct of_device_id *of_id; > > + struct device_node *child; > > + const char *drvname; > > + unsigned int i; > > + int ret; > > + > > + child = dw_hdmi_get_phy_of_node(dw_dev, &of_id); > > + if (!child || !of_id || !of_id->data) { > > + dev_err(dw_dev->dev, "no supported PHY found in DT\n"); > > + return -EINVAL; > > + } > > + > > + drvname = of_id->data; > > + for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); i++) { > > + if (!strcmp(dw_hdmi_phys[i].drvname, drvname)) > > + phy_config = &dw_hdmi_phys[i]; > > + } > > + > > + if (!phy_config) { > > + dev_err(dw_dev->dev, "failed to find PHY configuration\n"); > > + return -EINVAL; > > + } > > + > > + if (phy_config->extra_init) { > > + ret = phy_config->extra_init(dw_dev); > > + if (ret) > > + return ret; > > + } > > + > > + phy->funcs = phy_config->funcs; > > + phy->funcs_arg = dw_dev; > > + > > + lookup.compatible = (char *)of_id->compatible; > > + lookup.platform_data = phy; > > + > > + request_module(drvname); > > + > > + ret = of_platform_populate(dw_dev->of_node, NULL, &lookup, dw_dev->dev); > > + if (ret) { > > + dev_err(dw_dev->dev, "failed to populate PHY driver\n"); > > + return ret; > > + } > > + > > + return 0; > > +} > > + > > +static int dw_hdmi_phy_init_pd(struct dw_hdmi_dev *dw_dev) > > +{ > > + const char *drv_name = dw_dev->config->phy->drv_name; > > + const struct dw_hdmi_phy_cfg *phy_config = NULL; > > + struct platform_device_info pdevinfo; > > + unsigned int i; > > + int ret; > > + > > + memset(&pdevinfo, 0, sizeof(pdevinfo)); > > + > > + for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); i++) { > > + if (!strcmp(dw_hdmi_phys[i].drvname, drv_name)) > > + phy_config = &dw_hdmi_phys[i]; > > + } > > + > > + if (!phy_config) { > > + dev_err(dw_dev->dev, "failed to find PHY configuration\n"); > > + return -EINVAL; > > + } > > + > > + if (phy_config->extra_init) { > > + ret = phy_config->extra_init(dw_dev); > > + if (ret) > > + return ret; > > + } > > + > > + dw_dev->phy_config.version = dw_dev->config->phy->version; > > + dw_dev->phy_config.cfg_clk = dw_dev->config->phy->cfg_clk; > > + dw_dev->phy_config.funcs = phy_config->funcs; > > + dw_dev->phy_config.funcs_arg = dw_dev; > > + > > + pdevinfo.parent = dw_dev->dev; > > + pdevinfo.id = PLATFORM_DEVID_NONE; > > + pdevinfo.name = drv_name; > > + pdevinfo.data = &dw_dev->phy_config; > > + pdevinfo.size_data = sizeof(dw_dev->phy_config); > > + pdevinfo.dma_mask = DMA_BIT_MASK(32); > > + > > + request_module(pdevinfo.name); > > + > > + dw_dev->phy_pdev = platform_device_register_full(&pdevinfo); > > + if (IS_ERR(dw_dev->phy_pdev)) { > > + dev_err(dw_dev->dev, "failed to register PHY device\n"); > > + return PTR_ERR(dw_dev->phy_pdev); > > + } > > + > > + return 0; > > +} > > + > > +static int dw_hdmi_phy_init(struct dw_hdmi_dev *dw_dev) > > +{ > > + int ret; > > + > > + if (dw_hdmi_has_dt(dw_dev)) { > > + /* init PHY based on device tree */ > > + ret = dw_hdmi_phy_init_dt(dw_dev); > > + } else { > > + /* init PHY based on platform device */ > > + ret = dw_hdmi_phy_init_pd(dw_dev); > > + } > > + > > + /* get the HDMI PHY reference */ > > + dw_dev->phy = devm_phy_get(dw_dev->dev, "hdmi-phy"); > > + if (IS_ERR(dw_dev->phy)) { > > + if (PTR_ERR(dw_dev->phy) != -EPROBE_DEFER) > > + dev_err(dw_dev->dev, "Couldn't get the HDMI PHY\n"); > > + return PTR_ERR(dw_dev->phy); > > + } > > + > > + return ret; > > +} > > + > > +static void dw_hdmi_phy_exit(struct dw_hdmi_dev *dw_dev) > > +{ > > + if (dw_hdmi_has_dt(dw_dev)) { > > + /* exit PHY based on device tree */ > > + of_platform_depopulate(dw_dev->dev); > > + } else { > > + /* exit PHY based on platform device */ > > + if (!IS_ERR(dw_dev->phy_pdev)) > > + platform_device_unregister(dw_dev->phy_pdev); > > + } > > +} > > + > > +static int dw_hdmi_phy_eq_init(struct dw_hdmi_dev *dw_dev, u16 acq, bool force) > > +{ > > + union phy_configure_opts opts; > > + struct phy_configure_opts_hdmi *hdmi_opts = &opts.hdmi; > > + int ret = 0; > > + > > + /* load the required options for calibration */ > > + hdmi_opts->calibration_acq = acq; > > + hdmi_opts->calibration_force = force; > > + /* to avoid other reconfigurations when is to calibrate */ > > + hdmi_opts->set_color_depth = 0; > > + hdmi_opts->set_tmds_bit_clock_ratio = 0; > > + hdmi_opts->set_scrambling = 0; > > + > > + /* set PHY configuration */ > > + ret = phy_configure(dw_dev->phy, &opts); > > + if (ret) { > > + dev_err(dw_dev->dev, "%s: Couldn't PHY configure (err: %d)\n", > > + __func__, ret); > > + goto err; > > + } > > + > > + /* call PHY calibrate */ > > + ret = phy_calibrate(dw_dev->phy); > > + if (ret) { > > + dev_err(dw_dev->dev, "%s: Couldn't PHY calibrate (err: %d)\n", > > + __func__, ret); > > + goto err; > > + } > > + > > +err: > > + return ret; > > +} > > + > > +static int dw_hdmi_phy_config(struct dw_hdmi_dev *dw_dev, > > + unsigned char color_depth, bool hdmi2, > > + bool scrambling) > > +{ > > + union phy_configure_opts opts; > > + struct phy_configure_opts_hdmi *hdmi_opts = &opts.hdmi; > > + int ret = 0; > > + > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_CBUSIOCTRL, > > + DW_HDMI_DATAPATH_CBUSZ_OFFSET, > > + DW_HDMI_DATAPATH_CBUSZ_MASK); > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_CBUSIOCTRL, > > + DW_HDMI_CBUS_SVSRETMODEZ_OFFSET, > > + DW_HDMI_CBUS_SVSRETMODEZ_MASK); > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_CBUSIOCTRL, > > + DW_HDMI_CBUS_PDDQ_OFFSET, > > + DW_HDMI_CBUS_PDDQ_MASK); > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_CBUSIOCTRL, > > + DW_HDMI_CBUS_RESET_OFFSET, > > + DW_HDMI_CBUS_RESET_MASK); > > + > > + /* load the required options for power on */ > > + hdmi_opts->color_depth = color_depth; > > + hdmi_opts->tmds_bit_clock_ratio = hdmi2; > > + hdmi_opts->scrambling = scrambling; > > + /* to avoid color depth reconfiguration before the power on */ > > + hdmi_opts->set_color_depth = 0; > > + > > + /* set PHY configuration */ > > + ret = phy_configure(dw_dev->phy, &opts); > > + if (ret) { > > + dev_err(dw_dev->dev, "%s: Couldn't PHY configure (err: %d)\n", > > + __func__, ret); > > + goto err; > > + } > > + > > + /* call PHY power off if needed */ > > + if (dw_dev->phy->power_count > 0) { > > + ret = phy_power_off(dw_dev->phy); > > + if (ret) { > > + dev_err(dw_dev->dev, "%s: Couldn't PHY power off (err: %d)\n", > > + __func__, ret); > > + } > > + } > > + > > + /* call PHY power on */ > > + ret = phy_power_on(dw_dev->phy); > > + if (ret) { > > + dev_err(dw_dev->dev, "%s: Couldn't PHY power on (err: %d)\n", > > + __func__, ret); > > + goto err; > > + } > > + > > +err: > > + return ret; > > +} > > + > > +static int dw_hdmi_phy_set_color_depth(struct dw_hdmi_dev *dw_dev, > > + u8 color_depth) > > +{ > > + union phy_configure_opts opts; > > + struct phy_configure_opts_hdmi *hdmi_opts = &opts.hdmi; > > + int ret = 0; > > + > > + /* load the required options for color depth reconfiguration */ > > + hdmi_opts->color_depth = color_depth; > > + /* to avoid other reconfigurations when is to set only color depth */ > > + hdmi_opts->set_tmds_bit_clock_ratio = 0; > > + hdmi_opts->set_scrambling = 0; > > + > > + /* set PHY configuration */ > > + ret = phy_configure(dw_dev->phy, &opts); > > + if (ret) { > > + dev_err(dw_dev->dev, "%s: Couldn't PHY configure (err: %d)\n", > > + __func__, ret); > > + goto err; > > + } > > + > > +err: > > + return ret; > > +} > > + > > +static void dw_hdmi_event_source_change(struct dw_hdmi_dev *dw_dev) > > +{ > > + if (dw_dev->registered) > > + v4l2_subdev_notify_event(&dw_dev->sd, &dw_hdmi_event_fmt); > > +} > > + > > +static int dw_hdmi_reset_ceavid(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_CEAVID_CONFIG, > > + DW_HDMI_CEAVID_RST_OFFSET, > > + DW_HDMI_CEAVID_RST_MASK); > > + msleep(100); > > + hdmi_mask_writel(dw_dev, 0x0, DW_HDMI_CEAVID_CONFIG, > > + DW_HDMI_CEAVID_RST_OFFSET, > > + DW_HDMI_CEAVID_RST_MASK); > > + > > + return 0; > > +} > > + > > +static void dw_hdmi_update_avmute(struct dw_hdmi_dev *dw_dev, u32 mbus_code) > > +{ > > + u32 val_l = 0x0, val_h = 0x0; > > + > > + switch (mbus_code) { > > + case MEDIA_BUS_FMT_YUYV8_1X16: /* YCbCr 4:2:2 */ > > + val_h = 0x00008000; > > + val_l = 0x00008000; > > + break; > > + case MEDIA_BUS_FMT_YUYV12_1X24: /* YCbCr 4:4:4 */ > > + val_h = 0x00008000; > > + val_l = 0x00008000; > > + break; > > + case MEDIA_BUS_FMT_YVYU8_1X16: /* YCbCr 4:2:0 */ > > + val_h = 0x00000f00; > > + val_l = 0x0f007f00; > > + break; > > + case MEDIA_BUS_FMT_RGB888_2X12_BE: /* RGB */ > > + default: > > + break; > > + } > > + > > + hdmi_writel(dw_dev, val_l, DW_HDMI_VM_CFG_CH_0_1); > > + hdmi_mask_writel(dw_dev, val_h, DW_HDMI_VM_CFG_CH2, > > + DW_HDMI_VM_CH2_COL_VALUE_OFFSET, > > + DW_HDMI_VM_CH2_COL_VALUE_MASK); > > +} > > + > > +static u32 dw_hdmi_get_mbus_code(struct dw_hdmi_dev *dw_dev) > > +{ > > + enum hdmi_colorspace cs = dw_dev->aviif.avi.colorspace; > > + > > + switch (cs) { > > + case HDMI_COLORSPACE_RGB: /* RGB */ > > + return MEDIA_BUS_FMT_RGB888_2X12_BE; > > + case HDMI_COLORSPACE_YUV422: /* YCbCr 4:2:2 */ > > + return MEDIA_BUS_FMT_YUYV8_1X16; > > + case HDMI_COLORSPACE_YUV444: /* YCbCr 4:4:4 */ > > + return MEDIA_BUS_FMT_YUYV12_1X24; > > + case HDMI_COLORSPACE_YUV420: /* YCbCr 4:2:0 */ > > + return MEDIA_BUS_FMT_YVYU8_1X16; > > + default: > > + return MEDIA_BUS_FMT_RGB888_2X12_BE; > > + } > > +} > > + > > +static u8 dw_hdmi_infoframe_checksum(u8 *ptr, size_t size) > > +{ > > + u8 csum = 0; > > + size_t i; > > + > > + for (i = 0; i < size; i++) > > + csum += ptr[i]; > > + return 256 - csum; > > +} > > + > > +static void dw_hdmi_get_raw_infoframe_avi(struct dw_hdmi_dev *dw_dev, > > + struct dw_hdmi_infoframe_cfg *fcfg) > > +{ > > + u32 pb, ph = hdmi_readl(dw_dev, fcfg->header_addr); > > + union hdmi_infoframe *frame = fcfg->frame; > > + u8 packet_hlen = sizeof(fcfg->header); > > + u8 packet_len = (ph >> 8) & 0xff; > > + unsigned int i, j, pos = 0; > > + u8 packet[35] = {0}; > > + > > + dev_dbg(dw_dev->dev, "%s[%s]: packet_header=0x%x\n", __func__, > > + fcfg->desc, ph); > > + > > + if (!ph) /* Fail silently if there is no packet */ > > + return; > > + > > + if ((packet_len + packet_hlen + 1) > sizeof(packet)) { > > + dev_dbg(dw_dev->dev, "%s: invalid length\n", __func__); > > + goto out; > > + } > > + > > + memcpy(packet, fcfg->header, packet_hlen); > > + packet[2] = packet_len; /* Replace fake header size by real header */ > > + pos += packet_hlen + 1; > > + > > + for (i = 0; i < fcfg->payload_len; i++) { > > + j = 0; > > + > > + /* specific for AVI */ > > + if (fcfg->header[0] == HDMI_INFOFRAME_TYPE_AVI && i == 1) { > > + /* read YQx, CNx, PRx from AVI PH register */ > > + u32 offset = DW_HDMI_PIX_REP_FACTOR_OFFSET; > > + u32 mask = DW_HDMI_QUANT_RANGE_MASK | > > + DW_HDMI_CONTENT_TYPE_MASK | > > + DW_HDMI_PIX_REP_FACTOR_MASK; > > + > > + pb = hdmi_mask_readl(dw_dev, fcfg->header_addr, offset, > > + mask); > > + packet[pos++] = pb & 0xff; > > + j = 1; > > + } > > + > > + /* the registers are aligned with the standard raw packet */ > > + pb = hdmi_readl(dw_dev, fcfg->payload_addr + 4 * i); > > + for (; j < 4; j++) { > > + if (pos >= (packet_len + packet_hlen + 1)) > > + break; > > + packet[pos++] = (pb >> (8 * j)) & 0xff; > > + } > > + } > > + > > + packet[3] = dw_hdmi_infoframe_checksum(packet, packet_len + > > + packet_hlen + 1); > > + > > + /*print all packet bytes */ > > + for (j = 0; j < pos; j++) { > > + dev_dbg(dw_dev->dev, "%s[%s]: packet=0x%x\n", __func__, > > + fcfg->desc, packet[j]); > > + } > > + > > + if (hdmi_infoframe_unpack(frame, packet, sizeof(packet))) { > > + dev_dbg(dw_dev->dev, "%s[%s]: failed to unpack\n", > > + __func__, fcfg->desc); > > + goto out; > > + } > > + > > + return; > > +out: > > + dev_err(dw_dev->dev, "[INVALID INFOFRAME]\n"); > > +} > > + > > +static void dw_hdmi_get_raw_infoframe_aud(struct dw_hdmi_dev *dw_dev, > > + struct dw_hdmi_infoframe_cfg *fcfg) > > +{ > > + u32 pb, ph = hdmi_readl(dw_dev, fcfg->header_addr); > > + union hdmi_infoframe *frame = fcfg->frame; > > + u8 packet_hlen = sizeof(fcfg->header); > > + u8 packet_len = (ph >> 8) & 0xff; > > + unsigned int i, j, pos = 0; > > + u8 packet[35] = {0}; > > + > > + dev_dbg(dw_dev->dev, "%s[%s]: packet_header=0x%x\n", __func__, > > + fcfg->desc, ph); > > + > > + if (!ph) /* Fail silently if there is no packet */ > > + return; > > + > > + if ((packet_len + packet_hlen + 1) > sizeof(packet)) { > > + dev_dbg(dw_dev->dev, "%s: invalid length\n", __func__); > > + goto out; > > + } > > + > > + memcpy(packet, fcfg->header, packet_hlen); > > + packet[2] = packet_len; /* Replace fake header size by real header */ > > + pos += packet_hlen + 1; > > + > > + for (i = 0; i < fcfg->payload_len; i++) { > > + j = 0; > > + > > + /* specific for audio */ > > + if (fcfg->header[0] == HDMI_INFOFRAME_TYPE_AUDIO && i == 1) { > > + pb = hdmi_readl(dw_dev, fcfg->payload_addr + 4 * i); > > + packet[pos++] = (pb | > > + ((pb & DW_HDMI_LFE_PLAYBACK_LEVEL_MASK) > > + >> DW_HDMI_LFE_PLAYBACK_LEVEL_OFFSET)) & > > + 0xff; > > + break; > > + } > > + > > + /* the registers are aligned with the standard raw packet */ > > + pb = hdmi_readl(dw_dev, fcfg->payload_addr + 4 * i); > > + for (; j < 4; j++) { > > + if (pos >= (packet_len + packet_hlen + 1)) > > + break; > > + packet[pos++] = (pb >> (8 * j)) & 0xff; > > + } > > + } > > + > > + packet[3] = dw_hdmi_infoframe_checksum(packet, packet_len + > > + packet_hlen + 1); > > + > > + /*print all packet bytes */ > > + for (j = 0; j < pos; j++) { > > + dev_dbg(dw_dev->dev, "%s[%s]: packet=0x%x\n", __func__, > > + fcfg->desc, packet[j]); > > + } > > + > > + if (hdmi_infoframe_unpack(frame, packet, sizeof(packet))) { > > + dev_dbg(dw_dev->dev, "%s[%s]: failed to unpack\n", > > + __func__, fcfg->desc); > > + goto out; > > + } > > + > > + return; > > +out: > > + dev_err(dw_dev->dev, "[INVALID INFOFRAME]\n"); > > +} > > + > > +static void dw_hdmi_get_raw_infoframe_vs(struct dw_hdmi_dev *dw_dev, > > + struct dw_hdmi_infoframe_cfg *fcfg) > > +{ > > + u32 pb, ph = hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_VSI_ST1, > > + DW_HDMI_LENGTH_OFFSET, > > + DW_HDMI_LENGTH_MASK); > > + union hdmi_infoframe *frame = fcfg->frame; > > + u8 packet_hlen = sizeof(fcfg->header); > > + unsigned int j, pos = 0; > > + u8 packet[35] = {0}; > > + u8 packet_len = ph; > > + > > + dev_dbg(dw_dev->dev, "%s[%s]: packet_header=0x%x\n", __func__, > > + fcfg->desc, ph); > > + > > + if (!ph) /* Fail silently if there is no packet */ > > + return; > > + > > + if ((packet_len + packet_hlen + 1) > sizeof(packet)) { > > + dev_dbg(dw_dev->dev, "%s: invalid length\n", __func__); > > + goto out; > > + } > > + > > + memcpy(packet, fcfg->header, packet_hlen); > > + packet[2] = packet_len; /* Replace fake header size by real header */ > > + pos += packet_hlen + 1; > > + > > + /* 24bit IEEE Registration identifier */ > > + pb = hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_VSI_ST0, > > + DW_HDMI_IEEE_REG_ID_OFFSET, > > + DW_HDMI_IEEE_REG_ID_MASK); > > + for (j = 0; j < 3; j++) { > > + if (pos >= (packet_len + packet_hlen + 1)) > > + break; > > + packet[pos++] = (pb >> (8 * j)) & 0xff; > > + } > > + > > + /* HDMI video format */ > > + pb = hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_VSI_ST1, > > + DW_HDMI_HDMI_VIDEO_FORMAT_OFFSET, > > + DW_HDMI_HDMI_VIDEO_FORMAT_MASK); > > + packet[pos++] = (pb << DW_HDMI_HDMI_VIDEO_FORMAT_OFFSET) & > > + 0xff; > > + > > + /* HDMI vic */ > > + pb = hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_VSI_ST1, > > + DW_HDMI_HDMI_VIC_OFFSET, > > + DW_HDMI_HDMI_VIC_MASK); > > + packet[pos++] = pb & 0xff; > > + > > + /* 3d structure */ > > + pb = hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_VSI_ST1, > > + DW_HDMI_H3D_STRUCTURE_OFFSET, > > + DW_HDMI_H3D_STRUCTURE_MASK); > > + packet[pos++] = (pb << 4) & 0xff; > > + > > + /* 3d ext data */ > > + pb = hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_VSI_ST1, > > + DW_HDMI_H3D_EXT_DATA_OFFSET, > > + DW_HDMI_H3D_EXT_DATA_MASK); > > + packet[pos++] = (pb << 4) & 0xff; > > + > > + packet[3] = dw_hdmi_infoframe_checksum(packet, packet_len + > > + packet_hlen + 1); > > + > > + /*print all packet bytes */ > > + for (j = 0; j < pos; j++) { > > + dev_dbg(dw_dev->dev, "%s[%s]: packet=0x%x\n", __func__, > > + fcfg->desc, packet[j]); > > + } > > + > > + if (hdmi_infoframe_unpack(frame, packet, sizeof(packet))) { > > + dev_dbg(dw_dev->dev, "%s[%s]: failed to unpack\n", > > + __func__, fcfg->desc); > > + goto out; > > + } > > + > > + return; > > +out: > > + dev_err(dw_dev->dev, "[INVALID INFOFRAME]\n"); > > +} > > + > > +static void dw_hdmi_get_raw_infoframe(struct dw_hdmi_dev *dw_dev, > > + struct dw_hdmi_infoframe_cfg *fcfg) > > +{ > > + switch (fcfg->header[0]) { > > + case HDMI_INFOFRAME_TYPE_AVI: > > + dw_hdmi_get_raw_infoframe_avi(dw_dev, fcfg); > > + break; > > + case HDMI_INFOFRAME_TYPE_SPD: > > + dev_dbg(dw_dev->dev, > > + "%s[%s]: not processed infoframe packet type %d\n", > > + __func__, fcfg->desc, fcfg->header[0]); > > + break; > > + case HDMI_INFOFRAME_TYPE_AUDIO: > > + dw_hdmi_get_raw_infoframe_aud(dw_dev, fcfg); > > + break; > > + case HDMI_INFOFRAME_TYPE_VENDOR: > > + dw_hdmi_get_raw_infoframe_vs(dw_dev, fcfg); > > + break; > > + case HDMI_INFOFRAME_TYPE_DRM: > > + dev_dbg(dw_dev->dev, > > + "%s[%s]: not processed infoframe packet type %d\n", > > + __func__, fcfg->desc, fcfg->header[0]); > > + break; > > + default: > > + dev_dbg(dw_dev->dev, > > + "%s[%s]: invalid infoframe packet type %d\n", > > + __func__, fcfg->desc, fcfg->header[0]); > > + break; > > + } > > +} > > + > > +/* Forward declaration needed because of color encoding change */ > > +static void dw_hdmi_power_off(struct dw_hdmi_dev *dw_dev); > > +static void dw_hdmi_controller_power_off(struct dw_hdmi_dev *dw_dev); > > +static int dw_hdmi_power_on(struct dw_hdmi_dev *dw_dev, unsigned int input); > > + > > +static void dw_hdmi_get_infoframes(struct dw_hdmi_dev *dw_dev) > > +{ > > + struct dw_hdmi_infoframe_cfg ifs[] = { > > + { > > + .desc = "AVI", > > + .header = {HDMI_INFOFRAME_TYPE_AVI, 2, > > + HDMI_AVI_INFOFRAME_SIZE}, > > + .header_addr = DW_HDMI_PDEC_AVI_HB, > > + .payload_addr = DW_HDMI_PDEC_AVI_PB, > > + .payload_len = DW_HDMI_PDEC_AVI_PBLEN, > > + .frame = &dw_dev->aviif, > > + .frame_size = sizeof(dw_dev->aviif), > > + }, { > > + .desc = "Audio", > > + .header = {HDMI_INFOFRAME_TYPE_AUDIO, 1, > > + HDMI_AUDIO_INFOFRAME_SIZE}, > > + .header_addr = DW_HDMI_PDEC_AIF_HB, > > + .payload_addr = DW_HDMI_PDEC_AIF_PB0, > > + .payload_len = DW_HDMI_PDEC_AIF_PBLEN, > > + .frame = &dw_dev->audioif, > > + .frame_size = sizeof(dw_dev->audioif), > > + }, { > > + .desc = "Vendor Specific", > > + .header = {HDMI_INFOFRAME_TYPE_VENDOR, 1, > > + HDMI_VENDOR_INFOFRAME_SIZE}, > > + .frame = &dw_dev->vsif, > > + .frame_size = sizeof(dw_dev->vsif), > > + }, > > + }; > > + union hdmi_vendor_any_infoframe *vendor; > > + struct hdmi_avi_infoframe *avi; > > + unsigned int i; > > + u32 old_mbus; > > + > > + for (i = 0; i < ARRAY_SIZE(ifs); i++) { > > + memset(ifs[i].frame, 0, ifs[i].frame_size); > > + dw_hdmi_get_raw_infoframe(dw_dev, &ifs[i]); > > + } > > + > > + /* Update color space */ > > + old_mbus = dw_dev->mbus_code; > > + dw_dev->mbus_code = dw_hdmi_get_mbus_code(dw_dev); > > + if (dw_dev->mbus_code != old_mbus && is_on(dw_dev)) { > > + dw_hdmi_power_off(dw_dev); > > + if (has_signal(dw_dev, dw_dev->configured_input)) > > + dw_hdmi_power_on(dw_dev, dw_dev->configured_input); > > + } > > + > > + /* Update AVMute value */ > > + dw_hdmi_update_avmute(dw_dev, dw_dev->mbus_code); > > + > > + vendor = &dw_dev->vsif.vendor; > > + avi = &dw_dev->aviif.avi; > > + > > + /* > > + * Update current VIC: When transmitting any extended video format > > + * indicated through use of the HDMI_VIC field in the HDMI Vendor > > + * Specific InfoFrame or any other format which is not described in > > + * the above cases, an HDMI Source shall set the AVI InfoFrame VIC > > + * field to zero. > > + */ > > + if (vendor->hdmi.vic && !avi->video_code) { > > + dw_dev->current_vic = vendor->hdmi.vic; > > + dw_dev->current_vic_is_4k = true; > > + } else { > > + dw_dev->current_vic = avi->video_code; > > + dw_dev->current_vic_is_4k = false; > > + } > > +} > > + > > +static int dw_hdmi_wait_phy_lock_poll(struct dw_hdmi_dev *dw_dev) > > +{ > > + int timeout = dw_dev->tmds_valid_wait_count; > > + > > + while (!dw_hdmi_tmds_valid(dw_dev) && timeout-- && !dw_dev->force_off) > > + usleep_range(5000, 10000); > > + > > + if (!dw_hdmi_tmds_valid(dw_dev)) > > + return -ETIMEDOUT; > > + > > + return 0; > > +} > > + > > +static void dw_hdmi_reset_datapath(struct dw_hdmi_dev *dw_dev) > > +{ > > + u32 val = DW_HDMI_TMDS_SWRESET | > > + DW_HDMI_HDCP_SWRESET | > > + DW_HDMI_VID_SWRESET | > > + DW_HDMI_PIXEL_SWRESET | > > + DW_HDMI_CEC_SWRESET | > > + DW_HDMI_AUD_SWRESET | > > + DW_HDMI_BUS_SWRESET | > > + DW_HDMI_HDMI_SWRESET | > > + DW_HDMI_MODET_SWRESET; > > + > > + hdmi_writel(dw_dev, val & dw_dev->reset_datapath_enable, > > + DW_HDMI_DMI_SW_RST); > > +} > > + > > +static void dw_hdmi_reset_audio(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_writel(dw_dev, DW_HDMI_AUD_SWRESET, DW_HDMI_DMI_SW_RST); > > +} > > + > > +static void dw_hdmi_restart_audio_fifo(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_AUD_FIFO_CTRL, > > + DW_HDMI_AFIF_INIT_OFFSET, > > + DW_HDMI_AFIF_INIT_MASK); > > + hdmi_mask_writel(dw_dev, 0x0, DW_HDMI_AUD_FIFO_CTRL, > > + DW_HDMI_AFIF_INIT_OFFSET, > > + DW_HDMI_AFIF_INIT_MASK); > > +} > > + > > +static int dw_hdmi_wait_audio_lock_poll(struct dw_hdmi_dev *dw_dev) > > +{ > > + int timeout = 10; > > + > > + while (!dw_hdmi_audio_valid(dw_dev) && timeout-- && !dw_dev->force_off) > > + usleep_range(5000, 10000); > > + > > + if (!dw_hdmi_audio_valid(dw_dev)) > > + return -ETIMEDOUT; > > + > > + return 0; > > +} > > + > > +static int dw_hdmi_config_audio(struct dw_hdmi_dev *dw_dev) > > +{ > > + int ret; > > + > > + ret = dw_hdmi_wait_audio_lock_poll(dw_dev); > > + if (ret) { > > + dev_err(dw_dev->dev, "failed to wait for audio pll lock\n"); > > + return ret; > > + } > > + > > + /* trigger offset for N and CTS interrupts */ > > + hdmi_mask_writel(dw_dev, 0x05, DW_HDMI_PDEC_ACRM_CTRL, > > + DW_HDMI_DELTACTS_IRQTRIG_OFFSET, > > + DW_HDMI_DELTACTS_IRQTRIG_MASK); > > + > > + /* Config */ > > + hdmi_mask_writel(dw_dev, 0x01, DW_HDMI_AUD_MUTE_CTRL, > > + DW_HDMI_AUD_MUTE_SEL_OFFSET, > > + DW_HDMI_AUD_MUTE_SEL_MASK); > > + > > + /* enable all outputs and select 16-bit for I2S */ > > + hdmi_writel(dw_dev, 0x00, DW_HDMI_AUD_SAO_CTRL); > > + > > + /* Start */ > > + dw_hdmi_restart_audio_fifo(dw_dev); > > + return 0; > > +} > > + > > +static void dw_hdmi_config_packet(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_PDEC_CTRL, > > + DW_HDMI_PFIFO_STORE_FILTER_EN_OFFSET, > > + DW_HDMI_PFIFO_STORE_FILTER_EN_MASK); > > + > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_PDEC_ASP_CTRL, > > + DW_HDMI_AUTO_VMUTE_OFFSET, > > + DW_HDMI_AUTO_VMUTE_MASK); > > + > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_PDEC_ASP_CTRL, > > + DW_HDMI_AUTO_SPFLAT_MUTE_OFFSET, > > + DW_HDMI_AUTO_SPFLAT_MUTE_MASK); > > + > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_PDEC_CTRL, > > + DW_HDMI_PD_FIFO_CLR_OFFSET, > > + DW_HDMI_PD_FIFO_CLR_MASK); > > +} > > + > > +static void dw_hdmi_wait_video_stable(struct dw_hdmi_dev *dw_dev) > > +{ > > + /* > > + * Empiric value. Video should be stable way longer before the > > + * end of this sleep time. Though, we can have some video change > > + * interrupts before the video is stable so filter them by sleeping. > > + */ > > + msleep(dw_dev->video_stable_wait_ms); > > +} > > + > > +static void dw_hdmi_enable_ints(struct dw_hdmi_dev *dw_dev) > > +{ > > + u32 pdec_ints = 0; > > + > > + /* video interrupts */ > > + hdmi_writel(dw_dev, DW_HDMI_CLK_CHANGE_ISTS | DW_HDMI_PLL_LCK_CHG_ISTS | > > + DW_HDMI_DCM_CURRENT_MODE_CHG_ISTS, DW_HDMI_IEN_SET); > > + hdmi_writel(dw_dev, (DW_HDMI_VACT_LIN_ISTS | DW_HDMI_HACT_PIX_ISTS), > > + DW_HDMI_MD_IEN_SET); > > + > > + /* infoframes interrupts */ > > + pdec_ints = (DW_HDMI_VSI_CKS_CHG_ISTS | > > + DW_HDMI_DRM_CKS_CHG_ISTS | > > + DW_HDMI_AVI_CKS_CHG_ISTS); > > + > > + /* audio interrupts */ > > + pdec_ints |= (DW_HDMI_AUD_TYPE_CHG_ISTS | > > + DW_HDMI_AIF_CKS_CHG_ISTS | > > + DW_HDMI_ACR_N_CHG_ISTS | > > + DW_HDMI_ACR_CTS_CHG_ISTS | > > + DW_HDMI_GCP_AV_MUTE_CHG_ISTS); > > + > > + hdmi_writel(dw_dev, pdec_ints, DW_HDMI_PDEC_IEN_SET); > > + hdmi_writel(dw_dev, (DW_HDMI_AFIF_OVERFL_ISTS | > > + DW_HDMI_AFIF_UNDERFL_ISTS | > > + DW_HDMI_AFIF_THS_PASS_ISTS), > > + DW_HDMI_AUD_FIFO_IEN_SET); > > +} > > + > > +static void dw_hdmi_disable_ints(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_writel(dw_dev, ~0x0, DW_HDMI_IEN_CLR); > > + hdmi_writel(dw_dev, ~0x0, DW_HDMI_MD_IEN_CLR); > > + > > + hdmi_writel(dw_dev, ~0x0, DW_HDMI_PDEC_IEN_CLR); > > + hdmi_writel(dw_dev, ~0x0, DW_HDMI_AUD_FIFO_IEN_CLR); > > +} > > + > > +static void dw_hdmi_clear_ints(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_writel(dw_dev, ~0x0, DW_HDMI_ICLR); > > + hdmi_writel(dw_dev, ~0x0, DW_HDMI_MD_ICLR); > > + > > + hdmi_writel(dw_dev, ~0x0, DW_HDMI_PDEC_ICLR); > > + hdmi_writel(dw_dev, ~0x0, DW_HDMI_AUD_FIFO_ICLR); > > +} > > + > > +static u32 dw_hdmi_get_int_val(struct dw_hdmi_dev *dw_dev, u32 ists, u32 ien) > > +{ > > + return hdmi_readl(dw_dev, ists) & hdmi_readl(dw_dev, ien); > > +} > > + > > +static u8 dw_hdmi_get_curr_vic(struct dw_hdmi_dev *dw_dev, bool *is_hdmi_vic) > > +{ > > + u8 vic = hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_AVI_PB, > > + DW_HDMI_VID_IDENT_CODE_OFFSET, > > + DW_HDMI_VID_IDENT_CODE_MASK); > > + > > + if (!vic) { > > + vic = hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_VSI_PAYLOAD0, > > + DW_HDMI_VSI_PAYLOAD1_HDMI_VIC_OFFSET, > > + DW_HDMI_VSI_PAYLOAD1_HDMI_VIC_MASK); > > + if (is_hdmi_vic) > > + *is_hdmi_vic = true; > > + } else { > > + if (is_hdmi_vic) > > + *is_hdmi_vic = false; > > + } > > + > > + return vic; > > +} > > + > > +static u32 dw_hdmi_get_evaltime(struct dw_hdmi_dev *dw_dev) > > +{ > > + return hdmi_mask_readl(dw_dev, DW_HDMI_CKM_EVLTM, > > + DW_HDMI_EVAL_TIME_OFFSET, > > + DW_HDMI_EVAL_TIME_MASK); > > +} > > + > > +static u32 dw_hdmi_get_clkrate(struct dw_hdmi_dev *dw_dev) > > +{ > > + return hdmi_mask_readl(dw_dev, DW_HDMI_CKM_RESULT, > > + DW_HDMI_CLKRATE_OFFSET, > > + DW_HDMI_CLKRATE_MASK); > > +} > > + > > +static u32 dw_hdmi_get_cts(struct dw_hdmi_dev *dw_dev) > > +{ > > + return hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_ACR_CTS, > > + DW_HDMI_CTS_DECODED_OFFSET, > > + DW_HDMI_CTS_DECODED_MASK); > > +} > > + > > +static u32 dw_hdmi_get_n(struct dw_hdmi_dev *dw_dev) > > +{ > > + return hdmi_mask_readl(dw_dev, DW_HDMI_PDEC_ACR_N, > > + DW_HDMI_N_DECODED_OFFSET, > > + DW_HDMI_N_DECODED_MASK); > > +} > > + > > +static u32 dw_hdmi_get_tmds_clk(struct dw_hdmi_dev *dw_dev) > > +{ > > + u32 rate = dw_hdmi_get_clkrate(dw_dev); > > + u64 tmp = (u64)rate * (u64)dw_dev->config->iref_clk * 1000000; > > + u32 evaltime = dw_hdmi_get_evaltime(dw_dev); > > + > > + do_div(tmp, evaltime); > > + return tmp; > > +} > > + > > +static u32 dw_hdmi_get_colordepth(struct dw_hdmi_dev *dw_dev) > > +{ > > + u32 dcm = hdmi_mask_readl(dw_dev, DW_HDMI_STS, > > + DW_HDMI_DCM_CURRENT_MODE_OFFSET, > > + DW_HDMI_DCM_CURRENT_MODE_MASK); > > + > > + switch (dcm) { > > + case 0x4: > > + return 24; > > + case 0x5: > > + return 30; > > + case 0x6: > > + return 36; > > + case 0x7: > > + return 48; > > + default: > > + return 24; > > + } > > +} > > + > > +static u64 dw_hdmi_get_pixelclk(struct dw_hdmi_dev *dw_dev) > > +{ > > + u32 tmds_clk = dw_hdmi_get_tmds_clk(dw_dev); > > + u32 cd = dw_hdmi_get_colordepth(dw_dev); > > + u32 pix_clk = 0; > > + > > + switch (cd) { > > + case 24: > > + pix_clk = tmds_clk; > > + break; > > + case 30: > > + pix_clk = (tmds_clk * 100) / 125; > > + break; > > + case 36: > > + pix_clk = (tmds_clk * 10) / 15; > > + break; > > + case 48: > > + pix_clk = tmds_clk / 2; > > + break; > > + default: > > + break; > > + } > > + > > + return pix_clk; > > +} > > + > > +static void dw_hdmi_set_input(struct dw_hdmi_dev *dw_dev, u32 input) > > +{ > > + hdmi_mask_writel(dw_dev, input, DW_HDMI_PHY_CTRL, > > + DW_HDMI_PORTSELECT_OFFSET, > > + DW_HDMI_PORTSELECT_MASK); > > + dw_dev->configured_input = input; > > + dw_dev->selected_input = input; > > + v4l2_subdev_notify(&dw_dev->sd, DW_HDMI_NOTIFY_INPUT_CHANGED, > > + &dw_dev->configured_input); > > +} > > + > > +static void dw_hdmi_enable_hpd(struct dw_hdmi_dev *dw_dev, u32 input_mask) > > +{ > > + hdmi_mask_writel(dw_dev, input_mask, DW_HDMI_SETUP_CTRL, > > + DW_HDMI_HOT_PLUG_DETECT_INPUT_X_OFFSET, > > + DW_HDMI_HOT_PLUG_DETECT_INPUT_X_MASK); > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_SETUP_CTRL, > > + DW_HDMI_HOT_PLUG_DETECT_OFFSET, > > + DW_HDMI_HOT_PLUG_DETECT_MASK); > > +} > > + > > +static void dw_hdmi_disable_hpd(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_mask_writel(dw_dev, 0x0, DW_HDMI_SETUP_CTRL, > > + DW_HDMI_HOT_PLUG_DETECT_INPUT_X_OFFSET, > > + DW_HDMI_HOT_PLUG_DETECT_INPUT_X_MASK); > > + hdmi_mask_writel(dw_dev, 0x0, DW_HDMI_SETUP_CTRL, > > + DW_HDMI_HOT_PLUG_DETECT_OFFSET, > > + DW_HDMI_HOT_PLUG_DETECT_MASK); > > +} > > + > > +static void dw_hdmi_enable_scdc(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_SCDC_CONFIG, > > + DW_HDMI_POWERPROVIDED_OFFSET, > > + DW_HDMI_POWERPROVIDED_MASK); > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_IEN_SET, > > + DW_HDMI_SCDCTMDSCFGCHANGE_ISTS_OFFSET, > > + DW_HDMI_SCDCTMDSCFGCHANGE_ISTS_MASK); > > +} > > + > > +static void dw_hdmi_disable_scdc(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_mask_writel(dw_dev, 0x0, DW_HDMI_SCDC_CONFIG, > > + DW_HDMI_POWERPROVIDED_OFFSET, > > + DW_HDMI_POWERPROVIDED_MASK); > > +} > > + > > +static void dw_hdmi_handle_audio_mute_change(struct dw_hdmi_dev *dw_dev); > > + > > +static void dw_hdmi_enable_hdmi_domain(struct dw_hdmi_dev *dw_dev, bool enable) > > +{ > > + hdmi_mask_writel(dw_dev, enable, DW_HDMI_DMI_DISABLE_IF, > > + DW_HDMI_HDMI_ENABLE_OFFSET, > > + DW_HDMI_HDMI_ENABLE_MASK); > > +} > > + > > +static int dw_hdmi_initial_config(struct dw_hdmi_dev *dw_dev, u32 input); > > + > > +static int dw_hdmi_config(struct dw_hdmi_dev *dw_dev, u32 input) > > +{ > > + u32 in_state = dw_dev->state; > > + u32 cd1 = 0, cd2 = 0; > > + int eqret, ret = 0; > > + > > + while (1) { > > + /* Give up silently if we are forcing off */ > > + if (dw_dev->force_off) { > > + ret = 0; > > + goto out; > > + } > > + /* Give up silently if input has disconnected */ > > + if (!has_signal(dw_dev, input)) { > > + ret = 0; > > + goto out; > > + } > > + > > + switch (dw_dev->state) { > > + case HDMI_STATE_POWER_OFF: > > + dw_hdmi_disable_ints(dw_dev); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_PHY_CONFIG); > > + break; > > + case HDMI_STATE_POWER_UP: > > + /* when connect the cable */ > > + dw_hdmi_disable_ints(dw_dev); > > + /* reset */ > > + if (dw_dev->hw_reset_on_hot_plug) > > + dw_hdmi_reset(dw_dev); > > + /* initial configuration */ > > + dw_hdmi_initial_config(dw_dev, input); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_PHY_CONFIG); > > + break; > > + case HDMI_STATE_PHY_CONFIG: > > + cd1 = 24; > > + dw_dev->is_hdmi2 = is_hdmi2(dw_dev); > > + dw_dev->is_scrambled = is_scrambled(dw_dev); > > + dw_hdmi_phy_config(dw_dev, cd1, dw_dev->is_hdmi2, > > + dw_dev->is_scrambled); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_HPD); > > + break; > > + case HDMI_STATE_HPD: > > + /* disable HDMI domain to avoid the DCM INTs */ > > + dw_hdmi_enable_hdmi_domain(dw_dev, false); > > + dw_hdmi_enable_scdc(dw_dev); > > + dw_hdmi_enable_hpd(dw_dev, dw_dev->input_stat); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_EQUALIZER); > > + break; > > + case HDMI_STATE_EQUALIZER: > > + if (dw_dev->phy_eq_on) { > > + bool phy_eq_force = dw_dev->phy_eq_force; > > + > > + eqret = dw_hdmi_phy_eq_init(dw_dev, 5, > > + phy_eq_force); > > + } else { > > + /* Clear equalizer error status if not on */ > > + eqret = 0; > > + } > > + > > + ret = dw_hdmi_wait_phy_lock_poll(dw_dev); > > + > > + /* Do not force equalizer */ > > + dw_dev->phy_eq_force = false; > > + > > + if (ret || eqret) { > > + if (ret || eqret == -ETIMEDOUT) { > > + /* No TMDSVALID signal: > > + * - force equalizer > > + */ > > + dw_dev->phy_eq_force = true; > > + } > > + break; > > + } > > + > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_DATAPATH); > > + break; > > + case HDMI_STATE_DATAPATH: > > + dw_hdmi_reset_datapath(dw_dev); > > + /* reenable HDMI domain */ > > + dw_hdmi_enable_hdmi_domain(dw_dev, true); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_VIDEO_UNSTABLE); > > + break; > > + case HDMI_STATE_VIDEO_UNSTABLE: > > + dw_hdmi_wait_video_stable(dw_dev); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_AUDIO); > > + break; > > + case HDMI_STATE_AUDIO: > > + ret = dw_hdmi_config_audio(dw_dev); > > + dw_hdmi_config_packet(dw_dev); > > + > > + if (in_state != HDMI_STATE_EQUALIZER) > > + dw_hdmi_clear_ints(dw_dev); > > + > > + dw_hdmi_get_infoframes(dw_dev); > > + > > + /* check if there was deep color changes */ > > + if (cd1) { > > + cd2 = dw_hdmi_get_colordepth(dw_dev); > > + if (cd1 != cd2) > > + dw_hdmi_phy_set_color_depth(dw_dev, > > + cd2); > > + } > > + > > + /* reset CEA video */ > > + dw_hdmi_reset_ceavid(dw_dev); > > + > > + dw_hdmi_enable_ints(dw_dev); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_POWER_ON); > > + break; > > + case HDMI_STATE_POWER_ON: > > + break; > > + default: > > + dev_err(dw_dev->dev, "%s called with state (%d)\n", > > + __func__, dw_dev->state); > > + ret = -EINVAL; > > + goto out; > > + } > > + > > + if (dw_dev->state == HDMI_STATE_POWER_ON) { > > + dev_info(dw_dev->dev, "HDMI-RX configured\n"); > > + dw_hdmi_event_source_change(dw_dev); > > + dw_hdmi_handle_audio_mute_change(dw_dev); > > + return 0; > > + } > > + } > > + > > +out: > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_POWER_OFF); > > + v4l2_subdev_notify(&dw_dev->sd, DW_HDMI_NOTIFY_IS_OFF, NULL); > > + return ret; > > +} > > + > > +static int __dw_hdmi_power_on(struct dw_hdmi_dev *dw_dev, u32 input) > > +{ > > + unsigned long flags; > > + int ret; > > + > > + ret = dw_hdmi_config(dw_dev, input); > > + > > + spin_lock_irqsave(&dw_dev->lock, flags); > > + dw_dev->pending_config = false; > > + spin_unlock_irqrestore(&dw_dev->lock, flags); > > + > > + return ret; > > +} > > + > > +static void dw_hdmi_work_handler(struct work_struct *work) > > +{ > > + struct dw_hdmi_dev *dw_dev = container_of(work, struct dw_hdmi_dev, > > + work); > > + > > + __dw_hdmi_power_on(dw_dev, dw_dev->configured_input); > > +} > > + > > +static int dw_hdmi_power_on(struct dw_hdmi_dev *dw_dev, u32 input) > > +{ > > + unsigned long flags; > > + > > + spin_lock_irqsave(&dw_dev->lock, flags); > > + if (dw_dev->pending_config) { > > + spin_unlock_irqrestore(&dw_dev->lock, flags); > > + return 0; > > + } > > + > > + INIT_WORK(&dw_dev->work, dw_hdmi_work_handler); > > + dw_dev->configured_input = input; > > + dw_dev->pending_config = true; > > + queue_work(dw_dev->wq, &dw_dev->work); > > + spin_unlock_irqrestore(&dw_dev->lock, flags); > > + return 0; > > +} > > + > > +static void dw_hdmi_power_off(struct dw_hdmi_dev *dw_dev) > > +{ > > + unsigned long flags; > > + > > + v4l2_subdev_notify(&dw_dev->sd, DW_HDMI_NOTIFY_IS_OFF, NULL); > > + > > + dw_dev->force_off = true; > > + flush_workqueue(dw_dev->wq); > > + dw_dev->force_off = false; > > + > > + spin_lock_irqsave(&dw_dev->lock, flags); > > + dw_dev->pending_config = false; > > + dw_dev->state = HDMI_STATE_POWER_OFF; > > + spin_unlock_irqrestore(&dw_dev->lock, flags); > > + > > + /* Reset variables */ > > + dw_dev->phy_eq_force = true; > > + dw_dev->audio_sf = 0; > > +} > > + > > +static void dw_hdmi_force_off(struct dw_hdmi_dev *dw_dev) > > +{ > > + unsigned long flags; > > + > > + v4l2_subdev_notify(&dw_dev->sd, DW_HDMI_NOTIFY_IS_OFF, NULL); > > + > > + dw_dev->force_off = true; > > + flush_workqueue(dw_dev->wq); > > + dw_dev->force_off = false; > > + > > + spin_lock_irqsave(&dw_dev->lock, flags); > > + dw_dev->pending_config = false; > > + spin_unlock_irqrestore(&dw_dev->lock, flags); > > +} > > + > > +static void dw_hdmi_power_up(struct dw_hdmi_dev *dw_dev) > > +{ > > + dw_hdmi_force_off(dw_dev); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_POWER_UP); > > + > > + /* Reset variables */ > > + dw_dev->phy_eq_force = true; > > + dw_dev->audio_sf = 0; > > +} > > + > > +static void dw_hdmi_controller_power_off(struct dw_hdmi_dev *dw_dev) > > +{ > > + dw_hdmi_force_off(dw_dev); > > + dw_hdmi_set_state(dw_dev, HDMI_STATE_EQUALIZER); > > + > > + dw_dev->phy_eq_force = false; > > + dw_dev->audio_sf = 0; > > +} > > + > > +static int dw_hdmi_query_dv_timings(struct v4l2_subdev *sd, > > + struct v4l2_dv_timings *timings); > > + > > +static void dw_hdmi_handle_video_change(struct dw_hdmi_dev *dw_dev) > > +{ > > + struct v4l2_dv_timings timings; > > + > > + if (is_on(dw_dev)) { > > + dev_dbg(dw_dev->dev, "[VIDEO] video change interrupt\n"); > > + dw_hdmi_query_dv_timings(&dw_dev->sd, &timings); > > + dw_hdmi_controller_power_off(dw_dev); > > + dw_hdmi_power_on(dw_dev, dw_dev->configured_input); > > + } > > +} > > + > > +static u32 dw_hdmi_round_freq(int freq) > > +{ > > + static const u32 base_freqs[] = { 32000, 44100, 48000, 0 }; > > + unsigned int i; > > + > > + for (i = 0; base_freqs[i]; i++) { > > + if ((freq <= (base_freqs[i] + DW_HDMI_AUDIO_FREQ_RANGE)) && > > + (freq >= (base_freqs[i] - DW_HDMI_AUDIO_FREQ_RANGE))) { > > + return base_freqs[i]; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static unsigned int dw_hdmi_get_sample_freq(struct dw_hdmi_dev *dw_dev) > > +{ > > + u64 tmp; > > + u32 cts; > > + u32 sf; > > + u32 n; > > + > > + n = dw_hdmi_get_n(dw_dev); > > + cts = dw_hdmi_get_cts(dw_dev); > > + > > + if (!n || !cts) { > > + dev_dbg(dw_dev->dev, "%s: cts: %d, n: %d\n", __func__, cts, n); > > + return 0; > > + } > > + > > + dev_dbg(dw_dev->dev, "%s: tmds_clk: %d\n", > > + __func__, dw_hdmi_get_tmds_clk(dw_dev)); > > + > > + /* regenerate the audio clock from tmds clock */ > > + tmp = (u64)dw_hdmi_get_tmds_clk(dw_dev) * (u64)n; > > + do_div(tmp, cts); > > + do_div(tmp, 128); > > + sf = tmp; > > + > > + dev_dbg(dw_dev->dev, "%s: sf: %d\n", __func__, sf); > > + sf = dw_hdmi_round_freq(sf); > > + dev_dbg(dw_dev->dev, "%s: sf(round): %d\n", __func__, sf); > > + > > + return sf; > > +} > > + > > +static void dw_hdmi_handle_audio_mute_change(struct dw_hdmi_dev *dw_dev) > > +{ > > + unsigned long flags; > > + unsigned int sf; > > + > > + spin_lock_irqsave(&dw_dev->event_lock, flags); > > + sf = dw_hdmi_get_sample_freq(dw_dev); > > + v4l2_subdev_notify(&dw_dev->sd, DW_HDMI_NOTIFY_AUDIO_CHANGED, &sf); > > + dw_dev->audio_sf = sf; > > + spin_unlock_irqrestore(&dw_dev->event_lock, flags); > > +} > > + > > +static void dw_hdmi_handle_audio_change(struct dw_hdmi_dev *dw_dev, > > + u32 afif_stat, u32 pdec_stat) > > +{ > > + bool restart = true; > > + > > + if (pdec_stat & DW_HDMI_GCP_AV_MUTE_CHG_ISTS) { > > + dev_dbg(dw_dev->dev, "[interrupt:audio] AV Mute change\n"); > > + dw_hdmi_handle_audio_mute_change(dw_dev); > > + restart = false; > > + } > > + > > + if (pdec_stat & DW_HDMI_AUD_TYPE_CHG_ISTS) { > > + dev_dbg(dw_dev->dev, "[interrupt:audio] type change\n"); > > + restart = true; > > + } > > + if (pdec_stat & DW_HDMI_AIF_CKS_CHG_ISTS) { > > + dev_dbg(dw_dev->dev, "[interrupt:audio] aif change\n"); > > + restart = true; > > + } > > + if (pdec_stat & DW_HDMI_ACR_N_CHG_ISTS) { > > + dev_dbg(dw_dev->dev, "[interrupt:audio] N change\n"); > > + restart = true; > > + } > > + if (pdec_stat & DW_HDMI_ACR_CTS_CHG_ISTS) { > > + dev_dbg(dw_dev->dev, "[interrupt:audio] CTS change\n"); > > + restart = true; > > + } > > + if (afif_stat & DW_HDMI_AFIF_UNDERFL_ISTS) { > > + dev_dbg(dw_dev->dev, "[interrupt:audio] fifo underflow\n"); > > + restart = true; > > + } > > + if (afif_stat & DW_HDMI_AFIF_OVERFL_ISTS) { > > + dev_dbg(dw_dev->dev, "[interrupt:audio] fifo overflow\n"); > > + restart = true; > > + } > > + if (afif_stat & DW_HDMI_AFIF_THS_PASS_ISTS) { > > + dev_dbg(dw_dev->dev, "[interrupt:audio] TRH Pass\n"); > > + dw_hdmi_handle_audio_mute_change(dw_dev); > > + restart = false; > > + } > > + > > + if (restart) { > > + dw_hdmi_handle_audio_mute_change(dw_dev); > > + dw_hdmi_wait_audio_lock_poll(dw_dev); > > + dw_hdmi_reset_audio(dw_dev); > > + dw_hdmi_restart_audio_fifo(dw_dev); > > + } > > +} > > + > > +static irqreturn_t dw_hdmi_irq_handler(int irq, void *dev_data) > > +{ > > + struct dw_hdmi_dev *dw_dev = dev_data; > > + u32 hdmi_ists = dw_hdmi_get_int_val(dw_dev, DW_HDMI_ISTS, DW_HDMI_IEN); > > + u32 md_ists = dw_hdmi_get_int_val(dw_dev, DW_HDMI_MD_ISTS, > > + DW_HDMI_MD_IEN); > > + u32 pdec_ists = dw_hdmi_get_int_val(dw_dev, DW_HDMI_PDEC_ISTS, > > + DW_HDMI_PDEC_IEN); > > + u32 afif_ists = dw_hdmi_get_int_val(dw_dev, DW_HDMI_AUD_FIFO_ISTS, > > + DW_HDMI_AUD_FIFO_IEN); > > + u32 cd = 0; > > + > > + dw_hdmi_clear_ints(dw_dev); > > + > > + /* video handling */ > > + if (hdmi_ists & DW_HDMI_CLK_CHANGE_ISTS) { > > + dev_dbg(dw_dev->dev, "[HDMI] clock rate change\n"); > > + dw_hdmi_power_off(dw_dev); > > + if (has_signal(dw_dev, dw_dev->configured_input)) > > + dw_hdmi_power_on(dw_dev, dw_dev->configured_input); > > + > > + return IRQ_HANDLED; > > + } > > + if (hdmi_ists & DW_HDMI_PLL_LCK_CHG_ISTS) { > > + dev_dbg(dw_dev->dev, > > + "[PHY] PLL lock state changed (tmds_valid: %d)\n", > > + dw_hdmi_tmds_valid(dw_dev)); > > + dw_hdmi_power_off(dw_dev); > > + > > + if (has_signal(dw_dev, dw_dev->configured_input)) > > + dw_hdmi_power_on(dw_dev, dw_dev->configured_input); > > + > > + return IRQ_HANDLED; > > + } > > + if (hdmi_ists & DW_HDMI_DCM_CURRENT_MODE_CHG_ISTS) { > > + dev_dbg(dw_dev->dev, > > + "[HDMI] deep color changed\n"); > > + > > + cd = dw_hdmi_get_colordepth(dw_dev); > > + if (cd) > > + dw_hdmi_phy_set_color_depth(dw_dev, cd); > > + > > + /* reset CEA video */ > > + dw_hdmi_reset_ceavid(dw_dev); > > + > > + return IRQ_HANDLED; > > + } > > + if (md_ists || pdec_ists & > > + (DW_HDMI_VSI_CKS_CHG_ISTS | > > + DW_HDMI_DRM_CKS_CHG_ISTS | > > + DW_HDMI_AVI_CKS_CHG_ISTS)) { > > + dw_hdmi_handle_video_change(dw_dev); > > + dev_dbg(dw_dev->dev, > > + " md_ists: 0x%x, pdec_ists: 0x%x\n", > > + md_ists, pdec_ists); > > + } > > + > > + /* infoframes */ > > + if (pdec_ists & DW_HDMI_AIF_CKS_CHG_ISTS) > > + dw_hdmi_get_infoframes(dw_dev); > > + > > + /* audio handling */ > > + if (pdec_ists & (DW_HDMI_AUD_TYPE_CHG_ISTS | > > + DW_HDMI_AIF_CKS_CHG_ISTS | > > + DW_HDMI_ACR_N_CHG_ISTS | > > + DW_HDMI_ACR_CTS_CHG_ISTS | > > + DW_HDMI_GCP_AV_MUTE_CHG_ISTS) || > > + afif_ists & (DW_HDMI_AFIF_UNDERFL_ISTS | > > + DW_HDMI_AFIF_OVERFL_ISTS | > > + DW_HDMI_AFIF_THS_PASS_ISTS)) { > > + dw_hdmi_handle_audio_change(dw_dev, afif_ists, pdec_ists); > > + } > > + > > + /* scdc */ > > + if (hdmi_ists & DW_HDMI_SCDCTMDSCFGCHANGE_ISTS_MASK) { > > + dev_dbg(dw_dev->dev, "[SCDC] hdmi2=%d->%d, scrambling=%d->%d\n", > > + dw_dev->is_hdmi2, is_hdmi2(dw_dev), > > + dw_dev->is_scrambled, is_scrambled(dw_dev)); > > + if (dw_dev->is_hdmi2 != is_hdmi2(dw_dev) || > > + dw_dev->is_scrambled != is_scrambled(dw_dev)) { > > + dw_dev->is_hdmi2 = is_hdmi2(dw_dev); > > + dw_dev->is_scrambled = is_scrambled(dw_dev); > > + dw_hdmi_power_off(dw_dev); > > + dw_hdmi_power_on(dw_dev, dw_dev->configured_input); > > + } > > + } > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int dw_hdmi_detect_tx_5v_ctrl(struct dw_hdmi_dev *dw_dev) > > +{ > > + bool current_on = dw_hdmi_5v_status(dw_dev, dw_dev->configured_input); > > + unsigned int input_count = dw_dev->config->phy->input_count; > > + unsigned int old_input = dw_dev->configured_input; > > + unsigned int new_input = old_input; > > + bool pending_config = false; > > + unsigned int stat = 0; > > + unsigned int i; > > + > > + for (i = 0; i < input_count; i++) { > > + bool on = dw_hdmi_5v_status(dw_dev, i); > > + > > + stat |= on << i; > > + > > + if (on && on != dw_dev->input_connected[i]) { > > + dw_hdmi_disable_ints(dw_dev); > > + dw_hdmi_power_off(dw_dev); > > + dw_hdmi_power_up(dw_dev); > > + dw_dev->input_connected[i] = true; > > + dw_hdmi_power_on(dw_dev, i); > > + dw_hdmi_set_input(dw_dev, i); > > + new_input = i; > > + pending_config = true; > > + } else { > > + dw_dev->input_connected[i] = on; > > + } > > + } > > + > > + dw_dev->input_stat = stat; > > + > > + if (!pending_config && !current_on) { > > + dw_hdmi_disable_ints(dw_dev); > > + dw_hdmi_disable_hpd(dw_dev); > > + dw_hdmi_disable_scdc(dw_dev); > > + dw_hdmi_power_off(dw_dev); > > + if (dw_dev->phy->power_count > 0) > > + phy_power_off(dw_dev->phy); > > + } > > + > > + dev_dbg(dw_dev->dev, "%s: stat=0x%x, input=%d->%d\n", __func__, > > + stat, old_input, new_input); > > + return v4l2_ctrl_s_ctrl(dw_dev->detect_tx_5v_ctrl, stat); > > +} > > + > > +static irqreturn_t dw_hdmi_5v_irq_handler(int irq, void *dev_data) > > +{ > > + struct dw_hdmi_dev *dw_dev = dev_data; > > + > > + dw_hdmi_detect_tx_5v_ctrl(dw_dev); > > + return IRQ_HANDLED; > > +} > > + > > +static irqreturn_t dw_hdmi_5v_hard_irq_handler(int irq, void *dev_data) > > +{ > > + struct dw_hdmi_dev *dw_dev = dev_data; > > + unsigned int input_count = dw_dev->config->phy->input_count; > > + u32 stat = 0x0; > > + unsigned int i; > > + > > + /* Clear interrupts */ > > + for (i = 0; i < input_count; i++) { > > + dw_hdmi_5v_disable(dw_dev, i); > > + dw_hdmi_5v_enable(dw_dev, i); > > + stat |= dw_hdmi_5v_status(dw_dev, i) << i; > > + } > > + > > + if (!stat) { > > + /* > > + * If there are no connected ports disable whole HPD and SCDC > > + * also. > > + */ > > + dw_hdmi_disable_hpd(dw_dev); > > + dw_hdmi_disable_scdc(dw_dev); > > + } > > + > > + return IRQ_WAKE_THREAD; > > +} > > + > > +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); > > + int ret; > > + > > + if (!has_signal(dw_dev, input)) > > + return -EINVAL; > > + > > + dw_dev->selected_input = input; > > + if (input == dw_dev->configured_input) > > + return 0; > > + > > + dw_hdmi_power_off(dw_dev); > > + ret = dw_hdmi_power_on(dw_dev, input); > > + dw_hdmi_set_input(dw_dev, input); > > + return ret; > > +} > > + > > +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_on(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_frame_interval(struct v4l2_subdev *sd, > > + struct v4l2_subdev_frame_interval *ival) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + u32 htot, vtot, fps; > > + unsigned long n, d; > > + u64 pclk; > > + > > + htot = hdmi_mask_readl(dw_dev, DW_HDMI_MD_HT1, > > + DW_HDMI_HTOT_PIX_OFFSET, > > + DW_HDMI_HTOT_PIX_MASK); > > + vtot = hdmi_readl(dw_dev, DW_HDMI_MD_VTL); > > + pclk = dw_hdmi_get_pixelclk(dw_dev); > > + > > + fps = (htot * vtot) > 0 ? div_u64((100 * pclk), (htot * vtot)) : 0; > > + if (!fps) > > + return 0; > > + > > + rational_best_approximation(fps, 100, fps, 100, &n, &d); > > + > > + ival->interval.numerator = d; > > + ival->interval.denominator = n; > > + > > + dev_dbg(dw_dev->dev, "%s: %lu / %lu\n", __func__, d, n); > > + > > + 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); > > + > > + 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); > > + > > + *timings = dw_dev->timings; > > + return 0; > > +} > > + > > +static u32 dw_hdmi_get_width(struct dw_hdmi_dev *dw_dev) > > +{ > > + u32 width = hdmi_readl(dw_dev, DW_HDMI_MD_HACT_PX); > > + u32 cd = dw_hdmi_get_colordepth(dw_dev); > > + > > + switch (cd) { > > + case 30: > > + width = (width * 100) / 125; > > + break; > > + case 36: > > + width = (width * 10) / 15; > > + break; > > + case 48: > > + width /= 2; > > + break; > > + case 24: > > + default: > > + break; > > + } > > + > > + if (dw_hdmi_get_mbus_code(dw_dev) == MEDIA_BUS_FMT_YVYU8_1X16) > > + width *= 2; > > + > > + return width; > > +} > > + > > +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; > > + > > + memset(timings, 0, sizeof(*timings)); > > + > > + if (is_off(dw_dev)) { > > + dev_dbg(dw_dev->dev, "%s: controller is off\n", __func__); > > + return -ENOLINK; > > + } > > + > > + if (!is_on(dw_dev)) { > > + dev_dbg(dw_dev->dev, "%s: controller is being configured\n", > > + __func__); > > + return -EAGAIN; > > + } > > + > > + timings->type = V4L2_DV_BT_656_1120; > > + bt->width = dw_hdmi_get_width(dw_dev); > > + bt->height = hdmi_readl(dw_dev, DW_HDMI_MD_VAL); > > + bt->interlaced = > > + hdmi_readl(dw_dev, DW_HDMI_MD_STS) & DW_HDMI_ILACE_STS ? > > + V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; > > + > > + if (hdmi_readl(dw_dev, DW_HDMI_ISTS) & DW_HDMI_VS_POL_ADJ_ISTS) > > + bt->polarities |= V4L2_DV_VSYNC_POS_POL; > > + if (hdmi_readl(dw_dev, DW_HDMI_ISTS) & DW_HDMI_HS_POL_ADJ_ISTS) > > + 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, DW_HDMI_MD_HT1, > > + DW_HDMI_HTOT_PIX_OFFSET, > > + DW_HDMI_HTOT_PIX_MASK); > > + /* HOFS = HSYNC + HBACK */ > > + hofs = hdmi_mask_readl(dw_dev, DW_HDMI_MD_HT1, > > + DW_HDMI_HOFS_PIX_OFFSET, > > + DW_HDMI_HOFS_PIX_MASK); > > + > > + bt->hfrontporch = htot - hofs - bt->width; > > + bt->hsync = hdmi_mask_readl(dw_dev, DW_HDMI_MD_HT0, > > + DW_HDMI_HS_CLK_OFFSET, > > + DW_HDMI_HS_CLK_MASK); > > + bt->hbackporch = hofs - bt->hsync; > > + > > + /* VTOT = VACT + VFRONT + VSYNC + VBACK */ > > + vtot = hdmi_readl(dw_dev, DW_HDMI_MD_VTL); > > + > > + bt->vsync = hdmi_readl(dw_dev, DW_HDMI_MD_VOL); > > + > > + bt->vbackporch = hdmi_readl(dw_dev, DW_HDMI_MD_VOL); > > + bt->vfrontporch = vtot - bt->height - bt->vsync - bt->vbackporch; > > + > > + if (bt->interlaced == V4L2_DV_INTERLACED) { > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_MD_VCTRL, > > + DW_HDMI_V_MODE_OFFSET, > > + DW_HDMI_V_MODE_MASK); > > + msleep(100); /* Wait for 2 fields */ > > + > > + vtot = hdmi_readl(dw_dev, DW_HDMI_MD_VTL); > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_MD_VCTRL, > > + DW_HDMI_V_OFFS_LIN_MODE_OFFSET, > > + DW_HDMI_V_OFFS_LIN_MODE_MASK); > > + msleep(50); /* Wait for 1 field */ > > + bt->il_vsync = hdmi_readl(dw_dev, DW_HDMI_MD_VOL); > > + > > + hdmi_mask_writel(dw_dev, 0x0, DW_HDMI_MD_VCTRL, > > + DW_HDMI_V_OFFS_LIN_MODE_OFFSET, > > + DW_HDMI_V_OFFS_LIN_MODE_MASK); > > + msleep(50); > > + bt->il_vbackporch = hdmi_readl(dw_dev, DW_HDMI_MD_VOL); > > + bt->il_vfrontporch = vtot - bt->height - bt->il_vsync - > > + bt->il_vbackporch; > > + > > + hdmi_mask_writel(dw_dev, 0x0, DW_HDMI_MD_VCTRL, > > + DW_HDMI_V_MODE_OFFSET, > > + DW_HDMI_V_MODE_MASK); > > + } > > + > > + 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; > > + } > > + } > > + > > + dev_dbg(dw_dev->dev, "%s: width=%u, height=%u, mbuscode=%u\n", __func__, > > + bt->width, bt->height, dw_hdmi_get_mbus_code(dw_dev)); > > + > > + return 0; > > +} > > + > > +static int dw_hdmi_enum_mbus_code(struct v4l2_subdev *sd, > > + struct v4l2_subdev_pad_config *cfg, > > + struct v4l2_subdev_mbus_code_enum *code) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + > > + if (code->index != 0) > > + return -EINVAL; > > + > > + code->code = dw_dev->mbus_code; > > + return 0; > > +} > > + > > +static int dw_hdmi_fill_format(struct dw_hdmi_dev *dw_dev, > > + struct v4l2_mbus_framefmt *format) > > +{ > > + enum hdmi_extended_colorimetry extcol; > > + enum hdmi_colorimetry col; > > + int ret; > > + > > + memset(format, 0, sizeof(*format)); > > + > > + /* Update timings */ > > + ret = dw_hdmi_query_dv_timings(&dw_dev->sd, &dw_dev->timings); > > + if (ret) > > + return ret; > > + > > + /* Update infoframe contents */ > > + dw_hdmi_get_infoframes(dw_dev); > > + > > + col = dw_dev->aviif.avi.colorimetry; > > + extcol = dw_dev->aviif.avi.extended_colorimetry; > > + > > + switch (col) { > > + case HDMI_COLORIMETRY_ITU_601: > > + format->colorspace = V4L2_COLORSPACE_SMPTE170M; > > + break; > > + case HDMI_COLORIMETRY_EXTENDED: > > + switch (extcol) { > > + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_601: > > + format->colorspace = V4L2_COLORSPACE_SMPTE170M; > > + break; > > + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_709: > > + case HDMI_EXTENDED_COLORIMETRY_S_YCC_601: > > + case HDMI_EXTENDED_COLORIMETRY_OPYCC_601: > > + case HDMI_EXTENDED_COLORIMETRY_OPRGB: > > + case HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM: > > + case HDMI_EXTENDED_COLORIMETRY_BT2020: > > + case HDMI_EXTENDED_COLORIMETRY_RESERVED: > > + default: > > + format->colorspace = V4L2_COLORSPACE_REC709; > > + break; > > + } > > + > > + break; > > + case HDMI_COLORIMETRY_NONE: > > + case HDMI_COLORIMETRY_ITU_709: > > + default: > > + format->colorspace = V4L2_COLORSPACE_REC709; > > + break; > > + } > > + > > + format->width = dw_dev->timings.bt.width; > > + format->height = dw_dev->timings.bt.height; > > + format->code = dw_dev->mbus_code; > > + if (dw_dev->timings.bt.interlaced) > > + format->field = V4L2_FIELD_ALTERNATE; > > + else > > + format->field = V4L2_FIELD_NONE; > > + > > + return 0; > > +} > > + > > +static int dw_hdmi_get_fmt(struct v4l2_subdev *sd, > > + struct v4l2_subdev_pad_config *cfg, > > + struct v4l2_subdev_format *format) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + > > + return dw_hdmi_fill_format(dw_dev, &format->format); > > +} > > + > > +static int dw_hdmi_set_fmt(struct v4l2_subdev *sd, > > + struct v4l2_subdev_pad_config *cfg, > > + struct v4l2_subdev_format *format) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + > > + if (format->format.code != dw_dev->mbus_code) { > > + dev_dbg(dw_dev->dev, "invalid format\n"); > > + return -EINVAL; > > + } > > + > > + return dw_hdmi_get_fmt(sd, cfg, format); > > +} > > + > > +static int dw_hdmi_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + unsigned int input_count = dw_dev->config->phy->input_count; > > + unsigned int i, j, start, end; > > + u8 *ptr = edid->edid; > > + u8 blocks; > > + > > + memset(edid->reserved, 0, sizeof(edid->reserved)); > > + > > + if (edid->pad >= input_count || !ptr) > > + return -EINVAL; > > + > > + blocks = dw_dev->curr_edid_blocks[edid->pad]; > > + > > + if (!edid->start_block && !edid->blocks) { > > + edid->blocks = blocks; > > + return 0; > > + } > > + if (!blocks) > > + return -ENODATA; > > + if (edid->start_block >= blocks) > > + return -EINVAL; > > + if ((edid->start_block + edid->blocks) > blocks) > > + edid->blocks = blocks - edid->start_block; > > + > > + start = (edid->start_block * 128) / sizeof(u32); > > + end = start + (edid->blocks * 128) / sizeof(u32); > > + > > + for (i = start; i < end; i++) { > > + u32 raw = dw_hdmi_edid_read(dw_dev, edid->pad, i * sizeof(u32)); > > + > > + if (!dw_hdmi_edid_4blocks_le(dw_dev)) { > > + u32 raw_srt = 0; > > + /* little endian representation, need to invert */ > > + for (j = 0; j < 4; j++) { > > + raw_srt |= ((raw >> (8 * (3 - j))) & 0xff) > > + << (j * 8); > > + } > > + raw = raw_srt; > > + } > > + memcpy(ptr, &raw, sizeof(u32)); > > + ptr += sizeof(u32); > > + } > > + > > + return 0; > > +} > > + > > +static int dw_hdmi_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + int input_count = dw_dev->config->phy->input_count; > > + int size, ret; > > + u32 *tmp; > > + > > + memset(edid->reserved, 0, sizeof(edid->reserved)); > > + > > + if (edid->pad >= input_count || !edid->edid || !edid->blocks) > > + return -EINVAL; > > + if (edid->start_block != 0) > > + return -EINVAL; > > + > > + /* Clear old EDID */ > > + size = dw_dev->curr_edid_blocks[edid->pad] * 128; > > + tmp = devm_kzalloc(dw_dev->dev, size, GFP_KERNEL); > > + if (!tmp) > > + return -ENOMEM; > > + > > + ret = dw_hdmi_edid_write(dw_dev, edid->pad, tmp, size / sizeof(u32)); > > + devm_kfree(dw_dev->dev, tmp); > > + > > + if (ret) > > + return ret; > > + > > + dw_dev->curr_edid_blocks[edid->pad] = 0; > > + > > + /* Set new EDID */ > > + if (dw_hdmi_edid_4blocks_le(dw_dev)) { > > + /* little endian representation, no need to invert bytes */ > > + ret = dw_hdmi_update_edid(dw_dev, edid->pad, (u8 *)edid->edid, > > + (edid->blocks * 128), false); > > + } else { > > + /* invert the order of bytes to register 32bit */ > > + ret = dw_hdmi_update_edid(dw_dev, edid->pad, (u8 *)edid->edid, > > + (edid->blocks * 128), true); > > + } > > + if (ret) > > + return ret; > > + > > + dw_dev->curr_edid_blocks[edid->pad] = edid->blocks; > > I don't see any checks on edid->blocks: that can't be right. The typical max > number of blocks for an EDID is 2 or 4, I haven't seen any implementations > for more than 4 blocks. > I agree! This will be addressed in the next patch series. > I also don't see any signaling of the HPD: it must be pulled low before you > start writing the EDID, and pulled high again at least 100ms later (150 ms or so > is recommended to keep a margin). > I agree! This will be addressed in the next patch series. > > + return 0; > > +} > > + > > +static int dw_hdmi_dv_timings_cap(struct v4l2_subdev *sd, > > + struct v4l2_dv_timings_cap *cap) > > +{ > > + unsigned int pad = cap->pad; > > + > > + *cap = dw_hdmi_timings_cap; > > + cap->pad = pad; > > + return 0; > > +} > > + > > +static int dw_hdmi_enum_dv_timings(struct v4l2_subdev *sd, > > + struct v4l2_enum_dv_timings *timings) > > +{ > > + return v4l2_enum_dv_timings_cap(timings, &dw_hdmi_timings_cap, > > + NULL, NULL); > > +} > > + > > +static int dw_hdmi_log_status(struct v4l2_subdev *sd) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + struct v4l2_dv_timings timings; > > + > > + v4l2_info(sd, "--- Chip configuration ---\n"); > > + v4l2_info(sd, "iref_clk=%dMHz\n", dw_dev->config->iref_clk); > > + v4l2_info(sd, "phy_drv=%s, phy_jtag_addr=0x%x\n", > > + dw_dev->phy ? "present" : "not present", > > + dw_dev->config->phy->jtag_addr); > > + > > + v4l2_info(sd, "--- Chip status ---\n"); > > + v4l2_info(sd, "selected_input=%d: signal=%d\n", dw_dev->selected_input, > > + has_signal(dw_dev, dw_dev->selected_input)); > > + v4l2_info(sd, "configured_input=%d: signal=%d\n", > > + dw_dev->configured_input, > > + has_signal(dw_dev, dw_dev->configured_input)); > > + > > + v4l2_info(sd, "--- Video status ---\n"); > > + v4l2_info(sd, "type=%s, color_depth=%dbits", > > + hdmi_readl(dw_dev, DW_HDMI_PDEC_STS) & > > + DW_HDMI_DVIDET ? "dvi" : "hdmi", > > + dw_hdmi_get_colordepth(dw_dev)); > > + > > + v4l2_info(sd, "--- Video timings ---\n"); > > + if (dw_hdmi_query_dv_timings(sd, &timings)) > > + v4l2_info(sd, "No video detected\n"); > > + else > > + v4l2_print_dv_timings(sd->name, "Detected format: ", > > + &timings, true); > > + v4l2_print_dv_timings(sd->name, "Configured format: ", > > + &dw_dev->timings, true); > > + > > + v4l2_ctrl_subdev_log_status(sd); > > + return 0; > > +} > > + > > +#ifdef CONFIG_VIDEO_ADV_DEBUG > > +static void dw_hdmi_invalid_register(struct dw_hdmi_dev *dw_dev, u64 reg) > > +{ > > + dev_err(dw_dev->dev, "register 0x%llx not supported\n", reg); > > + dev_err(dw_dev->dev, "0x0000-0x7fff: Main controller map\n"); > > + dev_err(dw_dev->dev, "0x8000-0x8fff: Debug registers\n"); > > + dev_err(dw_dev->dev, " 0x8000: TMDSVALID wait count\n"); > > + dev_err(dw_dev->dev, " 0x8001: SW state\n"); > > + dev_err(dw_dev->dev, " 0x8002: Equalizer ON/OFF\n"); > > + dev_err(dw_dev->dev, " 0x8003: PHY Version\n"); > > + dev_err(dw_dev->dev, " 0x8004: Video Stable Wait Time (ms)\n"); > > + dev_err(dw_dev->dev, " 0x8005: Clock wait time (ms)\n"); > > + dev_err(dw_dev->dev, " 0x8006: iref_clk value\n"); > > + dev_err(dw_dev->dev, " 0x8007: reset_datapath_enable mask value\n"); > > + dev_err(dw_dev->dev, " 0x8008: audio sample frequency (read only)\n"); > > + dev_err(dw_dev->dev, " 0x8009: hw_reset_on_hot_plug (1=enabled, 0=disabled)"); > > + dev_err(dw_dev->dev, "0x10000-0x100ff: PHY map\n"); > > +} > > + > > +static bool dw_hdmi_is_reserved_register(struct dw_hdmi_dev *dw_dev, u32 reg) > > +{ > > + /* > > + * NOTE: Some of the HDCP registers are write only. This means that > > + * a read from these registers will never return and can block the bus > > + * in some architectures. Disable the read to these registers and also > > + * disable the write as a safety measure because userspace should not > > + * be able to set HDCP registers. > > + */ > > + if (reg >= DW_HDMI_HDCP_CTRL && reg <= DW_HDMI_HDCP_STS) > > + return true; > > + if (reg == DW_HDMI_HDCP22_CONTROL) > > + return true; > > + if (reg == DW_HDMI_HDCP22_STATUS) > > + return true; > > + return false; > > +} > > + > > +static int dw_hdmi_g_register(struct v4l2_subdev *sd, > > + struct v4l2_dbg_register *reg) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + > > + switch (reg->reg >> 15) { > > + case 0: /* Controller core read */ > > + if (dw_hdmi_is_reserved_register(dw_dev, reg->reg & 0x7fff)) > > + return -EINVAL; > > + > > + reg->size = 4; > > + reg->val = hdmi_readl(dw_dev, reg->reg & 0x7fff); > > + return 0; > > + case 1: /* Debug registers */ > > + reg->size = 4; > > + > > + switch (reg->reg & GENMASK(14, 0)) { > > + case 0: > > + reg->val = dw_dev->tmds_valid_wait_count; > > + return 0; > > + case 1: > > + reg->val = dw_dev->state; > > + return 0; > > + case 2: > > + reg->val = dw_dev->phy_eq_on; > > + return 0; > > + case 3: > > + reg->val = dw_dev->config->phy->version; > > + return 0; > > + case 4: > > + reg->val = dw_dev->video_stable_wait_ms; > > + return 0; > > + case 5: > > + reg->val = dw_dev->has_clock_wait_ms; > > + return 0; > > + case 6: > > + reg->val = dw_dev->config->iref_clk; > > + return 0; > > + case 7: > > + reg->val = dw_dev->reset_datapath_enable; > > + return 0; > > + case 8: > > + reg->val = dw_hdmi_get_sample_freq(dw_dev); > > + return 0; > > + case 9: > > + reg->val = dw_dev->hw_reset_on_hot_plug; > > + return 0; > > + default: > > + break; > > + } > > + break; > > + case 2: /* PHY read */ > > + if ((reg->reg & ~0xff) != BIT(16)) > > + break; > > + > > + reg->size = 2; > > + reg->val = dw_hdmi_phy_read(dw_dev, reg->reg & 0xff); > > + return 0; > > + default: > > + break; > > + } > > + > > + dw_hdmi_invalid_register(dw_dev, reg->reg); > > + return 0; > > +} > > + > > +static int dw_hdmi_s_register(struct v4l2_subdev *sd, > > + const struct v4l2_dbg_register *reg) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + > > + switch (reg->reg >> 15) { > > + case 0: /* Controller core write */ > > + if (dw_hdmi_is_reserved_register(dw_dev, reg->reg & 0x7fff)) > > + return -EINVAL; > > + > > + hdmi_writel(dw_dev, reg->val & GENMASK(31, 0), > > + reg->reg & 0x7fff); > > + return 0; > > + case 1: /* Debug registers */ > > + switch (reg->reg & GENMASK(14, 0)) { > > + case 0: > > + dw_dev->tmds_valid_wait_count = reg->val; > > + return 0; > > + case 1: > > + dw_hdmi_set_state(dw_dev, reg->val & 0xff); > > + return 0; > > + case 2: > > + dw_dev->phy_eq_on = reg->val; > > + return 0; > > + case 4: > > + dw_dev->video_stable_wait_ms = reg->val; > > + return 0; > > + case 5: > > + dw_dev->has_clock_wait_ms = reg->val; > > + return 0; > > + case 7: > > + dw_dev->reset_datapath_enable = reg->val; > > + return 0; > > + /* case 8 is read-only */ > > + case 9: > > + dw_dev->hw_reset_on_hot_plug = reg->val; > > + return 0; > > + case 400: > > + dev_warn(dw_dev->dev, "synmp debug select timeout\n"); > > + return 0; > > + default: > > + break; > > + } > > + break; > > + case 2: /* PHY write */ > > + if ((reg->reg & ~0xff) != BIT(16)) > > + break; > > + dw_hdmi_phy_write(dw_dev, reg->val & 0xffff, reg->reg & 0xff); > > + return 0; > > + default: > > + break; > > + } > > + > > + dw_hdmi_invalid_register(dw_dev, reg->reg); > > + return 0; > > +} > > +#endif > > + > > +static int dw_hdmi_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, > > + struct v4l2_event_subscription *sub) > > +{ > > + switch (sub->type) { > > + case V4L2_EVENT_SOURCE_CHANGE: > > + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); > > + default: > > + return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); > > + } > > +} > > + > > +static int dw_hdmi_registered(struct v4l2_subdev *sd) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + > > + dw_dev->registered = true; > > + return 0; > > +} > > + > > +static void dw_hdmi_unregistered(struct v4l2_subdev *sd) > > +{ > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + > > + dw_dev->registered = false; > > +} > > + > > +static const struct v4l2_subdev_core_ops dw_hdmi_sd_core_ops = { > > + .log_status = dw_hdmi_log_status, > > +#ifdef CONFIG_VIDEO_ADV_DEBUG > > + .g_register = dw_hdmi_g_register, > > + .s_register = dw_hdmi_s_register, > > +#endif > > + .subscribe_event = dw_hdmi_subscribe_event, > > + .unsubscribe_event = v4l2_event_subdev_unsubscribe, > > +}; > > + > > +static const struct v4l2_subdev_video_ops dw_hdmi_sd_video_ops = { > > + .s_routing = dw_hdmi_s_routing, > > + .g_input_status = dw_hdmi_g_input_status, > > + .g_frame_interval = dw_hdmi_g_frame_interval, > > + .s_dv_timings = dw_hdmi_s_dv_timings, > > + .g_dv_timings = dw_hdmi_g_dv_timings, > > + .query_dv_timings = dw_hdmi_query_dv_timings, > > +}; > > + > > +static const struct v4l2_subdev_pad_ops dw_hdmi_sd_pad_ops = { > > + .enum_mbus_code = dw_hdmi_enum_mbus_code, > > + .get_fmt = dw_hdmi_get_fmt, > > + .set_fmt = dw_hdmi_set_fmt, > > + .get_edid = dw_hdmi_get_edid, > > + .set_edid = dw_hdmi_set_edid, > > + .dv_timings_cap = dw_hdmi_dv_timings_cap, > > + .enum_dv_timings = dw_hdmi_enum_dv_timings, > > +}; > > + > > +static const struct v4l2_subdev_ops dw_hdmi_sd_ops = { > > + .core = &dw_hdmi_sd_core_ops, > > + .video = &dw_hdmi_sd_video_ops, > > + .pad = &dw_hdmi_sd_pad_ops, > > +}; > > + > > +static const struct v4l2_subdev_internal_ops dw_hdmi_internal_ops = { > > + .registered = dw_hdmi_registered, > > + .unregistered = dw_hdmi_unregistered, > > +}; > > + > > +static int dw_hdmi_parse_pd(struct dw_hdmi_dev *dw_dev) > > +{ > > + /* PHY address already comes from platform data */ > > + if (!dw_dev->config->phy->jtag_addr) { > > + dev_err(dw_dev->dev, "missing PHY jtag address in PD\n"); > > + return -EINVAL; > > + } > > + > > + /* clock already comes from platform data */ > > + if (!dw_dev->config->iref_clk) { > > + dev_err(dw_dev->dev, "invalid cfg clock frequency in PD\n"); > > + return -EINVAL; > > + } > > + > > + /* PHY input count already comes from platform data */ > > + if (!dw_dev->config->phy->input_count) { > > + dev_err(dw_dev->dev, "invalid PHY input count\n"); > > + return -EINVAL; > > + } > > + > > + return 0; > > +} > > + > > +static int dw_hdmi_parse_dt(struct dw_hdmi_dev *dw_dev) > > +{ > > + struct device_node *phy_node, *np = dw_dev->of_node; > > + struct device_node *port_node; > > + u32 tmp = 0; > > + int ret; > > + > > + if (!np) { > > + dev_err(dw_dev->dev, "missing DT node\n"); > > + return -EINVAL; > > + } > > + > > + /* PHY properties parsing */ > > + phy_node = dw_hdmi_get_phy_of_node(dw_dev, NULL); > > + ret = of_property_read_u32(phy_node, "reg", &tmp); > > + if (ret) { > > + dev_err(dw_dev->dev, "missing PHY jtag address in DT\n"); > > + return ret; > > + } > > + > > + dw_dev->config->phy->jtag_addr = tmp & 0xff; > > + > > + /* Get config clock value */ > > + dw_dev->clk = devm_clk_get(dw_dev->dev, "cfg"); > > + if (IS_ERR(dw_dev->clk)) { > > + dev_err(dw_dev->dev, "failed to get cfg clock\n"); > > + return PTR_ERR(dw_dev->clk); > > + } > > + > > + ret = clk_prepare_enable(dw_dev->clk); > > + if (ret) { > > + dev_err(dw_dev->dev, "failed to enable cfg clock\n"); > > + return ret; > > + } > > + > > + dw_dev->config->iref_clk = clk_get_rate(dw_dev->clk) / 1000000U; > > + if (!dw_dev->config->iref_clk) { > > + dev_err(dw_dev->dev, "invalid cfg clock frequency\n"); > > + ret = -EINVAL; > > + goto err_clk; > > + } > > + > > + /* Get PHY input count through the port children count */ > > + port_node = of_get_child_by_name(phy_node, "port"); > > + dw_dev->config->phy->input_count = of_get_child_count(port_node); > > + if (!dw_dev->config->phy->input_count) { > > + dev_err(dw_dev->dev, "invalid PHY input count\n"); > > + ret = -EINVAL; > > + goto err_clk; > > + } > > + > > + return 0; > > + > > +err_clk: > > + clk_disable_unprepare(dw_dev->clk); > > + return ret; > > +} > > + > > +static void dw_hdmi_config_ced(struct dw_hdmi_dev *dw_dev) > > +{ > > + hdmi_mask_writel(dw_dev, 0x1f, DW_HDMI_HDMI20_CONTROL, > > + DW_HDMI_CTRLCHECKEN_OFFSET, > > + DW_HDMI_VIDDATACHECKEN_MASK | > > + DW_HDMI_DATAISCHECKEN_MASK | > > + DW_HDMI_GBCHECKEN_MASK | > > + DW_HDMI_PREAMBCHECKEN_MASK | > > + DW_HDMI_CTRLCHECKEN_MASK); > > +} > > + > > +static void dw_hdmi_config_mode_recover(struct dw_hdmi_dev *dw_dev) > > +{ > > + /*NOTE: avoid instability of md_ists interrupts */ > > + > > + /* set HDMI_MODE_HYST */ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_MODE_RECOVER, > > + DW_HDMI_HDMI_MODE_HYST_OFFSET, > > + DW_HDMI_HDMI_MODE_HYST_MASK); > > + /* set DVI_MODE_HYST */ > > + hdmi_mask_writel(dw_dev, 0x8, DW_HDMI_MODE_RECOVER, > > + DW_HDMI_DVI_MODE_HYST_OFFSET, > > + DW_HDMI_DVI_MODE_HYST_MASK); > > + /* set SPIKE_FILTER_EN */ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_MODE_RECOVER, > > + DW_HDMI_SPIKE_FILTER_EN_OFFSET, > > + DW_HDMI_SPIKE_FILTER_EN_MASK); > > + > > + /* enable BCH error correction */ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_PDEC_CTRL, > > + DW_HDMI_PDEC_BCH_EN_OFFSET, > > + DW_HDMI_PDEC_BCH_EN_MASK); > > +} > > + > > +static void dw_hdmi_config_scdc(struct dw_hdmi_dev *dw_dev) > > +{ > > + u32 chlock = dw_dev->config->iref_clk * 1000; > > + > > + /* set HDMI_CHLOCK_CONFIG */ > > + hdmi_mask_writel(dw_dev, chlock, DW_HDMI_CHLOCK_CONFIG, > > + DW_HDMI_MILISECTIMERLIMIT_OFFSET, > > + DW_HDMI_MILISECTIMERLIMIT_MASK); > > +} > > + > > +static void dw_hdmi_config_ceavid(struct dw_hdmi_dev *dw_dev) > > +{ > > + /* set CEA YCC 422 IPI Mapping */ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_CEAVID_CONFIG, > > + DW_HDMI_CEAVID_YCC422_IPIMAP_OFFSET, > > + DW_HDMI_CEAVID_YCC422_IPIMAP_MASK); > > + /* set CEA YCC 420 IPI Mapping */ > > + hdmi_mask_writel(dw_dev, 0x1, DW_HDMI_CEAVID_CONFIG, > > + DW_HDMI_CEAVID_YCC420_IPIMAP_OFFSET, > > + DW_HDMI_CEAVID_YCC420_IPIMAP_MASK); > > +} > > + > > +static int dw_hdmi_initial_config(struct dw_hdmi_dev *dw_dev, u32 input) > > +{ > > + /* disable interrupts */ > > + dw_hdmi_disable_ints(dw_dev); > > + /* Disable HPD */ > > + dw_hdmi_disable_hpd(dw_dev); > > + > > + /* select PHY port */ > > + hdmi_mask_writel(dw_dev, input, DW_HDMI_PHY_CTRL, > > + DW_HDMI_PORTSELECT_OFFSET, > > + DW_HDMI_PORTSELECT_MASK); > > + > > + /* ced */ > > + dw_hdmi_config_ced(dw_dev); > > + > > + /* HDMI recover configurations */ > > + dw_hdmi_config_mode_recover(dw_dev); > > + > > + /* scdc configurations */ > > + dw_hdmi_config_scdc(dw_dev); > > + > > + /* ceavid configuration */ > > + dw_hdmi_config_ceavid(dw_dev); > > + > > + return 0; > > +} > > + > > +static int dw_hdmi_rx_probe(struct platform_device *pdev) > > +{ > > + const struct v4l2_dv_timings timings_def = DW_HDMI_DEFAULT_TIMING; > > + struct dw_hdmi_rx_pdata *pdata = pdev->dev.platform_data; > > + struct device *dev = &pdev->dev; > > + struct v4l2_ctrl_handler *hdl; > > + struct dw_hdmi_dev *dw_dev; > > + struct v4l2_subdev *sd; > > + struct resource *res; > > + u32 input_count; > > + unsigned int i; > > + int ret, irq; > > + > > + /* Resource allocation */ > > + dw_dev = devm_kzalloc(dev, sizeof(*dw_dev), GFP_KERNEL); > > + if (!dw_dev) > > + return -ENOMEM; > > + > > + /* Resource initialization */ > > + if (!pdata) { > > + dev_err(dev, "missing platform data\n"); > > + return -EINVAL; > > + } > > + > > + dw_dev->dev = dev; > > + dw_dev->config = pdata; > > + dw_dev->state = HDMI_STATE_NO_INIT; > > + dw_dev->of_node = dev->of_node; > > + dw_dev->tmds_valid_wait_count = 100; > > + dw_dev->has_clock_wait_ms = 200; > > + dw_dev->video_stable_wait_ms = 200; > > + dw_dev->reset_datapath_enable = 0xFFFFFFFF; > > + dw_dev->phy_eq_on = true; > > + dw_dev->hw_reset_on_hot_plug = 1; > > + spin_lock_init(&dw_dev->lock); > > + spin_lock_init(&dw_dev->event_lock); > > + > > + if (dw_hdmi_has_dt(dw_dev)) { > > + /* init PHY based on device tree */ > > + ret = dw_hdmi_parse_dt(dw_dev); > > + } else { > > + /* init PHY based on platform data */ > > + ret = dw_hdmi_parse_pd(dw_dev); > > + } > > + if (ret) > > + return ret; > > + > > + input_count = dw_dev->config->phy->input_count; > > + > > + dw_dev->curr_edid_blocks = > > + devm_kzalloc(dev, > > + sizeof(*dw_dev->curr_edid_blocks) * input_count, > > + GFP_KERNEL); > > + if (!dw_dev->curr_edid_blocks) > > + return -ENOMEM; > > + > > + dw_dev->input_connected = > > + devm_kzalloc(dev, > > + sizeof(*dw_dev->input_connected) * input_count, > > + GFP_KERNEL); > > + if (!dw_dev->input_connected) > > + return -ENOMEM; > > + > > + /* Deferred work */ > > + dw_dev->wq = create_singlethread_workqueue(DW_HDMI_RX_DRVNAME); > > + if (!dw_dev->wq) { > > + dev_err(dev, "failed to create workqueue\n"); > > + return -ENOMEM; > > + } > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + dw_dev->regs = devm_ioremap_resource(dev, res); > > + if (IS_ERR(dw_dev->regs)) { > > + dev_err(dev, "failed to remap resource\n"); > > + ret = PTR_ERR(dw_dev->regs); > > + goto err_wq; > > + } > > + > > + /* Disable 5V and write EDID */ > > + for (i = 0; i < input_count; i++) { > > + dw_dev->curr_edid_blocks[i] = ARRAY_SIZE(dw_hdmi_edid) / 32; > > + dw_hdmi_5v_disable(dw_dev, i); > > + if (dw_hdmi_edid_4blocks_le(dw_dev)) { > > + /* little endian representation, needs to invert */ > > + ret = dw_hdmi_update_edid(dw_dev, i, (u8 *)dw_hdmi_edid, > > + ARRAY_SIZE(dw_hdmi_edid) * > > + sizeof(u32), > > + true); > > + } else { > > + /* no need to invert */ > > + ret = dw_hdmi_update_edid(dw_dev, i, (u8 *)dw_hdmi_edid, > > + ARRAY_SIZE(dw_hdmi_edid) * > > + sizeof(u32), > > + false); > > + } > > This should be dropped, start with an empty EDID and pull the HPD low. > I agree! This will be addressed in the next patch series. > > + if (ret) > > + goto err_wq; > > + } > > + > > + dw_hdmi_initial_config(dw_dev, 0); > > + > > + irq = platform_get_irq(pdev, 0); > > + if (irq < 0) { > > + ret = irq; > > + goto err_wq; > > + } > > + > > + ret = devm_request_threaded_irq(dev, irq, NULL, dw_hdmi_irq_handler, > > + IRQF_ONESHOT, DW_HDMI_RX_DRVNAME, > > + dw_dev); > > + if (ret) > > + goto err_wq; > > + > > + /* V4L2 initialization */ > > + sd = &dw_dev->sd; > > + v4l2_subdev_init(sd, &dw_hdmi_sd_ops); > > + strscpy(sd->name, dev_name(dev), sizeof(sd->name)); > > + sd->dev = dev; > > + sd->internal_ops = &dw_hdmi_internal_ops; > > + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; > > + > > + /* Control handlers */ > > + hdl = &dw_dev->hdl; > > + v4l2_ctrl_handler_init(hdl, 1); > > + dw_dev->detect_tx_5v_ctrl = > > + v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, > > + BIT(4) - 1, 0, 0); > > + > > + sd->ctrl_handler = hdl; > > + if (hdl->error) { > > + ret = hdl->error; > > + goto err_hdl; > > + } > > + > > + /* Wait for ctrl handler register before requesting 5v interrupt */ > > + irq = platform_get_irq(pdev, 1); > > + if (irq < 0) { > > + ret = irq; > > + goto err_hdl; > > + } > > + > > + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_5v_hard_irq_handler, > > + dw_hdmi_5v_irq_handler, IRQF_ONESHOT, > > + DW_HDMI_RX_DRVNAME "-5v-handler", > > + dw_dev); > > + if (ret) > > + goto err_hdl; > > + > > + /* PHY loading */ > > + ret = dw_hdmi_phy_init(dw_dev); > > + if (ret) > > + goto err_phy_exit; > > + > > + ret = v4l2_async_register_subdev(sd); > > + if (ret) { > > + dev_err(dev, "failed to register subdev\n"); > > + goto err_phy_exit; > > + } > > + > > + /* Fill initial format settings */ > > + dw_dev->timings = timings_def; > > + dw_dev->mbus_code = MEDIA_BUS_FMT_BGR888_1X24; > > + > > + dev_set_drvdata(dev, sd); > > + dw_dev->state = HDMI_STATE_POWER_OFF; > > + dw_dev->is_hdmi2 = is_hdmi2(dw_dev); > > + dw_dev->is_scrambled = is_scrambled(dw_dev); > > + dev_info(dev, "using PHY: %s (GEN%d)\n", pdata->phy->name, > > + pdata->phy->gen); > > + dev_info(dev, "HDMI mode=%s\n", dw_dev->is_hdmi2 ? "2.x" : "1.4"); > > + > > + /* Set initial input, if any */ > > + ret = dw_hdmi_detect_tx_5v_ctrl(dw_dev); > > + if (ret) { > > + dev_err(dev, "failed to set 5V ctrl initial value\n"); > > + goto err_subdev; > > + } > > + > > + dev_info(dev, "selected_input=%d, state=%s\n", > > + dw_dev->selected_input, get_state_name(dw_dev->state)); > > + > > + for (i = 0; i < input_count; i++) > > + dw_hdmi_5v_enable(dw_dev, i); > > + > > + dev_dbg(dev, "driver probed\n"); > > + return 0; > > + > > +err_subdev: > > + v4l2_async_unregister_subdev(sd); > > +err_phy_exit: > > + dw_hdmi_phy_exit(dw_dev); > > +err_hdl: > > + v4l2_ctrl_handler_free(hdl); > > +err_wq: > > + destroy_workqueue(dw_dev->wq); > > + if (dw_dev->clk) > > + clk_disable_unprepare(dw_dev->clk); > > + > > + return ret; > > +} > > + > > +static int dw_hdmi_rx_remove(struct platform_device *pdev) > > +{ > > + struct device *dev = &pdev->dev; > > + struct v4l2_subdev *sd = dev_get_drvdata(dev); > > + struct dw_hdmi_dev *dw_dev = to_dw_dev(sd); > > + unsigned int i, input_count = dw_dev->config->phy->input_count; > > + > > + dw_hdmi_disable_ints(dw_dev); > > + dw_hdmi_disable_hpd(dw_dev); > > + dw_hdmi_disable_scdc(dw_dev); > > + dw_hdmi_power_off(dw_dev); > > + phy_power_off(dw_dev->phy); > > + for (i = 0; i < input_count; i++) > > + dw_hdmi_5v_disable(dw_dev, i); > > + flush_workqueue(dw_dev->wq); > > + destroy_workqueue(dw_dev->wq); > > + dw_hdmi_phy_exit(dw_dev); > > + v4l2_ctrl_handler_free(sd->ctrl_handler); > > + clk_disable_unprepare(dw_dev->clk); > > + dev_dbg(dev, "driver removed\n"); > > + return 0; > > +} > > + > > +static const struct of_device_id dw_hdmi_rx_id[] = { > > + { .compatible = "snps,dw-hdmi-rx" }, > > + { }, > > +}; > > +MODULE_DEVICE_TABLE(of, dw_hdmi_rx_id); > > + > > +static struct platform_driver dw_hdmi_rx_driver = { > > + .probe = dw_hdmi_rx_probe, > > + .remove = dw_hdmi_rx_remove, > > + .driver = { > > + .name = DW_HDMI_RX_DRVNAME, > > + .of_match_table = dw_hdmi_rx_id, > > + } > > +}; > > +module_platform_driver(dw_hdmi_rx_driver); > > + > > +MODULE_AUTHOR("Jose Abreu <jose.abreu@xxxxxxxxxxxx>"); > > +MODULE_AUTHOR("Nelson Costa <nelson.costa@xxxxxxxxxxxx>"); > > +MODULE_DESCRIPTION("DesignWare HDMI Receiver Driver"); > > +MODULE_LICENSE("GPL"); > > diff --git a/drivers/media/platform/dwc/dw-hdmi-rx.h b/drivers/media/platform/dwc/dw-hdmi-rx.h > > new file mode 100644 > > index 0000000..f0ea1d4 > > --- /dev/null > > +++ b/drivers/media/platform/dwc/dw-hdmi-rx.h > > @@ -0,0 +1,476 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (c) 2018 - present Synopsys, Inc. and/or its affiliates. > > + * Synopsys DesignWare HDMI Receiver controller driver > > + * > > + * Author: Jose Abreu <jose.abreu@xxxxxxxxxxxx> > > + * Author: Nelson Costa <nelson.costa@xxxxxxxxxxxx> > > + */ > > + > > +#ifndef __DW_HDMI_RX_H__ > > +#define __DW_HDMI_RX_H__ > > + > > +#include <linux/bitops.h> > > + > > +/* id_hdmi Registers */ > > +#define DW_HDMI_SETUP_CTRL 0x0000 > > +#define DW_HDMI_HOT_PLUG_DETECT_INPUT_X_MASK GENMASK(27, 24) > > +#define DW_HDMI_HOT_PLUG_DETECT_INPUT_X_OFFSET 24 > > +#define DW_HDMI_HDMIBUS_RESET_OVR_EN_MASK BIT(21) > > +#define DW_HDMI_HDMIBUS_RESET_OVR_EN_OFFSET 21 > > +#define DW_HDMI_BUS_RESET_OVR_MASK BIT(20) > > +#define DW_HDMI_BUS_RESET_OVR_OFFSET 20 > > +#define DW_HDMI_HDMI_RESET_OVR_MASK BIT(19) > > +#define DW_HDMI_HDMI_RESET_OVR_OFFSET 19 > > +#define DW_HDMI_PON_RESET_OVR_MASK BIT(18) > > +#define DW_HDMI_PON_RESET_OVR_OFFSET 18 > > +#define DW_HDMI_RESET_OVR_MASK BIT(17) > > +#define DW_HDMI_RESET_OVR_OFFSET 17 > > +#define DW_HDMI_RESET_OVR_EN_MASK BIT(16) > > +#define DW_HDMI_RESET_OVR_EN_OFFSET 16 > > +#define DW_HDMI_EQ_OSM_OVR_MASK BIT(15) > > +#define DW_HDMI_EQ_OSM_OVR_OFFSET 15 > > +#define DW_HDMI_EQ_OSM_OVR_EN_MASK BIT(14) > > +#define DW_HDMI_EQ_OSM_OVR_EN_OFFSET 14 > > +#define DW_HDMI_NOWAIT_ACTIVITY_MASK BIT(13) > > +#define DW_HDMI_NOWAIT_ACTIVITY_OFFSET 13 > > +#define DW_HDMI_EQ_CAL_TIME_MASK GENMASK(12, 7) > > +#define DW_HDMI_EQ_CAL_TIME_OFFSET 7 > > +#define DW_HDMI_USE_PLL_LOCK_MASK BIT(6) > > +#define DW_HDMI_USE_PLL_LOCK_OFFSET 6 > > +#define DW_HDMI_FORCE_STATE_MASK BIT(5) > > +#define DW_HDMI_FORCE_STATE_OFFSET 5 > > +#define DW_HDMI_TARGET_STATE_MASK GENMASK(4, 1) > > +#define DW_HDMI_TARGET_STATE_OFFSET 1 > > +#define DW_HDMI_HOT_PLUG_DETECT_MASK BIT(0) > > +#define DW_HDMI_HOT_PLUG_DETECT_OFFSET 0 > > + > > +#define DW_HDMI_PLL_LCK_STS 0x0030 > > +#define DW_HDMI_PLL_LOCKED BIT(0) > > + > > +#define DW_HDMI_MODE_RECOVER 0x0080 > > +#define DW_HDMI_SPIKE_FILTER_EN_MASK BIT(18) > > +#define DW_HDMI_SPIKE_FILTER_EN_OFFSET 18 > > +#define DW_HDMI_DVI_MODE_HYST_MASK GENMASK(17, 13) > > +#define DW_HDMI_DVI_MODE_HYST_OFFSET 13 > > +#define DW_HDMI_HDMI_MODE_HYST_MASK GENMASK(12, 8) > > +#define DW_HDMI_HDMI_MODE_HYST_OFFSET 8 > > + > > +#define DW_HDMI_CKM_EVLTM 0x0094 > > +#define DW_HDMI_LOCK_HYST_MASK GENMASK(21, 20) > > +#define DW_HDMI_LOCK_HYST_OFFSET 20 > > +#define DW_HDMI_CLK_HYST_MASK GENMASK(18, 16) > > +#define DW_HDMI_CLK_HYST_OFFSET 16 > > +#define DW_HDMI_EVAL_TIME_MASK GENMASK(15, 4) > > +#define DW_HDMI_EVAL_TIME_OFFSET 4 > > +#define DW_HDMI_CLK_MEAS_INPUT_SRC_MASK BIT(0) > > +#define DW_HDMI_CLK_MEAS_INPUT_SRC_OFFSET 0 > > + > > +#define DW_HDMI_CKM_RESULT 0x009c > > +#define DW_HDMI_CLOCK_IN_RANGE BIT(17) > > +#define DW_HDMI_FREQ_LOCKED BIT(16) > > +#define DW_HDMI_CLKRATE_MASK GENMASK(15, 0) > > +#define DW_HDMI_CLKRATE_OFFSET 0 > > + > > +#define DW_HDMI_VM_CFG_CH_0_1 0x00b0 > > +#define DW_HDMI_VM_CH1_COL_VALUE_MASK GENMASK(31, 16) > > +#define DW_HDMI_VM_CH1_COL_VALUE_OFFSET 16 > > +#define DW_HDMI_VM_CH0_COL_VALUE_MASK GENMASK(15, 0) > > +#define DW_HDMI_VM_CH0_COL_VALUE_OFFSET 0 > > + > > +#define DW_HDMI_VM_CFG_CH2 0x00b4 > > +#define DW_HDMI_VM_CH2_COL_VALUE_MASK GENMASK(15, 0) > > +#define DW_HDMI_VM_CH2_COL_VALUE_OFFSET 0 > > + > > +#define DW_HDMI_STS 0x00bc > > +#define DW_HDMI_DCM_CURRENT_MODE_MASK GENMASK(31, 28) > > +#define DW_HDMI_DCM_CURRENT_MODE_OFFSET 28 > > +#define DW_HDMI_DCM_LAST_PIXEL_PHASE_STS_MASK GENMASK(27, 24) > > +#define DW_HDMI_DCM_LAST_PIXEL_PHASE_STS_OFFSET 24 > > +#define DW_HDMI_DCM_PHASE_DIFF_CNT_MASK GENMASK(23, 16) > > +#define DW_HDMI_DCM_PH_DIFF_CNT_OVERFL BIT(15) > > +#define DW_HDMI_DCM_GCP_ZERO_FIELDS_PASS BIT(14) > > +#define DW_HDMI_CTL3_STS BIT(13) > > +#define DW_HDMI_CTL2_STS BIT(12) > > +#define DW_HDMI_CTL1_STS BIT(11) > > +#define DW_HDMI_CTL0_STS BIT(10) > > +#define DW_HDMI_VS_POL_ADJ_STS BIT(9) > > +#define DW_HDMI_HS_POL_ADJ_STS BIT(8) > > +#define DW_HDMI_RES_OVERLOAD_STS BIT(7) > > +#define DW_HDMI_DCM_CURRENT_PP_MASK GENMASK(3, 0) > > +#define DW_HDMI_DCM_CURRENT_PP_OFFSET 0 > > + > > +/* id_hdcp_1_4 Registers */ > > +#define DW_HDMI_HDCP_CTRL 0x00c0 > > +#define DW_HDMI_HDCP_STS 0x00fc > > + > > +/* id_mode_detection Registers */ > > +#define DW_HDMI_MD_HT0 0x0148 > > +#define DW_HDMI_HTOT32_CLK_MASK GENMASK(31, 16) > > +#define DW_HDMI_HTOT32_CLK_OFFSET 16 > > +#define DW_HDMI_HS_CLK_MASK GENMASK(15, 0) > > +#define DW_HDMI_HS_CLK_OFFSET 0 > > + > > +#define DW_HDMI_MD_HT1 0x014c > > +#define DW_HDMI_HTOT_PIX_MASK GENMASK(31, 16) > > +#define DW_HDMI_HTOT_PIX_OFFSET 16 > > +#define DW_HDMI_HOFS_PIX_MASK GENMASK(15, 0) > > +#define DW_HDMI_HOFS_PIX_OFFSET 0 > > + > > +#define DW_HDMI_MD_HACT_PX 0x0150 > > + > > +#define DW_HDMI_MD_VCTRL 0x0158 > > +#define DW_HDMI_V_OFFS_LIN_MODE_MASK BIT(4) > > +#define DW_HDMI_V_OFFS_LIN_MODE_OFFSET 4 > > +#define DW_HDMI_V_EDGE_MASK BIT(1) > > +#define DW_HDMI_V_EDGE_OFFSET 1 > > +#define DW_HDMI_V_MODE_MASK BIT(0) > > +#define DW_HDMI_V_MODE_OFFSET 0 > > + > > +#define DW_HDMI_MD_VOL 0x0164 > > +#define DW_HDMI_MD_VAL 0x0168 > > +#define DW_HDMI_MD_VTL 0x0170 > > + > > +#define DW_HDMI_MD_STS 0x0180 > > +#define DW_HDMI_ILACE_STS BIT(3) > > +#define DW_HDMI_DE_ACTIVITY_STS BIT(2) > > +#define DW_HDMI_VS_ACT_STS BIT(1) > > +#define DW_HDMI_HS_ACT_STS BIT(0) > > + > > +/* id_phy_configuration Registers */ > > +#define DW_HDMI_PHY_CTRL 0x02c0 > > +#define DW_HDMI_PHYSVSRETMODEZ_MASK BIT(6) > > +#define DW_HDMI_PHYSVSRETMODEZ_OFFSET 6 > > +#define DW_HDMI_CFGCLKFREQ_MASK GENMASK(5, 4) > > +#define DW_HDMI_CFGCLKFREQ_OFFSET 4 > > +#define DW_HDMI_PORTSELECT_MASK GENMASK(3, 2) > > +#define DW_HDMI_PORTSELECT_OFFSET 2 > > +#define DW_HDMI_PHYPDDQ_MASK BIT(1) > > +#define DW_HDMI_PHYPDDQ_OFFSET 1 > > +#define DW_HDMI_PHYRESET_MASK BIT(0) > > +#define DW_HDMI_PHYRESET_OFFSET 0 > > + > > +#define DW_HDMI_JTAG_CONF 0x02ec > > +#define DW_HDMI_JTAG_TAP_TCLK 0x02f0 > > + > > +#define DW_HDMI_JTAG_TAP_IN 0x02f4 > > +#define DW_HDMI_JTAG_TMS BIT(4) > > +#define DW_HDMI_JTAG_TDI BIT(0) > > + > > +#define DW_HDMI_JTAG_TAP_OUT 0x02f8 > > +#define DW_HDMI_JTAG_ADDR 0x02fc > > + > > +/* id_packet_decoder Registers */ > > +#define DW_HDMI_PDEC_CTRL 0x0300 > > +#define DW_HDMI_PFIFO_STORE_FILTER_EN_MASK BIT(31) > > +#define DW_HDMI_PFIFO_STORE_FILTER_EN_OFFSET 31 > > +#define DW_HDMI_PD_FIFO_CLR_MASK BIT(5) > > +#define DW_HDMI_PD_FIFO_CLR_OFFSET 5 > > +#define DW_HDMI_PDEC_BCH_EN_MASK BIT(0) > > +#define DW_HDMI_PDEC_BCH_EN_OFFSET 0 > > + > > +#define DW_HDMI_PDEC_ACRM_CTRL 0x0330 > > +#define DW_HDMI_DELTACTS_IRQTRIG_MASK GENMASK(4, 2) > > +#define DW_HDMI_DELTACTS_IRQTRIG_OFFSET 2 > > + > > +#define DW_HDMI_PDEC_ASP_CTRL 0x0340 > > +#define DW_HDMI_AUTO_VMUTE_MASK BIT(6) > > +#define DW_HDMI_AUTO_VMUTE_OFFSET 6 > > +#define DW_HDMI_AUTO_SPFLAT_MUTE_MASK GENMASK(5, 2) > > +#define DW_HDMI_AUTO_SPFLAT_MUTE_OFFSET 2 > > + > > +#define DW_HDMI_PDEC_STS 0x0360 > > +#define DW_HDMI_DRM_CKS_CHG BIT(31) > > +#define DW_HDMI_DRM_RCV BIT(30) > > +#define DW_HDMI_NTSCVBI_CKS_CHG BIT(29) > > +#define DW_HDMI_DVIDET BIT(28) > > +#define DW_HDMI_VSI_CKS_CHG BIT(27) > > +#define DW_HDMI_GMD_CKS_CHG BIT(26) > > +#define DW_HDMI_AIF_CKS_CHG BIT(25) > > +#define DW_HDMI_AVI_CKS_CHG BIT(24) > > +#define DW_HDMI_ACR_N_CHG BIT(23) > > +#define DW_HDMI_ACR_CTS_CHG BIT(22) > > +#define DW_HDMI_GCP_AV_MUTE_CHG BIT(21) > > +#define DW_HDMI_GMD_RCV BIT(20) > > +#define DW_HDMI_AIF_RCV BIT(19) > > +#define DW_HDMI_AVI_RCV BIT(18) > > +#define DW_HDMI_ACR_RCV BIT(17) > > +#define DW_HDMI_GCP_RCV BIT(16) > > +#define DW_HDMI_VSI_RCV BIT(15) > > +#define DW_HDMI_AMP_RCV BIT(14) > > +#define DW_HDMI_NTSCVBI_RCV BIT(13) > > +#define DW_HDMI_OBA_LAYOUT BIT(12) > > +#define DW_HDMI_AUDS_LAYOUT BIT(11) > > +#define DW_HDMI_PD_FIFO_NEW_ENTRY BIT(8) > > +#define DW_HDMI_PD_FIFO_OVERFL BIT(4) > > +#define DW_HDMI_PD_FIFO_UNDERFL BIT(3) > > +#define DW_HDMI_PD_FIFO_TH_START_PASS BIT(2) > > +#define DW_HDMI_PD_FIFO_TH_MAX_PASS BIT(1) > > +#define DW_HDMI_PD_FIFO_TH_MIN_PASS BIT(0) > > + > > +#define DW_HDMI_PDEC_VSI_PAYLOAD0 0x0368 > > +#define DW_HDMI_VSI_PAYLOAD1_HDMI_VIC_MASK GENMASK(15, 8) > > +#define DW_HDMI_VSI_PAYLOAD1_HDMI_VIC_OFFSET 8 > > + > > +#define DW_HDMI_PDEC_ACR_CTS 0x0390 > > +#define DW_HDMI_CTS_DECODED_MASK GENMASK(19, 0) > > +#define DW_HDMI_CTS_DECODED_OFFSET 0 > > + > > +#define DW_HDMI_PDEC_ACR_N 0x0394 > > +#define DW_HDMI_N_DECODED_MASK GENMASK(19, 0) > > +#define DW_HDMI_N_DECODED_OFFSET 0 > > + > > +#define DW_HDMI_PDEC_AVI_HB 0x03a0 > > +#define DW_HDMI_QUANT_RANGE_MASK GENMASK(31, 30) > > +#define DW_HDMI_QUANT_RANGE_OFFSET 30 > > +#define DW_HDMI_CONTENT_TYPE_MASK GENMASK(29, 28) > > +#define DW_HDMI_CONTENT_TYPE_OFFSET 28 > > +#define DW_HDMI_PIX_REP_FACTOR_MASK GENMASK(27, 24) > > +#define DW_HDMI_PIX_REP_FACTOR_OFFSET 24 > > + > > +#define DW_HDMI_PDEC_AVI_PB 0x03a4 > > +#define DW_HDMI_VID_IDENT_CODE_MASK GENMASK(31, 24) > > +#define DW_HDMI_VID_IDENT_CODE_OFFSET 24 > > +#define DW_HDMI_IT_CONTENT BIT(23) > > +#define DW_HDMI_EXT_COLORIMETRY_MASK GENMASK(22, 20) > > +#define DW_HDMI_EXT_COLORIMETRY_OFFSET 20 > > +#define DW_HDMI_RGB_QUANT_RANGE_MASK GENMASK(19, 18) > > +#define DW_HDMI_RGB_QUANT_RANGE_OFFSET 18 > > +#define DW_HDMI_NON_UNIF_SCALE_MASK GENMASK(17, 16) > > +#define DW_HDMI_NON_UNIF_SCALE_OFFSET 16 > > +#define DW_HDMI_COLORIMETRY_MASK GENMASK(15, 14) > > +#define DW_HDMI_COLORIMETRY_OFFSET 14 > > +#define DW_HDMI_PIC_ASPECT_RAT_MASK GENMASK(13, 12) > > +#define DW_HDMI_PIC_ASPECT_RAT_OFFSET 12 > > +#define DW_HDMI_ACT_ASPECT_RAT_MASK GENMASK(11, 8) > > +#define DW_HDMI_ACT_ASPECT_RAT_OFFSET 8 > > +#define DW_HDMI_VIDEO_FORMAT_MASK GENMASK(7, 5) > > +#define DW_HDMI_VIDEO_FORMAT_OFFSET 5 > > +#define DW_HDMI_ACT_INFO_PRESENT BIT(4) > > +#define DW_HDMI_BAR_INFO_VALID_MASK GENMASK(3, 2) > > +#define DW_HDMI_BAR_INFO_VALID_OFFSET 2 > > +#define DW_HDMI_SCAN_INFO_MASK GENMASK(1, 0) > > +#define DW_HDMI_SCAN_INFO_OFFSET 0 > > + > > +#define DW_HDMI_PDEC_AVI_PBLEN 4 > > + > > +#define DW_HDMI_PDEC_AVI_TBB 0x03a8 > > +#define DW_HDMI_PDEC_AVI_LRB 0x03ac > > +#define DW_HDMI_PDEC_AIF_HB 0x03c4 > > +#define DW_HDMI_PDEC_AIF_PB0 0x03c8 > > + > > +#define DW_HDMI_PDEC_AIF_PB1 0x03cc > > +#define DW_HDMI_LFE_PLAYBACK_LEVEL_MASK GENMASK(9, 8) > > +#define DW_HDMI_LFE_PLAYBACK_LEVEL_OFFSET 8 > > + > > +#define DW_HDMI_PDEC_AIF_PBLEN 2 > > + > > +#define DW_HDMI_PDEC_VSI_ST0 0x3e0 > > +#define DW_HDMI_IEEE_REG_ID_MASK GENMASK(23, 0) > > +#define DW_HDMI_IEEE_REG_ID_OFFSET 0 > > + > > +#define DW_HDMI_PDEC_VSI_ST1 0x3e4 > > +#define DW_HDMI_H3D_EXT_DATA_MASK GENMASK(23, 20) > > +#define DW_HDMI_H3D_EXT_DATA_OFFSET 20 > > +#define DW_HDMI_H3D_STRUCTURE_MASK GENMASK(19, 16) > > +#define DW_HDMI_H3D_STRUCTURE_OFFSET 16 > > +#define DW_HDMI_HDMI_VIC_MASK GENMASK(15, 8) > > +#define DW_HDMI_HDMI_VIC_OFFSET 8 > > +#define DW_HDMI_HDMI_VIDEO_FORMAT_MASK GENMASK(7, 5) > > +#define DW_HDMI_HDMI_VIDEO_FORMAT_OFFSET 5 > > +#define DW_HDMI_LENGTH_MASK GENMASK(4, 0) > > +#define DW_HDMI_LENGTH_OFFSET 0 > > + > > +/* id_cea_video Registers */ > > +#define DW_HDMI_CEAVID_CONFIG 0x400 > > +#define DW_HDMI_CEAVID_RST_MASK BIT(31) > > +#define DW_HDMI_CEAVID_RST_OFFSET 31 > > +#define DW_HDMI_CEAVID_YCC422_IPIMAP_MASK BIT(21) > > +#define DW_HDMI_CEAVID_YCC422_IPIMAP_OFFSET 21 > > +#define DW_HDMI_CEAVID_YCC420_IPIMAP_MASK BIT(20) > > +#define DW_HDMI_CEAVID_YCC420_IPIMAP_OFFSET 20 > > + > > +/* id_hdmi_2_0 Registers */ > > +#define DW_HDMI_HDMI20_CONTROL 0x0800 > > +#define DW_HDMI_VIDDATACHECKEN_MASK BIT(12) > > +#define DW_HDMI_VIDDATACHECKEN_OFFSET 12 > > +#define DW_HDMI_DATAISCHECKEN_MASK BIT(11) > > +#define DW_HDMI_DATAISCHECKEN_OFFSET 11 > > +#define DW_HDMI_GBCHECKEN_MASK BIT(10) > > +#define DW_HDMI_GBCHECKEN_OFFSET 10 > > +#define DW_HDMI_PREAMBCHECKEN_MASK BIT(9) > > +#define DW_HDMI_PREAMBCHECKEN_OFFSET 9 > > +#define DW_HDMI_CTRLCHECKEN_MASK BIT(8) > > +#define DW_HDMI_CTRLCHECKEN_OFFSET 8 > > + > > +#define DW_HDMI_SCDC_CONFIG 0x0808 > > +#define DW_HDMI_HPDLOW_MASK BIT(1) > > +#define DW_HDMI_HPDLOW_OFFSET 1 > > +#define DW_HDMI_POWERPROVIDED_MASK BIT(0) > > +#define DW_HDMI_POWERPROVIDED_OFFSET 0 > > + > > +#define DW_HDMI_CHLOCK_CONFIG 0x080c > > +#define DW_HDMI_MILISECTIMERLIMIT_MASK GENMASK(15, 0) > > +#define DW_HDMI_MILISECTIMERLIMIT_OFFSET 0 > > + > > +#define DW_HDMI_HDCP22_CONTROL 0x081c > > + > > +#define DW_HDMI_SCDC_REGS0 0x0820 > > +#define DW_HDMI_SCDC_SCRAMBSTATUS_MASK BIT(24) > > +#define DW_HDMI_SCDC_SCRAMBSTATUS_OFFSET 24 > > +#define DW_HDMI_SCDC_TMDSBITCLKRATIO_MASK BIT(17) > > +#define DW_HDMI_SCDC_TMDSBITCLKRATIO_OFFSET 17 > > +#define DW_HDMI_SCDC_SCRAMBEN_MASK BIT(16) > > +#define DW_HDMI_SCDC_SCRAMBEN_OFFSET 16 > > + > > +#define DW_HDMI_HDCP22_STATUS 0x08fc > > + > > +/* id_mode_detection_interrupt Registers */ > > +#define DW_HDMI_MD_IEN_CLR 0x0fc0 > > +#define DW_HDMI_MD_IEN_SET 0x0fc4 > > + > > +#define DW_HDMI_MD_ISTS 0x0fc8 > > +#define DW_HDMI_VOFS_LIN_ISTS BIT(11) > > +#define DW_HDMI_VTOT_LIN_ISTS BIT(10) > > +#define DW_HDMI_VACT_LIN_ISTS BIT(9) > > +#define DW_HDMI_VS_CLK_ISTS BIT(8) > > +#define DW_HDMI_VTOT_CLK_ISTS BIT(7) > > +#define DW_HDMI_HACT_PIX_ISTS BIT(6) > > +#define DW_HDMI_HS_CLK_ISTS BIT(5) > > +#define DW_HDMI_HTOT32_CLK_ISTS BIT(4) > > +#define DW_HDMI_ILACE_ISTS BIT(3) > > +#define DW_HDMI_DE_ACTIVITY_ISTS BIT(2) > > +#define DW_HDMI_VS_ACT_ISTS BIT(1) > > +#define DW_HDMI_HS_ACT_ISTS BIT(0) > > + > > +#define DW_HDMI_MD_IEN 0x0fcc > > +#define DW_HDMI_MD_ICLR 0x0fd0 > > +#define DW_HDMI_MD_ISET 0x0fd4 > > + > > +/* id_hdmi_interrupt Registers */ > > +#define DW_HDMI_IEN_CLR 0x0fd8 > > +#define DW_HDMI_IEN_SET 0x0fdc > > + > > +#define DW_HDMI_ISTS 0x0fe0 > > +#define DW_HDMI_I2CMP_ARBLOST_ISTS_ISTS BIT(30) > > +#define DW_HDMI_I2CMPNACK_ISTS BIT(29) > > +#define DW_HDMI_I2CMPDONE_ISTS BIT(28) > > +#define DW_HDMI_VS_THR_REACHED_ISTS BIT(27) > > +#define DW_HDMI_VSYNC_ACT_EDGE_ISTS BIT(26) > > +#define DW_HDMI_AKSV_RCV_ISTS BIT(25) > > +#define DW_HDMI_PLL_CLOCK_GATED_ISTS BIT(24) > > +#define DW_HDMI_DESER_MISAL_ISTS BIT(23) > > +#define DW_HDMI_CDSENSE_CHG_ISTS BIT(22) > > +#define DW_HDMI_CEAVID_EMPTY_ISTS BIT(21) > > +#define DW_HDMI_CEAVID_FULL_ISTS BIT(20) > > +#define DW_HDMI_SCDCTMDSCFGCHANGE_ISTS_MASK BIT(19) > > +#define DW_HDMI_SCDCTMDSCFGCHANGE_ISTS_OFFSET 19 > > +#define DW_HDMI_SCDCSCSTATUSCHANGE_ISTS BIT(18) > > +#define DW_HDMI_SCDCCFGCHANGE_ISTS BIT(17) > > +#define DW_HDMI_DCM_CURRENT_MODE_CHG_ISTS BIT(16) > > +#define DW_HDMI_DCM_PH_DIFF_CNT_OVERFL_ISTS BIT(15) > > +#define DW_HDMI_DCM_GCP_ZERO_FIELDS_PASS_ISTS BIT(14) > > +#define DW_HDMI_CTL3_CHANGE_ISTS BIT(13) > > +#define DW_HDMI_CTL2_CHANGE_ISTS BIT(12) > > +#define DW_HDMI_CTL1_CHANGE_ISTS BIT(11) > > +#define DW_HDMI_CTL0_CHANGE_ISTS BIT(10) > > +#define DW_HDMI_VS_POL_ADJ_ISTS BIT(9) > > +#define DW_HDMI_HS_POL_ADJ_ISTS BIT(8) > > +#define DW_HDMI_RES_OVERLOAD_ISTS BIT(7) > > +#define DW_HDMI_CLK_CHANGE_ISTS BIT(6) > > +#define DW_HDMI_PLL_LCK_CHG_ISTS BIT(5) > > +#define DW_HDMI_EQGAIN_DONE_ISTS BIT(4) > > +#define DW_HDMI_OFFSCAL_DONE_ISTS BIT(3) > > +#define DW_HDMI_RESCAL_DONE_ISTS BIT(2) > > +#define DW_HDMI_ACT_CHANGE_ISTS BIT(1) > > +#define DW_HDMI_STATE_REACHED_ISTS BIT(0) > > + > > +#define DW_HDMI_IEN 0x0fe4 > > +#define DW_HDMI_ICLR 0x0fe8 > > +#define DW_HDMI_ISET 0x0fec > > + > > +/* id_packet_decoder_interrupt Registers */ > > +#define DW_HDMI_PDEC_IEN_CLR 0x0f78 > > +#define DW_HDMI_PDEC_IEN_SET 0x0f7c > > + > > +#define DW_HDMI_PDEC_ISTS 0x0f80 > > +#define DW_HDMI_DRM_CKS_CHG_ISTS BIT(31) > > +#define DW_HDMI_AUD_TYPE_CHG_ISTS BIT(29) > > +#define DW_HDMI_VSI_CKS_CHG_ISTS BIT(27) > > +#define DW_HDMI_AIF_CKS_CHG_ISTS BIT(25) > > +#define DW_HDMI_AVI_CKS_CHG_ISTS BIT(24) > > +#define DW_HDMI_ACR_N_CHG_ISTS BIT(23) > > +#define DW_HDMI_ACR_CTS_CHG_ISTS BIT(22) > > +#define DW_HDMI_GCP_AV_MUTE_CHG_ISTS BIT(21) > > +#define DW_HDMI_PD_FIFO_OVERFL_ISTS BIT(4) > > +#define DW_HDMI_PD_FIFO_UNDERFL_ISTS BIT(3) > > + > > +#define DW_HDMI_PDEC_IEN 0x0f84 > > +#define DW_HDMI_PDEC_ICLR 0x0f88 > > +#define DW_HDMI_PDEC_ISET 0x0f8c > > + > > +/* id_dmi Registers */ > > +#define DW_HDMI_DMI_SW_RST 0x0ff0 > > +#define DW_HDMI_TMDS_SWRESET BIT(16) > > +#define DW_HDMI_HDCP_SWRESET BIT(8) > > +#define DW_HDMI_VID_SWRESET BIT(7) > > +#define DW_HDMI_PIXEL_SWRESET BIT(6) > > +#define DW_HDMI_CEC_SWRESET BIT(5) > > +#define DW_HDMI_AUD_SWRESET BIT(4) > > +#define DW_HDMI_BUS_SWRESET BIT(3) > > +#define DW_HDMI_HDMI_SWRESET BIT(2) > > +#define DW_HDMI_MODET_SWRESET BIT(1) > > +#define DW_HDMI_MAIN_SWRESET BIT(0) > > + > > +#define DW_HDMI_DMI_DISABLE_IF 0x0ff4 > > +#define DW_HDMI_HDMI_ENABLE_MASK BIT(2) > > +#define DW_HDMI_HDMI_ENABLE_OFFSET 2 > > + > > +/* id_cbus Registers */ > > +#define DW_HDMI_CBUSIOCTRL 0x3020 > > +#define DW_HDMI_DATAPATH_CBUSZ_MASK BIT(24) > > +#define DW_HDMI_DATAPATH_CBUSZ_OFFSET 24 > > +#define DW_HDMI_CBUS_SVSRETMODEZ_MASK BIT(16) > > +#define DW_HDMI_CBUS_SVSRETMODEZ_OFFSET 16 > > +#define DW_HDMI_CBUS_PDDQ_MASK BIT(8) > > +#define DW_HDMI_CBUS_PDDQ_OFFSET 8 > > +#define DW_HDMI_CBUS_RESET_MASK BIT(0) > > +#define DW_HDMI_CBUS_RESET_OFFSET 0 > > + > > +/* id_audio Registers */ > > +#define DW_HDMI_AUD_CTRL 0x0200 > > + > > +#define DW_HDMI_AUD_PLL_CTRL 0x0208 > > +#define DW_HDMI_PLL_LOCK_STABLE_MASK BIT(31) > > +#define DW_HDMI_PLL_LOCK_STABLE_OFFSET 31 > > + > > +#define DW_HDMI_AUD_FIFO_CTRL 0x0240 > > +#define DW_HDMI_AFIF_INIT_MASK BIT(0) > > +#define DW_HDMI_AFIF_INIT_OFFSET 0 > > + > > +#define DW_HDMI_AUD_MUTE_CTRL 0x0258 > > +#define DW_HDMI_AUD_MUTE_SEL_MASK GENMASK(6, 5) > > +#define DW_HDMI_AUD_MUTE_SEL_OFFSET 5 > > + > > +#define DW_HDMI_AUD_SAO_CTRL 0x0260 > > +#define DW_HDMI_WS_DISABLE_MASK BIT(10) > > +#define DW_HDMI_WS_DISABLE_OFFSET 10 > > +#define DW_HDMI_I2S_32_16_MASK BIT(0) > > +#define DW_HDMI_I2S_32_16_OFFSET 0 > > + > > +/* id_audio_fifo_interrupt Registers */ > > +#define DW_HDMI_AUD_FIFO_IEN_CLR 0x0fa8 > > +#define DW_HDMI_AUD_FIFO_IEN_SET 0x0fac > > + > > +#define DW_HDMI_AUD_FIFO_ISTS 0x0fb0 > > +#define DW_HDMI_AFIF_OVERFL_ISTS BIT(4) > > +#define DW_HDMI_AFIF_UNDERFL_ISTS BIT(3) > > +#define DW_HDMI_AFIF_THS_PASS_ISTS BIT(2) > > +#define DW_HDMI_AFIF_TH_MAX_ISTS BIT(1) > > +#define DW_HDMI_AFIF_TH_MIN_ISTS BIT(0) > > + > > +#define DW_HDMI_AUD_FIFO_IEN 0x0fb4 > > +#define DW_HDMI_AUD_FIFO_ICLR 0x0fb8 > > +#define DW_HDMI_AUD_FIFO_ISET 0x0fbc > > + > > +#endif /* __DW_HDMI_RX_H__ */ > > diff --git a/include/media/dwc/dw-hdmi-rx-pdata.h b/include/media/dwc/dw-hdmi-rx-pdata.h > > new file mode 100644 > > index 0000000..a2a5440 > > --- /dev/null > > +++ b/include/media/dwc/dw-hdmi-rx-pdata.h > > @@ -0,0 +1,126 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +/* > > + * Copyright (c) 2018 - present Synopsys, Inc. and/or its affiliates. > > + * Synopsys DesignWare HDMI Receiver controller platform data > > + * > > + * Author: Jose Abreu <jose.abreu@xxxxxxxxxxxx> > > + * Author: Nelson Costa <nelson.costa@xxxxxxxxxxxx> > > + */ > > + > > +#ifndef __DW_HDMI_RX_PDATA_H__ > > +#define __DW_HDMI_RX_PDATA_H__ > > + > > +#define DW_HDMI_RX_DRVNAME "dw-hdmi-rx" > > + > > +/* Notify events */ > > +#define DW_HDMI_NOTIFY_IS_OFF 1 > > +#define DW_HDMI_NOTIFY_INPUT_CHANGED 2 > > +#define DW_HDMI_NOTIFY_AUDIO_CHANGED 3 > > +#define DW_HDMI_NOTIFY_IS_STABLE 4 > > + > > +/* HDCP 1.4 */ > > +#define DW_HDMI_HDCP14_BKSV_SIZE 2 > > +#define DW_HDMI_HDCP14_KEYS_SIZE (2 * 40) > > + > > +/** > > + * struct dw_hdmi_phy_config - Phy configuration for HDMI receiver. > > + * > > + * @name: The name of the phy. > > + * > > + * @drv_name: Driver name of the phy. > > + * > > + * @gen: The generation of the phy. > > + * > > + * @version: The version of the phy. > > + * > > + * @cfg_clk: The configuration clock used for phy. > > + * > > + * @input_count: Number of input ports supported by the phy. > > + * > > + * @jtag_addr: The JTAG address of phy. > > + */ > > +struct dw_hdmi_phy_config { > > + const char *name; > > + const char *drv_name; > > + unsigned int gen; > > + unsigned int version; > > + unsigned int cfg_clk; > > + unsigned int input_count; > > + u8 jtag_addr; > > +}; > > + > > +/** > > + * struct dw_hdmi_rx_pdata - Platform Data configuration for HDMI receiver. > > + * > > + * @phy: Phy configuration parameters. > > + * > > + * @iref_clk: Configuration clock. > > + * > > + * @dw_5v_status: 5v status callback. Shall return the status of the given > > + * input, i.e. shall be true if a cable is connected to the specified input. > > + * > > + * @dw_5v_detected: 5v detected callback. Shall return the status changes of > > + * the given input, i.e. shall be true if a cable was (dis)connected to a > > + * specified input. > > + * > > + * @dw_5v_disable: 5v disable callback. Shall clear the interrupt associated > > + * with the 5v sense controller. > > + * > > + * @dw_5v_enable: 5v enable callback. Shall enable the interrupt associated with > > + * the 5v sense controller. > > + * > > + * @dw_5v_arg: Argument to be used with the 5v sense callbacks. > > + * > > + * @dw_zcal_reset: Impedance calibration reset callback. Shall be called when > > + * the impedance calibration needs to be restarted. This is used by phy driver > > + * only. > > + * > > + * @dw_zcal_done: Impedance calibration status callback. Shall return true if > > + * the impedance calibration procedure has ended. This is used by phy driver > > + * only. > > + * > > + * @dw_zcal_arg: Argument to be used with the ZCAL calibration callbacks. > > + * > > + * @dw_edid_read: EDID read callback. > > + * > > + * @dw_edid_write: EDID write callback. > > + * > > + * @dw_edid_4blocks_le: EDID byte ordering callback. > > + * > > + * @dw_edid_arg: Argument to be used with the EDID callbacks. > > + * > > + * @dw_reset_all: Reset all callback. > > + * > > + * @dw_reset_arg: Argument to be used with Reset callbacks. > > + */ > > +struct dw_hdmi_rx_pdata { > > + /* Phy configuration */ > > + struct dw_hdmi_phy_config *phy; > > + /* Controller configuration */ > > + unsigned int iref_clk; /* MHz */ > > + > > + /* 5V sense interface */ > > + bool (*dw_5v_status)(void __iomem *regs, int input); > > + bool (*dw_5v_detected)(void __iomem *regs, int input); > > + void (*dw_5v_disable)(void __iomem *regs, int input); > > + void (*dw_5v_enable)(void __iomem *regs, int input); > > + void __iomem *dw_5v_arg; > > + > > + /* Zcal interface */ > > + void (*dw_zcal_reset)(void __iomem *regs); > > + bool (*dw_zcal_done)(void __iomem *regs); > > + void __iomem *dw_zcal_arg; > > + > > + /* EDID */ > > + u32 (*dw_edid_read)(void __iomem *regs, int input, u32 offset); > > + int (*dw_edid_write)(void __iomem *regs, int input, u32 *edid, > > + int size); > > + u32 (*dw_edid_4blocks_le)(void __iomem *regs); > > + void __iomem *dw_edid_arg; > > + > > + /* Reset functions */ > > + void (*dw_reset_all)(void __iomem *regs); > > + void __iomem *dw_reset_arg; > > +}; > > + > > +#endif /* __DW_HDMI_RX_PDATA_H__ */ > > > > Regards, > > Hans Thanks! BR, Nelson Costa