1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2024 NXP 4 */ 5 6 #include <linux/component.h> 7 #include <linux/mod_devicetable.h> 8 #include <linux/module.h> 9 #include <linux/platform_device.h> 10 #include <linux/regmap.h> 11 12 #include "dc-drv.h" 13 #include "dc-de.h" 14 15 #define TCON_CTRL 0x410 16 #define CTRL_RST_VAL 0x01401408 17 18 /* red: MAPBIT 29-20, green: MAPBIT 19-10, blue: MAPBIT 9-0 */ 19 #define MAPBIT3_0 0x418 20 #define MAPBIT7_4 0x41c 21 #define MAPBIT11_8 0x420 22 #define MAPBIT15_12 0x424 23 #define MAPBIT19_16 0x428 24 #define MAPBIT23_20 0x42c 25 #define MAPBIT27_24 0x430 26 #define MAPBIT31_28 0x434 27 28 static const struct dc_subdev_info dc_tc_info[] = { 29 { .reg_start = 0x5618c800, .id = 0, }, 30 { .reg_start = 0x5618e400, .id = 1, }, 31 }; 32 33 static const struct regmap_range dc_tc_regmap_ranges[] = { 34 regmap_reg_range(TCON_CTRL, TCON_CTRL), 35 regmap_reg_range(MAPBIT3_0, MAPBIT31_28), 36 }; 37 38 static const struct regmap_access_table dc_tc_regmap_access_table = { 39 .yes_ranges = dc_tc_regmap_ranges, 40 .n_yes_ranges = ARRAY_SIZE(dc_tc_regmap_ranges), 41 }; 42 43 static const struct regmap_config dc_tc_regmap_config = { 44 .reg_bits = 32, 45 .reg_stride = 4, 46 .val_bits = 32, 47 .fast_io = true, 48 .wr_table = &dc_tc_regmap_access_table, 49 .rd_table = &dc_tc_regmap_access_table, 50 .max_register = MAPBIT31_28, 51 }; 52 53 /* 54 * The pixels reach TCON are always in 30-bit BGR format. 55 * The first bridge always receives pixels in 30-bit RGB format. 56 * So, map the format to MEDIA_BUS_FMT_RGB101010_1X30. 57 */ 58 static const u32 dc_tc_mapbit[] = { 59 0x17161514, 0x1b1a1918, 0x0b0a1d1c, 0x0f0e0d0c, 60 0x13121110, 0x03020100, 0x07060504, 0x00000908, 61 }; 62 63 void dc_tc_init(struct dc_tc *tc) 64 { 65 /* reset TCON_CTRL to POR default so that TCON works in bypass mode */ 66 regmap_write(tc->reg, TCON_CTRL, CTRL_RST_VAL); 67 68 /* set format */ 69 regmap_bulk_write(tc->reg, MAPBIT3_0, dc_tc_mapbit, 70 ARRAY_SIZE(dc_tc_mapbit)); 71 } 72 73 static int dc_tc_bind(struct device *dev, struct device *master, void *data) 74 { 75 struct platform_device *pdev = to_platform_device(dev); 76 struct dc_drm_device *dc_drm = data; 77 struct resource *res; 78 void __iomem *base; 79 struct dc_tc *tc; 80 int id; 81 82 tc = devm_kzalloc(dev, sizeof(*tc), GFP_KERNEL); 83 if (!tc) 84 return -ENOMEM; 85 86 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 87 if (IS_ERR(base)) 88 return PTR_ERR(base); 89 90 tc->reg = devm_regmap_init_mmio(dev, base, &dc_tc_regmap_config); 91 if (IS_ERR(tc->reg)) 92 return PTR_ERR(tc->reg); 93 94 id = dc_subdev_get_id(dc_tc_info, ARRAY_SIZE(dc_tc_info), res); 95 if (id < 0) { 96 dev_err(dev, "failed to get instance number: %d\n", id); 97 return id; 98 } 99 100 tc->dev = dev; 101 dc_drm->tc[id] = tc; 102 103 return 0; 104 } 105 106 static const struct component_ops dc_tc_ops = { 107 .bind = dc_tc_bind, 108 }; 109 110 static int dc_tc_probe(struct platform_device *pdev) 111 { 112 int ret; 113 114 ret = component_add(&pdev->dev, &dc_tc_ops); 115 if (ret) 116 return dev_err_probe(&pdev->dev, ret, 117 "failed to add component\n"); 118 119 return 0; 120 } 121 122 static void dc_tc_remove(struct platform_device *pdev) 123 { 124 component_del(&pdev->dev, &dc_tc_ops); 125 } 126 127 static const struct of_device_id dc_tc_dt_ids[] = { 128 { .compatible = "fsl,imx8qxp-dc-tcon" }, 129 { /* sentinel */ } 130 }; 131 MODULE_DEVICE_TABLE(of, dc_tc_dt_ids); 132 133 struct platform_driver dc_tc_driver = { 134 .probe = dc_tc_probe, 135 .remove = dc_tc_remove, 136 .driver = { 137 .name = "imx8-dc-tcon", 138 .suppress_bind_attrs = true, 139 .of_match_table = dc_tc_dt_ids, 140 }, 141 }; 142