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> 205d3f30e0SThomas Zimmermann #include <drm/drm_fbdev_dma.h> 214a83c26aSDanilo 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 324a83c26aSDanilo Krummrich DEFINE_DRM_GEM_DMA_FOPS(logicvc_drm_fops); 33efeeaefeSPaul Kocialkowski 344a83c26aSDanilo 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 434a83c26aSDanilo 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 574a83c26aSDanilo 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; 3046f9f15e6SThomas Zimmermann unsigned int preferred_bpp; 305efeeaefeSPaul Kocialkowski int irq; 306efeeaefeSPaul Kocialkowski int ret; 307efeeaefeSPaul Kocialkowski 308efeeaefeSPaul Kocialkowski ret = of_reserved_mem_device_init(dev); 309efeeaefeSPaul Kocialkowski if (ret && ret != -ENODEV) { 310efeeaefeSPaul Kocialkowski dev_err(dev, "Failed to init memory region\n"); 311efeeaefeSPaul Kocialkowski goto error_early; 312efeeaefeSPaul Kocialkowski } 313efeeaefeSPaul Kocialkowski 314efeeaefeSPaul Kocialkowski reserved_mem_node = of_parse_phandle(of_node, "memory-region", 0); 315efeeaefeSPaul Kocialkowski if (reserved_mem_node) { 316efeeaefeSPaul Kocialkowski reserved_mem = of_reserved_mem_lookup(reserved_mem_node); 317efeeaefeSPaul Kocialkowski of_node_put(reserved_mem_node); 318efeeaefeSPaul Kocialkowski } 319efeeaefeSPaul Kocialkowski 320efeeaefeSPaul Kocialkowski /* Get regmap from parent if available. */ 321efeeaefeSPaul Kocialkowski if (of_node->parent) 322efeeaefeSPaul Kocialkowski regmap = syscon_node_to_regmap(of_node->parent); 323efeeaefeSPaul Kocialkowski 324efeeaefeSPaul Kocialkowski /* Register our own regmap otherwise. */ 325efeeaefeSPaul Kocialkowski if (IS_ERR_OR_NULL(regmap)) { 326efeeaefeSPaul Kocialkowski ret = of_address_to_resource(of_node, 0, &res); 327efeeaefeSPaul Kocialkowski if (ret) { 328efeeaefeSPaul Kocialkowski dev_err(dev, "Failed to get resource from address\n"); 329efeeaefeSPaul Kocialkowski goto error_reserved_mem; 330efeeaefeSPaul Kocialkowski } 331efeeaefeSPaul Kocialkowski 332efeeaefeSPaul Kocialkowski base = devm_ioremap_resource(dev, &res); 333efeeaefeSPaul Kocialkowski if (IS_ERR(base)) { 334efeeaefeSPaul Kocialkowski dev_err(dev, "Failed to map I/O base\n"); 335efeeaefeSPaul Kocialkowski ret = PTR_ERR(base); 336efeeaefeSPaul Kocialkowski goto error_reserved_mem; 337efeeaefeSPaul Kocialkowski } 338efeeaefeSPaul Kocialkowski 339efeeaefeSPaul Kocialkowski logicvc_drm_regmap_config.max_register = resource_size(&res) - 340efeeaefeSPaul Kocialkowski 4; 341efeeaefeSPaul Kocialkowski 342efeeaefeSPaul Kocialkowski regmap = devm_regmap_init_mmio(dev, base, 343efeeaefeSPaul Kocialkowski &logicvc_drm_regmap_config); 344efeeaefeSPaul Kocialkowski if (IS_ERR(regmap)) { 345efeeaefeSPaul Kocialkowski dev_err(dev, "Failed to create regmap for I/O\n"); 346efeeaefeSPaul Kocialkowski ret = PTR_ERR(regmap); 347efeeaefeSPaul Kocialkowski goto error_reserved_mem; 348efeeaefeSPaul Kocialkowski } 349efeeaefeSPaul Kocialkowski } 350efeeaefeSPaul Kocialkowski 351efeeaefeSPaul Kocialkowski irq = platform_get_irq(pdev, 0); 352efeeaefeSPaul Kocialkowski if (irq < 0) { 353efeeaefeSPaul Kocialkowski ret = -ENODEV; 354efeeaefeSPaul Kocialkowski goto error_reserved_mem; 355efeeaefeSPaul Kocialkowski } 356efeeaefeSPaul Kocialkowski 357efeeaefeSPaul Kocialkowski logicvc = devm_drm_dev_alloc(dev, &logicvc_drm_driver, 358efeeaefeSPaul Kocialkowski struct logicvc_drm, drm_dev); 359efeeaefeSPaul Kocialkowski if (IS_ERR(logicvc)) { 360efeeaefeSPaul Kocialkowski ret = PTR_ERR(logicvc); 361efeeaefeSPaul Kocialkowski goto error_reserved_mem; 362efeeaefeSPaul Kocialkowski } 363efeeaefeSPaul Kocialkowski 364efeeaefeSPaul Kocialkowski platform_set_drvdata(pdev, logicvc); 365efeeaefeSPaul Kocialkowski drm_dev = &logicvc->drm_dev; 366efeeaefeSPaul Kocialkowski 367efeeaefeSPaul Kocialkowski logicvc->regmap = regmap; 368efeeaefeSPaul Kocialkowski INIT_LIST_HEAD(&logicvc->layers_list); 369efeeaefeSPaul Kocialkowski 370efeeaefeSPaul Kocialkowski caps = logicvc_drm_caps_match(logicvc); 371efeeaefeSPaul Kocialkowski if (!caps) { 372efeeaefeSPaul Kocialkowski ret = -EINVAL; 373efeeaefeSPaul Kocialkowski goto error_reserved_mem; 374efeeaefeSPaul Kocialkowski } 375efeeaefeSPaul Kocialkowski 376efeeaefeSPaul Kocialkowski logicvc->caps = caps; 377efeeaefeSPaul Kocialkowski 378efeeaefeSPaul Kocialkowski if (reserved_mem) 379efeeaefeSPaul Kocialkowski logicvc->reserved_mem_base = reserved_mem->base; 380efeeaefeSPaul Kocialkowski 381efeeaefeSPaul Kocialkowski ret = logicvc_clocks_prepare(logicvc); 382efeeaefeSPaul Kocialkowski if (ret) { 383efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to prepare clocks\n"); 384efeeaefeSPaul Kocialkowski goto error_reserved_mem; 385efeeaefeSPaul Kocialkowski } 386efeeaefeSPaul Kocialkowski 387efeeaefeSPaul Kocialkowski ret = devm_request_irq(dev, irq, logicvc_drm_irq_handler, 0, 388efeeaefeSPaul Kocialkowski dev_name(dev), logicvc); 389efeeaefeSPaul Kocialkowski if (ret) { 390efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to request IRQ\n"); 391efeeaefeSPaul Kocialkowski goto error_clocks; 392efeeaefeSPaul Kocialkowski } 393efeeaefeSPaul Kocialkowski 394efeeaefeSPaul Kocialkowski ret = logicvc_drm_config_parse(logicvc); 395efeeaefeSPaul Kocialkowski if (ret && ret != -ENODEV) { 396efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to parse config\n"); 397efeeaefeSPaul Kocialkowski goto error_clocks; 398efeeaefeSPaul Kocialkowski } 399efeeaefeSPaul Kocialkowski 400efeeaefeSPaul Kocialkowski ret = drmm_mode_config_init(drm_dev); 401efeeaefeSPaul Kocialkowski if (ret) { 402efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to init mode config\n"); 403efeeaefeSPaul Kocialkowski goto error_clocks; 404efeeaefeSPaul Kocialkowski } 405efeeaefeSPaul Kocialkowski 406efeeaefeSPaul Kocialkowski ret = logicvc_layers_init(logicvc); 407efeeaefeSPaul Kocialkowski if (ret) { 408efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize layers\n"); 409efeeaefeSPaul Kocialkowski goto error_clocks; 410efeeaefeSPaul Kocialkowski } 411efeeaefeSPaul Kocialkowski 412efeeaefeSPaul Kocialkowski ret = logicvc_crtc_init(logicvc); 413efeeaefeSPaul Kocialkowski if (ret) { 414efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize CRTC\n"); 415efeeaefeSPaul Kocialkowski goto error_clocks; 416efeeaefeSPaul Kocialkowski } 417efeeaefeSPaul Kocialkowski 418efeeaefeSPaul Kocialkowski logicvc_layers_attach_crtc(logicvc); 419efeeaefeSPaul Kocialkowski 420efeeaefeSPaul Kocialkowski ret = logicvc_interface_init(logicvc); 421efeeaefeSPaul Kocialkowski if (ret) { 422efeeaefeSPaul Kocialkowski if (ret != -EPROBE_DEFER) 423efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize interface\n"); 424efeeaefeSPaul Kocialkowski 425efeeaefeSPaul Kocialkowski goto error_clocks; 426efeeaefeSPaul Kocialkowski } 427efeeaefeSPaul Kocialkowski 428efeeaefeSPaul Kocialkowski logicvc_interface_attach_crtc(logicvc); 429efeeaefeSPaul Kocialkowski 430efeeaefeSPaul Kocialkowski ret = logicvc_mode_init(logicvc); 431efeeaefeSPaul Kocialkowski if (ret) { 432efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to initialize KMS\n"); 433efeeaefeSPaul Kocialkowski goto error_clocks; 434efeeaefeSPaul Kocialkowski } 435efeeaefeSPaul Kocialkowski 436efeeaefeSPaul Kocialkowski ret = drm_dev_register(drm_dev, 0); 437efeeaefeSPaul Kocialkowski if (ret) { 438efeeaefeSPaul Kocialkowski drm_err(drm_dev, "Failed to register DRM device\n"); 439efeeaefeSPaul Kocialkowski goto error_mode; 440efeeaefeSPaul Kocialkowski } 441efeeaefeSPaul Kocialkowski 4426f9f15e6SThomas Zimmermann switch (drm_dev->mode_config.preferred_depth) { 4436f9f15e6SThomas Zimmermann case 16: 4446f9f15e6SThomas Zimmermann preferred_bpp = 16; 4456f9f15e6SThomas Zimmermann break; 4466f9f15e6SThomas Zimmermann case 24: 4476f9f15e6SThomas Zimmermann case 32: 4486f9f15e6SThomas Zimmermann default: 4496f9f15e6SThomas Zimmermann preferred_bpp = 32; 4506f9f15e6SThomas Zimmermann break; 4516f9f15e6SThomas Zimmermann } 4525d3f30e0SThomas Zimmermann drm_fbdev_dma_setup(drm_dev, preferred_bpp); 453efeeaefeSPaul Kocialkowski 454efeeaefeSPaul Kocialkowski return 0; 455efeeaefeSPaul Kocialkowski 456efeeaefeSPaul Kocialkowski error_mode: 457efeeaefeSPaul Kocialkowski logicvc_mode_fini(logicvc); 458efeeaefeSPaul Kocialkowski 459efeeaefeSPaul Kocialkowski error_clocks: 460efeeaefeSPaul Kocialkowski logicvc_clocks_unprepare(logicvc); 461efeeaefeSPaul Kocialkowski 462efeeaefeSPaul Kocialkowski error_reserved_mem: 463efeeaefeSPaul Kocialkowski of_reserved_mem_device_release(dev); 464efeeaefeSPaul Kocialkowski 465efeeaefeSPaul Kocialkowski error_early: 466efeeaefeSPaul Kocialkowski return ret; 467efeeaefeSPaul Kocialkowski } 468efeeaefeSPaul Kocialkowski 469*41a56a18SUwe Kleine-König static void logicvc_drm_remove(struct platform_device *pdev) 470efeeaefeSPaul Kocialkowski { 471efeeaefeSPaul Kocialkowski struct logicvc_drm *logicvc = platform_get_drvdata(pdev); 472efeeaefeSPaul Kocialkowski struct device *dev = &pdev->dev; 473efeeaefeSPaul Kocialkowski struct drm_device *drm_dev = &logicvc->drm_dev; 474efeeaefeSPaul Kocialkowski 475efeeaefeSPaul Kocialkowski drm_dev_unregister(drm_dev); 476efeeaefeSPaul Kocialkowski drm_atomic_helper_shutdown(drm_dev); 477efeeaefeSPaul Kocialkowski 478efeeaefeSPaul Kocialkowski logicvc_mode_fini(logicvc); 479efeeaefeSPaul Kocialkowski 480efeeaefeSPaul Kocialkowski logicvc_clocks_unprepare(logicvc); 481efeeaefeSPaul Kocialkowski 482efeeaefeSPaul Kocialkowski of_reserved_mem_device_release(dev); 483efeeaefeSPaul Kocialkowski } 484efeeaefeSPaul Kocialkowski 485efeeaefeSPaul Kocialkowski static const struct of_device_id logicvc_drm_of_table[] = { 486efeeaefeSPaul Kocialkowski { .compatible = "xylon,logicvc-3.02.a-display" }, 487efeeaefeSPaul Kocialkowski { .compatible = "xylon,logicvc-4.01.a-display" }, 488efeeaefeSPaul Kocialkowski {}, 489efeeaefeSPaul Kocialkowski }; 490efeeaefeSPaul Kocialkowski MODULE_DEVICE_TABLE(of, logicvc_drm_of_table); 491efeeaefeSPaul Kocialkowski 492efeeaefeSPaul Kocialkowski static struct platform_driver logicvc_drm_platform_driver = { 493efeeaefeSPaul Kocialkowski .probe = logicvc_drm_probe, 494*41a56a18SUwe Kleine-König .remove_new = logicvc_drm_remove, 495efeeaefeSPaul Kocialkowski .driver = { 496efeeaefeSPaul Kocialkowski .name = "logicvc-drm", 497efeeaefeSPaul Kocialkowski .of_match_table = logicvc_drm_of_table, 498efeeaefeSPaul Kocialkowski }, 499efeeaefeSPaul Kocialkowski }; 500efeeaefeSPaul Kocialkowski 501efeeaefeSPaul Kocialkowski module_platform_driver(logicvc_drm_platform_driver); 502efeeaefeSPaul Kocialkowski 503efeeaefeSPaul Kocialkowski MODULE_AUTHOR("Paul Kocialkowski <paul.kocialkowski@bootlin.com>"); 504efeeaefeSPaul Kocialkowski MODULE_DESCRIPTION("Xylon LogiCVC DRM driver"); 505efeeaefeSPaul Kocialkowski MODULE_LICENSE("GPL"); 506