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, 87 u32 port, u32 endpoint) 88 { 89 struct device_node *remote; 90 91 remote = of_graph_get_remote_node(np, port, endpoint); 92 if (remote) { 93 drm_of_component_match_add(master, match, compare_of, remote); 94 of_node_put(remote); 95 } 96 } 97 98 static int komeda_platform_probe(struct platform_device *pdev) 99 { 100 struct device *dev = &pdev->dev; 101 struct component_match *match = NULL; 102 struct device_node *child; 103 104 if (!dev->of_node) 105 return -ENODEV; 106 107 for_each_available_child_of_node(dev->of_node, child) { 108 if (of_node_cmp(child->name, "pipeline") != 0) 109 continue; 110 111 /* add connector */ 112 komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT, 0); 113 komeda_add_slave(dev, &match, child, KOMEDA_OF_PORT_OUTPUT, 1); 114 } 115 116 return component_master_add_with_match(dev, &komeda_master_ops, match); 117 } 118 119 static int komeda_platform_remove(struct platform_device *pdev) 120 { 121 component_master_del(&pdev->dev, &komeda_master_ops); 122 return 0; 123 } 124 125 static const struct komeda_product_data komeda_products[] = { 126 [MALI_D71] = { 127 .product_id = MALIDP_D71_PRODUCT_ID, 128 .identify = d71_identify, 129 }, 130 }; 131 132 static const struct of_device_id komeda_of_match[] = { 133 { .compatible = "arm,mali-d71", .data = &komeda_products[MALI_D71], }, 134 {}, 135 }; 136 137 MODULE_DEVICE_TABLE(of, komeda_of_match); 138 139 static struct platform_driver komeda_platform_driver = { 140 .probe = komeda_platform_probe, 141 .remove = komeda_platform_remove, 142 .driver = { 143 .name = "komeda", 144 .of_match_table = komeda_of_match, 145 .pm = NULL, 146 }, 147 }; 148 149 module_platform_driver(komeda_platform_driver); 150 151 MODULE_AUTHOR("James.Qian.Wang <james.qian.wang@arm.com>"); 152 MODULE_DESCRIPTION("Komeda KMS driver"); 153 MODULE_LICENSE("GPL v2"); 154