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

mcde_drv.c (13701B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2018 Linus Walleij <linus.walleij@linaro.org>
      4 * Parts of this file were based on the MCDE driver by Marcus Lorentzon
      5 * (C) ST-Ericsson SA 2013
      6 */
      7
      8/**
      9 * DOC: ST-Ericsson MCDE Driver
     10 *
     11 * The MCDE (short for multi-channel display engine) is a graphics
     12 * controller found in the Ux500 chipsets, such as NovaThor U8500.
     13 * It was initially conceptualized by ST Microelectronics for the
     14 * successor of the Nomadik line, STn8500 but productified in the
     15 * ST-Ericsson U8500 where is was used for mass-market deployments
     16 * in Android phones from Samsung and Sony Ericsson.
     17 *
     18 * It can do 1080p30 on SDTV CCIR656, DPI-2, DBI-2 or DSI for
     19 * panels with or without frame buffering and can convert most
     20 * input formats including most variants of RGB and YUV.
     21 *
     22 * The hardware has four display pipes, and the layout is a little
     23 * bit like this::
     24 *
     25 *   Memory     -> Overlay -> Channel -> FIFO -> 8 formatters -> DSI/DPI
     26 *   External      0..5       0..3       A,B,    6 x DSI         bridge
     27 *   source 0..9                         C0,C1   2 x DPI
     28 *
     29 * FIFOs A and B are for LCD and HDMI while FIFO CO/C1 are for
     30 * panels with embedded buffer.
     31 * 6 of the formatters are for DSI, 3 pairs for VID/CMD respectively.
     32 * 2 of the formatters are for DPI.
     33 *
     34 * Behind the formatters are the DSI or DPI ports that route to
     35 * the external pins of the chip. As there are 3 DSI ports and one
     36 * DPI port, it is possible to configure up to 4 display pipelines
     37 * (effectively using channels 0..3) for concurrent use.
     38 *
     39 * In the current DRM/KMS setup, we use one external source, one overlay,
     40 * one FIFO and one formatter which we connect to the simple CMA framebuffer
     41 * helpers. We then provide a bridge to the DSI port, and on the DSI port
     42 * bridge we connect hang a panel bridge or other bridge. This may be subject
     43 * to change as we exploit more of the hardware capabilities.
     44 *
     45 * TODO:
     46 *
     47 * - Enabled damaged rectangles using drm_plane_enable_fb_damage_clips()
     48 *   so we can selectively just transmit the damaged area to a
     49 *   command-only display.
     50 * - Enable mixing of more planes, possibly at the cost of moving away
     51 *   from using the simple framebuffer pipeline.
     52 * - Enable output to bridges such as the AV8100 HDMI encoder from
     53 *   the DSI bridge.
     54 */
     55
     56#include <linux/clk.h>
     57#include <linux/component.h>
     58#include <linux/dma-buf.h>
     59#include <linux/irq.h>
     60#include <linux/io.h>
     61#include <linux/module.h>
     62#include <linux/of_platform.h>
     63#include <linux/platform_device.h>
     64#include <linux/regulator/consumer.h>
     65#include <linux/slab.h>
     66#include <linux/delay.h>
     67
     68#include <drm/drm_atomic_helper.h>
     69#include <drm/drm_bridge.h>
     70#include <drm/drm_drv.h>
     71#include <drm/drm_fb_cma_helper.h>
     72#include <drm/drm_fb_helper.h>
     73#include <drm/drm_gem.h>
     74#include <drm/drm_gem_cma_helper.h>
     75#include <drm/drm_gem_framebuffer_helper.h>
     76#include <drm/drm_managed.h>
     77#include <drm/drm_of.h>
     78#include <drm/drm_probe_helper.h>
     79#include <drm/drm_panel.h>
     80#include <drm/drm_vblank.h>
     81
     82#include "mcde_drm.h"
     83
     84#define DRIVER_DESC	"DRM module for MCDE"
     85
     86#define MCDE_PID 0x000001FC
     87#define MCDE_PID_METALFIX_VERSION_SHIFT 0
     88#define MCDE_PID_METALFIX_VERSION_MASK 0x000000FF
     89#define MCDE_PID_DEVELOPMENT_VERSION_SHIFT 8
     90#define MCDE_PID_DEVELOPMENT_VERSION_MASK 0x0000FF00
     91#define MCDE_PID_MINOR_VERSION_SHIFT 16
     92#define MCDE_PID_MINOR_VERSION_MASK 0x00FF0000
     93#define MCDE_PID_MAJOR_VERSION_SHIFT 24
     94#define MCDE_PID_MAJOR_VERSION_MASK 0xFF000000
     95
     96static const struct drm_mode_config_funcs mcde_mode_config_funcs = {
     97	.fb_create = drm_gem_fb_create_with_dirty,
     98	.atomic_check = drm_atomic_helper_check,
     99	.atomic_commit = drm_atomic_helper_commit,
    100};
    101
    102static const struct drm_mode_config_helper_funcs mcde_mode_config_helpers = {
    103	/*
    104	 * Using this function is necessary to commit atomic updates
    105	 * that need the CRTC to be enabled before a commit, as is
    106	 * the case with e.g. DSI displays.
    107	 */
    108	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
    109};
    110
    111static irqreturn_t mcde_irq(int irq, void *data)
    112{
    113	struct mcde *mcde = data;
    114	u32 val;
    115
    116	val = readl(mcde->regs + MCDE_MISERR);
    117
    118	mcde_display_irq(mcde);
    119
    120	if (val)
    121		dev_info(mcde->dev, "some error IRQ\n");
    122	writel(val, mcde->regs + MCDE_RISERR);
    123
    124	return IRQ_HANDLED;
    125}
    126
    127static int mcde_modeset_init(struct drm_device *drm)
    128{
    129	struct drm_mode_config *mode_config;
    130	struct mcde *mcde = to_mcde(drm);
    131	int ret;
    132
    133	/*
    134	 * If no other bridge was found, check if we have a DPI panel or
    135	 * any other bridge connected directly to the MCDE DPI output.
    136	 * If a DSI bridge is found, DSI will take precedence.
    137	 *
    138	 * TODO: more elaborate bridge selection if we have more than one
    139	 * thing attached to the system.
    140	 */
    141	if (!mcde->bridge) {
    142		struct drm_panel *panel;
    143		struct drm_bridge *bridge;
    144
    145		ret = drm_of_find_panel_or_bridge(drm->dev->of_node,
    146						  0, 0, &panel, &bridge);
    147		if (ret) {
    148			dev_err(drm->dev,
    149				"Could not locate any output bridge or panel\n");
    150			return ret;
    151		}
    152		if (panel) {
    153			bridge = drm_panel_bridge_add_typed(panel,
    154					DRM_MODE_CONNECTOR_DPI);
    155			if (IS_ERR(bridge)) {
    156				dev_err(drm->dev,
    157					"Could not connect panel bridge\n");
    158				return PTR_ERR(bridge);
    159			}
    160		}
    161		mcde->dpi_output = true;
    162		mcde->bridge = bridge;
    163		mcde->flow_mode = MCDE_DPI_FORMATTER_FLOW;
    164	}
    165
    166	mode_config = &drm->mode_config;
    167	mode_config->funcs = &mcde_mode_config_funcs;
    168	mode_config->helper_private = &mcde_mode_config_helpers;
    169	/* This hardware can do 1080p */
    170	mode_config->min_width = 1;
    171	mode_config->max_width = 1920;
    172	mode_config->min_height = 1;
    173	mode_config->max_height = 1080;
    174
    175	ret = drm_vblank_init(drm, 1);
    176	if (ret) {
    177		dev_err(drm->dev, "failed to init vblank\n");
    178		return ret;
    179	}
    180
    181	ret = mcde_display_init(drm);
    182	if (ret) {
    183		dev_err(drm->dev, "failed to init display\n");
    184		return ret;
    185	}
    186
    187	/* Attach the bridge. */
    188	ret = drm_simple_display_pipe_attach_bridge(&mcde->pipe,
    189						    mcde->bridge);
    190	if (ret) {
    191		dev_err(drm->dev, "failed to attach display output bridge\n");
    192		return ret;
    193	}
    194
    195	drm_mode_config_reset(drm);
    196	drm_kms_helper_poll_init(drm);
    197
    198	return 0;
    199}
    200
    201DEFINE_DRM_GEM_CMA_FOPS(drm_fops);
    202
    203static const struct drm_driver mcde_drm_driver = {
    204	.driver_features =
    205		DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
    206	.lastclose = drm_fb_helper_lastclose,
    207	.ioctls = NULL,
    208	.fops = &drm_fops,
    209	.name = "mcde",
    210	.desc = DRIVER_DESC,
    211	.date = "20180529",
    212	.major = 1,
    213	.minor = 0,
    214	.patchlevel = 0,
    215	DRM_GEM_CMA_DRIVER_OPS,
    216};
    217
    218static int mcde_drm_bind(struct device *dev)
    219{
    220	struct drm_device *drm = dev_get_drvdata(dev);
    221	int ret;
    222
    223	ret = drmm_mode_config_init(drm);
    224	if (ret)
    225		return ret;
    226
    227	ret = component_bind_all(drm->dev, drm);
    228	if (ret) {
    229		dev_err(dev, "can't bind component devices\n");
    230		return ret;
    231	}
    232
    233	ret = mcde_modeset_init(drm);
    234	if (ret)
    235		goto unbind;
    236
    237	ret = drm_dev_register(drm, 0);
    238	if (ret < 0)
    239		goto unbind;
    240
    241	drm_fbdev_generic_setup(drm, 32);
    242
    243	return 0;
    244
    245unbind:
    246	component_unbind_all(drm->dev, drm);
    247	return ret;
    248}
    249
    250static void mcde_drm_unbind(struct device *dev)
    251{
    252	struct drm_device *drm = dev_get_drvdata(dev);
    253
    254	drm_dev_unregister(drm);
    255	drm_atomic_helper_shutdown(drm);
    256	component_unbind_all(drm->dev, drm);
    257}
    258
    259static const struct component_master_ops mcde_drm_comp_ops = {
    260	.bind = mcde_drm_bind,
    261	.unbind = mcde_drm_unbind,
    262};
    263
    264static struct platform_driver *const mcde_component_drivers[] = {
    265	&mcde_dsi_driver,
    266};
    267
    268static int mcde_probe(struct platform_device *pdev)
    269{
    270	struct device *dev = &pdev->dev;
    271	struct drm_device *drm;
    272	struct mcde *mcde;
    273	struct component_match *match = NULL;
    274	u32 pid;
    275	int irq;
    276	int ret;
    277	int i;
    278
    279	mcde = devm_drm_dev_alloc(dev, &mcde_drm_driver, struct mcde, drm);
    280	if (IS_ERR(mcde))
    281		return PTR_ERR(mcde);
    282	drm = &mcde->drm;
    283	mcde->dev = dev;
    284	platform_set_drvdata(pdev, drm);
    285
    286	/* First obtain and turn on the main power */
    287	mcde->epod = devm_regulator_get(dev, "epod");
    288	if (IS_ERR(mcde->epod)) {
    289		ret = PTR_ERR(mcde->epod);
    290		dev_err(dev, "can't get EPOD regulator\n");
    291		return ret;
    292	}
    293	ret = regulator_enable(mcde->epod);
    294	if (ret) {
    295		dev_err(dev, "can't enable EPOD regulator\n");
    296		return ret;
    297	}
    298	mcde->vana = devm_regulator_get(dev, "vana");
    299	if (IS_ERR(mcde->vana)) {
    300		ret = PTR_ERR(mcde->vana);
    301		dev_err(dev, "can't get VANA regulator\n");
    302		goto regulator_epod_off;
    303	}
    304	ret = regulator_enable(mcde->vana);
    305	if (ret) {
    306		dev_err(dev, "can't enable VANA regulator\n");
    307		goto regulator_epod_off;
    308	}
    309	/*
    310	 * The vendor code uses ESRAM (onchip RAM) and need to activate
    311	 * the v-esram34 regulator, but we don't use that yet
    312	 */
    313
    314	/* Clock the silicon so we can access the registers */
    315	mcde->mcde_clk = devm_clk_get(dev, "mcde");
    316	if (IS_ERR(mcde->mcde_clk)) {
    317		dev_err(dev, "unable to get MCDE main clock\n");
    318		ret = PTR_ERR(mcde->mcde_clk);
    319		goto regulator_off;
    320	}
    321	ret = clk_prepare_enable(mcde->mcde_clk);
    322	if (ret) {
    323		dev_err(dev, "failed to enable MCDE main clock\n");
    324		goto regulator_off;
    325	}
    326	dev_info(dev, "MCDE clk rate %lu Hz\n", clk_get_rate(mcde->mcde_clk));
    327
    328	mcde->lcd_clk = devm_clk_get(dev, "lcd");
    329	if (IS_ERR(mcde->lcd_clk)) {
    330		dev_err(dev, "unable to get LCD clock\n");
    331		ret = PTR_ERR(mcde->lcd_clk);
    332		goto clk_disable;
    333	}
    334	mcde->hdmi_clk = devm_clk_get(dev, "hdmi");
    335	if (IS_ERR(mcde->hdmi_clk)) {
    336		dev_err(dev, "unable to get HDMI clock\n");
    337		ret = PTR_ERR(mcde->hdmi_clk);
    338		goto clk_disable;
    339	}
    340
    341	mcde->regs = devm_platform_ioremap_resource(pdev, 0);
    342	if (IS_ERR(mcde->regs)) {
    343		dev_err(dev, "no MCDE regs\n");
    344		ret = -EINVAL;
    345		goto clk_disable;
    346	}
    347
    348	irq = platform_get_irq(pdev, 0);
    349	if (irq < 0) {
    350		ret = irq;
    351		goto clk_disable;
    352	}
    353
    354	ret = devm_request_irq(dev, irq, mcde_irq, 0, "mcde", mcde);
    355	if (ret) {
    356		dev_err(dev, "failed to request irq %d\n", ret);
    357		goto clk_disable;
    358	}
    359
    360	/*
    361	 * Check hardware revision, we only support U8500v2 version
    362	 * as this was the only version used for mass market deployment,
    363	 * but surely you can add more versions if you have them and
    364	 * need them.
    365	 */
    366	pid = readl(mcde->regs + MCDE_PID);
    367	dev_info(dev, "found MCDE HW revision %d.%d (dev %d, metal fix %d)\n",
    368		 (pid & MCDE_PID_MAJOR_VERSION_MASK)
    369		 >> MCDE_PID_MAJOR_VERSION_SHIFT,
    370		 (pid & MCDE_PID_MINOR_VERSION_MASK)
    371		 >> MCDE_PID_MINOR_VERSION_SHIFT,
    372		 (pid & MCDE_PID_DEVELOPMENT_VERSION_MASK)
    373		 >> MCDE_PID_DEVELOPMENT_VERSION_SHIFT,
    374		 (pid & MCDE_PID_METALFIX_VERSION_MASK)
    375		 >> MCDE_PID_METALFIX_VERSION_SHIFT);
    376	if (pid != 0x03000800) {
    377		dev_err(dev, "unsupported hardware revision\n");
    378		ret = -ENODEV;
    379		goto clk_disable;
    380	}
    381
    382	/* Disable and clear any pending interrupts */
    383	mcde_display_disable_irqs(mcde);
    384	writel(0, mcde->regs + MCDE_IMSCERR);
    385	writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR);
    386
    387	/* Spawn child devices for the DSI ports */
    388	devm_of_platform_populate(dev);
    389
    390	/* Create something that will match the subdrivers when we bind */
    391	for (i = 0; i < ARRAY_SIZE(mcde_component_drivers); i++) {
    392		struct device_driver *drv = &mcde_component_drivers[i]->driver;
    393		struct device *p = NULL, *d;
    394
    395		while ((d = platform_find_device_by_driver(p, drv))) {
    396			put_device(p);
    397			component_match_add(dev, &match, component_compare_dev, d);
    398			p = d;
    399		}
    400		put_device(p);
    401	}
    402	if (!match) {
    403		dev_err(dev, "no matching components\n");
    404		ret = -ENODEV;
    405		goto clk_disable;
    406	}
    407	if (IS_ERR(match)) {
    408		dev_err(dev, "could not create component match\n");
    409		ret = PTR_ERR(match);
    410		goto clk_disable;
    411	}
    412
    413	/*
    414	 * Perform an invasive reset of the MCDE and all blocks by
    415	 * cutting the power to the subsystem, then bring it back up
    416	 * later when we enable the display as a result of
    417	 * component_master_add_with_match().
    418	 */
    419	ret = regulator_disable(mcde->epod);
    420	if (ret) {
    421		dev_err(dev, "can't disable EPOD regulator\n");
    422		return ret;
    423	}
    424	/* Wait 50 ms so we are sure we cut the power */
    425	usleep_range(50000, 70000);
    426
    427	ret = component_master_add_with_match(&pdev->dev, &mcde_drm_comp_ops,
    428					      match);
    429	if (ret) {
    430		dev_err(dev, "failed to add component master\n");
    431		/*
    432		 * The EPOD regulator is already disabled at this point so some
    433		 * special errorpath code is needed
    434		 */
    435		clk_disable_unprepare(mcde->mcde_clk);
    436		regulator_disable(mcde->vana);
    437		return ret;
    438	}
    439
    440	return 0;
    441
    442clk_disable:
    443	clk_disable_unprepare(mcde->mcde_clk);
    444regulator_off:
    445	regulator_disable(mcde->vana);
    446regulator_epod_off:
    447	regulator_disable(mcde->epod);
    448	return ret;
    449
    450}
    451
    452static int mcde_remove(struct platform_device *pdev)
    453{
    454	struct drm_device *drm = platform_get_drvdata(pdev);
    455	struct mcde *mcde = to_mcde(drm);
    456
    457	component_master_del(&pdev->dev, &mcde_drm_comp_ops);
    458	clk_disable_unprepare(mcde->mcde_clk);
    459	regulator_disable(mcde->vana);
    460	regulator_disable(mcde->epod);
    461
    462	return 0;
    463}
    464
    465static const struct of_device_id mcde_of_match[] = {
    466	{
    467		.compatible = "ste,mcde",
    468	},
    469	{},
    470};
    471
    472static struct platform_driver mcde_driver = {
    473	.driver = {
    474		.name           = "mcde",
    475		.of_match_table = of_match_ptr(mcde_of_match),
    476	},
    477	.probe = mcde_probe,
    478	.remove = mcde_remove,
    479};
    480
    481static struct platform_driver *const component_drivers[] = {
    482	&mcde_dsi_driver,
    483};
    484
    485static int __init mcde_drm_register(void)
    486{
    487	int ret;
    488
    489	if (drm_firmware_drivers_only())
    490		return -ENODEV;
    491
    492	ret = platform_register_drivers(component_drivers,
    493					ARRAY_SIZE(component_drivers));
    494	if (ret)
    495		return ret;
    496
    497	return platform_driver_register(&mcde_driver);
    498}
    499
    500static void __exit mcde_drm_unregister(void)
    501{
    502	platform_unregister_drivers(component_drivers,
    503				    ARRAY_SIZE(component_drivers));
    504	platform_driver_unregister(&mcde_driver);
    505}
    506
    507module_init(mcde_drm_register);
    508module_exit(mcde_drm_unregister);
    509
    510MODULE_ALIAS("platform:mcde-drm");
    511MODULE_DESCRIPTION(DRIVER_DESC);
    512MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
    513MODULE_LICENSE("GPL");