1 /* 2 * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. 3 * Copyright (C) 2017 Linaro Ltd. 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 and 7 * only version 2 as published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 */ 15 #include <linux/clk.h> 16 #include <linux/init.h> 17 #include <linux/ioctl.h> 18 #include <linux/list.h> 19 #include <linux/module.h> 20 #include <linux/of_device.h> 21 #include <linux/platform_device.h> 22 #include <linux/slab.h> 23 #include <linux/types.h> 24 #include <linux/pm_runtime.h> 25 #include <media/videobuf2-v4l2.h> 26 #include <media/v4l2-mem2mem.h> 27 #include <media/v4l2-ioctl.h> 28 29 #include "core.h" 30 #include "vdec.h" 31 #include "venc.h" 32 #include "firmware.h" 33 34 static void venus_event_notify(struct venus_core *core, u32 event) 35 { 36 struct venus_inst *inst; 37 38 switch (event) { 39 case EVT_SYS_WATCHDOG_TIMEOUT: 40 case EVT_SYS_ERROR: 41 break; 42 default: 43 return; 44 } 45 46 mutex_lock(&core->lock); 47 core->sys_error = true; 48 list_for_each_entry(inst, &core->instances, list) 49 inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL); 50 mutex_unlock(&core->lock); 51 52 disable_irq_nosync(core->irq); 53 54 /* 55 * Delay recovery to ensure venus has completed any pending cache 56 * operations. Without this sleep, we see device reset when firmware is 57 * unloaded after a system error. 58 */ 59 schedule_delayed_work(&core->work, msecs_to_jiffies(100)); 60 } 61 62 static const struct hfi_core_ops venus_core_ops = { 63 .event_notify = venus_event_notify, 64 }; 65 66 static void venus_sys_error_handler(struct work_struct *work) 67 { 68 struct venus_core *core = 69 container_of(work, struct venus_core, work.work); 70 int ret = 0; 71 72 dev_warn(core->dev, "system error has occurred, starting recovery!\n"); 73 74 pm_runtime_get_sync(core->dev); 75 76 hfi_core_deinit(core, true); 77 hfi_destroy(core); 78 mutex_lock(&core->lock); 79 venus_shutdown(core->dev); 80 81 pm_runtime_put_sync(core->dev); 82 83 ret |= hfi_create(core, &venus_core_ops); 84 85 pm_runtime_get_sync(core->dev); 86 87 ret |= venus_boot(core->dev, core->res->fwname); 88 89 ret |= hfi_core_resume(core, true); 90 91 enable_irq(core->irq); 92 93 mutex_unlock(&core->lock); 94 95 ret |= hfi_core_init(core); 96 97 pm_runtime_put_sync(core->dev); 98 99 if (ret) { 100 disable_irq_nosync(core->irq); 101 dev_warn(core->dev, "recovery failed (%d)\n", ret); 102 schedule_delayed_work(&core->work, msecs_to_jiffies(10)); 103 return; 104 } 105 106 mutex_lock(&core->lock); 107 core->sys_error = false; 108 mutex_unlock(&core->lock); 109 } 110 111 static int venus_clks_get(struct venus_core *core) 112 { 113 const struct venus_resources *res = core->res; 114 struct device *dev = core->dev; 115 unsigned int i; 116 117 for (i = 0; i < res->clks_num; i++) { 118 core->clks[i] = devm_clk_get(dev, res->clks[i]); 119 if (IS_ERR(core->clks[i])) 120 return PTR_ERR(core->clks[i]); 121 } 122 123 return 0; 124 } 125 126 static int venus_clks_enable(struct venus_core *core) 127 { 128 const struct venus_resources *res = core->res; 129 unsigned int i; 130 int ret; 131 132 for (i = 0; i < res->clks_num; i++) { 133 ret = clk_prepare_enable(core->clks[i]); 134 if (ret) 135 goto err; 136 } 137 138 return 0; 139 err: 140 while (i--) 141 clk_disable_unprepare(core->clks[i]); 142 143 return ret; 144 } 145 146 static void venus_clks_disable(struct venus_core *core) 147 { 148 const struct venus_resources *res = core->res; 149 unsigned int i = res->clks_num; 150 151 while (i--) 152 clk_disable_unprepare(core->clks[i]); 153 } 154 155 static int venus_probe(struct platform_device *pdev) 156 { 157 struct device *dev = &pdev->dev; 158 struct venus_core *core; 159 struct resource *r; 160 int ret; 161 162 core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); 163 if (!core) 164 return -ENOMEM; 165 166 core->dev = dev; 167 platform_set_drvdata(pdev, core); 168 169 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 170 core->base = devm_ioremap_resource(dev, r); 171 if (IS_ERR(core->base)) 172 return PTR_ERR(core->base); 173 174 core->irq = platform_get_irq(pdev, 0); 175 if (core->irq < 0) 176 return core->irq; 177 178 core->res = of_device_get_match_data(dev); 179 if (!core->res) 180 return -ENODEV; 181 182 ret = venus_clks_get(core); 183 if (ret) 184 return ret; 185 186 ret = dma_set_mask_and_coherent(dev, core->res->dma_mask); 187 if (ret) 188 return ret; 189 190 INIT_LIST_HEAD(&core->instances); 191 mutex_init(&core->lock); 192 INIT_DELAYED_WORK(&core->work, venus_sys_error_handler); 193 194 ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, hfi_isr_thread, 195 IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 196 "venus", core); 197 if (ret) 198 return ret; 199 200 ret = hfi_create(core, &venus_core_ops); 201 if (ret) 202 return ret; 203 204 pm_runtime_enable(dev); 205 206 ret = pm_runtime_get_sync(dev); 207 if (ret < 0) 208 goto err_runtime_disable; 209 210 ret = venus_boot(dev, core->res->fwname); 211 if (ret) 212 goto err_runtime_disable; 213 214 ret = hfi_core_resume(core, true); 215 if (ret) 216 goto err_venus_shutdown; 217 218 ret = hfi_core_init(core); 219 if (ret) 220 goto err_venus_shutdown; 221 222 ret = v4l2_device_register(dev, &core->v4l2_dev); 223 if (ret) 224 goto err_core_deinit; 225 226 ret = of_platform_populate(dev->of_node, NULL, NULL, dev); 227 if (ret) 228 goto err_dev_unregister; 229 230 ret = pm_runtime_put_sync(dev); 231 if (ret) 232 goto err_dev_unregister; 233 234 return 0; 235 236 err_dev_unregister: 237 v4l2_device_unregister(&core->v4l2_dev); 238 err_core_deinit: 239 hfi_core_deinit(core, false); 240 err_venus_shutdown: 241 venus_shutdown(dev); 242 err_runtime_disable: 243 pm_runtime_set_suspended(dev); 244 pm_runtime_disable(dev); 245 hfi_destroy(core); 246 return ret; 247 } 248 249 static int venus_remove(struct platform_device *pdev) 250 { 251 struct venus_core *core = platform_get_drvdata(pdev); 252 struct device *dev = core->dev; 253 int ret; 254 255 ret = pm_runtime_get_sync(dev); 256 WARN_ON(ret < 0); 257 258 ret = hfi_core_deinit(core, true); 259 WARN_ON(ret); 260 261 hfi_destroy(core); 262 venus_shutdown(dev); 263 of_platform_depopulate(dev); 264 265 pm_runtime_put_sync(dev); 266 pm_runtime_disable(dev); 267 268 v4l2_device_unregister(&core->v4l2_dev); 269 270 return ret; 271 } 272 273 static __maybe_unused int venus_runtime_suspend(struct device *dev) 274 { 275 struct venus_core *core = dev_get_drvdata(dev); 276 int ret; 277 278 ret = hfi_core_suspend(core); 279 280 venus_clks_disable(core); 281 282 return ret; 283 } 284 285 static __maybe_unused int venus_runtime_resume(struct device *dev) 286 { 287 struct venus_core *core = dev_get_drvdata(dev); 288 int ret; 289 290 ret = venus_clks_enable(core); 291 if (ret) 292 return ret; 293 294 ret = hfi_core_resume(core, false); 295 if (ret) 296 goto err_clks_disable; 297 298 return 0; 299 300 err_clks_disable: 301 venus_clks_disable(core); 302 return ret; 303 } 304 305 static const struct dev_pm_ops venus_pm_ops = { 306 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 307 pm_runtime_force_resume) 308 SET_RUNTIME_PM_OPS(venus_runtime_suspend, venus_runtime_resume, NULL) 309 }; 310 311 static const struct freq_tbl msm8916_freq_table[] = { 312 { 352800, 228570000 }, /* 1920x1088 @ 30 + 1280x720 @ 30 */ 313 { 244800, 160000000 }, /* 1920x1088 @ 30 */ 314 { 108000, 100000000 }, /* 1280x720 @ 30 */ 315 }; 316 317 static const struct reg_val msm8916_reg_preset[] = { 318 { 0xe0020, 0x05555556 }, 319 { 0xe0024, 0x05555556 }, 320 { 0x80124, 0x00000003 }, 321 }; 322 323 static const struct venus_resources msm8916_res = { 324 .freq_tbl = msm8916_freq_table, 325 .freq_tbl_size = ARRAY_SIZE(msm8916_freq_table), 326 .reg_tbl = msm8916_reg_preset, 327 .reg_tbl_size = ARRAY_SIZE(msm8916_reg_preset), 328 .clks = { "core", "iface", "bus", }, 329 .clks_num = 3, 330 .max_load = 352800, /* 720p@30 + 1080p@30 */ 331 .hfi_version = HFI_VERSION_1XX, 332 .vmem_id = VIDC_RESOURCE_NONE, 333 .vmem_size = 0, 334 .vmem_addr = 0, 335 .dma_mask = 0xddc00000 - 1, 336 .fwname = "qcom/venus-1.8/venus.mdt", 337 }; 338 339 static const struct freq_tbl msm8996_freq_table[] = { 340 { 1944000, 490000000 }, /* 4k UHD @ 60 */ 341 { 972000, 320000000 }, /* 4k UHD @ 30 */ 342 { 489600, 150000000 }, /* 1080p @ 60 */ 343 { 244800, 75000000 }, /* 1080p @ 30 */ 344 }; 345 346 static const struct reg_val msm8996_reg_preset[] = { 347 { 0x80010, 0xffffffff }, 348 { 0x80018, 0x00001556 }, 349 { 0x8001C, 0x00001556 }, 350 }; 351 352 static const struct venus_resources msm8996_res = { 353 .freq_tbl = msm8996_freq_table, 354 .freq_tbl_size = ARRAY_SIZE(msm8996_freq_table), 355 .reg_tbl = msm8996_reg_preset, 356 .reg_tbl_size = ARRAY_SIZE(msm8996_reg_preset), 357 .clks = {"core", "iface", "bus", "mbus" }, 358 .clks_num = 4, 359 .max_load = 2563200, 360 .hfi_version = HFI_VERSION_3XX, 361 .vmem_id = VIDC_RESOURCE_NONE, 362 .vmem_size = 0, 363 .vmem_addr = 0, 364 .dma_mask = 0xddc00000 - 1, 365 .fwname = "qcom/venus-4.2/venus.mdt", 366 }; 367 368 static const struct of_device_id venus_dt_match[] = { 369 { .compatible = "qcom,msm8916-venus", .data = &msm8916_res, }, 370 { .compatible = "qcom,msm8996-venus", .data = &msm8996_res, }, 371 { } 372 }; 373 MODULE_DEVICE_TABLE(of, venus_dt_match); 374 375 static struct platform_driver qcom_venus_driver = { 376 .probe = venus_probe, 377 .remove = venus_remove, 378 .driver = { 379 .name = "qcom-venus", 380 .of_match_table = venus_dt_match, 381 .pm = &venus_pm_ops, 382 }, 383 }; 384 module_platform_driver(qcom_venus_driver); 385 386 MODULE_ALIAS("platform:qcom-venus"); 387 MODULE_DESCRIPTION("Qualcomm Venus video encoder and decoder driver"); 388 MODULE_LICENSE("GPL v2"); 389