1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2021 MediaTek Inc. 4 */ 5 6 #include <linux/bitfield.h> 7 #include <linux/clk.h> 8 #include <linux/component.h> 9 #include <linux/module.h> 10 #include <linux/of.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 #include "mtk_drm_drv.h" 18 19 #define DISP_GAMMA_EN 0x0000 20 #define GAMMA_EN BIT(0) 21 #define DISP_GAMMA_CFG 0x0020 22 #define GAMMA_RELAY_MODE BIT(0) 23 #define GAMMA_LUT_EN BIT(1) 24 #define GAMMA_DITHERING BIT(2) 25 #define GAMMA_LUT_TYPE BIT(2) 26 #define DISP_GAMMA_SIZE 0x0030 27 #define DISP_GAMMA_SIZE_HSIZE GENMASK(28, 16) 28 #define DISP_GAMMA_SIZE_VSIZE GENMASK(12, 0) 29 #define DISP_GAMMA_BANK 0x0100 30 #define DISP_GAMMA_BANK_BANK GENMASK(1, 0) 31 #define DISP_GAMMA_BANK_DATA_MODE BIT(2) 32 #define DISP_GAMMA_LUT 0x0700 33 #define DISP_GAMMA_LUT1 0x0b00 34 35 /* For 10 bit LUT layout, R/G/B are in the same register */ 36 #define DISP_GAMMA_LUT_10BIT_R GENMASK(29, 20) 37 #define DISP_GAMMA_LUT_10BIT_G GENMASK(19, 10) 38 #define DISP_GAMMA_LUT_10BIT_B GENMASK(9, 0) 39 40 /* For 12 bit LUT layout, R/G are in LUT, B is in LUT1 */ 41 #define DISP_GAMMA_LUT_12BIT_R GENMASK(11, 0) 42 #define DISP_GAMMA_LUT_12BIT_G GENMASK(23, 12) 43 #define DISP_GAMMA_LUT_12BIT_B GENMASK(11, 0) 44 45 struct mtk_disp_gamma_data { 46 bool has_dither; 47 bool lut_diff; 48 u16 lut_bank_size; 49 u16 lut_size; 50 u8 lut_bits; 51 }; 52 53 /** 54 * struct mtk_disp_gamma - Display Gamma driver structure 55 * @clk: clock for DISP_GAMMA block 56 * @regs: MMIO registers base 57 * @cmdq_reg: CMDQ Client register 58 * @data: platform data for DISP_GAMMA 59 */ 60 struct mtk_disp_gamma { 61 struct clk *clk; 62 void __iomem *regs; 63 struct cmdq_client_reg cmdq_reg; 64 const struct mtk_disp_gamma_data *data; 65 }; 66 67 int mtk_gamma_clk_enable(struct device *dev) 68 { 69 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 70 71 return clk_prepare_enable(gamma->clk); 72 } 73 74 void mtk_gamma_clk_disable(struct device *dev) 75 { 76 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 77 78 clk_disable_unprepare(gamma->clk); 79 } 80 81 unsigned int mtk_gamma_get_lut_size(struct device *dev) 82 { 83 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 84 85 if (gamma && gamma->data) 86 return gamma->data->lut_size; 87 return 0; 88 } 89 90 static bool mtk_gamma_lut_is_descending(struct drm_color_lut *lut, u32 lut_size) 91 { 92 u64 first, last; 93 int last_entry = lut_size - 1; 94 95 first = lut[0].red + lut[0].green + lut[0].blue; 96 last = lut[last_entry].red + lut[last_entry].green + lut[last_entry].blue; 97 98 return !!(first > last); 99 } 100 101 /* 102 * SoCs supporting 12-bits LUTs are using a new register layout that does 103 * always support (by HW) both 12-bits and 10-bits LUT but, on those, we 104 * ignore the support for 10-bits in this driver and always use 12-bits. 105 * 106 * Summarizing: 107 * - SoC HW support 9/10-bits LUT only 108 * - Old register layout 109 * - 10-bits LUT supported 110 * - 9-bits LUT not supported 111 * - SoC HW support both 10/12bits LUT 112 * - New register layout 113 * - 12-bits LUT supported 114 * - 10-its LUT not supported 115 */ 116 void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state) 117 { 118 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 119 void __iomem *lut0_base = gamma->regs + DISP_GAMMA_LUT; 120 void __iomem *lut1_base = gamma->regs + DISP_GAMMA_LUT1; 121 u32 cfg_val, data_mode, lbank_val, word[2]; 122 u8 lut_bits = gamma->data->lut_bits; 123 int cur_bank, num_lut_banks; 124 struct drm_color_lut *lut; 125 unsigned int i; 126 127 /* If there's no gamma lut there's nothing to do here. */ 128 if (!state->gamma_lut) 129 return; 130 131 num_lut_banks = gamma->data->lut_size / gamma->data->lut_bank_size; 132 lut = (struct drm_color_lut *)state->gamma_lut->data; 133 134 /* Switch to 12 bits data mode if supported */ 135 data_mode = FIELD_PREP(DISP_GAMMA_BANK_DATA_MODE, !!(lut_bits == 12)); 136 137 for (cur_bank = 0; cur_bank < num_lut_banks; cur_bank++) { 138 139 /* Switch gamma bank and set data mode before writing LUT */ 140 if (num_lut_banks > 1) { 141 lbank_val = FIELD_PREP(DISP_GAMMA_BANK_BANK, cur_bank); 142 lbank_val |= data_mode; 143 writel(lbank_val, gamma->regs + DISP_GAMMA_BANK); 144 } 145 146 for (i = 0; i < gamma->data->lut_bank_size; i++) { 147 int n = cur_bank * gamma->data->lut_bank_size + i; 148 struct drm_color_lut diff, hwlut; 149 150 hwlut.red = drm_color_lut_extract(lut[n].red, lut_bits); 151 hwlut.green = drm_color_lut_extract(lut[n].green, lut_bits); 152 hwlut.blue = drm_color_lut_extract(lut[n].blue, lut_bits); 153 154 if (!gamma->data->lut_diff || (i % 2 == 0)) { 155 if (lut_bits == 12) { 156 word[0] = FIELD_PREP(DISP_GAMMA_LUT_12BIT_R, hwlut.red); 157 word[0] |= FIELD_PREP(DISP_GAMMA_LUT_12BIT_G, hwlut.green); 158 word[1] = FIELD_PREP(DISP_GAMMA_LUT_12BIT_B, hwlut.blue); 159 } else { 160 word[0] = FIELD_PREP(DISP_GAMMA_LUT_10BIT_R, hwlut.red); 161 word[0] |= FIELD_PREP(DISP_GAMMA_LUT_10BIT_G, hwlut.green); 162 word[0] |= FIELD_PREP(DISP_GAMMA_LUT_10BIT_B, hwlut.blue); 163 } 164 } else { 165 diff.red = lut[n].red - lut[n - 1].red; 166 diff.red = drm_color_lut_extract(diff.red, lut_bits); 167 168 diff.green = lut[n].green - lut[n - 1].green; 169 diff.green = drm_color_lut_extract(diff.green, lut_bits); 170 171 diff.blue = lut[n].blue - lut[n - 1].blue; 172 diff.blue = drm_color_lut_extract(diff.blue, lut_bits); 173 174 if (lut_bits == 12) { 175 word[0] = FIELD_PREP(DISP_GAMMA_LUT_12BIT_R, diff.red); 176 word[0] |= FIELD_PREP(DISP_GAMMA_LUT_12BIT_G, diff.green); 177 word[1] = FIELD_PREP(DISP_GAMMA_LUT_12BIT_B, diff.blue); 178 } else { 179 word[0] = FIELD_PREP(DISP_GAMMA_LUT_10BIT_R, diff.red); 180 word[0] |= FIELD_PREP(DISP_GAMMA_LUT_10BIT_G, diff.green); 181 word[0] |= FIELD_PREP(DISP_GAMMA_LUT_10BIT_B, diff.blue); 182 } 183 } 184 writel(word[0], lut0_base + i * 4); 185 if (lut_bits == 12) 186 writel(word[1], lut1_base + i * 4); 187 } 188 } 189 190 cfg_val = readl(gamma->regs + DISP_GAMMA_CFG); 191 192 if (!gamma->data->has_dither) { 193 /* Descending or Rising LUT */ 194 if (mtk_gamma_lut_is_descending(lut, gamma->data->lut_size - 1)) 195 cfg_val |= FIELD_PREP(GAMMA_LUT_TYPE, 1); 196 else 197 cfg_val &= ~GAMMA_LUT_TYPE; 198 } 199 200 /* Enable the gamma table */ 201 cfg_val |= FIELD_PREP(GAMMA_LUT_EN, 1); 202 203 /* Disable RELAY mode to pass the processed image */ 204 cfg_val &= ~GAMMA_RELAY_MODE; 205 206 writel(cfg_val, gamma->regs + DISP_GAMMA_CFG); 207 } 208 209 void mtk_gamma_config(struct device *dev, unsigned int w, 210 unsigned int h, unsigned int vrefresh, 211 unsigned int bpc, struct cmdq_pkt *cmdq_pkt) 212 { 213 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 214 u32 sz; 215 216 sz = FIELD_PREP(DISP_GAMMA_SIZE_HSIZE, w); 217 sz |= FIELD_PREP(DISP_GAMMA_SIZE_VSIZE, h); 218 219 mtk_ddp_write(cmdq_pkt, sz, &gamma->cmdq_reg, gamma->regs, DISP_GAMMA_SIZE); 220 if (gamma->data && gamma->data->has_dither) 221 mtk_dither_set_common(gamma->regs, &gamma->cmdq_reg, bpc, 222 DISP_GAMMA_CFG, GAMMA_DITHERING, cmdq_pkt); 223 } 224 225 void mtk_gamma_start(struct device *dev) 226 { 227 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 228 229 writel(GAMMA_EN, gamma->regs + DISP_GAMMA_EN); 230 } 231 232 void mtk_gamma_stop(struct device *dev) 233 { 234 struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); 235 236 writel_relaxed(0x0, gamma->regs + DISP_GAMMA_EN); 237 } 238 239 static int mtk_disp_gamma_bind(struct device *dev, struct device *master, 240 void *data) 241 { 242 return 0; 243 } 244 245 static void mtk_disp_gamma_unbind(struct device *dev, struct device *master, 246 void *data) 247 { 248 } 249 250 static const struct component_ops mtk_disp_gamma_component_ops = { 251 .bind = mtk_disp_gamma_bind, 252 .unbind = mtk_disp_gamma_unbind, 253 }; 254 255 static int mtk_disp_gamma_probe(struct platform_device *pdev) 256 { 257 struct device *dev = &pdev->dev; 258 struct mtk_disp_gamma *priv; 259 struct resource *res; 260 int ret; 261 262 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 263 if (!priv) 264 return -ENOMEM; 265 266 priv->clk = devm_clk_get(dev, NULL); 267 if (IS_ERR(priv->clk)) { 268 dev_err(dev, "failed to get gamma clk\n"); 269 return PTR_ERR(priv->clk); 270 } 271 272 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 273 priv->regs = devm_ioremap_resource(dev, res); 274 if (IS_ERR(priv->regs)) { 275 dev_err(dev, "failed to ioremap gamma\n"); 276 return PTR_ERR(priv->regs); 277 } 278 279 #if IS_REACHABLE(CONFIG_MTK_CMDQ) 280 ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); 281 if (ret) 282 dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); 283 #endif 284 285 priv->data = of_device_get_match_data(dev); 286 platform_set_drvdata(pdev, priv); 287 288 ret = component_add(dev, &mtk_disp_gamma_component_ops); 289 if (ret) 290 dev_err(dev, "Failed to add component: %d\n", ret); 291 292 return ret; 293 } 294 295 static void mtk_disp_gamma_remove(struct platform_device *pdev) 296 { 297 component_del(&pdev->dev, &mtk_disp_gamma_component_ops); 298 } 299 300 static const struct mtk_disp_gamma_data mt8173_gamma_driver_data = { 301 .has_dither = true, 302 .lut_bank_size = 512, 303 .lut_bits = 10, 304 .lut_size = 512, 305 }; 306 307 static const struct mtk_disp_gamma_data mt8183_gamma_driver_data = { 308 .lut_bank_size = 512, 309 .lut_bits = 10, 310 .lut_diff = true, 311 .lut_size = 512, 312 }; 313 314 static const struct mtk_disp_gamma_data mt8195_gamma_driver_data = { 315 .lut_bank_size = 256, 316 .lut_bits = 12, 317 .lut_diff = true, 318 .lut_size = 1024, 319 }; 320 321 static const struct of_device_id mtk_disp_gamma_driver_dt_match[] = { 322 { .compatible = "mediatek,mt8173-disp-gamma", 323 .data = &mt8173_gamma_driver_data}, 324 { .compatible = "mediatek,mt8183-disp-gamma", 325 .data = &mt8183_gamma_driver_data}, 326 { .compatible = "mediatek,mt8195-disp-gamma", 327 .data = &mt8195_gamma_driver_data}, 328 {}, 329 }; 330 MODULE_DEVICE_TABLE(of, mtk_disp_gamma_driver_dt_match); 331 332 struct platform_driver mtk_disp_gamma_driver = { 333 .probe = mtk_disp_gamma_probe, 334 .remove_new = mtk_disp_gamma_remove, 335 .driver = { 336 .name = "mediatek-disp-gamma", 337 .owner = THIS_MODULE, 338 .of_match_table = mtk_disp_gamma_driver_dt_match, 339 }, 340 }; 341