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