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

mxsfb_drv.c (10334B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Copyright (C) 2016 Marek Vasut <marex@denx.de>
      4 *
      5 * This code is based on drivers/video/fbdev/mxsfb.c :
      6 * Copyright (C) 2010 Juergen Beisert, Pengutronix
      7 * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
      8 * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
      9 */
     10
     11#include <linux/clk.h>
     12#include <linux/dma-mapping.h>
     13#include <linux/io.h>
     14#include <linux/module.h>
     15#include <linux/of_device.h>
     16#include <linux/platform_device.h>
     17#include <linux/pm_runtime.h>
     18
     19#include <drm/drm_atomic_helper.h>
     20#include <drm/drm_bridge.h>
     21#include <drm/drm_connector.h>
     22#include <drm/drm_drv.h>
     23#include <drm/drm_fb_helper.h>
     24#include <drm/drm_fourcc.h>
     25#include <drm/drm_gem_cma_helper.h>
     26#include <drm/drm_gem_framebuffer_helper.h>
     27#include <drm/drm_mode_config.h>
     28#include <drm/drm_module.h>
     29#include <drm/drm_of.h>
     30#include <drm/drm_probe_helper.h>
     31#include <drm/drm_vblank.h>
     32
     33#include "mxsfb_drv.h"
     34#include "mxsfb_regs.h"
     35
     36enum mxsfb_devtype {
     37	MXSFB_V3,
     38	MXSFB_V4,
     39	/*
     40	 * Starting at i.MX6 the hardware version register is gone, use the
     41	 * i.MX family number as the version.
     42	 */
     43	MXSFB_V6,
     44};
     45
     46static const struct mxsfb_devdata mxsfb_devdata[] = {
     47	[MXSFB_V3] = {
     48		.transfer_count	= LCDC_V3_TRANSFER_COUNT,
     49		.cur_buf	= LCDC_V3_CUR_BUF,
     50		.next_buf	= LCDC_V3_NEXT_BUF,
     51		.hs_wdth_mask	= 0xff,
     52		.hs_wdth_shift	= 24,
     53		.has_overlay	= false,
     54		.has_ctrl2	= false,
     55		.has_crc32	= false,
     56	},
     57	[MXSFB_V4] = {
     58		.transfer_count	= LCDC_V4_TRANSFER_COUNT,
     59		.cur_buf	= LCDC_V4_CUR_BUF,
     60		.next_buf	= LCDC_V4_NEXT_BUF,
     61		.hs_wdth_mask	= 0x3fff,
     62		.hs_wdth_shift	= 18,
     63		.has_overlay	= false,
     64		.has_ctrl2	= true,
     65		.has_crc32	= true,
     66	},
     67	[MXSFB_V6] = {
     68		.transfer_count	= LCDC_V4_TRANSFER_COUNT,
     69		.cur_buf	= LCDC_V4_CUR_BUF,
     70		.next_buf	= LCDC_V4_NEXT_BUF,
     71		.hs_wdth_mask	= 0x3fff,
     72		.hs_wdth_shift	= 18,
     73		.has_overlay	= true,
     74		.has_ctrl2	= true,
     75		.has_crc32	= true,
     76	},
     77};
     78
     79void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb)
     80{
     81	if (mxsfb->clk_axi)
     82		clk_prepare_enable(mxsfb->clk_axi);
     83}
     84
     85void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb)
     86{
     87	if (mxsfb->clk_axi)
     88		clk_disable_unprepare(mxsfb->clk_axi);
     89}
     90
     91static struct drm_framebuffer *
     92mxsfb_fb_create(struct drm_device *dev, struct drm_file *file_priv,
     93		const struct drm_mode_fb_cmd2 *mode_cmd)
     94{
     95	const struct drm_format_info *info;
     96
     97	info = drm_get_format_info(dev, mode_cmd);
     98	if (!info)
     99		return ERR_PTR(-EINVAL);
    100
    101	if (mode_cmd->width * info->cpp[0] != mode_cmd->pitches[0]) {
    102		dev_dbg(dev->dev, "Invalid pitch: fb width must match pitch\n");
    103		return ERR_PTR(-EINVAL);
    104	}
    105
    106	return drm_gem_fb_create(dev, file_priv, mode_cmd);
    107}
    108
    109static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
    110	.fb_create		= mxsfb_fb_create,
    111	.atomic_check		= drm_atomic_helper_check,
    112	.atomic_commit		= drm_atomic_helper_commit,
    113};
    114
    115static const struct drm_mode_config_helper_funcs mxsfb_mode_config_helpers = {
    116	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
    117};
    118
    119static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb)
    120{
    121	struct drm_device *drm = mxsfb->drm;
    122	struct drm_connector_list_iter iter;
    123	struct drm_panel *panel;
    124	struct drm_bridge *bridge;
    125	int ret;
    126
    127	ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
    128					  &bridge);
    129	if (ret)
    130		return ret;
    131
    132	if (panel) {
    133		bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
    134							 DRM_MODE_CONNECTOR_DPI);
    135		if (IS_ERR(bridge))
    136			return PTR_ERR(bridge);
    137	}
    138
    139	if (!bridge)
    140		return -ENODEV;
    141
    142	ret = drm_bridge_attach(&mxsfb->encoder, bridge, NULL, 0);
    143	if (ret)
    144		return dev_err_probe(drm->dev, ret, "Failed to attach bridge\n");
    145
    146	mxsfb->bridge = bridge;
    147
    148	/*
    149	 * Get hold of the connector. This is a bit of a hack, until the bridge
    150	 * API gives us bus flags and formats.
    151	 */
    152	drm_connector_list_iter_begin(drm, &iter);
    153	mxsfb->connector = drm_connector_list_iter_next(&iter);
    154	drm_connector_list_iter_end(&iter);
    155
    156	return 0;
    157}
    158
    159static irqreturn_t mxsfb_irq_handler(int irq, void *data)
    160{
    161	struct drm_device *drm = data;
    162	struct mxsfb_drm_private *mxsfb = drm->dev_private;
    163	u32 reg, crc;
    164	u64 vbc;
    165
    166	reg = readl(mxsfb->base + LCDC_CTRL1);
    167
    168	if (reg & CTRL1_CUR_FRAME_DONE_IRQ) {
    169		drm_crtc_handle_vblank(&mxsfb->crtc);
    170		if (mxsfb->crc_active) {
    171			crc = readl(mxsfb->base + LCDC_V4_CRC_STAT);
    172			vbc = drm_crtc_accurate_vblank_count(&mxsfb->crtc);
    173			drm_crtc_add_crc_entry(&mxsfb->crtc, true, vbc, &crc);
    174		}
    175	}
    176
    177	writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
    178
    179	return IRQ_HANDLED;
    180}
    181
    182static void mxsfb_irq_disable(struct drm_device *drm)
    183{
    184	struct mxsfb_drm_private *mxsfb = drm->dev_private;
    185
    186	mxsfb_enable_axi_clk(mxsfb);
    187
    188	/* Disable and clear VBLANK IRQ */
    189	writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
    190	writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
    191
    192	mxsfb_disable_axi_clk(mxsfb);
    193}
    194
    195static int mxsfb_irq_install(struct drm_device *dev, int irq)
    196{
    197	if (irq == IRQ_NOTCONNECTED)
    198		return -ENOTCONN;
    199
    200	mxsfb_irq_disable(dev);
    201
    202	return request_irq(irq, mxsfb_irq_handler, 0,  dev->driver->name, dev);
    203}
    204
    205static void mxsfb_irq_uninstall(struct drm_device *dev)
    206{
    207	struct mxsfb_drm_private *mxsfb = dev->dev_private;
    208
    209	mxsfb_irq_disable(dev);
    210	free_irq(mxsfb->irq, dev);
    211}
    212
    213static int mxsfb_load(struct drm_device *drm,
    214		      const struct mxsfb_devdata *devdata)
    215{
    216	struct platform_device *pdev = to_platform_device(drm->dev);
    217	struct mxsfb_drm_private *mxsfb;
    218	struct resource *res;
    219	int ret;
    220
    221	mxsfb = devm_kzalloc(&pdev->dev, sizeof(*mxsfb), GFP_KERNEL);
    222	if (!mxsfb)
    223		return -ENOMEM;
    224
    225	mxsfb->drm = drm;
    226	drm->dev_private = mxsfb;
    227	mxsfb->devdata = devdata;
    228
    229	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    230	mxsfb->base = devm_ioremap_resource(drm->dev, res);
    231	if (IS_ERR(mxsfb->base))
    232		return PTR_ERR(mxsfb->base);
    233
    234	mxsfb->clk = devm_clk_get(drm->dev, NULL);
    235	if (IS_ERR(mxsfb->clk))
    236		return PTR_ERR(mxsfb->clk);
    237
    238	mxsfb->clk_axi = devm_clk_get(drm->dev, "axi");
    239	if (IS_ERR(mxsfb->clk_axi))
    240		mxsfb->clk_axi = NULL;
    241
    242	mxsfb->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi");
    243	if (IS_ERR(mxsfb->clk_disp_axi))
    244		mxsfb->clk_disp_axi = NULL;
    245
    246	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
    247	if (ret)
    248		return ret;
    249
    250	pm_runtime_enable(drm->dev);
    251
    252	/* Modeset init */
    253	drm_mode_config_init(drm);
    254
    255	ret = mxsfb_kms_init(mxsfb);
    256	if (ret < 0) {
    257		dev_err(drm->dev, "Failed to initialize KMS pipeline\n");
    258		goto err_vblank;
    259	}
    260
    261	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
    262	if (ret < 0) {
    263		dev_err(drm->dev, "Failed to initialise vblank\n");
    264		goto err_vblank;
    265	}
    266
    267	/* Start with vertical blanking interrupt reporting disabled. */
    268	drm_crtc_vblank_off(&mxsfb->crtc);
    269
    270	ret = mxsfb_attach_bridge(mxsfb);
    271	if (ret) {
    272		dev_err_probe(drm->dev, ret, "Cannot connect bridge\n");
    273		goto err_vblank;
    274	}
    275
    276	drm->mode_config.min_width	= MXSFB_MIN_XRES;
    277	drm->mode_config.min_height	= MXSFB_MIN_YRES;
    278	drm->mode_config.max_width	= MXSFB_MAX_XRES;
    279	drm->mode_config.max_height	= MXSFB_MAX_YRES;
    280	drm->mode_config.funcs		= &mxsfb_mode_config_funcs;
    281	drm->mode_config.helper_private	= &mxsfb_mode_config_helpers;
    282
    283	drm_mode_config_reset(drm);
    284
    285	ret = platform_get_irq(pdev, 0);
    286	if (ret < 0)
    287		goto err_vblank;
    288	mxsfb->irq = ret;
    289
    290	pm_runtime_get_sync(drm->dev);
    291	ret = mxsfb_irq_install(drm, mxsfb->irq);
    292	pm_runtime_put_sync(drm->dev);
    293
    294	if (ret < 0) {
    295		dev_err(drm->dev, "Failed to install IRQ handler\n");
    296		goto err_vblank;
    297	}
    298
    299	drm_kms_helper_poll_init(drm);
    300
    301	platform_set_drvdata(pdev, drm);
    302
    303	drm_helper_hpd_irq_event(drm);
    304
    305	return 0;
    306
    307err_vblank:
    308	pm_runtime_disable(drm->dev);
    309
    310	return ret;
    311}
    312
    313static void mxsfb_unload(struct drm_device *drm)
    314{
    315	drm_kms_helper_poll_fini(drm);
    316	drm_mode_config_cleanup(drm);
    317
    318	pm_runtime_get_sync(drm->dev);
    319	mxsfb_irq_uninstall(drm);
    320	pm_runtime_put_sync(drm->dev);
    321
    322	drm->dev_private = NULL;
    323
    324	pm_runtime_disable(drm->dev);
    325}
    326
    327DEFINE_DRM_GEM_CMA_FOPS(fops);
    328
    329static const struct drm_driver mxsfb_driver = {
    330	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
    331	DRM_GEM_CMA_DRIVER_OPS,
    332	.fops	= &fops,
    333	.name	= "mxsfb-drm",
    334	.desc	= "MXSFB Controller DRM",
    335	.date	= "20160824",
    336	.major	= 1,
    337	.minor	= 0,
    338};
    339
    340static const struct of_device_id mxsfb_dt_ids[] = {
    341	{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devdata[MXSFB_V3], },
    342	{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devdata[MXSFB_V4], },
    343	{ .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devdata[MXSFB_V6], },
    344	{ /* sentinel */ }
    345};
    346MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);
    347
    348static int mxsfb_probe(struct platform_device *pdev)
    349{
    350	struct drm_device *drm;
    351	const struct of_device_id *of_id =
    352			of_match_device(mxsfb_dt_ids, &pdev->dev);
    353	int ret;
    354
    355	if (!pdev->dev.of_node)
    356		return -ENODEV;
    357
    358	drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev);
    359	if (IS_ERR(drm))
    360		return PTR_ERR(drm);
    361
    362	ret = mxsfb_load(drm, of_id->data);
    363	if (ret)
    364		goto err_free;
    365
    366	ret = drm_dev_register(drm, 0);
    367	if (ret)
    368		goto err_unload;
    369
    370	drm_fbdev_generic_setup(drm, 32);
    371
    372	return 0;
    373
    374err_unload:
    375	mxsfb_unload(drm);
    376err_free:
    377	drm_dev_put(drm);
    378
    379	return ret;
    380}
    381
    382static int mxsfb_remove(struct platform_device *pdev)
    383{
    384	struct drm_device *drm = platform_get_drvdata(pdev);
    385
    386	drm_dev_unregister(drm);
    387	drm_atomic_helper_shutdown(drm);
    388	mxsfb_unload(drm);
    389	drm_dev_put(drm);
    390
    391	return 0;
    392}
    393
    394static void mxsfb_shutdown(struct platform_device *pdev)
    395{
    396	struct drm_device *drm = platform_get_drvdata(pdev);
    397
    398	drm_atomic_helper_shutdown(drm);
    399}
    400
    401#ifdef CONFIG_PM_SLEEP
    402static int mxsfb_suspend(struct device *dev)
    403{
    404	struct drm_device *drm = dev_get_drvdata(dev);
    405
    406	return drm_mode_config_helper_suspend(drm);
    407}
    408
    409static int mxsfb_resume(struct device *dev)
    410{
    411	struct drm_device *drm = dev_get_drvdata(dev);
    412
    413	return drm_mode_config_helper_resume(drm);
    414}
    415#endif
    416
    417static const struct dev_pm_ops mxsfb_pm_ops = {
    418	SET_SYSTEM_SLEEP_PM_OPS(mxsfb_suspend, mxsfb_resume)
    419};
    420
    421static struct platform_driver mxsfb_platform_driver = {
    422	.probe		= mxsfb_probe,
    423	.remove		= mxsfb_remove,
    424	.shutdown	= mxsfb_shutdown,
    425	.driver	= {
    426		.name		= "mxsfb",
    427		.of_match_table	= mxsfb_dt_ids,
    428		.pm		= &mxsfb_pm_ops,
    429	},
    430};
    431
    432drm_module_platform_driver(mxsfb_platform_driver);
    433
    434MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
    435MODULE_DESCRIPTION("Freescale MXS DRM/KMS driver");
    436MODULE_LICENSE("GPL");