1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2024 NXP 4 */ 5 6 #include <linux/component.h> 7 #include <linux/ioport.h> 8 #include <linux/module.h> 9 #include <linux/platform_device.h> 10 #include <linux/regmap.h> 11 12 #include <drm/drm_fourcc.h> 13 14 #include "dc-drv.h" 15 #include "dc-fu.h" 16 17 #define BASEADDRESS(x) (0x10 + FRAC_OFFSET * (x)) 18 #define SOURCEBUFFERATTRIBUTES(x) (0x14 + FRAC_OFFSET * (x)) 19 #define SOURCEBUFFERDIMENSION(x) (0x18 + FRAC_OFFSET * (x)) 20 #define COLORCOMPONENTBITS(x) (0x1c + FRAC_OFFSET * (x)) 21 #define COLORCOMPONENTSHIFT(x) (0x20 + FRAC_OFFSET * (x)) 22 #define LAYEROFFSET(x) (0x24 + FRAC_OFFSET * (x)) 23 #define CLIPWINDOWOFFSET(x) (0x28 + FRAC_OFFSET * (x)) 24 #define CLIPWINDOWDIMENSIONS(x) (0x2c + FRAC_OFFSET * (x)) 25 #define CONSTANTCOLOR(x) (0x30 + FRAC_OFFSET * (x)) 26 #define LAYERPROPERTY(x) (0x34 + FRAC_OFFSET * (x)) 27 #define FRAMEDIMENSIONS 0x150 28 29 struct dc_fl { 30 struct dc_fu fu; 31 }; 32 33 static const struct dc_subdev_info dc_fl_info[] = { 34 { .reg_start = 0x56180ac0, .id = 0, }, 35 }; 36 37 static const struct regmap_range dc_fl_regmap_ranges[] = { 38 regmap_reg_range(STATICCONTROL, FRAMEDIMENSIONS), 39 }; 40 41 static const struct regmap_access_table dc_fl_regmap_access_table = { 42 .yes_ranges = dc_fl_regmap_ranges, 43 .n_yes_ranges = ARRAY_SIZE(dc_fl_regmap_ranges), 44 }; 45 46 static const struct regmap_config dc_fl_cfg_regmap_config = { 47 .name = "cfg", 48 .reg_bits = 32, 49 .reg_stride = 4, 50 .val_bits = 32, 51 .fast_io = true, 52 .wr_table = &dc_fl_regmap_access_table, 53 .rd_table = &dc_fl_regmap_access_table, 54 .max_register = FRAMEDIMENSIONS, 55 }; 56 57 static void dc_fl_set_fmt(struct dc_fu *fu, enum dc_fu_frac frac, 58 const struct drm_format_info *format) 59 { 60 u32 bits = 0, shifts = 0; 61 62 dc_fu_set_src_bpp(fu, frac, format->cpp[0] * 8); 63 64 regmap_write_bits(fu->reg_cfg, LAYERPROPERTY(frac), 65 YUVCONVERSIONMODE_MASK, 66 YUVCONVERSIONMODE(YUVCONVERSIONMODE_OFF)); 67 68 dc_fu_get_pixel_format_bits(fu, format->format, &bits); 69 dc_fu_get_pixel_format_shifts(fu, format->format, &shifts); 70 71 regmap_write(fu->reg_cfg, COLORCOMPONENTBITS(frac), bits); 72 regmap_write(fu->reg_cfg, COLORCOMPONENTSHIFT(frac), shifts); 73 } 74 75 static void dc_fl_set_framedimensions(struct dc_fu *fu, int w, int h) 76 { 77 regmap_write(fu->reg_cfg, FRAMEDIMENSIONS, 78 FRAMEWIDTH(w) | FRAMEHEIGHT(h)); 79 } 80 81 static void dc_fl_init(struct dc_fu *fu) 82 { 83 dc_fu_common_hw_init(fu); 84 dc_fu_shdldreq_sticky(fu, 0xff); 85 } 86 87 static void dc_fl_set_ops(struct dc_fu *fu) 88 { 89 memcpy(&fu->ops, &dc_fu_common_ops, sizeof(dc_fu_common_ops)); 90 fu->ops.init = dc_fl_init; 91 fu->ops.set_fmt = dc_fl_set_fmt; 92 fu->ops.set_framedimensions = dc_fl_set_framedimensions; 93 } 94 95 static int dc_fl_bind(struct device *dev, struct device *master, void *data) 96 { 97 struct platform_device *pdev = to_platform_device(dev); 98 struct dc_drm_device *dc_drm = data; 99 struct resource *res_pec; 100 void __iomem *base_cfg; 101 struct dc_fl *fl; 102 struct dc_fu *fu; 103 int i, id; 104 105 fl = devm_kzalloc(dev, sizeof(*fl), GFP_KERNEL); 106 if (!fl) 107 return -ENOMEM; 108 109 fu = &fl->fu; 110 111 res_pec = platform_get_resource(pdev, IORESOURCE_MEM, 0); 112 113 base_cfg = devm_platform_ioremap_resource_byname(pdev, "cfg"); 114 if (IS_ERR(base_cfg)) 115 return PTR_ERR(base_cfg); 116 117 fu->reg_cfg = devm_regmap_init_mmio(dev, base_cfg, 118 &dc_fl_cfg_regmap_config); 119 if (IS_ERR(fu->reg_cfg)) 120 return PTR_ERR(fu->reg_cfg); 121 122 id = dc_subdev_get_id(dc_fl_info, ARRAY_SIZE(dc_fl_info), res_pec); 123 if (id < 0) { 124 dev_err(dev, "failed to get instance number: %d\n", id); 125 return id; 126 } 127 128 fu->link_id = LINK_ID_FETCHLAYER0; 129 fu->id = DC_FETCHUNIT_FL0; 130 for (i = 0; i < DC_FETCHUNIT_FRAC_NUM; i++) { 131 fu->reg_baseaddr[i] = BASEADDRESS(i); 132 fu->reg_sourcebufferattributes[i] = SOURCEBUFFERATTRIBUTES(i); 133 fu->reg_sourcebufferdimension[i] = SOURCEBUFFERDIMENSION(i); 134 fu->reg_layeroffset[i] = LAYEROFFSET(i); 135 fu->reg_clipwindowoffset[i] = CLIPWINDOWOFFSET(i); 136 fu->reg_clipwindowdimensions[i] = CLIPWINDOWDIMENSIONS(i); 137 fu->reg_constantcolor[i] = CONSTANTCOLOR(i); 138 fu->reg_layerproperty[i] = LAYERPROPERTY(i); 139 } 140 snprintf(fu->name, sizeof(fu->name), "FetchLayer%d", id); 141 142 dc_fl_set_ops(fu); 143 144 dc_drm->fu_disp[fu->id] = fu; 145 146 return 0; 147 } 148 149 static const struct component_ops dc_fl_ops = { 150 .bind = dc_fl_bind, 151 }; 152 153 static int dc_fl_probe(struct platform_device *pdev) 154 { 155 int ret; 156 157 ret = component_add(&pdev->dev, &dc_fl_ops); 158 if (ret) 159 return dev_err_probe(&pdev->dev, ret, 160 "failed to add component\n"); 161 162 return 0; 163 } 164 165 static void dc_fl_remove(struct platform_device *pdev) 166 { 167 component_del(&pdev->dev, &dc_fl_ops); 168 } 169 170 static const struct of_device_id dc_fl_dt_ids[] = { 171 { .compatible = "fsl,imx8qxp-dc-fetchlayer" }, 172 { /* sentinel */ } 173 }; 174 MODULE_DEVICE_TABLE(of, dc_fl_dt_ids); 175 176 struct platform_driver dc_fl_driver = { 177 .probe = dc_fl_probe, 178 .remove = dc_fl_remove, 179 .driver = { 180 .name = "imx8-dc-fetchlayer", 181 .suppress_bind_attrs = true, 182 .of_match_table = dc_fl_dt_ids, 183 }, 184 }; 185