1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2021 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_disp_drv.h" 15 #include "mtk_drm_crtc.h" 16 #include "mtk_drm_ddp_comp.h" 17 18 #define DISP_GAMMA_EN 0x0000 19 #define GAMMA_EN BIT(0) 20 #define DISP_GAMMA_CFG 0x0020 21 #define GAMMA_LUT_EN BIT(1) 22 #define GAMMA_DITHERING BIT(2) 23 #define DISP_GAMMA_SIZE 0x0030 24 #define DISP_GAMMA_LUT 0x0700 25 26 #define LUT_10BIT_MASK 0x03ff 27 28 struct mtk_disp_gamma_data { 29 bool has_dither; 30 }; 31 32 /* 33 * struct mtk_disp_gamma - DISP_GAMMA driver structure 34 */ 35 struct mtk_disp_gamma { 36 struct clk *clk; 37 void __iomem *regs; 38 struct cmdq_client_reg cmdq_reg; 39 const struct mtk_disp_gamma_data *data; 40 }; 41 42 int mtk_gamma_clk_enable(struct device *dev) 43 { 44 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 45 46 return clk_prepare_enable(gamma->clk); 47 } 48 49 void mtk_gamma_clk_disable(struct device *dev) 50 { 51 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 52 53 clk_disable_unprepare(gamma->clk); 54 } 55 56 void mtk_gamma_set_common(void __iomem *regs, struct drm_crtc_state *state) 57 { 58 unsigned int i, reg; 59 struct drm_color_lut *lut; 60 void __iomem *lut_base; 61 u32 word; 62 63 if (state->gamma_lut) { 64 reg = readl(regs + DISP_GAMMA_CFG); 65 reg = reg | GAMMA_LUT_EN; 66 writel(reg, regs + DISP_GAMMA_CFG); 67 lut_base = regs + DISP_GAMMA_LUT; 68 lut = (struct drm_color_lut *)state->gamma_lut->data; 69 for (i = 0; i < MTK_LUT_SIZE; i++) { 70 word = (((lut[i].red >> 6) & LUT_10BIT_MASK) << 20) + 71 (((lut[i].green >> 6) & LUT_10BIT_MASK) << 10) + 72 ((lut[i].blue >> 6) & LUT_10BIT_MASK); 73 writel(word, (lut_base + i * 4)); 74 } 75 } 76 } 77 78 void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state) 79 { 80 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 81 82 mtk_gamma_set_common(gamma->regs, state); 83 } 84 85 void mtk_gamma_config(struct device *dev, unsigned int w, 86 unsigned int h, unsigned int vrefresh, 87 unsigned int bpc, struct cmdq_pkt *cmdq_pkt) 88 { 89 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 90 91 mtk_ddp_write(cmdq_pkt, h << 16 | w, &gamma->cmdq_reg, gamma->regs, 92 DISP_GAMMA_SIZE); 93 if (gamma->data && gamma->data->has_dither) 94 mtk_dither_set_common(gamma->regs, &gamma->cmdq_reg, bpc, 95 DISP_GAMMA_CFG, GAMMA_DITHERING, cmdq_pkt); 96 } 97 98 void mtk_gamma_start(struct device *dev) 99 { 100 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 101 102 writel(GAMMA_EN, gamma->regs + DISP_GAMMA_EN); 103 } 104 105 void mtk_gamma_stop(struct device *dev) 106 { 107 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 108 109 writel_relaxed(0x0, gamma->regs + DISP_GAMMA_EN); 110 } 111 112 static int mtk_disp_gamma_bind(struct device *dev, struct device *master, 113 void *data) 114 { 115 return 0; 116 } 117 118 static void mtk_disp_gamma_unbind(struct device *dev, struct device *master, 119 void *data) 120 { 121 } 122 123 static const struct component_ops mtk_disp_gamma_component_ops = { 124 .bind = mtk_disp_gamma_bind, 125 .unbind = mtk_disp_gamma_unbind, 126 }; 127 128 static int mtk_disp_gamma_probe(struct platform_device *pdev) 129 { 130 struct device *dev = &pdev->dev; 131 struct mtk_disp_gamma *priv; 132 struct resource *res; 133 int ret; 134 135 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 136 if (!priv) 137 return -ENOMEM; 138 139 priv->clk = devm_clk_get(dev, NULL); 140 if (IS_ERR(priv->clk)) { 141 dev_err(dev, "failed to get gamma clk\n"); 142 return PTR_ERR(priv->clk); 143 } 144 145 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 146 priv->regs = devm_ioremap_resource(dev, res); 147 if (IS_ERR(priv->regs)) { 148 dev_err(dev, "failed to ioremap gamma\n"); 149 return PTR_ERR(priv->regs); 150 } 151 152 #if IS_REACHABLE(CONFIG_MTK_CMDQ) 153 ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); 154 if (ret) 155 dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); 156 #endif 157 158 priv->data = of_device_get_match_data(dev); 159 platform_set_drvdata(pdev, priv); 160 161 ret = component_add(dev, &mtk_disp_gamma_component_ops); 162 if (ret) 163 dev_err(dev, "Failed to add component: %d\n", ret); 164 165 return ret; 166 } 167 168 static int mtk_disp_gamma_remove(struct platform_device *pdev) 169 { 170 component_del(&pdev->dev, &mtk_disp_gamma_component_ops); 171 172 return 0; 173 } 174 175 static const struct mtk_disp_gamma_data mt8173_gamma_driver_data = { 176 .has_dither = true, 177 }; 178 179 static const struct of_device_id mtk_disp_gamma_driver_dt_match[] = { 180 { .compatible = "mediatek,mt8173-disp-gamma", 181 .data = &mt8173_gamma_driver_data}, 182 { .compatible = "mediatek,mt8183-disp-gamma"}, 183 {}, 184 }; 185 MODULE_DEVICE_TABLE(of, mtk_disp_gamma_driver_dt_match); 186 187 struct platform_driver mtk_disp_gamma_driver = { 188 .probe = mtk_disp_gamma_probe, 189 .remove = mtk_disp_gamma_remove, 190 .driver = { 191 .name = "mediatek-disp-gamma", 192 .owner = THIS_MODULE, 193 .of_match_table = mtk_disp_gamma_driver_dt_match, 194 }, 195 }; 196