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