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 "dc-drv.h" 14 #include "dc-pe.h" 15 16 #define PIXENGCFG_STATIC 0x8 17 #define DIV_MASK GENMASK(23, 16) 18 #define DIV(x) FIELD_PREP(DIV_MASK, (x)) 19 #define DIV_RESET 0x80 20 #define SYNC_MODE BIT(8) 21 #define SINGLE 0 22 #define POWERDOWN BIT(4) 23 24 #define PIXENGCFG_DYNAMIC 0xc 25 26 #define PIXENGCFG_TRIGGER 0x14 27 #define SYNC_TRIGGER BIT(0) 28 29 #define STATICCONTROL 0x8 30 #define PERFCOUNTMODE BIT(12) 31 #define KICK_MODE BIT(8) 32 #define EXTERNAL BIT(8) 33 34 #define CONTROL 0xc 35 #define GAMMAAPPLYENABLE BIT(0) 36 37 static const struct dc_subdev_info dc_ed_info[] = { 38 { .reg_start = 0x56180980, .id = 0, }, 39 { .reg_start = 0x56180a00, .id = 1, }, 40 { .reg_start = 0x561809c0, .id = 4, }, 41 { .reg_start = 0x56180a40, .id = 5, }, 42 }; 43 44 static const struct regmap_range dc_ed_pec_regmap_write_ranges[] = { 45 regmap_reg_range(PIXENGCFG_STATIC, PIXENGCFG_STATIC), 46 regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), 47 regmap_reg_range(PIXENGCFG_TRIGGER, PIXENGCFG_TRIGGER), 48 }; 49 50 static const struct regmap_access_table dc_ed_pec_regmap_write_table = { 51 .yes_ranges = dc_ed_pec_regmap_write_ranges, 52 .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_write_ranges), 53 }; 54 55 static const struct regmap_range dc_ed_pec_regmap_read_ranges[] = { 56 regmap_reg_range(PIXENGCFG_STATIC, PIXENGCFG_STATIC), 57 regmap_reg_range(PIXENGCFG_DYNAMIC, PIXENGCFG_DYNAMIC), 58 }; 59 60 static const struct regmap_access_table dc_ed_pec_regmap_read_table = { 61 .yes_ranges = dc_ed_pec_regmap_read_ranges, 62 .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_read_ranges), 63 }; 64 65 static const struct regmap_range dc_ed_pec_regmap_volatile_ranges[] = { 66 regmap_reg_range(PIXENGCFG_TRIGGER, PIXENGCFG_TRIGGER), 67 }; 68 69 static const struct regmap_access_table dc_ed_pec_regmap_volatile_table = { 70 .yes_ranges = dc_ed_pec_regmap_volatile_ranges, 71 .n_yes_ranges = ARRAY_SIZE(dc_ed_pec_regmap_volatile_ranges), 72 }; 73 74 static const struct regmap_config dc_ed_pec_regmap_config = { 75 .name = "pec", 76 .reg_bits = 32, 77 .reg_stride = 4, 78 .val_bits = 32, 79 .fast_io = true, 80 .wr_table = &dc_ed_pec_regmap_write_table, 81 .rd_table = &dc_ed_pec_regmap_read_table, 82 .volatile_table = &dc_ed_pec_regmap_volatile_table, 83 .max_register = PIXENGCFG_TRIGGER, 84 }; 85 86 static const struct regmap_range dc_ed_regmap_ranges[] = { 87 regmap_reg_range(STATICCONTROL, STATICCONTROL), 88 regmap_reg_range(CONTROL, CONTROL), 89 }; 90 91 static const struct regmap_access_table dc_ed_regmap_access_table = { 92 .yes_ranges = dc_ed_regmap_ranges, 93 .n_yes_ranges = ARRAY_SIZE(dc_ed_regmap_ranges), 94 }; 95 96 static const struct regmap_config dc_ed_cfg_regmap_config = { 97 .name = "cfg", 98 .reg_bits = 32, 99 .reg_stride = 4, 100 .val_bits = 32, 101 .fast_io = true, 102 .wr_table = &dc_ed_regmap_access_table, 103 .rd_table = &dc_ed_regmap_access_table, 104 .max_register = CONTROL, 105 }; 106 107 static const enum dc_link_id src_sels[] = { 108 LINK_ID_NONE, 109 LINK_ID_CONSTFRAME0, 110 LINK_ID_CONSTFRAME1, 111 LINK_ID_CONSTFRAME4, 112 LINK_ID_CONSTFRAME5, 113 LINK_ID_LAYERBLEND3, 114 LINK_ID_LAYERBLEND2, 115 LINK_ID_LAYERBLEND1, 116 LINK_ID_LAYERBLEND0, 117 }; 118 119 static inline void dc_ed_pec_enable_shden(struct dc_ed *ed) 120 { 121 regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, SHDEN, SHDEN); 122 } 123 124 static inline void dc_ed_pec_poweron(struct dc_ed *ed) 125 { 126 regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, POWERDOWN, 0); 127 } 128 129 static inline void dc_ed_pec_sync_mode_single(struct dc_ed *ed) 130 { 131 regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, SYNC_MODE, SINGLE); 132 } 133 134 static inline void dc_ed_pec_div_reset(struct dc_ed *ed) 135 { 136 regmap_write_bits(ed->reg_pec, PIXENGCFG_STATIC, DIV_MASK, 137 DIV(DIV_RESET)); 138 } 139 140 void dc_ed_pec_src_sel(struct dc_ed *ed, enum dc_link_id src) 141 { 142 int i; 143 144 for (i = 0; i < ARRAY_SIZE(src_sels); i++) { 145 if (src_sels[i] == src) { 146 regmap_write(ed->reg_pec, PIXENGCFG_DYNAMIC, src); 147 return; 148 } 149 } 150 } 151 152 void dc_ed_pec_sync_trigger(struct dc_ed *ed) 153 { 154 regmap_write(ed->reg_pec, PIXENGCFG_TRIGGER, SYNC_TRIGGER); 155 } 156 157 static inline void dc_ed_enable_shden(struct dc_ed *ed) 158 { 159 regmap_write_bits(ed->reg_cfg, STATICCONTROL, SHDEN, SHDEN); 160 } 161 162 static inline void dc_ed_kick_mode_external(struct dc_ed *ed) 163 { 164 regmap_write_bits(ed->reg_cfg, STATICCONTROL, KICK_MODE, EXTERNAL); 165 } 166 167 static inline void dc_ed_disable_perfcountmode(struct dc_ed *ed) 168 { 169 regmap_write_bits(ed->reg_cfg, STATICCONTROL, PERFCOUNTMODE, 0); 170 } 171 172 static inline void dc_ed_disable_gamma_apply(struct dc_ed *ed) 173 { 174 regmap_write_bits(ed->reg_cfg, CONTROL, GAMMAAPPLYENABLE, 0); 175 } 176 177 void dc_ed_init(struct dc_ed *ed) 178 { 179 dc_ed_pec_src_sel(ed, LINK_ID_NONE); 180 dc_ed_pec_enable_shden(ed); 181 dc_ed_pec_poweron(ed); 182 dc_ed_pec_sync_mode_single(ed); 183 dc_ed_pec_div_reset(ed); 184 dc_ed_enable_shden(ed); 185 dc_ed_disable_perfcountmode(ed); 186 dc_ed_kick_mode_external(ed); 187 dc_ed_disable_gamma_apply(ed); 188 } 189 190 static int dc_ed_bind(struct device *dev, struct device *master, void *data) 191 { 192 struct platform_device *pdev = to_platform_device(dev); 193 struct dc_drm_device *dc_drm = data; 194 struct resource *res_pec; 195 void __iomem *base_pec; 196 void __iomem *base_cfg; 197 struct dc_ed *ed; 198 int id; 199 200 ed = devm_kzalloc(dev, sizeof(*ed), GFP_KERNEL); 201 if (!ed) 202 return -ENOMEM; 203 204 base_pec = devm_platform_get_and_ioremap_resource(pdev, 0, &res_pec); 205 if (IS_ERR(base_pec)) 206 return PTR_ERR(base_pec); 207 208 base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); 209 if (IS_ERR(base_cfg)) 210 return PTR_ERR(base_cfg); 211 212 ed->reg_pec = devm_regmap_init_mmio(dev, base_pec, 213 &dc_ed_pec_regmap_config); 214 if (IS_ERR(ed->reg_pec)) 215 return PTR_ERR(ed->reg_pec); 216 217 ed->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, 218 &dc_ed_cfg_regmap_config); 219 if (IS_ERR(ed->reg_cfg)) 220 return PTR_ERR(ed->reg_cfg); 221 222 ed->irq_shdload = platform_get_irq_byname(pdev, "shdload"); 223 if (ed->irq_shdload < 0) 224 return ed->irq_shdload; 225 226 ed->dev = dev; 227 228 id = dc_subdev_get_id(dc_ed_info, ARRAY_SIZE(dc_ed_info), res_pec); 229 if (id < 0) { 230 dev_err(dev, "failed to get instance number: %d\n", id); 231 return id; 232 } 233 234 switch (id) { 235 case 0: 236 dc_drm->ed_cont[0] = ed; 237 break; 238 case 1: 239 dc_drm->ed_cont[1] = ed; 240 break; 241 case 4: 242 dc_drm->ed_safe[0] = ed; 243 break; 244 case 5: 245 dc_drm->ed_safe[1] = ed; 246 break; 247 } 248 249 return 0; 250 } 251 252 static const struct component_ops dc_ed_ops = { 253 .bind = dc_ed_bind, 254 }; 255 256 static int dc_ed_probe(struct platform_device *pdev) 257 { 258 int ret; 259 260 ret = component_add(&pdev->dev, &dc_ed_ops); 261 if (ret) 262 return dev_err_probe(&pdev->dev, ret, 263 "failed to add component\n"); 264 265 return 0; 266 } 267 268 static void dc_ed_remove(struct platform_device *pdev) 269 { 270 component_del(&pdev->dev, &dc_ed_ops); 271 } 272 273 static const struct of_device_id dc_ed_dt_ids[] = { 274 { .compatible = "fsl,imx8qxp-dc-extdst" }, 275 { /* sentinel */ } 276 }; 277 MODULE_DEVICE_TABLE(of, dc_ed_dt_ids); 278 279 struct platform_driver dc_ed_driver = { 280 .probe = dc_ed_probe, 281 .remove = dc_ed_remove, 282 .driver = { 283 .name = "imx8-dc-extdst", 284 .suppress_bind_attrs = true, 285 .of_match_table = dc_ed_dt_ids, 286 }, 287 }; 288