162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Maxime Ripard 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Maxime Ripard <maxime.ripard@free-electrons.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/component.h> 1062306a36Sopenharmony_ci#include <linux/i2c.h> 1162306a36Sopenharmony_ci#include <linux/iopoll.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <linux/reset.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2062306a36Sopenharmony_ci#include <drm/drm_edid.h> 2162306a36Sopenharmony_ci#include <drm/drm_encoder.h> 2262306a36Sopenharmony_ci#include <drm/drm_of.h> 2362306a36Sopenharmony_ci#include <drm/drm_panel.h> 2462306a36Sopenharmony_ci#include <drm/drm_print.h> 2562306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2662306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "sun4i_backend.h" 2962306a36Sopenharmony_ci#include "sun4i_crtc.h" 3062306a36Sopenharmony_ci#include "sun4i_drv.h" 3162306a36Sopenharmony_ci#include "sun4i_hdmi.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic inline struct sun4i_hdmi * 3462306a36Sopenharmony_cidrm_encoder_to_sun4i_hdmi(struct drm_encoder *encoder) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return container_of(encoder, struct sun4i_hdmi, 3762306a36Sopenharmony_ci encoder); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic inline struct sun4i_hdmi * 4162306a36Sopenharmony_cidrm_connector_to_sun4i_hdmi(struct drm_connector *connector) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return container_of(connector, struct sun4i_hdmi, 4462306a36Sopenharmony_ci connector); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int sun4i_hdmi_setup_avi_infoframes(struct sun4i_hdmi *hdmi, 4862306a36Sopenharmony_ci struct drm_display_mode *mode) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct hdmi_avi_infoframe frame; 5162306a36Sopenharmony_ci u8 buffer[17]; 5262306a36Sopenharmony_ci int i, ret; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci ret = drm_hdmi_avi_infoframe_from_display_mode(&frame, 5562306a36Sopenharmony_ci &hdmi->connector, mode); 5662306a36Sopenharmony_ci if (ret < 0) { 5762306a36Sopenharmony_ci DRM_ERROR("Failed to get infoframes from mode\n"); 5862306a36Sopenharmony_ci return ret; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci ret = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer)); 6262306a36Sopenharmony_ci if (ret < 0) { 6362306a36Sopenharmony_ci DRM_ERROR("Failed to pack infoframes\n"); 6462306a36Sopenharmony_ci return ret; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci for (i = 0; i < sizeof(buffer); i++) 6862306a36Sopenharmony_ci writeb(buffer[i], hdmi->base + SUN4I_HDMI_AVI_INFOFRAME_REG(i)); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return 0; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int sun4i_hdmi_atomic_check(struct drm_encoder *encoder, 7462306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 7562306a36Sopenharmony_ci struct drm_connector_state *conn_state) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc_state->mode; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLCLK) 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic void sun4i_hdmi_disable(struct drm_encoder *encoder) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); 8862306a36Sopenharmony_ci u32 val; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Disabling the HDMI Output\n"); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG); 9362306a36Sopenharmony_ci val &= ~SUN4I_HDMI_VID_CTRL_ENABLE; 9462306a36Sopenharmony_ci writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci clk_disable_unprepare(hdmi->tmds_clk); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void sun4i_hdmi_enable(struct drm_encoder *encoder) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; 10262306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); 10362306a36Sopenharmony_ci struct drm_display_info *display = &hdmi->connector.display_info; 10462306a36Sopenharmony_ci u32 val = 0; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Enabling the HDMI Output\n"); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci clk_prepare_enable(hdmi->tmds_clk); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci sun4i_hdmi_setup_avi_infoframes(hdmi, mode); 11162306a36Sopenharmony_ci val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); 11262306a36Sopenharmony_ci val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END); 11362306a36Sopenharmony_ci writel(val, hdmi->base + SUN4I_HDMI_PKT_CTRL_REG(0)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci val = SUN4I_HDMI_VID_CTRL_ENABLE; 11662306a36Sopenharmony_ci if (display->is_hdmi) 11762306a36Sopenharmony_ci val |= SUN4I_HDMI_VID_CTRL_HDMI_MODE; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void sun4i_hdmi_mode_set(struct drm_encoder *encoder, 12362306a36Sopenharmony_ci struct drm_display_mode *mode, 12462306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); 12762306a36Sopenharmony_ci unsigned int x, y; 12862306a36Sopenharmony_ci u32 val; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci clk_set_rate(hdmi->mod_clk, mode->crtc_clock * 1000); 13162306a36Sopenharmony_ci clk_set_rate(hdmi->tmds_clk, mode->crtc_clock * 1000); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Set input sync enable */ 13462306a36Sopenharmony_ci writel(SUN4I_HDMI_UNKNOWN_INPUT_SYNC, 13562306a36Sopenharmony_ci hdmi->base + SUN4I_HDMI_UNKNOWN_REG); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Setup output pad (?) controls 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * This is done here instead of at probe/bind time because 14162306a36Sopenharmony_ci * the controller seems to toggle some of the bits on its own. 14262306a36Sopenharmony_ci * 14362306a36Sopenharmony_ci * We can't just initialize the register there, we need to 14462306a36Sopenharmony_ci * protect the clock bits that have already been read out and 14562306a36Sopenharmony_ci * cached by the clock framework. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci val = readl(hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG); 14862306a36Sopenharmony_ci val &= SUN4I_HDMI_PAD_CTRL1_HALVE_CLK; 14962306a36Sopenharmony_ci val |= hdmi->variant->pad_ctrl1_init_val; 15062306a36Sopenharmony_ci writel(val, hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG); 15162306a36Sopenharmony_ci val = readl(hdmi->base + SUN4I_HDMI_PAD_CTRL1_REG); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Setup timing registers */ 15462306a36Sopenharmony_ci writel(SUN4I_HDMI_VID_TIMING_X(mode->hdisplay) | 15562306a36Sopenharmony_ci SUN4I_HDMI_VID_TIMING_Y(mode->vdisplay), 15662306a36Sopenharmony_ci hdmi->base + SUN4I_HDMI_VID_TIMING_ACT_REG); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci x = mode->htotal - mode->hsync_start; 15962306a36Sopenharmony_ci y = mode->vtotal - mode->vsync_start; 16062306a36Sopenharmony_ci writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y), 16162306a36Sopenharmony_ci hdmi->base + SUN4I_HDMI_VID_TIMING_BP_REG); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci x = mode->hsync_start - mode->hdisplay; 16462306a36Sopenharmony_ci y = mode->vsync_start - mode->vdisplay; 16562306a36Sopenharmony_ci writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y), 16662306a36Sopenharmony_ci hdmi->base + SUN4I_HDMI_VID_TIMING_FP_REG); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci x = mode->hsync_end - mode->hsync_start; 16962306a36Sopenharmony_ci y = mode->vsync_end - mode->vsync_start; 17062306a36Sopenharmony_ci writel(SUN4I_HDMI_VID_TIMING_X(x) | SUN4I_HDMI_VID_TIMING_Y(y), 17162306a36Sopenharmony_ci hdmi->base + SUN4I_HDMI_VID_TIMING_SPW_REG); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci val = SUN4I_HDMI_VID_TIMING_POL_TX_CLK; 17462306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_PHSYNC) 17562306a36Sopenharmony_ci val |= SUN4I_HDMI_VID_TIMING_POL_HSYNC; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_PVSYNC) 17862306a36Sopenharmony_ci val |= SUN4I_HDMI_VID_TIMING_POL_VSYNC; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci writel(val, hdmi->base + SUN4I_HDMI_VID_TIMING_POL_REG); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic enum drm_mode_status sun4i_hdmi_mode_valid(struct drm_encoder *encoder, 18462306a36Sopenharmony_ci const struct drm_display_mode *mode) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder); 18762306a36Sopenharmony_ci unsigned long rate = mode->clock * 1000; 18862306a36Sopenharmony_ci unsigned long diff = rate / 200; /* +-0.5% allowed by HDMI spec */ 18962306a36Sopenharmony_ci long rounded_rate; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* 165 MHz is the typical max pixelclock frequency for HDMI <= 1.2 */ 19262306a36Sopenharmony_ci if (rate > 165000000) 19362306a36Sopenharmony_ci return MODE_CLOCK_HIGH; 19462306a36Sopenharmony_ci rounded_rate = clk_round_rate(hdmi->tmds_clk, rate); 19562306a36Sopenharmony_ci if (rounded_rate > 0 && 19662306a36Sopenharmony_ci max_t(unsigned long, rounded_rate, rate) - 19762306a36Sopenharmony_ci min_t(unsigned long, rounded_rate, rate) < diff) 19862306a36Sopenharmony_ci return MODE_OK; 19962306a36Sopenharmony_ci return MODE_NOCLOCK; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic const struct drm_encoder_helper_funcs sun4i_hdmi_helper_funcs = { 20362306a36Sopenharmony_ci .atomic_check = sun4i_hdmi_atomic_check, 20462306a36Sopenharmony_ci .disable = sun4i_hdmi_disable, 20562306a36Sopenharmony_ci .enable = sun4i_hdmi_enable, 20662306a36Sopenharmony_ci .mode_set = sun4i_hdmi_mode_set, 20762306a36Sopenharmony_ci .mode_valid = sun4i_hdmi_mode_valid, 20862306a36Sopenharmony_ci}; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int sun4i_hdmi_get_modes(struct drm_connector *connector) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector); 21362306a36Sopenharmony_ci struct edid *edid; 21462306a36Sopenharmony_ci int ret; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci edid = drm_get_edid(connector, hdmi->ddc_i2c ?: hdmi->i2c); 21762306a36Sopenharmony_ci if (!edid) 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Monitor is %s monitor\n", 22162306a36Sopenharmony_ci connector->display_info.is_hdmi ? "an HDMI" : "a DVI"); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci drm_connector_update_edid_property(connector, edid); 22462306a36Sopenharmony_ci cec_s_phys_addr_from_edid(hdmi->cec_adap, edid); 22562306a36Sopenharmony_ci ret = drm_add_edid_modes(connector, edid); 22662306a36Sopenharmony_ci kfree(edid); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci return ret; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct device_node *phandle, *remote; 23462306a36Sopenharmony_ci struct i2c_adapter *ddc; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci remote = of_graph_get_remote_node(dev->of_node, 1, -1); 23762306a36Sopenharmony_ci if (!remote) 23862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0); 24162306a36Sopenharmony_ci of_node_put(remote); 24262306a36Sopenharmony_ci if (!phandle) 24362306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ddc = of_get_i2c_adapter_by_node(phandle); 24662306a36Sopenharmony_ci of_node_put(phandle); 24762306a36Sopenharmony_ci if (!ddc) 24862306a36Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return ddc; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = { 25462306a36Sopenharmony_ci .get_modes = sun4i_hdmi_get_modes, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic enum drm_connector_status 25862306a36Sopenharmony_cisun4i_hdmi_connector_detect(struct drm_connector *connector, bool force) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector); 26162306a36Sopenharmony_ci unsigned long reg; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci reg = readl(hdmi->base + SUN4I_HDMI_HPD_REG); 26462306a36Sopenharmony_ci if (!(reg & SUN4I_HDMI_HPD_HIGH)) { 26562306a36Sopenharmony_ci cec_phys_addr_invalidate(hdmi->cec_adap); 26662306a36Sopenharmony_ci return connector_status_disconnected; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return connector_status_connected; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic const struct drm_connector_funcs sun4i_hdmi_connector_funcs = { 27362306a36Sopenharmony_ci .detect = sun4i_hdmi_connector_detect, 27462306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 27562306a36Sopenharmony_ci .destroy = drm_connector_cleanup, 27662306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 27762306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 27862306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 27962306a36Sopenharmony_ci}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci#ifdef CONFIG_DRM_SUN4I_HDMI_CEC 28262306a36Sopenharmony_cistatic int sun4i_hdmi_cec_pin_read(struct cec_adapter *adap) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = cec_get_drvdata(adap); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return readl(hdmi->base + SUN4I_HDMI_CEC) & SUN4I_HDMI_CEC_RX; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void sun4i_hdmi_cec_pin_low(struct cec_adapter *adap) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = cec_get_drvdata(adap); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Start driving the CEC pin low */ 29462306a36Sopenharmony_ci writel(SUN4I_HDMI_CEC_ENABLE, hdmi->base + SUN4I_HDMI_CEC); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void sun4i_hdmi_cec_pin_high(struct cec_adapter *adap) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = cec_get_drvdata(adap); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * Stop driving the CEC pin, the pull up will take over 30362306a36Sopenharmony_ci * unless another CEC device is driving the pin low. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci writel(0, hdmi->base + SUN4I_HDMI_CEC); 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic const struct cec_pin_ops sun4i_hdmi_cec_pin_ops = { 30962306a36Sopenharmony_ci .read = sun4i_hdmi_cec_pin_read, 31062306a36Sopenharmony_ci .low = sun4i_hdmi_cec_pin_low, 31162306a36Sopenharmony_ci .high = sun4i_hdmi_cec_pin_high, 31262306a36Sopenharmony_ci}; 31362306a36Sopenharmony_ci#endif 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci#define SUN4I_HDMI_PAD_CTRL1_MASK (GENMASK(24, 7) | GENMASK(5, 0)) 31662306a36Sopenharmony_ci#define SUN4I_HDMI_PLL_CTRL_MASK (GENMASK(31, 8) | GENMASK(3, 0)) 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* Only difference from sun5i is AMP is 4 instead of 6 */ 31962306a36Sopenharmony_cistatic const struct sun4i_hdmi_variant sun4i_variant = { 32062306a36Sopenharmony_ci .pad_ctrl0_init_val = SUN4I_HDMI_PAD_CTRL0_TXEN | 32162306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_CKEN | 32262306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWENG | 32362306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWEND | 32462306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWENC | 32562306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_LDODEN | 32662306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_LDOCEN | 32762306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_BIASEN, 32862306a36Sopenharmony_ci .pad_ctrl1_init_val = SUN4I_HDMI_PAD_CTRL1_REG_AMP(4) | 32962306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_EMP(2) | 33062306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_DENCK | 33162306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_DEN | 33262306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT | 33362306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_EMP_OPT | 33462306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT | 33562306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_AMP_OPT, 33662306a36Sopenharmony_ci .pll_ctrl_init_val = SUN4I_HDMI_PLL_CTRL_VCO_S(8) | 33762306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_CS(7) | 33862306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_CP_S(15) | 33962306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_S(7) | 34062306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4) | 34162306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_SDIV2 | 34262306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_LDO2_EN | 34362306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_LDO1_EN | 34462306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_HV_IS_33 | 34562306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_BWS | 34662306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_PLL_EN, 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), 34962306a36Sopenharmony_ci .ddc_clk_pre_divider = 2, 35062306a36Sopenharmony_ci .ddc_clk_m_offset = 1, 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), 35362306a36Sopenharmony_ci .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), 35462306a36Sopenharmony_ci .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), 35562306a36Sopenharmony_ci .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), 35662306a36Sopenharmony_ci .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), 35762306a36Sopenharmony_ci .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), 35862306a36Sopenharmony_ci .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), 35962306a36Sopenharmony_ci .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), 36062306a36Sopenharmony_ci .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), 36162306a36Sopenharmony_ci .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), 36262306a36Sopenharmony_ci .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), 36362306a36Sopenharmony_ci .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), 36462306a36Sopenharmony_ci .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, 36762306a36Sopenharmony_ci .ddc_fifo_has_dir = true, 36862306a36Sopenharmony_ci}; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic const struct sun4i_hdmi_variant sun5i_variant = { 37162306a36Sopenharmony_ci .pad_ctrl0_init_val = SUN4I_HDMI_PAD_CTRL0_TXEN | 37262306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_CKEN | 37362306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWENG | 37462306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWEND | 37562306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWENC | 37662306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_LDODEN | 37762306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_LDOCEN | 37862306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_BIASEN, 37962306a36Sopenharmony_ci .pad_ctrl1_init_val = SUN4I_HDMI_PAD_CTRL1_REG_AMP(6) | 38062306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_EMP(2) | 38162306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_DENCK | 38262306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_DEN | 38362306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT | 38462306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_EMP_OPT | 38562306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT | 38662306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_AMP_OPT, 38762306a36Sopenharmony_ci .pll_ctrl_init_val = SUN4I_HDMI_PLL_CTRL_VCO_S(8) | 38862306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_CS(7) | 38962306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_CP_S(15) | 39062306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_S(7) | 39162306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4) | 39262306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_SDIV2 | 39362306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_LDO2_EN | 39462306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_LDO1_EN | 39562306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_HV_IS_33 | 39662306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_BWS | 39762306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_PLL_EN, 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci .ddc_clk_reg = REG_FIELD(SUN4I_HDMI_DDC_CLK_REG, 0, 6), 40062306a36Sopenharmony_ci .ddc_clk_pre_divider = 2, 40162306a36Sopenharmony_ci .ddc_clk_m_offset = 1, 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci .field_ddc_en = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 31, 31), 40462306a36Sopenharmony_ci .field_ddc_start = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 30, 30), 40562306a36Sopenharmony_ci .field_ddc_reset = REG_FIELD(SUN4I_HDMI_DDC_CTRL_REG, 0, 0), 40662306a36Sopenharmony_ci .field_ddc_addr_reg = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 31), 40762306a36Sopenharmony_ci .field_ddc_slave_addr = REG_FIELD(SUN4I_HDMI_DDC_ADDR_REG, 0, 6), 40862306a36Sopenharmony_ci .field_ddc_int_status = REG_FIELD(SUN4I_HDMI_DDC_INT_STATUS_REG, 0, 8), 40962306a36Sopenharmony_ci .field_ddc_fifo_clear = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 31, 31), 41062306a36Sopenharmony_ci .field_ddc_fifo_rx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), 41162306a36Sopenharmony_ci .field_ddc_fifo_tx_thres = REG_FIELD(SUN4I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), 41262306a36Sopenharmony_ci .field_ddc_byte_count = REG_FIELD(SUN4I_HDMI_DDC_BYTE_COUNT_REG, 0, 9), 41362306a36Sopenharmony_ci .field_ddc_cmd = REG_FIELD(SUN4I_HDMI_DDC_CMD_REG, 0, 2), 41462306a36Sopenharmony_ci .field_ddc_sda_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 9, 9), 41562306a36Sopenharmony_ci .field_ddc_sck_en = REG_FIELD(SUN4I_HDMI_DDC_LINE_CTRL_REG, 8, 8), 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci .ddc_fifo_reg = SUN4I_HDMI_DDC_FIFO_DATA_REG, 41862306a36Sopenharmony_ci .ddc_fifo_has_dir = true, 41962306a36Sopenharmony_ci}; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic const struct sun4i_hdmi_variant sun6i_variant = { 42262306a36Sopenharmony_ci .has_ddc_parent_clk = true, 42362306a36Sopenharmony_ci .has_reset_control = true, 42462306a36Sopenharmony_ci .pad_ctrl0_init_val = 0xff | 42562306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_TXEN | 42662306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_CKEN | 42762306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWENG | 42862306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWEND | 42962306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_PWENC | 43062306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_LDODEN | 43162306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL0_LDOCEN, 43262306a36Sopenharmony_ci .pad_ctrl1_init_val = SUN4I_HDMI_PAD_CTRL1_REG_AMP(6) | 43362306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_EMP(4) | 43462306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_DENCK | 43562306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_REG_DEN | 43662306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_EMPCK_OPT | 43762306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_EMP_OPT | 43862306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_PWSDT | 43962306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_PWSCK | 44062306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_AMPCK_OPT | 44162306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_AMP_OPT | 44262306a36Sopenharmony_ci SUN4I_HDMI_PAD_CTRL1_UNKNOWN, 44362306a36Sopenharmony_ci .pll_ctrl_init_val = SUN4I_HDMI_PLL_CTRL_VCO_S(8) | 44462306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_CS(3) | 44562306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_CP_S(10) | 44662306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_S(4) | 44762306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_VCO_GAIN(4) | 44862306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_SDIV2 | 44962306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_LDO2_EN | 45062306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_LDO1_EN | 45162306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_HV_IS_33 | 45262306a36Sopenharmony_ci SUN4I_HDMI_PLL_CTRL_PLL_EN, 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci .ddc_clk_reg = REG_FIELD(SUN6I_HDMI_DDC_CLK_REG, 0, 6), 45562306a36Sopenharmony_ci .ddc_clk_pre_divider = 1, 45662306a36Sopenharmony_ci .ddc_clk_m_offset = 2, 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci .tmds_clk_div_offset = 1, 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci .field_ddc_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 0, 0), 46162306a36Sopenharmony_ci .field_ddc_start = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 27, 27), 46262306a36Sopenharmony_ci .field_ddc_reset = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 31, 31), 46362306a36Sopenharmony_ci .field_ddc_addr_reg = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 31), 46462306a36Sopenharmony_ci .field_ddc_slave_addr = REG_FIELD(SUN6I_HDMI_DDC_ADDR_REG, 1, 7), 46562306a36Sopenharmony_ci .field_ddc_int_status = REG_FIELD(SUN6I_HDMI_DDC_INT_STATUS_REG, 0, 8), 46662306a36Sopenharmony_ci .field_ddc_fifo_clear = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 18, 18), 46762306a36Sopenharmony_ci .field_ddc_fifo_rx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 4, 7), 46862306a36Sopenharmony_ci .field_ddc_fifo_tx_thres = REG_FIELD(SUN6I_HDMI_DDC_FIFO_CTRL_REG, 0, 3), 46962306a36Sopenharmony_ci .field_ddc_byte_count = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 16, 25), 47062306a36Sopenharmony_ci .field_ddc_cmd = REG_FIELD(SUN6I_HDMI_DDC_CMD_REG, 0, 2), 47162306a36Sopenharmony_ci .field_ddc_sda_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 6, 6), 47262306a36Sopenharmony_ci .field_ddc_sck_en = REG_FIELD(SUN6I_HDMI_DDC_CTRL_REG, 4, 4), 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci .ddc_fifo_reg = SUN6I_HDMI_DDC_FIFO_DATA_REG, 47562306a36Sopenharmony_ci .ddc_fifo_thres_incl = true, 47662306a36Sopenharmony_ci}; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic const struct regmap_config sun4i_hdmi_regmap_config = { 47962306a36Sopenharmony_ci .reg_bits = 32, 48062306a36Sopenharmony_ci .val_bits = 32, 48162306a36Sopenharmony_ci .reg_stride = 4, 48262306a36Sopenharmony_ci .max_register = 0x580, 48362306a36Sopenharmony_ci}; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int sun4i_hdmi_bind(struct device *dev, struct device *master, 48662306a36Sopenharmony_ci void *data) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 48962306a36Sopenharmony_ci struct drm_device *drm = data; 49062306a36Sopenharmony_ci struct cec_connector_info conn_info; 49162306a36Sopenharmony_ci struct sun4i_drv *drv = drm->dev_private; 49262306a36Sopenharmony_ci struct sun4i_hdmi *hdmi; 49362306a36Sopenharmony_ci u32 reg; 49462306a36Sopenharmony_ci int ret; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); 49762306a36Sopenharmony_ci if (!hdmi) 49862306a36Sopenharmony_ci return -ENOMEM; 49962306a36Sopenharmony_ci dev_set_drvdata(dev, hdmi); 50062306a36Sopenharmony_ci hdmi->dev = dev; 50162306a36Sopenharmony_ci hdmi->drv = drv; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci hdmi->variant = of_device_get_match_data(dev); 50462306a36Sopenharmony_ci if (!hdmi->variant) 50562306a36Sopenharmony_ci return -EINVAL; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci hdmi->base = devm_platform_ioremap_resource(pdev, 0); 50862306a36Sopenharmony_ci if (IS_ERR(hdmi->base)) { 50962306a36Sopenharmony_ci dev_err(dev, "Couldn't map the HDMI encoder registers\n"); 51062306a36Sopenharmony_ci return PTR_ERR(hdmi->base); 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (hdmi->variant->has_reset_control) { 51462306a36Sopenharmony_ci hdmi->reset = devm_reset_control_get(dev, NULL); 51562306a36Sopenharmony_ci if (IS_ERR(hdmi->reset)) { 51662306a36Sopenharmony_ci dev_err(dev, "Couldn't get the HDMI reset control\n"); 51762306a36Sopenharmony_ci return PTR_ERR(hdmi->reset); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ret = reset_control_deassert(hdmi->reset); 52162306a36Sopenharmony_ci if (ret) { 52262306a36Sopenharmony_ci dev_err(dev, "Couldn't deassert HDMI reset\n"); 52362306a36Sopenharmony_ci return ret; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci hdmi->bus_clk = devm_clk_get(dev, "ahb"); 52862306a36Sopenharmony_ci if (IS_ERR(hdmi->bus_clk)) { 52962306a36Sopenharmony_ci dev_err(dev, "Couldn't get the HDMI bus clock\n"); 53062306a36Sopenharmony_ci ret = PTR_ERR(hdmi->bus_clk); 53162306a36Sopenharmony_ci goto err_assert_reset; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci clk_prepare_enable(hdmi->bus_clk); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci hdmi->mod_clk = devm_clk_get(dev, "mod"); 53662306a36Sopenharmony_ci if (IS_ERR(hdmi->mod_clk)) { 53762306a36Sopenharmony_ci dev_err(dev, "Couldn't get the HDMI mod clock\n"); 53862306a36Sopenharmony_ci ret = PTR_ERR(hdmi->mod_clk); 53962306a36Sopenharmony_ci goto err_disable_bus_clk; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci clk_prepare_enable(hdmi->mod_clk); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci hdmi->pll0_clk = devm_clk_get(dev, "pll-0"); 54462306a36Sopenharmony_ci if (IS_ERR(hdmi->pll0_clk)) { 54562306a36Sopenharmony_ci dev_err(dev, "Couldn't get the HDMI PLL 0 clock\n"); 54662306a36Sopenharmony_ci ret = PTR_ERR(hdmi->pll0_clk); 54762306a36Sopenharmony_ci goto err_disable_mod_clk; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci hdmi->pll1_clk = devm_clk_get(dev, "pll-1"); 55162306a36Sopenharmony_ci if (IS_ERR(hdmi->pll1_clk)) { 55262306a36Sopenharmony_ci dev_err(dev, "Couldn't get the HDMI PLL 1 clock\n"); 55362306a36Sopenharmony_ci ret = PTR_ERR(hdmi->pll1_clk); 55462306a36Sopenharmony_ci goto err_disable_mod_clk; 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci hdmi->regmap = devm_regmap_init_mmio(dev, hdmi->base, 55862306a36Sopenharmony_ci &sun4i_hdmi_regmap_config); 55962306a36Sopenharmony_ci if (IS_ERR(hdmi->regmap)) { 56062306a36Sopenharmony_ci dev_err(dev, "Couldn't create HDMI encoder regmap\n"); 56162306a36Sopenharmony_ci ret = PTR_ERR(hdmi->regmap); 56262306a36Sopenharmony_ci goto err_disable_mod_clk; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci ret = sun4i_tmds_create(hdmi); 56662306a36Sopenharmony_ci if (ret) { 56762306a36Sopenharmony_ci dev_err(dev, "Couldn't create the TMDS clock\n"); 56862306a36Sopenharmony_ci goto err_disable_mod_clk; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (hdmi->variant->has_ddc_parent_clk) { 57262306a36Sopenharmony_ci hdmi->ddc_parent_clk = devm_clk_get(dev, "ddc"); 57362306a36Sopenharmony_ci if (IS_ERR(hdmi->ddc_parent_clk)) { 57462306a36Sopenharmony_ci dev_err(dev, "Couldn't get the HDMI DDC clock\n"); 57562306a36Sopenharmony_ci ret = PTR_ERR(hdmi->ddc_parent_clk); 57662306a36Sopenharmony_ci goto err_disable_mod_clk; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci } else { 57962306a36Sopenharmony_ci hdmi->ddc_parent_clk = hdmi->tmds_clk; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci writel(SUN4I_HDMI_CTRL_ENABLE, hdmi->base + SUN4I_HDMI_CTRL_REG); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci writel(hdmi->variant->pad_ctrl0_init_val, 58562306a36Sopenharmony_ci hdmi->base + SUN4I_HDMI_PAD_CTRL0_REG); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci reg = readl(hdmi->base + SUN4I_HDMI_PLL_CTRL_REG); 58862306a36Sopenharmony_ci reg &= SUN4I_HDMI_PLL_CTRL_DIV_MASK; 58962306a36Sopenharmony_ci reg |= hdmi->variant->pll_ctrl_init_val; 59062306a36Sopenharmony_ci writel(reg, hdmi->base + SUN4I_HDMI_PLL_CTRL_REG); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci ret = sun4i_hdmi_i2c_create(dev, hdmi); 59362306a36Sopenharmony_ci if (ret) { 59462306a36Sopenharmony_ci dev_err(dev, "Couldn't create the HDMI I2C adapter\n"); 59562306a36Sopenharmony_ci goto err_disable_mod_clk; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci hdmi->ddc_i2c = sun4i_hdmi_get_ddc(dev); 59962306a36Sopenharmony_ci if (IS_ERR(hdmi->ddc_i2c)) { 60062306a36Sopenharmony_ci ret = PTR_ERR(hdmi->ddc_i2c); 60162306a36Sopenharmony_ci if (ret == -ENODEV) 60262306a36Sopenharmony_ci hdmi->ddc_i2c = NULL; 60362306a36Sopenharmony_ci else 60462306a36Sopenharmony_ci goto err_del_i2c_adapter; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci drm_encoder_helper_add(&hdmi->encoder, 60862306a36Sopenharmony_ci &sun4i_hdmi_helper_funcs); 60962306a36Sopenharmony_ci ret = drm_simple_encoder_init(drm, &hdmi->encoder, 61062306a36Sopenharmony_ci DRM_MODE_ENCODER_TMDS); 61162306a36Sopenharmony_ci if (ret) { 61262306a36Sopenharmony_ci dev_err(dev, "Couldn't initialise the HDMI encoder\n"); 61362306a36Sopenharmony_ci goto err_put_ddc_i2c; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci hdmi->encoder.possible_crtcs = drm_of_find_possible_crtcs(drm, 61762306a36Sopenharmony_ci dev->of_node); 61862306a36Sopenharmony_ci if (!hdmi->encoder.possible_crtcs) { 61962306a36Sopenharmony_ci ret = -EPROBE_DEFER; 62062306a36Sopenharmony_ci goto err_put_ddc_i2c; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci#ifdef CONFIG_DRM_SUN4I_HDMI_CEC 62462306a36Sopenharmony_ci hdmi->cec_adap = cec_pin_allocate_adapter(&sun4i_hdmi_cec_pin_ops, 62562306a36Sopenharmony_ci hdmi, "sun4i", CEC_CAP_DEFAULTS | CEC_CAP_CONNECTOR_INFO); 62662306a36Sopenharmony_ci ret = PTR_ERR_OR_ZERO(hdmi->cec_adap); 62762306a36Sopenharmony_ci if (ret < 0) 62862306a36Sopenharmony_ci goto err_cleanup_connector; 62962306a36Sopenharmony_ci writel(readl(hdmi->base + SUN4I_HDMI_CEC) & ~SUN4I_HDMI_CEC_TX, 63062306a36Sopenharmony_ci hdmi->base + SUN4I_HDMI_CEC); 63162306a36Sopenharmony_ci#endif 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci drm_connector_helper_add(&hdmi->connector, 63462306a36Sopenharmony_ci &sun4i_hdmi_connector_helper_funcs); 63562306a36Sopenharmony_ci ret = drm_connector_init_with_ddc(drm, &hdmi->connector, 63662306a36Sopenharmony_ci &sun4i_hdmi_connector_funcs, 63762306a36Sopenharmony_ci DRM_MODE_CONNECTOR_HDMIA, 63862306a36Sopenharmony_ci hdmi->ddc_i2c); 63962306a36Sopenharmony_ci if (ret) { 64062306a36Sopenharmony_ci dev_err(dev, 64162306a36Sopenharmony_ci "Couldn't initialise the HDMI connector\n"); 64262306a36Sopenharmony_ci goto err_cleanup_connector; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci cec_fill_conn_info_from_drm(&conn_info, &hdmi->connector); 64562306a36Sopenharmony_ci cec_s_conn_info(hdmi->cec_adap, &conn_info); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* There is no HPD interrupt, so we need to poll the controller */ 64862306a36Sopenharmony_ci hdmi->connector.polled = DRM_CONNECTOR_POLL_CONNECT | 64962306a36Sopenharmony_ci DRM_CONNECTOR_POLL_DISCONNECT; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci ret = cec_register_adapter(hdmi->cec_adap, dev); 65262306a36Sopenharmony_ci if (ret < 0) 65362306a36Sopenharmony_ci goto err_cleanup_connector; 65462306a36Sopenharmony_ci drm_connector_attach_encoder(&hdmi->connector, &hdmi->encoder); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cierr_cleanup_connector: 65962306a36Sopenharmony_ci cec_delete_adapter(hdmi->cec_adap); 66062306a36Sopenharmony_ci drm_encoder_cleanup(&hdmi->encoder); 66162306a36Sopenharmony_cierr_put_ddc_i2c: 66262306a36Sopenharmony_ci i2c_put_adapter(hdmi->ddc_i2c); 66362306a36Sopenharmony_cierr_del_i2c_adapter: 66462306a36Sopenharmony_ci i2c_del_adapter(hdmi->i2c); 66562306a36Sopenharmony_cierr_disable_mod_clk: 66662306a36Sopenharmony_ci clk_disable_unprepare(hdmi->mod_clk); 66762306a36Sopenharmony_cierr_disable_bus_clk: 66862306a36Sopenharmony_ci clk_disable_unprepare(hdmi->bus_clk); 66962306a36Sopenharmony_cierr_assert_reset: 67062306a36Sopenharmony_ci reset_control_assert(hdmi->reset); 67162306a36Sopenharmony_ci return ret; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic void sun4i_hdmi_unbind(struct device *dev, struct device *master, 67562306a36Sopenharmony_ci void *data) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct sun4i_hdmi *hdmi = dev_get_drvdata(dev); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci cec_unregister_adapter(hdmi->cec_adap); 68062306a36Sopenharmony_ci i2c_del_adapter(hdmi->i2c); 68162306a36Sopenharmony_ci i2c_put_adapter(hdmi->ddc_i2c); 68262306a36Sopenharmony_ci clk_disable_unprepare(hdmi->mod_clk); 68362306a36Sopenharmony_ci clk_disable_unprepare(hdmi->bus_clk); 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic const struct component_ops sun4i_hdmi_ops = { 68762306a36Sopenharmony_ci .bind = sun4i_hdmi_bind, 68862306a36Sopenharmony_ci .unbind = sun4i_hdmi_unbind, 68962306a36Sopenharmony_ci}; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic int sun4i_hdmi_probe(struct platform_device *pdev) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci return component_add(&pdev->dev, &sun4i_hdmi_ops); 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic void sun4i_hdmi_remove(struct platform_device *pdev) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci component_del(&pdev->dev, &sun4i_hdmi_ops); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic const struct of_device_id sun4i_hdmi_of_table[] = { 70262306a36Sopenharmony_ci { .compatible = "allwinner,sun4i-a10-hdmi", .data = &sun4i_variant, }, 70362306a36Sopenharmony_ci { .compatible = "allwinner,sun5i-a10s-hdmi", .data = &sun5i_variant, }, 70462306a36Sopenharmony_ci { .compatible = "allwinner,sun6i-a31-hdmi", .data = &sun6i_variant, }, 70562306a36Sopenharmony_ci { } 70662306a36Sopenharmony_ci}; 70762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sun4i_hdmi_of_table); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic struct platform_driver sun4i_hdmi_driver = { 71062306a36Sopenharmony_ci .probe = sun4i_hdmi_probe, 71162306a36Sopenharmony_ci .remove_new = sun4i_hdmi_remove, 71262306a36Sopenharmony_ci .driver = { 71362306a36Sopenharmony_ci .name = "sun4i-hdmi", 71462306a36Sopenharmony_ci .of_match_table = sun4i_hdmi_of_table, 71562306a36Sopenharmony_ci }, 71662306a36Sopenharmony_ci}; 71762306a36Sopenharmony_cimodule_platform_driver(sun4i_hdmi_driver); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 72062306a36Sopenharmony_ciMODULE_DESCRIPTION("Allwinner A10 HDMI Driver"); 72162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 722