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