1efeeaefeSPaul Kocialkowski // SPDX-License-Identifier: GPL-2.0+ 2efeeaefeSPaul Kocialkowski /* 3efeeaefeSPaul Kocialkowski * Copyright (C) 2019-2022 Bootlin 4efeeaefeSPaul Kocialkowski * Author: Paul Kocialkowski <paul.kocialkowski@bootlin.com> 5efeeaefeSPaul Kocialkowski */ 6efeeaefeSPaul Kocialkowski 7efeeaefeSPaul Kocialkowski #include <linux/bitfield.h> 8efeeaefeSPaul Kocialkowski #include <linux/clk.h> 9efeeaefeSPaul Kocialkowski #include <linux/mfd/syscon.h> 10efeeaefeSPaul Kocialkowski #include <linux/module.h> 11efeeaefeSPaul Kocialkowski #include <linux/of.h> 12efeeaefeSPaul Kocialkowski #include <linux/of_address.h> 13efeeaefeSPaul Kocialkowski #include <linux/of_device.h> 14efeeaefeSPaul Kocialkowski #include <linux/of_reserved_mem.h> 15efeeaefeSPaul Kocialkowski #include <linux/regmap.h> 16efeeaefeSPaul Kocialkowski #include <linux/types.h> 17efeeaefeSPaul Kocialkowski 18efeeaefeSPaul Kocialkowski #include <drm/drm_atomic_helper.h> 19efeeaefeSPaul Kocialkowski #include <drm/drm_drv.h> 20efeeaefeSPaul Kocialkowski #include <drm/drm_fb_helper.h> 21*4a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h> 22efeeaefeSPaul Kocialkowski #include <drm/drm_print.h> 23efeeaefeSPaul Kocialkowski 24efeeaefeSPaul Kocialkowski #include "logicvc_crtc.h" 25efeeaefeSPaul Kocialkowski #include "logicvc_drm.h" 26efeeaefeSPaul Kocialkowski #include "logicvc_interface.h" 27efeeaefeSPaul Kocialkowski #include "logicvc_mode.h" 28efeeaefeSPaul Kocialkowski #include "logicvc_layer.h" 29efeeaefeSPaul Kocialkowski #include "logicvc_of.h" 30efeeaefeSPaul Kocialkowski #include "logicvc_regs.h" 31efeeaefeSPaul Kocialkowski 32*4a83c26aSDanilo Krummrich DEFINE_DRM_GEM_DMA_FOPS(logicvc_drm_fops); 33efeeaefeSPaul Kocialkowski 34*4a83c26aSDanilo Krummrich static int logicvc_drm_gem_dma_dumb_create(struct drm_file *file_priv, 35efeeaefeSPaul Kocialkowski struct drm_device *drm_dev, 36efeeaefeSPaul Kocialkowski struct drm_mode_create_dumb *args) 37efeeaefeSPaul Kocialkowski { 38efeeaefeSPaul Kocialkowski struct logicvc_drm *logicvc = logicvc_drm(drm_dev); 39efeeaefeSPaul Kocialkowski 40efeeaefeSPaul Kocialkowski /* Stride is always fixed to its configuration value. */ 41efeeaefeSPaul Kocialkowski args->pitch = logicvc->config.row_stride * DIV_ROUND_UP(args->bpp, 8); 42efeeaefeSPaul Kocialkowski 43*4a83c26aSDanilo Krummrich return drm_gem_dma_dumb_create_internal(file_priv, drm_dev, args); 44efeeaefeSPaul Kocialkowski } 45efeeaefeSPaul Kocialkowski 46efeeaefeSPaul Kocialkowski static struct drm_driver logicvc_drm_driver = { 47efeeaefeSPaul Kocialkowski .driver_features = DRIVER_GEM | DRIVER_MODESET | 48efeeaefeSPaul Kocialkowski DRIVER_ATOMIC, 49efeeaefeSPaul Kocialkowski 50efeeaefeSPaul Kocialkowski .fops = &logicvc_drm_fops, 51efeeaefeSPaul Kocialkowski .name = "logicvc-drm", 52efeeaefeSPaul Kocialkowski .desc = "Xylon LogiCVC DRM driver", 53efeeaefeSPaul Kocialkowski .date = "20200403", 54efeeaefeSPaul Kocialkowski .major = 1, 55efeeaefeSPaul Kocialkowski .minor = 0, 56efeeaefeSPaul Kocialkowski 57*4a83c26aSDanilo Krummrich DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(logicvc_drm_gem_dma_dumb_create), 58efeeaefeSPaul Kocialkowski }; 59efeeaefeSPaul Kocialkowski 60efeeaefeSPaul Kocialkowski static struct regmap_config logicvc_drm_regmap_config = { 61efeeaefeSPaul Kocialkowski .reg_bits = 32, 62efeeaefeSPaul Kocialkowski .val_bits = 32, 63efeeaefeSPaul Kocialkowski .reg_stride = 4, 64efeeaefeSPaul Kocialkowski .name = "logicvc-drm", 65efeeaefeSPaul Kocialkowski }; 66efeeaefeSPaul Kocialkowski 67efeeaefeSPaul Kocialkowski static irqreturn_t logicvc_drm_irq_handler(int irq, void *data) 68efeeaefeSPaul Kocialkowski { 69efeeaefeSPaul Kocialkowski struct logicvc_drm *logicvc = data; 70efeeaefeSPaul Kocialkowski irqreturn_t ret = IRQ_NONE; 71efeeaefeSPaul Kocialkowski u32 stat = 0; 72efeeaefeSPaul Kocialkowski 73efeeaefeSPaul Kocialkowski /* Get pending interrupt sources. */ 74efeeaefeSPaul Kocialkowski regmap_read(logicvc->regmap, LOGICVC_INT_STAT_REG, &stat); 75efeeaefeSPaul Kocialkowski 76efeeaefeSPaul Kocialkowski /* Clear all pending interrupt sources. */ 77efeeaefeSPaul Kocialkowski regmap_write(logicvc->regmap, LOGICVC_INT_STAT_REG, stat); 78efeeaefeSPaul Kocialkowski 79efeeaefeSPaul Kocialkowski if (stat & LOGICVC_INT_STAT_V_SYNC) { 80efeeaefeSPaul Kocialkowski logicvc_crtc_vblank_handler(logicvc); 81efeeaefeSPaul Kocialkowski ret = IRQ_HANDLED; 82efeeaefeSPaul Kocialkowski } 83efeeaefeSPaul Kocialkowski 84efeeaefeSPaul Kocialkowski return ret; 85efeeaefeSPaul Kocialkowski } 86efeeaefeSPaul Kocialkowski 87efeeaefeSPaul Kocialkowski static int logicvc_drm_config_parse(struct logicvc_drm *logicvc) 88efeeaefeSPaul Kocialkowski { 89efeeaefeSPaul Kocialkowski struct drm_device *drm_dev = &logicvc->drm_dev; 90efeeaefeSPaul Kocialkowski struct device *dev = drm_dev->dev; 91efeeaefeSPaul Kocialkowski struct device_node *of_node = dev->of_node; 92efeeaefeSPaul Kocialkowski struct logicvc_drm_config *config = &logicvc->config; 93efeeaefeSPaul Kocialkowski struct device_node *layers_node; 94efeeaefeSPaul Kocialkowski int ret; 95efeeaefeSPaul Kocialkowski 96efeeaefeSPaul Kocialkowski logicvc_of_property_parse_bool(of_node, LOGICVC_OF_PROPERTY_DITHERING, 97efeeaefeSPaul Kocialkowski &config->dithering); 98efeeaefeSPaul Kocialkowski logicvc_of_property_parse_bool(of_node, 99efeeaefeSPaul Kocialkowski LOGICVC_OF_PROPERTY_BACKGROUND_LAYER, 100efeeaefeSPaul Kocialkowski &config->background_layer); 101efeeaefeSPaul Kocialkowski logicvc_of_property_parse_bool(of_node, 102efeeaefeSPaul Kocialkowski LOGICVC_OF_PROPERTY_LAYERS_CONFIGURABLE, 103efeeaefeSPaul Kocialkowski &config->layers_configurable); 104efeeaefeSPaul Kocialkowski 105efeeaefeSPaul Kocialkowski ret = logicvc_of_property_parse_u32(of_node, 106efeeaefeSPaul Kocialkowski LOGICVC_OF_PROPERTY_DISPLAY_INTERFACE, 107efeeaefeSPaul Kocialkowski &config->display_interface); 108efeeaefeSPaul Kocialkowski if (ret) 109efeeaefeSPaul Kocialkowski return ret; 110efeeaefeSPaul Kocialkowski 111efeeaefeSPaul Kocialkowski ret = logicvc_of_property_parse_u32(of_node, 112efeeaefeSPaul Kocialkowski LOGICVC_OF_PROPERTY_DISPLAY_COLORSPACE, 113efeeaefeSPaul Kocialkowski &config->display_colorspace); 114efeeaefeSPaul Kocialkowski if (ret) 115efeeaefeSPaul Kocialkowski return ret; 116efeeaefeSPaul Kocialkowski 117efeeaefeSPaul Kocialkowski ret = logicvc_of_property_parse_u32(of_node, 118efeeaefeSPaul Kocialkowski LOGICVC_OF_PROPERTY_DISPLAY_DEPTH, 119efeeaefeSPaul Kocialkowski &config->display_depth); 120efeeaefeSPaul Kocialkowski if (ret) 121efeeaefeSPaul Kocialkowski return ret; 122efeeaefeSPaul Kocialkowski 123efeeaefeSPaul Kocialkowski ret = logicvc_of_property_parse_u32(of_node, 124efeeaefeSPaul Kocialkowski LOGICVC_OF_PROPERTY_ROW_STRIDE, 125efeeaefeSPaul Kocialkowski &config->row_stride); 126efeeaefeSPaul Kocialkowski if (ret) 127efeeaefeSPaul Kocialkowski return ret; 128efeeaefeSPaul Kocialkowski 129efeeaefeSPaul Kocialkowski layers_node = of_get_child_by_name(of_node, "layers"); 130efeeaefeSPaul Kocialkowski if (!layers_node) { 131efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Missing non-optional layers node\n"); 132efeeaefeSPaul Kocialkowski return -EINVAL; 133efeeaefeSPaul Kocialkowski } 134efeeaefeSPaul Kocialkowski 135efeeaefeSPaul Kocialkowski config->layers_count = of_get_child_count(layers_node); 136efeeaefeSPaul Kocialkowski if (!config->layers_count) { 137efeeaefeSPaul Kocialkowski drm_err(drm_dev, 138efeeaefeSPaul Kocialkowski "Missing a non-optional layers children node\n"); 139efeeaefeSPaul Kocialkowski return -EINVAL; 140efeeaefeSPaul Kocialkowski } 141efeeaefeSPaul Kocialkowski 142efeeaefeSPaul Kocialkowski return 0; 143efeeaefeSPaul Kocialkowski } 144efeeaefeSPaul Kocialkowski 145efeeaefeSPaul Kocialkowski static int logicvc_clocks_prepare(struct logicvc_drm *logicvc) 146efeeaefeSPaul Kocialkowski { 147efeeaefeSPaul Kocialkowski struct drm_device *drm_dev = &logicvc->drm_dev; 148efeeaefeSPaul Kocialkowski struct device *dev = drm_dev->dev; 149efeeaefeSPaul Kocialkowski 150efeeaefeSPaul Kocialkowski struct { 151efeeaefeSPaul Kocialkowski struct clk **clk; 152efeeaefeSPaul Kocialkowski char *name; 153efeeaefeSPaul Kocialkowski bool optional; 154efeeaefeSPaul Kocialkowski } clocks_map[] = { 155efeeaefeSPaul Kocialkowski { 156efeeaefeSPaul Kocialkowski .clk = &logicvc->vclk, 157efeeaefeSPaul Kocialkowski .name = "vclk", 158efeeaefeSPaul Kocialkowski .optional = false, 159efeeaefeSPaul Kocialkowski }, 160efeeaefeSPaul Kocialkowski { 161efeeaefeSPaul Kocialkowski .clk = &logicvc->vclk2, 162efeeaefeSPaul Kocialkowski .name = "vclk2", 163efeeaefeSPaul Kocialkowski .optional = true, 164efeeaefeSPaul Kocialkowski }, 165efeeaefeSPaul Kocialkowski { 166efeeaefeSPaul Kocialkowski .clk = &logicvc->lvdsclk, 167efeeaefeSPaul Kocialkowski .name = "lvdsclk", 168efeeaefeSPaul Kocialkowski .optional = true, 169efeeaefeSPaul Kocialkowski }, 170efeeaefeSPaul Kocialkowski { 171efeeaefeSPaul Kocialkowski .clk = &logicvc->lvdsclkn, 172efeeaefeSPaul Kocialkowski .name = "lvdsclkn", 173efeeaefeSPaul Kocialkowski .optional = true, 174efeeaefeSPaul Kocialkowski }, 175efeeaefeSPaul Kocialkowski }; 176efeeaefeSPaul Kocialkowski unsigned int i; 177efeeaefeSPaul Kocialkowski int ret; 178efeeaefeSPaul Kocialkowski 179efeeaefeSPaul Kocialkowski for (i = 0; i < ARRAY_SIZE(clocks_map); i++) { 180efeeaefeSPaul Kocialkowski struct clk *clk; 181efeeaefeSPaul Kocialkowski 182efeeaefeSPaul Kocialkowski clk = devm_clk_get(dev, clocks_map[i].name); 183efeeaefeSPaul Kocialkowski if (IS_ERR(clk)) { 184efeeaefeSPaul Kocialkowski if (PTR_ERR(clk) == -ENOENT && clocks_map[i].optional) 185efeeaefeSPaul Kocialkowski continue; 186efeeaefeSPaul Kocialkowski 187efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Missing non-optional clock %s\n", 188efeeaefeSPaul Kocialkowski clocks_map[i].name); 189efeeaefeSPaul Kocialkowski 190efeeaefeSPaul Kocialkowski ret = PTR_ERR(clk); 191efeeaefeSPaul Kocialkowski goto error; 192efeeaefeSPaul Kocialkowski } 193efeeaefeSPaul Kocialkowski 194efeeaefeSPaul Kocialkowski ret = clk_prepare_enable(clk); 195efeeaefeSPaul Kocialkowski if (ret) { 196efeeaefeSPaul Kocialkowski drm_err(drm_dev, 197efeeaefeSPaul Kocialkowski "Failed to prepare and enable clock %s\n", 198efeeaefeSPaul Kocialkowski clocks_map[i].name); 199efeeaefeSPaul Kocialkowski goto error; 200efeeaefeSPaul Kocialkowski } 201efeeaefeSPaul Kocialkowski 202efeeaefeSPaul Kocialkowski *clocks_map[i].clk = clk; 203efeeaefeSPaul Kocialkowski } 204efeeaefeSPaul Kocialkowski 205efeeaefeSPaul Kocialkowski return 0; 206efeeaefeSPaul Kocialkowski 207efeeaefeSPaul Kocialkowski error: 208efeeaefeSPaul Kocialkowski for (i = 0; i < ARRAY_SIZE(clocks_map); i++) { 209efeeaefeSPaul Kocialkowski if (!*clocks_map[i].clk) 210efeeaefeSPaul Kocialkowski continue; 211efeeaefeSPaul Kocialkowski 212efeeaefeSPaul Kocialkowski clk_disable_unprepare(*clocks_map[i].clk); 213efeeaefeSPaul Kocialkowski *clocks_map[i].clk = NULL; 214efeeaefeSPaul Kocialkowski } 215efeeaefeSPaul Kocialkowski 216efeeaefeSPaul Kocialkowski return ret; 217efeeaefeSPaul Kocialkowski } 218efeeaefeSPaul Kocialkowski 219efeeaefeSPaul Kocialkowski static int logicvc_clocks_unprepare(struct logicvc_drm *logicvc) 220efeeaefeSPaul Kocialkowski { 221efeeaefeSPaul Kocialkowski struct clk **clocks[] = { 222efeeaefeSPaul Kocialkowski &logicvc->vclk, 223efeeaefeSPaul Kocialkowski &logicvc->vclk2, 224efeeaefeSPaul Kocialkowski &logicvc->lvdsclk, 225efeeaefeSPaul Kocialkowski &logicvc->lvdsclkn, 226efeeaefeSPaul Kocialkowski }; 227efeeaefeSPaul Kocialkowski unsigned int i; 228efeeaefeSPaul Kocialkowski 229efeeaefeSPaul Kocialkowski for (i = 0; i < ARRAY_SIZE(clocks); i++) { 230efeeaefeSPaul Kocialkowski if (!*clocks[i]) 231efeeaefeSPaul Kocialkowski continue; 232efeeaefeSPaul Kocialkowski 233efeeaefeSPaul Kocialkowski clk_disable_unprepare(*clocks[i]); 234efeeaefeSPaul Kocialkowski *clocks[i] = NULL; 235efeeaefeSPaul Kocialkowski } 236efeeaefeSPaul Kocialkowski 237efeeaefeSPaul Kocialkowski return 0; 238efeeaefeSPaul Kocialkowski } 239efeeaefeSPaul Kocialkowski 240efeeaefeSPaul Kocialkowski static const struct logicvc_drm_caps logicvc_drm_caps[] = { 241efeeaefeSPaul Kocialkowski { 242efeeaefeSPaul Kocialkowski .major = 3, 243efeeaefeSPaul Kocialkowski .layer_address = false, 244efeeaefeSPaul Kocialkowski }, 245efeeaefeSPaul Kocialkowski { 246efeeaefeSPaul Kocialkowski .major = 4, 247efeeaefeSPaul Kocialkowski .layer_address = true, 248efeeaefeSPaul Kocialkowski }, 249efeeaefeSPaul Kocialkowski { 250efeeaefeSPaul Kocialkowski .major = 5, 251efeeaefeSPaul Kocialkowski .layer_address = true, 252efeeaefeSPaul Kocialkowski }, 253efeeaefeSPaul Kocialkowski }; 254efeeaefeSPaul Kocialkowski 255efeeaefeSPaul Kocialkowski static const struct logicvc_drm_caps * 256efeeaefeSPaul Kocialkowski logicvc_drm_caps_match(struct logicvc_drm *logicvc) 257efeeaefeSPaul Kocialkowski { 258efeeaefeSPaul Kocialkowski struct drm_device *drm_dev = &logicvc->drm_dev; 259efeeaefeSPaul Kocialkowski const struct logicvc_drm_caps *caps = NULL; 260efeeaefeSPaul Kocialkowski unsigned int major, minor; 261efeeaefeSPaul Kocialkowski char level; 262efeeaefeSPaul Kocialkowski unsigned int i; 263efeeaefeSPaul Kocialkowski u32 version; 264efeeaefeSPaul Kocialkowski 265efeeaefeSPaul Kocialkowski regmap_read(logicvc->regmap, LOGICVC_IP_VERSION_REG, &version); 266efeeaefeSPaul Kocialkowski 267efeeaefeSPaul Kocialkowski major = FIELD_GET(LOGICVC_IP_VERSION_MAJOR_MASK, version); 268efeeaefeSPaul Kocialkowski minor = FIELD_GET(LOGICVC_IP_VERSION_MINOR_MASK, version); 269efeeaefeSPaul Kocialkowski level = FIELD_GET(LOGICVC_IP_VERSION_LEVEL_MASK, version) + 'a'; 270efeeaefeSPaul Kocialkowski 271efeeaefeSPaul Kocialkowski for (i = 0; i < ARRAY_SIZE(logicvc_drm_caps); i++) { 272efeeaefeSPaul Kocialkowski if (logicvc_drm_caps[i].major && 273efeeaefeSPaul Kocialkowski logicvc_drm_caps[i].major != major) 274efeeaefeSPaul Kocialkowski continue; 275efeeaefeSPaul Kocialkowski 276efeeaefeSPaul Kocialkowski if (logicvc_drm_caps[i].minor && 277efeeaefeSPaul Kocialkowski logicvc_drm_caps[i].minor != minor) 278efeeaefeSPaul Kocialkowski continue; 279efeeaefeSPaul Kocialkowski 280efeeaefeSPaul Kocialkowski if (logicvc_drm_caps[i].level && 281efeeaefeSPaul Kocialkowski logicvc_drm_caps[i].level != level) 282efeeaefeSPaul Kocialkowski continue; 283efeeaefeSPaul Kocialkowski 284efeeaefeSPaul Kocialkowski caps = &logicvc_drm_caps[i]; 285efeeaefeSPaul Kocialkowski } 286efeeaefeSPaul Kocialkowski 287efeeaefeSPaul Kocialkowski drm_info(drm_dev, "LogiCVC version %d.%02d.%c\n", major, minor, level); 288efeeaefeSPaul Kocialkowski 289efeeaefeSPaul Kocialkowski return caps; 290efeeaefeSPaul Kocialkowski } 291efeeaefeSPaul Kocialkowski 292efeeaefeSPaul Kocialkowski static int logicvc_drm_probe(struct platform_device *pdev) 293efeeaefeSPaul Kocialkowski { 294efeeaefeSPaul Kocialkowski struct device_node *of_node = pdev->dev.of_node; 295efeeaefeSPaul Kocialkowski struct device_node *reserved_mem_node; 296efeeaefeSPaul Kocialkowski struct reserved_mem *reserved_mem = NULL; 297efeeaefeSPaul Kocialkowski const struct logicvc_drm_caps *caps; 298efeeaefeSPaul Kocialkowski struct logicvc_drm *logicvc; 299efeeaefeSPaul Kocialkowski struct device *dev = &pdev->dev; 300efeeaefeSPaul Kocialkowski struct drm_device *drm_dev; 3015e803436SDan Carpenter struct regmap *regmap = NULL; 302efeeaefeSPaul Kocialkowski struct resource res; 303efeeaefeSPaul Kocialkowski void __iomem *base; 304efeeaefeSPaul Kocialkowski int irq; 305efeeaefeSPaul Kocialkowski int ret; 306efeeaefeSPaul Kocialkowski 307efeeaefeSPaul Kocialkowski ret = of_reserved_mem_device_init(dev); 308efeeaefeSPaul Kocialkowski if (ret && ret != -ENODEV) { 309efeeaefeSPaul Kocialkowski dev_err(dev, "Failed to init memory region\n"); 310efeeaefeSPaul Kocialkowski goto error_early; 311efeeaefeSPaul Kocialkowski } 312efeeaefeSPaul Kocialkowski 313efeeaefeSPaul Kocialkowski reserved_mem_node = of_parse_phandle(of_node, "memory-region", 0); 314efeeaefeSPaul Kocialkowski if (reserved_mem_node) { 315efeeaefeSPaul Kocialkowski reserved_mem = of_reserved_mem_lookup(reserved_mem_node); 316efeeaefeSPaul Kocialkowski of_node_put(reserved_mem_node); 317efeeaefeSPaul Kocialkowski } 318efeeaefeSPaul Kocialkowski 319efeeaefeSPaul Kocialkowski /* Get regmap from parent if available. */ 320efeeaefeSPaul Kocialkowski if (of_node->parent) 321efeeaefeSPaul Kocialkowski regmap = syscon_node_to_regmap(of_node->parent); 322efeeaefeSPaul Kocialkowski 323efeeaefeSPaul Kocialkowski /* Register our own regmap otherwise. */ 324efeeaefeSPaul Kocialkowski if (IS_ERR_OR_NULL(regmap)) { 325efeeaefeSPaul Kocialkowski ret = of_address_to_resource(of_node, 0, &res); 326efeeaefeSPaul Kocialkowski if (ret) { 327efeeaefeSPaul Kocialkowski dev_err(dev, "Failed to get resource from address\n"); 328efeeaefeSPaul Kocialkowski goto error_reserved_mem; 329efeeaefeSPaul Kocialkowski } 330efeeaefeSPaul Kocialkowski 331efeeaefeSPaul Kocialkowski base = devm_ioremap_resource(dev, &res); 332efeeaefeSPaul Kocialkowski if (IS_ERR(base)) { 333efeeaefeSPaul Kocialkowski dev_err(dev, "Failed to map I/O base\n"); 334efeeaefeSPaul Kocialkowski ret = PTR_ERR(base); 335efeeaefeSPaul Kocialkowski goto error_reserved_mem; 336efeeaefeSPaul Kocialkowski } 337efeeaefeSPaul Kocialkowski 338efeeaefeSPaul Kocialkowski logicvc_drm_regmap_config.max_register = resource_size(&res) - 339efeeaefeSPaul Kocialkowski 4; 340efeeaefeSPaul Kocialkowski 341efeeaefeSPaul Kocialkowski regmap = devm_regmap_init_mmio(dev, base, 342efeeaefeSPaul Kocialkowski &logicvc_drm_regmap_config); 343efeeaefeSPaul Kocialkowski if (IS_ERR(regmap)) { 344efeeaefeSPaul Kocialkowski dev_err(dev, "Failed to create regmap for I/O\n"); 345efeeaefeSPaul Kocialkowski ret = PTR_ERR(regmap); 346efeeaefeSPaul Kocialkowski goto error_reserved_mem; 347efeeaefeSPaul Kocialkowski } 348efeeaefeSPaul Kocialkowski } 349efeeaefeSPaul Kocialkowski 350efeeaefeSPaul Kocialkowski irq = platform_get_irq(pdev, 0); 351efeeaefeSPaul Kocialkowski if (irq < 0) { 352efeeaefeSPaul Kocialkowski ret = -ENODEV; 353efeeaefeSPaul Kocialkowski goto error_reserved_mem; 354efeeaefeSPaul Kocialkowski } 355efeeaefeSPaul Kocialkowski 356efeeaefeSPaul Kocialkowski logicvc = devm_drm_dev_alloc(dev, &logicvc_drm_driver, 357efeeaefeSPaul Kocialkowski struct logicvc_drm, drm_dev); 358efeeaefeSPaul Kocialkowski if (IS_ERR(logicvc)) { 359efeeaefeSPaul Kocialkowski ret = PTR_ERR(logicvc); 360efeeaefeSPaul Kocialkowski goto error_reserved_mem; 361efeeaefeSPaul Kocialkowski } 362efeeaefeSPaul Kocialkowski 363efeeaefeSPaul Kocialkowski platform_set_drvdata(pdev, logicvc); 364efeeaefeSPaul Kocialkowski drm_dev = &logicvc->drm_dev; 365efeeaefeSPaul Kocialkowski 366efeeaefeSPaul Kocialkowski logicvc->regmap = regmap; 367efeeaefeSPaul Kocialkowski INIT_LIST_HEAD(&logicvc->layers_list); 368efeeaefeSPaul Kocialkowski 369efeeaefeSPaul Kocialkowski caps = logicvc_drm_caps_match(logicvc); 370efeeaefeSPaul Kocialkowski if (!caps) { 371efeeaefeSPaul Kocialkowski ret = -EINVAL; 372efeeaefeSPaul Kocialkowski goto error_reserved_mem; 373efeeaefeSPaul Kocialkowski } 374efeeaefeSPaul Kocialkowski 375efeeaefeSPaul Kocialkowski logicvc->caps = caps; 376efeeaefeSPaul Kocialkowski 377efeeaefeSPaul Kocialkowski if (reserved_mem) 378efeeaefeSPaul Kocialkowski logicvc->reserved_mem_base = reserved_mem->base; 379efeeaefeSPaul Kocialkowski 380efeeaefeSPaul Kocialkowski ret = logicvc_clocks_prepare(logicvc); 381efeeaefeSPaul Kocialkowski if (ret) { 382efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to prepare clocks\n"); 383efeeaefeSPaul Kocialkowski goto error_reserved_mem; 384efeeaefeSPaul Kocialkowski } 385efeeaefeSPaul Kocialkowski 386efeeaefeSPaul Kocialkowski ret = devm_request_irq(dev, irq, logicvc_drm_irq_handler, 0, 387efeeaefeSPaul Kocialkowski dev_name(dev), logicvc); 388efeeaefeSPaul Kocialkowski if (ret) { 389efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to request IRQ\n"); 390efeeaefeSPaul Kocialkowski goto error_clocks; 391efeeaefeSPaul Kocialkowski } 392efeeaefeSPaul Kocialkowski 393efeeaefeSPaul Kocialkowski ret = logicvc_drm_config_parse(logicvc); 394efeeaefeSPaul Kocialkowski if (ret && ret != -ENODEV) { 395efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to parse config\n"); 396efeeaefeSPaul Kocialkowski goto error_clocks; 397efeeaefeSPaul Kocialkowski } 398efeeaefeSPaul Kocialkowski 399efeeaefeSPaul Kocialkowski ret = drmm_mode_config_init(drm_dev); 400efeeaefeSPaul Kocialkowski if (ret) { 401efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to init mode config\n"); 402efeeaefeSPaul Kocialkowski goto error_clocks; 403efeeaefeSPaul Kocialkowski } 404efeeaefeSPaul Kocialkowski 405efeeaefeSPaul Kocialkowski ret = logicvc_layers_init(logicvc); 406efeeaefeSPaul Kocialkowski if (ret) { 407efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize layers\n"); 408efeeaefeSPaul Kocialkowski goto error_clocks; 409efeeaefeSPaul Kocialkowski } 410efeeaefeSPaul Kocialkowski 411efeeaefeSPaul Kocialkowski ret = logicvc_crtc_init(logicvc); 412efeeaefeSPaul Kocialkowski if (ret) { 413efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize CRTC\n"); 414efeeaefeSPaul Kocialkowski goto error_clocks; 415efeeaefeSPaul Kocialkowski } 416efeeaefeSPaul Kocialkowski 417efeeaefeSPaul Kocialkowski logicvc_layers_attach_crtc(logicvc); 418efeeaefeSPaul Kocialkowski 419efeeaefeSPaul Kocialkowski ret = logicvc_interface_init(logicvc); 420efeeaefeSPaul Kocialkowski if (ret) { 421efeeaefeSPaul Kocialkowski if (ret != -EPROBE_DEFER) 422efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize interface\n"); 423efeeaefeSPaul Kocialkowski 424efeeaefeSPaul Kocialkowski goto error_clocks; 425efeeaefeSPaul Kocialkowski } 426efeeaefeSPaul Kocialkowski 427efeeaefeSPaul Kocialkowski logicvc_interface_attach_crtc(logicvc); 428efeeaefeSPaul Kocialkowski 429efeeaefeSPaul Kocialkowski ret = logicvc_mode_init(logicvc); 430efeeaefeSPaul Kocialkowski if (ret) { 431efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize KMS\n"); 432efeeaefeSPaul Kocialkowski goto error_clocks; 433efeeaefeSPaul Kocialkowski } 434efeeaefeSPaul Kocialkowski 435efeeaefeSPaul Kocialkowski ret = drm_dev_register(drm_dev, 0); 436efeeaefeSPaul Kocialkowski if (ret) { 437efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to register DRM device\n"); 438efeeaefeSPaul Kocialkowski goto error_mode; 439efeeaefeSPaul Kocialkowski } 440efeeaefeSPaul Kocialkowski 441efeeaefeSPaul Kocialkowski drm_fbdev_generic_setup(drm_dev, drm_dev->mode_config.preferred_depth); 442efeeaefeSPaul Kocialkowski 443efeeaefeSPaul Kocialkowski return 0; 444efeeaefeSPaul Kocialkowski 445efeeaefeSPaul Kocialkowski error_mode: 446efeeaefeSPaul Kocialkowski logicvc_mode_fini(logicvc); 447efeeaefeSPaul Kocialkowski 448efeeaefeSPaul Kocialkowski error_clocks: 449efeeaefeSPaul Kocialkowski logicvc_clocks_unprepare(logicvc); 450efeeaefeSPaul Kocialkowski 451efeeaefeSPaul Kocialkowski error_reserved_mem: 452efeeaefeSPaul Kocialkowski of_reserved_mem_device_release(dev); 453efeeaefeSPaul Kocialkowski 454efeeaefeSPaul Kocialkowski error_early: 455efeeaefeSPaul Kocialkowski return ret; 456efeeaefeSPaul Kocialkowski } 457efeeaefeSPaul Kocialkowski 458efeeaefeSPaul Kocialkowski static int logicvc_drm_remove(struct platform_device *pdev) 459efeeaefeSPaul Kocialkowski { 460efeeaefeSPaul Kocialkowski struct logicvc_drm *logicvc = platform_get_drvdata(pdev); 461efeeaefeSPaul Kocialkowski struct device *dev = &pdev->dev; 462efeeaefeSPaul Kocialkowski struct drm_device *drm_dev = &logicvc->drm_dev; 463efeeaefeSPaul Kocialkowski 464efeeaefeSPaul Kocialkowski drm_dev_unregister(drm_dev); 465efeeaefeSPaul Kocialkowski drm_atomic_helper_shutdown(drm_dev); 466efeeaefeSPaul Kocialkowski 467efeeaefeSPaul Kocialkowski logicvc_mode_fini(logicvc); 468efeeaefeSPaul Kocialkowski 469efeeaefeSPaul Kocialkowski logicvc_clocks_unprepare(logicvc); 470efeeaefeSPaul Kocialkowski 471efeeaefeSPaul Kocialkowski of_reserved_mem_device_release(dev); 472efeeaefeSPaul Kocialkowski 473efeeaefeSPaul Kocialkowski return 0; 474efeeaefeSPaul Kocialkowski } 475efeeaefeSPaul Kocialkowski 476efeeaefeSPaul Kocialkowski static const struct of_device_id logicvc_drm_of_table[] = { 477efeeaefeSPaul Kocialkowski { .compatible = "xylon,logicvc-3.02.a-display" }, 478efeeaefeSPaul Kocialkowski { .compatible = "xylon,logicvc-4.01.a-display" }, 479efeeaefeSPaul Kocialkowski {}, 480efeeaefeSPaul Kocialkowski }; 481efeeaefeSPaul Kocialkowski MODULE_DEVICE_TABLE(of, logicvc_drm_of_table); 482efeeaefeSPaul Kocialkowski 483efeeaefeSPaul Kocialkowski static struct platform_driver logicvc_drm_platform_driver = { 484efeeaefeSPaul Kocialkowski .probe = logicvc_drm_probe, 485efeeaefeSPaul Kocialkowski .remove = logicvc_drm_remove, 486efeeaefeSPaul Kocialkowski .driver = { 487efeeaefeSPaul Kocialkowski .name = "logicvc-drm", 488efeeaefeSPaul Kocialkowski .of_match_table = logicvc_drm_of_table, 489efeeaefeSPaul Kocialkowski }, 490efeeaefeSPaul Kocialkowski }; 491efeeaefeSPaul Kocialkowski 492efeeaefeSPaul Kocialkowski module_platform_driver(logicvc_drm_platform_driver); 493efeeaefeSPaul Kocialkowski 494efeeaefeSPaul Kocialkowski MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 495efeeaefeSPaul Kocialkowski MODULE_DESCRIPTION("Xylon LogiCVC DRM driver"); 496efeeaefeSPaul Kocialkowski MODULE_LICENSE("GPL"); 497