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_drv.c (13459B)


      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) 2014 Endless Mobile
      6 *
      7 * Written by:
      8 *     Jasper St. Pierre <jstpierre@mecheye.net>
      9 */
     10
     11#include <linux/component.h>
     12#include <linux/module.h>
     13#include <linux/of_graph.h>
     14#include <linux/sys_soc.h>
     15#include <linux/platform_device.h>
     16#include <linux/soc/amlogic/meson-canvas.h>
     17
     18#include <drm/drm_aperture.h>
     19#include <drm/drm_atomic_helper.h>
     20#include <drm/drm_drv.h>
     21#include <drm/drm_fb_helper.h>
     22#include <drm/drm_gem_cma_helper.h>
     23#include <drm/drm_gem_framebuffer_helper.h>
     24#include <drm/drm_modeset_helper_vtables.h>
     25#include <drm/drm_module.h>
     26#include <drm/drm_probe_helper.h>
     27#include <drm/drm_vblank.h>
     28
     29#include "meson_crtc.h"
     30#include "meson_drv.h"
     31#include "meson_overlay.h"
     32#include "meson_plane.h"
     33#include "meson_osd_afbcd.h"
     34#include "meson_registers.h"
     35#include "meson_encoder_cvbs.h"
     36#include "meson_encoder_hdmi.h"
     37#include "meson_viu.h"
     38#include "meson_vpp.h"
     39#include "meson_rdma.h"
     40
     41#define DRIVER_NAME "meson"
     42#define DRIVER_DESC "Amlogic Meson DRM driver"
     43
     44/**
     45 * DOC: Video Processing Unit
     46 *
     47 * VPU Handles the Global Video Processing, it includes management of the
     48 * clocks gates, blocks reset lines and power domains.
     49 *
     50 * What is missing :
     51 *
     52 * - Full reset of entire video processing HW blocks
     53 * - Scaling and setup of the VPU clock
     54 * - Bus clock gates
     55 * - Powering up video processing HW blocks
     56 * - Powering Up HDMI controller and PHY
     57 */
     58
     59static const struct drm_mode_config_funcs meson_mode_config_funcs = {
     60	.atomic_check        = drm_atomic_helper_check,
     61	.atomic_commit       = drm_atomic_helper_commit,
     62	.fb_create           = drm_gem_fb_create,
     63};
     64
     65static const struct drm_mode_config_helper_funcs meson_mode_config_helpers = {
     66	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
     67};
     68
     69static irqreturn_t meson_irq(int irq, void *arg)
     70{
     71	struct drm_device *dev = arg;
     72	struct meson_drm *priv = dev->dev_private;
     73
     74	(void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG));
     75
     76	meson_crtc_irq(priv);
     77
     78	return IRQ_HANDLED;
     79}
     80
     81static int meson_dumb_create(struct drm_file *file, struct drm_device *dev,
     82			     struct drm_mode_create_dumb *args)
     83{
     84	/*
     85	 * We need 64bytes aligned stride, and PAGE aligned size
     86	 */
     87	args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), SZ_64);
     88	args->size = PAGE_ALIGN(args->pitch * args->height);
     89
     90	return drm_gem_cma_dumb_create_internal(file, dev, args);
     91}
     92
     93DEFINE_DRM_GEM_CMA_FOPS(fops);
     94
     95static const struct drm_driver meson_driver = {
     96	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
     97
     98	/* CMA Ops */
     99	DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(meson_dumb_create),
    100
    101	/* Misc */
    102	.fops			= &fops,
    103	.name			= DRIVER_NAME,
    104	.desc			= DRIVER_DESC,
    105	.date			= "20161109",
    106	.major			= 1,
    107	.minor			= 0,
    108};
    109
    110static bool meson_vpu_has_available_connectors(struct device *dev)
    111{
    112	struct device_node *ep, *remote;
    113
    114	/* Parses each endpoint and check if remote exists */
    115	for_each_endpoint_of_node(dev->of_node, ep) {
    116		/* If the endpoint node exists, consider it enabled */
    117		remote = of_graph_get_remote_port(ep);
    118		if (remote)
    119			return true;
    120	}
    121
    122	return false;
    123}
    124
    125static struct regmap_config meson_regmap_config = {
    126	.reg_bits       = 32,
    127	.val_bits       = 32,
    128	.reg_stride     = 4,
    129	.max_register   = 0x1000,
    130};
    131
    132static void meson_vpu_init(struct meson_drm *priv)
    133{
    134	u32 value;
    135
    136	/*
    137	 * Slave dc0 and dc5 connected to master port 1.
    138	 * By default other slaves are connected to master port 0.
    139	 */
    140	value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1) |
    141		VPU_RDARB_SLAVE_TO_MASTER_PORT(5, 1);
    142	writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C1));
    143
    144	/* Slave dc0 connected to master port 1 */
    145	value = VPU_RDARB_SLAVE_TO_MASTER_PORT(0, 1);
    146	writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L1C2));
    147
    148	/* Slave dc4 and dc7 connected to master port 1 */
    149	value = VPU_RDARB_SLAVE_TO_MASTER_PORT(4, 1) |
    150		VPU_RDARB_SLAVE_TO_MASTER_PORT(7, 1);
    151	writel_relaxed(value, priv->io_base + _REG(VPU_RDARB_MODE_L2C1));
    152
    153	/* Slave dc1 connected to master port 1 */
    154	value = VPU_RDARB_SLAVE_TO_MASTER_PORT(1, 1);
    155	writel_relaxed(value, priv->io_base + _REG(VPU_WRARB_MODE_L2C1));
    156}
    157
    158struct meson_drm_soc_attr {
    159	struct meson_drm_soc_limits limits;
    160	const struct soc_device_attribute *attrs;
    161};
    162
    163static const struct meson_drm_soc_attr meson_drm_soc_attrs[] = {
    164	/* S805X/S805Y HDMI PLL won't lock for HDMI PHY freq > 1,65GHz */
    165	{
    166		.limits = {
    167			.max_hdmi_phy_freq = 1650000,
    168		},
    169		.attrs = (const struct soc_device_attribute []) {
    170			{ .soc_id = "GXL (S805*)", },
    171			{ /* sentinel */ }
    172		}
    173	},
    174};
    175
    176static int meson_drv_bind_master(struct device *dev, bool has_components)
    177{
    178	struct platform_device *pdev = to_platform_device(dev);
    179	const struct meson_drm_match_data *match;
    180	struct meson_drm *priv;
    181	struct drm_device *drm;
    182	struct resource *res;
    183	void __iomem *regs;
    184	int ret, i;
    185
    186	/* Checks if an output connector is available */
    187	if (!meson_vpu_has_available_connectors(dev)) {
    188		dev_err(dev, "No output connector available\n");
    189		return -ENODEV;
    190	}
    191
    192	match = of_device_get_match_data(dev);
    193	if (!match)
    194		return -ENODEV;
    195
    196	drm = drm_dev_alloc(&meson_driver, dev);
    197	if (IS_ERR(drm))
    198		return PTR_ERR(drm);
    199
    200	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
    201	if (!priv) {
    202		ret = -ENOMEM;
    203		goto free_drm;
    204	}
    205	drm->dev_private = priv;
    206	priv->drm = drm;
    207	priv->dev = dev;
    208	priv->compat = match->compat;
    209	priv->afbcd.ops = match->afbcd_ops;
    210
    211	regs = devm_platform_ioremap_resource_byname(pdev, "vpu");
    212	if (IS_ERR(regs)) {
    213		ret = PTR_ERR(regs);
    214		goto free_drm;
    215	}
    216
    217	priv->io_base = regs;
    218
    219	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi");
    220	if (!res) {
    221		ret = -EINVAL;
    222		goto free_drm;
    223	}
    224	/* Simply ioremap since it may be a shared register zone */
    225	regs = devm_ioremap(dev, res->start, resource_size(res));
    226	if (!regs) {
    227		ret = -EADDRNOTAVAIL;
    228		goto free_drm;
    229	}
    230
    231	priv->hhi = devm_regmap_init_mmio(dev, regs,
    232					  &meson_regmap_config);
    233	if (IS_ERR(priv->hhi)) {
    234		dev_err(&pdev->dev, "Couldn't create the HHI regmap\n");
    235		ret = PTR_ERR(priv->hhi);
    236		goto free_drm;
    237	}
    238
    239	priv->canvas = meson_canvas_get(dev);
    240	if (IS_ERR(priv->canvas)) {
    241		ret = PTR_ERR(priv->canvas);
    242		goto free_drm;
    243	}
    244
    245	ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1);
    246	if (ret)
    247		goto free_drm;
    248	ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0);
    249	if (ret) {
    250		meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
    251		goto free_drm;
    252	}
    253	ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1);
    254	if (ret) {
    255		meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
    256		meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
    257		goto free_drm;
    258	}
    259	ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2);
    260	if (ret) {
    261		meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
    262		meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
    263		meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
    264		goto free_drm;
    265	}
    266
    267	priv->vsync_irq = platform_get_irq(pdev, 0);
    268
    269	ret = drm_vblank_init(drm, 1);
    270	if (ret)
    271		goto free_drm;
    272
    273	/* Assign limits per soc revision/package */
    274	for (i = 0 ; i < ARRAY_SIZE(meson_drm_soc_attrs) ; ++i) {
    275		if (soc_device_match(meson_drm_soc_attrs[i].attrs)) {
    276			priv->limits = &meson_drm_soc_attrs[i].limits;
    277			break;
    278		}
    279	}
    280
    281	/*
    282	 * Remove early framebuffers (ie. simplefb). The framebuffer can be
    283	 * located anywhere in RAM
    284	 */
    285	ret = drm_aperture_remove_framebuffers(false, &meson_driver);
    286	if (ret)
    287		goto free_drm;
    288
    289	ret = drmm_mode_config_init(drm);
    290	if (ret)
    291		goto free_drm;
    292	drm->mode_config.max_width = 3840;
    293	drm->mode_config.max_height = 2160;
    294	drm->mode_config.funcs = &meson_mode_config_funcs;
    295	drm->mode_config.helper_private	= &meson_mode_config_helpers;
    296
    297	/* Hardware Initialization */
    298
    299	meson_vpu_init(priv);
    300	meson_venc_init(priv);
    301	meson_vpp_init(priv);
    302	meson_viu_init(priv);
    303	if (priv->afbcd.ops) {
    304		ret = priv->afbcd.ops->init(priv);
    305		if (ret)
    306			goto free_drm;
    307	}
    308
    309	/* Encoder Initialization */
    310
    311	ret = meson_encoder_cvbs_init(priv);
    312	if (ret)
    313		goto exit_afbcd;
    314
    315	if (has_components) {
    316		ret = component_bind_all(drm->dev, drm);
    317		if (ret) {
    318			dev_err(drm->dev, "Couldn't bind all components\n");
    319			goto exit_afbcd;
    320		}
    321	}
    322
    323	ret = meson_encoder_hdmi_init(priv);
    324	if (ret)
    325		goto exit_afbcd;
    326
    327	ret = meson_plane_create(priv);
    328	if (ret)
    329		goto exit_afbcd;
    330
    331	ret = meson_overlay_create(priv);
    332	if (ret)
    333		goto exit_afbcd;
    334
    335	ret = meson_crtc_create(priv);
    336	if (ret)
    337		goto exit_afbcd;
    338
    339	ret = request_irq(priv->vsync_irq, meson_irq, 0, drm->driver->name, drm);
    340	if (ret)
    341		goto exit_afbcd;
    342
    343	drm_mode_config_reset(drm);
    344
    345	drm_kms_helper_poll_init(drm);
    346
    347	platform_set_drvdata(pdev, priv);
    348
    349	ret = drm_dev_register(drm, 0);
    350	if (ret)
    351		goto uninstall_irq;
    352
    353	drm_fbdev_generic_setup(drm, 32);
    354
    355	return 0;
    356
    357uninstall_irq:
    358	free_irq(priv->vsync_irq, drm);
    359exit_afbcd:
    360	if (priv->afbcd.ops)
    361		priv->afbcd.ops->exit(priv);
    362free_drm:
    363	drm_dev_put(drm);
    364
    365	return ret;
    366}
    367
    368static int meson_drv_bind(struct device *dev)
    369{
    370	return meson_drv_bind_master(dev, true);
    371}
    372
    373static void meson_drv_unbind(struct device *dev)
    374{
    375	struct meson_drm *priv = dev_get_drvdata(dev);
    376	struct drm_device *drm = priv->drm;
    377
    378	if (priv->canvas) {
    379		meson_canvas_free(priv->canvas, priv->canvas_id_osd1);
    380		meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0);
    381		meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1);
    382		meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2);
    383	}
    384
    385	drm_dev_unregister(drm);
    386	drm_kms_helper_poll_fini(drm);
    387	drm_atomic_helper_shutdown(drm);
    388	component_unbind_all(dev, drm);
    389	free_irq(priv->vsync_irq, drm);
    390	drm_dev_put(drm);
    391
    392	if (priv->afbcd.ops)
    393		priv->afbcd.ops->exit(priv);
    394}
    395
    396static const struct component_master_ops meson_drv_master_ops = {
    397	.bind	= meson_drv_bind,
    398	.unbind	= meson_drv_unbind,
    399};
    400
    401static int __maybe_unused meson_drv_pm_suspend(struct device *dev)
    402{
    403	struct meson_drm *priv = dev_get_drvdata(dev);
    404
    405	if (!priv)
    406		return 0;
    407
    408	return drm_mode_config_helper_suspend(priv->drm);
    409}
    410
    411static int __maybe_unused meson_drv_pm_resume(struct device *dev)
    412{
    413	struct meson_drm *priv = dev_get_drvdata(dev);
    414
    415	if (!priv)
    416		return 0;
    417
    418	meson_vpu_init(priv);
    419	meson_venc_init(priv);
    420	meson_vpp_init(priv);
    421	meson_viu_init(priv);
    422	if (priv->afbcd.ops)
    423		priv->afbcd.ops->init(priv);
    424
    425	return drm_mode_config_helper_resume(priv->drm);
    426}
    427
    428static void meson_drv_shutdown(struct platform_device *pdev)
    429{
    430	struct meson_drm *priv = dev_get_drvdata(&pdev->dev);
    431
    432	if (!priv)
    433		return;
    434
    435	drm_kms_helper_poll_fini(priv->drm);
    436	drm_atomic_helper_shutdown(priv->drm);
    437}
    438
    439/* Possible connectors nodes to ignore */
    440static const struct of_device_id connectors_match[] = {
    441	{ .compatible = "composite-video-connector" },
    442	{ .compatible = "svideo-connector" },
    443	{}
    444};
    445
    446static int meson_drv_probe(struct platform_device *pdev)
    447{
    448	struct component_match *match = NULL;
    449	struct device_node *np = pdev->dev.of_node;
    450	struct device_node *ep, *remote;
    451	int count = 0;
    452
    453	for_each_endpoint_of_node(np, ep) {
    454		remote = of_graph_get_remote_port_parent(ep);
    455		if (!remote || !of_device_is_available(remote)) {
    456			of_node_put(remote);
    457			continue;
    458		}
    459
    460		/* If an analog connector is detected, count it as an output */
    461		if (of_match_node(connectors_match, remote)) {
    462			++count;
    463			of_node_put(remote);
    464			continue;
    465		}
    466
    467		dev_dbg(&pdev->dev, "parent %pOF remote match add %pOF parent %s\n",
    468			np, remote, dev_name(&pdev->dev));
    469
    470		component_match_add(&pdev->dev, &match, component_compare_of, remote);
    471
    472		of_node_put(remote);
    473
    474		++count;
    475	}
    476
    477	if (count && !match)
    478		return meson_drv_bind_master(&pdev->dev, false);
    479
    480	/* If some endpoints were found, initialize the nodes */
    481	if (count) {
    482		dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count);
    483
    484		return component_master_add_with_match(&pdev->dev,
    485						       &meson_drv_master_ops,
    486						       match);
    487	}
    488
    489	/* If no output endpoints were available, simply bail out */
    490	return 0;
    491};
    492
    493static struct meson_drm_match_data meson_drm_gxbb_data = {
    494	.compat = VPU_COMPATIBLE_GXBB,
    495};
    496
    497static struct meson_drm_match_data meson_drm_gxl_data = {
    498	.compat = VPU_COMPATIBLE_GXL,
    499};
    500
    501static struct meson_drm_match_data meson_drm_gxm_data = {
    502	.compat = VPU_COMPATIBLE_GXM,
    503	.afbcd_ops = &meson_afbcd_gxm_ops,
    504};
    505
    506static struct meson_drm_match_data meson_drm_g12a_data = {
    507	.compat = VPU_COMPATIBLE_G12A,
    508	.afbcd_ops = &meson_afbcd_g12a_ops,
    509};
    510
    511static const struct of_device_id dt_match[] = {
    512	{ .compatible = "amlogic,meson-gxbb-vpu",
    513	  .data       = (void *)&meson_drm_gxbb_data },
    514	{ .compatible = "amlogic,meson-gxl-vpu",
    515	  .data       = (void *)&meson_drm_gxl_data },
    516	{ .compatible = "amlogic,meson-gxm-vpu",
    517	  .data       = (void *)&meson_drm_gxm_data },
    518	{ .compatible = "amlogic,meson-g12a-vpu",
    519	  .data       = (void *)&meson_drm_g12a_data },
    520	{}
    521};
    522MODULE_DEVICE_TABLE(of, dt_match);
    523
    524static const struct dev_pm_ops meson_drv_pm_ops = {
    525	SET_SYSTEM_SLEEP_PM_OPS(meson_drv_pm_suspend, meson_drv_pm_resume)
    526};
    527
    528static struct platform_driver meson_drm_platform_driver = {
    529	.probe      = meson_drv_probe,
    530	.shutdown   = meson_drv_shutdown,
    531	.driver     = {
    532		.name	= "meson-drm",
    533		.of_match_table = dt_match,
    534		.pm = &meson_drv_pm_ops,
    535	},
    536};
    537
    538drm_module_platform_driver(meson_drm_platform_driver);
    539
    540MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>");
    541MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
    542MODULE_DESCRIPTION(DRIVER_DESC);
    543MODULE_LICENSE("GPL");