1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2017 MediaTek Inc. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/component.h> 8 #include <linux/module.h> 9 #include <linux/of_device.h> 10 #include <linux/of_irq.h> 11 #include <linux/platform_device.h> 12 #include <linux/soc/mediatek/mtk-cmdq.h> 13 14 #include "mtk_drm_crtc.h" 15 #include "mtk_drm_ddp_comp.h" 16 17 #define DISP_COLOR_CFG_MAIN 0x0400 18 #define DISP_COLOR_START_MT2701 0x0f00 19 #define DISP_COLOR_START_MT8167 0x0400 20 #define DISP_COLOR_START_MT8173 0x0c00 21 #define DISP_COLOR_START(comp) ((comp)->data->color_offset) 22 #define DISP_COLOR_WIDTH(comp) (DISP_COLOR_START(comp) + 0x50) 23 #define DISP_COLOR_HEIGHT(comp) (DISP_COLOR_START(comp) + 0x54) 24 25 #define COLOR_BYPASS_ALL BIT(7) 26 #define COLOR_SEQ_SEL BIT(13) 27 28 struct mtk_disp_color_data { 29 unsigned int color_offset; 30 }; 31 32 /** 33 * struct mtk_disp_color - DISP_COLOR driver structure 34 * @ddp_comp: structure containing type enum and hardware resources 35 * @crtc: associated crtc to report irq events to 36 * @data: platform colour driver data 37 */ 38 struct mtk_disp_color { 39 struct mtk_ddp_comp ddp_comp; 40 struct drm_crtc *crtc; 41 struct clk *clk; 42 const struct mtk_disp_color_data *data; 43 }; 44 45 static inline struct mtk_disp_color *comp_to_color(struct mtk_ddp_comp *comp) 46 { 47 return container_of(comp, struct mtk_disp_color, ddp_comp); 48 } 49 50 static int mtk_color_clk_enable(struct device *dev) 51 { 52 struct mtk_disp_color *color = dev_get_drvdata(dev); 53 54 return clk_prepare_enable(color->clk); 55 } 56 57 static void mtk_color_clk_disable(struct device *dev) 58 { 59 struct mtk_disp_color *color = dev_get_drvdata(dev); 60 61 clk_disable_unprepare(color->clk); 62 } 63 64 static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w, 65 unsigned int h, unsigned int vrefresh, 66 unsigned int bpc, struct cmdq_pkt *cmdq_pkt) 67 { 68 struct mtk_disp_color *color = comp_to_color(comp); 69 70 mtk_ddp_write(cmdq_pkt, w, comp, DISP_COLOR_WIDTH(color)); 71 mtk_ddp_write(cmdq_pkt, h, comp, DISP_COLOR_HEIGHT(color)); 72 } 73 74 static void mtk_color_start(struct mtk_ddp_comp *comp) 75 { 76 struct mtk_disp_color *color = comp_to_color(comp); 77 78 writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL, 79 comp->regs + DISP_COLOR_CFG_MAIN); 80 writel(0x1, comp->regs + DISP_COLOR_START(color)); 81 } 82 83 static const struct mtk_ddp_comp_funcs mtk_disp_color_funcs = { 84 .clk_enable = mtk_color_clk_enable, 85 .clk_disable = mtk_color_clk_disable, 86 .config = mtk_color_config, 87 .start = mtk_color_start, 88 }; 89 90 static int mtk_disp_color_bind(struct device *dev, struct device *master, 91 void *data) 92 { 93 struct mtk_disp_color *priv = dev_get_drvdata(dev); 94 struct drm_device *drm_dev = data; 95 int ret; 96 97 ret = mtk_ddp_comp_register(drm_dev, &priv->ddp_comp); 98 if (ret < 0) { 99 dev_err(dev, "Failed to register component %pOF: %d\n", 100 dev->of_node, ret); 101 return ret; 102 } 103 104 return 0; 105 } 106 107 static void mtk_disp_color_unbind(struct device *dev, struct device *master, 108 void *data) 109 { 110 struct mtk_disp_color *priv = dev_get_drvdata(dev); 111 struct drm_device *drm_dev = data; 112 113 mtk_ddp_comp_unregister(drm_dev, &priv->ddp_comp); 114 } 115 116 static const struct component_ops mtk_disp_color_component_ops = { 117 .bind = mtk_disp_color_bind, 118 .unbind = mtk_disp_color_unbind, 119 }; 120 121 static int mtk_disp_color_probe(struct platform_device *pdev) 122 { 123 struct device *dev = &pdev->dev; 124 struct mtk_disp_color *priv; 125 int comp_id; 126 int ret; 127 128 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 129 if (!priv) 130 return -ENOMEM; 131 132 priv->clk = devm_clk_get(dev, NULL); 133 if (IS_ERR(priv->clk)) { 134 dev_err(dev, "failed to get color clk\n"); 135 return PTR_ERR(priv->clk); 136 } 137 138 comp_id = mtk_ddp_comp_get_id(dev->of_node, MTK_DISP_COLOR); 139 if (comp_id < 0) { 140 dev_err(dev, "Failed to identify by alias: %d\n", comp_id); 141 return comp_id; 142 } 143 144 ret = mtk_ddp_comp_init(dev->of_node, &priv->ddp_comp, comp_id, 145 &mtk_disp_color_funcs); 146 if (ret) { 147 if (ret != -EPROBE_DEFER) 148 dev_err(dev, "Failed to initialize component: %d\n", 149 ret); 150 151 return ret; 152 } 153 154 priv->data = of_device_get_match_data(dev); 155 156 platform_set_drvdata(pdev, priv); 157 158 ret = component_add(dev, &mtk_disp_color_component_ops); 159 if (ret) 160 dev_err(dev, "Failed to add component: %d\n", ret); 161 162 return ret; 163 } 164 165 static int mtk_disp_color_remove(struct platform_device *pdev) 166 { 167 component_del(&pdev->dev, &mtk_disp_color_component_ops); 168 169 return 0; 170 } 171 172 static const struct mtk_disp_color_data mt2701_color_driver_data = { 173 .color_offset = DISP_COLOR_START_MT2701, 174 }; 175 176 static const struct mtk_disp_color_data mt8167_color_driver_data = { 177 .color_offset = DISP_COLOR_START_MT8167, 178 }; 179 180 static const struct mtk_disp_color_data mt8173_color_driver_data = { 181 .color_offset = DISP_COLOR_START_MT8173, 182 }; 183 184 static const struct of_device_id mtk_disp_color_driver_dt_match[] = { 185 { .compatible = "mediatek,mt2701-disp-color", 186 .data = &mt2701_color_driver_data}, 187 { .compatible = "mediatek,mt8167-disp-color", 188 .data = &mt8167_color_driver_data}, 189 { .compatible = "mediatek,mt8173-disp-color", 190 .data = &mt8173_color_driver_data}, 191 {}, 192 }; 193 MODULE_DEVICE_TABLE(of, mtk_disp_color_driver_dt_match); 194 195 struct platform_driver mtk_disp_color_driver = { 196 .probe = mtk_disp_color_probe, 197 .remove = mtk_disp_color_remove, 198 .driver = { 199 .name = "mediatek-disp-color", 200 .owner = THIS_MODULE, 201 .of_match_table = mtk_disp_color_driver_dt_match, 202 }, 203 }; 204