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/io.h> 8 #include <linux/of_device.h> 9 #include <linux/of_graph.h> 10 #include <linux/platform_device.h> 11 #ifdef CONFIG_DEBUG_FS 12 #include <linux/debugfs.h> 13 #include <linux/seq_file.h> 14 #endif 15 16 #include <drm/drm_print.h> 17 18 #include "komeda_dev.h" 19 20 static int komeda_register_show(struct seq_file *sf, void *x) 21 { 22 struct komeda_dev *mdev = sf->private; 23 int i; 24 25 if (mdev->funcs->dump_register) 26 mdev->funcs->dump_register(mdev, sf); 27 28 for (i = 0; i < mdev->n_pipelines; i++) 29 komeda_pipeline_dump_register(mdev->pipelines[i], sf); 30 31 return 0; 32 } 33 34 static int komeda_register_open(struct inode *inode, struct file *filp) 35 { 36 return single_open(filp, komeda_register_show, inode->i_private); 37 } 38 39 static const struct file_operations komeda_register_fops = { 40 .owner = THIS_MODULE, 41 .open = komeda_register_open, 42 .read = seq_read, 43 .llseek = seq_lseek, 44 .release = single_release, 45 }; 46 47 #ifdef CONFIG_DEBUG_FS 48 static void komeda_debugfs_init(struct komeda_dev *mdev) 49 { 50 if (!debugfs_initialized()) 51 return; 52 53 mdev->debugfs_root = debugfs_create_dir("komeda", NULL); 54 if (IS_ERR_OR_NULL(mdev->debugfs_root)) 55 return; 56 57 debugfs_create_file("register", 0444, mdev->debugfs_root, 58 mdev, &komeda_register_fops); 59 } 60 #endif 61 62 static int komeda_parse_pipe_dt(struct komeda_dev *mdev, struct device_node *np) 63 { 64 struct komeda_pipeline *pipe; 65 struct clk *clk; 66 u32 pipe_id; 67 int ret = 0; 68 69 ret = of_property_read_u32(np, "reg", &pipe_id); 70 if (ret != 0 || pipe_id >= mdev->n_pipelines) 71 return -EINVAL; 72 73 pipe = mdev->pipelines[pipe_id]; 74 75 clk = of_clk_get_by_name(np, "aclk"); 76 if (IS_ERR(clk)) { 77 DRM_ERROR("get aclk for pipeline %d failed!\n", pipe_id); 78 return PTR_ERR(clk); 79 } 80 pipe->aclk = clk; 81 82 clk = of_clk_get_by_name(np, "pxclk"); 83 if (IS_ERR(clk)) { 84 DRM_ERROR("get pxclk for pipeline %d failed!\n", pipe_id); 85 return PTR_ERR(clk); 86 } 87 pipe->pxlclk = clk; 88 89 /* enum ports */ 90 pipe->of_output_dev = 91 of_graph_get_remote_node(np, KOMEDA_OF_PORT_OUTPUT, 0); 92 pipe->of_output_port = 93 of_graph_get_port_by_id(np, KOMEDA_OF_PORT_OUTPUT); 94 95 pipe->of_node = np; 96 97 return 0; 98 } 99 100 static int komeda_parse_dt(struct device *dev, struct komeda_dev *mdev) 101 { 102 struct platform_device *pdev = to_platform_device(dev); 103 struct device_node *child, *np = dev->of_node; 104 struct clk *clk; 105 int ret; 106 107 clk = devm_clk_get(dev, "mclk"); 108 if (IS_ERR(clk)) 109 return PTR_ERR(clk); 110 111 mdev->mclk = clk; 112 mdev->irq = platform_get_irq(pdev, 0); 113 if (mdev->irq < 0) { 114 DRM_ERROR("could not get IRQ number.\n"); 115 return mdev->irq; 116 } 117 118 for_each_available_child_of_node(np, child) { 119 if (of_node_cmp(child->name, "pipeline") == 0) { 120 ret = komeda_parse_pipe_dt(mdev, child); 121 if (ret) { 122 DRM_ERROR("parse pipeline dt error!\n"); 123 of_node_put(child); 124 break; 125 } 126 } 127 } 128 129 return ret; 130 } 131 132 struct komeda_dev *komeda_dev_create(struct device *dev) 133 { 134 struct platform_device *pdev = to_platform_device(dev); 135 const struct komeda_product_data *product; 136 struct komeda_dev *mdev; 137 struct resource *io_res; 138 int err = 0; 139 140 product = of_device_get_match_data(dev); 141 if (!product) 142 return ERR_PTR(-ENODEV); 143 144 io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 145 if (!io_res) { 146 DRM_ERROR("No registers defined.\n"); 147 return ERR_PTR(-ENODEV); 148 } 149 150 mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL); 151 if (!mdev) 152 return ERR_PTR(-ENOMEM); 153 154 mdev->dev = dev; 155 mdev->reg_base = devm_ioremap_resource(dev, io_res); 156 if (IS_ERR(mdev->reg_base)) { 157 DRM_ERROR("Map register space failed.\n"); 158 err = PTR_ERR(mdev->reg_base); 159 mdev->reg_base = NULL; 160 goto err_cleanup; 161 } 162 163 mdev->pclk = devm_clk_get(dev, "pclk"); 164 if (IS_ERR(mdev->pclk)) { 165 DRM_ERROR("Get APB clk failed.\n"); 166 err = PTR_ERR(mdev->pclk); 167 mdev->pclk = NULL; 168 goto err_cleanup; 169 } 170 171 /* Enable APB clock to access the registers */ 172 clk_prepare_enable(mdev->pclk); 173 174 mdev->funcs = product->identify(mdev->reg_base, &mdev->chip); 175 if (!komeda_product_match(mdev, product->product_id)) { 176 DRM_ERROR("DT configured %x mismatch with real HW %x.\n", 177 product->product_id, 178 MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id)); 179 err = -ENODEV; 180 goto err_cleanup; 181 } 182 183 DRM_INFO("Found ARM Mali-D%x version r%dp%d\n", 184 MALIDP_CORE_ID_PRODUCT_ID(mdev->chip.core_id), 185 MALIDP_CORE_ID_MAJOR(mdev->chip.core_id), 186 MALIDP_CORE_ID_MINOR(mdev->chip.core_id)); 187 188 mdev->funcs->init_format_table(mdev); 189 190 err = mdev->funcs->enum_resources(mdev); 191 if (err) { 192 DRM_ERROR("enumerate display resource failed.\n"); 193 goto err_cleanup; 194 } 195 196 err = komeda_parse_dt(dev, mdev); 197 if (err) { 198 DRM_ERROR("parse device tree failed.\n"); 199 goto err_cleanup; 200 } 201 202 err = komeda_assemble_pipelines(mdev); 203 if (err) { 204 DRM_ERROR("assemble display pipelines failed.\n"); 205 goto err_cleanup; 206 } 207 208 #ifdef CONFIG_DEBUG_FS 209 komeda_debugfs_init(mdev); 210 #endif 211 212 return mdev; 213 214 err_cleanup: 215 komeda_dev_destroy(mdev); 216 return ERR_PTR(err); 217 } 218 219 void komeda_dev_destroy(struct komeda_dev *mdev) 220 { 221 struct device *dev = mdev->dev; 222 struct komeda_dev_funcs *funcs = mdev->funcs; 223 int i; 224 225 #ifdef CONFIG_DEBUG_FS 226 debugfs_remove_recursive(mdev->debugfs_root); 227 #endif 228 229 for (i = 0; i < mdev->n_pipelines; i++) { 230 komeda_pipeline_destroy(mdev, mdev->pipelines[i]); 231 mdev->pipelines[i] = NULL; 232 } 233 234 mdev->n_pipelines = 0; 235 236 if (funcs && funcs->cleanup) 237 funcs->cleanup(mdev); 238 239 if (mdev->reg_base) { 240 devm_iounmap(dev, mdev->reg_base); 241 mdev->reg_base = NULL; 242 } 243 244 if (mdev->mclk) { 245 devm_clk_put(dev, mdev->mclk); 246 mdev->mclk = NULL; 247 } 248 249 if (mdev->pclk) { 250 clk_disable_unprepare(mdev->pclk); 251 devm_clk_put(dev, mdev->pclk); 252 mdev->pclk = NULL; 253 } 254 255 devm_kfree(dev, mdev); 256 } 257