cachepc-linux

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

meson_encoder_hdmi.c (13564B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2016 BayLibre, SAS
      4 * Author: Neil Armstrong <narmstrong@baylibre.com>
      5 * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/component.h>
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/of_device.h>
     13#include <linux/of_graph.h>
     14#include <linux/regulator/consumer.h>
     15#include <linux/reset.h>
     16
     17#include <media/cec-notifier.h>
     18
     19#include <drm/drm_atomic_helper.h>
     20#include <drm/drm_bridge.h>
     21#include <drm/drm_bridge_connector.h>
     22#include <drm/drm_device.h>
     23#include <drm/drm_edid.h>
     24#include <drm/drm_probe_helper.h>
     25#include <drm/drm_simple_kms_helper.h>
     26
     27#include <linux/media-bus-format.h>
     28#include <linux/videodev2.h>
     29
     30#include "meson_drv.h"
     31#include "meson_registers.h"
     32#include "meson_vclk.h"
     33#include "meson_venc.h"
     34#include "meson_encoder_hdmi.h"
     35
     36struct meson_encoder_hdmi {
     37	struct drm_encoder encoder;
     38	struct drm_bridge bridge;
     39	struct drm_bridge *next_bridge;
     40	struct drm_connector *connector;
     41	struct meson_drm *priv;
     42	unsigned long output_bus_fmt;
     43	struct cec_notifier *cec_notifier;
     44};
     45
     46#define bridge_to_meson_encoder_hdmi(x) \
     47	container_of(x, struct meson_encoder_hdmi, bridge)
     48
     49static int meson_encoder_hdmi_attach(struct drm_bridge *bridge,
     50				     enum drm_bridge_attach_flags flags)
     51{
     52	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
     53
     54	return drm_bridge_attach(bridge->encoder, encoder_hdmi->next_bridge,
     55				 &encoder_hdmi->bridge, flags);
     56}
     57
     58static void meson_encoder_hdmi_detach(struct drm_bridge *bridge)
     59{
     60	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
     61
     62	cec_notifier_conn_unregister(encoder_hdmi->cec_notifier);
     63	encoder_hdmi->cec_notifier = NULL;
     64}
     65
     66static void meson_encoder_hdmi_set_vclk(struct meson_encoder_hdmi *encoder_hdmi,
     67					const struct drm_display_mode *mode)
     68{
     69	struct meson_drm *priv = encoder_hdmi->priv;
     70	int vic = drm_match_cea_mode(mode);
     71	unsigned int phy_freq;
     72	unsigned int vclk_freq;
     73	unsigned int venc_freq;
     74	unsigned int hdmi_freq;
     75
     76	vclk_freq = mode->clock;
     77
     78	/* For 420, pixel clock is half unlike venc clock */
     79	if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
     80		vclk_freq /= 2;
     81
     82	/* TMDS clock is pixel_clock * 10 */
     83	phy_freq = vclk_freq * 10;
     84
     85	if (!vic) {
     86		meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq,
     87				 vclk_freq, vclk_freq, vclk_freq, false);
     88		return;
     89	}
     90
     91	/* 480i/576i needs global pixel doubling */
     92	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
     93		vclk_freq *= 2;
     94
     95	venc_freq = vclk_freq;
     96	hdmi_freq = vclk_freq;
     97
     98	/* VENC double pixels for 1080i, 720p and YUV420 modes */
     99	if (meson_venc_hdmi_venc_repeat(vic) ||
    100	    encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
    101		venc_freq *= 2;
    102
    103	vclk_freq = max(venc_freq, hdmi_freq);
    104
    105	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
    106		venc_freq /= 2;
    107
    108	dev_dbg(priv->dev, "vclk:%d phy=%d venc=%d hdmi=%d enci=%d\n",
    109		phy_freq, vclk_freq, venc_freq, hdmi_freq,
    110		priv->venc.hdmi_use_enci);
    111
    112	meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq,
    113			 venc_freq, hdmi_freq, priv->venc.hdmi_use_enci);
    114}
    115
    116static enum drm_mode_status meson_encoder_hdmi_mode_valid(struct drm_bridge *bridge,
    117					const struct drm_display_info *display_info,
    118					const struct drm_display_mode *mode)
    119{
    120	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
    121	struct meson_drm *priv = encoder_hdmi->priv;
    122	bool is_hdmi2_sink = display_info->hdmi.scdc.supported;
    123	unsigned int phy_freq;
    124	unsigned int vclk_freq;
    125	unsigned int venc_freq;
    126	unsigned int hdmi_freq;
    127	int vic = drm_match_cea_mode(mode);
    128	enum drm_mode_status status;
    129
    130	dev_dbg(priv->dev, "Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
    131
    132	/* If sink does not support 540MHz, reject the non-420 HDMI2 modes */
    133	if (display_info->max_tmds_clock &&
    134	    mode->clock > display_info->max_tmds_clock &&
    135	    !drm_mode_is_420_only(display_info, mode) &&
    136	    !drm_mode_is_420_also(display_info, mode))
    137		return MODE_BAD;
    138
    139	/* Check against non-VIC supported modes */
    140	if (!vic) {
    141		status = meson_venc_hdmi_supported_mode(mode);
    142		if (status != MODE_OK)
    143			return status;
    144
    145		return meson_vclk_dmt_supported_freq(priv, mode->clock);
    146	/* Check against supported VIC modes */
    147	} else if (!meson_venc_hdmi_supported_vic(vic))
    148		return MODE_BAD;
    149
    150	vclk_freq = mode->clock;
    151
    152	/* For 420, pixel clock is half unlike venc clock */
    153	if (drm_mode_is_420_only(display_info, mode) ||
    154	    (!is_hdmi2_sink &&
    155	     drm_mode_is_420_also(display_info, mode)))
    156		vclk_freq /= 2;
    157
    158	/* TMDS clock is pixel_clock * 10 */
    159	phy_freq = vclk_freq * 10;
    160
    161	/* 480i/576i needs global pixel doubling */
    162	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
    163		vclk_freq *= 2;
    164
    165	venc_freq = vclk_freq;
    166	hdmi_freq = vclk_freq;
    167
    168	/* VENC double pixels for 1080i, 720p and YUV420 modes */
    169	if (meson_venc_hdmi_venc_repeat(vic) ||
    170	    drm_mode_is_420_only(display_info, mode) ||
    171	    (!is_hdmi2_sink &&
    172	     drm_mode_is_420_also(display_info, mode)))
    173		venc_freq *= 2;
    174
    175	vclk_freq = max(venc_freq, hdmi_freq);
    176
    177	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
    178		venc_freq /= 2;
    179
    180	dev_dbg(priv->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n",
    181		__func__, phy_freq, vclk_freq, venc_freq, hdmi_freq);
    182
    183	return meson_vclk_vic_supported_freq(priv, phy_freq, vclk_freq);
    184}
    185
    186static void meson_encoder_hdmi_atomic_enable(struct drm_bridge *bridge,
    187					     struct drm_bridge_state *bridge_state)
    188{
    189	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
    190	struct drm_atomic_state *state = bridge_state->base.state;
    191	unsigned int ycrcb_map = VPU_HDMI_OUTPUT_CBYCR;
    192	struct meson_drm *priv = encoder_hdmi->priv;
    193	struct drm_connector_state *conn_state;
    194	const struct drm_display_mode *mode;
    195	struct drm_crtc_state *crtc_state;
    196	struct drm_connector *connector;
    197	bool yuv420_mode = false;
    198	int vic;
    199
    200	connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
    201	if (WARN_ON(!connector))
    202		return;
    203
    204	conn_state = drm_atomic_get_new_connector_state(state, connector);
    205	if (WARN_ON(!conn_state))
    206		return;
    207
    208	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
    209	if (WARN_ON(!crtc_state))
    210		return;
    211
    212	mode = &crtc_state->adjusted_mode;
    213
    214	vic = drm_match_cea_mode(mode);
    215
    216	dev_dbg(priv->dev, "\"%s\" vic %d\n", mode->name, vic);
    217
    218	if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) {
    219		ycrcb_map = VPU_HDMI_OUTPUT_CRYCB;
    220		yuv420_mode = true;
    221	}
    222
    223	/* VENC + VENC-DVI Mode setup */
    224	meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode);
    225
    226	/* VCLK Set clock */
    227	meson_encoder_hdmi_set_vclk(encoder_hdmi, mode);
    228
    229	if (encoder_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
    230		/* Setup YUV420 to HDMI-TX, no 10bit diphering */
    231		writel_relaxed(2 | (2 << 2),
    232			       priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
    233	else
    234		/* Setup YUV444 to HDMI-TX, no 10bit diphering */
    235		writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
    236
    237	dev_dbg(priv->dev, "%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP");
    238
    239	if (priv->venc.hdmi_use_enci)
    240		writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN));
    241	else
    242		writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
    243}
    244
    245static void meson_encoder_hdmi_atomic_disable(struct drm_bridge *bridge,
    246					     struct drm_bridge_state *bridge_state)
    247{
    248	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
    249	struct meson_drm *priv = encoder_hdmi->priv;
    250
    251	writel_bits_relaxed(0x3, 0,
    252			    priv->io_base + _REG(VPU_HDMI_SETTING));
    253
    254	writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN));
    255	writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
    256}
    257
    258static const u32 meson_encoder_hdmi_out_bus_fmts[] = {
    259	MEDIA_BUS_FMT_YUV8_1X24,
    260	MEDIA_BUS_FMT_UYYVYY8_0_5X24,
    261};
    262
    263static u32 *
    264meson_encoder_hdmi_get_inp_bus_fmts(struct drm_bridge *bridge,
    265					struct drm_bridge_state *bridge_state,
    266					struct drm_crtc_state *crtc_state,
    267					struct drm_connector_state *conn_state,
    268					u32 output_fmt,
    269					unsigned int *num_input_fmts)
    270{
    271	u32 *input_fmts = NULL;
    272	int i;
    273
    274	*num_input_fmts = 0;
    275
    276	for (i = 0 ; i < ARRAY_SIZE(meson_encoder_hdmi_out_bus_fmts) ; ++i) {
    277		if (output_fmt == meson_encoder_hdmi_out_bus_fmts[i]) {
    278			*num_input_fmts = 1;
    279			input_fmts = kcalloc(*num_input_fmts,
    280					     sizeof(*input_fmts),
    281					     GFP_KERNEL);
    282			if (!input_fmts)
    283				return NULL;
    284
    285			input_fmts[0] = output_fmt;
    286
    287			break;
    288		}
    289	}
    290
    291	return input_fmts;
    292}
    293
    294static int meson_encoder_hdmi_atomic_check(struct drm_bridge *bridge,
    295					struct drm_bridge_state *bridge_state,
    296					struct drm_crtc_state *crtc_state,
    297					struct drm_connector_state *conn_state)
    298{
    299	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
    300	struct drm_connector_state *old_conn_state =
    301		drm_atomic_get_old_connector_state(conn_state->state, conn_state->connector);
    302	struct meson_drm *priv = encoder_hdmi->priv;
    303
    304	encoder_hdmi->output_bus_fmt = bridge_state->output_bus_cfg.format;
    305
    306	dev_dbg(priv->dev, "output_bus_fmt %lx\n", encoder_hdmi->output_bus_fmt);
    307
    308	if (!drm_connector_atomic_hdr_metadata_equal(old_conn_state, conn_state))
    309		crtc_state->mode_changed = true;
    310
    311	return 0;
    312}
    313
    314static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge,
    315					  enum drm_connector_status status)
    316{
    317	struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
    318	struct edid *edid;
    319
    320	if (!encoder_hdmi->cec_notifier)
    321		return;
    322
    323	if (status == connector_status_connected) {
    324		edid = drm_bridge_get_edid(encoder_hdmi->next_bridge, encoder_hdmi->connector);
    325		if (!edid)
    326			return;
    327
    328		cec_notifier_set_phys_addr_from_edid(encoder_hdmi->cec_notifier, edid);
    329	} else
    330		cec_notifier_phys_addr_invalidate(encoder_hdmi->cec_notifier);
    331}
    332
    333static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = {
    334	.attach = meson_encoder_hdmi_attach,
    335	.detach = meson_encoder_hdmi_detach,
    336	.mode_valid = meson_encoder_hdmi_mode_valid,
    337	.hpd_notify = meson_encoder_hdmi_hpd_notify,
    338	.atomic_enable = meson_encoder_hdmi_atomic_enable,
    339	.atomic_disable = meson_encoder_hdmi_atomic_disable,
    340	.atomic_get_input_bus_fmts = meson_encoder_hdmi_get_inp_bus_fmts,
    341	.atomic_check = meson_encoder_hdmi_atomic_check,
    342	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
    343	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
    344	.atomic_reset = drm_atomic_helper_bridge_reset,
    345};
    346
    347int meson_encoder_hdmi_init(struct meson_drm *priv)
    348{
    349	struct meson_encoder_hdmi *meson_encoder_hdmi;
    350	struct platform_device *pdev;
    351	struct device_node *remote;
    352	int ret;
    353
    354	meson_encoder_hdmi = devm_kzalloc(priv->dev, sizeof(*meson_encoder_hdmi), GFP_KERNEL);
    355	if (!meson_encoder_hdmi)
    356		return -ENOMEM;
    357
    358	/* HDMI Transceiver Bridge */
    359	remote = of_graph_get_remote_node(priv->dev->of_node, 1, 0);
    360	if (!remote) {
    361		dev_err(priv->dev, "HDMI transceiver device is disabled");
    362		return 0;
    363	}
    364
    365	meson_encoder_hdmi->next_bridge = of_drm_find_bridge(remote);
    366	if (!meson_encoder_hdmi->next_bridge) {
    367		dev_err(priv->dev, "Failed to find HDMI transceiver bridge\n");
    368		return -EPROBE_DEFER;
    369	}
    370
    371	/* HDMI Encoder Bridge */
    372	meson_encoder_hdmi->bridge.funcs = &meson_encoder_hdmi_bridge_funcs;
    373	meson_encoder_hdmi->bridge.of_node = priv->dev->of_node;
    374	meson_encoder_hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
    375	meson_encoder_hdmi->bridge.interlace_allowed = true;
    376
    377	drm_bridge_add(&meson_encoder_hdmi->bridge);
    378
    379	meson_encoder_hdmi->priv = priv;
    380
    381	/* Encoder */
    382	ret = drm_simple_encoder_init(priv->drm, &meson_encoder_hdmi->encoder,
    383				      DRM_MODE_ENCODER_TMDS);
    384	if (ret) {
    385		dev_err(priv->dev, "Failed to init HDMI encoder: %d\n", ret);
    386		return ret;
    387	}
    388
    389	meson_encoder_hdmi->encoder.possible_crtcs = BIT(0);
    390
    391	/* Attach HDMI Encoder Bridge to Encoder */
    392	ret = drm_bridge_attach(&meson_encoder_hdmi->encoder, &meson_encoder_hdmi->bridge, NULL,
    393				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
    394	if (ret) {
    395		dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
    396		return ret;
    397	}
    398
    399	/* Initialize & attach Bridge Connector */
    400	meson_encoder_hdmi->connector = drm_bridge_connector_init(priv->drm,
    401							&meson_encoder_hdmi->encoder);
    402	if (IS_ERR(meson_encoder_hdmi->connector)) {
    403		dev_err(priv->dev, "Unable to create HDMI bridge connector\n");
    404		return PTR_ERR(meson_encoder_hdmi->connector);
    405	}
    406	drm_connector_attach_encoder(meson_encoder_hdmi->connector,
    407				     &meson_encoder_hdmi->encoder);
    408
    409	/*
    410	 * We should have now in place:
    411	 * encoder->[hdmi encoder bridge]->[dw-hdmi bridge]->[display connector bridge]->[display connector]
    412	 */
    413
    414	/*
    415	 * drm_connector_attach_max_bpc_property() requires the
    416	 * connector to have a state.
    417	 */
    418	drm_atomic_helper_connector_reset(meson_encoder_hdmi->connector);
    419
    420	if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL) ||
    421	    meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
    422	    meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
    423		drm_connector_attach_hdr_output_metadata_property(meson_encoder_hdmi->connector);
    424
    425	drm_connector_attach_max_bpc_property(meson_encoder_hdmi->connector, 8, 8);
    426
    427	/* Handle this here until handled by drm_bridge_connector_init() */
    428	meson_encoder_hdmi->connector->ycbcr_420_allowed = true;
    429
    430	pdev = of_find_device_by_node(remote);
    431	if (pdev) {
    432		struct cec_connector_info conn_info;
    433		struct cec_notifier *notifier;
    434
    435		cec_fill_conn_info_from_drm(&conn_info, meson_encoder_hdmi->connector);
    436
    437		notifier = cec_notifier_conn_register(&pdev->dev, NULL, &conn_info);
    438		if (!notifier)
    439			return -ENOMEM;
    440
    441		meson_encoder_hdmi->cec_notifier = notifier;
    442	}
    443
    444	dev_dbg(priv->dev, "HDMI encoder initialized\n");
    445
    446	return 0;
    447}