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