Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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_fbdev_generic.h>
 24#include <drm/drm_fourcc.h>
 25#include <drm/drm_gem_dma_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_DMA_FOPS(fops);
328
329static const struct drm_driver mxsfb_driver = {
330	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
331	DRM_GEM_DMA_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");