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