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, struct drm_encoder *encoder, 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 return drm_bridge_attach(encoder, ldb_ch->next_bridge, bridge, 120 DRM_BRIDGE_ATTACH_NO_CONNECTOR); 121 } 122 EXPORT_SYMBOL_GPL(ldb_bridge_attach_helper); 123 124 int ldb_init_helper(struct ldb *ldb) 125 { 126 struct device *dev = ldb->dev; 127 struct device_node *np = dev->of_node; 128 struct device_node *child; 129 int ret; 130 u32 i; 131 132 ldb->regmap = syscon_node_to_regmap(np->parent); 133 if (IS_ERR(ldb->regmap)) { 134 ret = PTR_ERR(ldb->regmap); 135 if (ret != -EPROBE_DEFER) 136 DRM_DEV_ERROR(dev, "failed to get regmap: %d\n", ret); 137 return ret; 138 } 139 140 for_each_available_child_of_node(np, child) { 141 struct ldb_channel *ldb_ch; 142 143 ret = of_property_read_u32(child, "reg", &i); 144 if (ret || i > MAX_LDB_CHAN_NUM - 1) { 145 ret = -EINVAL; 146 DRM_DEV_ERROR(dev, 147 "invalid channel node address: %u\n", i); 148 of_node_put(child); 149 return ret; 150 } 151 152 ldb_ch = ldb->channel[i]; 153 ldb_ch->ldb = ldb; 154 ldb_ch->chno = i; 155 ldb_ch->is_available = true; 156 ldb_ch->np = child; 157 158 ldb->available_ch_cnt++; 159 } 160 161 return 0; 162 } 163 EXPORT_SYMBOL_GPL(ldb_init_helper); 164 165 int ldb_find_next_bridge_helper(struct ldb *ldb) 166 { 167 struct device *dev = ldb->dev; 168 struct ldb_channel *ldb_ch; 169 int ret, i; 170 171 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) { 172 ldb_ch = ldb->channel[i]; 173 174 if (!ldb_ch->is_available) 175 continue; 176 177 ldb_ch->next_bridge = devm_drm_of_get_bridge(dev, ldb_ch->np, 178 1, 0); 179 if (IS_ERR(ldb_ch->next_bridge)) { 180 ret = PTR_ERR(ldb_ch->next_bridge); 181 if (ret != -EPROBE_DEFER) 182 DRM_DEV_ERROR(dev, 183 "failed to get next bridge: %d\n", 184 ret); 185 return ret; 186 } 187 } 188 189 return 0; 190 } 191 EXPORT_SYMBOL_GPL(ldb_find_next_bridge_helper); 192 193 void ldb_add_bridge_helper(struct ldb *ldb) 194 { 195 struct ldb_channel *ldb_ch; 196 int i; 197 198 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) { 199 ldb_ch = ldb->channel[i]; 200 201 if (!ldb_ch->is_available) 202 continue; 203 204 ldb_ch->bridge.driver_private = ldb_ch; 205 ldb_ch->bridge.of_node = ldb_ch->np; 206 207 drm_bridge_add(&ldb_ch->bridge); 208 } 209 } 210 EXPORT_SYMBOL_GPL(ldb_add_bridge_helper); 211 212 void ldb_remove_bridge_helper(struct ldb *ldb) 213 { 214 struct ldb_channel *ldb_ch; 215 int i; 216 217 for (i = 0; i < MAX_LDB_CHAN_NUM; i++) { 218 ldb_ch = ldb->channel[i]; 219 220 if (!ldb_ch->is_available) 221 continue; 222 223 drm_bridge_remove(&ldb_ch->bridge); 224 } 225 } 226 EXPORT_SYMBOL_GPL(ldb_remove_bridge_helper); 227 228 MODULE_DESCRIPTION("i.MX8 LVDS Display Bridge(LDB)/Pixel Mapper bridge helper"); 229 MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>"); 230 MODULE_LICENSE("GPL"); 231