xref: /linux/drivers/gpu/drm/imx/dc/dc-de.c (revision 8d2b0853add1d7534dc0794e3c8e0b9e8c4ec640)
1*9f09e317SLiu Ying // SPDX-License-Identifier: GPL-2.0+
2*9f09e317SLiu Ying /*
3*9f09e317SLiu Ying  * Copyright 2024 NXP
4*9f09e317SLiu Ying  */
5*9f09e317SLiu Ying 
6*9f09e317SLiu Ying #include <linux/component.h>
7*9f09e317SLiu Ying #include <linux/mod_devicetable.h>
8*9f09e317SLiu Ying #include <linux/module.h>
9*9f09e317SLiu Ying #include <linux/of_platform.h>
10*9f09e317SLiu Ying #include <linux/platform_device.h>
11*9f09e317SLiu Ying #include <linux/pm.h>
12*9f09e317SLiu Ying #include <linux/pm_runtime.h>
13*9f09e317SLiu Ying #include <linux/regmap.h>
14*9f09e317SLiu Ying 
15*9f09e317SLiu Ying #include "dc-de.h"
16*9f09e317SLiu Ying #include "dc-drv.h"
17*9f09e317SLiu Ying 
18*9f09e317SLiu Ying #define POLARITYCTRL		0xc
19*9f09e317SLiu Ying #define  POLEN_HIGH		BIT(2)
20*9f09e317SLiu Ying 
21*9f09e317SLiu Ying static const struct dc_subdev_info dc_de_info[] = {
22*9f09e317SLiu Ying 	{ .reg_start = 0x5618b400, .id = 0, },
23*9f09e317SLiu Ying 	{ .reg_start = 0x5618b420, .id = 1, },
24*9f09e317SLiu Ying };
25*9f09e317SLiu Ying 
26*9f09e317SLiu Ying static const struct regmap_range dc_de_regmap_ranges[] = {
27*9f09e317SLiu Ying 	regmap_reg_range(POLARITYCTRL, POLARITYCTRL),
28*9f09e317SLiu Ying };
29*9f09e317SLiu Ying 
30*9f09e317SLiu Ying static const struct regmap_access_table dc_de_regmap_access_table = {
31*9f09e317SLiu Ying 	.yes_ranges = dc_de_regmap_ranges,
32*9f09e317SLiu Ying 	.n_yes_ranges = ARRAY_SIZE(dc_de_regmap_ranges),
33*9f09e317SLiu Ying };
34*9f09e317SLiu Ying 
35*9f09e317SLiu Ying static const struct regmap_config dc_de_top_regmap_config = {
36*9f09e317SLiu Ying 	.name = "top",
37*9f09e317SLiu Ying 	.reg_bits = 32,
38*9f09e317SLiu Ying 	.reg_stride = 4,
39*9f09e317SLiu Ying 	.val_bits = 32,
40*9f09e317SLiu Ying 	.fast_io = true,
41*9f09e317SLiu Ying 	.wr_table = &dc_de_regmap_access_table,
42*9f09e317SLiu Ying 	.rd_table = &dc_de_regmap_access_table,
43*9f09e317SLiu Ying 	.max_register = POLARITYCTRL,
44*9f09e317SLiu Ying };
45*9f09e317SLiu Ying 
46*9f09e317SLiu Ying static inline void dc_dec_init(struct dc_de *de)
47*9f09e317SLiu Ying {
48*9f09e317SLiu Ying 	regmap_write_bits(de->reg_top, POLARITYCTRL, POLARITYCTRL, POLEN_HIGH);
49*9f09e317SLiu Ying }
50*9f09e317SLiu Ying 
51*9f09e317SLiu Ying static int dc_de_bind(struct device *dev, struct device *master, void *data)
52*9f09e317SLiu Ying {
53*9f09e317SLiu Ying 	struct platform_device *pdev = to_platform_device(dev);
54*9f09e317SLiu Ying 	struct dc_drm_device *dc_drm = data;
55*9f09e317SLiu Ying 	struct resource *res_top;
56*9f09e317SLiu Ying 	void __iomem *base_top;
57*9f09e317SLiu Ying 	struct dc_de *de;
58*9f09e317SLiu Ying 	int ret, id;
59*9f09e317SLiu Ying 
60*9f09e317SLiu Ying 	de = devm_kzalloc(dev, sizeof(*de), GFP_KERNEL);
61*9f09e317SLiu Ying 	if (!de)
62*9f09e317SLiu Ying 		return -ENOMEM;
63*9f09e317SLiu Ying 
64*9f09e317SLiu Ying 	base_top = devm_platform_get_and_ioremap_resource(pdev, 0, &res_top);
65*9f09e317SLiu Ying 	if (IS_ERR(base_top))
66*9f09e317SLiu Ying 		return PTR_ERR(base_top);
67*9f09e317SLiu Ying 
68*9f09e317SLiu Ying 	de->reg_top = devm_regmap_init_mmio(dev, base_top,
69*9f09e317SLiu Ying 					    &dc_de_top_regmap_config);
70*9f09e317SLiu Ying 	if (IS_ERR(de->reg_top))
71*9f09e317SLiu Ying 		return PTR_ERR(de->reg_top);
72*9f09e317SLiu Ying 
73*9f09e317SLiu Ying 	de->irq_shdload = platform_get_irq_byname(pdev, "shdload");
74*9f09e317SLiu Ying 	if (de->irq_shdload < 0)
75*9f09e317SLiu Ying 		return de->irq_shdload;
76*9f09e317SLiu Ying 
77*9f09e317SLiu Ying 	de->irq_framecomplete = platform_get_irq_byname(pdev, "framecomplete");
78*9f09e317SLiu Ying 	if (de->irq_framecomplete < 0)
79*9f09e317SLiu Ying 		return de->irq_framecomplete;
80*9f09e317SLiu Ying 
81*9f09e317SLiu Ying 	de->irq_seqcomplete = platform_get_irq_byname(pdev, "seqcomplete");
82*9f09e317SLiu Ying 	if (de->irq_seqcomplete < 0)
83*9f09e317SLiu Ying 		return de->irq_seqcomplete;
84*9f09e317SLiu Ying 
85*9f09e317SLiu Ying 	de->dev = dev;
86*9f09e317SLiu Ying 
87*9f09e317SLiu Ying 	dev_set_drvdata(dev, de);
88*9f09e317SLiu Ying 
89*9f09e317SLiu Ying 	ret = devm_pm_runtime_enable(dev);
90*9f09e317SLiu Ying 	if (ret)
91*9f09e317SLiu Ying 		return ret;
92*9f09e317SLiu Ying 
93*9f09e317SLiu Ying 	id = dc_subdev_get_id(dc_de_info, ARRAY_SIZE(dc_de_info), res_top);
94*9f09e317SLiu Ying 	if (id < 0) {
95*9f09e317SLiu Ying 		dev_err(dev, "failed to get instance number: %d\n", id);
96*9f09e317SLiu Ying 		return id;
97*9f09e317SLiu Ying 	}
98*9f09e317SLiu Ying 
99*9f09e317SLiu Ying 	dc_drm->de[id] = de;
100*9f09e317SLiu Ying 
101*9f09e317SLiu Ying 	return 0;
102*9f09e317SLiu Ying }
103*9f09e317SLiu Ying 
104*9f09e317SLiu Ying /*
105*9f09e317SLiu Ying  * It's possible to get the child device pointers from the child component
106*9f09e317SLiu Ying  * bind callbacks, but it depends on the component helper behavior to bind
107*9f09e317SLiu Ying  * the display engine component first.  To avoid the dependency, post bind
108*9f09e317SLiu Ying  * to get the pointers from dc_drm in a safe manner.
109*9f09e317SLiu Ying  */
110*9f09e317SLiu Ying void dc_de_post_bind(struct dc_drm_device *dc_drm)
111*9f09e317SLiu Ying {
112*9f09e317SLiu Ying 	struct dc_de *de;
113*9f09e317SLiu Ying 	int i;
114*9f09e317SLiu Ying 
115*9f09e317SLiu Ying 	for (i = 0; i < DC_DISPLAYS; i++) {
116*9f09e317SLiu Ying 		de = dc_drm->de[i];
117*9f09e317SLiu Ying 		de->fg = dc_drm->fg[i];
118*9f09e317SLiu Ying 		de->tc = dc_drm->tc[i];
119*9f09e317SLiu Ying 	}
120*9f09e317SLiu Ying }
121*9f09e317SLiu Ying 
122*9f09e317SLiu Ying static const struct component_ops dc_de_ops = {
123*9f09e317SLiu Ying 	.bind = dc_de_bind,
124*9f09e317SLiu Ying };
125*9f09e317SLiu Ying 
126*9f09e317SLiu Ying static int dc_de_probe(struct platform_device *pdev)
127*9f09e317SLiu Ying {
128*9f09e317SLiu Ying 	int ret;
129*9f09e317SLiu Ying 
130*9f09e317SLiu Ying 	ret = devm_of_platform_populate(&pdev->dev);
131*9f09e317SLiu Ying 	if (ret < 0)
132*9f09e317SLiu Ying 		return ret;
133*9f09e317SLiu Ying 
134*9f09e317SLiu Ying 	ret = component_add(&pdev->dev, &dc_de_ops);
135*9f09e317SLiu Ying 	if (ret)
136*9f09e317SLiu Ying 		return dev_err_probe(&pdev->dev, ret,
137*9f09e317SLiu Ying 				     "failed to add component\n");
138*9f09e317SLiu Ying 
139*9f09e317SLiu Ying 	return 0;
140*9f09e317SLiu Ying }
141*9f09e317SLiu Ying 
142*9f09e317SLiu Ying static void dc_de_remove(struct platform_device *pdev)
143*9f09e317SLiu Ying {
144*9f09e317SLiu Ying 	component_del(&pdev->dev, &dc_de_ops);
145*9f09e317SLiu Ying }
146*9f09e317SLiu Ying 
147*9f09e317SLiu Ying static int dc_de_runtime_resume(struct device *dev)
148*9f09e317SLiu Ying {
149*9f09e317SLiu Ying 	struct dc_de *de = dev_get_drvdata(dev);
150*9f09e317SLiu Ying 
151*9f09e317SLiu Ying 	dc_dec_init(de);
152*9f09e317SLiu Ying 	dc_fg_init(de->fg);
153*9f09e317SLiu Ying 	dc_tc_init(de->tc);
154*9f09e317SLiu Ying 
155*9f09e317SLiu Ying 	return 0;
156*9f09e317SLiu Ying }
157*9f09e317SLiu Ying 
158*9f09e317SLiu Ying static const struct dev_pm_ops dc_de_pm_ops = {
159*9f09e317SLiu Ying 	RUNTIME_PM_OPS(NULL, dc_de_runtime_resume, NULL)
160*9f09e317SLiu Ying };
161*9f09e317SLiu Ying 
162*9f09e317SLiu Ying static const struct of_device_id dc_de_dt_ids[] = {
163*9f09e317SLiu Ying 	{ .compatible = "fsl,imx8qxp-dc-display-engine" },
164*9f09e317SLiu Ying 	{ /* sentinel */ }
165*9f09e317SLiu Ying };
166*9f09e317SLiu Ying MODULE_DEVICE_TABLE(of, dc_de_dt_ids);
167*9f09e317SLiu Ying 
168*9f09e317SLiu Ying struct platform_driver dc_de_driver = {
169*9f09e317SLiu Ying 	.probe = dc_de_probe,
170*9f09e317SLiu Ying 	.remove = dc_de_remove,
171*9f09e317SLiu Ying 	.driver = {
172*9f09e317SLiu Ying 		.name = "imx8-dc-display-engine",
173*9f09e317SLiu Ying 		.suppress_bind_attrs = true,
174*9f09e317SLiu Ying 		.of_match_table = dc_de_dt_ids,
175*9f09e317SLiu Ying 		.pm = pm_sleep_ptr(&dc_de_pm_ops),
176*9f09e317SLiu Ying 	},
177*9f09e317SLiu Ying };
178