1 /* 2 * Copyright (C) 2016 BayLibre, SAS 3 * Author: Neil Armstrong <narmstrong@baylibre.com> 4 * Copyright (C) 2014 Endless Mobile 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of the 9 * License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, see <http://www.gnu.org/licenses/>. 18 * 19 * Written by: 20 * Jasper St. Pierre <jstpierre@mecheye.net> 21 */ 22 23 #include <linux/kernel.h> 24 #include <linux/module.h> 25 #include <linux/mutex.h> 26 #include <linux/platform_device.h> 27 #include <linux/of_graph.h> 28 29 #include <drm/drmP.h> 30 #include <drm/drm_atomic.h> 31 #include <drm/drm_atomic_helper.h> 32 #include <drm/drm_flip_work.h> 33 #include <drm/drm_crtc_helper.h> 34 #include <drm/drm_plane_helper.h> 35 #include <drm/drm_gem_cma_helper.h> 36 #include <drm/drm_fb_cma_helper.h> 37 #include <drm/drm_rect.h> 38 #include <drm/drm_fb_helper.h> 39 40 #include "meson_drv.h" 41 #include "meson_plane.h" 42 #include "meson_crtc.h" 43 #include "meson_venc_cvbs.h" 44 45 #include "meson_vpp.h" 46 #include "meson_viu.h" 47 #include "meson_venc.h" 48 #include "meson_canvas.h" 49 #include "meson_registers.h" 50 51 #define DRIVER_NAME "meson" 52 #define DRIVER_DESC "Amlogic Meson DRM driver" 53 54 /* 55 * Video Processing Unit 56 * 57 * VPU Handles the Global Video Processing, it includes management of the 58 * clocks gates, blocks reset lines and power domains. 59 * 60 * What is missing : 61 * - Full reset of entire video processing HW blocks 62 * - Scaling and setup of the VPU clock 63 * - Bus clock gates 64 * - Powering up video processing HW blocks 65 * - Powering Up HDMI controller and PHY 66 */ 67 68 static void meson_fb_output_poll_changed(struct drm_device *dev) 69 { 70 struct meson_drm *priv = dev->dev_private; 71 72 drm_fbdev_cma_hotplug_event(priv->fbdev); 73 } 74 75 static const struct drm_mode_config_funcs meson_mode_config_funcs = { 76 .output_poll_changed = meson_fb_output_poll_changed, 77 .atomic_check = drm_atomic_helper_check, 78 .atomic_commit = drm_atomic_helper_commit, 79 .fb_create = drm_fb_cma_create, 80 }; 81 82 static irqreturn_t meson_irq(int irq, void *arg) 83 { 84 struct drm_device *dev = arg; 85 struct meson_drm *priv = dev->dev_private; 86 87 (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG)); 88 89 meson_crtc_irq(priv); 90 91 return IRQ_HANDLED; 92 } 93 94 DEFINE_DRM_GEM_CMA_FOPS(fops); 95 96 static struct drm_driver meson_driver = { 97 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | 98 DRIVER_MODESET | DRIVER_PRIME | 99 DRIVER_ATOMIC, 100 101 /* IRQ */ 102 .irq_handler = meson_irq, 103 104 /* PRIME Ops */ 105 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 106 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 107 .gem_prime_import = drm_gem_prime_import, 108 .gem_prime_export = drm_gem_prime_export, 109 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 110 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 111 .gem_prime_vmap = drm_gem_cma_prime_vmap, 112 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 113 .gem_prime_mmap = drm_gem_cma_prime_mmap, 114 115 /* GEM Ops */ 116 .dumb_create = drm_gem_cma_dumb_create, 117 .dumb_destroy = drm_gem_dumb_destroy, 118 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 119 .gem_free_object_unlocked = drm_gem_cma_free_object, 120 .gem_vm_ops = &drm_gem_cma_vm_ops, 121 122 /* Misc */ 123 .fops = &fops, 124 .name = DRIVER_NAME, 125 .desc = DRIVER_DESC, 126 .date = "20161109", 127 .major = 1, 128 .minor = 0, 129 }; 130 131 static bool meson_vpu_has_available_connectors(struct device *dev) 132 { 133 struct device_node *ep, *remote; 134 135 /* Parses each endpoint and check if remote exists */ 136 for_each_endpoint_of_node(dev->of_node, ep) { 137 /* If the endpoint node exists, consider it enabled */ 138 remote = of_graph_get_remote_port(ep); 139 if (remote) 140 return true; 141 } 142 143 return false; 144 } 145 146 static struct regmap_config meson_regmap_config = { 147 .reg_bits = 32, 148 .val_bits = 32, 149 .reg_stride = 4, 150 .max_register = 0x1000, 151 }; 152 153 static int meson_drv_probe(struct platform_device *pdev) 154 { 155 struct device *dev = &pdev->dev; 156 struct meson_drm *priv; 157 struct drm_device *drm; 158 struct resource *res; 159 void __iomem *regs; 160 int ret; 161 162 /* Checks if an output connector is available */ 163 if (!meson_vpu_has_available_connectors(dev)) { 164 dev_err(dev, "No output connector available\n"); 165 return -ENODEV; 166 } 167 168 drm = drm_dev_alloc(&meson_driver, dev); 169 if (IS_ERR(drm)) 170 return PTR_ERR(drm); 171 172 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 173 if (!priv) { 174 ret = -ENOMEM; 175 goto free_drm; 176 } 177 drm->dev_private = priv; 178 priv->drm = drm; 179 priv->dev = dev; 180 181 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu"); 182 regs = devm_ioremap_resource(dev, res); 183 if (IS_ERR(regs)) 184 return PTR_ERR(regs); 185 186 priv->io_base = regs; 187 188 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); 189 /* Simply ioremap since it may be a shared register zone */ 190 regs = devm_ioremap(dev, res->start, resource_size(res)); 191 if (!regs) 192 return -EADDRNOTAVAIL; 193 194 priv->hhi = devm_regmap_init_mmio(dev, regs, 195 &meson_regmap_config); 196 if (IS_ERR(priv->hhi)) { 197 dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); 198 return PTR_ERR(priv->hhi); 199 } 200 201 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); 202 /* Simply ioremap since it may be a shared register zone */ 203 regs = devm_ioremap(dev, res->start, resource_size(res)); 204 if (!regs) 205 return -EADDRNOTAVAIL; 206 207 priv->dmc = devm_regmap_init_mmio(dev, regs, 208 &meson_regmap_config); 209 if (IS_ERR(priv->dmc)) { 210 dev_err(&pdev->dev, "Couldn't create the DMC regmap\n"); 211 return PTR_ERR(priv->dmc); 212 } 213 214 priv->vsync_irq = platform_get_irq(pdev, 0); 215 216 drm_vblank_init(drm, 1); 217 drm_mode_config_init(drm); 218 219 /* Encoder Initialization */ 220 221 ret = meson_venc_cvbs_create(priv); 222 if (ret) 223 goto free_drm; 224 225 /* Hardware Initialization */ 226 227 meson_venc_init(priv); 228 meson_vpp_init(priv); 229 meson_viu_init(priv); 230 231 ret = meson_plane_create(priv); 232 if (ret) 233 goto free_drm; 234 235 ret = meson_crtc_create(priv); 236 if (ret) 237 goto free_drm; 238 239 ret = drm_irq_install(drm, priv->vsync_irq); 240 if (ret) 241 goto free_drm; 242 243 drm_mode_config_reset(drm); 244 drm->mode_config.max_width = 8192; 245 drm->mode_config.max_height = 8192; 246 drm->mode_config.funcs = &meson_mode_config_funcs; 247 248 priv->fbdev = drm_fbdev_cma_init(drm, 32, 249 drm->mode_config.num_connector); 250 if (IS_ERR(priv->fbdev)) { 251 ret = PTR_ERR(priv->fbdev); 252 goto free_drm; 253 } 254 255 drm_kms_helper_poll_init(drm); 256 257 platform_set_drvdata(pdev, priv); 258 259 ret = drm_dev_register(drm, 0); 260 if (ret) 261 goto free_drm; 262 263 return 0; 264 265 free_drm: 266 drm_dev_unref(drm); 267 268 return ret; 269 } 270 271 static int meson_drv_remove(struct platform_device *pdev) 272 { 273 struct drm_device *drm = dev_get_drvdata(&pdev->dev); 274 struct meson_drm *priv = drm->dev_private; 275 276 drm_dev_unregister(drm); 277 drm_kms_helper_poll_fini(drm); 278 drm_fbdev_cma_fini(priv->fbdev); 279 drm_mode_config_cleanup(drm); 280 drm_vblank_cleanup(drm); 281 drm_dev_unref(drm); 282 283 return 0; 284 } 285 286 static const struct of_device_id dt_match[] = { 287 { .compatible = "amlogic,meson-gxbb-vpu" }, 288 { .compatible = "amlogic,meson-gxl-vpu" }, 289 { .compatible = "amlogic,meson-gxm-vpu" }, 290 {} 291 }; 292 MODULE_DEVICE_TABLE(of, dt_match); 293 294 static struct platform_driver meson_drm_platform_driver = { 295 .probe = meson_drv_probe, 296 .remove = meson_drv_remove, 297 .driver = { 298 .name = "meson-drm", 299 .of_match_table = dt_match, 300 }, 301 }; 302 303 module_platform_driver(meson_drm_platform_driver); 304 305 MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>"); 306 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 307 MODULE_DESCRIPTION(DRIVER_DESC); 308 MODULE_LICENSE("GPL"); 309