1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * RZ/G2L Display Unit DRM driver 4 * 5 * Copyright (C) 2023 Renesas Electronics Corporation 6 * 7 * Based on rcar_du_drv.c 8 */ 9 10 #include <linux/dma-mapping.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 15 #include <drm/clients/drm_client_setup.h> 16 #include <drm/drm_atomic_helper.h> 17 #include <drm/drm_drv.h> 18 #include <drm/drm_fbdev_dma.h> 19 #include <drm/drm_gem_dma_helper.h> 20 #include <drm/drm_print.h> 21 #include <drm/drm_probe_helper.h> 22 23 #include "rzg2l_du_drv.h" 24 #include "rzg2l_du_kms.h" 25 26 /* ----------------------------------------------------------------------------- 27 * Device Information 28 */ 29 30 static const struct rzg2l_du_device_info rzg2l_du_r9a07g043u_info = { 31 .channels_mask = BIT(0), 32 .routes = { 33 [RZG2L_DU_OUTPUT_DPAD0] = { 34 .possible_outputs = BIT(0), 35 .port = 0, 36 }, 37 }, 38 }; 39 40 static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = { 41 .channels_mask = BIT(0), 42 .routes = { 43 [RZG2L_DU_OUTPUT_DSI0] = { 44 .possible_outputs = BIT(0), 45 .port = 0, 46 }, 47 [RZG2L_DU_OUTPUT_DPAD0] = { 48 .possible_outputs = BIT(0), 49 .port = 1, 50 } 51 } 52 }; 53 54 static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = { 55 .channels_mask = BIT(0), 56 .routes = { 57 [RZG2L_DU_OUTPUT_DSI0] = { 58 .possible_outputs = BIT(0), 59 .port = 0, 60 }, 61 }, 62 }; 63 64 static const struct of_device_id rzg2l_du_of_table[] = { 65 { .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info }, 66 { .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info }, 67 { .compatible = "renesas,r9a09g057-du", .data = &rzg2l_du_r9a09g057_info }, 68 { /* sentinel */ } 69 }; 70 71 MODULE_DEVICE_TABLE(of, rzg2l_du_of_table); 72 73 const char *rzg2l_du_output_name(enum rzg2l_du_output output) 74 { 75 static const char * const names[] = { 76 [RZG2L_DU_OUTPUT_DSI0] = "DSI0", 77 [RZG2L_DU_OUTPUT_DPAD0] = "DPAD0" 78 }; 79 80 if (output >= ARRAY_SIZE(names)) 81 return "UNKNOWN"; 82 83 return names[output]; 84 } 85 86 /* ----------------------------------------------------------------------------- 87 * DRM operations 88 */ 89 90 DEFINE_DRM_GEM_DMA_FOPS(rzg2l_du_fops); 91 92 static const struct drm_driver rzg2l_du_driver = { 93 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 94 DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(rzg2l_du_dumb_create), 95 DRM_FBDEV_DMA_DRIVER_OPS, 96 .fops = &rzg2l_du_fops, 97 .name = "rzg2l-du", 98 .desc = "Renesas RZ/G2L Display Unit", 99 .major = 1, 100 .minor = 0, 101 }; 102 103 /* ----------------------------------------------------------------------------- 104 * Platform driver 105 */ 106 107 static void rzg2l_du_remove(struct platform_device *pdev) 108 { 109 struct rzg2l_du_device *rcdu = platform_get_drvdata(pdev); 110 struct drm_device *ddev = &rcdu->ddev; 111 112 drm_dev_unregister(ddev); 113 drm_atomic_helper_shutdown(ddev); 114 115 drm_kms_helper_poll_fini(ddev); 116 } 117 118 static void rzg2l_du_shutdown(struct platform_device *pdev) 119 { 120 struct rzg2l_du_device *rcdu = platform_get_drvdata(pdev); 121 122 drm_atomic_helper_shutdown(&rcdu->ddev); 123 } 124 125 static int rzg2l_du_probe(struct platform_device *pdev) 126 { 127 struct rzg2l_du_device *rcdu; 128 int ret; 129 130 if (drm_firmware_drivers_only()) 131 return -ENODEV; 132 133 /* Allocate and initialize the RZ/G2L device structure. */ 134 rcdu = devm_drm_dev_alloc(&pdev->dev, &rzg2l_du_driver, 135 struct rzg2l_du_device, ddev); 136 if (IS_ERR(rcdu)) 137 return PTR_ERR(rcdu); 138 139 rcdu->dev = &pdev->dev; 140 rcdu->info = of_device_get_match_data(rcdu->dev); 141 142 platform_set_drvdata(pdev, rcdu); 143 144 /* I/O resources */ 145 rcdu->mmio = devm_platform_ioremap_resource(pdev, 0); 146 if (IS_ERR(rcdu->mmio)) 147 return PTR_ERR(rcdu->mmio); 148 149 ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 150 if (ret) 151 return ret; 152 153 /* DRM/KMS objects */ 154 ret = rzg2l_du_modeset_init(rcdu); 155 if (ret < 0) { 156 /* 157 * Don't use dev_err_probe(), as it would overwrite the probe 158 * deferral reason recorded in rzg2l_du_modeset_init(). 159 */ 160 if (ret != -EPROBE_DEFER) 161 dev_err(&pdev->dev, 162 "failed to initialize DRM/KMS (%d)\n", ret); 163 goto error; 164 } 165 166 /* 167 * Register the DRM device with the core and the connectors with 168 * sysfs. 169 */ 170 ret = drm_dev_register(&rcdu->ddev, 0); 171 if (ret) 172 goto error; 173 174 drm_info(&rcdu->ddev, "Device %s probed\n", dev_name(&pdev->dev)); 175 176 drm_client_setup(&rcdu->ddev, NULL); 177 178 return 0; 179 180 error: 181 drm_kms_helper_poll_fini(&rcdu->ddev); 182 return ret; 183 } 184 185 static struct platform_driver rzg2l_du_platform_driver = { 186 .probe = rzg2l_du_probe, 187 .remove = rzg2l_du_remove, 188 .shutdown = rzg2l_du_shutdown, 189 .driver = { 190 .name = "rzg2l-du", 191 .of_match_table = rzg2l_du_of_table, 192 }, 193 }; 194 195 module_platform_driver(rzg2l_du_platform_driver); 196 197 MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); 198 MODULE_DESCRIPTION("Renesas RZ/G2L Display Unit DRM Driver"); 199 MODULE_LICENSE("GPL"); 200