1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2021 MediaTek Inc.
6 #include <linux/bitfield.h>
8 #include <linux/component.h>
9 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/soc/mediatek/mtk-cmdq.h>
15 #include "mtk_ddp_comp.h"
16 #include "mtk_disp_drv.h"
17 #include "mtk_drm_drv.h"
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
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)
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)
45 struct mtk_disp_gamma_data
{
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
60 struct mtk_disp_gamma
{
63 struct cmdq_client_reg cmdq_reg
;
64 const struct mtk_disp_gamma_data
*data
;
67 int mtk_gamma_clk_enable(struct device
*dev
)
69 struct mtk_disp_gamma
*gamma
= dev_get_drvdata(dev
);
71 return clk_prepare_enable(gamma
->clk
);
74 void mtk_gamma_clk_disable(struct device
*dev
)
76 struct mtk_disp_gamma
*gamma
= dev_get_drvdata(dev
);
78 clk_disable_unprepare(gamma
->clk
);
81 unsigned int mtk_gamma_get_lut_size(struct device
*dev
)
83 struct mtk_disp_gamma
*gamma
= dev_get_drvdata(dev
);
85 if (gamma
&& gamma
->data
)
86 return gamma
->data
->lut_size
;
90 static bool mtk_gamma_lut_is_descending(struct drm_color_lut
*lut
, u32 lut_size
)
93 int last_entry
= lut_size
- 1;
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
;
98 return !!(first
> last
);
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.
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
116 void mtk_gamma_set(struct device
*dev
, struct drm_crtc_state
*state
)
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
;
127 /* If there's no gamma lut there's nothing to do here. */
128 if (!state
->gamma_lut
)
131 num_lut_banks
= gamma
->data
->lut_size
/ gamma
->data
->lut_bank_size
;
132 lut
= (struct drm_color_lut
*)state
->gamma_lut
->data
;
134 /* Switch to 12 bits data mode if supported */
135 data_mode
= FIELD_PREP(DISP_GAMMA_BANK_DATA_MODE
, !!(lut_bits
== 12));
137 for (cur_bank
= 0; cur_bank
< num_lut_banks
; cur_bank
++) {
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
);
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
;
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
);
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
);
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
);
165 diff
.red
= lut
[n
].red
- lut
[n
- 1].red
;
166 diff
.red
= drm_color_lut_extract(diff
.red
, lut_bits
);
168 diff
.green
= lut
[n
].green
- lut
[n
- 1].green
;
169 diff
.green
= drm_color_lut_extract(diff
.green
, lut_bits
);
171 diff
.blue
= lut
[n
].blue
- lut
[n
- 1].blue
;
172 diff
.blue
= drm_color_lut_extract(diff
.blue
, lut_bits
);
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
);
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
);
184 writel(word
[0], lut0_base
+ i
* 4);
186 writel(word
[1], lut1_base
+ i
* 4);
190 cfg_val
= readl(gamma
->regs
+ DISP_GAMMA_CFG
);
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);
197 cfg_val
&= ~GAMMA_LUT_TYPE
;
200 /* Enable the gamma table */
201 cfg_val
|= FIELD_PREP(GAMMA_LUT_EN
, 1);
203 /* Disable RELAY mode to pass the processed image */
204 cfg_val
&= ~GAMMA_RELAY_MODE
;
206 writel(cfg_val
, gamma
->regs
+ DISP_GAMMA_CFG
);
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
)
213 struct mtk_disp_gamma
*gamma
= dev_get_drvdata(dev
);
216 sz
= FIELD_PREP(DISP_GAMMA_SIZE_HSIZE
, w
);
217 sz
|= FIELD_PREP(DISP_GAMMA_SIZE_VSIZE
, h
);
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
);
225 void mtk_gamma_start(struct device
*dev
)
227 struct mtk_disp_gamma
*gamma
= dev_get_drvdata(dev
);
229 writel(GAMMA_EN
, gamma
->regs
+ DISP_GAMMA_EN
);
232 void mtk_gamma_stop(struct device
*dev
)
234 struct mtk_disp_gamma
*gamma
= dev_get_drvdata(dev
);
236 writel_relaxed(0x0, gamma
->regs
+ DISP_GAMMA_EN
);
239 static int mtk_disp_gamma_bind(struct device
*dev
, struct device
*master
,
245 static void mtk_disp_gamma_unbind(struct device
*dev
, struct device
*master
,
250 static const struct component_ops mtk_disp_gamma_component_ops
= {
251 .bind
= mtk_disp_gamma_bind
,
252 .unbind
= mtk_disp_gamma_unbind
,
255 static int mtk_disp_gamma_probe(struct platform_device
*pdev
)
257 struct device
*dev
= &pdev
->dev
;
258 struct mtk_disp_gamma
*priv
;
259 struct resource
*res
;
262 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
266 priv
->clk
= devm_clk_get(dev
, NULL
);
267 if (IS_ERR(priv
->clk
))
268 return dev_err_probe(dev
, PTR_ERR(priv
->clk
),
269 "failed to get gamma clk\n");
271 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
272 priv
->regs
= devm_ioremap_resource(dev
, res
);
273 if (IS_ERR(priv
->regs
))
274 return dev_err_probe(dev
, PTR_ERR(priv
->regs
),
275 "failed to ioremap gamma\n");
277 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
278 ret
= cmdq_dev_get_client_reg(dev
, &priv
->cmdq_reg
, 0);
280 dev_dbg(dev
, "get mediatek,gce-client-reg fail!\n");
283 priv
->data
= of_device_get_match_data(dev
);
284 platform_set_drvdata(pdev
, priv
);
286 ret
= component_add(dev
, &mtk_disp_gamma_component_ops
);
288 return dev_err_probe(dev
, ret
, "Failed to add component\n");
293 static void mtk_disp_gamma_remove(struct platform_device
*pdev
)
295 component_del(&pdev
->dev
, &mtk_disp_gamma_component_ops
);
298 static const struct mtk_disp_gamma_data mt8173_gamma_driver_data
= {
300 .lut_bank_size
= 512,
305 static const struct mtk_disp_gamma_data mt8183_gamma_driver_data
= {
306 .lut_bank_size
= 512,
312 static const struct mtk_disp_gamma_data mt8195_gamma_driver_data
= {
313 .lut_bank_size
= 256,
319 static const struct of_device_id mtk_disp_gamma_driver_dt_match
[] = {
320 { .compatible
= "mediatek,mt8173-disp-gamma",
321 .data
= &mt8173_gamma_driver_data
},
322 { .compatible
= "mediatek,mt8183-disp-gamma",
323 .data
= &mt8183_gamma_driver_data
},
324 { .compatible
= "mediatek,mt8195-disp-gamma",
325 .data
= &mt8195_gamma_driver_data
},
328 MODULE_DEVICE_TABLE(of
, mtk_disp_gamma_driver_dt_match
);
330 struct platform_driver mtk_disp_gamma_driver
= {
331 .probe
= mtk_disp_gamma_probe
,
332 .remove
= mtk_disp_gamma_remove
,
334 .name
= "mediatek-disp-gamma",
335 .of_match_table
= mtk_disp_gamma_driver_dt_match
,