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>
19*8ab59da2SThomas 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
141b6366814SThomas Zimmermann 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);
178b6366814SThomas Zimmermann drm_atomic_helper_shutdown(dev);
179923310baSJyri Sarha tilcdc_irq_uninstall(dev);
18016ea975eSRob Clark 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);
187f8e7bce3SChristophe JAILLET
18816ea975eSRob Clark 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);
194ce7b700dSAishwarya Pant
19516ea975eSRob Clark drm_dev_put(dev);
19616ea975eSRob Clark }
19770a59dd8SDaniel Vetter
tilcdc_init(const struct drm_driver * ddrv,struct device * dev)19816ea975eSRob Clark static int tilcdc_init(const struct drm_driver *ddrv, struct device *dev)
199923310baSJyri Sarha {
200923310baSJyri Sarha struct drm_device *ddev;
201923310baSJyri Sarha struct platform_device *pdev = to_platform_device(dev);
20216ea975eSRob Clark struct device_node *node = dev->of_node;
20316ea975eSRob Clark struct tilcdc_drm_private *priv;
204dc28aa07SBenoit Parrot struct resource *res;
20516ea975eSRob Clark u32 bpp = 0;
20616ea975eSRob Clark int ret;
207923310baSJyri Sarha
2083366ba38SMarkus Elfring priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
20916ea975eSRob Clark if (!priv)
21016ea975eSRob Clark return -ENOMEM;
211923310baSJyri Sarha
212923310baSJyri Sarha ddev = drm_dev_alloc(ddrv, dev);
213923310baSJyri Sarha if (IS_ERR(ddev))
214923310baSJyri Sarha return PTR_ERR(ddev);
215923310baSJyri Sarha
2169e79e062SJyri Sarha ddev->dev_private = priv;
2179e79e062SJyri Sarha platform_set_drvdata(pdev, ddev);
21816ea975eSRob Clark drm_mode_config_init(ddev);
219103cd8bcSJyri Sarha
220923310baSJyri Sarha priv->is_componentized =
221103cd8bcSJyri Sarha tilcdc_get_external_components(dev, NULL) > 0;
22216ea975eSRob Clark
223b478e336SEzequiel Garcia priv->wq = alloc_ordered_workqueue("tilcdc", 0);
224b478e336SEzequiel Garcia if (!priv->wq) {
2259e79e062SJyri Sarha ret = -ENOMEM;
226b478e336SEzequiel Garcia goto init_failed;
22716ea975eSRob Clark }
22816ea975eSRob Clark
22916ea975eSRob Clark res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
230923310baSJyri Sarha if (!res) {
23116ea975eSRob Clark dev_err(dev, "failed to get memory resource\n");
2329e79e062SJyri Sarha ret = -EINVAL;
23316ea975eSRob Clark goto init_failed;
23416ea975eSRob Clark }
2354bdc0d67SChristoph Hellwig
23616ea975eSRob Clark priv->mmio = ioremap(res->start, resource_size(res));
237923310baSJyri Sarha if (!priv->mmio) {
23816ea975eSRob Clark dev_err(dev, "failed to ioremap\n");
2399e79e062SJyri Sarha ret = -ENOMEM;
24016ea975eSRob Clark goto init_failed;
24116ea975eSRob Clark }
242923310baSJyri Sarha
24316ea975eSRob Clark priv->clk = clk_get(dev, "fck");
244923310baSJyri Sarha if (IS_ERR(priv->clk)) {
24516ea975eSRob Clark dev_err(dev, "failed to get functional clock\n");
2469e79e062SJyri Sarha ret = -ENODEV;
24716ea975eSRob Clark goto init_failed;
24816ea975eSRob Clark }
249923310baSJyri Sarha
25016ea975eSRob Clark pm_runtime_enable(dev);
25116ea975eSRob Clark
252923310baSJyri Sarha /* Determine LCD IP Version */
253923310baSJyri Sarha pm_runtime_get_sync(dev);
25416ea975eSRob Clark 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;
262923310baSJyri Sarha default:
26316ea975eSRob Clark dev_warn(dev, "Unknown PID Reg value 0x%08x, "
264923310baSJyri Sarha "defaulting to LCD revision 1\n",
26516ea975eSRob Clark tilcdc_read(ddev, LCDC_PID_REG));
26616ea975eSRob Clark priv->rev = 1;
26716ea975eSRob Clark break;
26816ea975eSRob Clark }
269923310baSJyri Sarha
27016ea975eSRob Clark pm_runtime_put_sync(dev);
271bcc5a6f5SJyri Sarha
272bcc5a6f5SJyri Sarha if (priv->rev == 1) {
273bcc5a6f5SJyri Sarha DBG("Revision 1 LCDC supports only RGB565 format");
274bcc5a6f5SJyri Sarha priv->pixelformats = tilcdc_rev1_formats;
275c5665385SJyri Sarha priv->num_pixelformats = ARRAY_SIZE(tilcdc_rev1_formats);
276bcc5a6f5SJyri 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 =
285c5665385SJyri Sarha ARRAY_SIZE(tilcdc_crossed_formats);
286bcc5a6f5SJyri 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 =
291c5665385SJyri Sarha ARRAY_SIZE(tilcdc_straight_formats);
292bcc5a6f5SJyri 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 =
298c5665385SJyri Sarha ARRAY_SIZE(tilcdc_legacy_formats);
299bcc5a6f5SJyri Sarha bpp = 16; /* This is just a guess */
300bcc5a6f5SJyri Sarha }
301bcc5a6f5SJyri Sarha }
302882bceffSJyri 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);
3229963d36dSJyri Sarha
32316ea975eSRob Clark ret = tilcdc_crtc_create(ddev);
3249963d36dSJyri Sarha if (ret < 0) {
3259e79e062SJyri Sarha dev_err(dev, "failed to create crtc\n");
32616ea975eSRob Clark goto init_failed;
3279963d36dSJyri Sarha }
32816ea975eSRob Clark modeset_init(ddev);
329432973fdSJyri Sarha
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
340103cd8bcSJyri Sarha
341923310baSJyri Sarha if (priv->is_componentized) {
342103cd8bcSJyri Sarha ret = component_bind_all(dev, ddev);
3439e79e062SJyri Sarha if (ret < 0)
344103cd8bcSJyri Sarha goto init_failed;
345ec9eab09SJyri Sarha
346103cd8bcSJyri Sarha ret = tilcdc_add_component_encoder(ddev);
3479e79e062SJyri Sarha if (ret < 0)
348ec9eab09SJyri Sarha goto init_failed;
349ec9eab09SJyri Sarha } else {
350ec9eab09SJyri Sarha ret = tilcdc_attach_external_device(ddev);
351ec9eab09SJyri Sarha if (ret)
352103cd8bcSJyri Sarha goto init_failed;
353103cd8bcSJyri Sarha }
354ec9eab09SJyri Sarha
355ec9eab09SJyri Sarha if (!priv->external_connector &&
356923310baSJyri Sarha ((priv->num_encoders == 0) || (priv->num_connectors == 0))) {
357a132b5a5SSjoerd Simons dev_err(dev, "no encoders/connectors found\n");
3589e79e062SJyri Sarha ret = -EPROBE_DEFER;
359103cd8bcSJyri Sarha goto init_failed;
360103cd8bcSJyri Sarha }
361923310baSJyri Sarha
36216ea975eSRob Clark ret = drm_vblank_init(ddev, 1);
363923310baSJyri Sarha if (ret < 0) {
3649e79e062SJyri Sarha dev_err(dev, "failed to initialize vblank\n");
36516ea975eSRob Clark goto init_failed;
36616ea975eSRob Clark }
367b6366814SThomas Zimmermann
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
37316ea975eSRob Clark ret = tilcdc_irq_install(ddev, priv->irq);
374923310baSJyri Sarha if (ret < 0) {
3759e79e062SJyri Sarha dev_err(dev, "failed to install IRQ handler\n");
37616ea975eSRob Clark goto init_failed;
37716ea975eSRob Clark }
378923310baSJyri Sarha
379522a76f8SJyri Sarha drm_mode_config_reset(ddev);
380923310baSJyri Sarha
381923310baSJyri Sarha drm_kms_helper_poll_init(ddev);
382923310baSJyri Sarha
383923310baSJyri Sarha ret = drm_dev_register(ddev, 0);
3849e79e062SJyri Sarha if (ret)
385f5ef0762SThomas Zimmermann goto init_failed;
38616ea975eSRob Clark priv->is_registered = true;
38745cf8756SNoralf Trønnes
38816ea975eSRob Clark drm_fbdev_dma_setup(ddev, bpp);
38916ea975eSRob Clark return 0;
3909e79e062SJyri Sarha
3919e79e062SJyri Sarha init_failed:
392d0ec32caSJyri Sarha tilcdc_fini(ddev);
39316ea975eSRob Clark platform_set_drvdata(pdev, NULL);
39416ea975eSRob Clark
39516ea975eSRob Clark return ret;
396514d1a1fSJyri Sarha }
39716ea975eSRob Clark
39816ea975eSRob Clark #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: */
420f3a99946SJyri Sarha REG(2, false, LCDC_RAW_STAT_REG),
42116ea975eSRob Clark REG(2, false, LCDC_MASKED_STAT_REG),
42216ea975eSRob Clark 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),
42629ddd6e1SJyri Sarha #undef REG
42716ea975eSRob Clark };
42816ea975eSRob Clark
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 {
455b5c3714fSDaniel Vetter struct drm_info_node *node = (struct drm_info_node *) m->private;
456b5c3714fSDaniel Vetter struct drm_device *dev = node->minor->dev;
457b5c3714fSDaniel Vetter struct drm_printer p = drm_seq_file_printer(m);
45816ea975eSRob Clark drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p);
45916ea975eSRob Clark return 0;
46016ea975eSRob Clark }
46165b7da27SArnd Bergmann
46265b7da27SArnd Bergmann static struct drm_info_list tilcdc_debugfs_list[] = {
46316ea975eSRob Clark { "regs", tilcdc_regs_show, 0, NULL },
46416ea975eSRob Clark { "mm", tilcdc_mm_show, 0, NULL },
4657ce84471SWambui Karuga };
46616ea975eSRob Clark
tilcdc_debugfs_init(struct drm_minor * minor)46716ea975eSRob Clark static void tilcdc_debugfs_init(struct drm_minor *minor)
46816ea975eSRob Clark {
469f1c1a1f3SWambui Karuga struct tilcdc_module *mod;
47016ea975eSRob Clark
47116ea975eSRob Clark 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 }
4794a83c26aSDanilo Krummrich #endif
48016ea975eSRob Clark
48170a59dd8SDaniel Vetter DEFINE_DRM_GEM_DMA_FOPS(fops);
4820424fdafSDaniel Vetter
4834a83c26aSDanilo Krummrich static const struct drm_driver tilcdc_driver = {
48416ea975eSRob Clark .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
48516ea975eSRob Clark 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 {
5034fdce78aSSouptick Joarder struct drm_device *ddev = dev_get_drvdata(dev);
50416ea975eSRob Clark int ret = 0;
5054fdce78aSSouptick Joarder
50616ea975eSRob Clark ret = drm_mode_config_helper_suspend(ddev);
50785fd27f8SDarren Etheridge
50885fd27f8SDarren Etheridge /* Select sleep pin state */
50985fd27f8SDarren Etheridge pinctrl_pm_select_sleep_state(dev);
5104fdce78aSSouptick Joarder
51116ea975eSRob Clark 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);
517416a07fbSDave Gerlach
518416a07fbSDave Gerlach /* Select default pin state */
5194fdce78aSSouptick Joarder pinctrl_pm_select_default_state(dev);
52016ea975eSRob Clark return drm_mode_config_helper_resume(ddev);
52116ea975eSRob Clark }
52216ea975eSRob Clark
52316ea975eSRob Clark static DEFINE_SIMPLE_DEV_PM_OPS(tilcdc_pm_ops,
52416ea975eSRob Clark tilcdc_pm_suspend, tilcdc_pm_resume);
52516ea975eSRob Clark
52616ea975eSRob Clark /*
52716ea975eSRob Clark * Platform driver:
52816ea975eSRob Clark */
tilcdc_bind(struct device * dev)52916ea975eSRob Clark static int tilcdc_bind(struct device *dev)
530103cd8bcSJyri Sarha {
531103cd8bcSJyri Sarha return tilcdc_init(&tilcdc_driver, dev);
532923310baSJyri Sarha }
533103cd8bcSJyri Sarha
tilcdc_unbind(struct device * dev)534103cd8bcSJyri Sarha static void tilcdc_unbind(struct device *dev)
535103cd8bcSJyri Sarha {
536103cd8bcSJyri 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
54220a98acbSJyri Sarha tilcdc_fini(ddev);
543923310baSJyri Sarha 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)588fef6d35dSXiang wangx static void tilcdc_pdev_shutdown(struct platform_device *pdev)
58916ea975eSRob Clark {
590507b72b2SBartosz Golaszewski drm_atomic_helper_shutdown(platform_get_drvdata(pdev));
59116ea975eSRob Clark }
59216ea975eSRob Clark
59316ea975eSRob Clark static const struct of_device_id tilcdc_of_match[] = {
59416ea975eSRob Clark { .compatible = "ti,am33xx-tilcdc", },
59516ea975eSRob Clark { .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,
60316ea975eSRob Clark .shutdown = tilcdc_pdev_shutdown,
60416ea975eSRob Clark .driver = {
60516ea975eSRob Clark .name = "tilcdc",
60616ea975eSRob Clark .pm = pm_sleep_ptr(&tilcdc_pm_ops),
6079b71ce89SJavier Martinez Canillas .of_match_table = tilcdc_of_match,
6089b71ce89SJavier Martinez Canillas },
6099b71ce89SJavier Martinez Canillas };
61016ea975eSRob Clark
tilcdc_drm_init(void)6110d4bbaf9SRob Clark static int __init tilcdc_drm_init(void)
61216ea975eSRob Clark {
61316ea975eSRob Clark if (drm_firmware_drivers_only())
61416ea975eSRob Clark return -ENODEV;
61516ea975eSRob Clark
61616ea975eSRob Clark DBG("init");
61716ea975eSRob Clark tilcdc_panel_init();
61816ea975eSRob Clark return platform_driver_register(&tilcdc_platform_driver);
619eb565a2bSGuido Martínez }
62016ea975eSRob Clark
tilcdc_drm_fini(void)62116ea975eSRob Clark static void __exit tilcdc_drm_fini(void)
6222023d84dSGuido Martínez {
62316ea975eSRob Clark DBG("fini");
62416ea975eSRob Clark platform_driver_unregister(&tilcdc_platform_driver);
62516ea975eSRob Clark tilcdc_panel_fini();
62616ea975eSRob Clark }
62716ea975eSRob Clark
628 module_init(tilcdc_drm_init);
629 module_exit(tilcdc_drm_fini);
630
631 MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
632 MODULE_DESCRIPTION("TI LCD Controller DRM Driver");
633 MODULE_LICENSE("GPL");
634