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 <drm/drm_of.h> 12 #include "komeda_dev.h" 13 #include "komeda_kms.h" 14 15 struct komeda_drv { 16 struct komeda_dev *mdev; 17 struct komeda_kms_dev *kms; 18 }; 19 20 struct komeda_dev *dev_to_mdev(struct device *dev) 21 { 22 struct komeda_drv *mdrv = dev_get_drvdata(dev); 23 24 return mdrv ? mdrv->mdev : NULL; 25 } 26 27 static void komeda_unbind(struct device *dev) 28 { 29 struct komeda_drv *mdrv = dev_get_drvdata(dev); 30 31 if (!mdrv) 32 return; 33 34 komeda_kms_detach(mdrv->kms); 35 komeda_dev_destroy(mdrv->mdev); 36 37 dev_set_drvdata(dev, NULL); 38 devm_kfree(dev, mdrv); 39 } 40 41 static int komeda_bind(struct device *dev) 42 { 43 struct komeda_drv *mdrv; 44 int err; 45 46 mdrv = devm_kzalloc(dev, sizeof(*mdrv), GFP_KERNEL); 47 if (!mdrv) 48 return -ENOMEM; 49 50 mdrv->mdev = komeda_dev_create(dev); 51 if (IS_ERR(mdrv->mdev)) { 52 err = PTR_ERR(mdrv->mdev); 53 goto free_mdrv; 54 } 55 56 mdrv->kms = komeda_kms_attach(mdrv->mdev); 57 if (IS_ERR(mdrv->kms)) { 58 err = PTR_ERR(mdrv->kms); 59 goto destroy_mdev; 60 } 61 62 dev_set_drvdata(dev, mdrv); 63 64 return 0; 65 66 destroy_mdev: 67 komeda_dev_destroy(mdrv->mdev); 68 69 free_mdrv: 70 devm_kfree(dev, mdrv); 71 return err; 72 } 73 74 static const struct component_master_ops komeda_master_ops = { 75 .bind = komeda_bind, 76 .unbind = komeda_unbind, 77 }; 78 79 static int compare_of(struct device *dev, void *data) 80 { 81 return dev->of_node == data; 82 } 83 84 static void komeda_add_slave(struct device *master, 85 struct component_match **match, 86 struct device_node *np, int port) 87 { 88 struct device_node *remote; 89 90 remote = of_graph_get_remote_node(np, port, 0); 91 if (remote) { 92 drm_of_component_match_add(master, match, compare_of, remote); 93 of_node_put(remote); 94 } 95 } 96 97 static int komeda_platform_probe(struct platform_device *pdev) 98 { 99 struct device *dev = &pdev->dev; 100 struct component_match *match = NULL; 101 struct device_node *child; 102 103 if (!dev->of_node) 104 return -ENODEV; 105 106 for_each_available_child_of_node(dev->of_node, child) { 107 if (of_node_cmp(child->name, "pipeline") != 0) 108 continue; 109 110 /* add connector */ 111 komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT); 112 } 113 114 return component_master_add_with_match(dev, &komeda_master_ops, match); 115 } 116 117 static int komeda_platform_remove(struct platform_device *pdev) 118 { 119 component_master_del(&pdev->dev, &komeda_master_ops); 120 return 0; 121 } 122 123 static const struct komeda_product_data komeda_products[] = { 124 [MALI_D71] = { 125 .product_id = MALIDP_D71_PRODUCT_ID, 126 .identify = d71_identify, 127 }, 128 }; 129 130 static const struct of_device_id komeda_of_match[] = { 131 { .compatible = "arm,mali-d71", .data = &komeda_products[MALI_D71], }, 132 {}, 133 }; 134 135 MODULE_DEVICE_TABLE(of, komeda_of_match); 136 137 static struct platform_driver komeda_platform_driver = { 138 .probe = komeda_platform_probe, 139 .remove = komeda_platform_remove, 140 .driver = { 141 .name = "komeda", 142 .of_match_table = komeda_of_match, 143 .pm = NULL, 144 }, 145 }; 146 147 module_platform_driver(komeda_platform_driver); 148 149 MODULE_AUTHOR("James.Qian.Wang <james.qian.wang@arm.com>"); 150 MODULE_DESCRIPTION("Komeda KMS driver"); 151 MODULE_LICENSE("GPL v2"); 152