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