218 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*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.h>10#include <linux/platform_device.h>11#include <linux/soc/mediatek/mtk-cmdq.h>12 13#include "mtk_crtc.h"14#include "mtk_ddp_comp.h"15#include "mtk_disp_drv.h"16#include "mtk_drm_drv.h"17 18#define DISP_CCORR_EN 0x000019#define CCORR_EN BIT(0)20#define DISP_CCORR_CFG 0x002021#define CCORR_RELAY_MODE BIT(0)22#define CCORR_ENGINE_EN BIT(1)23#define CCORR_GAMMA_OFF BIT(2)24#define CCORR_WGAMUT_SRC_CLIP BIT(3)25#define DISP_CCORR_SIZE 0x003026#define DISP_CCORR_COEF_0 0x008027#define DISP_CCORR_COEF_1 0x008428#define DISP_CCORR_COEF_2 0x008829#define DISP_CCORR_COEF_3 0x008C30#define DISP_CCORR_COEF_4 0x009031 32struct mtk_disp_ccorr_data {33 u32 matrix_bits;34};35 36struct mtk_disp_ccorr {37 struct clk *clk;38 void __iomem *regs;39 struct cmdq_client_reg cmdq_reg;40 const struct mtk_disp_ccorr_data *data;41};42 43int mtk_ccorr_clk_enable(struct device *dev)44{45 struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);46 47 return clk_prepare_enable(ccorr->clk);48}49 50void mtk_ccorr_clk_disable(struct device *dev)51{52 struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);53 54 clk_disable_unprepare(ccorr->clk);55}56 57void mtk_ccorr_config(struct device *dev, unsigned int w,58 unsigned int h, unsigned int vrefresh,59 unsigned int bpc, struct cmdq_pkt *cmdq_pkt)60{61 struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);62 63 mtk_ddp_write(cmdq_pkt, w << 16 | h, &ccorr->cmdq_reg, ccorr->regs,64 DISP_CCORR_SIZE);65 mtk_ddp_write(cmdq_pkt, CCORR_ENGINE_EN, &ccorr->cmdq_reg, ccorr->regs,66 DISP_CCORR_CFG);67}68 69void mtk_ccorr_start(struct device *dev)70{71 struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);72 73 writel(CCORR_EN, ccorr->regs + DISP_CCORR_EN);74}75 76void mtk_ccorr_stop(struct device *dev)77{78 struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);79 80 writel_relaxed(0x0, ccorr->regs + DISP_CCORR_EN);81}82 83/* Converts a DRM S31.32 value to the HW S1.n format. */84static u16 mtk_ctm_s31_32_to_s1_n(u64 in, u32 n)85{86 u16 r;87 88 /* Sign bit. */89 r = in & BIT_ULL(63) ? BIT(n + 1) : 0;90 91 if ((in & GENMASK_ULL(62, 33)) > 0) {92 /* identity value 0x100000000 -> 0x400(mt8183), */93 /* identity value 0x100000000 -> 0x800(mt8192), */94 /* if bigger this, set it to max 0x7ff. */95 r |= GENMASK(n, 0);96 } else {97 /* take the n+1 most important bits. */98 r |= (in >> (32 - n)) & GENMASK(n, 0);99 }100 101 return r;102}103 104void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state)105{106 struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);107 struct drm_property_blob *blob = state->ctm;108 struct drm_color_ctm *ctm;109 const u64 *input;110 uint16_t coeffs[9] = { 0 };111 int i;112 struct cmdq_pkt *cmdq_pkt = NULL;113 u32 matrix_bits = ccorr->data->matrix_bits;114 115 if (!blob)116 return;117 118 ctm = (struct drm_color_ctm *)blob->data;119 input = ctm->matrix;120 121 for (i = 0; i < ARRAY_SIZE(coeffs); i++)122 coeffs[i] = mtk_ctm_s31_32_to_s1_n(input[i], matrix_bits);123 124 mtk_ddp_write(cmdq_pkt, coeffs[0] << 16 | coeffs[1],125 &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_0);126 mtk_ddp_write(cmdq_pkt, coeffs[2] << 16 | coeffs[3],127 &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_1);128 mtk_ddp_write(cmdq_pkt, coeffs[4] << 16 | coeffs[5],129 &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_2);130 mtk_ddp_write(cmdq_pkt, coeffs[6] << 16 | coeffs[7],131 &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_3);132 mtk_ddp_write(cmdq_pkt, coeffs[8] << 16,133 &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_4);134}135 136static int mtk_disp_ccorr_bind(struct device *dev, struct device *master,137 void *data)138{139 return 0;140}141 142static void mtk_disp_ccorr_unbind(struct device *dev, struct device *master,143 void *data)144{145}146 147static const struct component_ops mtk_disp_ccorr_component_ops = {148 .bind = mtk_disp_ccorr_bind,149 .unbind = mtk_disp_ccorr_unbind,150};151 152static int mtk_disp_ccorr_probe(struct platform_device *pdev)153{154 struct device *dev = &pdev->dev;155 struct mtk_disp_ccorr *priv;156 int ret;157 158 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);159 if (!priv)160 return -ENOMEM;161 162 priv->clk = devm_clk_get(dev, NULL);163 if (IS_ERR(priv->clk))164 return dev_err_probe(dev, PTR_ERR(priv->clk),165 "failed to get ccorr clk\n");166 167 priv->regs = devm_platform_ioremap_resource(pdev, 0);168 if (IS_ERR(priv->regs))169 return dev_err_probe(dev, PTR_ERR(priv->regs),170 "failed to ioremap ccorr\n");171 172#if IS_REACHABLE(CONFIG_MTK_CMDQ)173 ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0);174 if (ret)175 dev_dbg(dev, "get mediatek,gce-client-reg fail!\n");176#endif177 178 priv->data = of_device_get_match_data(dev);179 platform_set_drvdata(pdev, priv);180 181 ret = component_add(dev, &mtk_disp_ccorr_component_ops);182 if (ret)183 return dev_err_probe(dev, ret, "Failed to add component\n");184 185 return 0;186}187 188static void mtk_disp_ccorr_remove(struct platform_device *pdev)189{190 component_del(&pdev->dev, &mtk_disp_ccorr_component_ops);191}192 193static const struct mtk_disp_ccorr_data mt8183_ccorr_driver_data = {194 .matrix_bits = 10,195};196 197static const struct mtk_disp_ccorr_data mt8192_ccorr_driver_data = {198 .matrix_bits = 11,199};200 201static const struct of_device_id mtk_disp_ccorr_driver_dt_match[] = {202 { .compatible = "mediatek,mt8183-disp-ccorr",203 .data = &mt8183_ccorr_driver_data},204 { .compatible = "mediatek,mt8192-disp-ccorr",205 .data = &mt8192_ccorr_driver_data},206 {},207};208MODULE_DEVICE_TABLE(of, mtk_disp_ccorr_driver_dt_match);209 210struct platform_driver mtk_disp_ccorr_driver = {211 .probe = mtk_disp_ccorr_probe,212 .remove_new = mtk_disp_ccorr_remove,213 .driver = {214 .name = "mediatek-disp-ccorr",215 .of_match_table = mtk_disp_ccorr_driver_dt_match,216 },217};218