drivers/gpu/drm/meson/meson_encoder_hdmi.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/meson/meson_encoder_hdmi.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/meson/meson_encoder_hdmi.c
Extension
.c
Size
14900 bytes
Lines
495
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct meson_encoder_hdmi {
	struct drm_encoder encoder;
	struct drm_bridge bridge;
	struct drm_connector *connector;
	struct meson_drm *priv;
	unsigned long output_bus_fmt;
	struct cec_notifier *cec_notifier;
};

#define bridge_to_meson_encoder_hdmi(x) \
	container_of(x, struct meson_encoder_hdmi, bridge)

static int meson_encoder_hdmi_attach(struct drm_bridge *bridge,
				     struct drm_encoder *encoder,
				     enum drm_bridge_attach_flags flags)
{
	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);

	return drm_bridge_attach(encoder, encoder_hdmi->bridge.next_bridge,
				 &encoder_hdmi->bridge, flags);
}

static void meson_encoder_hdmi_detach(struct drm_bridge *bridge)
{
	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);

	cec_notifier_conn_unregister(encoder_hdmi->cec_notifier);
	encoder_hdmi->cec_notifier = NULL;
}

static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi,
					const struct drm_display_mode *mode)
{
	struct meson_drm *priv = encoder_hdmi->priv;
	int vic = drm_match_cea_mode(mode);
	unsigned long long phy_freq;
	unsigned long long vclk_freq;
	unsigned long long venc_freq;
	unsigned long long hdmi_freq;

	vclk_freq = mode->clock * 1000ULL;

	/* For 420, pixel clock is half unlike venc clock */
	if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
		vclk_freq /= 2;

	/* TMDS clock is pixel_clock * 10 */
	phy_freq = vclk_freq * 10;

	if (!vic) {
		meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq,
				 vclk_freq, vclk_freq, vclk_freq, false);
		return;
	}

	/* 480i/576i needs global pixel doubling */
	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
		vclk_freq *= 2;

	venc_freq = vclk_freq;
	hdmi_freq = vclk_freq;

	/* VENC double pixels for 1080i, 720p and YUV420 modes */
	if (meson_venc_hdmi_venc_repeat(vic) ||
	    encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
		venc_freq *= 2;

	vclk_freq = max(venc_freq, hdmi_freq);

	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
		venc_freq /= 2;

	dev_dbg(priv->dev,
		"phy:%lluHz vclk=%lluHz venc=%lluHz hdmi=%lluHz enci=%d\n",
		phy_freq, vclk_freq, venc_freq, hdmi_freq,
		priv->venc.hdmi_use_enci);

	meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq,
			 venc_freq, hdmi_freq, priv->venc.hdmi_use_enci);
}

static enum drm_mode_status meson_encoder_hdmi_mode_valid(struct drm_bridge *bridge,
					const struct drm_display_info *display_info,
					const struct drm_display_mode *mode)
{
	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
	struct meson_drm *priv = encoder_hdmi->priv;
	bool is_hdmi2_sink = display_info->hdmi.scdc.supported;
	unsigned long long clock = mode->clock * 1000ULL;
	unsigned long long phy_freq;

Annotation

Implementation Notes