brintos

brintos / linux-shallow public Read only

0
0
Text · 7.0 KiB · ff2883c Raw
296 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * shmob_drm_drv.c  --  SH Mobile DRM driver4 *5 * Copyright (C) 2012 Renesas Electronics Corporation6 *7 * Laurent Pinchart (laurent.pinchart@ideasonboard.com)8 */9 10#include <linux/clk.h>11#include <linux/io.h>12#include <linux/mm.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/pm.h>17#include <linux/pm_runtime.h>18#include <linux/slab.h>19 20#include <drm/drm_atomic_helper.h>21#include <drm/drm_drv.h>22#include <drm/drm_fbdev_dma.h>23#include <drm/drm_gem_dma_helper.h>24#include <drm/drm_modeset_helper.h>25#include <drm/drm_module.h>26#include <drm/drm_probe_helper.h>27#include <drm/drm_vblank.h>28 29#include "shmob_drm_drv.h"30#include "shmob_drm_kms.h"31#include "shmob_drm_plane.h"32#include "shmob_drm_regs.h"33 34/* -----------------------------------------------------------------------------35 * Hardware initialization36 */37 38static int shmob_drm_setup_clocks(struct shmob_drm_device *sdev,39				  enum shmob_drm_clk_source clksrc)40{41	struct clk *clk;42	char *clkname;43 44	switch (clksrc) {45	case SHMOB_DRM_CLK_BUS:46		clkname = "fck";47		sdev->lddckr = LDDCKR_ICKSEL_BUS;48		break;49	case SHMOB_DRM_CLK_PERIPHERAL:50		clkname = "media";51		sdev->lddckr = LDDCKR_ICKSEL_MIPI;52		break;53	case SHMOB_DRM_CLK_EXTERNAL:54		clkname = "lclk";55		sdev->lddckr = LDDCKR_ICKSEL_HDMI;56		break;57	default:58		return -EINVAL;59	}60 61	clk = devm_clk_get(sdev->dev, clkname);62	if (IS_ERR(clk)) {63		dev_err(sdev->dev, "cannot get dot clock %s\n", clkname);64		return PTR_ERR(clk);65	}66 67	sdev->clock = clk;68	return 0;69}70 71/* -----------------------------------------------------------------------------72 * DRM operations73 */74 75static irqreturn_t shmob_drm_irq(int irq, void *arg)76{77	struct drm_device *dev = arg;78	struct shmob_drm_device *sdev = to_shmob_device(dev);79	unsigned long flags;80	u32 status;81 82	/* Acknowledge interrupts. Putting interrupt enable and interrupt flag83	 * bits in the same register is really brain-dead design and requires84	 * taking a spinlock.85	 */86	spin_lock_irqsave(&sdev->irq_lock, flags);87	status = lcdc_read(sdev, LDINTR);88	lcdc_write(sdev, LDINTR, status ^ LDINTR_STATUS_MASK);89	spin_unlock_irqrestore(&sdev->irq_lock, flags);90 91	if (status & LDINTR_VES) {92		drm_crtc_handle_vblank(&sdev->crtc.base);93		shmob_drm_crtc_finish_page_flip(&sdev->crtc);94	}95 96	return IRQ_HANDLED;97}98 99DEFINE_DRM_GEM_DMA_FOPS(shmob_drm_fops);100 101static const struct drm_driver shmob_drm_driver = {102	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,103	DRM_GEM_DMA_DRIVER_OPS,104	.fops			= &shmob_drm_fops,105	.name			= "shmob-drm",106	.desc			= "Renesas SH Mobile DRM",107	.date			= "20120424",108	.major			= 1,109	.minor			= 0,110};111 112/* -----------------------------------------------------------------------------113 * Power management114 */115 116static int shmob_drm_pm_suspend(struct device *dev)117{118	struct shmob_drm_device *sdev = dev_get_drvdata(dev);119 120	return drm_mode_config_helper_suspend(&sdev->ddev);121}122 123static int shmob_drm_pm_resume(struct device *dev)124{125	struct shmob_drm_device *sdev = dev_get_drvdata(dev);126 127	return drm_mode_config_helper_resume(&sdev->ddev);128}129 130static int shmob_drm_pm_runtime_suspend(struct device *dev)131{132	struct shmob_drm_device *sdev = dev_get_drvdata(dev);133 134	if (sdev->clock)135		clk_disable_unprepare(sdev->clock);136 137	return 0;138}139 140static int shmob_drm_pm_runtime_resume(struct device *dev)141{142	struct shmob_drm_device *sdev = dev_get_drvdata(dev);143	int ret;144 145	if (sdev->clock) {146		ret = clk_prepare_enable(sdev->clock);147		if (ret < 0)148			return ret;149	}150 151	return 0;152}153 154static const struct dev_pm_ops shmob_drm_pm_ops = {155	SYSTEM_SLEEP_PM_OPS(shmob_drm_pm_suspend, shmob_drm_pm_resume)156	RUNTIME_PM_OPS(shmob_drm_pm_runtime_suspend,157		       shmob_drm_pm_runtime_resume, NULL)158};159 160/* -----------------------------------------------------------------------------161 * Platform driver162 */163 164static void shmob_drm_remove(struct platform_device *pdev)165{166	struct shmob_drm_device *sdev = platform_get_drvdata(pdev);167	struct drm_device *ddev = &sdev->ddev;168 169	drm_dev_unregister(ddev);170	drm_atomic_helper_shutdown(ddev);171	drm_kms_helper_poll_fini(ddev);172}173 174static void shmob_drm_shutdown(struct platform_device *pdev)175{176	struct shmob_drm_device *sdev = platform_get_drvdata(pdev);177 178	drm_atomic_helper_shutdown(&sdev->ddev);179}180 181static int shmob_drm_probe(struct platform_device *pdev)182{183	struct shmob_drm_platform_data *pdata = pdev->dev.platform_data;184	const struct shmob_drm_config *config;185	struct shmob_drm_device *sdev;186	struct drm_device *ddev;187	int ret;188 189	config = of_device_get_match_data(&pdev->dev);190	if (!config && !pdata) {191		dev_err(&pdev->dev, "no platform data\n");192		return -EINVAL;193	}194 195	/*196	 * Allocate and initialize the DRM device, driver private data, I/O197	 * resources and clocks.198	 */199	sdev = devm_drm_dev_alloc(&pdev->dev, &shmob_drm_driver,200				  struct shmob_drm_device, ddev);201	if (IS_ERR(sdev))202		return PTR_ERR(sdev);203 204	ddev = &sdev->ddev;205	sdev->dev = &pdev->dev;206	if (config) {207		sdev->config = *config;208	} else {209		sdev->pdata = pdata;210		sdev->config.clk_source = pdata->clk_source;211		sdev->config.clk_div = pdata->iface.clk_div;212	}213	spin_lock_init(&sdev->irq_lock);214 215	platform_set_drvdata(pdev, sdev);216 217	sdev->mmio = devm_platform_ioremap_resource(pdev, 0);218	if (IS_ERR(sdev->mmio))219		return PTR_ERR(sdev->mmio);220 221	ret = shmob_drm_setup_clocks(sdev, sdev->config.clk_source);222	if (ret < 0)223		return ret;224 225	ret = devm_pm_runtime_enable(&pdev->dev);226	if (ret)227		return ret;228 229	ret = drm_vblank_init(ddev, 1);230	if (ret < 0) {231		dev_err(&pdev->dev, "failed to initialize vblank\n");232		return ret;233	}234 235	ret = shmob_drm_modeset_init(sdev);236	if (ret < 0)237		return dev_err_probe(&pdev->dev, ret,238				     "failed to initialize mode setting\n");239 240	ret = platform_get_irq(pdev, 0);241	if (ret < 0)242		goto err_modeset_cleanup;243	sdev->irq = ret;244 245	ret = devm_request_irq(&pdev->dev, sdev->irq, shmob_drm_irq, 0,246			       ddev->driver->name, ddev);247	if (ret < 0) {248		dev_err(&pdev->dev, "failed to install IRQ handler\n");249		goto err_modeset_cleanup;250	}251 252	/*253	 * Register the DRM device with the core and the connectors with254	 * sysfs.255	 */256	ret = drm_dev_register(ddev, 0);257	if (ret < 0)258		goto err_modeset_cleanup;259 260	drm_fbdev_dma_setup(ddev, 16);261 262	return 0;263 264err_modeset_cleanup:265	drm_kms_helper_poll_fini(ddev);266	return ret;267}268 269static const struct shmob_drm_config shmob_arm_config = {270	.clk_source = SHMOB_DRM_CLK_BUS,271	.clk_div = 5,272};273 274static const struct of_device_id shmob_drm_of_table[] __maybe_unused = {275	{ .compatible = "renesas,r8a7740-lcdc",	.data = &shmob_arm_config, },276	{ .compatible = "renesas,sh73a0-lcdc",	.data = &shmob_arm_config, },277	{ /* sentinel */ }278};279 280static struct platform_driver shmob_drm_platform_driver = {281	.probe		= shmob_drm_probe,282	.remove_new	= shmob_drm_remove,283	.shutdown	= shmob_drm_shutdown,284	.driver		= {285		.name	= "shmob-drm",286		.of_match_table = of_match_ptr(shmob_drm_of_table),287		.pm	= &shmob_drm_pm_ops,288	},289};290 291drm_module_platform_driver(shmob_drm_platform_driver);292 293MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");294MODULE_DESCRIPTION("Renesas SH Mobile DRM Driver");295MODULE_LICENSE("GPL");296