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