1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright 2024 NXP
4 */
5
6 #include <linux/bitfield.h>
7 #include <linux/bits.h>
8 #include <linux/component.h>
9 #include <linux/ioport.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/regmap.h>
13
14 #include "dc-drv.h"
15 #include "dc-pe.h"
16
17 #define STATICCONTROL 0x8
18
19 #define FRAMEDIMENSIONS 0xc
20 #define HEIGHT(x) FIELD_PREP(GENMASK(29, 16), ((x) - 1))
21 #define WIDTH(x) FIELD_PREP(GENMASK(13, 0), ((x) - 1))
22
23 #define CONSTANTCOLOR 0x10
24 #define BLUE(x) FIELD_PREP(GENMASK(15, 8), (x))
25
26 static const struct dc_subdev_info dc_cf_info[] = {
27 { .reg_start = 0x56180960, .id = 0, },
28 { .reg_start = 0x561809e0, .id = 1, },
29 { .reg_start = 0x561809a0, .id = 4, },
30 { .reg_start = 0x56180a20, .id = 5, },
31 };
32
33 static const struct regmap_range dc_cf_regmap_ranges[] = {
34 regmap_reg_range(STATICCONTROL, CONSTANTCOLOR),
35 };
36
37 static const struct regmap_access_table dc_cf_regmap_access_table = {
38 .yes_ranges = dc_cf_regmap_ranges,
39 .n_yes_ranges = ARRAY_SIZE(dc_cf_regmap_ranges),
40 };
41
42 static const struct regmap_config dc_cf_cfg_regmap_config = {
43 .name = "cfg",
44 .reg_bits = 32,
45 .reg_stride = 4,
46 .val_bits = 32,
47 .fast_io = true,
48 .wr_table = &dc_cf_regmap_access_table,
49 .rd_table = &dc_cf_regmap_access_table,
50 .max_register = CONSTANTCOLOR,
51 };
52
dc_cf_enable_shden(struct dc_cf * cf)53 static inline void dc_cf_enable_shden(struct dc_cf *cf)
54 {
55 regmap_write(cf->reg_cfg, STATICCONTROL, SHDEN);
56 }
57
dc_cf_get_link_id(struct dc_cf * cf)58 enum dc_link_id dc_cf_get_link_id(struct dc_cf *cf)
59 {
60 return cf->link;
61 }
62
dc_cf_framedimensions(struct dc_cf * cf,unsigned int w,unsigned int h)63 void dc_cf_framedimensions(struct dc_cf *cf, unsigned int w,
64 unsigned int h)
65 {
66 regmap_write(cf->reg_cfg, FRAMEDIMENSIONS, WIDTH(w) | HEIGHT(h));
67 }
68
dc_cf_constantcolor_black(struct dc_cf * cf)69 void dc_cf_constantcolor_black(struct dc_cf *cf)
70 {
71 regmap_write(cf->reg_cfg, CONSTANTCOLOR, 0);
72 }
73
dc_cf_constantcolor_blue(struct dc_cf * cf)74 void dc_cf_constantcolor_blue(struct dc_cf *cf)
75 {
76 regmap_write(cf->reg_cfg, CONSTANTCOLOR, BLUE(0xff));
77 }
78
dc_cf_init(struct dc_cf * cf)79 void dc_cf_init(struct dc_cf *cf)
80 {
81 dc_cf_enable_shden(cf);
82 }
83
dc_cf_bind(struct device * dev,struct device * master,void * data)84 static int dc_cf_bind(struct device *dev, struct device *master, void *data)
85 {
86 struct platform_device *pdev = to_platform_device(dev);
87 struct dc_drm_device *dc_drm = data;
88 struct resource *res_pec;
89 void __iomem *base_cfg;
90 struct dc_cf *cf;
91 int id;
92
93 cf = devm_kzalloc(dev, sizeof(*cf), GFP_KERNEL);
94 if (!cf)
95 return -ENOMEM;
96
97 res_pec = platform_get_resource(pdev, IORESOURCE_MEM, 0);
98
99 base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg");
100 if (IS_ERR(base_cfg))
101 return PTR_ERR(base_cfg);
102
103 cf->reg_cfg = devm_regmap_init_mmio(dev, base_cfg,
104 &dc_cf_cfg_regmap_config);
105 if (IS_ERR(cf->reg_cfg))
106 return PTR_ERR(cf->reg_cfg);
107
108 id = dc_subdev_get_id(dc_cf_info, ARRAY_SIZE(dc_cf_info), res_pec);
109 if (id < 0) {
110 dev_err(dev, "failed to get instance number: %d\n", id);
111 return id;
112 }
113
114 switch (id) {
115 case 0:
116 cf->link = LINK_ID_CONSTFRAME0;
117 dc_drm->cf_cont[0] = cf;
118 break;
119 case 1:
120 cf->link = LINK_ID_CONSTFRAME1;
121 dc_drm->cf_cont[1] = cf;
122 break;
123 case 4:
124 cf->link = LINK_ID_CONSTFRAME4;
125 dc_drm->cf_safe[0] = cf;
126 break;
127 case 5:
128 cf->link = LINK_ID_CONSTFRAME5;
129 dc_drm->cf_safe[1] = cf;
130 break;
131 }
132
133 return 0;
134 }
135
136 static const struct component_ops dc_cf_ops = {
137 .bind = dc_cf_bind,
138 };
139
dc_cf_probe(struct platform_device * pdev)140 static int dc_cf_probe(struct platform_device *pdev)
141 {
142 int ret;
143
144 ret = component_add(&pdev->dev, &dc_cf_ops);
145 if (ret)
146 return dev_err_probe(&pdev->dev, ret,
147 "failed to add component\n");
148
149 return 0;
150 }
151
dc_cf_remove(struct platform_device * pdev)152 static void dc_cf_remove(struct platform_device *pdev)
153 {
154 component_del(&pdev->dev, &dc_cf_ops);
155 }
156
157 static const struct of_device_id dc_cf_dt_ids[] = {
158 { .compatible = "fsl,imx8qxp-dc-constframe" },
159 { /* sentinel */ }
160 };
161 MODULE_DEVICE_TABLE(of, dc_cf_dt_ids);
162
163 struct platform_driver dc_cf_driver = {
164 .probe = dc_cf_probe,
165 .remove = dc_cf_remove,
166 .driver = {
167 .name = "imx8-dc-constframe",
168 .suppress_bind_attrs = true,
169 .of_match_table = dc_cf_dt_ids,
170 },
171 };
172