File Index
Files and directories
Sign in
FILE

fsl_dcu_drm_drv.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright 2015 Freescale Semiconductor, Inc.
  4 *
  5 * Freescale DCU drm device driver
  6 */
  7
  8#include <linux/clk.h>
  9#include <linux/clk-provider.h>
 10#include <linux/console.h>
 11#include <linux/io.h>
 12#include <linux/mfd/syscon.h>
 13#include <linux/mm.h>
 14#include <linux/module.h>
 15#include <linux/of_platform.h>
 16#include <linux/platform_device.h>
 17#include <linux/pm.h>
 18#include <linux/pm_runtime.h>
 19#include <linux/regmap.h>
 20
 21#include <drm/clients/drm_client_setup.h>
 22#include <drm/drm_atomic_helper.h>
 23#include <drm/drm_drv.h>
 24#include <drm/drm_fbdev_dma.h>
 25#include <drm/drm_gem_dma_helper.h>
 26#include <drm/drm_modeset_helper.h>
 27#include <drm/drm_module.h>
 28#include <drm/drm_probe_helper.h>
 29#include <drm/drm_vblank.h>
 30
 31#include "fsl_dcu_drm_crtc.h"
 32#include "fsl_dcu_drm_drv.h"
 33#include "fsl_tcon.h"
 34
 35static int legacyfb_depth = 24;
 36module_param(legacyfb_depth, int, 0444);
 37
 38static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
 39{
 40	if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE)
 41		return true;
 42
 43	return false;
 44}
 45
 46static const struct regmap_config fsl_dcu_regmap_config = {
 47	.reg_bits = 32,
 48	.reg_stride = 4,
 49	.val_bits = 32,
 50
 51	.volatile_reg = fsl_dcu_drm_is_volatile_reg,
 52};
 53
 54static void fsl_dcu_irq_reset(struct drm_device *dev)
 55{
 56	struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 57
 58	regmap_write(fsl_dev->regmap, DCU_INT_STATUS, ~0);
 59	regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0);
 60}
 61
 62static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg)
 63{
 64	struct drm_device *dev = arg;
 65	struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 66	unsigned int int_status;
 67	int ret;
 68
 69	ret = regmap_read(fsl_dev->regmap, DCU_INT_STATUS, &int_status);
 70	if (ret) {
 71		dev_err(dev->dev, "read DCU_INT_STATUS failed\n");
 72		return IRQ_NONE;
 73	}
 74
 75	if (int_status & DCU_INT_STATUS_VBLANK)
 76		drm_handle_vblank(dev, 0);
 77
 78	regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status);
 79
 80	return IRQ_HANDLED;
 81}
 82
 83static int fsl_dcu_irq_install(struct drm_device *dev, unsigned int irq)
 84{
 85	if (irq == IRQ_NOTCONNECTED)
 86		return -ENOTCONN;
 87
 88	fsl_dcu_irq_reset(dev);
 89
 90	return request_irq(irq, fsl_dcu_drm_irq, 0, dev->driver->name, dev);
 91}
 92
 93static void fsl_dcu_irq_uninstall(struct drm_device *dev)
 94{
 95	struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
 96
 97	fsl_dcu_irq_reset(dev);
 98	free_irq(fsl_dev->irq, dev);
 99}
100
101static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
102{
103	struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
104	struct regmap *scfg;
105	int ret;
106
107	ret = fsl_dcu_drm_modeset_init(fsl_dev);
108	if (ret < 0)
109		return dev_err_probe(dev->dev, ret, "failed to initialize mode setting\n");
110
111	scfg = syscon_regmap_lookup_by_compatible("fsl,ls1021a-scfg");
112	if (PTR_ERR(scfg) != -ENODEV) {
113		/*
114		 * For simplicity, enable the PIXCLK unconditionally,
115		 * resulting in increased power consumption. Disabling
116		 * the clock in PM or on unload could be implemented as
117		 * a future improvement.
118		 */
119		ret = regmap_update_bits(scfg, SCFG_PIXCLKCR, SCFG_PIXCLKCR_PXCEN,
120					 SCFG_PIXCLKCR_PXCEN);
121		if (ret < 0)
122			return dev_err_probe(dev->dev, ret, "failed to enable pixclk\n");
123	}
124
125	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
126	if (ret < 0) {
127		dev_err(dev->dev, "failed to initialize vblank\n");
128		goto done_vblank;
129	}
130
131	ret = fsl_dcu_irq_install(dev, fsl_dev->irq);
132	if (ret < 0) {
133		dev_err(dev->dev, "failed to install IRQ handler\n");
134		goto done_irq;
135	}
136
137	if (legacyfb_depth != 16 && legacyfb_depth != 24 &&
138	    legacyfb_depth != 32) {
139		dev_warn(dev->dev,
140			"Invalid legacyfb_depth.  Defaulting to 24bpp\n");
141		legacyfb_depth = 24;
142	}
143
144	return 0;
145done_irq:
146	drm_kms_helper_poll_fini(dev);
147
148	drm_mode_config_cleanup(dev);
149done_vblank:
150	dev->dev_private = NULL;
151
152	return ret;
153}
154
155static void fsl_dcu_unload(struct drm_device *dev)
156{
157	drm_atomic_helper_shutdown(dev);
158	drm_kms_helper_poll_fini(dev);
159
160	drm_mode_config_cleanup(dev);
161	fsl_dcu_irq_uninstall(dev);
162
163	dev->dev_private = NULL;
164}
165
166DEFINE_DRM_GEM_DMA_FOPS(fsl_dcu_drm_fops);
167
168static const struct drm_driver fsl_dcu_drm_driver = {
169	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
170	.load			= fsl_dcu_load,
171	.unload			= fsl_dcu_unload,
172	DRM_GEM_DMA_DRIVER_OPS,
173	DRM_FBDEV_DMA_DRIVER_OPS,
174	.fops			= &fsl_dcu_drm_fops,
175	.name			= "fsl-dcu-drm",
176	.desc			= "Freescale DCU DRM",
177	.major			= 1,
178	.minor			= 1,
179};
180
181#ifdef CONFIG_PM_SLEEP
182static int fsl_dcu_drm_pm_suspend(struct device *dev)
183{
184	struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
185	int ret;
186
187	if (!fsl_dev)
188		return 0;
189
190	disable_irq(fsl_dev->irq);
191
192	ret = drm_mode_config_helper_suspend(fsl_dev->drm);
193	if (ret) {
194		enable_irq(fsl_dev->irq);
195		return ret;
196	}
197
198	clk_disable_unprepare(fsl_dev->clk);
199
200	return 0;
201}
202
203static int fsl_dcu_drm_pm_resume(struct device *dev)
204{
205	struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
206	int ret;
207
208	if (!fsl_dev)
209		return 0;
210
211	ret = clk_prepare_enable(fsl_dev->clk);
212	if (ret < 0) {
213		dev_err(dev, "failed to enable dcu clk\n");
214		return ret;
215	}
216
217	if (fsl_dev->tcon)
218		fsl_tcon_bypass_enable(fsl_dev->tcon);
219	fsl_dcu_drm_init_planes(fsl_dev->drm);
220	enable_irq(fsl_dev->irq);
221
222	drm_mode_config_helper_resume(fsl_dev->drm);
223
224	return 0;
225}
226#endif
227
228static const struct dev_pm_ops fsl_dcu_drm_pm_ops = {
229	SET_SYSTEM_SLEEP_PM_OPS(fsl_dcu_drm_pm_suspend, fsl_dcu_drm_pm_resume)
230};
231
232static const struct fsl_dcu_soc_data fsl_dcu_ls1021a_data = {
233	.name = "ls1021a",
234	.total_layer = 16,
235	.max_layer = 4,
236	.layer_regs = LS1021A_LAYER_REG_NUM,
237};
238
239static const struct fsl_dcu_soc_data fsl_dcu_vf610_data = {
240	.name = "vf610",
241	.total_layer = 64,
242	.max_layer = 6,
243	.layer_regs = VF610_LAYER_REG_NUM,
244};
245
246static const struct of_device_id fsl_dcu_of_match[] = {
247	{
248		.compatible = "fsl,ls1021a-dcu",
249		.data = &fsl_dcu_ls1021a_data,
250	}, {
251		.compatible = "fsl,vf610-dcu",
252		.data = &fsl_dcu_vf610_data,
253	}, {
254	},
255};
256MODULE_DEVICE_TABLE(of, fsl_dcu_of_match);
257
258static int fsl_dcu_drm_probe(struct platform_device *pdev)
259{
260	struct fsl_dcu_drm_device *fsl_dev;
261	struct drm_device *drm;
262	struct device *dev = &pdev->dev;
263	void __iomem *base;
264	struct clk *pix_clk_in;
265	char pix_clk_name[32];
266	const char *pix_clk_in_name;
267	const struct of_device_id *id;
268	int ret;
269	u8 div_ratio_shift = 0;
270
271	fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL);
272	if (!fsl_dev)
273		return -ENOMEM;
274
275	id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
276	if (!id)
277		return -ENODEV;
278	fsl_dev->soc = id->data;
279
280	base = devm_platform_ioremap_resource(pdev, 0);
281	if (IS_ERR(base)) {
282		ret = PTR_ERR(base);
283		return ret;
284	}
285
286	fsl_dev->irq = platform_get_irq(pdev, 0);
287	if (fsl_dev->irq < 0)
288		return fsl_dev->irq;
289
290	fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
291			&fsl_dcu_regmap_config);
292	if (IS_ERR(fsl_dev->regmap)) {
293		dev_err(dev, "regmap init failed\n");
294		return PTR_ERR(fsl_dev->regmap);
295	}
296
297	fsl_dev->clk = devm_clk_get(dev, "dcu");
298	if (IS_ERR(fsl_dev->clk)) {
299		dev_err(dev, "failed to get dcu clock\n");
300		return PTR_ERR(fsl_dev->clk);
301	}
302	ret = clk_prepare_enable(fsl_dev->clk);
303	if (ret < 0) {
304		dev_err(dev, "failed to enable dcu clk\n");
305		return ret;
306	}
307
308	pix_clk_in = devm_clk_get(dev, "pix");
309	if (IS_ERR(pix_clk_in)) {
310		/* legancy binding, use dcu clock as pixel clock input */
311		pix_clk_in = fsl_dev->clk;
312	}
313
314	if (of_property_read_bool(dev->of_node, "big-endian"))
315		div_ratio_shift = 24;
316
317	pix_clk_in_name = __clk_get_name(pix_clk_in);
318	snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
319	fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
320			pix_clk_in_name, 0, base + DCU_DIV_RATIO,
321			div_ratio_shift, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
322	if (IS_ERR(fsl_dev->pix_clk)) {
323		dev_err(dev, "failed to register pix clk\n");
324		ret = PTR_ERR(fsl_dev->pix_clk);
325		goto disable_clk;
326	}
327
328	fsl_dev->tcon = fsl_tcon_init(dev);
329
330	drm = drm_dev_alloc(&fsl_dcu_drm_driver, dev);
331	if (IS_ERR(drm)) {
332		ret = PTR_ERR(drm);
333		goto unregister_pix_clk;
334	}
335
336	fsl_dev->dev = dev;
337	fsl_dev->drm = drm;
338	fsl_dev->np = dev->of_node;
339	drm->dev_private = fsl_dev;
340	dev_set_drvdata(dev, fsl_dev);
341
342	ret = drm_dev_register(drm, 0);
343	if (ret < 0)
344		goto put;
345
346	drm_client_setup_with_color_mode(drm, legacyfb_depth);
347
348	return 0;
349
350put:
351	drm_dev_put(drm);
352unregister_pix_clk:
353	clk_unregister(fsl_dev->pix_clk);
354disable_clk:
355	clk_disable_unprepare(fsl_dev->clk);
356	return ret;
357}
358
359static void fsl_dcu_drm_remove(struct platform_device *pdev)
360{
361	struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
362
363	drm_dev_unregister(fsl_dev->drm);
364	drm_dev_put(fsl_dev->drm);
365	clk_disable_unprepare(fsl_dev->clk);
366	clk_unregister(fsl_dev->pix_clk);
367}
368
369static void fsl_dcu_drm_shutdown(struct platform_device *pdev)
370{
371	struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
372
373	drm_atomic_helper_shutdown(fsl_dev->drm);
374}
375
376static struct platform_driver fsl_dcu_drm_platform_driver = {
377	.probe		= fsl_dcu_drm_probe,
378	.remove		= fsl_dcu_drm_remove,
379	.shutdown	= fsl_dcu_drm_shutdown,
380	.driver		= {
381		.name	= "fsl-dcu",
382		.pm	= &fsl_dcu_drm_pm_ops,
383		.of_match_table = fsl_dcu_of_match,
384	},
385};
386
387drm_module_platform_driver(fsl_dcu_drm_platform_driver);
388
389MODULE_DESCRIPTION("Freescale DCU DRM Driver");
390MODULE_LICENSE("GPL");
~/ Read-only file preview.