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