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