1dbf21777SIcenowy Zheng // SPDX-License-Identifier: GPL-2.0-only 2dbf21777SIcenowy Zheng /* 3dbf21777SIcenowy Zheng * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me> 4dbf21777SIcenowy Zheng */ 5dbf21777SIcenowy Zheng 6dbf21777SIcenowy Zheng #include <linux/dma-mapping.h> 7dbf21777SIcenowy Zheng #include <linux/irqreturn.h> 8dbf21777SIcenowy Zheng #include <linux/of.h> 9dbf21777SIcenowy Zheng #include <linux/of_graph.h> 10dbf21777SIcenowy Zheng 11dbf21777SIcenowy Zheng #include "vs_crtc.h" 12dbf21777SIcenowy Zheng #include "vs_dc.h" 13dbf21777SIcenowy Zheng #include "vs_dc_top_regs.h" 14dbf21777SIcenowy Zheng #include "vs_drm.h" 15dbf21777SIcenowy Zheng #include "vs_hwdb.h" 16dbf21777SIcenowy Zheng 17dbf21777SIcenowy Zheng static const struct regmap_config vs_dc_regmap_cfg = { 18dbf21777SIcenowy Zheng .reg_bits = 32, 19dbf21777SIcenowy Zheng .val_bits = 32, 20dbf21777SIcenowy Zheng .reg_stride = sizeof(u32), 21dbf21777SIcenowy Zheng /* VSDC_OVL_CONFIG_EX(1) */ 22dbf21777SIcenowy Zheng .max_register = 0x2544, 23dbf21777SIcenowy Zheng }; 24dbf21777SIcenowy Zheng 25dbf21777SIcenowy Zheng static const struct of_device_id vs_dc_driver_dt_match[] = { 26dbf21777SIcenowy Zheng { .compatible = "verisilicon,dc" }, 27dbf21777SIcenowy Zheng {}, 28dbf21777SIcenowy Zheng }; 29dbf21777SIcenowy Zheng MODULE_DEVICE_TABLE(of, vs_dc_driver_dt_match); 30dbf21777SIcenowy Zheng 31dbf21777SIcenowy Zheng static irqreturn_t vs_dc_irq_handler(int irq, void *private) 32dbf21777SIcenowy Zheng { 33dbf21777SIcenowy Zheng struct vs_dc *dc = private; 34dbf21777SIcenowy Zheng u32 irqs; 35dbf21777SIcenowy Zheng 36dbf21777SIcenowy Zheng regmap_read(dc->regs, VSDC_TOP_IRQ_ACK, &irqs); 37dbf21777SIcenowy Zheng 38dbf21777SIcenowy Zheng vs_drm_handle_irq(dc, irqs); 39dbf21777SIcenowy Zheng 40dbf21777SIcenowy Zheng return IRQ_HANDLED; 41dbf21777SIcenowy Zheng } 42dbf21777SIcenowy Zheng 43dbf21777SIcenowy Zheng static int vs_dc_probe(struct platform_device *pdev) 44dbf21777SIcenowy Zheng { 45dbf21777SIcenowy Zheng struct device *dev = &pdev->dev; 46dbf21777SIcenowy Zheng struct vs_dc *dc; 47dbf21777SIcenowy Zheng void __iomem *regs; 48dbf21777SIcenowy Zheng unsigned int port_count, i; 49f2edbafcSIcenowy Zheng /* pix%u */ 50f2edbafcSIcenowy Zheng char pixclk_name[14]; 51dbf21777SIcenowy Zheng int irq, ret; 52dbf21777SIcenowy Zheng 53dbf21777SIcenowy Zheng if (!dev->of_node) { 54dbf21777SIcenowy Zheng dev_err(dev, "can't find DC devices\n"); 55dbf21777SIcenowy Zheng return -ENODEV; 56dbf21777SIcenowy Zheng } 57dbf21777SIcenowy Zheng 58dbf21777SIcenowy Zheng port_count = of_graph_get_port_count(dev->of_node); 59dbf21777SIcenowy Zheng if (!port_count) { 60dbf21777SIcenowy Zheng dev_err(dev, "can't find DC downstream ports\n"); 61dbf21777SIcenowy Zheng return -ENODEV; 62dbf21777SIcenowy Zheng } 63dbf21777SIcenowy Zheng if (port_count > VSDC_MAX_OUTPUTS) { 64dbf21777SIcenowy Zheng dev_err(dev, "too many DC downstream ports than possible\n"); 65dbf21777SIcenowy Zheng return -EINVAL; 66dbf21777SIcenowy Zheng } 67dbf21777SIcenowy Zheng 68dbf21777SIcenowy Zheng ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 69dbf21777SIcenowy Zheng if (ret) { 70dbf21777SIcenowy Zheng dev_err(dev, "No suitable DMA available\n"); 71dbf21777SIcenowy Zheng return ret; 72dbf21777SIcenowy Zheng } 73dbf21777SIcenowy Zheng 74dbf21777SIcenowy Zheng dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL); 75dbf21777SIcenowy Zheng if (!dc) 76dbf21777SIcenowy Zheng return -ENOMEM; 77dbf21777SIcenowy Zheng 78dbf21777SIcenowy Zheng dc->rsts[0].id = "core"; 79dbf21777SIcenowy Zheng dc->rsts[1].id = "axi"; 80dbf21777SIcenowy Zheng dc->rsts[2].id = "ahb"; 81dbf21777SIcenowy Zheng 82dbf21777SIcenowy Zheng ret = devm_reset_control_bulk_get_optional_shared(dev, VSDC_RESET_COUNT, 83dbf21777SIcenowy Zheng dc->rsts); 84dbf21777SIcenowy Zheng if (ret) { 85dbf21777SIcenowy Zheng dev_err(dev, "can't get reset lines\n"); 86dbf21777SIcenowy Zheng return ret; 87dbf21777SIcenowy Zheng } 88dbf21777SIcenowy Zheng 89dbf21777SIcenowy Zheng dc->core_clk = devm_clk_get_enabled(dev, "core"); 90dbf21777SIcenowy Zheng if (IS_ERR(dc->core_clk)) { 91dbf21777SIcenowy Zheng dev_err(dev, "can't get core clock\n"); 92dbf21777SIcenowy Zheng return PTR_ERR(dc->core_clk); 93dbf21777SIcenowy Zheng } 94dbf21777SIcenowy Zheng 95dbf21777SIcenowy Zheng dc->axi_clk = devm_clk_get_enabled(dev, "axi"); 96dbf21777SIcenowy Zheng if (IS_ERR(dc->axi_clk)) { 97dbf21777SIcenowy Zheng dev_err(dev, "can't get axi clock\n"); 98dbf21777SIcenowy Zheng return PTR_ERR(dc->axi_clk); 99dbf21777SIcenowy Zheng } 100dbf21777SIcenowy Zheng 101dbf21777SIcenowy Zheng dc->ahb_clk = devm_clk_get_enabled(dev, "ahb"); 102dbf21777SIcenowy Zheng if (IS_ERR(dc->ahb_clk)) { 103dbf21777SIcenowy Zheng dev_err(dev, "can't get ahb clock\n"); 104dbf21777SIcenowy Zheng return PTR_ERR(dc->ahb_clk); 105dbf21777SIcenowy Zheng } 106dbf21777SIcenowy Zheng 107dbf21777SIcenowy Zheng irq = platform_get_irq(pdev, 0); 108dbf21777SIcenowy Zheng if (irq < 0) { 109dbf21777SIcenowy Zheng dev_err(dev, "can't get irq\n"); 110dbf21777SIcenowy Zheng return irq; 111dbf21777SIcenowy Zheng } 112dbf21777SIcenowy Zheng 113dbf21777SIcenowy Zheng ret = reset_control_bulk_deassert(VSDC_RESET_COUNT, dc->rsts); 114dbf21777SIcenowy Zheng if (ret) { 115dbf21777SIcenowy Zheng dev_err(dev, "can't deassert reset lines\n"); 116dbf21777SIcenowy Zheng return ret; 117dbf21777SIcenowy Zheng } 118dbf21777SIcenowy Zheng 119dbf21777SIcenowy Zheng regs = devm_platform_ioremap_resource(pdev, 0); 120dbf21777SIcenowy Zheng if (IS_ERR(regs)) { 121dbf21777SIcenowy Zheng dev_err(dev, "can't map registers"); 122dbf21777SIcenowy Zheng ret = PTR_ERR(regs); 123dbf21777SIcenowy Zheng goto err_rst_assert; 124dbf21777SIcenowy Zheng } 125dbf21777SIcenowy Zheng 126dbf21777SIcenowy Zheng dc->regs = devm_regmap_init_mmio(dev, regs, &vs_dc_regmap_cfg); 127dbf21777SIcenowy Zheng if (IS_ERR(dc->regs)) { 128dbf21777SIcenowy Zheng ret = PTR_ERR(dc->regs); 129dbf21777SIcenowy Zheng goto err_rst_assert; 130dbf21777SIcenowy Zheng } 131dbf21777SIcenowy Zheng 132dbf21777SIcenowy Zheng ret = vs_fill_chip_identity(dc->regs, &dc->identity); 133dbf21777SIcenowy Zheng if (ret) 134dbf21777SIcenowy Zheng goto err_rst_assert; 135dbf21777SIcenowy Zheng 136dbf21777SIcenowy Zheng dev_info(dev, "Found DC%x rev %x customer %x\n", dc->identity.model, 137dbf21777SIcenowy Zheng dc->identity.revision, dc->identity.customer_id); 138dbf21777SIcenowy Zheng 139dbf21777SIcenowy Zheng if (port_count > dc->identity.display_count) { 140dbf21777SIcenowy Zheng dev_err(dev, "too many downstream ports than HW capability\n"); 141dbf21777SIcenowy Zheng ret = -EINVAL; 142dbf21777SIcenowy Zheng goto err_rst_assert; 143dbf21777SIcenowy Zheng } 144dbf21777SIcenowy Zheng 145dbf21777SIcenowy Zheng for (i = 0; i < dc->identity.display_count; i++) { 146dbf21777SIcenowy Zheng snprintf(pixclk_name, sizeof(pixclk_name), "pix%u", i); 147dbf21777SIcenowy Zheng dc->pix_clk[i] = devm_clk_get(dev, pixclk_name); 148dbf21777SIcenowy Zheng if (IS_ERR(dc->pix_clk[i])) { 149dbf21777SIcenowy Zheng dev_err(dev, "can't get pixel clk %u\n", i); 150dbf21777SIcenowy Zheng ret = PTR_ERR(dc->pix_clk[i]); 151dbf21777SIcenowy Zheng goto err_rst_assert; 152dbf21777SIcenowy Zheng } 153dbf21777SIcenowy Zheng } 154dbf21777SIcenowy Zheng 155dbf21777SIcenowy Zheng ret = devm_request_irq(dev, irq, vs_dc_irq_handler, 0, 156dbf21777SIcenowy Zheng dev_name(dev), dc); 157dbf21777SIcenowy Zheng if (ret) { 158dbf21777SIcenowy Zheng dev_err(dev, "can't request irq\n"); 159dbf21777SIcenowy Zheng goto err_rst_assert; 160dbf21777SIcenowy Zheng } 161dbf21777SIcenowy Zheng 162dbf21777SIcenowy Zheng dev_set_drvdata(dev, dc); 163dbf21777SIcenowy Zheng 164dbf21777SIcenowy Zheng ret = vs_drm_initialize(dc, pdev); 165dbf21777SIcenowy Zheng if (ret) 166dbf21777SIcenowy Zheng goto err_rst_assert; 167dbf21777SIcenowy Zheng 168dbf21777SIcenowy Zheng return 0; 169dbf21777SIcenowy Zheng 170dbf21777SIcenowy Zheng err_rst_assert: 171dbf21777SIcenowy Zheng reset_control_bulk_assert(VSDC_RESET_COUNT, dc->rsts); 172dbf21777SIcenowy Zheng return ret; 173dbf21777SIcenowy Zheng } 174dbf21777SIcenowy Zheng 175dbf21777SIcenowy Zheng static void vs_dc_remove(struct platform_device *pdev) 176dbf21777SIcenowy Zheng { 177dbf21777SIcenowy Zheng struct vs_dc *dc = dev_get_drvdata(&pdev->dev); 178dbf21777SIcenowy Zheng 179dbf21777SIcenowy Zheng vs_drm_finalize(dc); 180dbf21777SIcenowy Zheng 181dbf21777SIcenowy Zheng dev_set_drvdata(&pdev->dev, NULL); 182dbf21777SIcenowy Zheng 183dbf21777SIcenowy Zheng reset_control_bulk_assert(VSDC_RESET_COUNT, dc->rsts); 184dbf21777SIcenowy Zheng } 185dbf21777SIcenowy Zheng 186dbf21777SIcenowy Zheng static void vs_dc_shutdown(struct platform_device *pdev) 187dbf21777SIcenowy Zheng { 188dbf21777SIcenowy Zheng struct vs_dc *dc = dev_get_drvdata(&pdev->dev); 189dbf21777SIcenowy Zheng 190dbf21777SIcenowy Zheng vs_drm_shutdown_handler(dc); 191dbf21777SIcenowy Zheng } 192dbf21777SIcenowy Zheng 193*e6fbe345SIcenowy Zheng static struct platform_driver vs_dc_platform_driver = { 194dbf21777SIcenowy Zheng .probe = vs_dc_probe, 195dbf21777SIcenowy Zheng .remove = vs_dc_remove, 196dbf21777SIcenowy Zheng .shutdown = vs_dc_shutdown, 197dbf21777SIcenowy Zheng .driver = { 198dbf21777SIcenowy Zheng .name = "verisilicon-dc", 199dbf21777SIcenowy Zheng .of_match_table = vs_dc_driver_dt_match, 200dbf21777SIcenowy Zheng }, 201dbf21777SIcenowy Zheng }; 202dbf21777SIcenowy Zheng 203dbf21777SIcenowy Zheng module_platform_driver(vs_dc_platform_driver); 204dbf21777SIcenowy Zheng 205dbf21777SIcenowy Zheng MODULE_AUTHOR("Icenowy Zheng <uwu@icenowy.me>"); 206dbf21777SIcenowy Zheng MODULE_DESCRIPTION("Verisilicon display controller driver"); 207dbf21777SIcenowy Zheng MODULE_LICENSE("GPL"); 208