xref: /linux/drivers/gpu/drm/verisilicon/vs_dc.c (revision 0fc8f6200d2313278fbf4539bbab74677c685531)
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