169a4237aSYongqiang Niu // SPDX-License-Identifier: GPL-2.0-only 269a4237aSYongqiang Niu /* 369a4237aSYongqiang Niu * Copyright (c) 2021 MediaTek Inc. 469a4237aSYongqiang Niu */ 569a4237aSYongqiang Niu 669a4237aSYongqiang Niu #include <linux/clk.h> 769a4237aSYongqiang Niu #include <linux/component.h> 869a4237aSYongqiang Niu #include <linux/module.h> 969a4237aSYongqiang Niu #include <linux/of_device.h> 1069a4237aSYongqiang Niu #include <linux/of_irq.h> 1169a4237aSYongqiang Niu #include <linux/platform_device.h> 1269a4237aSYongqiang Niu #include <linux/soc/mediatek/mtk-cmdq.h> 1369a4237aSYongqiang Niu 1469a4237aSYongqiang Niu #include "mtk_disp_drv.h" 1569a4237aSYongqiang Niu #include "mtk_drm_crtc.h" 1669a4237aSYongqiang Niu #include "mtk_drm_ddp_comp.h" 17*807e2f3fSMiles Chen #include "mtk_drm_drv.h" 1869a4237aSYongqiang Niu 1969a4237aSYongqiang Niu #define DISP_GAMMA_EN 0x0000 2069a4237aSYongqiang Niu #define GAMMA_EN BIT(0) 2169a4237aSYongqiang Niu #define DISP_GAMMA_CFG 0x0020 2269a4237aSYongqiang Niu #define GAMMA_LUT_EN BIT(1) 2369a4237aSYongqiang Niu #define GAMMA_DITHERING BIT(2) 2469a4237aSYongqiang Niu #define DISP_GAMMA_SIZE 0x0030 2569a4237aSYongqiang Niu #define DISP_GAMMA_LUT 0x0700 2669a4237aSYongqiang Niu 2769a4237aSYongqiang Niu #define LUT_10BIT_MASK 0x03ff 2869a4237aSYongqiang Niu 2969a4237aSYongqiang Niu struct mtk_disp_gamma_data { 304a15d1acSYongqiang Niu bool has_dither; 31ba99d08dSYongqiang Niu bool lut_diff; 3269a4237aSYongqiang Niu }; 3369a4237aSYongqiang Niu 34cebecaf1SLee Jones /* 3569a4237aSYongqiang Niu * struct mtk_disp_gamma - DISP_GAMMA driver structure 3669a4237aSYongqiang Niu */ 3769a4237aSYongqiang Niu struct mtk_disp_gamma { 3869a4237aSYongqiang Niu struct clk *clk; 3969a4237aSYongqiang Niu void __iomem *regs; 4069a4237aSYongqiang Niu struct cmdq_client_reg cmdq_reg; 4169a4237aSYongqiang Niu const struct mtk_disp_gamma_data *data; 4269a4237aSYongqiang Niu }; 4369a4237aSYongqiang Niu 4469a4237aSYongqiang Niu int mtk_gamma_clk_enable(struct device *dev) 4569a4237aSYongqiang Niu { 4669a4237aSYongqiang Niu struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 4769a4237aSYongqiang Niu 4869a4237aSYongqiang Niu return clk_prepare_enable(gamma->clk); 4969a4237aSYongqiang Niu } 5069a4237aSYongqiang Niu 5169a4237aSYongqiang Niu void mtk_gamma_clk_disable(struct device *dev) 5269a4237aSYongqiang Niu { 5369a4237aSYongqiang Niu struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 5469a4237aSYongqiang Niu 5569a4237aSYongqiang Niu clk_disable_unprepare(gamma->clk); 5669a4237aSYongqiang Niu } 5769a4237aSYongqiang Niu 58ba99d08dSYongqiang Niu void mtk_gamma_set_common(void __iomem *regs, struct drm_crtc_state *state, bool lut_diff) 5969a4237aSYongqiang Niu { 6069a4237aSYongqiang Niu unsigned int i, reg; 6169a4237aSYongqiang Niu struct drm_color_lut *lut; 6269a4237aSYongqiang Niu void __iomem *lut_base; 6369a4237aSYongqiang Niu u32 word; 64ba99d08dSYongqiang Niu u32 diff[3] = {0}; 6569a4237aSYongqiang Niu 6669a4237aSYongqiang Niu if (state->gamma_lut) { 6769a4237aSYongqiang Niu reg = readl(regs + DISP_GAMMA_CFG); 6869a4237aSYongqiang Niu reg = reg | GAMMA_LUT_EN; 6969a4237aSYongqiang Niu writel(reg, regs + DISP_GAMMA_CFG); 7069a4237aSYongqiang Niu lut_base = regs + DISP_GAMMA_LUT; 7169a4237aSYongqiang Niu lut = (struct drm_color_lut *)state->gamma_lut->data; 7269a4237aSYongqiang Niu for (i = 0; i < MTK_LUT_SIZE; i++) { 73ba99d08dSYongqiang Niu 74ba99d08dSYongqiang Niu if (!lut_diff || (i % 2 == 0)) { 7569a4237aSYongqiang Niu word = (((lut[i].red >> 6) & LUT_10BIT_MASK) << 20) + 7669a4237aSYongqiang Niu (((lut[i].green >> 6) & LUT_10BIT_MASK) << 10) + 7769a4237aSYongqiang Niu ((lut[i].blue >> 6) & LUT_10BIT_MASK); 78ba99d08dSYongqiang Niu } else { 79ba99d08dSYongqiang Niu diff[0] = (lut[i].red >> 6) - (lut[i - 1].red >> 6); 80ba99d08dSYongqiang Niu diff[1] = (lut[i].green >> 6) - (lut[i - 1].green >> 6); 81ba99d08dSYongqiang Niu diff[2] = (lut[i].blue >> 6) - (lut[i - 1].blue >> 6); 82ba99d08dSYongqiang Niu 83ba99d08dSYongqiang Niu word = ((diff[0] & LUT_10BIT_MASK) << 20) + 84ba99d08dSYongqiang Niu ((diff[1] & LUT_10BIT_MASK) << 10) + 85ba99d08dSYongqiang Niu (diff[2] & LUT_10BIT_MASK); 86ba99d08dSYongqiang Niu } 8769a4237aSYongqiang Niu writel(word, (lut_base + i * 4)); 8869a4237aSYongqiang Niu } 8969a4237aSYongqiang Niu } 9069a4237aSYongqiang Niu } 9169a4237aSYongqiang Niu 9269a4237aSYongqiang Niu void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state) 9369a4237aSYongqiang Niu { 9469a4237aSYongqiang Niu struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 95ba99d08dSYongqiang Niu bool lut_diff = false; 9669a4237aSYongqiang Niu 97ba99d08dSYongqiang Niu if (gamma->data) 98ba99d08dSYongqiang Niu lut_diff = gamma->data->lut_diff; 99ba99d08dSYongqiang Niu 100ba99d08dSYongqiang Niu mtk_gamma_set_common(gamma->regs, state, lut_diff); 10169a4237aSYongqiang Niu } 10269a4237aSYongqiang Niu 10369a4237aSYongqiang Niu void mtk_gamma_config(struct device *dev, unsigned int w, 10469a4237aSYongqiang Niu unsigned int h, unsigned int vrefresh, 10569a4237aSYongqiang Niu unsigned int bpc, struct cmdq_pkt *cmdq_pkt) 10669a4237aSYongqiang Niu { 10769a4237aSYongqiang Niu struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 10869a4237aSYongqiang Niu 10969a4237aSYongqiang Niu mtk_ddp_write(cmdq_pkt, h << 16 | w, &gamma->cmdq_reg, gamma->regs, 11069a4237aSYongqiang Niu DISP_GAMMA_SIZE); 1114a15d1acSYongqiang Niu if (gamma->data && gamma->data->has_dither) 1124a15d1acSYongqiang Niu mtk_dither_set_common(gamma->regs, &gamma->cmdq_reg, bpc, 1134a15d1acSYongqiang Niu DISP_GAMMA_CFG, GAMMA_DITHERING, cmdq_pkt); 11469a4237aSYongqiang Niu } 11569a4237aSYongqiang Niu 11669a4237aSYongqiang Niu void mtk_gamma_start(struct device *dev) 11769a4237aSYongqiang Niu { 11869a4237aSYongqiang Niu struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 11969a4237aSYongqiang Niu 12069a4237aSYongqiang Niu writel(GAMMA_EN, gamma->regs + DISP_GAMMA_EN); 12169a4237aSYongqiang Niu } 12269a4237aSYongqiang Niu 12369a4237aSYongqiang Niu void mtk_gamma_stop(struct device *dev) 12469a4237aSYongqiang Niu { 12569a4237aSYongqiang Niu struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 12669a4237aSYongqiang Niu 12769a4237aSYongqiang Niu writel_relaxed(0x0, gamma->regs + DISP_GAMMA_EN); 12869a4237aSYongqiang Niu } 12969a4237aSYongqiang Niu 13069a4237aSYongqiang Niu static int mtk_disp_gamma_bind(struct device *dev, struct device *master, 13169a4237aSYongqiang Niu void *data) 13269a4237aSYongqiang Niu { 13369a4237aSYongqiang Niu return 0; 13469a4237aSYongqiang Niu } 13569a4237aSYongqiang Niu 13669a4237aSYongqiang Niu static void mtk_disp_gamma_unbind(struct device *dev, struct device *master, 13769a4237aSYongqiang Niu void *data) 13869a4237aSYongqiang Niu { 13969a4237aSYongqiang Niu } 14069a4237aSYongqiang Niu 14169a4237aSYongqiang Niu static const struct component_ops mtk_disp_gamma_component_ops = { 14269a4237aSYongqiang Niu .bind = mtk_disp_gamma_bind, 14369a4237aSYongqiang Niu .unbind = mtk_disp_gamma_unbind, 14469a4237aSYongqiang Niu }; 14569a4237aSYongqiang Niu 14669a4237aSYongqiang Niu static int mtk_disp_gamma_probe(struct platform_device *pdev) 14769a4237aSYongqiang Niu { 14869a4237aSYongqiang Niu struct device *dev = &pdev->dev; 14969a4237aSYongqiang Niu struct mtk_disp_gamma *priv; 15069a4237aSYongqiang Niu struct resource *res; 15169a4237aSYongqiang Niu int ret; 15269a4237aSYongqiang Niu 15369a4237aSYongqiang Niu priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 15469a4237aSYongqiang Niu if (!priv) 15569a4237aSYongqiang Niu return -ENOMEM; 15669a4237aSYongqiang Niu 15769a4237aSYongqiang Niu priv->clk = devm_clk_get(dev, NULL); 15869a4237aSYongqiang Niu if (IS_ERR(priv->clk)) { 15969a4237aSYongqiang Niu dev_err(dev, "failed to get gamma clk\n"); 16069a4237aSYongqiang Niu return PTR_ERR(priv->clk); 16169a4237aSYongqiang Niu } 16269a4237aSYongqiang Niu 16369a4237aSYongqiang Niu res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 16469a4237aSYongqiang Niu priv->regs = devm_ioremap_resource(dev, res); 16569a4237aSYongqiang Niu if (IS_ERR(priv->regs)) { 16669a4237aSYongqiang Niu dev_err(dev, "failed to ioremap gamma\n"); 16769a4237aSYongqiang Niu return PTR_ERR(priv->regs); 16869a4237aSYongqiang Niu } 16969a4237aSYongqiang Niu 17069a4237aSYongqiang Niu #if IS_REACHABLE(CONFIG_MTK_CMDQ) 17169a4237aSYongqiang Niu ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); 17269a4237aSYongqiang Niu if (ret) 17369a4237aSYongqiang Niu dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); 17469a4237aSYongqiang Niu #endif 17569a4237aSYongqiang Niu 17669a4237aSYongqiang Niu priv->data = of_device_get_match_data(dev); 17769a4237aSYongqiang Niu platform_set_drvdata(pdev, priv); 17869a4237aSYongqiang Niu 17969a4237aSYongqiang Niu ret = component_add(dev, &mtk_disp_gamma_component_ops); 18069a4237aSYongqiang Niu if (ret) 18169a4237aSYongqiang Niu dev_err(dev, "Failed to add component: %d\n", ret); 18269a4237aSYongqiang Niu 18369a4237aSYongqiang Niu return ret; 18469a4237aSYongqiang Niu } 18569a4237aSYongqiang Niu 18669a4237aSYongqiang Niu static int mtk_disp_gamma_remove(struct platform_device *pdev) 18769a4237aSYongqiang Niu { 18869a4237aSYongqiang Niu component_del(&pdev->dev, &mtk_disp_gamma_component_ops); 18969a4237aSYongqiang Niu 19069a4237aSYongqiang Niu return 0; 19169a4237aSYongqiang Niu } 19269a4237aSYongqiang Niu 1934a15d1acSYongqiang Niu static const struct mtk_disp_gamma_data mt8173_gamma_driver_data = { 1944a15d1acSYongqiang Niu .has_dither = true, 1954a15d1acSYongqiang Niu }; 1964a15d1acSYongqiang Niu 197ba99d08dSYongqiang Niu static const struct mtk_disp_gamma_data mt8183_gamma_driver_data = { 198ba99d08dSYongqiang Niu .lut_diff = true, 199ba99d08dSYongqiang Niu }; 200ba99d08dSYongqiang Niu 20169a4237aSYongqiang Niu static const struct of_device_id mtk_disp_gamma_driver_dt_match[] = { 2024a15d1acSYongqiang Niu { .compatible = "mediatek,mt8173-disp-gamma", 2034a15d1acSYongqiang Niu .data = &mt8173_gamma_driver_data}, 204ba99d08dSYongqiang Niu { .compatible = "mediatek,mt8183-disp-gamma", 205ba99d08dSYongqiang Niu .data = &mt8183_gamma_driver_data}, 20669a4237aSYongqiang Niu {}, 20769a4237aSYongqiang Niu }; 20869a4237aSYongqiang Niu MODULE_DEVICE_TABLE(of, mtk_disp_gamma_driver_dt_match); 20969a4237aSYongqiang Niu 21069a4237aSYongqiang Niu struct platform_driver mtk_disp_gamma_driver = { 21169a4237aSYongqiang Niu .probe = mtk_disp_gamma_probe, 21269a4237aSYongqiang Niu .remove = mtk_disp_gamma_remove, 21369a4237aSYongqiang Niu .driver = { 21469a4237aSYongqiang Niu .name = "mediatek-disp-gamma", 21569a4237aSYongqiang Niu .owner = THIS_MODULE, 21669a4237aSYongqiang Niu .of_match_table = mtk_disp_gamma_driver_dt_match, 21769a4237aSYongqiang Niu }, 21869a4237aSYongqiang Niu }; 219