162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 BayLibre, SAS 462306a36Sopenharmony_ci * Author: Neil Armstrong <narmstrong@baylibre.com> 562306a36Sopenharmony_ci * Copyright (C) 2015 Amlogic, Inc. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/component.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_graph.h> 1462306a36Sopenharmony_ci#include <linux/of_platform.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1762306a36Sopenharmony_ci#include <linux/reset.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <media/cec-notifier.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 2262306a36Sopenharmony_ci#include <drm/drm_bridge.h> 2362306a36Sopenharmony_ci#include <drm/drm_bridge_connector.h> 2462306a36Sopenharmony_ci#include <drm/drm_device.h> 2562306a36Sopenharmony_ci#include <drm/drm_edid.h> 2662306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2762306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/media-bus-format.h> 3062306a36Sopenharmony_ci#include <linux/videodev2.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "meson_drv.h" 3362306a36Sopenharmony_ci#include "meson_registers.h" 3462306a36Sopenharmony_ci#include "meson_vclk.h" 3562306a36Sopenharmony_ci#include "meson_venc.h" 3662306a36Sopenharmony_ci#include "meson_encoder_hdmi.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistruct meson_encoder_hdmi { 3962306a36Sopenharmony_ci struct drm_encoder encoder; 4062306a36Sopenharmony_ci struct drm_bridge bridge; 4162306a36Sopenharmony_ci struct drm_bridge *next_bridge; 4262306a36Sopenharmony_ci struct drm_connector *connector; 4362306a36Sopenharmony_ci struct meson_drm *priv; 4462306a36Sopenharmony_ci unsigned long output_bus_fmt; 4562306a36Sopenharmony_ci struct cec_notifier *cec_notifier; 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define bridge_to_meson_encoder_hdmi(x) \ 4962306a36Sopenharmony_ci container_of(x, struct meson_encoder_hdmi, bridge) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int meson_encoder_hdmi_attach(struct drm_bridge *bridge, 5262306a36Sopenharmony_ci enum drm_bridge_attach_flags flags) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return drm_bridge_attach(bridge->encoder, encoder_hdmi->next_bridge, 5762306a36Sopenharmony_ci &encoder_hdmi->bridge, flags); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic void meson_encoder_hdmi_detach(struct drm_bridge *bridge) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci cec_notifier_conn_unregister(encoder_hdmi->cec_notifier); 6562306a36Sopenharmony_ci encoder_hdmi->cec_notifier = NULL; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi, 6962306a36Sopenharmony_ci const struct drm_display_mode *mode) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct meson_drm *priv = encoder_hdmi->priv; 7262306a36Sopenharmony_ci int vic = drm_match_cea_mode(mode); 7362306a36Sopenharmony_ci unsigned int phy_freq; 7462306a36Sopenharmony_ci unsigned int vclk_freq; 7562306a36Sopenharmony_ci unsigned int venc_freq; 7662306a36Sopenharmony_ci unsigned int hdmi_freq; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci vclk_freq = mode->clock; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci /* For 420, pixel clock is half unlike venc clock */ 8162306a36Sopenharmony_ci if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) 8262306a36Sopenharmony_ci vclk_freq /= 2; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* TMDS clock is pixel_clock * 10 */ 8562306a36Sopenharmony_ci phy_freq = vclk_freq * 10; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!vic) { 8862306a36Sopenharmony_ci meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq, 8962306a36Sopenharmony_ci vclk_freq, vclk_freq, vclk_freq, false); 9062306a36Sopenharmony_ci return; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* 480i/576i needs global pixel doubling */ 9462306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLCLK) 9562306a36Sopenharmony_ci vclk_freq *= 2; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci venc_freq = vclk_freq; 9862306a36Sopenharmony_ci hdmi_freq = vclk_freq; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* VENC double pixels for 1080i, 720p and YUV420 modes */ 10162306a36Sopenharmony_ci if (meson_venc_hdmi_venc_repeat(vic) || 10262306a36Sopenharmony_ci encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) 10362306a36Sopenharmony_ci venc_freq *= 2; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci vclk_freq = max(venc_freq, hdmi_freq); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLCLK) 10862306a36Sopenharmony_ci venc_freq /= 2; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci dev_dbg(priv->dev, "vclk:%d phy=%d venc=%d hdmi=%d enci=%d\n", 11162306a36Sopenharmony_ci phy_freq, vclk_freq, venc_freq, hdmi_freq, 11262306a36Sopenharmony_ci priv->venc.hdmi_use_enci); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq, 11562306a36Sopenharmony_ci venc_freq, hdmi_freq, priv->venc.hdmi_use_enci); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic enum drm_mode_status meson_encoder_hdmi_mode_valid(struct drm_bridge *bridge, 11962306a36Sopenharmony_ci const struct drm_display_info *display_info, 12062306a36Sopenharmony_ci const struct drm_display_mode *mode) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); 12362306a36Sopenharmony_ci struct meson_drm *priv = encoder_hdmi->priv; 12462306a36Sopenharmony_ci bool is_hdmi2_sink = display_info->hdmi.scdc.supported; 12562306a36Sopenharmony_ci unsigned int phy_freq; 12662306a36Sopenharmony_ci unsigned int vclk_freq; 12762306a36Sopenharmony_ci unsigned int venc_freq; 12862306a36Sopenharmony_ci unsigned int hdmi_freq; 12962306a36Sopenharmony_ci int vic = drm_match_cea_mode(mode); 13062306a36Sopenharmony_ci enum drm_mode_status status; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci dev_dbg(priv->dev, "Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* If sink does not support 540MHz, reject the non-420 HDMI2 modes */ 13562306a36Sopenharmony_ci if (display_info->max_tmds_clock && 13662306a36Sopenharmony_ci mode->clock > display_info->max_tmds_clock && 13762306a36Sopenharmony_ci !drm_mode_is_420_only(display_info, mode) && 13862306a36Sopenharmony_ci !drm_mode_is_420_also(display_info, mode)) 13962306a36Sopenharmony_ci return MODE_BAD; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Check against non-VIC supported modes */ 14262306a36Sopenharmony_ci if (!vic) { 14362306a36Sopenharmony_ci status = meson_venc_hdmi_supported_mode(mode); 14462306a36Sopenharmony_ci if (status != MODE_OK) 14562306a36Sopenharmony_ci return status; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return meson_vclk_dmt_supported_freq(priv, mode->clock); 14862306a36Sopenharmony_ci /* Check against supported VIC modes */ 14962306a36Sopenharmony_ci } else if (!meson_venc_hdmi_supported_vic(vic)) 15062306a36Sopenharmony_ci return MODE_BAD; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci vclk_freq = mode->clock; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* For 420, pixel clock is half unlike venc clock */ 15562306a36Sopenharmony_ci if (drm_mode_is_420_only(display_info, mode) || 15662306a36Sopenharmony_ci (!is_hdmi2_sink && 15762306a36Sopenharmony_ci drm_mode_is_420_also(display_info, mode))) 15862306a36Sopenharmony_ci vclk_freq /= 2; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* TMDS clock is pixel_clock * 10 */ 16162306a36Sopenharmony_ci phy_freq = vclk_freq * 10; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* 480i/576i needs global pixel doubling */ 16462306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLCLK) 16562306a36Sopenharmony_ci vclk_freq *= 2; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci venc_freq = vclk_freq; 16862306a36Sopenharmony_ci hdmi_freq = vclk_freq; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* VENC double pixels for 1080i, 720p and YUV420 modes */ 17162306a36Sopenharmony_ci if (meson_venc_hdmi_venc_repeat(vic) || 17262306a36Sopenharmony_ci drm_mode_is_420_only(display_info, mode) || 17362306a36Sopenharmony_ci (!is_hdmi2_sink && 17462306a36Sopenharmony_ci drm_mode_is_420_also(display_info, mode))) 17562306a36Sopenharmony_ci venc_freq *= 2; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci vclk_freq = max(venc_freq, hdmi_freq); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_DBLCLK) 18062306a36Sopenharmony_ci venc_freq /= 2; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci dev_dbg(priv->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n", 18362306a36Sopenharmony_ci __func__, phy_freq, vclk_freq, venc_freq, hdmi_freq); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return meson_vclk_vic_supported_freq(priv, phy_freq, vclk_freq); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge, 18962306a36Sopenharmony_ci struct drm_bridge_state *bridge_state) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); 19262306a36Sopenharmony_ci struct drm_atomic_state *state = bridge_state->base.state; 19362306a36Sopenharmony_ci unsigned int ycrcb_map = VPU_HDMI_OUTPUT_CBYCR; 19462306a36Sopenharmony_ci struct meson_drm *priv = encoder_hdmi->priv; 19562306a36Sopenharmony_ci struct drm_connector_state *conn_state; 19662306a36Sopenharmony_ci const struct drm_display_mode *mode; 19762306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 19862306a36Sopenharmony_ci struct drm_connector *connector; 19962306a36Sopenharmony_ci bool yuv420_mode = false; 20062306a36Sopenharmony_ci int vic; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); 20362306a36Sopenharmony_ci if (WARN_ON(!connector)) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci conn_state = drm_atomic_get_new_connector_state(state, connector); 20762306a36Sopenharmony_ci if (WARN_ON(!conn_state)) 20862306a36Sopenharmony_ci return; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc); 21162306a36Sopenharmony_ci if (WARN_ON(!crtc_state)) 21262306a36Sopenharmony_ci return; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci mode = &crtc_state->adjusted_mode; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci vic = drm_match_cea_mode(mode); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci dev_dbg(priv->dev, "\"%s\" vic %d\n", mode->name, vic); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) { 22162306a36Sopenharmony_ci ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; 22262306a36Sopenharmony_ci yuv420_mode = true; 22362306a36Sopenharmony_ci } else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) 22462306a36Sopenharmony_ci ycrcb_map = VPU_HDMI_OUTPUT_CRYCB; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* VENC + VENC-DVI Mode setup */ 22762306a36Sopenharmony_ci meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* VCLK Set clock */ 23062306a36Sopenharmony_ci meson_encoder_hdmi_set_vclk(encoder_hdmi, mode); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) 23362306a36Sopenharmony_ci /* Setup YUV420 to HDMI-TX, no 10bit diphering */ 23462306a36Sopenharmony_ci writel_relaxed(2 | (2 << 2), 23562306a36Sopenharmony_ci priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); 23662306a36Sopenharmony_ci else if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYVY8_1X16) 23762306a36Sopenharmony_ci /* Setup YUV422 to HDMI-TX, no 10bit diphering */ 23862306a36Sopenharmony_ci writel_relaxed(1 | (2 << 2), 23962306a36Sopenharmony_ci priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci /* Setup YUV444 to HDMI-TX, no 10bit diphering */ 24262306a36Sopenharmony_ci writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci dev_dbg(priv->dev, "%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP"); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (priv->venc.hdmi_use_enci) 24762306a36Sopenharmony_ci writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN)); 24862306a36Sopenharmony_ci else 24962306a36Sopenharmony_ci writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void meson_encoder_hdmi_atomic_disable(struct drm_bridge *bridge, 25362306a36Sopenharmony_ci struct drm_bridge_state *bridge_state) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); 25662306a36Sopenharmony_ci struct meson_drm *priv = encoder_hdmi->priv; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci writel_bits_relaxed(0x3, 0, 25962306a36Sopenharmony_ci priv->io_base + _REG(VPU_HDMI_SETTING)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); 26262306a36Sopenharmony_ci writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic const u32 meson_encoder_hdmi_out_bus_fmts[] = { 26662306a36Sopenharmony_ci MEDIA_BUS_FMT_YUV8_1X24, 26762306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_1X16, 26862306a36Sopenharmony_ci MEDIA_BUS_FMT_UYYVYY8_0_5X24, 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic u32 * 27262306a36Sopenharmony_cimeson_encoder_hdmi_get_inp_bus_fmts(struct drm_bridge *bridge, 27362306a36Sopenharmony_ci struct drm_bridge_state *bridge_state, 27462306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 27562306a36Sopenharmony_ci struct drm_connector_state *conn_state, 27662306a36Sopenharmony_ci u32 output_fmt, 27762306a36Sopenharmony_ci unsigned int *num_input_fmts) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci u32 *input_fmts = NULL; 28062306a36Sopenharmony_ci int i; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci *num_input_fmts = 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci for (i = 0 ; i < ARRAY_SIZE(meson_encoder_hdmi_out_bus_fmts) ; ++i) { 28562306a36Sopenharmony_ci if (output_fmt == meson_encoder_hdmi_out_bus_fmts[i]) { 28662306a36Sopenharmony_ci *num_input_fmts = 1; 28762306a36Sopenharmony_ci input_fmts = kcalloc(*num_input_fmts, 28862306a36Sopenharmony_ci sizeof(*input_fmts), 28962306a36Sopenharmony_ci GFP_KERNEL); 29062306a36Sopenharmony_ci if (!input_fmts) 29162306a36Sopenharmony_ci return NULL; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci input_fmts[0] = output_fmt; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return input_fmts; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int meson_encoder_hdmi_atomic_check(struct drm_bridge *bridge, 30362306a36Sopenharmony_ci struct drm_bridge_state *bridge_state, 30462306a36Sopenharmony_ci struct drm_crtc_state *crtc_state, 30562306a36Sopenharmony_ci struct drm_connector_state *conn_state) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); 30862306a36Sopenharmony_ci struct drm_connector_state *old_conn_state = 30962306a36Sopenharmony_ci drm_atomic_get_old_connector_state(conn_state->state, conn_state->connector); 31062306a36Sopenharmony_ci struct meson_drm *priv = encoder_hdmi->priv; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci encoder_hdmi->output_bus_fmt = bridge_state->output_bus_cfg.format; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci dev_dbg(priv->dev, "output_bus_fmt %lx\n", encoder_hdmi->output_bus_fmt); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (!drm_connector_atomic_hdr_metadata_equal(old_conn_state, conn_state)) 31762306a36Sopenharmony_ci crtc_state->mode_changed = true; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge, 32362306a36Sopenharmony_ci enum drm_connector_status status) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge); 32662306a36Sopenharmony_ci struct edid *edid; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!encoder_hdmi->cec_notifier) 32962306a36Sopenharmony_ci return; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (status == connector_status_connected) { 33262306a36Sopenharmony_ci edid = drm_bridge_get_edid(encoder_hdmi->next_bridge, encoder_hdmi->connector); 33362306a36Sopenharmony_ci if (!edid) 33462306a36Sopenharmony_ci return; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci cec_notifier_set_phys_addr_from_edid(encoder_hdmi->cec_notifier, edid); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci kfree(edid); 33962306a36Sopenharmony_ci } else 34062306a36Sopenharmony_ci cec_notifier_phys_addr_invalidate(encoder_hdmi->cec_notifier); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = { 34462306a36Sopenharmony_ci .attach = meson_encoder_hdmi_attach, 34562306a36Sopenharmony_ci .detach = meson_encoder_hdmi_detach, 34662306a36Sopenharmony_ci .mode_valid = meson_encoder_hdmi_mode_valid, 34762306a36Sopenharmony_ci .hpd_notify = meson_encoder_hdmi_hpd_notify, 34862306a36Sopenharmony_ci .atomic_enable = meson_encoder_hdmi_atomic_enable, 34962306a36Sopenharmony_ci .atomic_disable = meson_encoder_hdmi_atomic_disable, 35062306a36Sopenharmony_ci .atomic_get_input_bus_fmts = meson_encoder_hdmi_get_inp_bus_fmts, 35162306a36Sopenharmony_ci .atomic_check = meson_encoder_hdmi_atomic_check, 35262306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 35362306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 35462306a36Sopenharmony_ci .atomic_reset = drm_atomic_helper_bridge_reset, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ciint meson_encoder_hdmi_init(struct meson_drm *priv) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct meson_encoder_hdmi *meson_encoder_hdmi; 36062306a36Sopenharmony_ci struct platform_device *pdev; 36162306a36Sopenharmony_ci struct device_node *remote; 36262306a36Sopenharmony_ci int ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci meson_encoder_hdmi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_hdmi), GFP_KERNEL); 36562306a36Sopenharmony_ci if (!meson_encoder_hdmi) 36662306a36Sopenharmony_ci return -ENOMEM; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* HDMI Transceiver Bridge */ 36962306a36Sopenharmony_ci remote = of_graph_get_remote_node(priv->dev->of_node, 1, 0); 37062306a36Sopenharmony_ci if (!remote) { 37162306a36Sopenharmony_ci dev_err(priv->dev, "HDMI transceiver device is disabled"); 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote); 37662306a36Sopenharmony_ci if (!meson_encoder_hdmi->next_bridge) { 37762306a36Sopenharmony_ci dev_err(priv->dev, "Failed to find HDMI transceiver bridge\n"); 37862306a36Sopenharmony_ci ret = -EPROBE_DEFER; 37962306a36Sopenharmony_ci goto err_put_node; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* HDMI Encoder Bridge */ 38362306a36Sopenharmony_ci meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs; 38462306a36Sopenharmony_ci meson_encoder_hdmi->bridge.of_node = priv->dev->of_node; 38562306a36Sopenharmony_ci meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; 38662306a36Sopenharmony_ci meson_encoder_hdmi->bridge.interlace_allowed = true; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci drm_bridge_add(&meson_encoder_hdmi->bridge); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci meson_encoder_hdmi->priv = priv; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Encoder */ 39362306a36Sopenharmony_ci ret = drm_simple_encoder_init(priv->drm, &meson_encoder_hdmi->encoder, 39462306a36Sopenharmony_ci DRM_MODE_ENCODER_TMDS); 39562306a36Sopenharmony_ci if (ret) { 39662306a36Sopenharmony_ci dev_err(priv->dev, "Failed to init HDMI encoder: %d\n", ret); 39762306a36Sopenharmony_ci goto err_put_node; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci meson_encoder_hdmi->encoder.possible_crtcs = BIT(0); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Attach HDMI Encoder Bridge to Encoder */ 40362306a36Sopenharmony_ci ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL, 40462306a36Sopenharmony_ci DRM_BRIDGE_ATTACH_NO_CONNECTOR); 40562306a36Sopenharmony_ci if (ret) { 40662306a36Sopenharmony_ci dev_err(priv->dev, "Failed to attach bridge: %d\n", ret); 40762306a36Sopenharmony_ci goto err_put_node; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Initialize & attach Bridge Connector */ 41162306a36Sopenharmony_ci meson_encoder_hdmi->connector = drm_bridge_connector_init(priv->drm, 41262306a36Sopenharmony_ci &meson_encoder_hdmi->encoder); 41362306a36Sopenharmony_ci if (IS_ERR(meson_encoder_hdmi->connector)) { 41462306a36Sopenharmony_ci dev_err(priv->dev, "Unable to create HDMI bridge connector\n"); 41562306a36Sopenharmony_ci ret = PTR_ERR(meson_encoder_hdmi->connector); 41662306a36Sopenharmony_ci goto err_put_node; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci drm_connector_attach_encoder(meson_encoder_hdmi->connector, 41962306a36Sopenharmony_ci &meson_encoder_hdmi->encoder); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* 42262306a36Sopenharmony_ci * We should have now in place: 42362306a36Sopenharmony_ci * encoder->[hdmi encoder bridge]->[dw-hdmi bridge]->[display connector bridge]->[display connector] 42462306a36Sopenharmony_ci */ 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * drm_connector_attach_max_bpc_property() requires the 42862306a36Sopenharmony_ci * connector to have a state. 42962306a36Sopenharmony_ci */ 43062306a36Sopenharmony_ci drm_atomic_helper_connector_reset(meson_encoder_hdmi->connector); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) || 43362306a36Sopenharmony_ci meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) || 43462306a36Sopenharmony_ci meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) 43562306a36Sopenharmony_ci drm_connector_attach_hdr_output_metadata_property(meson_encoder_hdmi->connector); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci drm_connector_attach_max_bpc_property(meson_encoder_hdmi->connector, 8, 8); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci /* Handle this here until handled by drm_bridge_connector_init() */ 44062306a36Sopenharmony_ci meson_encoder_hdmi->connector->ycbcr_420_allowed = true; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci pdev = of_find_device_by_node(remote); 44362306a36Sopenharmony_ci of_node_put(remote); 44462306a36Sopenharmony_ci if (pdev) { 44562306a36Sopenharmony_ci struct cec_connector_info conn_info; 44662306a36Sopenharmony_ci struct cec_notifier *notifier; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci cec_fill_conn_info_from_drm(&conn_info, meson_encoder_hdmi->connector); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci notifier = cec_notifier_conn_register(&pdev->dev, NULL, &conn_info); 45162306a36Sopenharmony_ci if (!notifier) { 45262306a36Sopenharmony_ci put_device(&pdev->dev); 45362306a36Sopenharmony_ci return -ENOMEM; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci meson_encoder_hdmi->cec_notifier = notifier; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci priv->encoders[MESON_ENC_HDMI] = meson_encoder_hdmi; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci dev_dbg(priv->dev, "HDMI encoder initialized\n"); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cierr_put_node: 46662306a36Sopenharmony_ci of_node_put(remote); 46762306a36Sopenharmony_ci return ret; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_civoid meson_encoder_hdmi_remove(struct meson_drm *priv) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct meson_encoder_hdmi *meson_encoder_hdmi; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (priv->encoders[MESON_ENC_HDMI]) { 47562306a36Sopenharmony_ci meson_encoder_hdmi = priv->encoders[MESON_ENC_HDMI]; 47662306a36Sopenharmony_ci drm_bridge_remove(&meson_encoder_hdmi->bridge); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci} 479