2 * Copyright (c) 2015 MediaTek Inc.
4 * YT Shen <yt.shen@mediatek.com>
5 * CK Hu <ck.hu@mediatek.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <linux/clk.h>
19 #include <linux/of_address.h>
20 #include <linux/of_irq.h>
21 #include <linux/of_platform.h>
22 #include <linux/platform_device.h>
24 #include "mtk_drm_drv.h"
25 #include "mtk_drm_plane.h"
26 #include "mtk_drm_ddp_comp.h"
27 #include "mtk_drm_crtc.h"
29 #define DISP_OD_EN 0x0000
30 #define DISP_OD_INTEN 0x0008
31 #define DISP_OD_INTSTA 0x000c
32 #define DISP_OD_CFG 0x0020
33 #define DISP_OD_SIZE 0x0030
34 #define DISP_DITHER_5 0x0114
35 #define DISP_DITHER_7 0x011c
36 #define DISP_DITHER_15 0x013c
37 #define DISP_DITHER_16 0x0140
39 #define DISP_REG_UFO_START 0x0000
41 #define DISP_COLOR_CFG_MAIN 0x0400
42 #define DISP_COLOR_START 0x0c00
43 #define DISP_COLOR_WIDTH 0x0c50
44 #define DISP_COLOR_HEIGHT 0x0c54
46 #define DISP_AAL_EN 0x0000
47 #define DISP_AAL_SIZE 0x0030
49 #define DISP_GAMMA_EN 0x0000
50 #define DISP_GAMMA_CFG 0x0020
51 #define DISP_GAMMA_SIZE 0x0030
52 #define DISP_GAMMA_LUT 0x0700
54 #define LUT_10BIT_MASK 0x03ff
56 #define COLOR_BYPASS_ALL BIT(7)
57 #define COLOR_SEQ_SEL BIT(13)
59 #define OD_RELAYMODE BIT(0)
61 #define UFO_BYPASS BIT(2)
65 #define GAMMA_EN BIT(0)
66 #define GAMMA_LUT_EN BIT(1)
68 #define DISP_DITHERING BIT(2)
69 #define DITHER_LSB_ERR_SHIFT_R(x) (((x) & 0x7) << 28)
70 #define DITHER_OVFLW_BIT_R(x) (((x) & 0x7) << 24)
71 #define DITHER_ADD_LSHIFT_R(x) (((x) & 0x7) << 20)
72 #define DITHER_ADD_RSHIFT_R(x) (((x) & 0x7) << 16)
73 #define DITHER_NEW_BIT_MODE BIT(0)
74 #define DITHER_LSB_ERR_SHIFT_B(x) (((x) & 0x7) << 28)
75 #define DITHER_OVFLW_BIT_B(x) (((x) & 0x7) << 24)
76 #define DITHER_ADD_LSHIFT_B(x) (((x) & 0x7) << 20)
77 #define DITHER_ADD_RSHIFT_B(x) (((x) & 0x7) << 16)
78 #define DITHER_LSB_ERR_SHIFT_G(x) (((x) & 0x7) << 12)
79 #define DITHER_OVFLW_BIT_G(x) (((x) & 0x7) << 8)
80 #define DITHER_ADD_LSHIFT_G(x) (((x) & 0x7) << 4)
81 #define DITHER_ADD_RSHIFT_G(x) (((x) & 0x7) << 0)
83 void mtk_dither_set(struct mtk_ddp_comp
*comp
, unsigned int bpc
,
86 /* If bpc equal to 0, the dithering function didn't be enabled */
90 if (bpc
>= MTK_MIN_BPC
) {
91 writel(0, comp
->regs
+ DISP_DITHER_5
);
92 writel(0, comp
->regs
+ DISP_DITHER_7
);
93 writel(DITHER_LSB_ERR_SHIFT_R(MTK_MAX_BPC
- bpc
) |
94 DITHER_ADD_LSHIFT_R(MTK_MAX_BPC
- bpc
) |
96 comp
->regs
+ DISP_DITHER_15
);
97 writel(DITHER_LSB_ERR_SHIFT_B(MTK_MAX_BPC
- bpc
) |
98 DITHER_ADD_LSHIFT_B(MTK_MAX_BPC
- bpc
) |
99 DITHER_LSB_ERR_SHIFT_G(MTK_MAX_BPC
- bpc
) |
100 DITHER_ADD_LSHIFT_G(MTK_MAX_BPC
- bpc
),
101 comp
->regs
+ DISP_DITHER_16
);
102 writel(DISP_DITHERING
, comp
->regs
+ CFG
);
106 static void mtk_color_config(struct mtk_ddp_comp
*comp
, unsigned int w
,
107 unsigned int h
, unsigned int vrefresh
,
110 writel(w
, comp
->regs
+ DISP_COLOR_WIDTH
);
111 writel(h
, comp
->regs
+ DISP_COLOR_HEIGHT
);
114 static void mtk_color_start(struct mtk_ddp_comp
*comp
)
116 writel(COLOR_BYPASS_ALL
| COLOR_SEQ_SEL
,
117 comp
->regs
+ DISP_COLOR_CFG_MAIN
);
118 writel(0x1, comp
->regs
+ DISP_COLOR_START
);
121 static void mtk_od_config(struct mtk_ddp_comp
*comp
, unsigned int w
,
122 unsigned int h
, unsigned int vrefresh
,
125 writel(w
<< 16 | h
, comp
->regs
+ DISP_OD_SIZE
);
126 writel(OD_RELAYMODE
, comp
->regs
+ DISP_OD_CFG
);
127 mtk_dither_set(comp
, bpc
, DISP_OD_CFG
);
130 static void mtk_od_start(struct mtk_ddp_comp
*comp
)
132 writel(1, comp
->regs
+ DISP_OD_EN
);
135 static void mtk_ufoe_start(struct mtk_ddp_comp
*comp
)
137 writel(UFO_BYPASS
, comp
->regs
+ DISP_REG_UFO_START
);
140 static void mtk_aal_config(struct mtk_ddp_comp
*comp
, unsigned int w
,
141 unsigned int h
, unsigned int vrefresh
,
144 writel(h
<< 16 | w
, comp
->regs
+ DISP_AAL_SIZE
);
147 static void mtk_aal_start(struct mtk_ddp_comp
*comp
)
149 writel(AAL_EN
, comp
->regs
+ DISP_AAL_EN
);
152 static void mtk_aal_stop(struct mtk_ddp_comp
*comp
)
154 writel_relaxed(0x0, comp
->regs
+ DISP_AAL_EN
);
157 static void mtk_gamma_config(struct mtk_ddp_comp
*comp
, unsigned int w
,
158 unsigned int h
, unsigned int vrefresh
,
161 writel(h
<< 16 | w
, comp
->regs
+ DISP_GAMMA_SIZE
);
162 mtk_dither_set(comp
, bpc
, DISP_GAMMA_CFG
);
165 static void mtk_gamma_start(struct mtk_ddp_comp
*comp
)
167 writel(GAMMA_EN
, comp
->regs
+ DISP_GAMMA_EN
);
170 static void mtk_gamma_stop(struct mtk_ddp_comp
*comp
)
172 writel_relaxed(0x0, comp
->regs
+ DISP_GAMMA_EN
);
175 static void mtk_gamma_set(struct mtk_ddp_comp
*comp
,
176 struct drm_crtc_state
*state
)
179 struct drm_color_lut
*lut
;
180 void __iomem
*lut_base
;
183 if (state
->gamma_lut
) {
184 reg
= readl(comp
->regs
+ DISP_GAMMA_CFG
);
185 reg
= reg
| GAMMA_LUT_EN
;
186 writel(reg
, comp
->regs
+ DISP_GAMMA_CFG
);
187 lut_base
= comp
->regs
+ DISP_GAMMA_LUT
;
188 lut
= (struct drm_color_lut
*)state
->gamma_lut
->data
;
189 for (i
= 0; i
< MTK_LUT_SIZE
; i
++) {
190 word
= (((lut
[i
].red
>> 6) & LUT_10BIT_MASK
) << 20) +
191 (((lut
[i
].green
>> 6) & LUT_10BIT_MASK
) << 10) +
192 ((lut
[i
].blue
>> 6) & LUT_10BIT_MASK
);
193 writel(word
, (lut_base
+ i
* 4));
198 static const struct mtk_ddp_comp_funcs ddp_aal
= {
199 .gamma_set
= mtk_gamma_set
,
200 .config
= mtk_aal_config
,
201 .start
= mtk_aal_start
,
202 .stop
= mtk_aal_stop
,
205 static const struct mtk_ddp_comp_funcs ddp_gamma
= {
206 .gamma_set
= mtk_gamma_set
,
207 .config
= mtk_gamma_config
,
208 .start
= mtk_gamma_start
,
209 .stop
= mtk_gamma_stop
,
212 static const struct mtk_ddp_comp_funcs ddp_color
= {
213 .config
= mtk_color_config
,
214 .start
= mtk_color_start
,
217 static const struct mtk_ddp_comp_funcs ddp_od
= {
218 .config
= mtk_od_config
,
219 .start
= mtk_od_start
,
222 static const struct mtk_ddp_comp_funcs ddp_ufoe
= {
223 .start
= mtk_ufoe_start
,
226 static const char * const mtk_ddp_comp_stem
[MTK_DDP_COMP_TYPE_MAX
] = {
227 [MTK_DISP_OVL
] = "ovl",
228 [MTK_DISP_RDMA
] = "rdma",
229 [MTK_DISP_WDMA
] = "wdma",
230 [MTK_DISP_COLOR
] = "color",
231 [MTK_DISP_AAL
] = "aal",
232 [MTK_DISP_GAMMA
] = "gamma",
233 [MTK_DISP_UFOE
] = "ufoe",
236 [MTK_DISP_PWM
] = "pwm",
237 [MTK_DISP_MUTEX
] = "mutex",
238 [MTK_DISP_OD
] = "od",
241 struct mtk_ddp_comp_match
{
242 enum mtk_ddp_comp_type type
;
244 const struct mtk_ddp_comp_funcs
*funcs
;
247 static const struct mtk_ddp_comp_match mtk_ddp_matches
[DDP_COMPONENT_ID_MAX
] = {
248 [DDP_COMPONENT_AAL
] = { MTK_DISP_AAL
, 0, &ddp_aal
},
249 [DDP_COMPONENT_COLOR0
] = { MTK_DISP_COLOR
, 0, &ddp_color
},
250 [DDP_COMPONENT_COLOR1
] = { MTK_DISP_COLOR
, 1, &ddp_color
},
251 [DDP_COMPONENT_DPI0
] = { MTK_DPI
, 0, NULL
},
252 [DDP_COMPONENT_DSI0
] = { MTK_DSI
, 0, NULL
},
253 [DDP_COMPONENT_DSI1
] = { MTK_DSI
, 1, NULL
},
254 [DDP_COMPONENT_GAMMA
] = { MTK_DISP_GAMMA
, 0, &ddp_gamma
},
255 [DDP_COMPONENT_OD
] = { MTK_DISP_OD
, 0, &ddp_od
},
256 [DDP_COMPONENT_OVL0
] = { MTK_DISP_OVL
, 0, NULL
},
257 [DDP_COMPONENT_OVL1
] = { MTK_DISP_OVL
, 1, NULL
},
258 [DDP_COMPONENT_PWM0
] = { MTK_DISP_PWM
, 0, NULL
},
259 [DDP_COMPONENT_RDMA0
] = { MTK_DISP_RDMA
, 0, NULL
},
260 [DDP_COMPONENT_RDMA1
] = { MTK_DISP_RDMA
, 1, NULL
},
261 [DDP_COMPONENT_RDMA2
] = { MTK_DISP_RDMA
, 2, NULL
},
262 [DDP_COMPONENT_UFOE
] = { MTK_DISP_UFOE
, 0, &ddp_ufoe
},
263 [DDP_COMPONENT_WDMA0
] = { MTK_DISP_WDMA
, 0, NULL
},
264 [DDP_COMPONENT_WDMA1
] = { MTK_DISP_WDMA
, 1, NULL
},
267 int mtk_ddp_comp_get_id(struct device_node
*node
,
268 enum mtk_ddp_comp_type comp_type
)
270 int id
= of_alias_get_id(node
, mtk_ddp_comp_stem
[comp_type
]);
273 for (i
= 0; i
< ARRAY_SIZE(mtk_ddp_matches
); i
++) {
274 if (comp_type
== mtk_ddp_matches
[i
].type
&&
275 (id
< 0 || id
== mtk_ddp_matches
[i
].alias_id
))
282 int mtk_ddp_comp_init(struct device
*dev
, struct device_node
*node
,
283 struct mtk_ddp_comp
*comp
, enum mtk_ddp_comp_id comp_id
,
284 const struct mtk_ddp_comp_funcs
*funcs
)
286 enum mtk_ddp_comp_type type
;
287 struct device_node
*larb_node
;
288 struct platform_device
*larb_pdev
;
290 if (comp_id
< 0 || comp_id
>= DDP_COMPONENT_ID_MAX
)
294 comp
->funcs
= funcs
?: mtk_ddp_matches
[comp_id
].funcs
;
296 if (comp_id
== DDP_COMPONENT_DPI0
||
297 comp_id
== DDP_COMPONENT_DSI0
||
298 comp_id
== DDP_COMPONENT_PWM0
) {
305 comp
->regs
= of_iomap(node
, 0);
306 comp
->irq
= of_irq_get(node
, 0);
307 comp
->clk
= of_clk_get(node
, 0);
308 if (IS_ERR(comp
->clk
))
311 type
= mtk_ddp_matches
[comp_id
].type
;
313 /* Only DMA capable components need the LARB property */
314 comp
->larb_dev
= NULL
;
315 if (type
!= MTK_DISP_OVL
&&
316 type
!= MTK_DISP_RDMA
&&
317 type
!= MTK_DISP_WDMA
)
320 larb_node
= of_parse_phandle(node
, "mediatek,larb", 0);
323 "Missing mediadek,larb phandle in %s node\n",
328 larb_pdev
= of_find_device_by_node(larb_node
);
330 dev_warn(dev
, "Waiting for larb device %s\n",
331 larb_node
->full_name
);
332 of_node_put(larb_node
);
333 return -EPROBE_DEFER
;
335 of_node_put(larb_node
);
337 comp
->larb_dev
= &larb_pdev
->dev
;
342 int mtk_ddp_comp_register(struct drm_device
*drm
, struct mtk_ddp_comp
*comp
)
344 struct mtk_drm_private
*private = drm
->dev_private
;
346 if (private->ddp_comp
[comp
->id
])
349 private->ddp_comp
[comp
->id
] = comp
;
353 void mtk_ddp_comp_unregister(struct drm_device
*drm
, struct mtk_ddp_comp
*comp
)
355 struct mtk_drm_private
*private = drm
->dev_private
;
357 private->ddp_comp
[comp
->id
] = NULL
;