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