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