1*0e177d5cSLiu Ying // SPDX-License-Identifier: GPL-2.0+ 2*0e177d5cSLiu Ying /* 3*0e177d5cSLiu Ying * Copyright 2024 NXP 4*0e177d5cSLiu Ying */ 5*0e177d5cSLiu Ying 6*0e177d5cSLiu Ying #include <linux/clk.h> 7*0e177d5cSLiu Ying #include <linux/component.h> 8*0e177d5cSLiu Ying #include <linux/mod_devicetable.h> 9*0e177d5cSLiu Ying #include <linux/module.h> 10*0e177d5cSLiu Ying #include <linux/of.h> 11*0e177d5cSLiu Ying #include <linux/of_platform.h> 12*0e177d5cSLiu Ying #include <linux/platform_device.h> 13*0e177d5cSLiu Ying #include <linux/pm.h> 14*0e177d5cSLiu Ying #include <linux/pm_runtime.h> 15*0e177d5cSLiu Ying 16*0e177d5cSLiu Ying #include "dc-drv.h" 17*0e177d5cSLiu Ying #include "dc-fu.h" 18*0e177d5cSLiu Ying #include "dc-pe.h" 19*0e177d5cSLiu Ying 20*0e177d5cSLiu Ying static int dc_pe_bind(struct device *dev, struct device *master, void *data) 21*0e177d5cSLiu Ying { 22*0e177d5cSLiu Ying struct dc_drm_device *dc_drm = data; 23*0e177d5cSLiu Ying struct dc_pe *pe; 24*0e177d5cSLiu Ying int ret; 25*0e177d5cSLiu Ying 26*0e177d5cSLiu Ying pe = devm_kzalloc(dev, sizeof(*pe), GFP_KERNEL); 27*0e177d5cSLiu Ying if (!pe) 28*0e177d5cSLiu Ying return -ENOMEM; 29*0e177d5cSLiu Ying 30*0e177d5cSLiu Ying pe->clk_axi = devm_clk_get(dev, NULL); 31*0e177d5cSLiu Ying if (IS_ERR(pe->clk_axi)) 32*0e177d5cSLiu Ying return dev_err_probe(dev, PTR_ERR(pe->clk_axi), 33*0e177d5cSLiu Ying "failed to get AXI clock\n"); 34*0e177d5cSLiu Ying 35*0e177d5cSLiu Ying pe->dev = dev; 36*0e177d5cSLiu Ying 37*0e177d5cSLiu Ying dev_set_drvdata(dev, pe); 38*0e177d5cSLiu Ying 39*0e177d5cSLiu Ying ret = devm_pm_runtime_enable(dev); 40*0e177d5cSLiu Ying if (ret) 41*0e177d5cSLiu Ying return ret; 42*0e177d5cSLiu Ying 43*0e177d5cSLiu Ying dc_drm->pe = pe; 44*0e177d5cSLiu Ying 45*0e177d5cSLiu Ying return 0; 46*0e177d5cSLiu Ying } 47*0e177d5cSLiu Ying 48*0e177d5cSLiu Ying /* 49*0e177d5cSLiu Ying * It's possible to get the child device pointers from the child component 50*0e177d5cSLiu Ying * bind callbacks, but it depends on the component helper behavior to bind 51*0e177d5cSLiu Ying * the pixel engine component first. To avoid the dependency, post bind to 52*0e177d5cSLiu Ying * get the pointers from dc_drm in a safe manner. 53*0e177d5cSLiu Ying */ 54*0e177d5cSLiu Ying void dc_pe_post_bind(struct dc_drm_device *dc_drm) 55*0e177d5cSLiu Ying { 56*0e177d5cSLiu Ying struct dc_pe *pe = dc_drm->pe; 57*0e177d5cSLiu Ying int i; 58*0e177d5cSLiu Ying 59*0e177d5cSLiu Ying for (i = 0; i < DC_DISPLAYS; i++) { 60*0e177d5cSLiu Ying pe->cf_safe[i] = dc_drm->cf_safe[i]; 61*0e177d5cSLiu Ying pe->cf_cont[i] = dc_drm->cf_cont[i]; 62*0e177d5cSLiu Ying pe->ed_safe[i] = dc_drm->ed_safe[i]; 63*0e177d5cSLiu Ying pe->ed_cont[i] = dc_drm->ed_cont[i]; 64*0e177d5cSLiu Ying } 65*0e177d5cSLiu Ying 66*0e177d5cSLiu Ying for (i = 0; i < DC_DISP_FU_CNT; i++) 67*0e177d5cSLiu Ying pe->fu_disp[i] = dc_drm->fu_disp[i]; 68*0e177d5cSLiu Ying 69*0e177d5cSLiu Ying for (i = 0; i < DC_LB_CNT; i++) 70*0e177d5cSLiu Ying pe->lb[i] = dc_drm->lb[i]; 71*0e177d5cSLiu Ying } 72*0e177d5cSLiu Ying 73*0e177d5cSLiu Ying static const struct component_ops dc_pe_ops = { 74*0e177d5cSLiu Ying .bind = dc_pe_bind, 75*0e177d5cSLiu Ying }; 76*0e177d5cSLiu Ying 77*0e177d5cSLiu Ying static int dc_pe_probe(struct platform_device *pdev) 78*0e177d5cSLiu Ying { 79*0e177d5cSLiu Ying int ret; 80*0e177d5cSLiu Ying 81*0e177d5cSLiu Ying ret = devm_of_platform_populate(&pdev->dev); 82*0e177d5cSLiu Ying if (ret < 0) 83*0e177d5cSLiu Ying return ret; 84*0e177d5cSLiu Ying 85*0e177d5cSLiu Ying ret = component_add(&pdev->dev, &dc_pe_ops); 86*0e177d5cSLiu Ying if (ret) 87*0e177d5cSLiu Ying return dev_err_probe(&pdev->dev, ret, 88*0e177d5cSLiu Ying "failed to add component\n"); 89*0e177d5cSLiu Ying 90*0e177d5cSLiu Ying return 0; 91*0e177d5cSLiu Ying } 92*0e177d5cSLiu Ying 93*0e177d5cSLiu Ying static void dc_pe_remove(struct platform_device *pdev) 94*0e177d5cSLiu Ying { 95*0e177d5cSLiu Ying component_del(&pdev->dev, &dc_pe_ops); 96*0e177d5cSLiu Ying } 97*0e177d5cSLiu Ying 98*0e177d5cSLiu Ying static int dc_pe_runtime_suspend(struct device *dev) 99*0e177d5cSLiu Ying { 100*0e177d5cSLiu Ying struct dc_pe *pe = dev_get_drvdata(dev); 101*0e177d5cSLiu Ying 102*0e177d5cSLiu Ying clk_disable_unprepare(pe->clk_axi); 103*0e177d5cSLiu Ying 104*0e177d5cSLiu Ying return 0; 105*0e177d5cSLiu Ying } 106*0e177d5cSLiu Ying 107*0e177d5cSLiu Ying static int dc_pe_runtime_resume(struct device *dev) 108*0e177d5cSLiu Ying { 109*0e177d5cSLiu Ying struct dc_pe *pe = dev_get_drvdata(dev); 110*0e177d5cSLiu Ying int i, ret; 111*0e177d5cSLiu Ying 112*0e177d5cSLiu Ying ret = clk_prepare_enable(pe->clk_axi); 113*0e177d5cSLiu Ying if (ret) { 114*0e177d5cSLiu Ying dev_err(dev, "failed to enable AXI clock: %d\n", ret); 115*0e177d5cSLiu Ying return ret; 116*0e177d5cSLiu Ying } 117*0e177d5cSLiu Ying 118*0e177d5cSLiu Ying for (i = 0; i < ARRAY_SIZE(pe->cf_safe); i++) 119*0e177d5cSLiu Ying dc_cf_init(pe->cf_safe[i]); 120*0e177d5cSLiu Ying 121*0e177d5cSLiu Ying for (i = 0; i < ARRAY_SIZE(pe->cf_cont); i++) 122*0e177d5cSLiu Ying dc_cf_init(pe->cf_cont[i]); 123*0e177d5cSLiu Ying 124*0e177d5cSLiu Ying for (i = 0; i < ARRAY_SIZE(pe->ed_safe); i++) 125*0e177d5cSLiu Ying dc_ed_init(pe->ed_safe[i]); 126*0e177d5cSLiu Ying 127*0e177d5cSLiu Ying for (i = 0; i < ARRAY_SIZE(pe->ed_cont); i++) 128*0e177d5cSLiu Ying dc_ed_init(pe->ed_cont[i]); 129*0e177d5cSLiu Ying 130*0e177d5cSLiu Ying for (i = 0; i < ARRAY_SIZE(pe->fu_disp); i++) 131*0e177d5cSLiu Ying pe->fu_disp[i]->ops.init(pe->fu_disp[i]); 132*0e177d5cSLiu Ying 133*0e177d5cSLiu Ying for (i = 0; i < ARRAY_SIZE(pe->lb); i++) 134*0e177d5cSLiu Ying dc_lb_init(pe->lb[i]); 135*0e177d5cSLiu Ying 136*0e177d5cSLiu Ying return 0; 137*0e177d5cSLiu Ying } 138*0e177d5cSLiu Ying 139*0e177d5cSLiu Ying static const struct dev_pm_ops dc_pe_pm_ops = { 140*0e177d5cSLiu Ying RUNTIME_PM_OPS(dc_pe_runtime_suspend, dc_pe_runtime_resume, NULL) 141*0e177d5cSLiu Ying }; 142*0e177d5cSLiu Ying 143*0e177d5cSLiu Ying static const struct of_device_id dc_pe_dt_ids[] = { 144*0e177d5cSLiu Ying { .compatible = "fsl,imx8qxp-dc-pixel-engine", }, 145*0e177d5cSLiu Ying { /* sentinel */ } 146*0e177d5cSLiu Ying }; 147*0e177d5cSLiu Ying MODULE_DEVICE_TABLE(of, dc_pe_dt_ids); 148*0e177d5cSLiu Ying 149*0e177d5cSLiu Ying struct platform_driver dc_pe_driver = { 150*0e177d5cSLiu Ying .probe = dc_pe_probe, 151*0e177d5cSLiu Ying .remove = dc_pe_remove, 152*0e177d5cSLiu Ying .driver = { 153*0e177d5cSLiu Ying .name = "imx8-dc-pixel-engine", 154*0e177d5cSLiu Ying .suppress_bind_attrs = true, 155*0e177d5cSLiu Ying .of_match_table = dc_pe_dt_ids, 156*0e177d5cSLiu Ying .pm = pm_sleep_ptr(&dc_pe_pm_ops), 157*0e177d5cSLiu Ying }, 158*0e177d5cSLiu Ying }; 159