162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 462306a36Sopenharmony_ci * Zheng Yang <zhengyang@rock-chips.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <drm/drm_edid.h> 862306a36Sopenharmony_ci#include <drm/drm_of.h> 962306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 1062306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/regmap.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "rk3066_hdmi.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "rockchip_drm_drv.h" 2062306a36Sopenharmony_ci#include "rockchip_drm_vop.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define DEFAULT_PLLA_RATE 30000000 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct hdmi_data_info { 2562306a36Sopenharmony_ci int vic; /* The CEA Video ID (VIC) of the current drm display mode. */ 2662306a36Sopenharmony_ci unsigned int enc_out_format; 2762306a36Sopenharmony_ci unsigned int colorimetry; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistruct rk3066_hdmi_i2c { 3162306a36Sopenharmony_ci struct i2c_adapter adap; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci u8 ddc_addr; 3462306a36Sopenharmony_ci u8 segment_addr; 3562306a36Sopenharmony_ci u8 stat; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci struct mutex i2c_lock; /* For i2c operation. */ 3862306a36Sopenharmony_ci struct completion cmpltn; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct rk3066_hdmi { 4262306a36Sopenharmony_ci struct device *dev; 4362306a36Sopenharmony_ci struct drm_device *drm_dev; 4462306a36Sopenharmony_ci struct regmap *grf_regmap; 4562306a36Sopenharmony_ci int irq; 4662306a36Sopenharmony_ci struct clk *hclk; 4762306a36Sopenharmony_ci void __iomem *regs; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci struct drm_connector connector; 5062306a36Sopenharmony_ci struct rockchip_encoder encoder; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci struct rk3066_hdmi_i2c *i2c; 5362306a36Sopenharmony_ci struct i2c_adapter *ddc; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci unsigned int tmdsclk; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci struct hdmi_data_info hdmi_data; 5862306a36Sopenharmony_ci struct drm_display_mode previous_mode; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic struct rk3066_hdmi *encoder_to_rk3066_hdmi(struct drm_encoder *encoder) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return container_of(rkencoder, struct rk3066_hdmi, encoder); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct rk3066_hdmi *connector_to_rk3066_hdmi(struct drm_connector *connector) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return container_of(connector, struct rk3066_hdmi, connector); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic inline u8 hdmi_readb(struct rk3066_hdmi *hdmi, u16 offset) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci return readl_relaxed(hdmi->regs + offset); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic inline void hdmi_writeb(struct rk3066_hdmi *hdmi, u16 offset, u32 val) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci writel_relaxed(val, hdmi->regs + offset); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic inline void hdmi_modb(struct rk3066_hdmi *hdmi, u16 offset, 8462306a36Sopenharmony_ci u32 msk, u32 val) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci u8 temp = hdmi_readb(hdmi, offset) & ~msk; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci temp |= val & msk; 8962306a36Sopenharmony_ci hdmi_writeb(hdmi, offset, temp); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void rk3066_hdmi_i2c_init(struct rk3066_hdmi *hdmi) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int ddc_bus_freq; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci ddc_bus_freq = (hdmi->tmdsclk >> 2) / HDMI_SCL_RATE; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF); 9962306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Clear the EDID interrupt flag and mute the interrupt. */ 10262306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_INTR_MASK1, HDMI_INTR_EDID_MASK, 0); 10362306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_INTR_STATUS1, HDMI_INTR_EDID_MASK); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic inline u8 rk3066_hdmi_get_power_mode(struct rk3066_hdmi *hdmi) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci return hdmi_readb(hdmi, HDMI_SYS_CTRL) & HDMI_SYS_POWER_MODE_MASK; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void rk3066_hdmi_set_power_mode(struct rk3066_hdmi *hdmi, int mode) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci u8 current_mode, next_mode; 11462306a36Sopenharmony_ci u8 i = 0; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci current_mode = rk3066_hdmi_get_power_mode(hdmi); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci DRM_DEV_DEBUG(hdmi->dev, "mode :%d\n", mode); 11962306a36Sopenharmony_ci DRM_DEV_DEBUG(hdmi->dev, "current_mode :%d\n", current_mode); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (current_mode == mode) 12262306a36Sopenharmony_ci return; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci do { 12562306a36Sopenharmony_ci if (current_mode > mode) { 12662306a36Sopenharmony_ci next_mode = current_mode / 2; 12762306a36Sopenharmony_ci } else { 12862306a36Sopenharmony_ci if (current_mode < HDMI_SYS_POWER_MODE_A) 12962306a36Sopenharmony_ci next_mode = HDMI_SYS_POWER_MODE_A; 13062306a36Sopenharmony_ci else 13162306a36Sopenharmony_ci next_mode = current_mode * 2; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci DRM_DEV_DEBUG(hdmi->dev, "%d: next_mode :%d\n", i, next_mode); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (next_mode != HDMI_SYS_POWER_MODE_D) { 13762306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_SYS_CTRL, 13862306a36Sopenharmony_ci HDMI_SYS_POWER_MODE_MASK, next_mode); 13962306a36Sopenharmony_ci } else { 14062306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_SYS_CTRL, 14162306a36Sopenharmony_ci HDMI_SYS_POWER_MODE_D | 14262306a36Sopenharmony_ci HDMI_SYS_PLL_RESET_MASK); 14362306a36Sopenharmony_ci usleep_range(90, 100); 14462306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_SYS_CTRL, 14562306a36Sopenharmony_ci HDMI_SYS_POWER_MODE_D | 14662306a36Sopenharmony_ci HDMI_SYS_PLLB_RESET); 14762306a36Sopenharmony_ci usleep_range(90, 100); 14862306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_SYS_CTRL, 14962306a36Sopenharmony_ci HDMI_SYS_POWER_MODE_D); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci current_mode = next_mode; 15262306a36Sopenharmony_ci i = i + 1; 15362306a36Sopenharmony_ci } while ((next_mode != mode) && (i < 5)); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * When the IP controller isn't configured with accurate video timing, 15762306a36Sopenharmony_ci * DDC_CLK should be equal to the PLLA frequency, which is 30MHz, 15862306a36Sopenharmony_ci * so we need to init the TMDS rate to the PCLK rate and reconfigure 15962306a36Sopenharmony_ci * the DDC clock. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci if (mode < HDMI_SYS_POWER_MODE_D) 16262306a36Sopenharmony_ci hdmi->tmdsclk = DEFAULT_PLLA_RATE; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic int 16662306a36Sopenharmony_cirk3066_hdmi_upload_frame(struct rk3066_hdmi *hdmi, int setup_rc, 16762306a36Sopenharmony_ci union hdmi_infoframe *frame, u32 frame_index, 16862306a36Sopenharmony_ci u32 mask, u32 disable, u32 enable) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci if (mask) 17162306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_CP_AUTO_SEND_CTRL, mask, disable); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_CP_BUF_INDEX, frame_index); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (setup_rc >= 0) { 17662306a36Sopenharmony_ci u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE]; 17762306a36Sopenharmony_ci ssize_t rc, i; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci rc = hdmi_infoframe_pack(frame, packed_frame, 18062306a36Sopenharmony_ci sizeof(packed_frame)); 18162306a36Sopenharmony_ci if (rc < 0) 18262306a36Sopenharmony_ci return rc; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci for (i = 0; i < rc; i++) 18562306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_CP_BUF_ACC_HB0 + i * 4, 18662306a36Sopenharmony_ci packed_frame[i]); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (mask) 18962306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_CP_AUTO_SEND_CTRL, mask, enable); 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return setup_rc; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int rk3066_hdmi_config_avi(struct rk3066_hdmi *hdmi, 19662306a36Sopenharmony_ci struct drm_display_mode *mode) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci union hdmi_infoframe frame; 19962306a36Sopenharmony_ci int rc; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, 20262306a36Sopenharmony_ci &hdmi->connector, mode); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444) 20562306a36Sopenharmony_ci frame.avi.colorspace = HDMI_COLORSPACE_YUV444; 20662306a36Sopenharmony_ci else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422) 20762306a36Sopenharmony_ci frame.avi.colorspace = HDMI_COLORSPACE_YUV422; 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci frame.avi.colorspace = HDMI_COLORSPACE_RGB; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci frame.avi.colorimetry = hdmi->hdmi_data.colorimetry; 21262306a36Sopenharmony_ci frame.avi.scan_mode = HDMI_SCAN_MODE_NONE; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return rk3066_hdmi_upload_frame(hdmi, rc, &frame, 21562306a36Sopenharmony_ci HDMI_INFOFRAME_AVI, 0, 0, 0); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int rk3066_hdmi_config_video_timing(struct rk3066_hdmi *hdmi, 21962306a36Sopenharmony_ci struct drm_display_mode *mode) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int value, vsync_offset; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* Set the details for the external polarity and interlace mode. */ 22462306a36Sopenharmony_ci value = HDMI_EXT_VIDEO_SET_EN; 22562306a36Sopenharmony_ci value |= mode->flags & DRM_MODE_FLAG_PHSYNC ? 22662306a36Sopenharmony_ci HDMI_VIDEO_HSYNC_ACTIVE_HIGH : HDMI_VIDEO_HSYNC_ACTIVE_LOW; 22762306a36Sopenharmony_ci value |= mode->flags & DRM_MODE_FLAG_PVSYNC ? 22862306a36Sopenharmony_ci HDMI_VIDEO_VSYNC_ACTIVE_HIGH : HDMI_VIDEO_VSYNC_ACTIVE_LOW; 22962306a36Sopenharmony_ci value |= mode->flags & DRM_MODE_FLAG_INTERLACE ? 23062306a36Sopenharmony_ci HDMI_VIDEO_MODE_INTERLACE : HDMI_VIDEO_MODE_PROGRESSIVE; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (hdmi->hdmi_data.vic == 2 || hdmi->hdmi_data.vic == 3) 23362306a36Sopenharmony_ci vsync_offset = 6; 23462306a36Sopenharmony_ci else 23562306a36Sopenharmony_ci vsync_offset = 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci value |= vsync_offset << HDMI_VIDEO_VSYNC_OFFSET_SHIFT; 23862306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_VIDEO_PARA, value); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Set the details for the external video timing. */ 24162306a36Sopenharmony_ci value = mode->htotal; 24262306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_HTOTAL_L, value & 0xFF); 24362306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_HTOTAL_H, (value >> 8) & 0xFF); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci value = mode->htotal - mode->hdisplay; 24662306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_HBLANK_L, value & 0xFF); 24762306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_HBLANK_H, (value >> 8) & 0xFF); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci value = mode->htotal - mode->hsync_start; 25062306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_HDELAY_L, value & 0xFF); 25162306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_HDELAY_H, (value >> 8) & 0xFF); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci value = mode->hsync_end - mode->hsync_start; 25462306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_HDURATION_L, value & 0xFF); 25562306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_HDURATION_H, (value >> 8) & 0xFF); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci value = mode->vtotal; 25862306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_VTOTAL_L, value & 0xFF); 25962306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_VTOTAL_H, (value >> 8) & 0xFF); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci value = mode->vtotal - mode->vdisplay; 26262306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_VBLANK_L, value & 0xFF); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci value = mode->vtotal - mode->vsync_start + vsync_offset; 26562306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_VDELAY, value & 0xFF); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci value = mode->vsync_end - mode->vsync_start; 26862306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EXT_VDURATION, value & 0xFF); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void 27462306a36Sopenharmony_cirk3066_hdmi_phy_write(struct rk3066_hdmi *hdmi, u16 offset, u8 value) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci hdmi_writeb(hdmi, offset, value); 27762306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_SYS_CTRL, 27862306a36Sopenharmony_ci HDMI_SYS_PLL_RESET_MASK, HDMI_SYS_PLL_RESET); 27962306a36Sopenharmony_ci usleep_range(90, 100); 28062306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_SYS_CTRL, HDMI_SYS_PLL_RESET_MASK, 0); 28162306a36Sopenharmony_ci usleep_range(900, 1000); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void rk3066_hdmi_config_phy(struct rk3066_hdmi *hdmi) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci /* TMDS uses the same frequency as dclk. */ 28762306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_DEEP_COLOR_MODE, 0x22); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* 29062306a36Sopenharmony_ci * The semi-public documentation does not describe the hdmi registers 29162306a36Sopenharmony_ci * used by the function rk3066_hdmi_phy_write(), so we keep using 29262306a36Sopenharmony_ci * these magic values for now. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci if (hdmi->tmdsclk > 100000000) { 29562306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x158, 0x0E); 29662306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x15c, 0x00); 29762306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x160, 0x60); 29862306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x164, 0x00); 29962306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x168, 0xDA); 30062306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x16c, 0xA1); 30162306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x170, 0x0e); 30262306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x174, 0x22); 30362306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x178, 0x00); 30462306a36Sopenharmony_ci } else if (hdmi->tmdsclk > 50000000) { 30562306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x158, 0x06); 30662306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x15c, 0x00); 30762306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x160, 0x60); 30862306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x164, 0x00); 30962306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x168, 0xCA); 31062306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x16c, 0xA3); 31162306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x170, 0x0e); 31262306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x174, 0x20); 31362306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x178, 0x00); 31462306a36Sopenharmony_ci } else { 31562306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x158, 0x02); 31662306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x15c, 0x00); 31762306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x160, 0x60); 31862306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x164, 0x00); 31962306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x168, 0xC2); 32062306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x16c, 0xA2); 32162306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x170, 0x0e); 32262306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x174, 0x20); 32362306a36Sopenharmony_ci rk3066_hdmi_phy_write(hdmi, 0x178, 0x00); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int rk3066_hdmi_setup(struct rk3066_hdmi *hdmi, 32862306a36Sopenharmony_ci struct drm_display_mode *mode) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct drm_display_info *display = &hdmi->connector.display_info; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci hdmi->hdmi_data.vic = drm_match_cea_mode(mode); 33362306a36Sopenharmony_ci hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (hdmi->hdmi_data.vic == 6 || hdmi->hdmi_data.vic == 7 || 33662306a36Sopenharmony_ci hdmi->hdmi_data.vic == 21 || hdmi->hdmi_data.vic == 22 || 33762306a36Sopenharmony_ci hdmi->hdmi_data.vic == 2 || hdmi->hdmi_data.vic == 3 || 33862306a36Sopenharmony_ci hdmi->hdmi_data.vic == 17 || hdmi->hdmi_data.vic == 18) 33962306a36Sopenharmony_ci hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601; 34062306a36Sopenharmony_ci else 34162306a36Sopenharmony_ci hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci hdmi->tmdsclk = mode->clock * 1000; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Mute video and audio output. */ 34662306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, HDMI_VIDEO_AUDIO_DISABLE_MASK, 34762306a36Sopenharmony_ci HDMI_AUDIO_DISABLE | HDMI_VIDEO_DISABLE); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Set power state to mode B. */ 35062306a36Sopenharmony_ci if (rk3066_hdmi_get_power_mode(hdmi) != HDMI_SYS_POWER_MODE_B) 35162306a36Sopenharmony_ci rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_B); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* Input video mode is RGB 24 bit. Use external data enable signal. */ 35462306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_AV_CTRL1, 35562306a36Sopenharmony_ci HDMI_VIDEO_DE_MASK, HDMI_VIDEO_EXTERNAL_DE); 35662306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_VIDEO_CTRL1, 35762306a36Sopenharmony_ci HDMI_VIDEO_OUTPUT_RGB444 | 35862306a36Sopenharmony_ci HDMI_VIDEO_INPUT_DATA_DEPTH_8BIT | 35962306a36Sopenharmony_ci HDMI_VIDEO_INPUT_COLOR_RGB); 36062306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_DEEP_COLOR_MODE, 0x20); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci rk3066_hdmi_config_video_timing(hdmi, mode); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (display->is_hdmi) { 36562306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK, 36662306a36Sopenharmony_ci HDMI_VIDEO_MODE_HDMI); 36762306a36Sopenharmony_ci rk3066_hdmi_config_avi(hdmi, mode); 36862306a36Sopenharmony_ci } else { 36962306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_HDCP_CTRL, HDMI_VIDEO_MODE_MASK, 0); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci rk3066_hdmi_config_phy(hdmi); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_E); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* 37762306a36Sopenharmony_ci * When the IP controller is configured with accurate video 37862306a36Sopenharmony_ci * timing, the TMDS clock source should be switched to 37962306a36Sopenharmony_ci * DCLK_LCDC, so we need to init the TMDS rate to the pixel mode 38062306a36Sopenharmony_ci * clock rate and reconfigure the DDC clock. 38162306a36Sopenharmony_ci */ 38262306a36Sopenharmony_ci rk3066_hdmi_i2c_init(hdmi); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* Unmute video output. */ 38562306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, 38662306a36Sopenharmony_ci HDMI_VIDEO_AUDIO_DISABLE_MASK, HDMI_AUDIO_DISABLE); 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic void 39162306a36Sopenharmony_cirk3066_hdmi_encoder_mode_set(struct drm_encoder *encoder, 39262306a36Sopenharmony_ci struct drm_display_mode *mode, 39362306a36Sopenharmony_ci struct drm_display_mode *adj_mode) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Store the display mode for plugin/DPMS poweron events. */ 39862306a36Sopenharmony_ci drm_mode_copy(&hdmi->previous_mode, adj_mode); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void rk3066_hdmi_encoder_enable(struct drm_encoder *encoder) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); 40462306a36Sopenharmony_ci int mux, val; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); 40762306a36Sopenharmony_ci if (mux) 40862306a36Sopenharmony_ci val = (HDMI_VIDEO_SEL << 16) | HDMI_VIDEO_SEL; 40962306a36Sopenharmony_ci else 41062306a36Sopenharmony_ci val = HDMI_VIDEO_SEL << 16; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci regmap_write(hdmi->grf_regmap, GRF_SOC_CON0, val); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder enable select: vop%s\n", 41562306a36Sopenharmony_ci (mux) ? "1" : "0"); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci rk3066_hdmi_setup(hdmi, &hdmi->previous_mode); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void rk3066_hdmi_encoder_disable(struct drm_encoder *encoder) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = encoder_to_rk3066_hdmi(encoder); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci DRM_DEV_DEBUG(hdmi->dev, "hdmi encoder disable\n"); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (rk3066_hdmi_get_power_mode(hdmi) == HDMI_SYS_POWER_MODE_E) { 42762306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_VIDEO_CTRL2, 42862306a36Sopenharmony_ci HDMI_VIDEO_AUDIO_DISABLE_MASK); 42962306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_VIDEO_CTRL2, 43062306a36Sopenharmony_ci HDMI_AUDIO_CP_LOGIC_RESET_MASK, 43162306a36Sopenharmony_ci HDMI_AUDIO_CP_LOGIC_RESET); 43262306a36Sopenharmony_ci usleep_range(500, 510); 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_A); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic bool 43862306a36Sopenharmony_cirk3066_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, 43962306a36Sopenharmony_ci const struct drm_display_mode *mode, 44062306a36Sopenharmony_ci struct drm_display_mode *adj_mode) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci return true; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic int 44662306a36Sopenharmony_cirk3066_hdmi_encoder_atomic_check(struct drm_encoder *encoder, 44762306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 44862306a36Sopenharmony_ci struct drm_connector_state *conn_state) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci s->output_mode = ROCKCHIP_OUT_MODE_P888; 45362306a36Sopenharmony_ci s->output_type = DRM_MODE_CONNECTOR_HDMIA; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic const 45962306a36Sopenharmony_cistruct drm_encoder_helper_funcs rk3066_hdmi_encoder_helper_funcs = { 46062306a36Sopenharmony_ci .enable = rk3066_hdmi_encoder_enable, 46162306a36Sopenharmony_ci .disable = rk3066_hdmi_encoder_disable, 46262306a36Sopenharmony_ci .mode_fixup = rk3066_hdmi_encoder_mode_fixup, 46362306a36Sopenharmony_ci .mode_set = rk3066_hdmi_encoder_mode_set, 46462306a36Sopenharmony_ci .atomic_check = rk3066_hdmi_encoder_atomic_check, 46562306a36Sopenharmony_ci}; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic enum drm_connector_status 46862306a36Sopenharmony_cirk3066_hdmi_connector_detect(struct drm_connector *connector, bool force) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return (hdmi_readb(hdmi, HDMI_HPG_MENS_STA) & HDMI_HPG_IN_STATUS_HIGH) ? 47362306a36Sopenharmony_ci connector_status_connected : connector_status_disconnected; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int rk3066_hdmi_connector_get_modes(struct drm_connector *connector) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); 47962306a36Sopenharmony_ci struct edid *edid; 48062306a36Sopenharmony_ci int ret = 0; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!hdmi->ddc) 48362306a36Sopenharmony_ci return 0; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci edid = drm_get_edid(connector, hdmi->ddc); 48662306a36Sopenharmony_ci if (edid) { 48762306a36Sopenharmony_ci drm_connector_update_edid_property(connector, edid); 48862306a36Sopenharmony_ci ret = drm_add_edid_modes(connector, edid); 48962306a36Sopenharmony_ci kfree(edid); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return ret; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic enum drm_mode_status 49662306a36Sopenharmony_cirk3066_hdmi_connector_mode_valid(struct drm_connector *connector, 49762306a36Sopenharmony_ci struct drm_display_mode *mode) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci u32 vic = drm_match_cea_mode(mode); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (vic > 1) 50262306a36Sopenharmony_ci return MODE_OK; 50362306a36Sopenharmony_ci else 50462306a36Sopenharmony_ci return MODE_BAD; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic struct drm_encoder * 50862306a36Sopenharmony_cirk3066_hdmi_connector_best_encoder(struct drm_connector *connector) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = connector_to_rk3066_hdmi(connector); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return &hdmi->encoder.encoder; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int 51662306a36Sopenharmony_cirk3066_hdmi_probe_single_connector_modes(struct drm_connector *connector, 51762306a36Sopenharmony_ci uint32_t maxX, uint32_t maxY) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci if (maxX > 1920) 52062306a36Sopenharmony_ci maxX = 1920; 52162306a36Sopenharmony_ci if (maxY > 1080) 52262306a36Sopenharmony_ci maxY = 1080; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return drm_helper_probe_single_connector_modes(connector, maxX, maxY); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic void rk3066_hdmi_connector_destroy(struct drm_connector *connector) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci drm_connector_unregister(connector); 53062306a36Sopenharmony_ci drm_connector_cleanup(connector); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic const struct drm_connector_funcs rk3066_hdmi_connector_funcs = { 53462306a36Sopenharmony_ci .fill_modes = rk3066_hdmi_probe_single_connector_modes, 53562306a36Sopenharmony_ci .detect = rk3066_hdmi_connector_detect, 53662306a36Sopenharmony_ci .destroy = rk3066_hdmi_connector_destroy, 53762306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 53862306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 53962306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 54062306a36Sopenharmony_ci}; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic const 54362306a36Sopenharmony_cistruct drm_connector_helper_funcs rk3066_hdmi_connector_helper_funcs = { 54462306a36Sopenharmony_ci .get_modes = rk3066_hdmi_connector_get_modes, 54562306a36Sopenharmony_ci .mode_valid = rk3066_hdmi_connector_mode_valid, 54662306a36Sopenharmony_ci .best_encoder = rk3066_hdmi_connector_best_encoder, 54762306a36Sopenharmony_ci}; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic int 55062306a36Sopenharmony_cirk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct drm_encoder *encoder = &hdmi->encoder.encoder; 55362306a36Sopenharmony_ci struct device *dev = hdmi->dev; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci encoder->possible_crtcs = 55662306a36Sopenharmony_ci drm_of_find_possible_crtcs(drm, dev->of_node); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* 55962306a36Sopenharmony_ci * If we failed to find the CRTC(s) which this encoder is 56062306a36Sopenharmony_ci * supposed to be connected to, it's because the CRTC has 56162306a36Sopenharmony_ci * not been registered yet. Defer probing, and hope that 56262306a36Sopenharmony_ci * the required CRTC is added later. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci if (encoder->possible_crtcs == 0) 56562306a36Sopenharmony_ci return -EPROBE_DEFER; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci drm_encoder_helper_add(encoder, &rk3066_hdmi_encoder_helper_funcs); 56862306a36Sopenharmony_ci drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci drm_connector_helper_add(&hdmi->connector, 57362306a36Sopenharmony_ci &rk3066_hdmi_connector_helper_funcs); 57462306a36Sopenharmony_ci drm_connector_init_with_ddc(drm, &hdmi->connector, 57562306a36Sopenharmony_ci &rk3066_hdmi_connector_funcs, 57662306a36Sopenharmony_ci DRM_MODE_CONNECTOR_HDMIA, 57762306a36Sopenharmony_ci hdmi->ddc); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci drm_connector_attach_encoder(&hdmi->connector, encoder); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic irqreturn_t rk3066_hdmi_hardirq(int irq, void *dev_id) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = dev_id; 58762306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 58862306a36Sopenharmony_ci u8 interrupt; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (rk3066_hdmi_get_power_mode(hdmi) == HDMI_SYS_POWER_MODE_A) 59162306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_SYS_CTRL, HDMI_SYS_POWER_MODE_B); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci interrupt = hdmi_readb(hdmi, HDMI_INTR_STATUS1); 59462306a36Sopenharmony_ci if (interrupt) 59562306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_INTR_STATUS1, interrupt); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (interrupt & HDMI_INTR_EDID_MASK) { 59862306a36Sopenharmony_ci hdmi->i2c->stat = interrupt; 59962306a36Sopenharmony_ci complete(&hdmi->i2c->cmpltn); 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (interrupt & (HDMI_INTR_HOTPLUG | HDMI_INTR_MSENS)) 60362306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci return ret; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic irqreturn_t rk3066_hdmi_irq(int irq, void *dev_id) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = dev_id; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci drm_helper_hpd_irq_event(hdmi->connector.dev); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return IRQ_HANDLED; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic int rk3066_hdmi_i2c_read(struct rk3066_hdmi *hdmi, struct i2c_msg *msgs) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci int length = msgs->len; 62062306a36Sopenharmony_ci u8 *buf = msgs->buf; 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ret = wait_for_completion_timeout(&hdmi->i2c->cmpltn, HZ / 10); 62462306a36Sopenharmony_ci if (!ret || hdmi->i2c->stat & HDMI_INTR_EDID_ERR) 62562306a36Sopenharmony_ci return -EAGAIN; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci while (length--) 62862306a36Sopenharmony_ci *buf++ = hdmi_readb(hdmi, HDMI_DDC_READ_FIFO_ADDR); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return 0; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int rk3066_hdmi_i2c_write(struct rk3066_hdmi *hdmi, struct i2c_msg *msgs) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci /* 63662306a36Sopenharmony_ci * The DDC module only supports read EDID message, so 63762306a36Sopenharmony_ci * we assume that each word write to this i2c adapter 63862306a36Sopenharmony_ci * should be the offset of the EDID word address. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci if (msgs->len != 1 || 64162306a36Sopenharmony_ci (msgs->addr != DDC_ADDR && msgs->addr != DDC_SEGMENT_ADDR)) 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci reinit_completion(&hdmi->i2c->cmpltn); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (msgs->addr == DDC_SEGMENT_ADDR) 64762306a36Sopenharmony_ci hdmi->i2c->segment_addr = msgs->buf[0]; 64862306a36Sopenharmony_ci if (msgs->addr == DDC_ADDR) 64962306a36Sopenharmony_ci hdmi->i2c->ddc_addr = msgs->buf[0]; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* Set edid fifo first address. */ 65262306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EDID_FIFO_ADDR, 0x00); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci /* Set edid word address 0x00/0x80. */ 65562306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* Set edid segment pointer. */ 65862306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic int rk3066_hdmi_i2c_xfer(struct i2c_adapter *adap, 66462306a36Sopenharmony_ci struct i2c_msg *msgs, int num) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = i2c_get_adapdata(adap); 66762306a36Sopenharmony_ci struct rk3066_hdmi_i2c *i2c = hdmi->i2c; 66862306a36Sopenharmony_ci int i, ret = 0; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci mutex_lock(&i2c->i2c_lock); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci rk3066_hdmi_i2c_init(hdmi); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* Unmute HDMI EDID interrupt. */ 67562306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_INTR_MASK1, 67662306a36Sopenharmony_ci HDMI_INTR_EDID_MASK, HDMI_INTR_EDID_MASK); 67762306a36Sopenharmony_ci i2c->stat = 0; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci for (i = 0; i < num; i++) { 68062306a36Sopenharmony_ci DRM_DEV_DEBUG(hdmi->dev, 68162306a36Sopenharmony_ci "xfer: num: %d/%d, len: %d, flags: %#x\n", 68262306a36Sopenharmony_ci i + 1, num, msgs[i].len, msgs[i].flags); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (msgs[i].flags & I2C_M_RD) 68562306a36Sopenharmony_ci ret = rk3066_hdmi_i2c_read(hdmi, &msgs[i]); 68662306a36Sopenharmony_ci else 68762306a36Sopenharmony_ci ret = rk3066_hdmi_i2c_write(hdmi, &msgs[i]); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (ret < 0) 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (!ret) 69462306a36Sopenharmony_ci ret = num; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci /* Mute HDMI EDID interrupt. */ 69762306a36Sopenharmony_ci hdmi_modb(hdmi, HDMI_INTR_MASK1, HDMI_INTR_EDID_MASK, 0); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci mutex_unlock(&i2c->i2c_lock); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return ret; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic u32 rk3066_hdmi_i2c_func(struct i2c_adapter *adapter) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic const struct i2c_algorithm rk3066_hdmi_algorithm = { 71062306a36Sopenharmony_ci .master_xfer = rk3066_hdmi_i2c_xfer, 71162306a36Sopenharmony_ci .functionality = rk3066_hdmi_i2c_func, 71262306a36Sopenharmony_ci}; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic struct i2c_adapter *rk3066_hdmi_i2c_adapter(struct rk3066_hdmi *hdmi) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct i2c_adapter *adap; 71762306a36Sopenharmony_ci struct rk3066_hdmi_i2c *i2c; 71862306a36Sopenharmony_ci int ret; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL); 72162306a36Sopenharmony_ci if (!i2c) 72262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci mutex_init(&i2c->i2c_lock); 72562306a36Sopenharmony_ci init_completion(&i2c->cmpltn); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci adap = &i2c->adap; 72862306a36Sopenharmony_ci adap->class = I2C_CLASS_DDC; 72962306a36Sopenharmony_ci adap->owner = THIS_MODULE; 73062306a36Sopenharmony_ci adap->dev.parent = hdmi->dev; 73162306a36Sopenharmony_ci adap->dev.of_node = hdmi->dev->of_node; 73262306a36Sopenharmony_ci adap->algo = &rk3066_hdmi_algorithm; 73362306a36Sopenharmony_ci strscpy(adap->name, "RK3066 HDMI", sizeof(adap->name)); 73462306a36Sopenharmony_ci i2c_set_adapdata(adap, hdmi); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci ret = i2c_add_adapter(adap); 73762306a36Sopenharmony_ci if (ret) { 73862306a36Sopenharmony_ci DRM_DEV_ERROR(hdmi->dev, "cannot add %s I2C adapter\n", 73962306a36Sopenharmony_ci adap->name); 74062306a36Sopenharmony_ci devm_kfree(hdmi->dev, i2c); 74162306a36Sopenharmony_ci return ERR_PTR(ret); 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci hdmi->i2c = i2c; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci DRM_DEV_DEBUG(hdmi->dev, "registered %s I2C bus driver\n", adap->name); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return adap; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic int rk3066_hdmi_bind(struct device *dev, struct device *master, 75262306a36Sopenharmony_ci void *data) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 75562306a36Sopenharmony_ci struct drm_device *drm = data; 75662306a36Sopenharmony_ci struct rk3066_hdmi *hdmi; 75762306a36Sopenharmony_ci int irq; 75862306a36Sopenharmony_ci int ret; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); 76162306a36Sopenharmony_ci if (!hdmi) 76262306a36Sopenharmony_ci return -ENOMEM; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci hdmi->dev = dev; 76562306a36Sopenharmony_ci hdmi->drm_dev = drm; 76662306a36Sopenharmony_ci hdmi->regs = devm_platform_ioremap_resource(pdev, 0); 76762306a36Sopenharmony_ci if (IS_ERR(hdmi->regs)) 76862306a36Sopenharmony_ci return PTR_ERR(hdmi->regs); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 77162306a36Sopenharmony_ci if (irq < 0) 77262306a36Sopenharmony_ci return irq; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci hdmi->hclk = devm_clk_get(dev, "hclk"); 77562306a36Sopenharmony_ci if (IS_ERR(hdmi->hclk)) { 77662306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "unable to get HDMI hclk clock\n"); 77762306a36Sopenharmony_ci return PTR_ERR(hdmi->hclk); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci ret = clk_prepare_enable(hdmi->hclk); 78162306a36Sopenharmony_ci if (ret) { 78262306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "cannot enable HDMI hclk clock: %d\n", ret); 78362306a36Sopenharmony_ci return ret; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci hdmi->grf_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, 78762306a36Sopenharmony_ci "rockchip,grf"); 78862306a36Sopenharmony_ci if (IS_ERR(hdmi->grf_regmap)) { 78962306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "unable to get rockchip,grf\n"); 79062306a36Sopenharmony_ci ret = PTR_ERR(hdmi->grf_regmap); 79162306a36Sopenharmony_ci goto err_disable_hclk; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci /* internal hclk = hdmi_hclk / 25 */ 79562306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_INTERNAL_CLK_DIVIDER, 25); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci hdmi->ddc = rk3066_hdmi_i2c_adapter(hdmi); 79862306a36Sopenharmony_ci if (IS_ERR(hdmi->ddc)) { 79962306a36Sopenharmony_ci ret = PTR_ERR(hdmi->ddc); 80062306a36Sopenharmony_ci hdmi->ddc = NULL; 80162306a36Sopenharmony_ci goto err_disable_hclk; 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_B); 80562306a36Sopenharmony_ci usleep_range(999, 1000); 80662306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_INTR_MASK1, HDMI_INTR_HOTPLUG); 80762306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_INTR_MASK2, 0); 80862306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_INTR_MASK3, 0); 80962306a36Sopenharmony_ci hdmi_writeb(hdmi, HDMI_INTR_MASK4, 0); 81062306a36Sopenharmony_ci rk3066_hdmi_set_power_mode(hdmi, HDMI_SYS_POWER_MODE_A); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci ret = rk3066_hdmi_register(drm, hdmi); 81362306a36Sopenharmony_ci if (ret) 81462306a36Sopenharmony_ci goto err_disable_i2c; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci dev_set_drvdata(dev, hdmi); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, rk3066_hdmi_hardirq, 81962306a36Sopenharmony_ci rk3066_hdmi_irq, IRQF_SHARED, 82062306a36Sopenharmony_ci dev_name(dev), hdmi); 82162306a36Sopenharmony_ci if (ret) { 82262306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to request hdmi irq: %d\n", ret); 82362306a36Sopenharmony_ci goto err_cleanup_hdmi; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return 0; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cierr_cleanup_hdmi: 82962306a36Sopenharmony_ci hdmi->connector.funcs->destroy(&hdmi->connector); 83062306a36Sopenharmony_ci hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); 83162306a36Sopenharmony_cierr_disable_i2c: 83262306a36Sopenharmony_ci i2c_put_adapter(hdmi->ddc); 83362306a36Sopenharmony_cierr_disable_hclk: 83462306a36Sopenharmony_ci clk_disable_unprepare(hdmi->hclk); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci return ret; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic void rk3066_hdmi_unbind(struct device *dev, struct device *master, 84062306a36Sopenharmony_ci void *data) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct rk3066_hdmi *hdmi = dev_get_drvdata(dev); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci hdmi->connector.funcs->destroy(&hdmi->connector); 84562306a36Sopenharmony_ci hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci i2c_put_adapter(hdmi->ddc); 84862306a36Sopenharmony_ci clk_disable_unprepare(hdmi->hclk); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic const struct component_ops rk3066_hdmi_ops = { 85262306a36Sopenharmony_ci .bind = rk3066_hdmi_bind, 85362306a36Sopenharmony_ci .unbind = rk3066_hdmi_unbind, 85462306a36Sopenharmony_ci}; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic int rk3066_hdmi_probe(struct platform_device *pdev) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci return component_add(&pdev->dev, &rk3066_hdmi_ops); 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cistatic void rk3066_hdmi_remove(struct platform_device *pdev) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci component_del(&pdev->dev, &rk3066_hdmi_ops); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic const struct of_device_id rk3066_hdmi_dt_ids[] = { 86762306a36Sopenharmony_ci { .compatible = "rockchip,rk3066-hdmi" }, 86862306a36Sopenharmony_ci { /* sentinel */ }, 86962306a36Sopenharmony_ci}; 87062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rk3066_hdmi_dt_ids); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistruct platform_driver rk3066_hdmi_driver = { 87362306a36Sopenharmony_ci .probe = rk3066_hdmi_probe, 87462306a36Sopenharmony_ci .remove_new = rk3066_hdmi_remove, 87562306a36Sopenharmony_ci .driver = { 87662306a36Sopenharmony_ci .name = "rockchip-rk3066-hdmi", 87762306a36Sopenharmony_ci .of_match_table = rk3066_hdmi_dt_ids, 87862306a36Sopenharmony_ci }, 87962306a36Sopenharmony_ci}; 880