Shawn Guo | 0a886f5 | 2016-09-22 19:52:39 +0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016 Linaro Ltd. |
| 3 | * Copyright 2016 ZTE Corporation. |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License version 2 as |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | */ |
| 10 | |
| 11 | #include <linux/clk.h> |
| 12 | #include <linux/component.h> |
| 13 | #include <linux/list.h> |
| 14 | #include <linux/module.h> |
| 15 | #include <linux/of_graph.h> |
| 16 | #include <linux/of_platform.h> |
| 17 | #include <linux/spinlock.h> |
| 18 | |
| 19 | #include <drm/drm_atomic_helper.h> |
| 20 | #include <drm/drm_crtc.h> |
| 21 | #include <drm/drm_crtc_helper.h> |
| 22 | #include <drm/drm_fb_cma_helper.h> |
| 23 | #include <drm/drm_fb_helper.h> |
| 24 | #include <drm/drm_gem_cma_helper.h> |
| 25 | #include <drm/drm_of.h> |
| 26 | #include <drm/drmP.h> |
| 27 | |
| 28 | #include "zx_drm_drv.h" |
| 29 | #include "zx_vou.h" |
| 30 | |
| 31 | struct zx_drm_private { |
| 32 | struct drm_fbdev_cma *fbdev; |
| 33 | }; |
| 34 | |
| 35 | static void zx_drm_fb_output_poll_changed(struct drm_device *drm) |
| 36 | { |
| 37 | struct zx_drm_private *priv = drm->dev_private; |
| 38 | |
| 39 | drm_fbdev_cma_hotplug_event(priv->fbdev); |
| 40 | } |
| 41 | |
| 42 | static const struct drm_mode_config_funcs zx_drm_mode_config_funcs = { |
| 43 | .fb_create = drm_fb_cma_create, |
| 44 | .output_poll_changed = zx_drm_fb_output_poll_changed, |
| 45 | .atomic_check = drm_atomic_helper_check, |
| 46 | .atomic_commit = drm_atomic_helper_commit, |
| 47 | }; |
| 48 | |
| 49 | static void zx_drm_lastclose(struct drm_device *drm) |
| 50 | { |
| 51 | struct zx_drm_private *priv = drm->dev_private; |
| 52 | |
| 53 | drm_fbdev_cma_restore_mode(priv->fbdev); |
| 54 | } |
| 55 | |
Daniel Vetter | d55f7e5 | 2017-03-08 15:12:56 +0100 | [diff] [blame] | 56 | DEFINE_DRM_GEM_CMA_FOPS(zx_drm_fops); |
Shawn Guo | 0a886f5 | 2016-09-22 19:52:39 +0800 | [diff] [blame] | 57 | |
| 58 | static struct drm_driver zx_drm_driver = { |
| 59 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | |
| 60 | DRIVER_ATOMIC, |
| 61 | .lastclose = zx_drm_lastclose, |
Shawn Guo | 0a886f5 | 2016-09-22 19:52:39 +0800 | [diff] [blame] | 62 | .gem_free_object = drm_gem_cma_free_object, |
| 63 | .gem_vm_ops = &drm_gem_cma_vm_ops, |
| 64 | .dumb_create = drm_gem_cma_dumb_create, |
| 65 | .dumb_map_offset = drm_gem_cma_dumb_map_offset, |
| 66 | .dumb_destroy = drm_gem_dumb_destroy, |
| 67 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
| 68 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
| 69 | .gem_prime_export = drm_gem_prime_export, |
| 70 | .gem_prime_import = drm_gem_prime_import, |
| 71 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, |
| 72 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, |
| 73 | .gem_prime_vmap = drm_gem_cma_prime_vmap, |
| 74 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, |
| 75 | .gem_prime_mmap = drm_gem_cma_prime_mmap, |
| 76 | .fops = &zx_drm_fops, |
| 77 | .name = "zx-vou", |
| 78 | .desc = "ZTE VOU Controller DRM", |
| 79 | .date = "20160811", |
| 80 | .major = 1, |
| 81 | .minor = 0, |
| 82 | }; |
| 83 | |
| 84 | static int zx_drm_bind(struct device *dev) |
| 85 | { |
| 86 | struct drm_device *drm; |
| 87 | struct zx_drm_private *priv; |
| 88 | int ret; |
| 89 | |
| 90 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
| 91 | if (!priv) |
| 92 | return -ENOMEM; |
| 93 | |
| 94 | drm = drm_dev_alloc(&zx_drm_driver, dev); |
Dan Carpenter | 72282a8 | 2016-11-15 12:53:01 +0300 | [diff] [blame] | 95 | if (IS_ERR(drm)) |
| 96 | return PTR_ERR(drm); |
Shawn Guo | 0a886f5 | 2016-09-22 19:52:39 +0800 | [diff] [blame] | 97 | |
| 98 | drm->dev_private = priv; |
| 99 | dev_set_drvdata(dev, drm); |
| 100 | |
| 101 | drm_mode_config_init(drm); |
| 102 | drm->mode_config.min_width = 16; |
| 103 | drm->mode_config.min_height = 16; |
| 104 | drm->mode_config.max_width = 4096; |
| 105 | drm->mode_config.max_height = 4096; |
| 106 | drm->mode_config.funcs = &zx_drm_mode_config_funcs; |
| 107 | |
| 108 | ret = component_bind_all(dev, drm); |
| 109 | if (ret) { |
| 110 | DRM_DEV_ERROR(dev, "failed to bind all components: %d\n", ret); |
| 111 | goto out_unregister; |
| 112 | } |
| 113 | |
| 114 | ret = drm_vblank_init(drm, drm->mode_config.num_crtc); |
| 115 | if (ret < 0) { |
| 116 | DRM_DEV_ERROR(dev, "failed to init vblank: %d\n", ret); |
| 117 | goto out_unbind; |
| 118 | } |
| 119 | |
| 120 | /* |
| 121 | * We will manage irq handler on our own. In this case, irq_enabled |
| 122 | * need to be true for using vblank core support. |
| 123 | */ |
| 124 | drm->irq_enabled = true; |
| 125 | |
| 126 | drm_mode_config_reset(drm); |
| 127 | drm_kms_helper_poll_init(drm); |
| 128 | |
Gabriel Krisman Bertazi | e4563f6 | 2017-02-02 14:26:40 -0200 | [diff] [blame] | 129 | priv->fbdev = drm_fbdev_cma_init(drm, 32, |
Shawn Guo | 0a886f5 | 2016-09-22 19:52:39 +0800 | [diff] [blame] | 130 | drm->mode_config.num_connector); |
| 131 | if (IS_ERR(priv->fbdev)) { |
| 132 | ret = PTR_ERR(priv->fbdev); |
| 133 | DRM_DEV_ERROR(dev, "failed to init cma fbdev: %d\n", ret); |
| 134 | priv->fbdev = NULL; |
| 135 | goto out_poll_fini; |
| 136 | } |
| 137 | |
| 138 | ret = drm_dev_register(drm, 0); |
| 139 | if (ret) |
| 140 | goto out_fbdev_fini; |
| 141 | |
| 142 | return 0; |
| 143 | |
| 144 | out_fbdev_fini: |
| 145 | if (priv->fbdev) { |
| 146 | drm_fbdev_cma_fini(priv->fbdev); |
| 147 | priv->fbdev = NULL; |
| 148 | } |
| 149 | out_poll_fini: |
| 150 | drm_kms_helper_poll_fini(drm); |
| 151 | drm_mode_config_cleanup(drm); |
| 152 | drm_vblank_cleanup(drm); |
| 153 | out_unbind: |
| 154 | component_unbind_all(dev, drm); |
| 155 | out_unregister: |
| 156 | dev_set_drvdata(dev, NULL); |
| 157 | drm->dev_private = NULL; |
| 158 | drm_dev_unref(drm); |
| 159 | return ret; |
| 160 | } |
| 161 | |
| 162 | static void zx_drm_unbind(struct device *dev) |
| 163 | { |
| 164 | struct drm_device *drm = dev_get_drvdata(dev); |
| 165 | struct zx_drm_private *priv = drm->dev_private; |
| 166 | |
| 167 | drm_dev_unregister(drm); |
| 168 | if (priv->fbdev) { |
| 169 | drm_fbdev_cma_fini(priv->fbdev); |
| 170 | priv->fbdev = NULL; |
| 171 | } |
| 172 | drm_kms_helper_poll_fini(drm); |
| 173 | drm_mode_config_cleanup(drm); |
| 174 | drm_vblank_cleanup(drm); |
| 175 | component_unbind_all(dev, drm); |
| 176 | dev_set_drvdata(dev, NULL); |
| 177 | drm->dev_private = NULL; |
| 178 | drm_dev_unref(drm); |
| 179 | } |
| 180 | |
| 181 | static const struct component_master_ops zx_drm_master_ops = { |
| 182 | .bind = zx_drm_bind, |
| 183 | .unbind = zx_drm_unbind, |
| 184 | }; |
| 185 | |
| 186 | static int compare_of(struct device *dev, void *data) |
| 187 | { |
| 188 | return dev->of_node == data; |
| 189 | } |
| 190 | |
| 191 | static int zx_drm_probe(struct platform_device *pdev) |
| 192 | { |
| 193 | struct device *dev = &pdev->dev; |
| 194 | struct device_node *parent = dev->of_node; |
| 195 | struct device_node *child; |
| 196 | struct component_match *match = NULL; |
| 197 | int ret; |
| 198 | |
Benjamin Gaignard | f3ca01d | 2017-05-29 17:45:51 +0200 | [diff] [blame] | 199 | ret = devm_of_platform_populate(dev); |
Shawn Guo | 0a886f5 | 2016-09-22 19:52:39 +0800 | [diff] [blame] | 200 | if (ret) |
| 201 | return ret; |
| 202 | |
| 203 | for_each_available_child_of_node(parent, child) { |
| 204 | component_match_add(dev, &match, compare_of, child); |
| 205 | of_node_put(child); |
| 206 | } |
| 207 | |
| 208 | return component_master_add_with_match(dev, &zx_drm_master_ops, match); |
| 209 | } |
| 210 | |
| 211 | static int zx_drm_remove(struct platform_device *pdev) |
| 212 | { |
| 213 | component_master_del(&pdev->dev, &zx_drm_master_ops); |
| 214 | return 0; |
| 215 | } |
| 216 | |
| 217 | static const struct of_device_id zx_drm_of_match[] = { |
| 218 | { .compatible = "zte,zx296718-vou", }, |
| 219 | { /* end */ }, |
| 220 | }; |
| 221 | MODULE_DEVICE_TABLE(of, zx_drm_of_match); |
| 222 | |
| 223 | static struct platform_driver zx_drm_platform_driver = { |
| 224 | .probe = zx_drm_probe, |
| 225 | .remove = zx_drm_remove, |
| 226 | .driver = { |
| 227 | .name = "zx-drm", |
| 228 | .of_match_table = zx_drm_of_match, |
| 229 | }, |
| 230 | }; |
| 231 | |
| 232 | static struct platform_driver *drivers[] = { |
| 233 | &zx_crtc_driver, |
| 234 | &zx_hdmi_driver, |
Shawn Guo | 098988c | 2017-01-19 22:28:38 +0800 | [diff] [blame] | 235 | &zx_tvenc_driver, |
Shawn Guo | 6911498 | 2017-04-11 19:30:26 +0800 | [diff] [blame] | 236 | &zx_vga_driver, |
Shawn Guo | 0a886f5 | 2016-09-22 19:52:39 +0800 | [diff] [blame] | 237 | &zx_drm_platform_driver, |
| 238 | }; |
| 239 | |
| 240 | static int zx_drm_init(void) |
| 241 | { |
| 242 | return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); |
| 243 | } |
| 244 | module_init(zx_drm_init); |
| 245 | |
| 246 | static void zx_drm_exit(void) |
| 247 | { |
| 248 | platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); |
| 249 | } |
| 250 | module_exit(zx_drm_exit); |
| 251 | |
| 252 | MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>"); |
| 253 | MODULE_DESCRIPTION("ZTE ZX VOU DRM driver"); |
| 254 | MODULE_LICENSE("GPL v2"); |