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_cvbs.c (8616B)


      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 * Copyright (C) 2014 Endless Mobile
      7 *
      8 * Written by:
      9 *     Jasper St. Pierre <jstpierre@mecheye.net>
     10 */
     11
     12#include <linux/export.h>
     13#include <linux/of_graph.h>
     14
     15#include <drm/drm_atomic_helper.h>
     16#include <drm/drm_bridge.h>
     17#include <drm/drm_bridge_connector.h>
     18#include <drm/drm_device.h>
     19#include <drm/drm_edid.h>
     20#include <drm/drm_probe_helper.h>
     21#include <drm/drm_simple_kms_helper.h>
     22
     23#include "meson_registers.h"
     24#include "meson_vclk.h"
     25#include "meson_encoder_cvbs.h"
     26
     27/* HHI VDAC Registers */
     28#define HHI_VDAC_CNTL0		0x2F4 /* 0xbd offset in data sheet */
     29#define HHI_VDAC_CNTL0_G12A	0x2EC /* 0xbd offset in data sheet */
     30#define HHI_VDAC_CNTL1		0x2F8 /* 0xbe offset in data sheet */
     31#define HHI_VDAC_CNTL1_G12A	0x2F0 /* 0xbe offset in data sheet */
     32
     33struct meson_encoder_cvbs {
     34	struct drm_encoder	encoder;
     35	struct drm_bridge	bridge;
     36	struct drm_bridge	*next_bridge;
     37	struct meson_drm	*priv;
     38};
     39
     40#define bridge_to_meson_encoder_cvbs(x) \
     41	container_of(x, struct meson_encoder_cvbs, bridge)
     42
     43/* Supported Modes */
     44
     45struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = {
     46	{ /* PAL */
     47		.enci = &meson_cvbs_enci_pal,
     48		.mode = {
     49			DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
     50				 720, 732, 795, 864, 0, 576, 580, 586, 625, 0,
     51				 DRM_MODE_FLAG_INTERLACE),
     52			.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
     53		},
     54	},
     55	{ /* NTSC */
     56		.enci = &meson_cvbs_enci_ntsc,
     57		.mode = {
     58			DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
     59				720, 739, 801, 858, 0, 480, 488, 494, 525, 0,
     60				DRM_MODE_FLAG_INTERLACE),
     61			.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
     62		},
     63	},
     64};
     65
     66static const struct meson_cvbs_mode *
     67meson_cvbs_get_mode(const struct drm_display_mode *req_mode)
     68{
     69	int i;
     70
     71	for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
     72		struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
     73
     74		if (drm_mode_match(req_mode, &meson_mode->mode,
     75				   DRM_MODE_MATCH_TIMINGS |
     76				   DRM_MODE_MATCH_CLOCK |
     77				   DRM_MODE_MATCH_FLAGS |
     78				   DRM_MODE_MATCH_3D_FLAGS))
     79			return meson_mode;
     80	}
     81
     82	return NULL;
     83}
     84
     85static int meson_encoder_cvbs_attach(struct drm_bridge *bridge,
     86				     enum drm_bridge_attach_flags flags)
     87{
     88	struct meson_encoder_cvbs *meson_encoder_cvbs =
     89					bridge_to_meson_encoder_cvbs(bridge);
     90
     91	return drm_bridge_attach(bridge->encoder, meson_encoder_cvbs->next_bridge,
     92				 &meson_encoder_cvbs->bridge, flags);
     93}
     94
     95static int meson_encoder_cvbs_get_modes(struct drm_bridge *bridge,
     96					struct drm_connector *connector)
     97{
     98	struct meson_encoder_cvbs *meson_encoder_cvbs =
     99					bridge_to_meson_encoder_cvbs(bridge);
    100	struct meson_drm *priv = meson_encoder_cvbs->priv;
    101	struct drm_display_mode *mode;
    102	int i;
    103
    104	for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
    105		struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
    106
    107		mode = drm_mode_duplicate(priv->drm, &meson_mode->mode);
    108		if (!mode) {
    109			dev_err(priv->dev, "Failed to create a new display mode\n");
    110			return 0;
    111		}
    112
    113		drm_mode_probed_add(connector, mode);
    114	}
    115
    116	return i;
    117}
    118
    119static int meson_encoder_cvbs_mode_valid(struct drm_bridge *bridge,
    120					const struct drm_display_info *display_info,
    121					const struct drm_display_mode *mode)
    122{
    123	if (meson_cvbs_get_mode(mode))
    124		return MODE_OK;
    125
    126	return MODE_BAD;
    127}
    128
    129static int meson_encoder_cvbs_atomic_check(struct drm_bridge *bridge,
    130					struct drm_bridge_state *bridge_state,
    131					struct drm_crtc_state *crtc_state,
    132					struct drm_connector_state *conn_state)
    133{
    134	if (meson_cvbs_get_mode(&crtc_state->mode))
    135		return 0;
    136
    137	return -EINVAL;
    138}
    139
    140static void meson_encoder_cvbs_atomic_enable(struct drm_bridge *bridge,
    141					     struct drm_bridge_state *bridge_state)
    142{
    143	struct meson_encoder_cvbs *encoder_cvbs = bridge_to_meson_encoder_cvbs(bridge);
    144	struct drm_atomic_state *state = bridge_state->base.state;
    145	struct meson_drm *priv = encoder_cvbs->priv;
    146	const struct meson_cvbs_mode *meson_mode;
    147	struct drm_connector_state *conn_state;
    148	struct drm_crtc_state *crtc_state;
    149	struct drm_connector *connector;
    150
    151	connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
    152	if (WARN_ON(!connector))
    153		return;
    154
    155	conn_state = drm_atomic_get_new_connector_state(state, connector);
    156	if (WARN_ON(!conn_state))
    157		return;
    158
    159	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
    160	if (WARN_ON(!crtc_state))
    161		return;
    162
    163	meson_mode = meson_cvbs_get_mode(&crtc_state->adjusted_mode);
    164	if (WARN_ON(!meson_mode))
    165		return;
    166
    167	meson_venci_cvbs_mode_set(priv, meson_mode->enci);
    168
    169	/* Setup 27MHz vclk2 for ENCI and VDAC */
    170	meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS,
    171			 MESON_VCLK_CVBS, MESON_VCLK_CVBS,
    172			 MESON_VCLK_CVBS, MESON_VCLK_CVBS,
    173			 true);
    174
    175	/* VDAC0 source is not from ATV */
    176	writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0,
    177			    priv->io_base + _REG(VENC_VDAC_DACSEL0));
    178
    179	if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
    180		regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1);
    181		regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
    182	} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
    183		 meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
    184		regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001);
    185		regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
    186	} else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
    187		regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001);
    188		regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
    189	}
    190}
    191
    192static void meson_encoder_cvbs_atomic_disable(struct drm_bridge *bridge,
    193					      struct drm_bridge_state *bridge_state)
    194{
    195	struct meson_encoder_cvbs *meson_encoder_cvbs =
    196					bridge_to_meson_encoder_cvbs(bridge);
    197	struct meson_drm *priv = meson_encoder_cvbs->priv;
    198
    199	/* Disable CVBS VDAC */
    200	if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
    201		regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
    202		regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
    203	} else {
    204		regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
    205		regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8);
    206	}
    207}
    208
    209static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = {
    210	.attach = meson_encoder_cvbs_attach,
    211	.mode_valid = meson_encoder_cvbs_mode_valid,
    212	.get_modes = meson_encoder_cvbs_get_modes,
    213	.atomic_enable = meson_encoder_cvbs_atomic_enable,
    214	.atomic_disable = meson_encoder_cvbs_atomic_disable,
    215	.atomic_check = meson_encoder_cvbs_atomic_check,
    216	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
    217	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
    218	.atomic_reset = drm_atomic_helper_bridge_reset,
    219};
    220
    221int meson_encoder_cvbs_init(struct meson_drm *priv)
    222{
    223	struct drm_device *drm = priv->drm;
    224	struct meson_encoder_cvbs *meson_encoder_cvbs;
    225	struct drm_connector *connector;
    226	struct device_node *remote;
    227	int ret;
    228
    229	meson_encoder_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_encoder_cvbs), GFP_KERNEL);
    230	if (!meson_encoder_cvbs)
    231		return -ENOMEM;
    232
    233	/* CVBS Connector Bridge */
    234	remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0);
    235	if (!remote) {
    236		dev_info(drm->dev, "CVBS Output connector not available\n");
    237		return 0;
    238	}
    239
    240	meson_encoder_cvbs->next_bridge = of_drm_find_bridge(remote);
    241	if (!meson_encoder_cvbs->next_bridge) {
    242		dev_err(priv->dev, "Failed to find CVBS Connector bridge\n");
    243		return -EPROBE_DEFER;
    244	}
    245
    246	/* CVBS Encoder Bridge */
    247	meson_encoder_cvbs->bridge.funcs = &meson_encoder_cvbs_bridge_funcs;
    248	meson_encoder_cvbs->bridge.of_node = priv->dev->of_node;
    249	meson_encoder_cvbs->bridge.type = DRM_MODE_CONNECTOR_Composite;
    250	meson_encoder_cvbs->bridge.ops = DRM_BRIDGE_OP_MODES;
    251	meson_encoder_cvbs->bridge.interlace_allowed = true;
    252
    253	drm_bridge_add(&meson_encoder_cvbs->bridge);
    254
    255	meson_encoder_cvbs->priv = priv;
    256
    257	/* Encoder */
    258	ret = drm_simple_encoder_init(priv->drm, &meson_encoder_cvbs->encoder,
    259				      DRM_MODE_ENCODER_TVDAC);
    260	if (ret) {
    261		dev_err(priv->dev, "Failed to init CVBS encoder: %d\n", ret);
    262		return ret;
    263	}
    264
    265	meson_encoder_cvbs->encoder.possible_crtcs = BIT(0);
    266
    267	/* Attach CVBS Encoder Bridge to Encoder */
    268	ret = drm_bridge_attach(&meson_encoder_cvbs->encoder, &meson_encoder_cvbs->bridge, NULL,
    269				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
    270	if (ret) {
    271		dev_err(priv->dev, "Failed to attach bridge: %d\n", ret);
    272		return ret;
    273	}
    274
    275	/* Initialize & attach Bridge Connector */
    276	connector = drm_bridge_connector_init(priv->drm, &meson_encoder_cvbs->encoder);
    277	if (IS_ERR(connector)) {
    278		dev_err(priv->dev, "Unable to create CVBS bridge connector\n");
    279		return PTR_ERR(connector);
    280	}
    281	drm_connector_attach_encoder(connector, &meson_encoder_cvbs->encoder);
    282
    283	return 0;
    284}