xref: /linux/drivers/gpu/drm/bridge/imx/imx93-pdfc.c (revision 4e69c1856bfd9ffb7e9d335a25842fa211628929)
1 // SPDX-License-Identifier: GPL-2.0+
2 
3 /*
4  * Copyright 2022-2025 NXP
5  */
6 
7 #include <linux/bitfield.h>
8 #include <linux/bits.h>
9 #include <linux/media-bus-format.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/of_graph.h>
14 #include <linux/platform_device.h>
15 #include <linux/regmap.h>
16 
17 #include <drm/drm_atomic_state_helper.h>
18 #include <drm/drm_bridge.h>
19 
20 #define IMX93_DISPLAY_MUX_REG		0x60
21 #define PARALLEL_DISP_FORMAT		GENMASK(10, 8)
22 #define FORMAT_RGB888_TO_RGB888		FIELD_PREP(PARALLEL_DISP_FORMAT, 0)
23 #define FORMAT_RGB888_TO_RGB666		FIELD_PREP(PARALLEL_DISP_FORMAT, 1)
24 #define FORMAT_RGB565_TO_RGB565		FIELD_PREP(PARALLEL_DISP_FORMAT, 2)
25 
26 struct imx93_pdfc {
27 	struct drm_bridge bridge;
28 	struct device *dev;
29 	struct regmap *regmap;
30 	u32 phy_bus_width;
31 };
32 
33 static struct imx93_pdfc *bridge_to_imx93_pdfc(struct drm_bridge *bridge)
34 {
35 	return container_of(bridge, struct imx93_pdfc, bridge);
36 }
37 
38 static int
39 imx93_pdfc_bridge_attach(struct drm_bridge *bridge, struct drm_encoder *encoder,
40 			 enum drm_bridge_attach_flags flags)
41 {
42 	return drm_bridge_attach(bridge->encoder, bridge->next_bridge, bridge, flags);
43 }
44 
45 static void imx93_pdfc_bridge_atomic_enable(struct drm_bridge *bridge,
46 					    struct drm_atomic_commit *state)
47 {
48 	struct imx93_pdfc *pdfc = bridge_to_imx93_pdfc(bridge);
49 	const struct drm_bridge_state *bridge_state;
50 	unsigned int mask = PARALLEL_DISP_FORMAT;
51 	unsigned int val;
52 
53 	bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
54 
55 	switch (bridge_state->output_bus_cfg.format) {
56 	case MEDIA_BUS_FMT_RGB888_1X24:
57 	case MEDIA_BUS_FMT_FIXED:
58 		val = FORMAT_RGB888_TO_RGB888;
59 		if (pdfc->phy_bus_width == 18) {
60 			/*
61 			 * Can be valid if physical bus limitation exists,
62 			 * therefore use dev_dbg().
63 			 */
64 			dev_dbg(pdfc->dev, "Truncate two LSBs from each color\n");
65 			val = FORMAT_RGB888_TO_RGB666;
66 		}
67 		break;
68 	case MEDIA_BUS_FMT_RGB666_1X18:
69 		val = FORMAT_RGB888_TO_RGB666;
70 		break;
71 	case MEDIA_BUS_FMT_RGB565_1X16:
72 		val = FORMAT_RGB565_TO_RGB565;
73 		break;
74 	}
75 
76 	regmap_update_bits(pdfc->regmap, IMX93_DISPLAY_MUX_REG, mask, val);
77 }
78 
79 /* TODO: Add YUV formats */
80 static const u32 imx93_pdfc_bus_output_fmts[] = {
81 	MEDIA_BUS_FMT_FIXED,
82 	MEDIA_BUS_FMT_RGB888_1X24,
83 	MEDIA_BUS_FMT_RGB666_1X18,
84 	MEDIA_BUS_FMT_RGB565_1X16,
85 };
86 
87 static bool imx93_pdfc_bus_output_fmt_supported(u32 fmt)
88 {
89 	int i;
90 
91 	for (i = 0; i < ARRAY_SIZE(imx93_pdfc_bus_output_fmts); i++) {
92 		if (imx93_pdfc_bus_output_fmts[i] == fmt)
93 			return true;
94 	}
95 
96 	return false;
97 }
98 
99 static u32 *
100 imx93_pdfc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
101 					    struct drm_bridge_state *bridge_state,
102 					    struct drm_crtc_state *crtc_state,
103 					    struct drm_connector_state *conn_state,
104 					    u32 output_fmt,
105 					    unsigned int *num_input_fmts)
106 {
107 	u32 *input_fmts;
108 
109 	*num_input_fmts = 0;
110 
111 	if (!imx93_pdfc_bus_output_fmt_supported(output_fmt))
112 		return NULL;
113 
114 	input_fmts = kmalloc_obj(*input_fmts);
115 	if (!input_fmts)
116 		return NULL;
117 
118 	*num_input_fmts = 1;
119 
120 	switch (output_fmt) {
121 	case MEDIA_BUS_FMT_RGB888_1X24:
122 	case MEDIA_BUS_FMT_RGB565_1X16:
123 		input_fmts[0] = output_fmt;
124 		break;
125 	case MEDIA_BUS_FMT_RGB666_1X18:
126 	case MEDIA_BUS_FMT_FIXED:
127 		input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
128 		break;
129 	}
130 
131 	return input_fmts;
132 }
133 
134 static int imx93_pdfc_bridge_atomic_check(struct drm_bridge *bridge,
135 					  struct drm_bridge_state *bridge_state,
136 					  struct drm_crtc_state *crtc_state,
137 					  struct drm_connector_state *conn_state)
138 {
139 	struct imx93_pdfc *pdfc = bridge_to_imx93_pdfc(bridge);
140 	u32 format = bridge_state->output_bus_cfg.format;
141 
142 	if (imx93_pdfc_bus_output_fmt_supported(format))
143 		return 0;
144 
145 	dev_warn(pdfc->dev, "Unsupported output bus format: 0x%x\n", format);
146 
147 	return -EINVAL;
148 }
149 
150 static const struct drm_bridge_funcs funcs = {
151 	.attach			= imx93_pdfc_bridge_attach,
152 	.atomic_enable		= imx93_pdfc_bridge_atomic_enable,
153 	.atomic_duplicate_state	= drm_atomic_helper_bridge_duplicate_state,
154 	.atomic_destroy_state	= drm_atomic_helper_bridge_destroy_state,
155 	.atomic_get_input_bus_fmts	= imx93_pdfc_bridge_atomic_get_input_bus_fmts,
156 	.atomic_check		= imx93_pdfc_bridge_atomic_check,
157 	.atomic_create_state	= drm_atomic_helper_bridge_create_state,
158 };
159 
160 static int imx93_pdfc_bridge_probe(struct platform_device *pdev)
161 {
162 	struct device *dev = &pdev->dev;
163 	struct drm_bridge *next_bridge;
164 	struct imx93_pdfc *pdfc;
165 	struct device_node *ep;
166 	int err;
167 
168 	pdfc = devm_drm_bridge_alloc(dev, struct imx93_pdfc, bridge, &funcs);
169 	if (IS_ERR(pdfc))
170 		return PTR_ERR(pdfc);
171 
172 	pdfc->regmap = syscon_node_to_regmap(dev->of_node->parent);
173 	if (IS_ERR(pdfc->regmap))
174 		return dev_err_probe(dev, PTR_ERR(pdfc->regmap),
175 				     "failed to get regmap\n");
176 
177 	/* No limits per default */
178 	pdfc->phy_bus_width = 24;
179 
180 	/* Get output ep (port1/endpoint) */
181 	ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
182 	if (ep) {
183 		err = of_property_read_u32(ep, "bus-width", &pdfc->phy_bus_width);
184 		of_node_put(ep);
185 
186 		/* bus-width is optional but it must have valid data if present */
187 		if (err && err != -EINVAL)
188 			return dev_err_probe(dev, err,
189 					     "failed to query bus-width\n");
190 	}
191 
192 	next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
193 	if (IS_ERR(next_bridge))
194 		return dev_err_probe(dev, PTR_ERR(next_bridge),
195 				     "failed to get next bridge\n");
196 	pdfc->dev = dev;
197 	pdfc->bridge.of_node = dev->of_node;
198 	pdfc->bridge.type = DRM_MODE_CONNECTOR_DPI;
199 	pdfc->bridge.next_bridge = drm_bridge_get(next_bridge);
200 
201 	return devm_drm_bridge_add(dev, &pdfc->bridge);
202 }
203 
204 static const struct of_device_id imx93_pdfc_dt_ids[] = {
205 	{ .compatible = "nxp,imx91-pdfc", },
206 	{ .compatible = "nxp,imx93-pdfc", },
207 	{ /* sentinel */ }
208 };
209 MODULE_DEVICE_TABLE(of, imx93_pdfc_dt_ids);
210 
211 static struct platform_driver imx93_pdfc_bridge_driver = {
212 	.probe	= imx93_pdfc_bridge_probe,
213 	.driver	= {
214 		.of_match_table = imx93_pdfc_dt_ids,
215 		.name = "imx93_pdfc",
216 	},
217 };
218 module_platform_driver(imx93_pdfc_bridge_driver);
219 
220 MODULE_DESCRIPTION("NXP i.MX93 parallel display format configuration driver");
221 MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
222 MODULE_LICENSE("GPL");
223