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 int meson_enable_vblank(struct drm_device *dev, unsigned int crtc) 83 { 84 struct meson_drm *priv = dev->dev_private; 85 86 meson_venc_enable_vsync(priv); 87 88 return 0; 89 } 90 91 static void meson_disable_vblank(struct drm_device *dev, unsigned int crtc) 92 { 93 struct meson_drm *priv = dev->dev_private; 94 95 meson_venc_disable_vsync(priv); 96 } 97 98 static irqreturn_t meson_irq(int irq, void *arg) 99 { 100 struct drm_device *dev = arg; 101 struct meson_drm *priv = dev->dev_private; 102 103 (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG)); 104 105 meson_crtc_irq(priv); 106 107 return IRQ_HANDLED; 108 } 109 110 static const struct file_operations fops = { 111 .owner = THIS_MODULE, 112 .open = drm_open, 113 .release = drm_release, 114 .unlocked_ioctl = drm_ioctl, 115 #ifdef CONFIG_COMPAT 116 .compat_ioctl = drm_compat_ioctl, 117 #endif 118 .poll = drm_poll, 119 .read = drm_read, 120 .llseek = no_llseek, 121 .mmap = drm_gem_cma_mmap, 122 }; 123 124 static struct drm_driver meson_driver = { 125 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | 126 DRIVER_MODESET | DRIVER_PRIME | 127 DRIVER_ATOMIC, 128 129 /* Vblank */ 130 .enable_vblank = meson_enable_vblank, 131 .disable_vblank = meson_disable_vblank, 132 .get_vblank_counter = drm_vblank_no_hw_counter, 133 134 /* IRQ */ 135 .irq_handler = meson_irq, 136 137 /* PRIME Ops */ 138 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 139 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 140 .gem_prime_import = drm_gem_prime_import, 141 .gem_prime_export = drm_gem_prime_export, 142 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 143 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 144 .gem_prime_vmap = drm_gem_cma_prime_vmap, 145 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 146 .gem_prime_mmap = drm_gem_cma_prime_mmap, 147 148 /* GEM Ops */ 149 .dumb_create = drm_gem_cma_dumb_create, 150 .dumb_destroy = drm_gem_dumb_destroy, 151 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 152 .gem_free_object_unlocked = drm_gem_cma_free_object, 153 .gem_vm_ops = &drm_gem_cma_vm_ops, 154 155 /* Misc */ 156 .fops = &fops, 157 .name = DRIVER_NAME, 158 .desc = DRIVER_DESC, 159 .date = "20161109", 160 .major = 1, 161 .minor = 0, 162 }; 163 164 static bool meson_vpu_has_available_connectors(struct device *dev) 165 { 166 struct device_node *ep, *remote; 167 168 /* Parses each endpoint and check if remote exists */ 169 for_each_endpoint_of_node(dev->of_node, ep) { 170 /* If the endpoint node exists, consider it enabled */ 171 remote = of_graph_get_remote_port(ep); 172 if (remote) 173 return true; 174 } 175 176 return false; 177 } 178 179 static struct regmap_config meson_regmap_config = { 180 .reg_bits = 32, 181 .val_bits = 32, 182 .reg_stride = 4, 183 .max_register = 0x1000, 184 }; 185 186 static int meson_drv_probe(struct platform_device *pdev) 187 { 188 struct device *dev = &pdev->dev; 189 struct meson_drm *priv; 190 struct drm_device *drm; 191 struct resource *res; 192 void __iomem *regs; 193 int ret; 194 195 /* Checks if an output connector is available */ 196 if (!meson_vpu_has_available_connectors(dev)) { 197 dev_err(dev, "No output connector available\n"); 198 return -ENODEV; 199 } 200 201 drm = drm_dev_alloc(&meson_driver, dev); 202 if (IS_ERR(drm)) 203 return PTR_ERR(drm); 204 205 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 206 if (!priv) { 207 ret = -ENOMEM; 208 goto free_drm; 209 } 210 drm->dev_private = priv; 211 priv->drm = drm; 212 priv->dev = dev; 213 214 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu"); 215 regs = devm_ioremap_resource(dev, res); 216 if (IS_ERR(regs)) 217 return PTR_ERR(regs); 218 219 priv->io_base = regs; 220 221 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); 222 /* Simply ioremap since it may be a shared register zone */ 223 regs = devm_ioremap(dev, res->start, resource_size(res)); 224 if (!regs) 225 return -EADDRNOTAVAIL; 226 227 priv->hhi = devm_regmap_init_mmio(dev, regs, 228 &meson_regmap_config); 229 if (IS_ERR(priv->hhi)) { 230 dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); 231 return PTR_ERR(priv->hhi); 232 } 233 234 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); 235 /* Simply ioremap since it may be a shared register zone */ 236 regs = devm_ioremap(dev, res->start, resource_size(res)); 237 if (!regs) 238 return -EADDRNOTAVAIL; 239 240 priv->dmc = devm_regmap_init_mmio(dev, regs, 241 &meson_regmap_config); 242 if (IS_ERR(priv->dmc)) { 243 dev_err(&pdev->dev, "Couldn't create the DMC regmap\n"); 244 return PTR_ERR(priv->dmc); 245 } 246 247 priv->vsync_irq = platform_get_irq(pdev, 0); 248 249 drm_vblank_init(drm, 1); 250 drm_mode_config_init(drm); 251 252 /* Encoder Initialization */ 253 254 ret = meson_venc_cvbs_create(priv); 255 if (ret) 256 goto free_drm; 257 258 /* Hardware Initialization */ 259 260 meson_venc_init(priv); 261 meson_vpp_init(priv); 262 meson_viu_init(priv); 263 264 ret = meson_plane_create(priv); 265 if (ret) 266 goto free_drm; 267 268 ret = meson_crtc_create(priv); 269 if (ret) 270 goto free_drm; 271 272 ret = drm_irq_install(drm, priv->vsync_irq); 273 if (ret) 274 goto free_drm; 275 276 drm_mode_config_reset(drm); 277 drm->mode_config.max_width = 8192; 278 drm->mode_config.max_height = 8192; 279 drm->mode_config.funcs = &meson_mode_config_funcs; 280 281 priv->fbdev = drm_fbdev_cma_init(drm, 32, 282 drm->mode_config.num_connector); 283 if (IS_ERR(priv->fbdev)) { 284 ret = PTR_ERR(priv->fbdev); 285 goto free_drm; 286 } 287 288 drm_kms_helper_poll_init(drm); 289 290 platform_set_drvdata(pdev, priv); 291 292 ret = drm_dev_register(drm, 0); 293 if (ret) 294 goto free_drm; 295 296 return 0; 297 298 free_drm: 299 drm_dev_unref(drm); 300 301 return ret; 302 } 303 304 static int meson_drv_remove(struct platform_device *pdev) 305 { 306 struct drm_device *drm = dev_get_drvdata(&pdev->dev); 307 struct meson_drm *priv = drm->dev_private; 308 309 drm_dev_unregister(drm); 310 drm_kms_helper_poll_fini(drm); 311 drm_fbdev_cma_fini(priv->fbdev); 312 drm_mode_config_cleanup(drm); 313 drm_vblank_cleanup(drm); 314 drm_dev_unref(drm); 315 316 return 0; 317 } 318 319 static const struct of_device_id dt_match[] = { 320 { .compatible = "amlogic,meson-gxbb-vpu" }, 321 { .compatible = "amlogic,meson-gxl-vpu" }, 322 { .compatible = "amlogic,meson-gxm-vpu" }, 323 {} 324 }; 325 MODULE_DEVICE_TABLE(of, dt_match); 326 327 static struct platform_driver meson_drm_platform_driver = { 328 .probe = meson_drv_probe, 329 .remove = meson_drv_remove, 330 .driver = { 331 .name = "meson-drm", 332 .of_match_table = dt_match, 333 }, 334 }; 335 336 module_platform_driver(meson_drm_platform_driver); 337 338 MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>"); 339 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 340 MODULE_DESCRIPTION(DRIVER_DESC); 341 MODULE_LICENSE("GPL"); 342