cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

meson_dw_hdmi.c (25890B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2016 BayLibre, SAS
      4 * Author: Neil Armstrong <narmstrong@baylibre.com>
      5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/component.h>
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/of_device.h>
     13#include <linux/of_graph.h>
     14#include <linux/regulator/consumer.h>
     15#include <linux/reset.h>
     16
     17#include <drm/bridge/dw_hdmi.h>
     18#include <drm/drm_atomic_helper.h>
     19#include <drm/drm_bridge.h>
     20#include <drm/drm_device.h>
     21#include <drm/drm_edid.h>
     22#include <drm/drm_probe_helper.h>
     23#include <drm/drm_print.h>
     24
     25#include <linux/videodev2.h>
     26
     27#include "meson_drv.h"
     28#include "meson_dw_hdmi.h"
     29#include "meson_registers.h"
     30
     31#define DRIVER_NAME "meson-dw-hdmi"
     32#define DRIVER_DESC "Amlogic Meson HDMI-TX DRM driver"
     33
     34/**
     35 * DOC: HDMI Output
     36 *
     37 * HDMI Output is composed of :
     38 *
     39 * - A Synopsys DesignWare HDMI Controller IP
     40 * - A TOP control block controlling the Clocks and PHY
     41 * - A custom HDMI PHY in order convert video to TMDS signal
     42 *
     43 * .. code::
     44 *
     45 *    ___________________________________
     46 *   |            HDMI TOP               |<= HPD
     47 *   |___________________________________|
     48 *   |                  |                |
     49 *   |  Synopsys HDMI   |   HDMI PHY     |=> TMDS
     50 *   |    Controller    |________________|
     51 *   |___________________________________|<=> DDC
     52 *
     53 *
     54 * The HDMI TOP block only supports HPD sensing.
     55 * The Synopsys HDMI Controller interrupt is routed
     56 * through the TOP Block interrupt.
     57 * Communication to the TOP Block and the Synopsys
     58 * HDMI Controller is done a pair of addr+read/write
     59 * registers.
     60 * The HDMI PHY is configured by registers in the
     61 * HHI register block.
     62 *
     63 * Pixel data arrives in 4:4:4 format from the VENC
     64 * block and the VPU HDMI mux selects either the ENCI
     65 * encoder for the 576i or 480i formats or the ENCP
     66 * encoder for all the other formats including
     67 * interlaced HD formats.
     68 * The VENC uses a DVI encoder on top of the ENCI
     69 * or ENCP encoders to generate DVI timings for the
     70 * HDMI controller.
     71 *
     72 * GXBB, GXL and GXM embeds the Synopsys DesignWare
     73 * HDMI TX IP version 2.01a with HDCP and I2C & S/PDIF
     74 * audio source interfaces.
     75 *
     76 * We handle the following features :
     77 *
     78 * - HPD Rise & Fall interrupt
     79 * - HDMI Controller Interrupt
     80 * - HDMI PHY Init for 480i to 1080p60
     81 * - VENC & HDMI Clock setup for 480i to 1080p60
     82 * - VENC Mode setup for 480i to 1080p60
     83 *
     84 * What is missing :
     85 *
     86 * - PHY, Clock and Mode setup for 2k && 4k modes
     87 * - SDDC Scrambling mode for HDMI 2.0a
     88 * - HDCP Setup
     89 * - CEC Management
     90 */
     91
     92/* TOP Block Communication Channel */
     93#define HDMITX_TOP_ADDR_REG	0x0
     94#define HDMITX_TOP_DATA_REG	0x4
     95#define HDMITX_TOP_CTRL_REG	0x8
     96#define HDMITX_TOP_G12A_OFFSET	0x8000
     97
     98/* Controller Communication Channel */
     99#define HDMITX_DWC_ADDR_REG	0x10
    100#define HDMITX_DWC_DATA_REG	0x14
    101#define HDMITX_DWC_CTRL_REG	0x18
    102
    103/* HHI Registers */
    104#define HHI_MEM_PD_REG0		0x100 /* 0x40 */
    105#define HHI_HDMI_CLK_CNTL	0x1cc /* 0x73 */
    106#define HHI_HDMI_PHY_CNTL0	0x3a0 /* 0xe8 */
    107#define HHI_HDMI_PHY_CNTL1	0x3a4 /* 0xe9 */
    108#define HHI_HDMI_PHY_CNTL2	0x3a8 /* 0xea */
    109#define HHI_HDMI_PHY_CNTL3	0x3ac /* 0xeb */
    110#define HHI_HDMI_PHY_CNTL4	0x3b0 /* 0xec */
    111#define HHI_HDMI_PHY_CNTL5	0x3b4 /* 0xed */
    112
    113static DEFINE_SPINLOCK(reg_lock);
    114
    115enum meson_venc_source {
    116	MESON_VENC_SOURCE_NONE = 0,
    117	MESON_VENC_SOURCE_ENCI = 1,
    118	MESON_VENC_SOURCE_ENCP = 2,
    119};
    120
    121struct meson_dw_hdmi;
    122
    123struct meson_dw_hdmi_data {
    124	unsigned int	(*top_read)(struct meson_dw_hdmi *dw_hdmi,
    125				    unsigned int addr);
    126	void		(*top_write)(struct meson_dw_hdmi *dw_hdmi,
    127				     unsigned int addr, unsigned int data);
    128	unsigned int	(*dwc_read)(struct meson_dw_hdmi *dw_hdmi,
    129				    unsigned int addr);
    130	void		(*dwc_write)(struct meson_dw_hdmi *dw_hdmi,
    131				     unsigned int addr, unsigned int data);
    132};
    133
    134struct meson_dw_hdmi {
    135	struct dw_hdmi_plat_data dw_plat_data;
    136	struct meson_drm *priv;
    137	struct device *dev;
    138	void __iomem *hdmitx;
    139	const struct meson_dw_hdmi_data *data;
    140	struct reset_control *hdmitx_apb;
    141	struct reset_control *hdmitx_ctrl;
    142	struct reset_control *hdmitx_phy;
    143	struct regulator *hdmi_supply;
    144	u32 irq_stat;
    145	struct dw_hdmi *hdmi;
    146	struct drm_bridge *bridge;
    147};
    148
    149static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi,
    150					const char *compat)
    151{
    152	return of_device_is_compatible(dw_hdmi->dev->of_node, compat);
    153}
    154
    155/* PHY (via TOP bridge) and Controller dedicated register interface */
    156
    157static unsigned int dw_hdmi_top_read(struct meson_dw_hdmi *dw_hdmi,
    158				     unsigned int addr)
    159{
    160	unsigned long flags;
    161	unsigned int data;
    162
    163	spin_lock_irqsave(&reg_lock, flags);
    164
    165	/* ADDR must be written twice */
    166	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
    167	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
    168
    169	/* Read needs a second DATA read */
    170	data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
    171	data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
    172
    173	spin_unlock_irqrestore(&reg_lock, flags);
    174
    175	return data;
    176}
    177
    178static unsigned int dw_hdmi_g12a_top_read(struct meson_dw_hdmi *dw_hdmi,
    179					  unsigned int addr)
    180{
    181	return readl(dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2));
    182}
    183
    184static inline void dw_hdmi_top_write(struct meson_dw_hdmi *dw_hdmi,
    185				     unsigned int addr, unsigned int data)
    186{
    187	unsigned long flags;
    188
    189	spin_lock_irqsave(&reg_lock, flags);
    190
    191	/* ADDR must be written twice */
    192	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
    193	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG);
    194
    195	/* Write needs single DATA write */
    196	writel(data, dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG);
    197
    198	spin_unlock_irqrestore(&reg_lock, flags);
    199}
    200
    201static inline void dw_hdmi_g12a_top_write(struct meson_dw_hdmi *dw_hdmi,
    202					  unsigned int addr, unsigned int data)
    203{
    204	writel(data, dw_hdmi->hdmitx + HDMITX_TOP_G12A_OFFSET + (addr << 2));
    205}
    206
    207/* Helper to change specific bits in PHY registers */
    208static inline void dw_hdmi_top_write_bits(struct meson_dw_hdmi *dw_hdmi,
    209					  unsigned int addr,
    210					  unsigned int mask,
    211					  unsigned int val)
    212{
    213	unsigned int data = dw_hdmi->data->top_read(dw_hdmi, addr);
    214
    215	data &= ~mask;
    216	data |= val;
    217
    218	dw_hdmi->data->top_write(dw_hdmi, addr, data);
    219}
    220
    221static unsigned int dw_hdmi_dwc_read(struct meson_dw_hdmi *dw_hdmi,
    222				     unsigned int addr)
    223{
    224	unsigned long flags;
    225	unsigned int data;
    226
    227	spin_lock_irqsave(&reg_lock, flags);
    228
    229	/* ADDR must be written twice */
    230	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
    231	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
    232
    233	/* Read needs a second DATA read */
    234	data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
    235	data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
    236
    237	spin_unlock_irqrestore(&reg_lock, flags);
    238
    239	return data;
    240}
    241
    242static unsigned int dw_hdmi_g12a_dwc_read(struct meson_dw_hdmi *dw_hdmi,
    243					  unsigned int addr)
    244{
    245	return readb(dw_hdmi->hdmitx + addr);
    246}
    247
    248static inline void dw_hdmi_dwc_write(struct meson_dw_hdmi *dw_hdmi,
    249				     unsigned int addr, unsigned int data)
    250{
    251	unsigned long flags;
    252
    253	spin_lock_irqsave(&reg_lock, flags);
    254
    255	/* ADDR must be written twice */
    256	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
    257	writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG);
    258
    259	/* Write needs single DATA write */
    260	writel(data, dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG);
    261
    262	spin_unlock_irqrestore(&reg_lock, flags);
    263}
    264
    265static inline void dw_hdmi_g12a_dwc_write(struct meson_dw_hdmi *dw_hdmi,
    266					  unsigned int addr, unsigned int data)
    267{
    268	writeb(data, dw_hdmi->hdmitx + addr);
    269}
    270
    271/* Helper to change specific bits in controller registers */
    272static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi,
    273					  unsigned int addr,
    274					  unsigned int mask,
    275					  unsigned int val)
    276{
    277	unsigned int data = dw_hdmi->data->dwc_read(dw_hdmi, addr);
    278
    279	data &= ~mask;
    280	data |= val;
    281
    282	dw_hdmi->data->dwc_write(dw_hdmi, addr, data);
    283}
    284
    285/* Bridge */
    286
    287/* Setup PHY bandwidth modes */
    288static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi,
    289				      const struct drm_display_mode *mode,
    290				      bool mode_is_420)
    291{
    292	struct meson_drm *priv = dw_hdmi->priv;
    293	unsigned int pixel_clock = mode->clock;
    294
    295	/* For 420, pixel clock is half unlike venc clock */
    296	if (mode_is_420) pixel_clock /= 2;
    297
    298	if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
    299	    dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) {
    300		if (pixel_clock >= 371250) {
    301			/* 5.94Gbps, 3.7125Gbps */
    302			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x333d3282);
    303			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2136315b);
    304		} else if (pixel_clock >= 297000) {
    305			/* 2.97Gbps */
    306			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303382);
    307			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2036315b);
    308		} else if (pixel_clock >= 148500) {
    309			/* 1.485Gbps */
    310			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303362);
    311			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2016315b);
    312		} else {
    313			/* 742.5Mbps, and below */
    314			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33604142);
    315			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x0016315b);
    316		}
    317	} else if (dw_hdmi_is_compatible(dw_hdmi,
    318					 "amlogic,meson-gxbb-dw-hdmi")) {
    319		if (pixel_clock >= 371250) {
    320			/* 5.94Gbps, 3.7125Gbps */
    321			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33353245);
    322			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2100115b);
    323		} else if (pixel_clock >= 297000) {
    324			/* 2.97Gbps */
    325			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33634283);
    326			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0xb000115b);
    327		} else {
    328			/* 1.485Gbps, and below */
    329			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33632122);
    330			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2000115b);
    331		}
    332	} else if (dw_hdmi_is_compatible(dw_hdmi,
    333					 "amlogic,meson-g12a-dw-hdmi")) {
    334		if (pixel_clock >= 371250) {
    335			/* 5.94Gbps, 3.7125Gbps */
    336			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x37eb65c4);
    337			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
    338			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x0000080b);
    339		} else if (pixel_clock >= 297000) {
    340			/* 2.97Gbps */
    341			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb6262);
    342			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
    343			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003);
    344		} else {
    345			/* 1.485Gbps, and below */
    346			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33eb4242);
    347			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2ab0ff3b);
    348			regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL5, 0x00000003);
    349		}
    350	}
    351}
    352
    353static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi)
    354{
    355	struct meson_drm *priv = dw_hdmi->priv;
    356
    357	/* Enable and software reset */
    358	regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xf);
    359
    360	mdelay(2);
    361
    362	/* Enable and unreset */
    363	regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xe);
    364
    365	mdelay(2);
    366}
    367
    368static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
    369			    const struct drm_display_info *display,
    370			    const struct drm_display_mode *mode)
    371{
    372	struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
    373	bool is_hdmi2_sink = display->hdmi.scdc.supported;
    374	struct meson_drm *priv = dw_hdmi->priv;
    375	unsigned int wr_clk =
    376		readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING));
    377	bool mode_is_420 = false;
    378
    379	DRM_DEBUG_DRIVER("\"%s\" div%d\n", mode->name,
    380			 mode->clock > 340000 ? 40 : 10);
    381
    382	if (drm_mode_is_420_only(display, mode) ||
    383	    (!is_hdmi2_sink &&
    384	     drm_mode_is_420_also(display, mode)))
    385		mode_is_420 = true;
    386
    387	/* Enable clocks */
    388	regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
    389
    390	/* Bring HDMITX MEM output of power down */
    391	regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0);
    392
    393	/* Bring out of reset */
    394	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_SW_RESET,  0);
    395
    396	/* Enable internal pixclk, tmds_clk, spdif_clk, i2s_clk, cecclk */
    397	dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL,
    398			       0x3, 0x3);
    399
    400	/* Enable cec_clk and hdcp22_tmdsclk_en */
    401	dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL,
    402			       0x3 << 4, 0x3 << 4);
    403
    404	/* Enable normal output to PHY */
    405	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
    406
    407	/* TMDS pattern setup */
    408	if (mode->clock > 340000 && !mode_is_420) {
    409		dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
    410				  0);
    411		dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
    412				  0x03ff03ff);
    413	} else {
    414		dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
    415				  0x001f001f);
    416		dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
    417				  0x001f001f);
    418	}
    419
    420	/* Load TMDS pattern */
    421	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1);
    422	msleep(20);
    423	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2);
    424
    425	/* Setup PHY parameters */
    426	meson_hdmi_phy_setup_mode(dw_hdmi, mode, mode_is_420);
    427
    428	/* Setup PHY */
    429	regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
    430			   0xffff << 16, 0x0390 << 16);
    431
    432	/* BIT_INVERT */
    433	if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
    434	    dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
    435	    dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
    436		regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
    437				   BIT(17), 0);
    438	else
    439		regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
    440				   BIT(17), BIT(17));
    441
    442	/* Disable clock, fifo, fifo_wr */
    443	regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0);
    444
    445	dw_hdmi_set_high_tmds_clock_ratio(hdmi, display);
    446
    447	msleep(100);
    448
    449	/* Reset PHY 3 times in a row */
    450	meson_dw_hdmi_phy_reset(dw_hdmi);
    451	meson_dw_hdmi_phy_reset(dw_hdmi);
    452	meson_dw_hdmi_phy_reset(dw_hdmi);
    453
    454	/* Temporary Disable VENC video stream */
    455	if (priv->venc.hdmi_use_enci)
    456		writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN));
    457	else
    458		writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
    459
    460	/* Temporary Disable HDMI video stream to HDMI-TX */
    461	writel_bits_relaxed(0x3, 0,
    462			    priv->io_base + _REG(VPU_HDMI_SETTING));
    463	writel_bits_relaxed(0xf << 8, 0,
    464			    priv->io_base + _REG(VPU_HDMI_SETTING));
    465
    466	/* Re-Enable VENC video stream */
    467	if (priv->venc.hdmi_use_enci)
    468		writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
    469	else
    470		writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
    471
    472	/* Push back HDMI clock settings */
    473	writel_bits_relaxed(0xf << 8, wr_clk & (0xf << 8),
    474			    priv->io_base + _REG(VPU_HDMI_SETTING));
    475
    476	/* Enable and Select HDMI video source for HDMI-TX */
    477	if (priv->venc.hdmi_use_enci)
    478		writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCI,
    479				    priv->io_base + _REG(VPU_HDMI_SETTING));
    480	else
    481		writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCP,
    482				    priv->io_base + _REG(VPU_HDMI_SETTING));
    483
    484	return 0;
    485}
    486
    487static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi,
    488				void *data)
    489{
    490	struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
    491	struct meson_drm *priv = dw_hdmi->priv;
    492
    493	DRM_DEBUG_DRIVER("\n");
    494
    495	regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0);
    496}
    497
    498static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi,
    499			     void *data)
    500{
    501	struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
    502
    503	return !!dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_STAT0) ?
    504		connector_status_connected : connector_status_disconnected;
    505}
    506
    507static void dw_hdmi_setup_hpd(struct dw_hdmi *hdmi,
    508			      void *data)
    509{
    510	struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data;
    511
    512	/* Setup HPD Filter */
    513	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER,
    514			  (0xa << 12) | 0xa0);
    515
    516	/* Clear interrupts */
    517	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
    518			  HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL);
    519
    520	/* Unmask interrupts */
    521	dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_INTR_MASKN,
    522			HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL,
    523			HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL);
    524}
    525
    526static const struct dw_hdmi_phy_ops meson_dw_hdmi_phy_ops = {
    527	.init = dw_hdmi_phy_init,
    528	.disable = dw_hdmi_phy_disable,
    529	.read_hpd = dw_hdmi_read_hpd,
    530	.setup_hpd = dw_hdmi_setup_hpd,
    531};
    532
    533static irqreturn_t dw_hdmi_top_irq(int irq, void *dev_id)
    534{
    535	struct meson_dw_hdmi *dw_hdmi = dev_id;
    536	u32 stat;
    537
    538	stat = dw_hdmi->data->top_read(dw_hdmi, HDMITX_TOP_INTR_STAT);
    539	dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, stat);
    540
    541	/* HPD Events, handle in the threaded interrupt handler */
    542	if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) {
    543		dw_hdmi->irq_stat = stat;
    544		return IRQ_WAKE_THREAD;
    545	}
    546
    547	/* HDMI Controller Interrupt */
    548	if (stat & 1)
    549		return IRQ_NONE;
    550
    551	/* TOFIX Handle HDCP Interrupts */
    552
    553	return IRQ_HANDLED;
    554}
    555
    556/* Threaded interrupt handler to manage HPD events */
    557static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id)
    558{
    559	struct meson_dw_hdmi *dw_hdmi = dev_id;
    560	u32 stat = dw_hdmi->irq_stat;
    561
    562	/* HPD Events */
    563	if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) {
    564		bool hpd_connected = false;
    565
    566		if (stat & HDMITX_TOP_INTR_HPD_RISE)
    567			hpd_connected = true;
    568
    569		dw_hdmi_setup_rx_sense(dw_hdmi->hdmi, hpd_connected,
    570				       hpd_connected);
    571
    572		drm_helper_hpd_irq_event(dw_hdmi->bridge->dev);
    573		drm_bridge_hpd_notify(dw_hdmi->bridge,
    574				      hpd_connected ? connector_status_connected
    575						    : connector_status_disconnected);
    576	}
    577
    578	return IRQ_HANDLED;
    579}
    580
    581/* DW HDMI Regmap */
    582
    583static int meson_dw_hdmi_reg_read(void *context, unsigned int reg,
    584				  unsigned int *result)
    585{
    586	struct meson_dw_hdmi *dw_hdmi = context;
    587
    588	*result = dw_hdmi->data->dwc_read(dw_hdmi, reg);
    589
    590	return 0;
    591
    592}
    593
    594static int meson_dw_hdmi_reg_write(void *context, unsigned int reg,
    595				   unsigned int val)
    596{
    597	struct meson_dw_hdmi *dw_hdmi = context;
    598
    599	dw_hdmi->data->dwc_write(dw_hdmi, reg, val);
    600
    601	return 0;
    602}
    603
    604static const struct regmap_config meson_dw_hdmi_regmap_config = {
    605	.reg_bits = 32,
    606	.val_bits = 8,
    607	.reg_read = meson_dw_hdmi_reg_read,
    608	.reg_write = meson_dw_hdmi_reg_write,
    609	.max_register = 0x10000,
    610	.fast_io = true,
    611};
    612
    613static const struct meson_dw_hdmi_data meson_dw_hdmi_gx_data = {
    614	.top_read = dw_hdmi_top_read,
    615	.top_write = dw_hdmi_top_write,
    616	.dwc_read = dw_hdmi_dwc_read,
    617	.dwc_write = dw_hdmi_dwc_write,
    618};
    619
    620static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = {
    621	.top_read = dw_hdmi_g12a_top_read,
    622	.top_write = dw_hdmi_g12a_top_write,
    623	.dwc_read = dw_hdmi_g12a_dwc_read,
    624	.dwc_write = dw_hdmi_g12a_dwc_write,
    625};
    626
    627static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
    628{
    629	struct meson_drm *priv = meson_dw_hdmi->priv;
    630
    631	/* Enable clocks */
    632	regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
    633
    634	/* Bring HDMITX MEM output of power down */
    635	regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0);
    636
    637	/* Reset HDMITX APB & TX & PHY */
    638	reset_control_reset(meson_dw_hdmi->hdmitx_apb);
    639	reset_control_reset(meson_dw_hdmi->hdmitx_ctrl);
    640	reset_control_reset(meson_dw_hdmi->hdmitx_phy);
    641
    642	/* Enable APB3 fail on error */
    643	if (!meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
    644		writel_bits_relaxed(BIT(15), BIT(15),
    645				    meson_dw_hdmi->hdmitx + HDMITX_TOP_CTRL_REG);
    646		writel_bits_relaxed(BIT(15), BIT(15),
    647				    meson_dw_hdmi->hdmitx + HDMITX_DWC_CTRL_REG);
    648	}
    649
    650	/* Bring out of reset */
    651	meson_dw_hdmi->data->top_write(meson_dw_hdmi,
    652				       HDMITX_TOP_SW_RESET,  0);
    653
    654	msleep(20);
    655
    656	meson_dw_hdmi->data->top_write(meson_dw_hdmi,
    657				       HDMITX_TOP_CLK_CNTL, 0xff);
    658
    659	/* Enable HDMI-TX Interrupt */
    660	meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
    661				       HDMITX_TOP_INTR_CORE);
    662
    663	meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN,
    664				       HDMITX_TOP_INTR_CORE);
    665
    666}
    667
    668static void meson_disable_regulator(void *data)
    669{
    670	regulator_disable(data);
    671}
    672
    673static void meson_disable_clk(void *data)
    674{
    675	clk_disable_unprepare(data);
    676}
    677
    678static int meson_enable_clk(struct device *dev, char *name)
    679{
    680	struct clk *clk;
    681	int ret;
    682
    683	clk = devm_clk_get(dev, name);
    684	if (IS_ERR(clk)) {
    685		dev_err(dev, "Unable to get %s pclk\n", name);
    686		return PTR_ERR(clk);
    687	}
    688
    689	ret = clk_prepare_enable(clk);
    690	if (!ret)
    691		ret = devm_add_action_or_reset(dev, meson_disable_clk, clk);
    692
    693	return ret;
    694}
    695
    696static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
    697				void *data)
    698{
    699	struct platform_device *pdev = to_platform_device(dev);
    700	const struct meson_dw_hdmi_data *match;
    701	struct meson_dw_hdmi *meson_dw_hdmi;
    702	struct drm_device *drm = data;
    703	struct meson_drm *priv = drm->dev_private;
    704	struct dw_hdmi_plat_data *dw_plat_data;
    705	int irq;
    706	int ret;
    707
    708	DRM_DEBUG_DRIVER("\n");
    709
    710	match = of_device_get_match_data(&pdev->dev);
    711	if (!match) {
    712		dev_err(&pdev->dev, "failed to get match data\n");
    713		return -ENODEV;
    714	}
    715
    716	meson_dw_hdmi = devm_kzalloc(dev, sizeof(*meson_dw_hdmi),
    717				     GFP_KERNEL);
    718	if (!meson_dw_hdmi)
    719		return -ENOMEM;
    720
    721	meson_dw_hdmi->priv = priv;
    722	meson_dw_hdmi->dev = dev;
    723	meson_dw_hdmi->data = match;
    724	dw_plat_data = &meson_dw_hdmi->dw_plat_data;
    725
    726	meson_dw_hdmi->hdmi_supply = devm_regulator_get_optional(dev, "hdmi");
    727	if (IS_ERR(meson_dw_hdmi->hdmi_supply)) {
    728		if (PTR_ERR(meson_dw_hdmi->hdmi_supply) == -EPROBE_DEFER)
    729			return -EPROBE_DEFER;
    730		meson_dw_hdmi->hdmi_supply = NULL;
    731	} else {
    732		ret = regulator_enable(meson_dw_hdmi->hdmi_supply);
    733		if (ret)
    734			return ret;
    735		ret = devm_add_action_or_reset(dev, meson_disable_regulator,
    736					       meson_dw_hdmi->hdmi_supply);
    737		if (ret)
    738			return ret;
    739	}
    740
    741	meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev,
    742						"hdmitx_apb");
    743	if (IS_ERR(meson_dw_hdmi->hdmitx_apb)) {
    744		dev_err(dev, "Failed to get hdmitx_apb reset\n");
    745		return PTR_ERR(meson_dw_hdmi->hdmitx_apb);
    746	}
    747
    748	meson_dw_hdmi->hdmitx_ctrl = devm_reset_control_get_exclusive(dev,
    749						"hdmitx");
    750	if (IS_ERR(meson_dw_hdmi->hdmitx_ctrl)) {
    751		dev_err(dev, "Failed to get hdmitx reset\n");
    752		return PTR_ERR(meson_dw_hdmi->hdmitx_ctrl);
    753	}
    754
    755	meson_dw_hdmi->hdmitx_phy = devm_reset_control_get_exclusive(dev,
    756						"hdmitx_phy");
    757	if (IS_ERR(meson_dw_hdmi->hdmitx_phy)) {
    758		dev_err(dev, "Failed to get hdmitx_phy reset\n");
    759		return PTR_ERR(meson_dw_hdmi->hdmitx_phy);
    760	}
    761
    762	meson_dw_hdmi->hdmitx = devm_platform_ioremap_resource(pdev, 0);
    763	if (IS_ERR(meson_dw_hdmi->hdmitx))
    764		return PTR_ERR(meson_dw_hdmi->hdmitx);
    765
    766	ret = meson_enable_clk(dev, "isfr");
    767	if (ret)
    768		return ret;
    769
    770	ret = meson_enable_clk(dev, "iahb");
    771	if (ret)
    772		return ret;
    773
    774	ret = meson_enable_clk(dev, "venci");
    775	if (ret)
    776		return ret;
    777
    778	dw_plat_data->regm = devm_regmap_init(dev, NULL, meson_dw_hdmi,
    779					      &meson_dw_hdmi_regmap_config);
    780	if (IS_ERR(dw_plat_data->regm))
    781		return PTR_ERR(dw_plat_data->regm);
    782
    783	irq = platform_get_irq(pdev, 0);
    784	if (irq < 0)
    785		return irq;
    786
    787	ret = devm_request_threaded_irq(dev, irq, dw_hdmi_top_irq,
    788					dw_hdmi_top_thread_irq, IRQF_SHARED,
    789					"dw_hdmi_top_irq", meson_dw_hdmi);
    790	if (ret) {
    791		dev_err(dev, "Failed to request hdmi top irq\n");
    792		return ret;
    793	}
    794
    795	meson_dw_hdmi_init(meson_dw_hdmi);
    796
    797	/* Bridge / Connector */
    798
    799	dw_plat_data->priv_data = meson_dw_hdmi;
    800	dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops;
    801	dw_plat_data->phy_name = "meson_dw_hdmi_phy";
    802	dw_plat_data->phy_data = meson_dw_hdmi;
    803	dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
    804	dw_plat_data->ycbcr_420_allowed = true;
    805	dw_plat_data->disable_cec = true;
    806	dw_plat_data->output_port = 1;
    807
    808	if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
    809	    dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
    810	    dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
    811		dw_plat_data->use_drm_infoframe = true;
    812
    813	platform_set_drvdata(pdev, meson_dw_hdmi);
    814
    815	meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev, &meson_dw_hdmi->dw_plat_data);
    816	if (IS_ERR(meson_dw_hdmi->hdmi))
    817		return PTR_ERR(meson_dw_hdmi->hdmi);
    818
    819	meson_dw_hdmi->bridge = of_drm_find_bridge(pdev->dev.of_node);
    820
    821	DRM_DEBUG_DRIVER("HDMI controller initialized\n");
    822
    823	return 0;
    824}
    825
    826static void meson_dw_hdmi_unbind(struct device *dev, struct device *master,
    827				   void *data)
    828{
    829	struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
    830
    831	dw_hdmi_unbind(meson_dw_hdmi->hdmi);
    832}
    833
    834static const struct component_ops meson_dw_hdmi_ops = {
    835	.bind	= meson_dw_hdmi_bind,
    836	.unbind	= meson_dw_hdmi_unbind,
    837};
    838
    839static int __maybe_unused meson_dw_hdmi_pm_suspend(struct device *dev)
    840{
    841	struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
    842
    843	if (!meson_dw_hdmi)
    844		return 0;
    845
    846	/* Reset TOP */
    847	meson_dw_hdmi->data->top_write(meson_dw_hdmi,
    848				       HDMITX_TOP_SW_RESET, 0);
    849
    850	return 0;
    851}
    852
    853static int __maybe_unused meson_dw_hdmi_pm_resume(struct device *dev)
    854{
    855	struct meson_dw_hdmi *meson_dw_hdmi = dev_get_drvdata(dev);
    856
    857	if (!meson_dw_hdmi)
    858		return 0;
    859
    860	meson_dw_hdmi_init(meson_dw_hdmi);
    861
    862	dw_hdmi_resume(meson_dw_hdmi->hdmi);
    863
    864	return 0;
    865}
    866
    867static int meson_dw_hdmi_probe(struct platform_device *pdev)
    868{
    869	return component_add(&pdev->dev, &meson_dw_hdmi_ops);
    870}
    871
    872static int meson_dw_hdmi_remove(struct platform_device *pdev)
    873{
    874	component_del(&pdev->dev, &meson_dw_hdmi_ops);
    875
    876	return 0;
    877}
    878
    879static const struct dev_pm_ops meson_dw_hdmi_pm_ops = {
    880	SET_SYSTEM_SLEEP_PM_OPS(meson_dw_hdmi_pm_suspend,
    881				meson_dw_hdmi_pm_resume)
    882};
    883
    884static const struct of_device_id meson_dw_hdmi_of_table[] = {
    885	{ .compatible = "amlogic,meson-gxbb-dw-hdmi",
    886	  .data = &meson_dw_hdmi_gx_data },
    887	{ .compatible = "amlogic,meson-gxl-dw-hdmi",
    888	  .data = &meson_dw_hdmi_gx_data },
    889	{ .compatible = "amlogic,meson-gxm-dw-hdmi",
    890	  .data = &meson_dw_hdmi_gx_data },
    891	{ .compatible = "amlogic,meson-g12a-dw-hdmi",
    892	  .data = &meson_dw_hdmi_g12a_data },
    893	{ }
    894};
    895MODULE_DEVICE_TABLE(of, meson_dw_hdmi_of_table);
    896
    897static struct platform_driver meson_dw_hdmi_platform_driver = {
    898	.probe		= meson_dw_hdmi_probe,
    899	.remove		= meson_dw_hdmi_remove,
    900	.driver		= {
    901		.name		= DRIVER_NAME,
    902		.of_match_table	= meson_dw_hdmi_of_table,
    903		.pm = &meson_dw_hdmi_pm_ops,
    904	},
    905};
    906module_platform_driver(meson_dw_hdmi_platform_driver);
    907
    908MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
    909MODULE_DESCRIPTION(DRIVER_DESC);
    910MODULE_LICENSE("GPL");