1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * (C) COPYRIGHT 2018 ARM Limited. All rights reserved. 4 * Author: James.Qian.Wang <james.qian.wang@arm.com> 5 * 6 */ 7 #include <linux/module.h> 8 #include <linux/kernel.h> 9 #include <linux/platform_device.h> 10 #include <linux/component.h> 11 #include <linux/pm_runtime.h> 12 #include <drm/drm_module.h> 13 #include <drm/drm_of.h> 14 #include "komeda_dev.h" 15 #include "komeda_kms.h" 16 17 struct komeda_drv { 18 struct komeda_dev *mdev; 19 struct komeda_kms_dev *kms; 20 }; 21 22 struct komeda_dev *dev_to_mdev(struct device *dev) 23 { 24 struct komeda_drv *mdrv = dev_get_drvdata(dev); 25 26 return mdrv ? mdrv->mdev : NULL; 27 } 28 29 static void komeda_unbind(struct device *dev) 30 { 31 struct komeda_drv *mdrv = dev_get_drvdata(dev); 32 33 if (!mdrv) 34 return; 35 36 komeda_kms_detach(mdrv->kms); 37 38 if (pm_runtime_enabled(dev)) 39 pm_runtime_disable(dev); 40 else 41 komeda_dev_suspend(mdrv->mdev); 42 43 komeda_dev_destroy(mdrv->mdev); 44 45 dev_set_drvdata(dev, NULL); 46 devm_kfree(dev, mdrv); 47 } 48 49 static int komeda_bind(struct device *dev) 50 { 51 struct komeda_drv *mdrv; 52 int err; 53 54 mdrv = devm_kzalloc(dev, sizeof(*mdrv), GFP_KERNEL); 55 if (!mdrv) 56 return -ENOMEM; 57 58 mdrv->mdev = komeda_dev_create(dev); 59 if (IS_ERR(mdrv->mdev)) { 60 err = PTR_ERR(mdrv->mdev); 61 goto free_mdrv; 62 } 63 64 pm_runtime_enable(dev); 65 if (!pm_runtime_enabled(dev)) 66 komeda_dev_resume(mdrv->mdev); 67 68 mdrv->kms = komeda_kms_attach(mdrv->mdev); 69 if (IS_ERR(mdrv->kms)) { 70 err = PTR_ERR(mdrv->kms); 71 goto destroy_mdev; 72 } 73 74 dev_set_drvdata(dev, mdrv); 75 76 return 0; 77 78 destroy_mdev: 79 if (pm_runtime_enabled(dev)) 80 pm_runtime_disable(dev); 81 else 82 komeda_dev_suspend(mdrv->mdev); 83 84 komeda_dev_destroy(mdrv->mdev); 85 86 free_mdrv: 87 devm_kfree(dev, mdrv); 88 return err; 89 } 90 91 static const struct component_master_ops komeda_master_ops = { 92 .bind = komeda_bind, 93 .unbind = komeda_unbind, 94 }; 95 96 static int compare_of(struct device *dev, void *data) 97 { 98 return dev->of_node == data; 99 } 100 101 static void komeda_add_slave(struct device *master, 102 struct component_match **match, 103 struct device_node *np, 104 u32 port, u32 endpoint) 105 { 106 struct device_node *remote; 107 108 remote = of_graph_get_remote_node(np, port, endpoint); 109 if (remote) { 110 drm_of_component_match_add(master, match, compare_of, remote); 111 of_node_put(remote); 112 } 113 } 114 115 static int komeda_platform_probe(struct platform_device *pdev) 116 { 117 struct device *dev = &pdev->dev; 118 struct component_match *match = NULL; 119 struct device_node *child; 120 121 if (!dev->of_node) 122 return -ENODEV; 123 124 for_each_available_child_of_node(dev->of_node, child) { 125 if (of_node_cmp(child->name, "pipeline") != 0) 126 continue; 127 128 /* add connector */ 129 komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT, 0); 130 komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT, 1); 131 } 132 133 return component_master_add_with_match(dev, &komeda_master_ops, match); 134 } 135 136 static int komeda_platform_remove(struct platform_device *pdev) 137 { 138 component_master_del(&pdev->dev, &komeda_master_ops); 139 return 0; 140 } 141 142 static const struct of_device_id komeda_of_match[] = { 143 { .compatible = "arm,mali-d71", .data = d71_identify, }, 144 { .compatible = "arm,mali-d32", .data = d71_identify, }, 145 {}, 146 }; 147 148 MODULE_DEVICE_TABLE(of, komeda_of_match); 149 150 static int __maybe_unused komeda_rt_pm_suspend(struct device *dev) 151 { 152 struct komeda_drv *mdrv = dev_get_drvdata(dev); 153 154 return komeda_dev_suspend(mdrv->mdev); 155 } 156 157 static int __maybe_unused komeda_rt_pm_resume(struct device *dev) 158 { 159 struct komeda_drv *mdrv = dev_get_drvdata(dev); 160 161 return komeda_dev_resume(mdrv->mdev); 162 } 163 164 static int __maybe_unused komeda_pm_suspend(struct device *dev) 165 { 166 struct komeda_drv *mdrv = dev_get_drvdata(dev); 167 int res; 168 169 res = drm_mode_config_helper_suspend(&mdrv->kms->base); 170 171 if (!pm_runtime_status_suspended(dev)) 172 komeda_dev_suspend(mdrv->mdev); 173 174 return res; 175 } 176 177 static int __maybe_unused komeda_pm_resume(struct device *dev) 178 { 179 struct komeda_drv *mdrv = dev_get_drvdata(dev); 180 181 if (!pm_runtime_status_suspended(dev)) 182 komeda_dev_resume(mdrv->mdev); 183 184 return drm_mode_config_helper_resume(&mdrv->kms->base); 185 } 186 187 static const struct dev_pm_ops komeda_pm_ops = { 188 SET_SYSTEM_SLEEP_PM_OPS(komeda_pm_suspend, komeda_pm_resume) 189 SET_RUNTIME_PM_OPS(komeda_rt_pm_suspend, komeda_rt_pm_resume, NULL) 190 }; 191 192 static struct platform_driver komeda_platform_driver = { 193 .probe = komeda_platform_probe, 194 .remove = komeda_platform_remove, 195 .driver = { 196 .name = "komeda", 197 .of_match_table = komeda_of_match, 198 .pm = &komeda_pm_ops, 199 }, 200 }; 201 202 drm_module_platform_driver(komeda_platform_driver); 203 204 MODULE_AUTHOR("James.Qian.Wang <james.qian.wang@arm.com>"); 205 MODULE_DESCRIPTION("Komeda KMS driver"); 206 MODULE_LICENSE("GPL v2"); 207