162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2014
462306a36Sopenharmony_ci * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/component.h>
962306a36Sopenharmony_ci#include <linux/debugfs.h>
1062306a36Sopenharmony_ci#include <linux/hdmi.h>
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/reset.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1862306a36Sopenharmony_ci#include <drm/drm_bridge.h>
1962306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
2062306a36Sopenharmony_ci#include <drm/drm_drv.h>
2162306a36Sopenharmony_ci#include <drm/drm_edid.h>
2262306a36Sopenharmony_ci#include <drm/drm_file.h>
2362306a36Sopenharmony_ci#include <drm/drm_print.h>
2462306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <sound/hdmi-codec.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "sti_hdmi.h"
2962306a36Sopenharmony_ci#include "sti_hdmi_tx3g4c28phy.h"
3062306a36Sopenharmony_ci#include "sti_vtg.h"
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define HDMI_CFG                        0x0000
3362306a36Sopenharmony_ci#define HDMI_INT_EN                     0x0004
3462306a36Sopenharmony_ci#define HDMI_INT_STA                    0x0008
3562306a36Sopenharmony_ci#define HDMI_INT_CLR                    0x000C
3662306a36Sopenharmony_ci#define HDMI_STA                        0x0010
3762306a36Sopenharmony_ci#define HDMI_ACTIVE_VID_XMIN            0x0100
3862306a36Sopenharmony_ci#define HDMI_ACTIVE_VID_XMAX            0x0104
3962306a36Sopenharmony_ci#define HDMI_ACTIVE_VID_YMIN            0x0108
4062306a36Sopenharmony_ci#define HDMI_ACTIVE_VID_YMAX            0x010C
4162306a36Sopenharmony_ci#define HDMI_DFLT_CHL0_DAT              0x0110
4262306a36Sopenharmony_ci#define HDMI_DFLT_CHL1_DAT              0x0114
4362306a36Sopenharmony_ci#define HDMI_DFLT_CHL2_DAT              0x0118
4462306a36Sopenharmony_ci#define HDMI_AUDIO_CFG                  0x0200
4562306a36Sopenharmony_ci#define HDMI_SPDIF_FIFO_STATUS          0x0204
4662306a36Sopenharmony_ci#define HDMI_SW_DI_1_HEAD_WORD          0x0210
4762306a36Sopenharmony_ci#define HDMI_SW_DI_1_PKT_WORD0          0x0214
4862306a36Sopenharmony_ci#define HDMI_SW_DI_1_PKT_WORD1          0x0218
4962306a36Sopenharmony_ci#define HDMI_SW_DI_1_PKT_WORD2          0x021C
5062306a36Sopenharmony_ci#define HDMI_SW_DI_1_PKT_WORD3          0x0220
5162306a36Sopenharmony_ci#define HDMI_SW_DI_1_PKT_WORD4          0x0224
5262306a36Sopenharmony_ci#define HDMI_SW_DI_1_PKT_WORD5          0x0228
5362306a36Sopenharmony_ci#define HDMI_SW_DI_1_PKT_WORD6          0x022C
5462306a36Sopenharmony_ci#define HDMI_SW_DI_CFG                  0x0230
5562306a36Sopenharmony_ci#define HDMI_SAMPLE_FLAT_MASK           0x0244
5662306a36Sopenharmony_ci#define HDMI_AUDN                       0x0400
5762306a36Sopenharmony_ci#define HDMI_AUD_CTS                    0x0404
5862306a36Sopenharmony_ci#define HDMI_SW_DI_2_HEAD_WORD          0x0600
5962306a36Sopenharmony_ci#define HDMI_SW_DI_2_PKT_WORD0          0x0604
6062306a36Sopenharmony_ci#define HDMI_SW_DI_2_PKT_WORD1          0x0608
6162306a36Sopenharmony_ci#define HDMI_SW_DI_2_PKT_WORD2          0x060C
6262306a36Sopenharmony_ci#define HDMI_SW_DI_2_PKT_WORD3          0x0610
6362306a36Sopenharmony_ci#define HDMI_SW_DI_2_PKT_WORD4          0x0614
6462306a36Sopenharmony_ci#define HDMI_SW_DI_2_PKT_WORD5          0x0618
6562306a36Sopenharmony_ci#define HDMI_SW_DI_2_PKT_WORD6          0x061C
6662306a36Sopenharmony_ci#define HDMI_SW_DI_3_HEAD_WORD          0x0620
6762306a36Sopenharmony_ci#define HDMI_SW_DI_3_PKT_WORD0          0x0624
6862306a36Sopenharmony_ci#define HDMI_SW_DI_3_PKT_WORD1          0x0628
6962306a36Sopenharmony_ci#define HDMI_SW_DI_3_PKT_WORD2          0x062C
7062306a36Sopenharmony_ci#define HDMI_SW_DI_3_PKT_WORD3          0x0630
7162306a36Sopenharmony_ci#define HDMI_SW_DI_3_PKT_WORD4          0x0634
7262306a36Sopenharmony_ci#define HDMI_SW_DI_3_PKT_WORD5          0x0638
7362306a36Sopenharmony_ci#define HDMI_SW_DI_3_PKT_WORD6          0x063C
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define HDMI_IFRAME_SLOT_AVI            1
7662306a36Sopenharmony_ci#define HDMI_IFRAME_SLOT_AUDIO          2
7762306a36Sopenharmony_ci#define HDMI_IFRAME_SLOT_VENDOR         3
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define  XCAT(prefix, x, suffix)        prefix ## x ## suffix
8062306a36Sopenharmony_ci#define  HDMI_SW_DI_N_HEAD_WORD(x)      XCAT(HDMI_SW_DI_, x, _HEAD_WORD)
8162306a36Sopenharmony_ci#define  HDMI_SW_DI_N_PKT_WORD0(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD0)
8262306a36Sopenharmony_ci#define  HDMI_SW_DI_N_PKT_WORD1(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD1)
8362306a36Sopenharmony_ci#define  HDMI_SW_DI_N_PKT_WORD2(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD2)
8462306a36Sopenharmony_ci#define  HDMI_SW_DI_N_PKT_WORD3(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD3)
8562306a36Sopenharmony_ci#define  HDMI_SW_DI_N_PKT_WORD4(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD4)
8662306a36Sopenharmony_ci#define  HDMI_SW_DI_N_PKT_WORD5(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD5)
8762306a36Sopenharmony_ci#define  HDMI_SW_DI_N_PKT_WORD6(x)      XCAT(HDMI_SW_DI_, x, _PKT_WORD6)
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define HDMI_SW_DI_MAX_WORD             7
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define HDMI_IFRAME_DISABLED            0x0
9262306a36Sopenharmony_ci#define HDMI_IFRAME_SINGLE_SHOT         0x1
9362306a36Sopenharmony_ci#define HDMI_IFRAME_FIELD               0x2
9462306a36Sopenharmony_ci#define HDMI_IFRAME_FRAME               0x3
9562306a36Sopenharmony_ci#define HDMI_IFRAME_MASK                0x3
9662306a36Sopenharmony_ci#define HDMI_IFRAME_CFG_DI_N(x, n)       ((x) << ((n-1)*4)) /* n from 1 to 6 */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#define HDMI_CFG_DEVICE_EN              BIT(0)
9962306a36Sopenharmony_ci#define HDMI_CFG_HDMI_NOT_DVI           BIT(1)
10062306a36Sopenharmony_ci#define HDMI_CFG_HDCP_EN                BIT(2)
10162306a36Sopenharmony_ci#define HDMI_CFG_ESS_NOT_OESS           BIT(3)
10262306a36Sopenharmony_ci#define HDMI_CFG_H_SYNC_POL_NEG         BIT(4)
10362306a36Sopenharmony_ci#define HDMI_CFG_V_SYNC_POL_NEG         BIT(6)
10462306a36Sopenharmony_ci#define HDMI_CFG_422_EN                 BIT(8)
10562306a36Sopenharmony_ci#define HDMI_CFG_FIFO_OVERRUN_CLR       BIT(12)
10662306a36Sopenharmony_ci#define HDMI_CFG_FIFO_UNDERRUN_CLR      BIT(13)
10762306a36Sopenharmony_ci#define HDMI_CFG_SW_RST_EN              BIT(31)
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define HDMI_INT_GLOBAL                 BIT(0)
11062306a36Sopenharmony_ci#define HDMI_INT_SW_RST                 BIT(1)
11162306a36Sopenharmony_ci#define HDMI_INT_PIX_CAP                BIT(3)
11262306a36Sopenharmony_ci#define HDMI_INT_HOT_PLUG               BIT(4)
11362306a36Sopenharmony_ci#define HDMI_INT_DLL_LCK                BIT(5)
11462306a36Sopenharmony_ci#define HDMI_INT_NEW_FRAME              BIT(6)
11562306a36Sopenharmony_ci#define HDMI_INT_GENCTRL_PKT            BIT(7)
11662306a36Sopenharmony_ci#define HDMI_INT_AUDIO_FIFO_XRUN        BIT(8)
11762306a36Sopenharmony_ci#define HDMI_INT_SINK_TERM_PRESENT      BIT(11)
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#define HDMI_DEFAULT_INT (HDMI_INT_SINK_TERM_PRESENT \
12062306a36Sopenharmony_ci			| HDMI_INT_DLL_LCK \
12162306a36Sopenharmony_ci			| HDMI_INT_HOT_PLUG \
12262306a36Sopenharmony_ci			| HDMI_INT_GLOBAL)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define HDMI_WORKING_INT (HDMI_INT_SINK_TERM_PRESENT \
12562306a36Sopenharmony_ci			| HDMI_INT_AUDIO_FIFO_XRUN \
12662306a36Sopenharmony_ci			| HDMI_INT_GENCTRL_PKT \
12762306a36Sopenharmony_ci			| HDMI_INT_NEW_FRAME \
12862306a36Sopenharmony_ci			| HDMI_INT_DLL_LCK \
12962306a36Sopenharmony_ci			| HDMI_INT_HOT_PLUG \
13062306a36Sopenharmony_ci			| HDMI_INT_PIX_CAP \
13162306a36Sopenharmony_ci			| HDMI_INT_SW_RST \
13262306a36Sopenharmony_ci			| HDMI_INT_GLOBAL)
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci#define HDMI_STA_SW_RST                 BIT(1)
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#define HDMI_AUD_CFG_8CH		BIT(0)
13762306a36Sopenharmony_ci#define HDMI_AUD_CFG_SPDIF_DIV_2	BIT(1)
13862306a36Sopenharmony_ci#define HDMI_AUD_CFG_SPDIF_DIV_3	BIT(2)
13962306a36Sopenharmony_ci#define HDMI_AUD_CFG_SPDIF_CLK_DIV_4	(BIT(1) | BIT(2))
14062306a36Sopenharmony_ci#define HDMI_AUD_CFG_CTS_CLK_256FS	BIT(12)
14162306a36Sopenharmony_ci#define HDMI_AUD_CFG_DTS_INVALID	BIT(16)
14262306a36Sopenharmony_ci#define HDMI_AUD_CFG_ONE_BIT_INVALID	(BIT(18) | BIT(19) | BIT(20) |  BIT(21))
14362306a36Sopenharmony_ci#define HDMI_AUD_CFG_CH12_VALID	BIT(28)
14462306a36Sopenharmony_ci#define HDMI_AUD_CFG_CH34_VALID	BIT(29)
14562306a36Sopenharmony_ci#define HDMI_AUD_CFG_CH56_VALID	BIT(30)
14662306a36Sopenharmony_ci#define HDMI_AUD_CFG_CH78_VALID	BIT(31)
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* sample flat mask */
14962306a36Sopenharmony_ci#define HDMI_SAMPLE_FLAT_NO	 0
15062306a36Sopenharmony_ci#define HDMI_SAMPLE_FLAT_SP0 BIT(0)
15162306a36Sopenharmony_ci#define HDMI_SAMPLE_FLAT_SP1 BIT(1)
15262306a36Sopenharmony_ci#define HDMI_SAMPLE_FLAT_SP2 BIT(2)
15362306a36Sopenharmony_ci#define HDMI_SAMPLE_FLAT_SP3 BIT(3)
15462306a36Sopenharmony_ci#define HDMI_SAMPLE_FLAT_ALL (HDMI_SAMPLE_FLAT_SP0 | HDMI_SAMPLE_FLAT_SP1 |\
15562306a36Sopenharmony_ci			      HDMI_SAMPLE_FLAT_SP2 | HDMI_SAMPLE_FLAT_SP3)
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci#define HDMI_INFOFRAME_HEADER_TYPE(x)    (((x) & 0xff) <<  0)
15862306a36Sopenharmony_ci#define HDMI_INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) <<  8)
15962306a36Sopenharmony_ci#define HDMI_INFOFRAME_HEADER_LEN(x)     (((x) & 0x0f) << 16)
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistruct sti_hdmi_connector {
16262306a36Sopenharmony_ci	struct drm_connector drm_connector;
16362306a36Sopenharmony_ci	struct drm_encoder *encoder;
16462306a36Sopenharmony_ci	struct sti_hdmi *hdmi;
16562306a36Sopenharmony_ci	struct drm_property *colorspace_property;
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci#define to_sti_hdmi_connector(x) \
16962306a36Sopenharmony_ci	container_of(x, struct sti_hdmi_connector, drm_connector)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic const struct drm_prop_enum_list colorspace_mode_names[] = {
17262306a36Sopenharmony_ci	{ HDMI_COLORSPACE_RGB, "rgb" },
17362306a36Sopenharmony_ci	{ HDMI_COLORSPACE_YUV422, "yuv422" },
17462306a36Sopenharmony_ci	{ HDMI_COLORSPACE_YUV444, "yuv444" },
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciu32 hdmi_read(struct sti_hdmi *hdmi, int offset)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	return readl(hdmi->regs + offset);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_civoid hdmi_write(struct sti_hdmi *hdmi, u32 val, int offset)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	writel(val, hdmi->regs + offset);
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci/*
18862306a36Sopenharmony_ci * HDMI interrupt handler threaded
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * @irq: irq number
19162306a36Sopenharmony_ci * @arg: connector structure
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistatic irqreturn_t hdmi_irq_thread(int irq, void *arg)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct sti_hdmi *hdmi = arg;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* Hot plug/unplug IRQ */
19862306a36Sopenharmony_ci	if (hdmi->irq_status & HDMI_INT_HOT_PLUG) {
19962306a36Sopenharmony_ci		hdmi->hpd = readl(hdmi->regs + HDMI_STA) & HDMI_STA_HOT_PLUG;
20062306a36Sopenharmony_ci		if (hdmi->drm_dev)
20162306a36Sopenharmony_ci			drm_helper_hpd_irq_event(hdmi->drm_dev);
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/* Sw reset and PLL lock are exclusive so we can use the same
20562306a36Sopenharmony_ci	 * event to signal them
20662306a36Sopenharmony_ci	 */
20762306a36Sopenharmony_ci	if (hdmi->irq_status & (HDMI_INT_SW_RST | HDMI_INT_DLL_LCK)) {
20862306a36Sopenharmony_ci		hdmi->event_received = true;
20962306a36Sopenharmony_ci		wake_up_interruptible(&hdmi->wait_event);
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* Audio FIFO underrun IRQ */
21362306a36Sopenharmony_ci	if (hdmi->irq_status & HDMI_INT_AUDIO_FIFO_XRUN)
21462306a36Sopenharmony_ci		DRM_INFO("Warning: audio FIFO underrun occurs!\n");
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return IRQ_HANDLED;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/*
22062306a36Sopenharmony_ci * HDMI interrupt handler
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci * @irq: irq number
22362306a36Sopenharmony_ci * @arg: connector structure
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_cistatic irqreturn_t hdmi_irq(int irq, void *arg)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct sti_hdmi *hdmi = arg;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* read interrupt status */
23062306a36Sopenharmony_ci	hdmi->irq_status = hdmi_read(hdmi, HDMI_INT_STA);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* clear interrupt status */
23362306a36Sopenharmony_ci	hdmi_write(hdmi, hdmi->irq_status, HDMI_INT_CLR);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* force sync bus write */
23662306a36Sopenharmony_ci	hdmi_read(hdmi, HDMI_INT_STA);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return IRQ_WAKE_THREAD;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci/*
24262306a36Sopenharmony_ci * Set hdmi active area depending on the drm display mode selected
24362306a36Sopenharmony_ci *
24462306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_cistatic void hdmi_active_area(struct sti_hdmi *hdmi)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	u32 xmin, xmax;
24962306a36Sopenharmony_ci	u32 ymin, ymax;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	xmin = sti_vtg_get_pixel_number(hdmi->mode, 1);
25262306a36Sopenharmony_ci	xmax = sti_vtg_get_pixel_number(hdmi->mode, hdmi->mode.hdisplay);
25362306a36Sopenharmony_ci	ymin = sti_vtg_get_line_number(hdmi->mode, 0);
25462306a36Sopenharmony_ci	ymax = sti_vtg_get_line_number(hdmi->mode, hdmi->mode.vdisplay - 1);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	hdmi_write(hdmi, xmin, HDMI_ACTIVE_VID_XMIN);
25762306a36Sopenharmony_ci	hdmi_write(hdmi, xmax, HDMI_ACTIVE_VID_XMAX);
25862306a36Sopenharmony_ci	hdmi_write(hdmi, ymin, HDMI_ACTIVE_VID_YMIN);
25962306a36Sopenharmony_ci	hdmi_write(hdmi, ymax, HDMI_ACTIVE_VID_YMAX);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci/*
26362306a36Sopenharmony_ci * Overall hdmi configuration
26462306a36Sopenharmony_ci *
26562306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure
26662306a36Sopenharmony_ci */
26762306a36Sopenharmony_cistatic void hdmi_config(struct sti_hdmi *hdmi)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct drm_connector *connector = hdmi->drm_connector;
27062306a36Sopenharmony_ci	u32 conf;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Clear overrun and underrun fifo */
27562306a36Sopenharmony_ci	conf = HDMI_CFG_FIFO_OVERRUN_CLR | HDMI_CFG_FIFO_UNDERRUN_CLR;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* Select encryption type and the framing mode */
27862306a36Sopenharmony_ci	conf |= HDMI_CFG_ESS_NOT_OESS;
27962306a36Sopenharmony_ci	if (connector->display_info.is_hdmi)
28062306a36Sopenharmony_ci		conf |= HDMI_CFG_HDMI_NOT_DVI;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* Set Hsync polarity */
28362306a36Sopenharmony_ci	if (hdmi->mode.flags & DRM_MODE_FLAG_NHSYNC) {
28462306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("H Sync Negative\n");
28562306a36Sopenharmony_ci		conf |= HDMI_CFG_H_SYNC_POL_NEG;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* Set Vsync polarity */
28962306a36Sopenharmony_ci	if (hdmi->mode.flags & DRM_MODE_FLAG_NVSYNC) {
29062306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("V Sync Negative\n");
29162306a36Sopenharmony_ci		conf |= HDMI_CFG_V_SYNC_POL_NEG;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	/* Enable HDMI */
29562306a36Sopenharmony_ci	conf |= HDMI_CFG_DEVICE_EN;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	hdmi_write(hdmi, conf, HDMI_CFG);
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/*
30162306a36Sopenharmony_ci * Helper to reset info frame
30262306a36Sopenharmony_ci *
30362306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure
30462306a36Sopenharmony_ci * @slot: infoframe to reset
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_cistatic void hdmi_infoframe_reset(struct sti_hdmi *hdmi,
30762306a36Sopenharmony_ci				 u32 slot)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	u32 val, i;
31062306a36Sopenharmony_ci	u32 head_offset, pack_offset;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	switch (slot) {
31362306a36Sopenharmony_ci	case HDMI_IFRAME_SLOT_AVI:
31462306a36Sopenharmony_ci		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI);
31562306a36Sopenharmony_ci		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI);
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	case HDMI_IFRAME_SLOT_AUDIO:
31862306a36Sopenharmony_ci		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO);
31962306a36Sopenharmony_ci		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO);
32062306a36Sopenharmony_ci		break;
32162306a36Sopenharmony_ci	case HDMI_IFRAME_SLOT_VENDOR:
32262306a36Sopenharmony_ci		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR);
32362306a36Sopenharmony_ci		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR);
32462306a36Sopenharmony_ci		break;
32562306a36Sopenharmony_ci	default:
32662306a36Sopenharmony_ci		DRM_ERROR("unsupported infoframe slot: %#x\n", slot);
32762306a36Sopenharmony_ci		return;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	/* Disable transmission for the selected slot */
33162306a36Sopenharmony_ci	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
33262306a36Sopenharmony_ci	val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, slot);
33362306a36Sopenharmony_ci	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	/* Reset info frame registers */
33662306a36Sopenharmony_ci	hdmi_write(hdmi, 0x0, head_offset);
33762306a36Sopenharmony_ci	for (i = 0; i < HDMI_SW_DI_MAX_WORD; i += sizeof(u32))
33862306a36Sopenharmony_ci		hdmi_write(hdmi, 0x0, pack_offset + i);
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/*
34262306a36Sopenharmony_ci * Helper to concatenate infoframe in 32 bits word
34362306a36Sopenharmony_ci *
34462306a36Sopenharmony_ci * @ptr: pointer on the hdmi internal structure
34562306a36Sopenharmony_ci * @size: size to write
34662306a36Sopenharmony_ci */
34762306a36Sopenharmony_cistatic inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	unsigned long value = 0;
35062306a36Sopenharmony_ci	size_t i;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	for (i = size; i > 0; i--)
35362306a36Sopenharmony_ci		value = (value << 8) | ptr[i - 1];
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return value;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci/*
35962306a36Sopenharmony_ci * Helper to write info frame
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure
36262306a36Sopenharmony_ci * @data: infoframe to write
36362306a36Sopenharmony_ci * @size: size to write
36462306a36Sopenharmony_ci */
36562306a36Sopenharmony_cistatic void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi,
36662306a36Sopenharmony_ci					  const u8 *data,
36762306a36Sopenharmony_ci					  size_t size)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	const u8 *ptr = data;
37062306a36Sopenharmony_ci	u32 val, slot, mode, i;
37162306a36Sopenharmony_ci	u32 head_offset, pack_offset;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	switch (*ptr) {
37462306a36Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_AVI:
37562306a36Sopenharmony_ci		slot = HDMI_IFRAME_SLOT_AVI;
37662306a36Sopenharmony_ci		mode = HDMI_IFRAME_FIELD;
37762306a36Sopenharmony_ci		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI);
37862306a36Sopenharmony_ci		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI);
37962306a36Sopenharmony_ci		break;
38062306a36Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_AUDIO:
38162306a36Sopenharmony_ci		slot = HDMI_IFRAME_SLOT_AUDIO;
38262306a36Sopenharmony_ci		mode = HDMI_IFRAME_FRAME;
38362306a36Sopenharmony_ci		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO);
38462306a36Sopenharmony_ci		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO);
38562306a36Sopenharmony_ci		break;
38662306a36Sopenharmony_ci	case HDMI_INFOFRAME_TYPE_VENDOR:
38762306a36Sopenharmony_ci		slot = HDMI_IFRAME_SLOT_VENDOR;
38862306a36Sopenharmony_ci		mode = HDMI_IFRAME_FRAME;
38962306a36Sopenharmony_ci		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_VENDOR);
39062306a36Sopenharmony_ci		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_VENDOR);
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	default:
39362306a36Sopenharmony_ci		DRM_ERROR("unsupported infoframe type: %#x\n", *ptr);
39462306a36Sopenharmony_ci		return;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* Disable transmission slot for updated infoframe */
39862306a36Sopenharmony_ci	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
39962306a36Sopenharmony_ci	val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, slot);
40062306a36Sopenharmony_ci	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	val = HDMI_INFOFRAME_HEADER_TYPE(*ptr++);
40362306a36Sopenharmony_ci	val |= HDMI_INFOFRAME_HEADER_VERSION(*ptr++);
40462306a36Sopenharmony_ci	val |= HDMI_INFOFRAME_HEADER_LEN(*ptr++);
40562306a36Sopenharmony_ci	writel(val, hdmi->regs + head_offset);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/*
40862306a36Sopenharmony_ci	 * Each subpack contains 4 bytes
40962306a36Sopenharmony_ci	 * The First Bytes of the first subpacket must contain the checksum
41062306a36Sopenharmony_ci	 * Packet size is increase by one.
41162306a36Sopenharmony_ci	 */
41262306a36Sopenharmony_ci	size = size - HDMI_INFOFRAME_HEADER_SIZE + 1;
41362306a36Sopenharmony_ci	for (i = 0; i < size; i += sizeof(u32)) {
41462306a36Sopenharmony_ci		size_t num;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		num = min_t(size_t, size - i, sizeof(u32));
41762306a36Sopenharmony_ci		val = hdmi_infoframe_subpack(ptr, num);
41862306a36Sopenharmony_ci		ptr += sizeof(u32);
41962306a36Sopenharmony_ci		writel(val, hdmi->regs + pack_offset + i);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* Enable transmission slot for updated infoframe */
42362306a36Sopenharmony_ci	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
42462306a36Sopenharmony_ci	val |= HDMI_IFRAME_CFG_DI_N(mode, slot);
42562306a36Sopenharmony_ci	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/*
42962306a36Sopenharmony_ci * Prepare and configure the AVI infoframe
43062306a36Sopenharmony_ci *
43162306a36Sopenharmony_ci * AVI infoframe are transmitted at least once per two video field and
43262306a36Sopenharmony_ci * contains information about HDMI transmission mode such as color space,
43362306a36Sopenharmony_ci * colorimetry, ...
43462306a36Sopenharmony_ci *
43562306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure
43662306a36Sopenharmony_ci *
43762306a36Sopenharmony_ci * Return negative value if error occurs
43862306a36Sopenharmony_ci */
43962306a36Sopenharmony_cistatic int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct drm_display_mode *mode = &hdmi->mode;
44262306a36Sopenharmony_ci	struct hdmi_avi_infoframe infoframe;
44362306a36Sopenharmony_ci	u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
44462306a36Sopenharmony_ci	int ret;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	ret = drm_hdmi_avi_infoframe_from_display_mode(&infoframe,
44962306a36Sopenharmony_ci						       hdmi->drm_connector, mode);
45062306a36Sopenharmony_ci	if (ret < 0) {
45162306a36Sopenharmony_ci		DRM_ERROR("failed to setup AVI infoframe: %d\n", ret);
45262306a36Sopenharmony_ci		return ret;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	/* fixed infoframe configuration not linked to the mode */
45662306a36Sopenharmony_ci	infoframe.colorspace = hdmi->colorspace;
45762306a36Sopenharmony_ci	infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
45862306a36Sopenharmony_ci	infoframe.colorimetry = HDMI_COLORIMETRY_NONE;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	ret = hdmi_avi_infoframe_pack(&infoframe, buffer, sizeof(buffer));
46162306a36Sopenharmony_ci	if (ret < 0) {
46262306a36Sopenharmony_ci		DRM_ERROR("failed to pack AVI infoframe: %d\n", ret);
46362306a36Sopenharmony_ci		return ret;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	hdmi_infoframe_write_infopack(hdmi, buffer, ret);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return 0;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/*
47262306a36Sopenharmony_ci * Prepare and configure the AUDIO infoframe
47362306a36Sopenharmony_ci *
47462306a36Sopenharmony_ci * AUDIO infoframe are transmitted once per frame and
47562306a36Sopenharmony_ci * contains information about HDMI transmission mode such as audio codec,
47662306a36Sopenharmony_ci * sample size, ...
47762306a36Sopenharmony_ci *
47862306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure
47962306a36Sopenharmony_ci *
48062306a36Sopenharmony_ci * Return negative value if error occurs
48162306a36Sopenharmony_ci */
48262306a36Sopenharmony_cistatic int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct hdmi_audio_params *audio = &hdmi->audio;
48562306a36Sopenharmony_ci	u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)];
48662306a36Sopenharmony_ci	int ret, val;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("enter %s, AIF %s\n", __func__,
48962306a36Sopenharmony_ci			 audio->enabled ? "enable" : "disable");
49062306a36Sopenharmony_ci	if (audio->enabled) {
49162306a36Sopenharmony_ci		/* set audio parameters stored*/
49262306a36Sopenharmony_ci		ret = hdmi_audio_infoframe_pack(&audio->cea, buffer,
49362306a36Sopenharmony_ci						sizeof(buffer));
49462306a36Sopenharmony_ci		if (ret < 0) {
49562306a36Sopenharmony_ci			DRM_ERROR("failed to pack audio infoframe: %d\n", ret);
49662306a36Sopenharmony_ci			return ret;
49762306a36Sopenharmony_ci		}
49862306a36Sopenharmony_ci		hdmi_infoframe_write_infopack(hdmi, buffer, ret);
49962306a36Sopenharmony_ci	} else {
50062306a36Sopenharmony_ci		/*disable audio info frame transmission */
50162306a36Sopenharmony_ci		val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
50262306a36Sopenharmony_ci		val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK,
50362306a36Sopenharmony_ci					     HDMI_IFRAME_SLOT_AUDIO);
50462306a36Sopenharmony_ci		hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	return 0;
50862306a36Sopenharmony_ci}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci/*
51162306a36Sopenharmony_ci * Prepare and configure the VS infoframe
51262306a36Sopenharmony_ci *
51362306a36Sopenharmony_ci * Vendor Specific infoframe are transmitted once per frame and
51462306a36Sopenharmony_ci * contains vendor specific information.
51562306a36Sopenharmony_ci *
51662306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure
51762306a36Sopenharmony_ci *
51862306a36Sopenharmony_ci * Return negative value if error occurs
51962306a36Sopenharmony_ci */
52062306a36Sopenharmony_ci#define HDMI_VENDOR_INFOFRAME_MAX_SIZE 6
52162306a36Sopenharmony_cistatic int hdmi_vendor_infoframe_config(struct sti_hdmi *hdmi)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct drm_display_mode *mode = &hdmi->mode;
52462306a36Sopenharmony_ci	struct hdmi_vendor_infoframe infoframe;
52562306a36Sopenharmony_ci	u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_VENDOR_INFOFRAME_MAX_SIZE];
52662306a36Sopenharmony_ci	int ret;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	ret = drm_hdmi_vendor_infoframe_from_display_mode(&infoframe,
53162306a36Sopenharmony_ci							  hdmi->drm_connector,
53262306a36Sopenharmony_ci							  mode);
53362306a36Sopenharmony_ci	if (ret < 0) {
53462306a36Sopenharmony_ci		/*
53562306a36Sopenharmony_ci		 * Going into that statement does not means vendor infoframe
53662306a36Sopenharmony_ci		 * fails. It just informed us that vendor infoframe is not
53762306a36Sopenharmony_ci		 * needed for the selected mode. Only  4k or stereoscopic 3D
53862306a36Sopenharmony_ci		 * mode requires vendor infoframe. So just simply return 0.
53962306a36Sopenharmony_ci		 */
54062306a36Sopenharmony_ci		return 0;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	ret = hdmi_vendor_infoframe_pack(&infoframe, buffer, sizeof(buffer));
54462306a36Sopenharmony_ci	if (ret < 0) {
54562306a36Sopenharmony_ci		DRM_ERROR("failed to pack VS infoframe: %d\n", ret);
54662306a36Sopenharmony_ci		return ret;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	hdmi_infoframe_write_infopack(hdmi, buffer, ret);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	return 0;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci#define HDMI_TIMEOUT_SWRESET  100   /*milliseconds */
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci/*
55762306a36Sopenharmony_ci * Software reset of the hdmi subsystem
55862306a36Sopenharmony_ci *
55962306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure
56062306a36Sopenharmony_ci *
56162306a36Sopenharmony_ci */
56262306a36Sopenharmony_cistatic void hdmi_swreset(struct sti_hdmi *hdmi)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	u32 val;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* Enable hdmi_audio clock only during hdmi reset */
56962306a36Sopenharmony_ci	if (clk_prepare_enable(hdmi->clk_audio))
57062306a36Sopenharmony_ci		DRM_INFO("Failed to prepare/enable hdmi_audio clk\n");
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* Sw reset */
57362306a36Sopenharmony_ci	hdmi->event_received = false;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	val = hdmi_read(hdmi, HDMI_CFG);
57662306a36Sopenharmony_ci	val |= HDMI_CFG_SW_RST_EN;
57762306a36Sopenharmony_ci	hdmi_write(hdmi, val, HDMI_CFG);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	/* Wait reset completed */
58062306a36Sopenharmony_ci	wait_event_interruptible_timeout(hdmi->wait_event,
58162306a36Sopenharmony_ci					 hdmi->event_received,
58262306a36Sopenharmony_ci					 msecs_to_jiffies
58362306a36Sopenharmony_ci					 (HDMI_TIMEOUT_SWRESET));
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	/*
58662306a36Sopenharmony_ci	 * HDMI_STA_SW_RST bit is set to '1' when SW_RST bit in HDMI_CFG is
58762306a36Sopenharmony_ci	 * set to '1' and clk_audio is running.
58862306a36Sopenharmony_ci	 */
58962306a36Sopenharmony_ci	if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_SW_RST) == 0)
59062306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("Warning: HDMI sw reset timeout occurs\n");
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	val = hdmi_read(hdmi, HDMI_CFG);
59362306a36Sopenharmony_ci	val &= ~HDMI_CFG_SW_RST_EN;
59462306a36Sopenharmony_ci	hdmi_write(hdmi, val, HDMI_CFG);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	/* Disable hdmi_audio clock. Not used anymore for drm purpose */
59762306a36Sopenharmony_ci	clk_disable_unprepare(hdmi->clk_audio);
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci#define DBGFS_PRINT_STR(str1, str2) seq_printf(s, "%-24s %s\n", str1, str2)
60162306a36Sopenharmony_ci#define DBGFS_PRINT_INT(str1, int2) seq_printf(s, "%-24s %d\n", str1, int2)
60262306a36Sopenharmony_ci#define DBGFS_DUMP(str, reg) seq_printf(s, "%s  %-25s 0x%08X", str, #reg, \
60362306a36Sopenharmony_ci					hdmi_read(hdmi, reg))
60462306a36Sopenharmony_ci#define DBGFS_DUMP_DI(reg, slot) DBGFS_DUMP("\n", reg(slot))
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic void hdmi_dbg_cfg(struct seq_file *s, int val)
60762306a36Sopenharmony_ci{
60862306a36Sopenharmony_ci	int tmp;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	seq_putc(s, '\t');
61162306a36Sopenharmony_ci	tmp = val & HDMI_CFG_HDMI_NOT_DVI;
61262306a36Sopenharmony_ci	DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI");
61362306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
61462306a36Sopenharmony_ci	tmp = val & HDMI_CFG_HDCP_EN;
61562306a36Sopenharmony_ci	DBGFS_PRINT_STR("HDCP:", tmp ? "enable" : "disable");
61662306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
61762306a36Sopenharmony_ci	tmp = val & HDMI_CFG_ESS_NOT_OESS;
61862306a36Sopenharmony_ci	DBGFS_PRINT_STR("HDCP mode:", tmp ? "ESS enable" : "OESS enable");
61962306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
62062306a36Sopenharmony_ci	tmp = val & HDMI_CFG_H_SYNC_POL_NEG;
62162306a36Sopenharmony_ci	DBGFS_PRINT_STR("Hsync polarity:", tmp ? "inverted" : "normal");
62262306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
62362306a36Sopenharmony_ci	tmp = val & HDMI_CFG_V_SYNC_POL_NEG;
62462306a36Sopenharmony_ci	DBGFS_PRINT_STR("Vsync polarity:", tmp ? "inverted" : "normal");
62562306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
62662306a36Sopenharmony_ci	tmp = val & HDMI_CFG_422_EN;
62762306a36Sopenharmony_ci	DBGFS_PRINT_STR("YUV422 format:", tmp ? "enable" : "disable");
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic void hdmi_dbg_sta(struct seq_file *s, int val)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	int tmp;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	seq_putc(s, '\t');
63562306a36Sopenharmony_ci	tmp = (val & HDMI_STA_DLL_LCK);
63662306a36Sopenharmony_ci	DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked");
63762306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
63862306a36Sopenharmony_ci	tmp = (val & HDMI_STA_HOT_PLUG);
63962306a36Sopenharmony_ci	DBGFS_PRINT_STR("hdmi cable:", tmp ? "connected" : "not connected");
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic void hdmi_dbg_sw_di_cfg(struct seq_file *s, int val)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	int tmp;
64562306a36Sopenharmony_ci	char *const en_di[] = {"no transmission",
64662306a36Sopenharmony_ci			       "single transmission",
64762306a36Sopenharmony_ci			       "once every field",
64862306a36Sopenharmony_ci			       "once every frame"};
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	seq_putc(s, '\t');
65162306a36Sopenharmony_ci	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1));
65262306a36Sopenharmony_ci	DBGFS_PRINT_STR("Data island 1:", en_di[tmp]);
65362306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
65462306a36Sopenharmony_ci	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 2)) >> 4;
65562306a36Sopenharmony_ci	DBGFS_PRINT_STR("Data island 2:", en_di[tmp]);
65662306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
65762306a36Sopenharmony_ci	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 3)) >> 8;
65862306a36Sopenharmony_ci	DBGFS_PRINT_STR("Data island 3:", en_di[tmp]);
65962306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
66062306a36Sopenharmony_ci	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 4)) >> 12;
66162306a36Sopenharmony_ci	DBGFS_PRINT_STR("Data island 4:", en_di[tmp]);
66262306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
66362306a36Sopenharmony_ci	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 5)) >> 16;
66462306a36Sopenharmony_ci	DBGFS_PRINT_STR("Data island 5:", en_di[tmp]);
66562306a36Sopenharmony_ci	seq_puts(s, "\t\t\t\t\t");
66662306a36Sopenharmony_ci	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 6)) >> 20;
66762306a36Sopenharmony_ci	DBGFS_PRINT_STR("Data island 6:", en_di[tmp]);
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_cistatic int hdmi_dbg_show(struct seq_file *s, void *data)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct drm_info_node *node = s->private;
67362306a36Sopenharmony_ci	struct sti_hdmi *hdmi = (struct sti_hdmi *)node->info_ent->data;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	seq_printf(s, "HDMI: (vaddr = 0x%p)", hdmi->regs);
67662306a36Sopenharmony_ci	DBGFS_DUMP("\n", HDMI_CFG);
67762306a36Sopenharmony_ci	hdmi_dbg_cfg(s, hdmi_read(hdmi, HDMI_CFG));
67862306a36Sopenharmony_ci	DBGFS_DUMP("", HDMI_INT_EN);
67962306a36Sopenharmony_ci	DBGFS_DUMP("\n", HDMI_STA);
68062306a36Sopenharmony_ci	hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA));
68162306a36Sopenharmony_ci	DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN);
68262306a36Sopenharmony_ci	seq_putc(s, '\t');
68362306a36Sopenharmony_ci	DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN));
68462306a36Sopenharmony_ci	DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX);
68562306a36Sopenharmony_ci	seq_putc(s, '\t');
68662306a36Sopenharmony_ci	DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX));
68762306a36Sopenharmony_ci	DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN);
68862306a36Sopenharmony_ci	seq_putc(s, '\t');
68962306a36Sopenharmony_ci	DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN));
69062306a36Sopenharmony_ci	DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX);
69162306a36Sopenharmony_ci	seq_putc(s, '\t');
69262306a36Sopenharmony_ci	DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX));
69362306a36Sopenharmony_ci	DBGFS_DUMP("", HDMI_SW_DI_CFG);
69462306a36Sopenharmony_ci	hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG));
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	DBGFS_DUMP("\n", HDMI_AUDIO_CFG);
69762306a36Sopenharmony_ci	DBGFS_DUMP("\n", HDMI_SPDIF_FIFO_STATUS);
69862306a36Sopenharmony_ci	DBGFS_DUMP("\n", HDMI_AUDN);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	seq_printf(s, "\n AVI Infoframe (Data Island slot N=%d):",
70162306a36Sopenharmony_ci		   HDMI_IFRAME_SLOT_AVI);
70262306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AVI);
70362306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AVI);
70462306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_AVI);
70562306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_AVI);
70662306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_AVI);
70762306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI);
70862306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI);
70962306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI);
71062306a36Sopenharmony_ci	seq_printf(s, "\n\n AUDIO Infoframe (Data Island slot N=%d):",
71162306a36Sopenharmony_ci		   HDMI_IFRAME_SLOT_AUDIO);
71262306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO);
71362306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO);
71462306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_AUDIO);
71562306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_AUDIO);
71662306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_AUDIO);
71762306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO);
71862306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO);
71962306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO);
72062306a36Sopenharmony_ci	seq_printf(s, "\n\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
72162306a36Sopenharmony_ci		   HDMI_IFRAME_SLOT_VENDOR);
72262306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR);
72362306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR);
72462306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD1, HDMI_IFRAME_SLOT_VENDOR);
72562306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD2, HDMI_IFRAME_SLOT_VENDOR);
72662306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD3, HDMI_IFRAME_SLOT_VENDOR);
72762306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR);
72862306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR);
72962306a36Sopenharmony_ci	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR);
73062306a36Sopenharmony_ci	seq_putc(s, '\n');
73162306a36Sopenharmony_ci	return 0;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_cistatic struct drm_info_list hdmi_debugfs_files[] = {
73562306a36Sopenharmony_ci	{ "hdmi", hdmi_dbg_show, 0, NULL },
73662306a36Sopenharmony_ci};
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic void hdmi_debugfs_init(struct sti_hdmi *hdmi, struct drm_minor *minor)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	unsigned int i;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hdmi_debugfs_files); i++)
74362306a36Sopenharmony_ci		hdmi_debugfs_files[i].data = hdmi;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	drm_debugfs_create_files(hdmi_debugfs_files,
74662306a36Sopenharmony_ci				 ARRAY_SIZE(hdmi_debugfs_files),
74762306a36Sopenharmony_ci				 minor->debugfs_root, minor);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic void sti_hdmi_disable(struct drm_bridge *bridge)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct sti_hdmi *hdmi = bridge->driver_private;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	u32 val = hdmi_read(hdmi, HDMI_CFG);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	if (!hdmi->enabled)
75762306a36Sopenharmony_ci		return;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/* Disable HDMI */
76262306a36Sopenharmony_ci	val &= ~HDMI_CFG_DEVICE_EN;
76362306a36Sopenharmony_ci	hdmi_write(hdmi, val, HDMI_CFG);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	hdmi_write(hdmi, 0xffffffff, HDMI_INT_CLR);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* Stop the phy */
76862306a36Sopenharmony_ci	hdmi->phy_ops->stop(hdmi);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	/* Reset info frame transmission */
77162306a36Sopenharmony_ci	hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AVI);
77262306a36Sopenharmony_ci	hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_AUDIO);
77362306a36Sopenharmony_ci	hdmi_infoframe_reset(hdmi, HDMI_IFRAME_SLOT_VENDOR);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* Set the default channel data to be a dark red */
77662306a36Sopenharmony_ci	hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL0_DAT);
77762306a36Sopenharmony_ci	hdmi_write(hdmi, 0x0000, HDMI_DFLT_CHL1_DAT);
77862306a36Sopenharmony_ci	hdmi_write(hdmi, 0x0060, HDMI_DFLT_CHL2_DAT);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* Disable/unprepare hdmi clock */
78162306a36Sopenharmony_ci	clk_disable_unprepare(hdmi->clk_phy);
78262306a36Sopenharmony_ci	clk_disable_unprepare(hdmi->clk_tmds);
78362306a36Sopenharmony_ci	clk_disable_unprepare(hdmi->clk_pix);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	hdmi->enabled = false;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	cec_notifier_set_phys_addr(hdmi->notifier, CEC_PHYS_ADDR_INVALID);
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci/*
79162306a36Sopenharmony_ci * sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent
79262306a36Sopenharmony_ci * clocks. None-coherent clocks means that audio and TMDS clocks have not the
79362306a36Sopenharmony_ci * same source (drifts between clocks). In this case assumption is that CTS is
79462306a36Sopenharmony_ci * automatically calculated by hardware.
79562306a36Sopenharmony_ci *
79662306a36Sopenharmony_ci * @audio_fs: audio frame clock frequency in Hz
79762306a36Sopenharmony_ci *
79862306a36Sopenharmony_ci * Values computed are based on table described in HDMI specification 1.4b
79962306a36Sopenharmony_ci *
80062306a36Sopenharmony_ci * Returns n value.
80162306a36Sopenharmony_ci */
80262306a36Sopenharmony_cistatic int sti_hdmi_audio_get_non_coherent_n(unsigned int audio_fs)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	unsigned int n;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	switch (audio_fs) {
80762306a36Sopenharmony_ci	case 32000:
80862306a36Sopenharmony_ci		n = 4096;
80962306a36Sopenharmony_ci		break;
81062306a36Sopenharmony_ci	case 44100:
81162306a36Sopenharmony_ci		n = 6272;
81262306a36Sopenharmony_ci		break;
81362306a36Sopenharmony_ci	case 48000:
81462306a36Sopenharmony_ci		n = 6144;
81562306a36Sopenharmony_ci		break;
81662306a36Sopenharmony_ci	case 88200:
81762306a36Sopenharmony_ci		n = 6272 * 2;
81862306a36Sopenharmony_ci		break;
81962306a36Sopenharmony_ci	case 96000:
82062306a36Sopenharmony_ci		n = 6144 * 2;
82162306a36Sopenharmony_ci		break;
82262306a36Sopenharmony_ci	case 176400:
82362306a36Sopenharmony_ci		n = 6272 * 4;
82462306a36Sopenharmony_ci		break;
82562306a36Sopenharmony_ci	case 192000:
82662306a36Sopenharmony_ci		n = 6144 * 4;
82762306a36Sopenharmony_ci		break;
82862306a36Sopenharmony_ci	default:
82962306a36Sopenharmony_ci		/* Not pre-defined, recommended value: 128 * fs / 1000 */
83062306a36Sopenharmony_ci		n = (audio_fs * 128) / 1000;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	return n;
83462306a36Sopenharmony_ci}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int hdmi_audio_configure(struct sti_hdmi *hdmi)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	int audio_cfg, n;
83962306a36Sopenharmony_ci	struct hdmi_audio_params *params = &hdmi->audio;
84062306a36Sopenharmony_ci	struct hdmi_audio_infoframe *info = &params->cea;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	if (!hdmi->enabled)
84562306a36Sopenharmony_ci		return 0;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	/* update N parameter */
84862306a36Sopenharmony_ci	n = sti_hdmi_audio_get_non_coherent_n(params->sample_rate);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("Audio rate = %d Hz, TMDS clock = %d Hz, n = %d\n",
85162306a36Sopenharmony_ci			 params->sample_rate, hdmi->mode.clock * 1000, n);
85262306a36Sopenharmony_ci	hdmi_write(hdmi, n, HDMI_AUDN);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	/* update HDMI registers according to configuration */
85562306a36Sopenharmony_ci	audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID |
85662306a36Sopenharmony_ci		    HDMI_AUD_CFG_ONE_BIT_INVALID;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	switch (info->channels) {
85962306a36Sopenharmony_ci	case 8:
86062306a36Sopenharmony_ci		audio_cfg |= HDMI_AUD_CFG_CH78_VALID;
86162306a36Sopenharmony_ci		fallthrough;
86262306a36Sopenharmony_ci	case 6:
86362306a36Sopenharmony_ci		audio_cfg |= HDMI_AUD_CFG_CH56_VALID;
86462306a36Sopenharmony_ci		fallthrough;
86562306a36Sopenharmony_ci	case 4:
86662306a36Sopenharmony_ci		audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH;
86762306a36Sopenharmony_ci		fallthrough;
86862306a36Sopenharmony_ci	case 2:
86962306a36Sopenharmony_ci		audio_cfg |= HDMI_AUD_CFG_CH12_VALID;
87062306a36Sopenharmony_ci		break;
87162306a36Sopenharmony_ci	default:
87262306a36Sopenharmony_ci		DRM_ERROR("ERROR: Unsupported number of channels (%d)!\n",
87362306a36Sopenharmony_ci			  info->channels);
87462306a36Sopenharmony_ci		return -EINVAL;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG);
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	return hdmi_audio_infoframe_config(hdmi);
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_cistatic void sti_hdmi_pre_enable(struct drm_bridge *bridge)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct sti_hdmi *hdmi = bridge->driver_private;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	if (hdmi->enabled)
88962306a36Sopenharmony_ci		return;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/* Prepare/enable clocks */
89262306a36Sopenharmony_ci	if (clk_prepare_enable(hdmi->clk_pix))
89362306a36Sopenharmony_ci		DRM_ERROR("Failed to prepare/enable hdmi_pix clk\n");
89462306a36Sopenharmony_ci	if (clk_prepare_enable(hdmi->clk_tmds))
89562306a36Sopenharmony_ci		DRM_ERROR("Failed to prepare/enable hdmi_tmds clk\n");
89662306a36Sopenharmony_ci	if (clk_prepare_enable(hdmi->clk_phy))
89762306a36Sopenharmony_ci		DRM_ERROR("Failed to prepare/enable hdmi_rejection_pll clk\n");
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	hdmi->enabled = true;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	/* Program hdmi serializer and start phy */
90262306a36Sopenharmony_ci	if (!hdmi->phy_ops->start(hdmi)) {
90362306a36Sopenharmony_ci		DRM_ERROR("Unable to start hdmi phy\n");
90462306a36Sopenharmony_ci		return;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	/* Program hdmi active area */
90862306a36Sopenharmony_ci	hdmi_active_area(hdmi);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	/* Enable working interrupts */
91162306a36Sopenharmony_ci	hdmi_write(hdmi, HDMI_WORKING_INT, HDMI_INT_EN);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	/* Program hdmi config */
91462306a36Sopenharmony_ci	hdmi_config(hdmi);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	/* Program AVI infoframe */
91762306a36Sopenharmony_ci	if (hdmi_avi_infoframe_config(hdmi))
91862306a36Sopenharmony_ci		DRM_ERROR("Unable to configure AVI infoframe\n");
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (hdmi->audio.enabled) {
92162306a36Sopenharmony_ci		if (hdmi_audio_configure(hdmi))
92262306a36Sopenharmony_ci			DRM_ERROR("Unable to configure audio\n");
92362306a36Sopenharmony_ci	} else {
92462306a36Sopenharmony_ci		hdmi_audio_infoframe_config(hdmi);
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	/* Program VS infoframe */
92862306a36Sopenharmony_ci	if (hdmi_vendor_infoframe_config(hdmi))
92962306a36Sopenharmony_ci		DRM_ERROR("Unable to configure VS infoframe\n");
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/* Sw reset */
93262306a36Sopenharmony_ci	hdmi_swreset(hdmi);
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic void sti_hdmi_set_mode(struct drm_bridge *bridge,
93662306a36Sopenharmony_ci			      const struct drm_display_mode *mode,
93762306a36Sopenharmony_ci			      const struct drm_display_mode *adjusted_mode)
93862306a36Sopenharmony_ci{
93962306a36Sopenharmony_ci	struct sti_hdmi *hdmi = bridge->driver_private;
94062306a36Sopenharmony_ci	int ret;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	/* Copy the drm display mode in the connector local structure */
94562306a36Sopenharmony_ci	drm_mode_copy(&hdmi->mode, mode);
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	/* Update clock framerate according to the selected mode */
94862306a36Sopenharmony_ci	ret = clk_set_rate(hdmi->clk_pix, mode->clock * 1000);
94962306a36Sopenharmony_ci	if (ret < 0) {
95062306a36Sopenharmony_ci		DRM_ERROR("Cannot set rate (%dHz) for hdmi_pix clk\n",
95162306a36Sopenharmony_ci			  mode->clock * 1000);
95262306a36Sopenharmony_ci		return;
95362306a36Sopenharmony_ci	}
95462306a36Sopenharmony_ci	ret = clk_set_rate(hdmi->clk_phy, mode->clock * 1000);
95562306a36Sopenharmony_ci	if (ret < 0) {
95662306a36Sopenharmony_ci		DRM_ERROR("Cannot set rate (%dHz) for hdmi_rejection_pll clk\n",
95762306a36Sopenharmony_ci			  mode->clock * 1000);
95862306a36Sopenharmony_ci		return;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic void sti_hdmi_bridge_nope(struct drm_bridge *bridge)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	/* do nothing */
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic const struct drm_bridge_funcs sti_hdmi_bridge_funcs = {
96862306a36Sopenharmony_ci	.pre_enable = sti_hdmi_pre_enable,
96962306a36Sopenharmony_ci	.enable = sti_hdmi_bridge_nope,
97062306a36Sopenharmony_ci	.disable = sti_hdmi_disable,
97162306a36Sopenharmony_ci	.post_disable = sti_hdmi_bridge_nope,
97262306a36Sopenharmony_ci	.mode_set = sti_hdmi_set_mode,
97362306a36Sopenharmony_ci};
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic int sti_hdmi_connector_get_modes(struct drm_connector *connector)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct sti_hdmi_connector *hdmi_connector
97862306a36Sopenharmony_ci		= to_sti_hdmi_connector(connector);
97962306a36Sopenharmony_ci	struct sti_hdmi *hdmi = hdmi_connector->hdmi;
98062306a36Sopenharmony_ci	struct edid *edid;
98162306a36Sopenharmony_ci	int count;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	edid = drm_get_edid(connector, hdmi->ddc_adapt);
98662306a36Sopenharmony_ci	if (!edid)
98762306a36Sopenharmony_ci		goto fail;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	cec_notifier_set_phys_addr_from_edid(hdmi->notifier, edid);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	count = drm_add_edid_modes(connector, edid);
99262306a36Sopenharmony_ci	drm_connector_update_edid_property(connector, edid);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	DRM_DEBUG_KMS("%s : %dx%d cm\n",
99562306a36Sopenharmony_ci		      (connector->display_info.is_hdmi ? "hdmi monitor" : "dvi monitor"),
99662306a36Sopenharmony_ci		      edid->width_cm, edid->height_cm);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	kfree(edid);
99962306a36Sopenharmony_ci	return count;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cifail:
100262306a36Sopenharmony_ci	DRM_ERROR("Can't read HDMI EDID\n");
100362306a36Sopenharmony_ci	return 0;
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci#define CLK_TOLERANCE_HZ 50
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic enum drm_mode_status
100962306a36Sopenharmony_cisti_hdmi_connector_mode_valid(struct drm_connector *connector,
101062306a36Sopenharmony_ci			      struct drm_display_mode *mode)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	int target = mode->clock * 1000;
101362306a36Sopenharmony_ci	int target_min = target - CLK_TOLERANCE_HZ;
101462306a36Sopenharmony_ci	int target_max = target + CLK_TOLERANCE_HZ;
101562306a36Sopenharmony_ci	int result;
101662306a36Sopenharmony_ci	struct sti_hdmi_connector *hdmi_connector
101762306a36Sopenharmony_ci		= to_sti_hdmi_connector(connector);
101862306a36Sopenharmony_ci	struct sti_hdmi *hdmi = hdmi_connector->hdmi;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	result = clk_round_rate(hdmi->clk_pix, target);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("target rate = %d => available rate = %d\n",
102462306a36Sopenharmony_ci			 target, result);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	if ((result < target_min) || (result > target_max)) {
102762306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("hdmi pixclk=%d not supported\n", target);
102862306a36Sopenharmony_ci		return MODE_BAD;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	return MODE_OK;
103262306a36Sopenharmony_ci}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_cistatic const
103562306a36Sopenharmony_cistruct drm_connector_helper_funcs sti_hdmi_connector_helper_funcs = {
103662306a36Sopenharmony_ci	.get_modes = sti_hdmi_connector_get_modes,
103762306a36Sopenharmony_ci	.mode_valid = sti_hdmi_connector_mode_valid,
103862306a36Sopenharmony_ci};
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci/* get detection status of display device */
104162306a36Sopenharmony_cistatic enum drm_connector_status
104262306a36Sopenharmony_cisti_hdmi_connector_detect(struct drm_connector *connector, bool force)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	struct sti_hdmi_connector *hdmi_connector
104562306a36Sopenharmony_ci		= to_sti_hdmi_connector(connector);
104662306a36Sopenharmony_ci	struct sti_hdmi *hdmi = hdmi_connector->hdmi;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	if (hdmi->hpd) {
105162306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("hdmi cable connected\n");
105262306a36Sopenharmony_ci		return connector_status_connected;
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("hdmi cable disconnected\n");
105662306a36Sopenharmony_ci	cec_notifier_set_phys_addr(hdmi->notifier, CEC_PHYS_ADDR_INVALID);
105762306a36Sopenharmony_ci	return connector_status_disconnected;
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic void sti_hdmi_connector_init_property(struct drm_device *drm_dev,
106162306a36Sopenharmony_ci					     struct drm_connector *connector)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct sti_hdmi_connector *hdmi_connector
106462306a36Sopenharmony_ci		= to_sti_hdmi_connector(connector);
106562306a36Sopenharmony_ci	struct sti_hdmi *hdmi = hdmi_connector->hdmi;
106662306a36Sopenharmony_ci	struct drm_property *prop;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	/* colorspace property */
106962306a36Sopenharmony_ci	hdmi->colorspace = DEFAULT_COLORSPACE_MODE;
107062306a36Sopenharmony_ci	prop = drm_property_create_enum(drm_dev, 0, "colorspace",
107162306a36Sopenharmony_ci					colorspace_mode_names,
107262306a36Sopenharmony_ci					ARRAY_SIZE(colorspace_mode_names));
107362306a36Sopenharmony_ci	if (!prop) {
107462306a36Sopenharmony_ci		DRM_ERROR("fails to create colorspace property\n");
107562306a36Sopenharmony_ci		return;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci	hdmi_connector->colorspace_property = prop;
107862306a36Sopenharmony_ci	drm_object_attach_property(&connector->base, prop, hdmi->colorspace);
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic int
108262306a36Sopenharmony_cisti_hdmi_connector_set_property(struct drm_connector *connector,
108362306a36Sopenharmony_ci				struct drm_connector_state *state,
108462306a36Sopenharmony_ci				struct drm_property *property,
108562306a36Sopenharmony_ci				uint64_t val)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	struct sti_hdmi_connector *hdmi_connector
108862306a36Sopenharmony_ci		= to_sti_hdmi_connector(connector);
108962306a36Sopenharmony_ci	struct sti_hdmi *hdmi = hdmi_connector->hdmi;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (property == hdmi_connector->colorspace_property) {
109262306a36Sopenharmony_ci		hdmi->colorspace = val;
109362306a36Sopenharmony_ci		return 0;
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	DRM_ERROR("failed to set hdmi connector property\n");
109762306a36Sopenharmony_ci	return -EINVAL;
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic int
110162306a36Sopenharmony_cisti_hdmi_connector_get_property(struct drm_connector *connector,
110262306a36Sopenharmony_ci				const struct drm_connector_state *state,
110362306a36Sopenharmony_ci				struct drm_property *property,
110462306a36Sopenharmony_ci				uint64_t *val)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	struct sti_hdmi_connector *hdmi_connector
110762306a36Sopenharmony_ci		= to_sti_hdmi_connector(connector);
110862306a36Sopenharmony_ci	struct sti_hdmi *hdmi = hdmi_connector->hdmi;
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	if (property == hdmi_connector->colorspace_property) {
111162306a36Sopenharmony_ci		*val = hdmi->colorspace;
111262306a36Sopenharmony_ci		return 0;
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	DRM_ERROR("failed to get hdmi connector property\n");
111662306a36Sopenharmony_ci	return -EINVAL;
111762306a36Sopenharmony_ci}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_cistatic int sti_hdmi_late_register(struct drm_connector *connector)
112062306a36Sopenharmony_ci{
112162306a36Sopenharmony_ci	struct sti_hdmi_connector *hdmi_connector
112262306a36Sopenharmony_ci		= to_sti_hdmi_connector(connector);
112362306a36Sopenharmony_ci	struct sti_hdmi *hdmi = hdmi_connector->hdmi;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	hdmi_debugfs_init(hdmi, hdmi->drm_dev->primary);
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	return 0;
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_cistatic const struct drm_connector_funcs sti_hdmi_connector_funcs = {
113162306a36Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
113262306a36Sopenharmony_ci	.detect = sti_hdmi_connector_detect,
113362306a36Sopenharmony_ci	.destroy = drm_connector_cleanup,
113462306a36Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
113562306a36Sopenharmony_ci	.atomic_set_property = sti_hdmi_connector_set_property,
113662306a36Sopenharmony_ci	.atomic_get_property = sti_hdmi_connector_get_property,
113762306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
113862306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
113962306a36Sopenharmony_ci	.late_register = sti_hdmi_late_register,
114062306a36Sopenharmony_ci};
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_cistatic struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	struct drm_encoder *encoder;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
114762306a36Sopenharmony_ci		if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS)
114862306a36Sopenharmony_ci			return encoder;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	return NULL;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic void hdmi_audio_shutdown(struct device *dev, void *data)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	struct sti_hdmi *hdmi = dev_get_drvdata(dev);
115762306a36Sopenharmony_ci	int audio_cfg;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* disable audio */
116262306a36Sopenharmony_ci	audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID |
116362306a36Sopenharmony_ci		    HDMI_AUD_CFG_ONE_BIT_INVALID;
116462306a36Sopenharmony_ci	hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	hdmi->audio.enabled = false;
116762306a36Sopenharmony_ci	hdmi_audio_infoframe_config(hdmi);
116862306a36Sopenharmony_ci}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cistatic int hdmi_audio_hw_params(struct device *dev,
117162306a36Sopenharmony_ci				void *data,
117262306a36Sopenharmony_ci				struct hdmi_codec_daifmt *daifmt,
117362306a36Sopenharmony_ci				struct hdmi_codec_params *params)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct sti_hdmi *hdmi = dev_get_drvdata(dev);
117662306a36Sopenharmony_ci	int ret;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	if ((daifmt->fmt != HDMI_I2S) || daifmt->bit_clk_inv ||
118162306a36Sopenharmony_ci	    daifmt->frame_clk_inv || daifmt->bit_clk_provider ||
118262306a36Sopenharmony_ci	    daifmt->frame_clk_provider) {
118362306a36Sopenharmony_ci		dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
118462306a36Sopenharmony_ci			daifmt->bit_clk_inv, daifmt->frame_clk_inv,
118562306a36Sopenharmony_ci			daifmt->bit_clk_provider,
118662306a36Sopenharmony_ci			daifmt->frame_clk_provider);
118762306a36Sopenharmony_ci		return -EINVAL;
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	hdmi->audio.sample_width = params->sample_width;
119162306a36Sopenharmony_ci	hdmi->audio.sample_rate = params->sample_rate;
119262306a36Sopenharmony_ci	hdmi->audio.cea = params->cea;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	hdmi->audio.enabled = true;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	ret = hdmi_audio_configure(hdmi);
119762306a36Sopenharmony_ci	if (ret < 0)
119862306a36Sopenharmony_ci		return ret;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	return 0;
120162306a36Sopenharmony_ci}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_cistatic int hdmi_audio_mute(struct device *dev, void *data,
120462306a36Sopenharmony_ci			   bool enable, int direction)
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	struct sti_hdmi *hdmi = dev_get_drvdata(dev);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("%s\n", enable ? "enable" : "disable");
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	if (enable)
121162306a36Sopenharmony_ci		hdmi_write(hdmi, HDMI_SAMPLE_FLAT_ALL, HDMI_SAMPLE_FLAT_MASK);
121262306a36Sopenharmony_ci	else
121362306a36Sopenharmony_ci		hdmi_write(hdmi, HDMI_SAMPLE_FLAT_NO, HDMI_SAMPLE_FLAT_MASK);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	return 0;
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_cistatic int hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct sti_hdmi *hdmi = dev_get_drvdata(dev);
122162306a36Sopenharmony_ci	struct drm_connector *connector = hdmi->drm_connector;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
122462306a36Sopenharmony_ci	memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	return 0;
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic const struct hdmi_codec_ops audio_codec_ops = {
123062306a36Sopenharmony_ci	.hw_params = hdmi_audio_hw_params,
123162306a36Sopenharmony_ci	.audio_shutdown = hdmi_audio_shutdown,
123262306a36Sopenharmony_ci	.mute_stream = hdmi_audio_mute,
123362306a36Sopenharmony_ci	.get_eld = hdmi_audio_get_eld,
123462306a36Sopenharmony_ci	.no_capture_mute = 1,
123562306a36Sopenharmony_ci};
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic int sti_hdmi_register_audio_driver(struct device *dev,
123862306a36Sopenharmony_ci					  struct sti_hdmi *hdmi)
123962306a36Sopenharmony_ci{
124062306a36Sopenharmony_ci	struct hdmi_codec_pdata codec_data = {
124162306a36Sopenharmony_ci		.ops = &audio_codec_ops,
124262306a36Sopenharmony_ci		.max_i2s_channels = 8,
124362306a36Sopenharmony_ci		.i2s = 1,
124462306a36Sopenharmony_ci	};
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	hdmi->audio.enabled = false;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	hdmi->audio_pdev = platform_device_register_data(
125162306a36Sopenharmony_ci		dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
125262306a36Sopenharmony_ci		&codec_data, sizeof(codec_data));
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	if (IS_ERR(hdmi->audio_pdev))
125562306a36Sopenharmony_ci		return PTR_ERR(hdmi->audio_pdev);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	DRM_INFO("%s Driver bound %s\n", HDMI_CODEC_DRV_NAME, dev_name(dev));
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	return 0;
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_cistatic int sti_hdmi_bind(struct device *dev, struct device *master, void *data)
126362306a36Sopenharmony_ci{
126462306a36Sopenharmony_ci	struct sti_hdmi *hdmi = dev_get_drvdata(dev);
126562306a36Sopenharmony_ci	struct drm_device *drm_dev = data;
126662306a36Sopenharmony_ci	struct drm_encoder *encoder;
126762306a36Sopenharmony_ci	struct sti_hdmi_connector *connector;
126862306a36Sopenharmony_ci	struct cec_connector_info conn_info;
126962306a36Sopenharmony_ci	struct drm_connector *drm_connector;
127062306a36Sopenharmony_ci	struct drm_bridge *bridge;
127162306a36Sopenharmony_ci	int err;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	/* Set the drm device handle */
127462306a36Sopenharmony_ci	hdmi->drm_dev = drm_dev;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	encoder = sti_hdmi_find_encoder(drm_dev);
127762306a36Sopenharmony_ci	if (!encoder)
127862306a36Sopenharmony_ci		return -EINVAL;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	connector = devm_kzalloc(dev, sizeof(*connector), GFP_KERNEL);
128162306a36Sopenharmony_ci	if (!connector)
128262306a36Sopenharmony_ci		return -EINVAL;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	connector->hdmi = hdmi;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
128762306a36Sopenharmony_ci	if (!bridge)
128862306a36Sopenharmony_ci		return -EINVAL;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	bridge->driver_private = hdmi;
129162306a36Sopenharmony_ci	bridge->funcs = &sti_hdmi_bridge_funcs;
129262306a36Sopenharmony_ci	drm_bridge_attach(encoder, bridge, NULL, 0);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	connector->encoder = encoder;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	drm_connector = (struct drm_connector *)connector;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	drm_connector->polled = DRM_CONNECTOR_POLL_HPD;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	drm_connector_init_with_ddc(drm_dev, drm_connector,
130162306a36Sopenharmony_ci				    &sti_hdmi_connector_funcs,
130262306a36Sopenharmony_ci				    DRM_MODE_CONNECTOR_HDMIA,
130362306a36Sopenharmony_ci				    hdmi->ddc_adapt);
130462306a36Sopenharmony_ci	drm_connector_helper_add(drm_connector,
130562306a36Sopenharmony_ci			&sti_hdmi_connector_helper_funcs);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	/* initialise property */
130862306a36Sopenharmony_ci	sti_hdmi_connector_init_property(drm_dev, drm_connector);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	hdmi->drm_connector = drm_connector;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	err = drm_connector_attach_encoder(drm_connector, encoder);
131362306a36Sopenharmony_ci	if (err) {
131462306a36Sopenharmony_ci		DRM_ERROR("Failed to attach a connector to a encoder\n");
131562306a36Sopenharmony_ci		goto err_sysfs;
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	err = sti_hdmi_register_audio_driver(dev, hdmi);
131962306a36Sopenharmony_ci	if (err) {
132062306a36Sopenharmony_ci		DRM_ERROR("Failed to attach an audio codec\n");
132162306a36Sopenharmony_ci		goto err_sysfs;
132262306a36Sopenharmony_ci	}
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	/* Initialize audio infoframe */
132562306a36Sopenharmony_ci	err = hdmi_audio_infoframe_init(&hdmi->audio.cea);
132662306a36Sopenharmony_ci	if (err) {
132762306a36Sopenharmony_ci		DRM_ERROR("Failed to init audio infoframe\n");
132862306a36Sopenharmony_ci		goto err_sysfs;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	cec_fill_conn_info_from_drm(&conn_info, drm_connector);
133262306a36Sopenharmony_ci	hdmi->notifier = cec_notifier_conn_register(&hdmi->dev, NULL,
133362306a36Sopenharmony_ci						    &conn_info);
133462306a36Sopenharmony_ci	if (!hdmi->notifier) {
133562306a36Sopenharmony_ci		hdmi->drm_connector = NULL;
133662306a36Sopenharmony_ci		return -ENOMEM;
133762306a36Sopenharmony_ci	}
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	/* Enable default interrupts */
134062306a36Sopenharmony_ci	hdmi_write(hdmi, HDMI_DEFAULT_INT, HDMI_INT_EN);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	return 0;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cierr_sysfs:
134562306a36Sopenharmony_ci	hdmi->drm_connector = NULL;
134662306a36Sopenharmony_ci	return -EINVAL;
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic void sti_hdmi_unbind(struct device *dev,
135062306a36Sopenharmony_ci		struct device *master, void *data)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	struct sti_hdmi *hdmi = dev_get_drvdata(dev);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	cec_notifier_conn_unregister(hdmi->notifier);
135562306a36Sopenharmony_ci}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_cistatic const struct component_ops sti_hdmi_ops = {
135862306a36Sopenharmony_ci	.bind = sti_hdmi_bind,
135962306a36Sopenharmony_ci	.unbind = sti_hdmi_unbind,
136062306a36Sopenharmony_ci};
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_cistatic const struct of_device_id hdmi_of_match[] = {
136362306a36Sopenharmony_ci	{
136462306a36Sopenharmony_ci		.compatible = "st,stih407-hdmi",
136562306a36Sopenharmony_ci		.data = &tx3g4c28phy_ops,
136662306a36Sopenharmony_ci	}, {
136762306a36Sopenharmony_ci		/* end node */
136862306a36Sopenharmony_ci	}
136962306a36Sopenharmony_ci};
137062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, hdmi_of_match);
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_cistatic int sti_hdmi_probe(struct platform_device *pdev)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
137562306a36Sopenharmony_ci	struct sti_hdmi *hdmi;
137662306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
137762306a36Sopenharmony_ci	struct resource *res;
137862306a36Sopenharmony_ci	struct device_node *ddc;
137962306a36Sopenharmony_ci	int ret;
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	DRM_INFO("%s\n", __func__);
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
138462306a36Sopenharmony_ci	if (!hdmi)
138562306a36Sopenharmony_ci		return -ENOMEM;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	ddc = of_parse_phandle(pdev->dev.of_node, "ddc", 0);
138862306a36Sopenharmony_ci	if (ddc) {
138962306a36Sopenharmony_ci		hdmi->ddc_adapt = of_get_i2c_adapter_by_node(ddc);
139062306a36Sopenharmony_ci		of_node_put(ddc);
139162306a36Sopenharmony_ci		if (!hdmi->ddc_adapt)
139262306a36Sopenharmony_ci			return -EPROBE_DEFER;
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	hdmi->dev = pdev->dev;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	/* Get resources */
139862306a36Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hdmi-reg");
139962306a36Sopenharmony_ci	if (!res) {
140062306a36Sopenharmony_ci		DRM_ERROR("Invalid hdmi resource\n");
140162306a36Sopenharmony_ci		ret = -ENOMEM;
140262306a36Sopenharmony_ci		goto release_adapter;
140362306a36Sopenharmony_ci	}
140462306a36Sopenharmony_ci	hdmi->regs = devm_ioremap(dev, res->start, resource_size(res));
140562306a36Sopenharmony_ci	if (!hdmi->regs) {
140662306a36Sopenharmony_ci		ret = -ENOMEM;
140762306a36Sopenharmony_ci		goto release_adapter;
140862306a36Sopenharmony_ci	}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	hdmi->phy_ops = (struct hdmi_phy_ops *)
141162306a36Sopenharmony_ci		of_match_node(hdmi_of_match, np)->data;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	/* Get clock resources */
141462306a36Sopenharmony_ci	hdmi->clk_pix = devm_clk_get(dev, "pix");
141562306a36Sopenharmony_ci	if (IS_ERR(hdmi->clk_pix)) {
141662306a36Sopenharmony_ci		DRM_ERROR("Cannot get hdmi_pix clock\n");
141762306a36Sopenharmony_ci		ret = PTR_ERR(hdmi->clk_pix);
141862306a36Sopenharmony_ci		goto release_adapter;
141962306a36Sopenharmony_ci	}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	hdmi->clk_tmds = devm_clk_get(dev, "tmds");
142262306a36Sopenharmony_ci	if (IS_ERR(hdmi->clk_tmds)) {
142362306a36Sopenharmony_ci		DRM_ERROR("Cannot get hdmi_tmds clock\n");
142462306a36Sopenharmony_ci		ret = PTR_ERR(hdmi->clk_tmds);
142562306a36Sopenharmony_ci		goto release_adapter;
142662306a36Sopenharmony_ci	}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	hdmi->clk_phy = devm_clk_get(dev, "phy");
142962306a36Sopenharmony_ci	if (IS_ERR(hdmi->clk_phy)) {
143062306a36Sopenharmony_ci		DRM_ERROR("Cannot get hdmi_phy clock\n");
143162306a36Sopenharmony_ci		ret = PTR_ERR(hdmi->clk_phy);
143262306a36Sopenharmony_ci		goto release_adapter;
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	hdmi->clk_audio = devm_clk_get(dev, "audio");
143662306a36Sopenharmony_ci	if (IS_ERR(hdmi->clk_audio)) {
143762306a36Sopenharmony_ci		DRM_ERROR("Cannot get hdmi_audio clock\n");
143862306a36Sopenharmony_ci		ret = PTR_ERR(hdmi->clk_audio);
143962306a36Sopenharmony_ci		goto release_adapter;
144062306a36Sopenharmony_ci	}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	hdmi->hpd = readl(hdmi->regs + HDMI_STA) & HDMI_STA_HOT_PLUG;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	init_waitqueue_head(&hdmi->wait_event);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	hdmi->irq = platform_get_irq_byname(pdev, "irq");
144762306a36Sopenharmony_ci	if (hdmi->irq < 0) {
144862306a36Sopenharmony_ci		DRM_ERROR("Cannot get HDMI irq\n");
144962306a36Sopenharmony_ci		ret = hdmi->irq;
145062306a36Sopenharmony_ci		goto release_adapter;
145162306a36Sopenharmony_ci	}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	ret = devm_request_threaded_irq(dev, hdmi->irq, hdmi_irq,
145462306a36Sopenharmony_ci			hdmi_irq_thread, IRQF_ONESHOT, dev_name(dev), hdmi);
145562306a36Sopenharmony_ci	if (ret) {
145662306a36Sopenharmony_ci		DRM_ERROR("Failed to register HDMI interrupt\n");
145762306a36Sopenharmony_ci		goto release_adapter;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	hdmi->reset = devm_reset_control_get(dev, "hdmi");
146162306a36Sopenharmony_ci	/* Take hdmi out of reset */
146262306a36Sopenharmony_ci	if (!IS_ERR(hdmi->reset))
146362306a36Sopenharmony_ci		reset_control_deassert(hdmi->reset);
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	platform_set_drvdata(pdev, hdmi);
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return component_add(&pdev->dev, &sti_hdmi_ops);
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci release_adapter:
147062306a36Sopenharmony_ci	i2c_put_adapter(hdmi->ddc_adapt);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	return ret;
147362306a36Sopenharmony_ci}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_cistatic void sti_hdmi_remove(struct platform_device *pdev)
147662306a36Sopenharmony_ci{
147762306a36Sopenharmony_ci	struct sti_hdmi *hdmi = dev_get_drvdata(&pdev->dev);
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	i2c_put_adapter(hdmi->ddc_adapt);
148062306a36Sopenharmony_ci	if (hdmi->audio_pdev)
148162306a36Sopenharmony_ci		platform_device_unregister(hdmi->audio_pdev);
148262306a36Sopenharmony_ci	component_del(&pdev->dev, &sti_hdmi_ops);
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistruct platform_driver sti_hdmi_driver = {
148662306a36Sopenharmony_ci	.driver = {
148762306a36Sopenharmony_ci		.name = "sti-hdmi",
148862306a36Sopenharmony_ci		.owner = THIS_MODULE,
148962306a36Sopenharmony_ci		.of_match_table = hdmi_of_match,
149062306a36Sopenharmony_ci	},
149162306a36Sopenharmony_ci	.probe = sti_hdmi_probe,
149262306a36Sopenharmony_ci	.remove_new = sti_hdmi_remove,
149362306a36Sopenharmony_ci};
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ciMODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
149662306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
149762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1498