1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2021 MediaTek Inc. |
4 | */ |
5 | |
6 | #include <drm/drm_fourcc.h> |
7 | #include <linux/clk.h> |
8 | #include <linux/component.h> |
9 | #include <linux/mod_devicetable.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/pm_runtime.h> |
12 | #include <linux/soc/mediatek/mtk-cmdq.h> |
13 | |
14 | #include "mtk_disp_drv.h" |
15 | #include "mtk_drm_drv.h" |
16 | #include "mtk_mdp_rdma.h" |
17 | |
18 | #define MDP_RDMA_EN 0x000 |
19 | #define FLD_ROT_ENABLE BIT(0) |
20 | #define MDP_RDMA_RESET 0x008 |
21 | #define MDP_RDMA_CON 0x020 |
22 | #define FLD_OUTPUT_10B BIT(5) |
23 | #define FLD_SIMPLE_MODE BIT(4) |
24 | #define MDP_RDMA_GMCIF_CON 0x028 |
25 | #define FLD_COMMAND_DIV BIT(0) |
26 | #define FLD_EXT_PREULTRA_EN BIT(3) |
27 | #define FLD_RD_REQ_TYPE GENMASK(7, 4) |
28 | #define VAL_RD_REQ_TYPE_BURST_8_ACCESS 7 |
29 | #define FLD_ULTRA_EN GENMASK(13, 12) |
30 | #define VAL_ULTRA_EN_ENABLE 1 |
31 | #define FLD_PRE_ULTRA_EN GENMASK(17, 16) |
32 | #define VAL_PRE_ULTRA_EN_ENABLE 1 |
33 | #define FLD_EXT_ULTRA_EN BIT(18) |
34 | #define MDP_RDMA_SRC_CON 0x030 |
35 | #define FLD_OUTPUT_ARGB BIT(25) |
36 | #define FLD_BIT_NUMBER GENMASK(19, 18) |
37 | #define FLD_SWAP BIT(14) |
38 | #define FLD_UNIFORM_CONFIG BIT(17) |
39 | #define RDMA_INPUT_10BIT BIT(18) |
40 | #define FLD_SRC_FORMAT GENMASK(3, 0) |
41 | #define MDP_RDMA_COMP_CON 0x038 |
42 | #define FLD_AFBC_EN BIT(22) |
43 | #define FLD_AFBC_YUV_TRANSFORM BIT(21) |
44 | #define FLD_UFBDC_EN BIT(12) |
45 | #define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE 0x060 |
46 | #define FLD_MF_BKGD_WB GENMASK(22, 0) |
47 | #define MDP_RDMA_MF_SRC_SIZE 0x070 |
48 | #define FLD_MF_SRC_H GENMASK(30, 16) |
49 | #define FLD_MF_SRC_W GENMASK(14, 0) |
50 | #define MDP_RDMA_MF_CLIP_SIZE 0x078 |
51 | #define FLD_MF_CLIP_H GENMASK(30, 16) |
52 | #define FLD_MF_CLIP_W GENMASK(14, 0) |
53 | #define MDP_RDMA_SRC_OFFSET_0 0x118 |
54 | #define FLD_SRC_OFFSET_0 GENMASK(31, 0) |
55 | #define MDP_RDMA_TRANSFORM_0 0x200 |
56 | #define FLD_INT_MATRIX_SEL GENMASK(27, 23) |
57 | #define FLD_TRANS_EN BIT(16) |
58 | #define MDP_RDMA_SRC_BASE_0 0xf00 |
59 | #define FLD_SRC_BASE_0 GENMASK(31, 0) |
60 | |
61 | #define RDMA_CSC_FULL709_TO_RGB 5 |
62 | #define RDMA_CSC_BT601_TO_RGB 6 |
63 | |
64 | static const u32 formats[] = { |
65 | DRM_FORMAT_XRGB8888, |
66 | DRM_FORMAT_ARGB8888, |
67 | DRM_FORMAT_BGRX8888, |
68 | DRM_FORMAT_BGRA8888, |
69 | DRM_FORMAT_ABGR8888, |
70 | DRM_FORMAT_XBGR8888, |
71 | DRM_FORMAT_RGB888, |
72 | DRM_FORMAT_BGR888, |
73 | DRM_FORMAT_RGB565, |
74 | DRM_FORMAT_UYVY, |
75 | DRM_FORMAT_YUYV, |
76 | }; |
77 | |
78 | enum rdma_format { |
79 | RDMA_INPUT_FORMAT_RGB565 = 0, |
80 | RDMA_INPUT_FORMAT_RGB888 = 1, |
81 | RDMA_INPUT_FORMAT_RGBA8888 = 2, |
82 | RDMA_INPUT_FORMAT_ARGB8888 = 3, |
83 | RDMA_INPUT_FORMAT_UYVY = 4, |
84 | RDMA_INPUT_FORMAT_YUY2 = 5, |
85 | RDMA_INPUT_FORMAT_Y8 = 7, |
86 | RDMA_INPUT_FORMAT_YV12 = 8, |
87 | RDMA_INPUT_FORMAT_UYVY_3PL = 9, |
88 | RDMA_INPUT_FORMAT_NV12 = 12, |
89 | RDMA_INPUT_FORMAT_UYVY_2PL = 13, |
90 | RDMA_INPUT_FORMAT_Y410 = 14 |
91 | }; |
92 | |
93 | struct mtk_mdp_rdma { |
94 | void __iomem *regs; |
95 | struct clk *clk; |
96 | struct cmdq_client_reg cmdq_reg; |
97 | }; |
98 | |
99 | static unsigned int rdma_fmt_convert(unsigned int fmt) |
100 | { |
101 | switch (fmt) { |
102 | default: |
103 | case DRM_FORMAT_RGB565: |
104 | return RDMA_INPUT_FORMAT_RGB565; |
105 | case DRM_FORMAT_BGR565: |
106 | return RDMA_INPUT_FORMAT_RGB565 | FLD_SWAP; |
107 | case DRM_FORMAT_RGB888: |
108 | return RDMA_INPUT_FORMAT_RGB888; |
109 | case DRM_FORMAT_BGR888: |
110 | return RDMA_INPUT_FORMAT_RGB888 | FLD_SWAP; |
111 | case DRM_FORMAT_RGBX8888: |
112 | case DRM_FORMAT_RGBA8888: |
113 | return RDMA_INPUT_FORMAT_ARGB8888; |
114 | case DRM_FORMAT_BGRX8888: |
115 | case DRM_FORMAT_BGRA8888: |
116 | return RDMA_INPUT_FORMAT_ARGB8888 | FLD_SWAP; |
117 | case DRM_FORMAT_XRGB8888: |
118 | case DRM_FORMAT_ARGB8888: |
119 | return RDMA_INPUT_FORMAT_RGBA8888; |
120 | case DRM_FORMAT_XBGR8888: |
121 | case DRM_FORMAT_ABGR8888: |
122 | return RDMA_INPUT_FORMAT_RGBA8888 | FLD_SWAP; |
123 | case DRM_FORMAT_ABGR2101010: |
124 | return RDMA_INPUT_FORMAT_RGBA8888 | FLD_SWAP | RDMA_INPUT_10BIT; |
125 | case DRM_FORMAT_ARGB2101010: |
126 | return RDMA_INPUT_FORMAT_RGBA8888 | RDMA_INPUT_10BIT; |
127 | case DRM_FORMAT_RGBA1010102: |
128 | return RDMA_INPUT_FORMAT_ARGB8888 | FLD_SWAP | RDMA_INPUT_10BIT; |
129 | case DRM_FORMAT_BGRA1010102: |
130 | return RDMA_INPUT_FORMAT_ARGB8888 | RDMA_INPUT_10BIT; |
131 | case DRM_FORMAT_UYVY: |
132 | return RDMA_INPUT_FORMAT_UYVY; |
133 | case DRM_FORMAT_YUYV: |
134 | return RDMA_INPUT_FORMAT_YUY2; |
135 | } |
136 | } |
137 | |
138 | static unsigned int rdma_color_convert(unsigned int color_encoding) |
139 | { |
140 | switch (color_encoding) { |
141 | default: |
142 | case DRM_COLOR_YCBCR_BT709: |
143 | return RDMA_CSC_FULL709_TO_RGB; |
144 | case DRM_COLOR_YCBCR_BT601: |
145 | return RDMA_CSC_BT601_TO_RGB; |
146 | } |
147 | } |
148 | |
149 | static void mtk_mdp_rdma_fifo_config(struct device *dev, struct cmdq_pkt *cmdq_pkt) |
150 | { |
151 | struct mtk_mdp_rdma *priv = dev_get_drvdata(dev); |
152 | |
153 | mtk_ddp_write_mask(cmdq_pkt, FLD_EXT_ULTRA_EN | VAL_PRE_ULTRA_EN_ENABLE << 16 | |
154 | VAL_ULTRA_EN_ENABLE << 12 | VAL_RD_REQ_TYPE_BURST_8_ACCESS << 4 | |
155 | FLD_EXT_PREULTRA_EN | FLD_COMMAND_DIV, cmdq_reg: &priv->cmdq_reg, |
156 | regs: priv->regs, MDP_RDMA_GMCIF_CON, FLD_EXT_ULTRA_EN | |
157 | FLD_PRE_ULTRA_EN | FLD_ULTRA_EN | FLD_RD_REQ_TYPE | |
158 | FLD_EXT_PREULTRA_EN | FLD_COMMAND_DIV); |
159 | } |
160 | |
161 | void mtk_mdp_rdma_start(struct device *dev, struct cmdq_pkt *cmdq_pkt) |
162 | { |
163 | struct mtk_mdp_rdma *priv = dev_get_drvdata(dev); |
164 | |
165 | mtk_ddp_write_mask(cmdq_pkt, FLD_ROT_ENABLE, cmdq_reg: &priv->cmdq_reg, |
166 | regs: priv->regs, MDP_RDMA_EN, FLD_ROT_ENABLE); |
167 | } |
168 | |
169 | void mtk_mdp_rdma_stop(struct device *dev, struct cmdq_pkt *cmdq_pkt) |
170 | { |
171 | struct mtk_mdp_rdma *priv = dev_get_drvdata(dev); |
172 | |
173 | mtk_ddp_write_mask(cmdq_pkt, value: 0, cmdq_reg: &priv->cmdq_reg, |
174 | regs: priv->regs, MDP_RDMA_EN, FLD_ROT_ENABLE); |
175 | mtk_ddp_write(cmdq_pkt, value: 1, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, MDP_RDMA_RESET); |
176 | mtk_ddp_write(cmdq_pkt, value: 0, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, MDP_RDMA_RESET); |
177 | } |
178 | |
179 | void mtk_mdp_rdma_config(struct device *dev, struct mtk_mdp_rdma_cfg *cfg, |
180 | struct cmdq_pkt *cmdq_pkt) |
181 | { |
182 | struct mtk_mdp_rdma *priv = dev_get_drvdata(dev); |
183 | const struct drm_format_info *fmt_info = drm_format_info(format: cfg->fmt); |
184 | bool csc_enable = fmt_info->is_yuv ? true : false; |
185 | unsigned int src_pitch_y = cfg->pitch; |
186 | unsigned int offset_y = 0; |
187 | |
188 | mtk_mdp_rdma_fifo_config(dev, cmdq_pkt); |
189 | |
190 | mtk_ddp_write_mask(cmdq_pkt, FLD_UNIFORM_CONFIG, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
191 | MDP_RDMA_SRC_CON, FLD_UNIFORM_CONFIG); |
192 | mtk_ddp_write_mask(cmdq_pkt, value: rdma_fmt_convert(fmt: cfg->fmt), cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
193 | MDP_RDMA_SRC_CON, FLD_SWAP | FLD_SRC_FORMAT | FLD_BIT_NUMBER); |
194 | |
195 | if (!csc_enable && fmt_info->has_alpha) |
196 | mtk_ddp_write_mask(cmdq_pkt, FLD_OUTPUT_ARGB, cmdq_reg: &priv->cmdq_reg, |
197 | regs: priv->regs, MDP_RDMA_SRC_CON, FLD_OUTPUT_ARGB); |
198 | else |
199 | mtk_ddp_write_mask(cmdq_pkt, value: 0, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
200 | MDP_RDMA_SRC_CON, FLD_OUTPUT_ARGB); |
201 | |
202 | mtk_ddp_write_mask(cmdq_pkt, value: cfg->addr0, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
203 | MDP_RDMA_SRC_BASE_0, FLD_SRC_BASE_0); |
204 | |
205 | mtk_ddp_write_mask(cmdq_pkt, value: src_pitch_y, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
206 | MDP_RDMA_MF_BKGD_SIZE_IN_BYTE, FLD_MF_BKGD_WB); |
207 | |
208 | mtk_ddp_write_mask(cmdq_pkt, value: 0, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, MDP_RDMA_COMP_CON, |
209 | FLD_AFBC_YUV_TRANSFORM | FLD_UFBDC_EN | FLD_AFBC_EN); |
210 | mtk_ddp_write_mask(cmdq_pkt, FLD_OUTPUT_10B, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
211 | MDP_RDMA_CON, FLD_OUTPUT_10B); |
212 | mtk_ddp_write_mask(cmdq_pkt, FLD_SIMPLE_MODE, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
213 | MDP_RDMA_CON, FLD_SIMPLE_MODE); |
214 | if (csc_enable) |
215 | mtk_ddp_write_mask(cmdq_pkt, value: rdma_color_convert(color_encoding: cfg->color_encoding) << 23, |
216 | cmdq_reg: &priv->cmdq_reg, regs: priv->regs, MDP_RDMA_TRANSFORM_0, |
217 | FLD_INT_MATRIX_SEL); |
218 | mtk_ddp_write_mask(cmdq_pkt, value: csc_enable << 16, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
219 | MDP_RDMA_TRANSFORM_0, FLD_TRANS_EN); |
220 | |
221 | offset_y = cfg->x_left * fmt_info->cpp[0] + cfg->y_top * src_pitch_y; |
222 | |
223 | mtk_ddp_write_mask(cmdq_pkt, value: offset_y, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
224 | MDP_RDMA_SRC_OFFSET_0, FLD_SRC_OFFSET_0); |
225 | mtk_ddp_write_mask(cmdq_pkt, value: cfg->width, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
226 | MDP_RDMA_MF_SRC_SIZE, FLD_MF_SRC_W); |
227 | mtk_ddp_write_mask(cmdq_pkt, value: cfg->height << 16, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
228 | MDP_RDMA_MF_SRC_SIZE, FLD_MF_SRC_H); |
229 | mtk_ddp_write_mask(cmdq_pkt, value: cfg->width, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
230 | MDP_RDMA_MF_CLIP_SIZE, FLD_MF_CLIP_W); |
231 | mtk_ddp_write_mask(cmdq_pkt, value: cfg->height << 16, cmdq_reg: &priv->cmdq_reg, regs: priv->regs, |
232 | MDP_RDMA_MF_CLIP_SIZE, FLD_MF_CLIP_H); |
233 | } |
234 | |
235 | const u32 *mtk_mdp_rdma_get_formats(struct device *dev) |
236 | { |
237 | return formats; |
238 | } |
239 | |
240 | size_t mtk_mdp_rdma_get_num_formats(struct device *dev) |
241 | { |
242 | return ARRAY_SIZE(formats); |
243 | } |
244 | |
245 | int mtk_mdp_rdma_power_on(struct device *dev) |
246 | { |
247 | int ret = pm_runtime_resume_and_get(dev); |
248 | |
249 | if (ret < 0) { |
250 | dev_err(dev, "Failed to power on: %d\n" , ret); |
251 | return ret; |
252 | } |
253 | return 0; |
254 | } |
255 | |
256 | void mtk_mdp_rdma_power_off(struct device *dev) |
257 | { |
258 | pm_runtime_put(dev); |
259 | } |
260 | |
261 | int mtk_mdp_rdma_clk_enable(struct device *dev) |
262 | { |
263 | struct mtk_mdp_rdma *rdma = dev_get_drvdata(dev); |
264 | |
265 | return clk_prepare_enable(clk: rdma->clk); |
266 | } |
267 | |
268 | void mtk_mdp_rdma_clk_disable(struct device *dev) |
269 | { |
270 | struct mtk_mdp_rdma *rdma = dev_get_drvdata(dev); |
271 | |
272 | clk_disable_unprepare(clk: rdma->clk); |
273 | } |
274 | |
275 | static int mtk_mdp_rdma_bind(struct device *dev, struct device *master, |
276 | void *data) |
277 | { |
278 | return 0; |
279 | } |
280 | |
281 | static void mtk_mdp_rdma_unbind(struct device *dev, struct device *master, |
282 | void *data) |
283 | { |
284 | } |
285 | |
286 | static const struct component_ops mtk_mdp_rdma_component_ops = { |
287 | .bind = mtk_mdp_rdma_bind, |
288 | .unbind = mtk_mdp_rdma_unbind, |
289 | }; |
290 | |
291 | static int mtk_mdp_rdma_probe(struct platform_device *pdev) |
292 | { |
293 | struct device *dev = &pdev->dev; |
294 | struct resource *res; |
295 | struct mtk_mdp_rdma *priv; |
296 | int ret = 0; |
297 | |
298 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
299 | if (!priv) |
300 | return -ENOMEM; |
301 | |
302 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
303 | priv->regs = devm_ioremap_resource(dev, res); |
304 | if (IS_ERR(ptr: priv->regs)) { |
305 | dev_err(dev, "failed to ioremap rdma\n" ); |
306 | return PTR_ERR(ptr: priv->regs); |
307 | } |
308 | |
309 | priv->clk = devm_clk_get(dev, NULL); |
310 | if (IS_ERR(ptr: priv->clk)) { |
311 | dev_err(dev, "failed to get rdma clk\n" ); |
312 | return PTR_ERR(ptr: priv->clk); |
313 | } |
314 | |
315 | #if IS_REACHABLE(CONFIG_MTK_CMDQ) |
316 | ret = cmdq_dev_get_client_reg(dev, client_reg: &priv->cmdq_reg, idx: 0); |
317 | if (ret) |
318 | dev_dbg(dev, "get mediatek,gce-client-reg fail!\n" ); |
319 | #endif |
320 | platform_set_drvdata(pdev, data: priv); |
321 | |
322 | pm_runtime_enable(dev); |
323 | |
324 | ret = component_add(dev, &mtk_mdp_rdma_component_ops); |
325 | if (ret != 0) { |
326 | pm_runtime_disable(dev); |
327 | dev_err(dev, "Failed to add component: %d\n" , ret); |
328 | } |
329 | return ret; |
330 | } |
331 | |
332 | static void mtk_mdp_rdma_remove(struct platform_device *pdev) |
333 | { |
334 | component_del(&pdev->dev, &mtk_mdp_rdma_component_ops); |
335 | pm_runtime_disable(dev: &pdev->dev); |
336 | } |
337 | |
338 | static const struct of_device_id mtk_mdp_rdma_driver_dt_match[] = { |
339 | { .compatible = "mediatek,mt8195-vdo1-rdma" , }, |
340 | {}, |
341 | }; |
342 | MODULE_DEVICE_TABLE(of, mtk_mdp_rdma_driver_dt_match); |
343 | |
344 | struct platform_driver mtk_mdp_rdma_driver = { |
345 | .probe = mtk_mdp_rdma_probe, |
346 | .remove_new = mtk_mdp_rdma_remove, |
347 | .driver = { |
348 | .name = "mediatek-mdp-rdma" , |
349 | .owner = THIS_MODULE, |
350 | .of_match_table = mtk_mdp_rdma_driver_dt_match, |
351 | }, |
352 | }; |
353 | |