brintos

brintos / linux-shallow public Read only

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