1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2024 NXP 4 */ 5 6 #include <linux/bitfield.h> 7 #include <linux/bits.h> 8 #include <linux/component.h> 9 #include <linux/mod_devicetable.h> 10 #include <linux/module.h> 11 #include <linux/platform_device.h> 12 #include <linux/regmap.h> 13 14 #include <drm/drm_blend.h> 15 16 #include "dc-drv.h" 17 #include "dc-pe.h" 18 19 #define PIXENGCFG_DYNAMIC 0x8 20 #define PIXENGCFG_DYNAMIC_PRIM_SEL_MASK GENMASK(5, 0) 21 #define PIXENGCFG_DYNAMIC_PRIM_SEL(x) \ 22 FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x)) 23 #define PIXENGCFG_DYNAMIC_SEC_SEL_MASK GENMASK(13, 8) 24 #define PIXENGCFG_DYNAMIC_SEC_SEL(x) \ 25 FIELD_PREP(PIXENGCFG_DYNAMIC_SEC_SEL_MASK, (x)) 26 27 #define STATICCONTROL 0x8 28 #define SHDTOKSEL_MASK GENMASK(4, 3) 29 #define SHDTOKSEL(x) FIELD_PREP(SHDTOKSEL_MASK, (x)) 30 #define SHDLDSEL_MASK GENMASK(2, 1) 31 #define SHDLDSEL(x) FIELD_PREP(SHDLDSEL_MASK, (x)) 32 33 #define CONTROL 0xc 34 #define CTRL_MODE_MASK BIT(0) 35 #define CTRL_MODE(x) FIELD_PREP(CTRL_MODE_MASK, (x)) 36 37 #define BLENDCONTROL 0x10 38 #define ALPHA_MASK GENMASK(23, 16) 39 #define ALPHA(x) FIELD_PREP(ALPHA_MASK, (x)) 40 #define PRIM_C_BLD_FUNC_MASK GENMASK(2, 0) 41 #define PRIM_C_BLD_FUNC(x) \ 42 FIELD_PREP(PRIM_C_BLD_FUNC_MASK, (x)) 43 #define SEC_C_BLD_FUNC_MASK GENMASK(6, 4) 44 #define SEC_C_BLD_FUNC(x) \ 45 FIELD_PREP(SEC_C_BLD_FUNC_MASK, (x)) 46 #define PRIM_A_BLD_FUNC_MASK GENMASK(10, 8) 47 #define PRIM_A_BLD_FUNC(x) \ 48 FIELD_PREP(PRIM_A_BLD_FUNC_MASK, (x)) 49 #define SEC_A_BLD_FUNC_MASK GENMASK(14, 12) 50 #define SEC_A_BLD_FUNC(x) \ 51 FIELD_PREP(SEC_A_BLD_FUNC_MASK, (x)) 52 53 #define POSITION 0x14 54 #define XPOS_MASK GENMASK(15, 0) 55 #define XPOS(x) FIELD_PREP(XPOS_MASK, (x)) 56 #define YPOS_MASK GENMASK(31, 16) 57 #define YPOS(x) FIELD_PREP(YPOS_MASK, (x)) 58 59 enum dc_lb_blend_func { 60 DC_LAYERBLEND_BLEND_ZERO, 61 DC_LAYERBLEND_BLEND_ONE, 62 DC_LAYERBLEND_BLEND_PRIM_ALPHA, 63 DC_LAYERBLEND_BLEND_ONE_MINUS_PRIM_ALPHA, 64 DC_LAYERBLEND_BLEND_SEC_ALPHA, 65 DC_LAYERBLEND_BLEND_ONE_MINUS_SEC_ALPHA, 66 DC_LAYERBLEND_BLEND_CONST_ALPHA, 67 DC_LAYERBLEND_BLEND_ONE_MINUS_CONST_ALPHA, 68 }; 69 70 enum dc_lb_shadow_sel { 71 BOTH = 0x2, 72 }; 73 74 static const struct dc_subdev_info dc_lb_info[] = { 75 { .reg_start = 0x56180ba0, .id = 0, }, 76 { .reg_start = 0x56180bc0, .id = 1, }, 77 { .reg_start = 0x56180be0, .id = 2, }, 78 { .reg_start = 0x56180c00, .id = 3, }, 79 }; 80 81 static const struct regmap_range dc_lb_pec_regmap_access_ranges[] = { 82 regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), 83 }; 84 85 static const struct regmap_access_table dc_lb_pec_regmap_access_table = { 86 .yes_ranges = dc_lb_pec_regmap_access_ranges, 87 .n_yes_ranges = ARRAY_SIZE(dc_lb_pec_regmap_access_ranges), 88 }; 89 90 static const struct regmap_config dc_lb_pec_regmap_config = { 91 .name = "pec", 92 .reg_bits = 32, 93 .reg_stride = 4, 94 .val_bits = 32, 95 .fast_io = true, 96 .wr_table = &dc_lb_pec_regmap_access_table, 97 .rd_table = &dc_lb_pec_regmap_access_table, 98 .max_register = PIXENGCFG_DYNAMIC, 99 }; 100 101 static const struct regmap_range dc_lb_regmap_ranges[] = { 102 regmap_reg_range(STATICCONTROL, POSITION), 103 }; 104 105 static const struct regmap_access_table dc_lb_regmap_access_table = { 106 .yes_ranges = dc_lb_regmap_ranges, 107 .n_yes_ranges = ARRAY_SIZE(dc_lb_regmap_ranges), 108 }; 109 110 static const struct regmap_config dc_lb_cfg_regmap_config = { 111 .name = "cfg", 112 .reg_bits = 32, 113 .reg_stride = 4, 114 .val_bits = 32, 115 .fast_io = true, 116 .wr_table = &dc_lb_regmap_access_table, 117 .rd_table = &dc_lb_regmap_access_table, 118 .max_register = POSITION, 119 }; 120 121 static const enum dc_link_id prim_sels[] = { 122 /* common options */ 123 LINK_ID_NONE, 124 LINK_ID_CONSTFRAME0, 125 LINK_ID_CONSTFRAME1, 126 LINK_ID_CONSTFRAME4, 127 LINK_ID_CONSTFRAME5, 128 /* 129 * special options: 130 * layerblend(n) has n special options, 131 * from layerblend0 to layerblend(n - 1), e.g., 132 * layerblend3 has 3 special options - 133 * layerblend0/1/2. 134 */ 135 LINK_ID_LAYERBLEND0, 136 LINK_ID_LAYERBLEND1, 137 LINK_ID_LAYERBLEND2, 138 LINK_ID_LAYERBLEND3, 139 }; 140 141 static const enum dc_link_id sec_sels[] = { 142 LINK_ID_NONE, 143 LINK_ID_FETCHWARP2, 144 LINK_ID_FETCHLAYER0, 145 }; 146 147 enum dc_link_id dc_lb_get_link_id(struct dc_lb *lb) 148 { 149 return lb->link; 150 } 151 152 void dc_lb_pec_dynamic_prim_sel(struct dc_lb *lb, enum dc_link_id prim) 153 { 154 int fixed_sels_num = ARRAY_SIZE(prim_sels) - 4; 155 int i; 156 157 for (i = 0; i < fixed_sels_num + lb->id; i++) { 158 if (prim_sels[i] == prim) { 159 regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, 160 PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, 161 PIXENGCFG_DYNAMIC_PRIM_SEL(prim)); 162 return; 163 } 164 } 165 166 dev_warn(lb->dev, "invalid primary input selection:%d\n", prim); 167 } 168 169 void dc_lb_pec_dynamic_sec_sel(struct dc_lb *lb, enum dc_link_id sec) 170 { 171 int i; 172 173 for (i = 0; i < ARRAY_SIZE(sec_sels); i++) { 174 if (sec_sels[i] == sec) { 175 regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, 176 PIXENGCFG_DYNAMIC_SEC_SEL_MASK, 177 PIXENGCFG_DYNAMIC_SEC_SEL(sec)); 178 return; 179 } 180 } 181 182 dev_warn(lb->dev, "invalid secondary input selection:%d\n", sec); 183 } 184 185 void dc_lb_pec_clken(struct dc_lb *lb, enum dc_pec_clken clken) 186 { 187 regmap_write_bits(lb->reg_pec, PIXENGCFG_DYNAMIC, CLKEN_MASK, 188 CLKEN(clken)); 189 } 190 191 static inline void dc_lb_enable_shden(struct dc_lb *lb) 192 { 193 regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDEN, SHDEN); 194 } 195 196 static inline void dc_lb_shdtoksel(struct dc_lb *lb, enum dc_lb_shadow_sel sel) 197 { 198 regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDTOKSEL_MASK, 199 SHDTOKSEL(sel)); 200 } 201 202 static inline void dc_lb_shdldsel(struct dc_lb *lb, enum dc_lb_shadow_sel sel) 203 { 204 regmap_write_bits(lb->reg_cfg, STATICCONTROL, SHDLDSEL_MASK, 205 SHDLDSEL(sel)); 206 } 207 208 void dc_lb_mode(struct dc_lb *lb, enum dc_lb_mode mode) 209 { 210 regmap_write_bits(lb->reg_cfg, CONTROL, CTRL_MODE_MASK, mode); 211 } 212 213 static inline void dc_lb_blendcontrol(struct dc_lb *lb) 214 { 215 u32 val = PRIM_A_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | 216 SEC_A_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | 217 PRIM_C_BLD_FUNC(DC_LAYERBLEND_BLEND_ZERO) | 218 SEC_C_BLD_FUNC(DC_LAYERBLEND_BLEND_CONST_ALPHA) | 219 ALPHA(DRM_BLEND_ALPHA_OPAQUE >> 8); 220 221 regmap_write(lb->reg_cfg, BLENDCONTROL, val); 222 } 223 224 void dc_lb_position(struct dc_lb *lb, int x, int y) 225 { 226 regmap_write(lb->reg_cfg, POSITION, XPOS(x) | YPOS(y)); 227 } 228 229 int dc_lb_get_id(struct dc_lb *lb) 230 { 231 return lb->id; 232 } 233 234 void dc_lb_init(struct dc_lb *lb) 235 { 236 dc_lb_pec_dynamic_prim_sel(lb, LINK_ID_NONE); 237 dc_lb_pec_dynamic_sec_sel(lb, LINK_ID_NONE); 238 dc_lb_pec_clken(lb, CLKEN_DISABLE); 239 dc_lb_shdldsel(lb, BOTH); 240 dc_lb_shdtoksel(lb, BOTH); 241 dc_lb_blendcontrol(lb); 242 dc_lb_enable_shden(lb); 243 } 244 245 static int dc_lb_bind(struct device *dev, struct device *master, void *data) 246 { 247 struct platform_device *pdev = to_platform_device(dev); 248 struct dc_drm_device *dc_drm = data; 249 struct resource *res_pec; 250 void __iomem *base_pec; 251 void __iomem *base_cfg; 252 struct dc_lb *lb; 253 254 lb = devm_kzalloc(dev, sizeof(*lb), GFP_KERNEL); 255 if (!lb) 256 return -ENOMEM; 257 258 base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec); 259 if (IS_ERR(base_pec)) 260 return PTR_ERR(base_pec); 261 262 base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); 263 if (IS_ERR(base_cfg)) 264 return PTR_ERR(base_cfg); 265 266 lb->reg_pec = devm_regmap_init_mmio(dev, base_pec, 267 &dc_lb_pec_regmap_config); 268 if (IS_ERR(lb->reg_pec)) 269 return PTR_ERR(lb->reg_pec); 270 271 lb->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, 272 &dc_lb_cfg_regmap_config); 273 if (IS_ERR(lb->reg_cfg)) 274 return PTR_ERR(lb->reg_cfg); 275 276 lb->id = dc_subdev_get_id(dc_lb_info, ARRAY_SIZE(dc_lb_info), res_pec); 277 if (lb->id < 0) { 278 dev_err(dev, "failed to get instance number: %d\n", lb->id); 279 return lb->id; 280 } 281 282 lb->dev = dev; 283 lb->link = LINK_ID_LAYERBLEND0 + lb->id; 284 285 dc_drm->lb[lb->id] = lb; 286 287 return 0; 288 } 289 290 static const struct component_ops dc_lb_ops = { 291 .bind = dc_lb_bind, 292 }; 293 294 static int dc_lb_probe(struct platform_device *pdev) 295 { 296 int ret; 297 298 ret = component_add(&pdev->dev, &dc_lb_ops); 299 if (ret) 300 return dev_err_probe(&pdev->dev, ret, 301 "failed to add component\n"); 302 303 return 0; 304 } 305 306 static void dc_lb_remove(struct platform_device *pdev) 307 { 308 component_del(&pdev->dev, &dc_lb_ops); 309 } 310 311 static const struct of_device_id dc_lb_dt_ids[] = { 312 { .compatible = "fsl,imx8qxp-dc-layerblend" }, 313 { /* sentinel */ } 314 }; 315 MODULE_DEVICE_TABLE(of, dc_lb_dt_ids); 316 317 struct platform_driver dc_lb_driver = { 318 .probe = dc_lb_probe, 319 .remove = dc_lb_remove, 320 .driver = { 321 .name = "imx8-dc-layerblend", 322 .suppress_bind_attrs = true, 323 .of_match_table = dc_lb_dt_ids, 324 }, 325 }; 326