1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2012 Sascha Hauer, Pengutronix 4 * Copyright 2019,2020,2022 NXP 5 */ 6 7 #include <linux/export.h> 8 #include <linux/media-bus-format.h> 9 #include <linux/mfd/syscon.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/regmap.h> 13 14 #include <drm/drm_bridge.h> 15 #include <drm/drm_of.h> 16 #include <drm/drm_print.h> 17 18 #include "imx-ldb-helper.h" 19 20 bool ldb_channel_is_single_link(struct ldb_channel *ldb_ch) 21 { 22 return ldb_ch->link_type == LDB_CH_SINGLE_LINK; 23 } 24 EXPORT_SYMBOL_GPL(ldb_channel_is_single_link); 25 26 bool ldb_channel_is_split_link(struct ldb_channel *ldb_ch) 27 { 28 return ldb_ch->link_type == LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS || 29 ldb_ch->link_type == LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS; 30 } 31 EXPORT_SYMBOL_GPL(ldb_channel_is_split_link); 32 33 int ldb_bridge_atomic_check_helper(struct drm_bridge *bridge, 34 struct drm_bridge_state *bridge_state, 35 struct drm_crtc_state *crtc_state, 36 struct drm_connector_state *conn_state) 37 { 38 struct ldb_channel *ldb_ch = bridge->driver_private; 39 40 ldb_ch->in_bus_format = bridge_state->input_bus_cfg.format; 41 ldb_ch->out_bus_format = bridge_state->output_bus_cfg.format; 42 43 return 0; 44 } 45 EXPORT_SYMBOL_GPL(ldb_bridge_atomic_check_helper); 46 47 void ldb_bridge_mode_set_helper(struct drm_bridge *bridge, 48 const struct drm_display_mode *mode, 49 const struct drm_display_mode *adjusted_mode) 50 { 51 struct ldb_channel *ldb_ch = bridge->driver_private; 52 struct ldb *ldb = ldb_ch->ldb; 53 bool is_split = ldb_channel_is_split_link(ldb_ch); 54 55 if (is_split) 56 ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN; 57 58 switch (ldb_ch->out_bus_format) { 59 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG: 60 break; 61 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: 62 if (ldb_ch->chno == 0 || is_split) 63 ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24; 64 if (ldb_ch->chno == 1 || is_split) 65 ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24; 66 break; 67 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: 68 if (ldb_ch->chno == 0 || is_split) 69 ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 | 70 LDB_BIT_MAP_CH0_JEIDA; 71 if (ldb_ch->chno == 1 || is_split) 72 ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 | 73 LDB_BIT_MAP_CH1_JEIDA; 74 break; 75 } 76 } 77 EXPORT_SYMBOL_GPL(ldb_bridge_mode_set_helper); 78 79 void ldb_bridge_enable_helper(struct drm_bridge *bridge) 80 { 81 struct ldb_channel *ldb_ch = bridge->driver_private; 82 struct ldb *ldb = ldb_ch->ldb; 83 84 /* 85 * Platform specific bridge drivers should set ldb_ctrl properly 86 * for the enablement, so just write the ctrl_reg here. 87 */ 88 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl); 89 } 90 EXPORT_SYMBOL_GPL(ldb_bridge_enable_helper); 91 92 void ldb_bridge_disable_helper(struct drm_bridge *bridge) 93 { 94 struct ldb_channel *ldb_ch = bridge->driver_private; 95 struct ldb *ldb = ldb_ch->ldb; 96 bool is_split = ldb_channel_is_split_link(ldb_ch); 97 98 if (ldb_ch->chno == 0 || is_split) 99 ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK; 100 if (ldb_ch->chno == 1 || is_split) 101 ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK; 102 103 regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl); 104 } 105 EXPORT_SYMBOL_GPL(ldb_bridge_disable_helper); 106 107 int ldb_bridge_attach_helper(struct drm_bridge *bridge, 108 enum drm_bridge_attach_flags flags) 109 { 110 struct ldb_channel *ldb_ch = bridge->driver_private; 111 struct ldb *ldb = ldb_ch->ldb; 112 113 if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { 114 DRM_DEV_ERROR(ldb->dev, 115 "do not support creating a drm_connector\n"); 116 return -EINVAL; 117 } 118 119 if (!bridge->encoder) { 120 DRM_DEV_ERROR(ldb->dev, "missing encoder\n"); 121 return -ENODEV; 122 } 123 124 return drm_bridge_attach(bridge->encoder, 125 ldb_ch->next_bridge, bridge, 126 DRM_BRIDGE_ATTACH_NO_CONNECTOR); 127 } 128 EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper); 129 130 int ldb_init_helper(struct ldb *ldb) 131 { 132 struct device *dev = ldb->dev; 133 struct device_node *np = dev->of_node; 134 struct device_node *child; 135 int ret; 136 u32 i; 137 138 ldb->regmap = syscon_node_to_regmap(np->parent); 139 if (IS_ERR(ldb->regmap)) { 140 ret = PTR_ERR(ldb->regmap); 141 if (ret != -EPROBE_DEFER) 142 DRM_DEV_ERROR(dev, "failed to get regmap: %d\n", ret); 143 return ret; 144 } 145 146 for_each_available_child_of_node(np, child) { 147 struct ldb_channel *ldb_ch; 148 149 ret = of_property_read_u32(child, "reg", &i); 150 if (ret || i > MAX_LDB_CHAN_NUM - 1) { 151 ret = -EINVAL; 152 DRM_DEV_ERROR(dev, 153 "invalid channel node address: %u\n", i); 154 of_node_put(child); 155 return ret; 156 } 157 158 ldb_ch = ldb->channel[i]; 159 ldb_ch->ldb = ldb; 160 ldb_ch->chno = i; 161 ldb_ch->is_available = true; 162 ldb_ch->np = child; 163 164 ldb->available_ch_cnt++; 165 } 166 167 return 0; 168 } 169 EXPORT_SYMBOL_GPL(ldb_init_helper); 170 171 int ldb_find_next_bridge_helper(struct ldb *ldb) 172 { 173 struct device *dev = ldb->dev; 174 struct ldb_channel *ldb_ch; 175 int ret, i; 176 177 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) { 178 ldb_ch = ldb->channel[i]; 179 180 if (!ldb_ch->is_available) 181 continue; 182 183 ldb_ch->next_bridge = devm_drm_of_get_bridge(dev, ldb_ch->np, 184 1, 0); 185 if (IS_ERR(ldb_ch->next_bridge)) { 186 ret = PTR_ERR(ldb_ch->next_bridge); 187 if (ret != -EPROBE_DEFER) 188 DRM_DEV_ERROR(dev, 189 "failed to get next bridge: %d\n", 190 ret); 191 return ret; 192 } 193 } 194 195 return 0; 196 } 197 EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper); 198 199 void ldb_add_bridge_helper(struct ldb *ldb, 200 const struct drm_bridge_funcs *bridge_funcs) 201 { 202 struct ldb_channel *ldb_ch; 203 int i; 204 205 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) { 206 ldb_ch = ldb->channel[i]; 207 208 if (!ldb_ch->is_available) 209 continue; 210 211 ldb_ch->bridge.driver_private = ldb_ch; 212 ldb_ch->bridge.funcs = bridge_funcs; 213 ldb_ch->bridge.of_node = ldb_ch->np; 214 215 drm_bridge_add(&ldb_ch->bridge); 216 } 217 } 218 EXPORT_SYMBOL_GPL(ldb_add_bridge_helper); 219 220 void ldb_remove_bridge_helper(struct ldb *ldb) 221 { 222 struct ldb_channel *ldb_ch; 223 int i; 224 225 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) { 226 ldb_ch = ldb->channel[i]; 227 228 if (!ldb_ch->is_available) 229 continue; 230 231 drm_bridge_remove(&ldb_ch->bridge); 232 } 233 } 234 EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper); 235 236 MODULE_DESCRIPTION("i.MX8 LVDS Display Bridge(LDB)/Pixel Mapper bridge helper"); 237 MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>"); 238 MODULE_LICENSE("GPL"); 239