Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Copyright (C) 2016 Marek Vasut <marex@denx.de>
  3 *
  4 * This code is based on drivers/video/fbdev/mxsfb.c :
  5 * Copyright (C) 2010 Juergen Beisert, Pengutronix
  6 * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
  7 * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
  8 *
  9 * This program is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU General Public License
 11 * as published by the Free Software Foundation; either version 2
 12 * of the License, or (at your option) any later version.
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 */
 18
 19#include <linux/module.h>
 20#include <linux/spinlock.h>
 21#include <linux/clk.h>
 22#include <linux/component.h>
 23#include <linux/list.h>
 24#include <linux/of_device.h>
 25#include <linux/of_graph.h>
 26#include <linux/of_reserved_mem.h>
 27#include <linux/pm_runtime.h>
 28#include <linux/reservation.h>
 29
 30#include <drm/drmP.h>
 31#include <drm/drm_atomic.h>
 32#include <drm/drm_atomic_helper.h>
 33#include <drm/drm_crtc.h>
 34#include <drm/drm_crtc_helper.h>
 35#include <drm/drm_fb_helper.h>
 36#include <drm/drm_fb_cma_helper.h>
 37#include <drm/drm_gem_cma_helper.h>
 38#include <drm/drm_of.h>
 39#include <drm/drm_panel.h>
 40#include <drm/drm_simple_kms_helper.h>
 41
 42#include "mxsfb_drv.h"
 43#include "mxsfb_regs.h"
 44
 45enum mxsfb_devtype {
 46	MXSFB_V3,
 47	MXSFB_V4,
 48};
 49
 50static const struct mxsfb_devdata mxsfb_devdata[] = {
 51	[MXSFB_V3] = {
 52		.transfer_count	= LCDC_V3_TRANSFER_COUNT,
 53		.cur_buf	= LCDC_V3_CUR_BUF,
 54		.next_buf	= LCDC_V3_NEXT_BUF,
 55		.debug0		= LCDC_V3_DEBUG0,
 56		.hs_wdth_mask	= 0xff,
 57		.hs_wdth_shift	= 24,
 58		.ipversion	= 3,
 59	},
 60	[MXSFB_V4] = {
 61		.transfer_count	= LCDC_V4_TRANSFER_COUNT,
 62		.cur_buf	= LCDC_V4_CUR_BUF,
 63		.next_buf	= LCDC_V4_NEXT_BUF,
 64		.debug0		= LCDC_V4_DEBUG0,
 65		.hs_wdth_mask	= 0x3fff,
 66		.hs_wdth_shift	= 18,
 67		.ipversion	= 4,
 68	},
 69};
 70
 71static const uint32_t mxsfb_formats[] = {
 72	DRM_FORMAT_XRGB8888,
 73	DRM_FORMAT_RGB565
 74};
 75
 76static struct mxsfb_drm_private *
 77drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe)
 78{
 79	return container_of(pipe, struct mxsfb_drm_private, pipe);
 80}
 81
 82void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb)
 83{
 84	if (mxsfb->clk_axi)
 85		clk_prepare_enable(mxsfb->clk_axi);
 86}
 87
 88void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb)
 89{
 90	if (mxsfb->clk_axi)
 91		clk_disable_unprepare(mxsfb->clk_axi);
 92}
 93
 94static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
 95	.fb_create		= drm_fb_cma_create,
 96	.atomic_check		= drm_atomic_helper_check,
 97	.atomic_commit		= drm_atomic_helper_commit,
 98};
 99
100static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe,
101			      struct drm_crtc_state *crtc_state)
102{
103	struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
104
105	mxsfb_crtc_enable(mxsfb);
106}
107
108static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe)
109{
110	struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
111
112	mxsfb_crtc_disable(mxsfb);
113}
114
115static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe,
116			      struct drm_plane_state *plane_state)
117{
118	struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe);
119
120	mxsfb_plane_atomic_update(mxsfb, plane_state);
121}
122
123static int mxsfb_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
124				 struct drm_plane_state *plane_state)
125{
126	return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
127}
128
129struct drm_simple_display_pipe_funcs mxsfb_funcs = {
130	.enable		= mxsfb_pipe_enable,
131	.disable	= mxsfb_pipe_disable,
132	.update		= mxsfb_pipe_update,
133	.prepare_fb	= mxsfb_pipe_prepare_fb,
134};
135
136static int mxsfb_load(struct drm_device *drm, unsigned long flags)
137{
138	struct platform_device *pdev = to_platform_device(drm->dev);
139	struct mxsfb_drm_private *mxsfb;
140	struct resource *res;
141	int ret;
142
143	mxsfb = devm_kzalloc(&pdev->dev, sizeof(*mxsfb), GFP_KERNEL);
144	if (!mxsfb)
145		return -ENOMEM;
146
147	drm->dev_private = mxsfb;
148	mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data];
149
150	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
151	mxsfb->base = devm_ioremap_resource(drm->dev, res);
152	if (IS_ERR(mxsfb->base))
153		return PTR_ERR(mxsfb->base);
154
155	mxsfb->clk = devm_clk_get(drm->dev, NULL);
156	if (IS_ERR(mxsfb->clk))
157		return PTR_ERR(mxsfb->clk);
158
159	mxsfb->clk_axi = devm_clk_get(drm->dev, "axi");
160	if (IS_ERR(mxsfb->clk_axi))
161		mxsfb->clk_axi = NULL;
162
163	mxsfb->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi");
164	if (IS_ERR(mxsfb->clk_disp_axi))
165		mxsfb->clk_disp_axi = NULL;
166
167	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
168	if (ret)
169		return ret;
170
171	pm_runtime_enable(drm->dev);
172
173	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
174	if (ret < 0) {
175		dev_err(drm->dev, "Failed to initialise vblank\n");
176		goto err_vblank;
177	}
178
179	/* Modeset init */
180	drm_mode_config_init(drm);
181
182	ret = mxsfb_create_output(drm);
183	if (ret < 0) {
184		dev_err(drm->dev, "Failed to create outputs\n");
185		goto err_vblank;
186	}
187
188	ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs,
189			mxsfb_formats, ARRAY_SIZE(mxsfb_formats),
190			&mxsfb->connector);
191	if (ret < 0) {
192		dev_err(drm->dev, "Cannot setup simple display pipe\n");
193		goto err_vblank;
194	}
195
196	ret = drm_panel_attach(mxsfb->panel, &mxsfb->connector);
197	if (ret) {
198		dev_err(drm->dev, "Cannot connect panel\n");
199		goto err_vblank;
200	}
201
202	drm->mode_config.min_width	= MXSFB_MIN_XRES;
203	drm->mode_config.min_height	= MXSFB_MIN_YRES;
204	drm->mode_config.max_width	= MXSFB_MAX_XRES;
205	drm->mode_config.max_height	= MXSFB_MAX_YRES;
206	drm->mode_config.funcs		= &mxsfb_mode_config_funcs;
207
208	drm_mode_config_reset(drm);
209
210	pm_runtime_get_sync(drm->dev);
211	ret = drm_irq_install(drm, platform_get_irq(pdev, 0));
212	pm_runtime_put_sync(drm->dev);
213
214	if (ret < 0) {
215		dev_err(drm->dev, "Failed to install IRQ handler\n");
216		goto err_irq;
217	}
218
219	drm_kms_helper_poll_init(drm);
220
221	mxsfb->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc,
222					  drm->mode_config.num_connector);
223	if (IS_ERR(mxsfb->fbdev)) {
224		mxsfb->fbdev = NULL;
225		dev_err(drm->dev, "Failed to init FB CMA area\n");
226		goto err_cma;
227	}
228
229	platform_set_drvdata(pdev, drm);
230
231	drm_helper_hpd_irq_event(drm);
232
233	return 0;
234
235err_cma:
236	drm_irq_uninstall(drm);
237err_irq:
238	drm_panel_detach(mxsfb->panel);
239err_vblank:
240	pm_runtime_disable(drm->dev);
241
242	return ret;
243}
244
245static void mxsfb_unload(struct drm_device *drm)
246{
247	struct mxsfb_drm_private *mxsfb = drm->dev_private;
248
249	if (mxsfb->fbdev)
250		drm_fbdev_cma_fini(mxsfb->fbdev);
251
252	drm_kms_helper_poll_fini(drm);
253	drm_mode_config_cleanup(drm);
254	drm_vblank_cleanup(drm);
255
256	pm_runtime_get_sync(drm->dev);
257	drm_irq_uninstall(drm);
258	pm_runtime_put_sync(drm->dev);
259
260	drm->dev_private = NULL;
261
262	pm_runtime_disable(drm->dev);
263}
264
265static void mxsfb_lastclose(struct drm_device *drm)
266{
267	struct mxsfb_drm_private *mxsfb = drm->dev_private;
268
269	drm_fbdev_cma_restore_mode(mxsfb->fbdev);
270}
271
272static int mxsfb_enable_vblank(struct drm_device *drm, unsigned int crtc)
273{
274	struct mxsfb_drm_private *mxsfb = drm->dev_private;
275
276	/* Clear and enable VBLANK IRQ */
277	mxsfb_enable_axi_clk(mxsfb);
278	writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
279	writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
280	mxsfb_disable_axi_clk(mxsfb);
281
282	return 0;
283}
284
285static void mxsfb_disable_vblank(struct drm_device *drm, unsigned int crtc)
286{
287	struct mxsfb_drm_private *mxsfb = drm->dev_private;
288
289	/* Disable and clear VBLANK IRQ */
290	mxsfb_enable_axi_clk(mxsfb);
291	writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
292	writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
293	mxsfb_disable_axi_clk(mxsfb);
294}
295
296static void mxsfb_irq_preinstall(struct drm_device *drm)
297{
298	mxsfb_disable_vblank(drm, 0);
299}
300
301static irqreturn_t mxsfb_irq_handler(int irq, void *data)
302{
303	struct drm_device *drm = data;
304	struct mxsfb_drm_private *mxsfb = drm->dev_private;
305	u32 reg;
306
307	mxsfb_enable_axi_clk(mxsfb);
308
309	reg = readl(mxsfb->base + LCDC_CTRL1);
310
311	if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
312		drm_crtc_handle_vblank(&mxsfb->pipe.crtc);
313
314	writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
315
316	mxsfb_disable_axi_clk(mxsfb);
317
318	return IRQ_HANDLED;
319}
320
321static const struct file_operations fops = {
322	.owner		= THIS_MODULE,
323	.open		= drm_open,
324	.release	= drm_release,
325	.unlocked_ioctl	= drm_ioctl,
326#ifdef CONFIG_COMPAT
327	.compat_ioctl	= drm_compat_ioctl,
328#endif
329	.poll		= drm_poll,
330	.read		= drm_read,
331	.llseek		= noop_llseek,
332	.mmap		= drm_gem_cma_mmap,
333};
334
335static struct drm_driver mxsfb_driver = {
336	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
337				  DRIVER_PRIME | DRIVER_ATOMIC |
338				  DRIVER_HAVE_IRQ,
339	.lastclose		= mxsfb_lastclose,
340	.irq_handler		= mxsfb_irq_handler,
341	.irq_preinstall		= mxsfb_irq_preinstall,
342	.irq_uninstall		= mxsfb_irq_preinstall,
343	.get_vblank_counter	= drm_vblank_no_hw_counter,
344	.enable_vblank		= mxsfb_enable_vblank,
345	.disable_vblank		= mxsfb_disable_vblank,
346	.gem_free_object	= drm_gem_cma_free_object,
347	.gem_vm_ops		= &drm_gem_cma_vm_ops,
348	.dumb_create		= drm_gem_cma_dumb_create,
349	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
350	.dumb_destroy		= drm_gem_dumb_destroy,
351	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
352	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
353	.gem_prime_export	= drm_gem_prime_export,
354	.gem_prime_import	= drm_gem_prime_import,
355	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
356	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
357	.gem_prime_vmap		= drm_gem_cma_prime_vmap,
358	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap,
359	.gem_prime_mmap		= drm_gem_cma_prime_mmap,
360	.fops	= &fops,
361	.name	= "mxsfb-drm",
362	.desc	= "MXSFB Controller DRM",
363	.date	= "20160824",
364	.major	= 1,
365	.minor	= 0,
366};
367
368static const struct platform_device_id mxsfb_devtype[] = {
369	{ .name = "imx23-fb", .driver_data = MXSFB_V3, },
370	{ .name = "imx28-fb", .driver_data = MXSFB_V4, },
371	{ .name = "imx6sx-fb", .driver_data = MXSFB_V4, },
372	{ /* sentinel */ }
373};
374MODULE_DEVICE_TABLE(platform, mxsfb_devtype);
375
376static const struct of_device_id mxsfb_dt_ids[] = {
377	{ .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], },
378	{ .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], },
379	{ .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devtype[2], },
380	{ /* sentinel */ }
381};
382MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);
383
384static int mxsfb_probe(struct platform_device *pdev)
385{
386	struct drm_device *drm;
387	const struct of_device_id *of_id =
388			of_match_device(mxsfb_dt_ids, &pdev->dev);
389	int ret;
390
391	if (!pdev->dev.of_node)
392		return -ENODEV;
393
394	if (of_id)
395		pdev->id_entry = of_id->data;
396
397	drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev);
398	if (!drm)
399		return -ENOMEM;
400
401	ret = mxsfb_load(drm, 0);
402	if (ret)
403		goto err_free;
404
405	ret = drm_dev_register(drm, 0);
406	if (ret)
407		goto err_unload;
408
409	return 0;
410
411err_unload:
412	mxsfb_unload(drm);
413err_free:
414	drm_dev_unref(drm);
415
416	return ret;
417}
418
419static int mxsfb_remove(struct platform_device *pdev)
420{
421	struct drm_device *drm = platform_get_drvdata(pdev);
422
423	drm_dev_unregister(drm);
424	mxsfb_unload(drm);
425	drm_dev_unref(drm);
426
427	return 0;
428}
429
430static struct platform_driver mxsfb_platform_driver = {
431	.probe		= mxsfb_probe,
432	.remove		= mxsfb_remove,
433	.id_table	= mxsfb_devtype,
434	.driver	= {
435		.name		= "mxsfb",
436		.of_match_table	= mxsfb_dt_ids,
437	},
438};
439
440module_platform_driver(mxsfb_platform_driver);
441
442MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
443MODULE_DESCRIPTION("Freescale MXS DRM/KMS driver");
444MODULE_LICENSE("GPL");