162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2021 MediaTek Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/component.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/soc/mediatek/mtk-cmdq.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "mtk_disp_drv.h" 1462306a36Sopenharmony_ci#include "mtk_drm_crtc.h" 1562306a36Sopenharmony_ci#include "mtk_drm_ddp_comp.h" 1662306a36Sopenharmony_ci#include "mtk_drm_drv.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define DISP_GAMMA_EN 0x0000 1962306a36Sopenharmony_ci#define GAMMA_EN BIT(0) 2062306a36Sopenharmony_ci#define DISP_GAMMA_CFG 0x0020 2162306a36Sopenharmony_ci#define GAMMA_LUT_EN BIT(1) 2262306a36Sopenharmony_ci#define GAMMA_DITHERING BIT(2) 2362306a36Sopenharmony_ci#define DISP_GAMMA_SIZE 0x0030 2462306a36Sopenharmony_ci#define DISP_GAMMA_LUT 0x0700 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define LUT_10BIT_MASK 0x03ff 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct mtk_disp_gamma_data { 2962306a36Sopenharmony_ci bool has_dither; 3062306a36Sopenharmony_ci bool lut_diff; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* 3462306a36Sopenharmony_ci * struct mtk_disp_gamma - DISP_GAMMA driver structure 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_cistruct mtk_disp_gamma { 3762306a36Sopenharmony_ci struct clk *clk; 3862306a36Sopenharmony_ci void __iomem *regs; 3962306a36Sopenharmony_ci struct cmdq_client_reg cmdq_reg; 4062306a36Sopenharmony_ci const struct mtk_disp_gamma_data *data; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciint mtk_gamma_clk_enable(struct device *dev) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return clk_prepare_enable(gamma->clk); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_civoid mtk_gamma_clk_disable(struct device *dev) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci clk_disable_unprepare(gamma->clk); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_civoid mtk_gamma_set_common(void __iomem *regs, struct drm_crtc_state *state, bool lut_diff) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci unsigned int i, reg; 6062306a36Sopenharmony_ci struct drm_color_lut *lut; 6162306a36Sopenharmony_ci void __iomem *lut_base; 6262306a36Sopenharmony_ci u32 word; 6362306a36Sopenharmony_ci u32 diff[3] = {0}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (state->gamma_lut) { 6662306a36Sopenharmony_ci reg = readl(regs + DISP_GAMMA_CFG); 6762306a36Sopenharmony_ci reg = reg | GAMMA_LUT_EN; 6862306a36Sopenharmony_ci writel(reg, regs + DISP_GAMMA_CFG); 6962306a36Sopenharmony_ci lut_base = regs + DISP_GAMMA_LUT; 7062306a36Sopenharmony_ci lut = (struct drm_color_lut *)state->gamma_lut->data; 7162306a36Sopenharmony_ci for (i = 0; i < MTK_LUT_SIZE; i++) { 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!lut_diff || (i % 2 == 0)) { 7462306a36Sopenharmony_ci word = (((lut[i].red >> 6) & LUT_10BIT_MASK) << 20) + 7562306a36Sopenharmony_ci (((lut[i].green >> 6) & LUT_10BIT_MASK) << 10) + 7662306a36Sopenharmony_ci ((lut[i].blue >> 6) & LUT_10BIT_MASK); 7762306a36Sopenharmony_ci } else { 7862306a36Sopenharmony_ci diff[0] = (lut[i].red >> 6) - (lut[i - 1].red >> 6); 7962306a36Sopenharmony_ci diff[1] = (lut[i].green >> 6) - (lut[i - 1].green >> 6); 8062306a36Sopenharmony_ci diff[2] = (lut[i].blue >> 6) - (lut[i - 1].blue >> 6); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci word = ((diff[0] & LUT_10BIT_MASK) << 20) + 8362306a36Sopenharmony_ci ((diff[1] & LUT_10BIT_MASK) << 10) + 8462306a36Sopenharmony_ci (diff[2] & LUT_10BIT_MASK); 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci writel(word, (lut_base + i * 4)); 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_civoid mtk_gamma_set(struct device *dev, struct drm_crtc_state *state) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 9462306a36Sopenharmony_ci bool lut_diff = false; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (gamma->data) 9762306a36Sopenharmony_ci lut_diff = gamma->data->lut_diff; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci mtk_gamma_set_common(gamma->regs, state, lut_diff); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_civoid mtk_gamma_config(struct device *dev, unsigned int w, 10362306a36Sopenharmony_ci unsigned int h, unsigned int vrefresh, 10462306a36Sopenharmony_ci unsigned int bpc, struct cmdq_pkt *cmdq_pkt) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci mtk_ddp_write(cmdq_pkt, h << 16 | w, &gamma->cmdq_reg, gamma->regs, 10962306a36Sopenharmony_ci DISP_GAMMA_SIZE); 11062306a36Sopenharmony_ci if (gamma->data && gamma->data->has_dither) 11162306a36Sopenharmony_ci mtk_dither_set_common(gamma->regs, &gamma->cmdq_reg, bpc, 11262306a36Sopenharmony_ci DISP_GAMMA_CFG, GAMMA_DITHERING, cmdq_pkt); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_civoid mtk_gamma_start(struct device *dev) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci writel(GAMMA_EN, gamma->regs + DISP_GAMMA_EN); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_civoid mtk_gamma_stop(struct device *dev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci writel_relaxed(0x0, gamma->regs + DISP_GAMMA_EN); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int mtk_disp_gamma_bind(struct device *dev, struct device *master, 13062306a36Sopenharmony_ci void *data) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci return 0; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic void mtk_disp_gamma_unbind(struct device *dev, struct device *master, 13662306a36Sopenharmony_ci void *data) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic const struct component_ops mtk_disp_gamma_component_ops = { 14162306a36Sopenharmony_ci .bind = mtk_disp_gamma_bind, 14262306a36Sopenharmony_ci .unbind = mtk_disp_gamma_unbind, 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int mtk_disp_gamma_probe(struct platform_device *pdev) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 14862306a36Sopenharmony_ci struct mtk_disp_gamma *priv; 14962306a36Sopenharmony_ci struct resource *res; 15062306a36Sopenharmony_ci int ret; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 15362306a36Sopenharmony_ci if (!priv) 15462306a36Sopenharmony_ci return -ENOMEM; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci priv->clk = devm_clk_get(dev, NULL); 15762306a36Sopenharmony_ci if (IS_ERR(priv->clk)) { 15862306a36Sopenharmony_ci dev_err(dev, "failed to get gamma clk\n"); 15962306a36Sopenharmony_ci return PTR_ERR(priv->clk); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 16362306a36Sopenharmony_ci priv->regs = devm_ioremap_resource(dev, res); 16462306a36Sopenharmony_ci if (IS_ERR(priv->regs)) { 16562306a36Sopenharmony_ci dev_err(dev, "failed to ioremap gamma\n"); 16662306a36Sopenharmony_ci return PTR_ERR(priv->regs); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#if IS_REACHABLE(CONFIG_MTK_CMDQ) 17062306a36Sopenharmony_ci ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); 17162306a36Sopenharmony_ci if (ret) 17262306a36Sopenharmony_ci dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); 17362306a36Sopenharmony_ci#endif 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci priv->data = of_device_get_match_data(dev); 17662306a36Sopenharmony_ci platform_set_drvdata(pdev, priv); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = component_add(dev, &mtk_disp_gamma_component_ops); 17962306a36Sopenharmony_ci if (ret) 18062306a36Sopenharmony_ci dev_err(dev, "Failed to add component: %d\n", ret); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic void mtk_disp_gamma_remove(struct platform_device *pdev) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci component_del(&pdev->dev, &mtk_disp_gamma_component_ops); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct mtk_disp_gamma_data mt8173_gamma_driver_data = { 19162306a36Sopenharmony_ci .has_dither = true, 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic const struct mtk_disp_gamma_data mt8183_gamma_driver_data = { 19562306a36Sopenharmony_ci .lut_diff = true, 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic const struct of_device_id mtk_disp_gamma_driver_dt_match[] = { 19962306a36Sopenharmony_ci { .compatible = "mediatek,mt8173-disp-gamma", 20062306a36Sopenharmony_ci .data = &mt8173_gamma_driver_data}, 20162306a36Sopenharmony_ci { .compatible = "mediatek,mt8183-disp-gamma", 20262306a36Sopenharmony_ci .data = &mt8183_gamma_driver_data}, 20362306a36Sopenharmony_ci {}, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mtk_disp_gamma_driver_dt_match); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistruct platform_driver mtk_disp_gamma_driver = { 20862306a36Sopenharmony_ci .probe = mtk_disp_gamma_probe, 20962306a36Sopenharmony_ci .remove_new = mtk_disp_gamma_remove, 21062306a36Sopenharmony_ci .driver = { 21162306a36Sopenharmony_ci .name = "mediatek-disp-gamma", 21262306a36Sopenharmony_ci .owner = THIS_MODULE, 21362306a36Sopenharmony_ci .of_match_table = mtk_disp_gamma_driver_dt_match, 21462306a36Sopenharmony_ci }, 21562306a36Sopenharmony_ci}; 216