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

sun4i_drv.c (12587B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2015 Free Electrons
      4 * Copyright (C) 2015 NextThing Co
      5 *
      6 * Maxime Ripard <maxime.ripard@free-electrons.com>
      7 */
      8
      9#include <linux/component.h>
     10#include <linux/dma-mapping.h>
     11#include <linux/kfifo.h>
     12#include <linux/module.h>
     13#include <linux/of_graph.h>
     14#include <linux/of_reserved_mem.h>
     15#include <linux/platform_device.h>
     16
     17#include <drm/drm_aperture.h>
     18#include <drm/drm_atomic_helper.h>
     19#include <drm/drm_drv.h>
     20#include <drm/drm_fb_cma_helper.h>
     21#include <drm/drm_fb_helper.h>
     22#include <drm/drm_gem_cma_helper.h>
     23#include <drm/drm_module.h>
     24#include <drm/drm_of.h>
     25#include <drm/drm_probe_helper.h>
     26#include <drm/drm_vblank.h>
     27
     28#include "sun4i_drv.h"
     29#include "sun4i_frontend.h"
     30#include "sun4i_framebuffer.h"
     31#include "sun4i_tcon.h"
     32#include "sun8i_tcon_top.h"
     33
     34static int drm_sun4i_gem_dumb_create(struct drm_file *file_priv,
     35				     struct drm_device *drm,
     36				     struct drm_mode_create_dumb *args)
     37{
     38	/* The hardware only allows even pitches for YUV buffers. */
     39	args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), 2);
     40
     41	return drm_gem_cma_dumb_create_internal(file_priv, drm, args);
     42}
     43
     44DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops);
     45
     46static const struct drm_driver sun4i_drv_driver = {
     47	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
     48
     49	/* Generic Operations */
     50	.fops			= &sun4i_drv_fops,
     51	.name			= "sun4i-drm",
     52	.desc			= "Allwinner sun4i Display Engine",
     53	.date			= "20150629",
     54	.major			= 1,
     55	.minor			= 0,
     56
     57	/* GEM Operations */
     58	DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(drm_sun4i_gem_dumb_create),
     59};
     60
     61static int sun4i_drv_bind(struct device *dev)
     62{
     63	struct drm_device *drm;
     64	struct sun4i_drv *drv;
     65	int ret;
     66
     67	drm = drm_dev_alloc(&sun4i_drv_driver, dev);
     68	if (IS_ERR(drm))
     69		return PTR_ERR(drm);
     70
     71	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
     72	if (!drv) {
     73		ret = -ENOMEM;
     74		goto free_drm;
     75	}
     76
     77	drm->dev_private = drv;
     78	INIT_LIST_HEAD(&drv->frontend_list);
     79	INIT_LIST_HEAD(&drv->engine_list);
     80	INIT_LIST_HEAD(&drv->tcon_list);
     81
     82	ret = of_reserved_mem_device_init(dev);
     83	if (ret && ret != -ENODEV) {
     84		dev_err(drm->dev, "Couldn't claim our memory region\n");
     85		goto free_drm;
     86	}
     87
     88	drm_mode_config_init(drm);
     89
     90	ret = component_bind_all(drm->dev, drm);
     91	if (ret) {
     92		dev_err(drm->dev, "Couldn't bind all pipelines components\n");
     93		goto cleanup_mode_config;
     94	}
     95
     96	/* drm_vblank_init calls kcalloc, which can fail */
     97	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
     98	if (ret)
     99		goto cleanup_mode_config;
    100
    101	/* Remove early framebuffers (ie. simplefb) */
    102	ret = drm_aperture_remove_framebuffers(false, &sun4i_drv_driver);
    103	if (ret)
    104		goto cleanup_mode_config;
    105
    106	sun4i_framebuffer_init(drm);
    107
    108	/* Enable connectors polling */
    109	drm_kms_helper_poll_init(drm);
    110
    111	ret = drm_dev_register(drm, 0);
    112	if (ret)
    113		goto finish_poll;
    114
    115	drm_fbdev_generic_setup(drm, 32);
    116
    117	dev_set_drvdata(dev, drm);
    118
    119	return 0;
    120
    121finish_poll:
    122	drm_kms_helper_poll_fini(drm);
    123cleanup_mode_config:
    124	drm_mode_config_cleanup(drm);
    125	of_reserved_mem_device_release(dev);
    126free_drm:
    127	drm_dev_put(drm);
    128	return ret;
    129}
    130
    131static void sun4i_drv_unbind(struct device *dev)
    132{
    133	struct drm_device *drm = dev_get_drvdata(dev);
    134
    135	dev_set_drvdata(dev, NULL);
    136	drm_dev_unregister(drm);
    137	drm_kms_helper_poll_fini(drm);
    138	drm_atomic_helper_shutdown(drm);
    139	drm_mode_config_cleanup(drm);
    140
    141	component_unbind_all(dev, NULL);
    142	of_reserved_mem_device_release(dev);
    143
    144	drm_dev_put(drm);
    145}
    146
    147static const struct component_master_ops sun4i_drv_master_ops = {
    148	.bind	= sun4i_drv_bind,
    149	.unbind	= sun4i_drv_unbind,
    150};
    151
    152static bool sun4i_drv_node_is_connector(struct device_node *node)
    153{
    154	return of_device_is_compatible(node, "hdmi-connector");
    155}
    156
    157static bool sun4i_drv_node_is_frontend(struct device_node *node)
    158{
    159	return of_device_is_compatible(node, "allwinner,sun4i-a10-display-frontend") ||
    160		of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
    161		of_device_is_compatible(node, "allwinner,sun6i-a31-display-frontend") ||
    162		of_device_is_compatible(node, "allwinner,sun7i-a20-display-frontend") ||
    163		of_device_is_compatible(node, "allwinner,sun8i-a23-display-frontend") ||
    164		of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend") ||
    165		of_device_is_compatible(node, "allwinner,sun9i-a80-display-frontend");
    166}
    167
    168static bool sun4i_drv_node_is_deu(struct device_node *node)
    169{
    170	return of_device_is_compatible(node, "allwinner,sun9i-a80-deu");
    171}
    172
    173static bool sun4i_drv_node_is_supported_frontend(struct device_node *node)
    174{
    175	if (IS_ENABLED(CONFIG_DRM_SUN4I_BACKEND))
    176		return !!of_match_node(sun4i_frontend_of_table, node);
    177
    178	return false;
    179}
    180
    181static bool sun4i_drv_node_is_tcon(struct device_node *node)
    182{
    183	return !!of_match_node(sun4i_tcon_of_table, node);
    184}
    185
    186static bool sun4i_drv_node_is_tcon_with_ch0(struct device_node *node)
    187{
    188	const struct of_device_id *match;
    189
    190	match = of_match_node(sun4i_tcon_of_table, node);
    191	if (match) {
    192		struct sun4i_tcon_quirks *quirks;
    193
    194		quirks = (struct sun4i_tcon_quirks *)match->data;
    195
    196		return quirks->has_channel_0;
    197	}
    198
    199	return false;
    200}
    201
    202static bool sun4i_drv_node_is_tcon_top(struct device_node *node)
    203{
    204	return IS_ENABLED(CONFIG_DRM_SUN8I_TCON_TOP) &&
    205		!!of_match_node(sun8i_tcon_top_of_table, node);
    206}
    207
    208/*
    209 * The encoder drivers use drm_of_find_possible_crtcs to get upstream
    210 * crtcs from the device tree using of_graph. For the results to be
    211 * correct, encoders must be probed/bound after _all_ crtcs have been
    212 * created. The existing code uses a depth first recursive traversal
    213 * of the of_graph, which means the encoders downstream of the TCON
    214 * get add right after the first TCON. The second TCON or CRTC will
    215 * never be properly associated with encoders connected to it.
    216 *
    217 * Also, in a dual display pipeline setup, both frontends can feed
    218 * either backend, and both backends can feed either TCON, we want
    219 * all components of the same type to be added before the next type
    220 * in the pipeline. Fortunately, the pipelines are perfectly symmetric,
    221 * i.e. components of the same type are at the same depth when counted
    222 * from the frontend. The only exception is the third pipeline in
    223 * the A80 SoC, which we do not support anyway.
    224 *
    225 * Hence we can use a breadth first search traversal order to add
    226 * components. We do not need to check for duplicates. The component
    227 * matching system handles this for us.
    228 */
    229struct endpoint_list {
    230	DECLARE_KFIFO(fifo, struct device_node *, 16);
    231};
    232
    233static void sun4i_drv_traverse_endpoints(struct endpoint_list *list,
    234					 struct device_node *node,
    235					 int port_id)
    236{
    237	struct device_node *ep, *remote, *port;
    238
    239	port = of_graph_get_port_by_id(node, port_id);
    240	if (!port) {
    241		DRM_DEBUG_DRIVER("No output to bind on port %d\n", port_id);
    242		return;
    243	}
    244
    245	for_each_available_child_of_node(port, ep) {
    246		remote = of_graph_get_remote_port_parent(ep);
    247		if (!remote) {
    248			DRM_DEBUG_DRIVER("Error retrieving the output node\n");
    249			continue;
    250		}
    251
    252		if (sun4i_drv_node_is_tcon(node)) {
    253			/*
    254			 * TCON TOP is always probed before TCON. However, TCON
    255			 * points back to TCON TOP when it is source for HDMI.
    256			 * We have to skip it here to prevent infinite looping
    257			 * between TCON TOP and TCON.
    258			 */
    259			if (sun4i_drv_node_is_tcon_top(remote)) {
    260				DRM_DEBUG_DRIVER("TCON output endpoint is TCON TOP... skipping\n");
    261				of_node_put(remote);
    262				continue;
    263			}
    264
    265			/*
    266			 * If the node is our TCON with channel 0, the first
    267			 * port is used for panel or bridges, and will not be
    268			 * part of the component framework.
    269			 */
    270			if (sun4i_drv_node_is_tcon_with_ch0(node)) {
    271				struct of_endpoint endpoint;
    272
    273				if (of_graph_parse_endpoint(ep, &endpoint)) {
    274					DRM_DEBUG_DRIVER("Couldn't parse endpoint\n");
    275					of_node_put(remote);
    276					continue;
    277				}
    278
    279				if (!endpoint.id) {
    280					DRM_DEBUG_DRIVER("Endpoint is our panel... skipping\n");
    281					of_node_put(remote);
    282					continue;
    283				}
    284			}
    285		}
    286
    287		kfifo_put(&list->fifo, remote);
    288	}
    289}
    290
    291static int sun4i_drv_add_endpoints(struct device *dev,
    292				   struct endpoint_list *list,
    293				   struct component_match **match,
    294				   struct device_node *node)
    295{
    296	int count = 0;
    297
    298	/*
    299	 * The frontend has been disabled in some of our old device
    300	 * trees. If we find a node that is the frontend and is
    301	 * disabled, we should just follow through and parse its
    302	 * child, but without adding it to the component list.
    303	 * Otherwise, we obviously want to add it to the list.
    304	 */
    305	if (!sun4i_drv_node_is_frontend(node) &&
    306	    !of_device_is_available(node))
    307		return 0;
    308
    309	/*
    310	 * The connectors will be the last nodes in our pipeline, we
    311	 * can just bail out.
    312	 */
    313	if (sun4i_drv_node_is_connector(node))
    314		return 0;
    315
    316	/*
    317	 * If the device is either just a regular device, or an
    318	 * enabled frontend supported by the driver, we add it to our
    319	 * component list.
    320	 */
    321	if (!(sun4i_drv_node_is_frontend(node) ||
    322	      sun4i_drv_node_is_deu(node)) ||
    323	    (sun4i_drv_node_is_supported_frontend(node) &&
    324	     of_device_is_available(node))) {
    325		/* Add current component */
    326		DRM_DEBUG_DRIVER("Adding component %pOF\n", node);
    327		drm_of_component_match_add(dev, match, component_compare_of, node);
    328		count++;
    329	}
    330
    331	/* each node has at least one output */
    332	sun4i_drv_traverse_endpoints(list, node, 1);
    333
    334	/* TCON TOP has second and third output */
    335	if (sun4i_drv_node_is_tcon_top(node)) {
    336		sun4i_drv_traverse_endpoints(list, node, 3);
    337		sun4i_drv_traverse_endpoints(list, node, 5);
    338	}
    339
    340	return count;
    341}
    342
    343#ifdef CONFIG_PM_SLEEP
    344static int sun4i_drv_drm_sys_suspend(struct device *dev)
    345{
    346	struct drm_device *drm = dev_get_drvdata(dev);
    347
    348	return drm_mode_config_helper_suspend(drm);
    349}
    350
    351static int sun4i_drv_drm_sys_resume(struct device *dev)
    352{
    353	struct drm_device *drm = dev_get_drvdata(dev);
    354
    355	return drm_mode_config_helper_resume(drm);
    356}
    357#endif
    358
    359static const struct dev_pm_ops sun4i_drv_drm_pm_ops = {
    360	SET_SYSTEM_SLEEP_PM_OPS(sun4i_drv_drm_sys_suspend,
    361				sun4i_drv_drm_sys_resume)
    362};
    363
    364static int sun4i_drv_probe(struct platform_device *pdev)
    365{
    366	struct component_match *match = NULL;
    367	struct device_node *np = pdev->dev.of_node, *endpoint;
    368	struct endpoint_list list;
    369	int i, ret, count = 0;
    370
    371	INIT_KFIFO(list.fifo);
    372
    373	/*
    374	 * DE2 and DE3 cores actually supports 40-bit addresses, but
    375	 * driver does not.
    376	 */
    377	dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
    378	dma_set_max_seg_size(&pdev->dev, UINT_MAX);
    379
    380	for (i = 0;; i++) {
    381		struct device_node *pipeline = of_parse_phandle(np,
    382								"allwinner,pipelines",
    383								i);
    384		if (!pipeline)
    385			break;
    386
    387		kfifo_put(&list.fifo, pipeline);
    388	}
    389
    390	while (kfifo_get(&list.fifo, &endpoint)) {
    391		/* process this endpoint */
    392		ret = sun4i_drv_add_endpoints(&pdev->dev, &list, &match,
    393					      endpoint);
    394
    395		/* sun4i_drv_add_endpoints can fail to allocate memory */
    396		if (ret < 0)
    397			return ret;
    398
    399		count += ret;
    400	}
    401
    402	if (count)
    403		return component_master_add_with_match(&pdev->dev,
    404						       &sun4i_drv_master_ops,
    405						       match);
    406	else
    407		return 0;
    408}
    409
    410static int sun4i_drv_remove(struct platform_device *pdev)
    411{
    412	component_master_del(&pdev->dev, &sun4i_drv_master_ops);
    413
    414	return 0;
    415}
    416
    417static const struct of_device_id sun4i_drv_of_table[] = {
    418	{ .compatible = "allwinner,sun4i-a10-display-engine" },
    419	{ .compatible = "allwinner,sun5i-a10s-display-engine" },
    420	{ .compatible = "allwinner,sun5i-a13-display-engine" },
    421	{ .compatible = "allwinner,sun6i-a31-display-engine" },
    422	{ .compatible = "allwinner,sun6i-a31s-display-engine" },
    423	{ .compatible = "allwinner,sun7i-a20-display-engine" },
    424	{ .compatible = "allwinner,sun8i-a23-display-engine" },
    425	{ .compatible = "allwinner,sun8i-a33-display-engine" },
    426	{ .compatible = "allwinner,sun8i-a83t-display-engine" },
    427	{ .compatible = "allwinner,sun8i-h3-display-engine" },
    428	{ .compatible = "allwinner,sun8i-r40-display-engine" },
    429	{ .compatible = "allwinner,sun8i-v3s-display-engine" },
    430	{ .compatible = "allwinner,sun9i-a80-display-engine" },
    431	{ .compatible = "allwinner,sun20i-d1-display-engine" },
    432	{ .compatible = "allwinner,sun50i-a64-display-engine" },
    433	{ .compatible = "allwinner,sun50i-h6-display-engine" },
    434	{ }
    435};
    436MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
    437
    438static struct platform_driver sun4i_drv_platform_driver = {
    439	.probe		= sun4i_drv_probe,
    440	.remove		= sun4i_drv_remove,
    441	.driver		= {
    442		.name		= "sun4i-drm",
    443		.of_match_table	= sun4i_drv_of_table,
    444		.pm = &sun4i_drv_drm_pm_ops,
    445	},
    446};
    447drm_module_platform_driver(sun4i_drv_platform_driver);
    448
    449MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
    450MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
    451MODULE_DESCRIPTION("Allwinner A10 Display Engine DRM/KMS Driver");
    452MODULE_LICENSE("GPL");