1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
216ea975eSRob Clark /*
316ea975eSRob Clark  * Copyright (C) 2012 Texas Instruments
416ea975eSRob Clark  * Author: Rob Clark <robdclark@gmail.com>
516ea975eSRob Clark  */
616ea975eSRob Clark 
716ea975eSRob Clark /* LCDC DRM driver, based on da8xx-fb */
816ea975eSRob Clark 
9103cd8bcSJyri Sarha #include <linux/component.h>
10fcb57664SSam Ravnborg #include <linux/mod_devicetable.h>
11fcb57664SSam Ravnborg #include <linux/module.h>
12416a07fbSDave Gerlach #include <linux/pinctrl/consumer.h>
13fcb57664SSam Ravnborg #include <linux/platform_device.h>
14fcb57664SSam Ravnborg #include <linux/pm_runtime.h>
15fcb57664SSam Ravnborg 
16edc43303SJyri Sarha #include <drm/drm_atomic_helper.h>
17fcb57664SSam Ravnborg #include <drm/drm_debugfs.h>
18fcb57664SSam Ravnborg #include <drm/drm_drv.h>
1910143427SThomas Zimmermann #include <drm/drm_fbdev_dma.h>
20fcb57664SSam Ravnborg #include <drm/drm_fourcc.h>
214a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
226025a157SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h>
23fcb57664SSam Ravnborg #include <drm/drm_mm.h>
24fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
25fcb57664SSam Ravnborg #include <drm/drm_vblank.h>
26fcb57664SSam Ravnborg 
27103cd8bcSJyri Sarha 
2816ea975eSRob Clark #include "tilcdc_drv.h"
29fcb57664SSam Ravnborg #include "tilcdc_external.h"
30fcb57664SSam Ravnborg #include "tilcdc_panel.h"
3116ea975eSRob Clark #include "tilcdc_regs.h"
3216ea975eSRob Clark 
3316ea975eSRob Clark static LIST_HEAD(module_list);
3416ea975eSRob Clark 
35bcc5a6f5SJyri Sarha static const u32 tilcdc_rev1_formats[] = { DRM_FORMAT_RGB565 };
36bcc5a6f5SJyri Sarha 
37bcc5a6f5SJyri Sarha static const u32 tilcdc_straight_formats[] = { DRM_FORMAT_RGB565,
38bcc5a6f5SJyri Sarha 					       DRM_FORMAT_BGR888,
39bcc5a6f5SJyri Sarha 					       DRM_FORMAT_XBGR8888 };
40bcc5a6f5SJyri Sarha 
41bcc5a6f5SJyri Sarha static const u32 tilcdc_crossed_formats[] = { DRM_FORMAT_BGR565,
42bcc5a6f5SJyri Sarha 					      DRM_FORMAT_RGB888,
43bcc5a6f5SJyri Sarha 					      DRM_FORMAT_XRGB8888 };
44bcc5a6f5SJyri Sarha 
45bcc5a6f5SJyri Sarha static const u32 tilcdc_legacy_formats[] = { DRM_FORMAT_RGB565,
46bcc5a6f5SJyri Sarha 					     DRM_FORMAT_RGB888,
47bcc5a6f5SJyri Sarha 					     DRM_FORMAT_XRGB8888 };
48bcc5a6f5SJyri Sarha 
tilcdc_module_init(struct tilcdc_module * mod,const char * name,const struct tilcdc_module_ops * funcs)4916ea975eSRob Clark void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
5016ea975eSRob Clark 		const struct tilcdc_module_ops *funcs)
5116ea975eSRob Clark {
5216ea975eSRob Clark 	mod->name = name;
5316ea975eSRob Clark 	mod->funcs = funcs;
5416ea975eSRob Clark 	INIT_LIST_HEAD(&mod->list);
5516ea975eSRob Clark 	list_add(&mod->list, &module_list);
5616ea975eSRob Clark }
5716ea975eSRob Clark 
tilcdc_module_cleanup(struct tilcdc_module * mod)5816ea975eSRob Clark void tilcdc_module_cleanup(struct tilcdc_module *mod)
5916ea975eSRob Clark {
6016ea975eSRob Clark 	list_del(&mod->list);
6116ea975eSRob Clark }
6216ea975eSRob Clark 
tilcdc_atomic_check(struct drm_device * dev,struct drm_atomic_state * state)6330457676SWei Yongjun static int tilcdc_atomic_check(struct drm_device *dev,
64edc43303SJyri Sarha 			       struct drm_atomic_state *state)
65edc43303SJyri Sarha {
66edc43303SJyri Sarha 	int ret;
67edc43303SJyri Sarha 
68edc43303SJyri Sarha 	ret = drm_atomic_helper_check_modeset(dev, state);
69edc43303SJyri Sarha 	if (ret)
70edc43303SJyri Sarha 		return ret;
71edc43303SJyri Sarha 
72edc43303SJyri Sarha 	ret = drm_atomic_helper_check_planes(dev, state);
73edc43303SJyri Sarha 	if (ret)
74edc43303SJyri Sarha 		return ret;
75edc43303SJyri Sarha 
76edc43303SJyri Sarha 	/*
77edc43303SJyri Sarha 	 * tilcdc ->atomic_check can update ->mode_changed if pixel format
78edc43303SJyri Sarha 	 * changes, hence will we check modeset changes again.
79edc43303SJyri Sarha 	 */
80edc43303SJyri Sarha 	ret = drm_atomic_helper_check_modeset(dev, state);
81edc43303SJyri Sarha 	if (ret)
82edc43303SJyri Sarha 		return ret;
83edc43303SJyri Sarha 
84edc43303SJyri Sarha 	return ret;
85edc43303SJyri Sarha }
86edc43303SJyri Sarha 
8716ea975eSRob Clark static const struct drm_mode_config_funcs mode_config_funcs = {
88931e691cSDaniel Vetter 	.fb_create = drm_gem_fb_create,
89edc43303SJyri Sarha 	.atomic_check = tilcdc_atomic_check,
90cb345decSDaniel Vetter 	.atomic_commit = drm_atomic_helper_commit,
9116ea975eSRob Clark };
9216ea975eSRob Clark 
modeset_init(struct drm_device * dev)939963d36dSJyri Sarha static void modeset_init(struct drm_device *dev)
9416ea975eSRob Clark {
9516ea975eSRob Clark 	struct tilcdc_drm_private *priv = dev->dev_private;
9616ea975eSRob Clark 	struct tilcdc_module *mod;
9716ea975eSRob Clark 
9816ea975eSRob Clark 	list_for_each_entry(mod, &module_list, list) {
9916ea975eSRob Clark 		DBG("loading module: %s", mod->name);
10016ea975eSRob Clark 		mod->funcs->modeset_init(mod, dev);
10116ea975eSRob Clark 	}
10216ea975eSRob Clark 
10316ea975eSRob Clark 	dev->mode_config.min_width = 0;
10416ea975eSRob Clark 	dev->mode_config.min_height = 0;
105882bceffSJyri Sarha 	dev->mode_config.max_width = priv->max_width;
10616ea975eSRob Clark 	dev->mode_config.max_height = 2048;
10716ea975eSRob Clark 	dev->mode_config.funcs = &mode_config_funcs;
10816ea975eSRob Clark }
10916ea975eSRob Clark 
11016ea975eSRob Clark #ifdef CONFIG_CPU_FREQ
cpufreq_transition(struct notifier_block * nb,unsigned long val,void * data)11116ea975eSRob Clark static int cpufreq_transition(struct notifier_block *nb,
11216ea975eSRob Clark 				     unsigned long val, void *data)
11316ea975eSRob Clark {
11416ea975eSRob Clark 	struct tilcdc_drm_private *priv = container_of(nb,
11516ea975eSRob Clark 			struct tilcdc_drm_private, freq_transition);
116a6b7ebaaSJyri Sarha 
117642e5167SJyri Sarha 	if (val == CPUFREQ_POSTCHANGE)
11816ea975eSRob Clark 		tilcdc_crtc_update_clk(priv->crtc);
11916ea975eSRob Clark 
12016ea975eSRob Clark 	return 0;
12116ea975eSRob Clark }
12216ea975eSRob Clark #endif
12316ea975eSRob Clark 
tilcdc_irq(int irq,void * arg)124b6366814SThomas Zimmermann static irqreturn_t tilcdc_irq(int irq, void *arg)
125b6366814SThomas Zimmermann {
126b6366814SThomas Zimmermann 	struct drm_device *dev = arg;
127b6366814SThomas Zimmermann 	struct tilcdc_drm_private *priv = dev->dev_private;
128b6366814SThomas Zimmermann 
129b6366814SThomas Zimmermann 	return tilcdc_crtc_irq(priv->crtc);
130b6366814SThomas Zimmermann }
131b6366814SThomas Zimmermann 
tilcdc_irq_install(struct drm_device * dev,unsigned int irq)132b6366814SThomas Zimmermann static int tilcdc_irq_install(struct drm_device *dev, unsigned int irq)
133b6366814SThomas Zimmermann {
134b6366814SThomas Zimmermann 	struct tilcdc_drm_private *priv = dev->dev_private;
135b6366814SThomas Zimmermann 	int ret;
136b6366814SThomas Zimmermann 
137b6366814SThomas Zimmermann 	ret = request_irq(irq, tilcdc_irq, 0, dev->driver->name, dev);
138b6366814SThomas Zimmermann 	if (ret)
139b6366814SThomas Zimmermann 		return ret;
140b6366814SThomas Zimmermann 
141*c56a4ad4STomi Valkeinen 	priv->irq_enabled = true;
142b6366814SThomas Zimmermann 
143b6366814SThomas Zimmermann 	return 0;
144b6366814SThomas Zimmermann }
145b6366814SThomas Zimmermann 
tilcdc_irq_uninstall(struct drm_device * dev)146b6366814SThomas Zimmermann static void tilcdc_irq_uninstall(struct drm_device *dev)
147b6366814SThomas Zimmermann {
148b6366814SThomas Zimmermann 	struct tilcdc_drm_private *priv = dev->dev_private;
149b6366814SThomas Zimmermann 
150b6366814SThomas Zimmermann 	if (!priv->irq_enabled)
151b6366814SThomas Zimmermann 		return;
152b6366814SThomas Zimmermann 
153b6366814SThomas Zimmermann 	free_irq(priv->irq, dev);
154b6366814SThomas Zimmermann 	priv->irq_enabled = false;
155b6366814SThomas Zimmermann }
156b6366814SThomas Zimmermann 
15716ea975eSRob Clark /*
15816ea975eSRob Clark  * DRM operations:
15916ea975eSRob Clark  */
16016ea975eSRob Clark 
tilcdc_fini(struct drm_device * dev)161923310baSJyri Sarha static void tilcdc_fini(struct drm_device *dev)
16216ea975eSRob Clark {
16316ea975eSRob Clark 	struct tilcdc_drm_private *priv = dev->dev_private;
16416ea975eSRob Clark 
165432973fdSJyri Sarha #ifdef CONFIG_CPU_FREQ
166432973fdSJyri Sarha 	if (priv->freq_transition.notifier_call)
167432973fdSJyri Sarha 		cpufreq_unregister_notifier(&priv->freq_transition,
168432973fdSJyri Sarha 					    CPUFREQ_TRANSITION_NOTIFIER);
169432973fdSJyri Sarha #endif
170432973fdSJyri Sarha 
1719e79e062SJyri Sarha 	if (priv->crtc)
1722d53a180SJyri Sarha 		tilcdc_crtc_shutdown(priv->crtc);
173103cd8bcSJyri Sarha 
1749e79e062SJyri Sarha 	if (priv->is_registered)
175923310baSJyri Sarha 		drm_dev_unregister(dev);
176923310baSJyri Sarha 
17716ea975eSRob Clark 	drm_kms_helper_poll_fini(dev);
17869f03be1SDouglas Anderson 	drm_atomic_helper_shutdown(dev);
179b6366814SThomas Zimmermann 	tilcdc_irq_uninstall(dev);
180923310baSJyri Sarha 	drm_mode_config_cleanup(dev);
18116ea975eSRob Clark 
18216ea975eSRob Clark 	if (priv->clk)
18316ea975eSRob Clark 		clk_put(priv->clk);
18416ea975eSRob Clark 
18516ea975eSRob Clark 	if (priv->mmio)
18616ea975eSRob Clark 		iounmap(priv->mmio);
18716ea975eSRob Clark 
188f8e7bce3SChristophe JAILLET 	if (priv->wq)
18916ea975eSRob Clark 		destroy_workqueue(priv->wq);
19016ea975eSRob Clark 
19116ea975eSRob Clark 	dev->dev_private = NULL;
19216ea975eSRob Clark 
19316ea975eSRob Clark 	pm_runtime_disable(dev->dev);
19416ea975eSRob Clark 
195ce7b700dSAishwarya Pant 	drm_dev_put(dev);
19616ea975eSRob Clark }
19716ea975eSRob Clark 
tilcdc_init(const struct drm_driver * ddrv,struct device * dev)19870a59dd8SDaniel Vetter static int tilcdc_init(const struct drm_driver *ddrv, struct device *dev)
19916ea975eSRob Clark {
200923310baSJyri Sarha 	struct drm_device *ddev;
201923310baSJyri Sarha 	struct platform_device *pdev = to_platform_device(dev);
202923310baSJyri Sarha 	struct device_node *node = dev->of_node;
20316ea975eSRob Clark 	struct tilcdc_drm_private *priv;
20416ea975eSRob Clark 	struct resource *res;
205dc28aa07SBenoit Parrot 	u32 bpp = 0;
20616ea975eSRob Clark 	int ret;
20716ea975eSRob Clark 
208923310baSJyri Sarha 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
2093366ba38SMarkus Elfring 	if (!priv)
21016ea975eSRob Clark 		return -ENOMEM;
21116ea975eSRob Clark 
212923310baSJyri Sarha 	ddev = drm_dev_alloc(ddrv, dev);
213923310baSJyri Sarha 	if (IS_ERR(ddev))
214923310baSJyri Sarha 		return PTR_ERR(ddev);
215923310baSJyri Sarha 
216923310baSJyri Sarha 	ddev->dev_private = priv;
2179e79e062SJyri Sarha 	platform_set_drvdata(pdev, ddev);
2189e79e062SJyri Sarha 	drm_mode_config_init(ddev);
21916ea975eSRob Clark 
220103cd8bcSJyri Sarha 	priv->is_componentized =
221923310baSJyri Sarha 		tilcdc_get_external_components(dev, NULL) > 0;
222103cd8bcSJyri Sarha 
22316ea975eSRob Clark 	priv->wq = alloc_ordered_workqueue("tilcdc", 0);
224b478e336SEzequiel Garcia 	if (!priv->wq) {
225b478e336SEzequiel Garcia 		ret = -ENOMEM;
2269e79e062SJyri Sarha 		goto init_failed;
227b478e336SEzequiel Garcia 	}
22816ea975eSRob Clark 
22916ea975eSRob Clark 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
23016ea975eSRob Clark 	if (!res) {
231923310baSJyri Sarha 		dev_err(dev, "failed to get memory resource\n");
23216ea975eSRob Clark 		ret = -EINVAL;
2339e79e062SJyri Sarha 		goto init_failed;
23416ea975eSRob Clark 	}
23516ea975eSRob Clark 
2364bdc0d67SChristoph Hellwig 	priv->mmio = ioremap(res->start, resource_size(res));
23716ea975eSRob Clark 	if (!priv->mmio) {
238923310baSJyri Sarha 		dev_err(dev, "failed to ioremap\n");
23916ea975eSRob Clark 		ret = -ENOMEM;
2409e79e062SJyri Sarha 		goto init_failed;
24116ea975eSRob Clark 	}
24216ea975eSRob Clark 
243923310baSJyri Sarha 	priv->clk = clk_get(dev, "fck");
24416ea975eSRob Clark 	if (IS_ERR(priv->clk)) {
245923310baSJyri Sarha 		dev_err(dev, "failed to get functional clock\n");
24616ea975eSRob Clark 		ret = -ENODEV;
2479e79e062SJyri Sarha 		goto init_failed;
24816ea975eSRob Clark 	}
24916ea975eSRob Clark 
250923310baSJyri Sarha 	pm_runtime_enable(dev);
25116ea975eSRob Clark 
25216ea975eSRob Clark 	/* Determine LCD IP Version */
253923310baSJyri Sarha 	pm_runtime_get_sync(dev);
254923310baSJyri Sarha 	switch (tilcdc_read(ddev, LCDC_PID_REG)) {
25516ea975eSRob Clark 	case 0x4c100102:
25616ea975eSRob Clark 		priv->rev = 1;
25716ea975eSRob Clark 		break;
25816ea975eSRob Clark 	case 0x4f200800:
25916ea975eSRob Clark 	case 0x4f201000:
26016ea975eSRob Clark 		priv->rev = 2;
26116ea975eSRob Clark 		break;
26216ea975eSRob Clark 	default:
263923310baSJyri Sarha 		dev_warn(dev, "Unknown PID Reg value 0x%08x, "
26416ea975eSRob Clark 			"defaulting to LCD revision 1\n",
265923310baSJyri Sarha 			tilcdc_read(ddev, LCDC_PID_REG));
26616ea975eSRob Clark 		priv->rev = 1;
26716ea975eSRob Clark 		break;
26816ea975eSRob Clark 	}
26916ea975eSRob Clark 
270923310baSJyri Sarha 	pm_runtime_put_sync(dev);
27116ea975eSRob Clark 
272bcc5a6f5SJyri Sarha 	if (priv->rev == 1) {
273bcc5a6f5SJyri Sarha 		DBG("Revision 1 LCDC supports only RGB565 format");
274bcc5a6f5SJyri Sarha 		priv->pixelformats = tilcdc_rev1_formats;
275bcc5a6f5SJyri Sarha 		priv->num_pixelformats = ARRAY_SIZE(tilcdc_rev1_formats);
276c5665385SJyri Sarha 		bpp = 16;
277bcc5a6f5SJyri Sarha 	} else {
278bcc5a6f5SJyri Sarha 		const char *str = "\0";
279bcc5a6f5SJyri Sarha 
280bcc5a6f5SJyri Sarha 		of_property_read_string(node, "blue-and-red-wiring", &str);
281bcc5a6f5SJyri Sarha 		if (0 == strcmp(str, "crossed")) {
282bcc5a6f5SJyri Sarha 			DBG("Configured for crossed blue and red wires");
283bcc5a6f5SJyri Sarha 			priv->pixelformats = tilcdc_crossed_formats;
284bcc5a6f5SJyri Sarha 			priv->num_pixelformats =
285bcc5a6f5SJyri Sarha 				ARRAY_SIZE(tilcdc_crossed_formats);
286c5665385SJyri Sarha 			bpp = 32; /* Choose bpp with RGB support for fbdef */
287bcc5a6f5SJyri Sarha 		} else if (0 == strcmp(str, "straight")) {
288bcc5a6f5SJyri Sarha 			DBG("Configured for straight blue and red wires");
289bcc5a6f5SJyri Sarha 			priv->pixelformats = tilcdc_straight_formats;
290bcc5a6f5SJyri Sarha 			priv->num_pixelformats =
291bcc5a6f5SJyri Sarha 				ARRAY_SIZE(tilcdc_straight_formats);
292c5665385SJyri Sarha 			bpp = 16; /* Choose bpp with RGB support for fbdef */
293bcc5a6f5SJyri Sarha 		} else {
294bcc5a6f5SJyri Sarha 			DBG("Blue and red wiring '%s' unknown, use legacy mode",
295bcc5a6f5SJyri Sarha 			    str);
296bcc5a6f5SJyri Sarha 			priv->pixelformats = tilcdc_legacy_formats;
297bcc5a6f5SJyri Sarha 			priv->num_pixelformats =
298bcc5a6f5SJyri Sarha 				ARRAY_SIZE(tilcdc_legacy_formats);
299c5665385SJyri Sarha 			bpp = 16; /* This is just a guess */
300bcc5a6f5SJyri Sarha 		}
301bcc5a6f5SJyri Sarha 	}
302bcc5a6f5SJyri Sarha 
303882bceffSJyri Sarha 	if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth))
304882bceffSJyri Sarha 		priv->max_bandwidth = TILCDC_DEFAULT_MAX_BANDWIDTH;
305882bceffSJyri Sarha 
306882bceffSJyri Sarha 	DBG("Maximum Bandwidth Value %d", priv->max_bandwidth);
307882bceffSJyri Sarha 
308882bceffSJyri Sarha 	if (of_property_read_u32(node, "max-width", &priv->max_width)) {
309882bceffSJyri Sarha 		if (priv->rev == 1)
310882bceffSJyri Sarha 			priv->max_width = TILCDC_DEFAULT_MAX_WIDTH_V1;
311882bceffSJyri Sarha 		else
312882bceffSJyri Sarha 			priv->max_width = TILCDC_DEFAULT_MAX_WIDTH_V2;
313882bceffSJyri Sarha 	}
314882bceffSJyri Sarha 
315882bceffSJyri Sarha 	DBG("Maximum Horizontal Pixel Width Value %dpixels", priv->max_width);
316882bceffSJyri Sarha 
317882bceffSJyri Sarha 	if (of_property_read_u32(node, "max-pixelclock",
318882bceffSJyri Sarha 				 &priv->max_pixelclock))
319882bceffSJyri Sarha 		priv->max_pixelclock = TILCDC_DEFAULT_MAX_PIXELCLOCK;
320882bceffSJyri Sarha 
321882bceffSJyri Sarha 	DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock);
322882bceffSJyri Sarha 
3239963d36dSJyri Sarha 	ret = tilcdc_crtc_create(ddev);
32416ea975eSRob Clark 	if (ret < 0) {
3259963d36dSJyri Sarha 		dev_err(dev, "failed to create crtc\n");
3269e79e062SJyri Sarha 		goto init_failed;
32716ea975eSRob Clark 	}
3289963d36dSJyri Sarha 	modeset_init(ddev);
32916ea975eSRob Clark 
330432973fdSJyri Sarha #ifdef CONFIG_CPU_FREQ
331432973fdSJyri Sarha 	priv->freq_transition.notifier_call = cpufreq_transition;
332432973fdSJyri Sarha 	ret = cpufreq_register_notifier(&priv->freq_transition,
333432973fdSJyri Sarha 			CPUFREQ_TRANSITION_NOTIFIER);
334432973fdSJyri Sarha 	if (ret) {
335432973fdSJyri Sarha 		dev_err(dev, "failed to register cpufreq notifier\n");
336432973fdSJyri Sarha 		priv->freq_transition.notifier_call = NULL;
337432973fdSJyri Sarha 		goto init_failed;
338432973fdSJyri Sarha 	}
339432973fdSJyri Sarha #endif
340432973fdSJyri Sarha 
341103cd8bcSJyri Sarha 	if (priv->is_componentized) {
342923310baSJyri Sarha 		ret = component_bind_all(dev, ddev);
343103cd8bcSJyri Sarha 		if (ret < 0)
3449e79e062SJyri Sarha 			goto init_failed;
345103cd8bcSJyri Sarha 
346ec9eab09SJyri Sarha 		ret = tilcdc_add_component_encoder(ddev);
347103cd8bcSJyri Sarha 		if (ret < 0)
3489e79e062SJyri Sarha 			goto init_failed;
349ec9eab09SJyri Sarha 	} else {
350ec9eab09SJyri Sarha 		ret = tilcdc_attach_external_device(ddev);
351ec9eab09SJyri Sarha 		if (ret)
352ec9eab09SJyri Sarha 			goto init_failed;
353103cd8bcSJyri Sarha 	}
354103cd8bcSJyri Sarha 
355ec9eab09SJyri Sarha 	if (!priv->external_connector &&
356ec9eab09SJyri Sarha 	    ((priv->num_encoders == 0) || (priv->num_connectors == 0))) {
357923310baSJyri Sarha 		dev_err(dev, "no encoders/connectors found\n");
358a132b5a5SSjoerd Simons 		ret = -EPROBE_DEFER;
3599e79e062SJyri Sarha 		goto init_failed;
360103cd8bcSJyri Sarha 	}
361103cd8bcSJyri Sarha 
362923310baSJyri Sarha 	ret = drm_vblank_init(ddev, 1);
36316ea975eSRob Clark 	if (ret < 0) {
364923310baSJyri Sarha 		dev_err(dev, "failed to initialize vblank\n");
3659e79e062SJyri Sarha 		goto init_failed;
36616ea975eSRob Clark 	}
36716ea975eSRob Clark 
368b6366814SThomas Zimmermann 	ret = platform_get_irq(pdev, 0);
369b6366814SThomas Zimmermann 	if (ret < 0)
370b6366814SThomas Zimmermann 		goto init_failed;
371b6366814SThomas Zimmermann 	priv->irq = ret;
372b6366814SThomas Zimmermann 
373b6366814SThomas Zimmermann 	ret = tilcdc_irq_install(ddev, priv->irq);
37416ea975eSRob Clark 	if (ret < 0) {
375923310baSJyri Sarha 		dev_err(dev, "failed to install IRQ handler\n");
3769e79e062SJyri Sarha 		goto init_failed;
37716ea975eSRob Clark 	}
37816ea975eSRob Clark 
379923310baSJyri Sarha 	drm_mode_config_reset(ddev);
380522a76f8SJyri Sarha 
381923310baSJyri Sarha 	drm_kms_helper_poll_init(ddev);
382923310baSJyri Sarha 
383923310baSJyri Sarha 	ret = drm_dev_register(ddev, 0);
384923310baSJyri Sarha 	if (ret)
3859e79e062SJyri Sarha 		goto init_failed;
386f5ef0762SThomas Zimmermann 	priv->is_registered = true;
38716ea975eSRob Clark 
38810143427SThomas Zimmermann 	drm_fbdev_dma_setup(ddev, bpp);
38916ea975eSRob Clark 	return 0;
39016ea975eSRob Clark 
3919e79e062SJyri Sarha init_failed:
3929e79e062SJyri Sarha 	tilcdc_fini(ddev);
39369f03be1SDouglas Anderson 	platform_set_drvdata(pdev, NULL);
394d0ec32caSJyri Sarha 
39516ea975eSRob Clark 	return ret;
39616ea975eSRob Clark }
39716ea975eSRob Clark 
398514d1a1fSJyri Sarha #if defined(CONFIG_DEBUG_FS)
39916ea975eSRob Clark static const struct {
40016ea975eSRob Clark 	const char *name;
40116ea975eSRob Clark 	uint8_t  rev;
40216ea975eSRob Clark 	uint8_t  save;
40316ea975eSRob Clark 	uint32_t reg;
40416ea975eSRob Clark } registers[] =		{
40516ea975eSRob Clark #define REG(rev, save, reg) { #reg, rev, save, reg }
40616ea975eSRob Clark 		/* exists in revision 1: */
40716ea975eSRob Clark 		REG(1, false, LCDC_PID_REG),
40816ea975eSRob Clark 		REG(1, true,  LCDC_CTRL_REG),
40916ea975eSRob Clark 		REG(1, false, LCDC_STAT_REG),
41016ea975eSRob Clark 		REG(1, true,  LCDC_RASTER_CTRL_REG),
41116ea975eSRob Clark 		REG(1, true,  LCDC_RASTER_TIMING_0_REG),
41216ea975eSRob Clark 		REG(1, true,  LCDC_RASTER_TIMING_1_REG),
41316ea975eSRob Clark 		REG(1, true,  LCDC_RASTER_TIMING_2_REG),
41416ea975eSRob Clark 		REG(1, true,  LCDC_DMA_CTRL_REG),
41516ea975eSRob Clark 		REG(1, true,  LCDC_DMA_FB_BASE_ADDR_0_REG),
41616ea975eSRob Clark 		REG(1, true,  LCDC_DMA_FB_CEILING_ADDR_0_REG),
41716ea975eSRob Clark 		REG(1, true,  LCDC_DMA_FB_BASE_ADDR_1_REG),
41816ea975eSRob Clark 		REG(1, true,  LCDC_DMA_FB_CEILING_ADDR_1_REG),
41916ea975eSRob Clark 		/* new in revision 2: */
42016ea975eSRob Clark 		REG(2, false, LCDC_RAW_STAT_REG),
42116ea975eSRob Clark 		REG(2, false, LCDC_MASKED_STAT_REG),
422f3a99946SJyri Sarha 		REG(2, true, LCDC_INT_ENABLE_SET_REG),
42316ea975eSRob Clark 		REG(2, false, LCDC_INT_ENABLE_CLR_REG),
42416ea975eSRob Clark 		REG(2, false, LCDC_END_OF_INT_IND_REG),
42516ea975eSRob Clark 		REG(2, true,  LCDC_CLK_ENABLE_REG),
42616ea975eSRob Clark #undef REG
42716ea975eSRob Clark };
42829ddd6e1SJyri Sarha 
42916ea975eSRob Clark #endif
43016ea975eSRob Clark 
43116ea975eSRob Clark #ifdef CONFIG_DEBUG_FS
tilcdc_regs_show(struct seq_file * m,void * arg)43216ea975eSRob Clark static int tilcdc_regs_show(struct seq_file *m, void *arg)
43316ea975eSRob Clark {
43416ea975eSRob Clark 	struct drm_info_node *node = (struct drm_info_node *) m->private;
43516ea975eSRob Clark 	struct drm_device *dev = node->minor->dev;
43616ea975eSRob Clark 	struct tilcdc_drm_private *priv = dev->dev_private;
43716ea975eSRob Clark 	unsigned i;
43816ea975eSRob Clark 
43916ea975eSRob Clark 	pm_runtime_get_sync(dev->dev);
44016ea975eSRob Clark 
44116ea975eSRob Clark 	seq_printf(m, "revision: %d\n", priv->rev);
44216ea975eSRob Clark 
44316ea975eSRob Clark 	for (i = 0; i < ARRAY_SIZE(registers); i++)
44416ea975eSRob Clark 		if (priv->rev >= registers[i].rev)
44516ea975eSRob Clark 			seq_printf(m, "%s:\t %08x\n", registers[i].name,
44616ea975eSRob Clark 					tilcdc_read(dev, registers[i].reg));
44716ea975eSRob Clark 
44816ea975eSRob Clark 	pm_runtime_put_sync(dev->dev);
44916ea975eSRob Clark 
45016ea975eSRob Clark 	return 0;
45116ea975eSRob Clark }
45216ea975eSRob Clark 
tilcdc_mm_show(struct seq_file * m,void * arg)45316ea975eSRob Clark static int tilcdc_mm_show(struct seq_file *m, void *arg)
45416ea975eSRob Clark {
45516ea975eSRob Clark 	struct drm_info_node *node = (struct drm_info_node *) m->private;
45616ea975eSRob Clark 	struct drm_device *dev = node->minor->dev;
457b5c3714fSDaniel Vetter 	struct drm_printer p = drm_seq_file_printer(m);
458b5c3714fSDaniel Vetter 	drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p);
459b5c3714fSDaniel Vetter 	return 0;
46016ea975eSRob Clark }
46116ea975eSRob Clark 
46216ea975eSRob Clark static struct drm_info_list tilcdc_debugfs_list[] = {
46365b7da27SArnd Bergmann 		{ "regs", tilcdc_regs_show, 0, NULL },
46465b7da27SArnd Bergmann 		{ "mm",   tilcdc_mm_show,   0, NULL },
46516ea975eSRob Clark };
46616ea975eSRob Clark 
tilcdc_debugfs_init(struct drm_minor * minor)4677ce84471SWambui Karuga static void tilcdc_debugfs_init(struct drm_minor *minor)
46816ea975eSRob Clark {
46916ea975eSRob Clark 	struct tilcdc_module *mod;
47016ea975eSRob Clark 
471f1c1a1f3SWambui Karuga 	drm_debugfs_create_files(tilcdc_debugfs_list,
47216ea975eSRob Clark 				 ARRAY_SIZE(tilcdc_debugfs_list),
47316ea975eSRob Clark 				 minor->debugfs_root, minor);
47416ea975eSRob Clark 
47516ea975eSRob Clark 	list_for_each_entry(mod, &module_list, list)
47616ea975eSRob Clark 		if (mod->funcs->debugfs_init)
47716ea975eSRob Clark 			mod->funcs->debugfs_init(mod, minor);
47816ea975eSRob Clark }
47916ea975eSRob Clark #endif
48016ea975eSRob Clark 
4814a83c26aSDanilo Krummrich DEFINE_DRM_GEM_DMA_FOPS(fops);
48216ea975eSRob Clark 
48370a59dd8SDaniel Vetter static const struct drm_driver tilcdc_driver = {
4840424fdafSDaniel Vetter 	.driver_features    = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
4854a83c26aSDanilo Krummrich 	DRM_GEM_DMA_DRIVER_OPS,
48616ea975eSRob Clark #ifdef CONFIG_DEBUG_FS
48716ea975eSRob Clark 	.debugfs_init       = tilcdc_debugfs_init,
48816ea975eSRob Clark #endif
48916ea975eSRob Clark 	.fops               = &fops,
49016ea975eSRob Clark 	.name               = "tilcdc",
49116ea975eSRob Clark 	.desc               = "TI LCD Controller DRM",
49216ea975eSRob Clark 	.date               = "20121205",
49316ea975eSRob Clark 	.major              = 1,
49416ea975eSRob Clark 	.minor              = 0,
49516ea975eSRob Clark };
49616ea975eSRob Clark 
49716ea975eSRob Clark /*
49816ea975eSRob Clark  * Power management:
49916ea975eSRob Clark  */
50016ea975eSRob Clark 
tilcdc_pm_suspend(struct device * dev)50116ea975eSRob Clark static int tilcdc_pm_suspend(struct device *dev)
50216ea975eSRob Clark {
50316ea975eSRob Clark 	struct drm_device *ddev = dev_get_drvdata(dev);
5044fdce78aSSouptick Joarder 	int ret = 0;
50516ea975eSRob Clark 
5064fdce78aSSouptick Joarder 	ret = drm_mode_config_helper_suspend(ddev);
50716ea975eSRob Clark 
50885fd27f8SDarren Etheridge 	/* Select sleep pin state */
50985fd27f8SDarren Etheridge 	pinctrl_pm_select_sleep_state(dev);
51085fd27f8SDarren Etheridge 
5114fdce78aSSouptick Joarder 	return ret;
51216ea975eSRob Clark }
51316ea975eSRob Clark 
tilcdc_pm_resume(struct device * dev)51416ea975eSRob Clark static int tilcdc_pm_resume(struct device *dev)
51516ea975eSRob Clark {
51616ea975eSRob Clark 	struct drm_device *ddev = dev_get_drvdata(dev);
51716ea975eSRob Clark 
518416a07fbSDave Gerlach 	/* Select default pin state */
519416a07fbSDave Gerlach 	pinctrl_pm_select_default_state(dev);
5204fdce78aSSouptick Joarder 	return  drm_mode_config_helper_resume(ddev);
52116ea975eSRob Clark }
52216ea975eSRob Clark 
5238d1c4e59SPaul Cercueil static DEFINE_SIMPLE_DEV_PM_OPS(tilcdc_pm_ops,
5248d1c4e59SPaul Cercueil 				tilcdc_pm_suspend, tilcdc_pm_resume);
52516ea975eSRob Clark 
52616ea975eSRob Clark /*
52716ea975eSRob Clark  * Platform driver:
52816ea975eSRob Clark  */
tilcdc_bind(struct device * dev)529103cd8bcSJyri Sarha static int tilcdc_bind(struct device *dev)
530103cd8bcSJyri Sarha {
531923310baSJyri Sarha 	return tilcdc_init(&tilcdc_driver, dev);
532103cd8bcSJyri Sarha }
533103cd8bcSJyri Sarha 
tilcdc_unbind(struct device * dev)534103cd8bcSJyri Sarha static void tilcdc_unbind(struct device *dev)
535103cd8bcSJyri Sarha {
53620a98acbSJyri Sarha 	struct drm_device *ddev = dev_get_drvdata(dev);
53720a98acbSJyri Sarha 
53820a98acbSJyri Sarha 	/* Check if a subcomponent has already triggered the unloading. */
53920a98acbSJyri Sarha 	if (!ddev->dev_private)
54020a98acbSJyri Sarha 		return;
54120a98acbSJyri Sarha 
54269f03be1SDouglas Anderson 	tilcdc_fini(ddev);
54369f03be1SDouglas Anderson 	dev_set_drvdata(dev, NULL);
544103cd8bcSJyri Sarha }
545103cd8bcSJyri Sarha 
546103cd8bcSJyri Sarha static const struct component_master_ops tilcdc_comp_ops = {
547103cd8bcSJyri Sarha 	.bind = tilcdc_bind,
548103cd8bcSJyri Sarha 	.unbind = tilcdc_unbind,
549103cd8bcSJyri Sarha };
550103cd8bcSJyri Sarha 
tilcdc_pdev_probe(struct platform_device * pdev)55116ea975eSRob Clark static int tilcdc_pdev_probe(struct platform_device *pdev)
55216ea975eSRob Clark {
553103cd8bcSJyri Sarha 	struct component_match *match = NULL;
554103cd8bcSJyri Sarha 	int ret;
555103cd8bcSJyri Sarha 
55616ea975eSRob Clark 	/* bail out early if no DT data: */
55716ea975eSRob Clark 	if (!pdev->dev.of_node) {
55816ea975eSRob Clark 		dev_err(&pdev->dev, "device-tree data is missing\n");
55916ea975eSRob Clark 		return -ENXIO;
56016ea975eSRob Clark 	}
56116ea975eSRob Clark 
562103cd8bcSJyri Sarha 	ret = tilcdc_get_external_components(&pdev->dev, &match);
563103cd8bcSJyri Sarha 	if (ret < 0)
564103cd8bcSJyri Sarha 		return ret;
565103cd8bcSJyri Sarha 	else if (ret == 0)
566923310baSJyri Sarha 		return tilcdc_init(&tilcdc_driver, &pdev->dev);
567103cd8bcSJyri Sarha 	else
568103cd8bcSJyri Sarha 		return component_master_add_with_match(&pdev->dev,
569103cd8bcSJyri Sarha 						       &tilcdc_comp_ops,
570103cd8bcSJyri Sarha 						       match);
57116ea975eSRob Clark }
57216ea975eSRob Clark 
tilcdc_pdev_remove(struct platform_device * pdev)57316ea975eSRob Clark static int tilcdc_pdev_remove(struct platform_device *pdev)
57416ea975eSRob Clark {
57520a98acbSJyri Sarha 	int ret;
576103cd8bcSJyri Sarha 
57720a98acbSJyri Sarha 	ret = tilcdc_get_external_components(&pdev->dev, NULL);
57820a98acbSJyri Sarha 	if (ret < 0)
57920a98acbSJyri Sarha 		return ret;
58020a98acbSJyri Sarha 	else if (ret == 0)
581923310baSJyri Sarha 		tilcdc_fini(platform_get_drvdata(pdev));
58220a98acbSJyri Sarha 	else
58320a98acbSJyri Sarha 		component_master_del(&pdev->dev, &tilcdc_comp_ops);
58416ea975eSRob Clark 
58516ea975eSRob Clark 	return 0;
58616ea975eSRob Clark }
58716ea975eSRob Clark 
tilcdc_pdev_shutdown(struct platform_device * pdev)58869f03be1SDouglas Anderson static void tilcdc_pdev_shutdown(struct platform_device *pdev)
58969f03be1SDouglas Anderson {
59069f03be1SDouglas Anderson 	drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
59169f03be1SDouglas Anderson }
59269f03be1SDouglas Anderson 
593fef6d35dSXiang wangx static const struct of_device_id tilcdc_of_match[] = {
59416ea975eSRob Clark 		{ .compatible = "ti,am33xx-tilcdc", },
595507b72b2SBartosz Golaszewski 		{ .compatible = "ti,da850-tilcdc", },
59616ea975eSRob Clark 		{ },
59716ea975eSRob Clark };
59816ea975eSRob Clark MODULE_DEVICE_TABLE(of, tilcdc_of_match);
59916ea975eSRob Clark 
60016ea975eSRob Clark static struct platform_driver tilcdc_platform_driver = {
60116ea975eSRob Clark 	.probe      = tilcdc_pdev_probe,
60216ea975eSRob Clark 	.remove     = tilcdc_pdev_remove,
60369f03be1SDouglas Anderson 	.shutdown   = tilcdc_pdev_shutdown,
60416ea975eSRob Clark 	.driver     = {
60516ea975eSRob Clark 		.name   = "tilcdc",
6068d1c4e59SPaul Cercueil 		.pm     = pm_sleep_ptr(&tilcdc_pm_ops),
60716ea975eSRob Clark 		.of_match_table = tilcdc_of_match,
60816ea975eSRob Clark 	},
60916ea975eSRob Clark };
61016ea975eSRob Clark 
tilcdc_drm_init(void)61116ea975eSRob Clark static int __init tilcdc_drm_init(void)
61216ea975eSRob Clark {
6139b71ce89SJavier Martinez Canillas 	if (drm_firmware_drivers_only())
6149b71ce89SJavier Martinez Canillas 		return -ENODEV;
6159b71ce89SJavier Martinez Canillas 
61616ea975eSRob Clark 	DBG("init");
6170d4bbaf9SRob Clark 	tilcdc_panel_init();
61816ea975eSRob Clark 	return platform_driver_register(&tilcdc_platform_driver);
61916ea975eSRob Clark }
62016ea975eSRob Clark 
tilcdc_drm_fini(void)62116ea975eSRob Clark static void __exit tilcdc_drm_fini(void)
62216ea975eSRob Clark {
62316ea975eSRob Clark 	DBG("fini");
62416ea975eSRob Clark 	platform_driver_unregister(&tilcdc_platform_driver);
625eb565a2bSGuido Martínez 	tilcdc_panel_fini();
62616ea975eSRob Clark }
62716ea975eSRob Clark 
6282023d84dSGuido Martínez module_init(tilcdc_drm_init);
62916ea975eSRob Clark module_exit(tilcdc_drm_fini);
63016ea975eSRob Clark 
63116ea975eSRob Clark MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
63216ea975eSRob Clark MODULE_DESCRIPTION("TI LCD Controller DRM Driver");
63316ea975eSRob Clark MODULE_LICENSE("GPL");
634