1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2015 MediaTek Inc. |
4 | */ |
5 | |
6 | #include <drm/drm_fourcc.h> |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/component.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/pm_runtime.h> |
14 | #include <linux/soc/mediatek/mtk-cmdq.h> |
15 | |
16 | #include "mtk_disp_drv.h" |
17 | #include "mtk_drm_crtc.h" |
18 | #include "mtk_drm_ddp_comp.h" |
19 | #include "mtk_drm_drv.h" |
20 | |
21 | #define DISP_REG_RDMA_INT_ENABLE 0x0000 |
22 | #define DISP_REG_RDMA_INT_STATUS 0x0004 |
23 | #define RDMA_TARGET_LINE_INT BIT(5) |
24 | #define RDMA_FIFO_UNDERFLOW_INT BIT(4) |
25 | #define RDMA_EOF_ABNORMAL_INT BIT(3) |
26 | #define RDMA_FRAME_END_INT BIT(2) |
27 | #define RDMA_FRAME_START_INT BIT(1) |
28 | #define RDMA_REG_UPDATE_INT BIT(0) |
29 | #define DISP_REG_RDMA_GLOBAL_CON 0x0010 |
30 | #define RDMA_ENGINE_EN BIT(0) |
31 | #define RDMA_MODE_MEMORY BIT(1) |
32 | #define DISP_REG_RDMA_SIZE_CON_0 0x0014 |
33 | #define RDMA_MATRIX_ENABLE BIT(17) |
34 | #define RDMA_MATRIX_INT_MTX_SEL GENMASK(23, 20) |
35 | #define RDMA_MATRIX_INT_MTX_BT601_to_RGB (6 << 20) |
36 | #define DISP_REG_RDMA_SIZE_CON_1 0x0018 |
37 | #define DISP_REG_RDMA_TARGET_LINE 0x001c |
38 | #define DISP_RDMA_MEM_CON 0x0024 |
39 | #define MEM_MODE_INPUT_FORMAT_RGB565 (0x000 << 4) |
40 | #define MEM_MODE_INPUT_FORMAT_RGB888 (0x001 << 4) |
41 | #define MEM_MODE_INPUT_FORMAT_RGBA8888 (0x002 << 4) |
42 | #define MEM_MODE_INPUT_FORMAT_ARGB8888 (0x003 << 4) |
43 | #define MEM_MODE_INPUT_FORMAT_UYVY (0x004 << 4) |
44 | #define MEM_MODE_INPUT_FORMAT_YUYV (0x005 << 4) |
45 | #define MEM_MODE_INPUT_SWAP BIT(8) |
46 | #define DISP_RDMA_MEM_SRC_PITCH 0x002c |
47 | #define DISP_RDMA_MEM_GMC_SETTING_0 0x0030 |
48 | #define DISP_REG_RDMA_FIFO_CON 0x0040 |
49 | #define RDMA_FIFO_UNDERFLOW_EN BIT(31) |
50 | #define RDMA_FIFO_PSEUDO_SIZE(bytes) (((bytes) / 16) << 16) |
51 | #define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes) ((bytes) / 16) |
52 | #define RDMA_FIFO_SIZE(rdma) ((rdma)->data->fifo_size) |
53 | #define DISP_RDMA_MEM_START_ADDR 0x0f00 |
54 | |
55 | #define RDMA_MEM_GMC 0x40402020 |
56 | |
57 | static const u32 mt8173_formats[] = { |
58 | DRM_FORMAT_XRGB8888, |
59 | DRM_FORMAT_ARGB8888, |
60 | DRM_FORMAT_BGRX8888, |
61 | DRM_FORMAT_BGRA8888, |
62 | DRM_FORMAT_ABGR8888, |
63 | DRM_FORMAT_XBGR8888, |
64 | DRM_FORMAT_RGB888, |
65 | DRM_FORMAT_BGR888, |
66 | DRM_FORMAT_RGB565, |
67 | DRM_FORMAT_UYVY, |
68 | DRM_FORMAT_YUYV, |
69 | }; |
70 | |
71 | struct mtk_disp_rdma_data { |
72 | unsigned int fifo_size; |
73 | const u32 *formats; |
74 | size_t num_formats; |
75 | }; |
76 | |
77 | /* |
78 | * struct mtk_disp_rdma - DISP_RDMA driver structure |
79 | * @data: local driver data |
80 | */ |
81 | struct mtk_disp_rdma { |
82 | struct clk *clk; |
83 | void __iomem *regs; |
84 | struct cmdq_client_reg cmdq_reg; |
85 | const struct mtk_disp_rdma_data *data; |
86 | void (*vblank_cb)(void *data); |
87 | void *vblank_cb_data; |
88 | u32 fifo_size; |
89 | }; |
90 | |
91 | static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id) |
92 | { |
93 | struct mtk_disp_rdma *priv = dev_id; |
94 | |
95 | /* Clear frame completion interrupt */ |
96 | writel(val: 0x0, addr: priv->regs + DISP_REG_RDMA_INT_STATUS); |
97 | |
98 | if (!priv->vblank_cb) |
99 | return IRQ_NONE; |
100 | |
101 | priv->vblank_cb(priv->vblank_cb_data); |
102 | |
103 | return IRQ_HANDLED; |
104 | } |
105 | |
106 | static void rdma_update_bits(struct device *dev, unsigned int reg, |
107 | unsigned int mask, unsigned int val) |
108 | { |
109 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
110 | unsigned int tmp = readl(addr: rdma->regs + reg); |
111 | |
112 | tmp = (tmp & ~mask) | (val & mask); |
113 | writel(val: tmp, addr: rdma->regs + reg); |
114 | } |
115 | |
116 | void mtk_rdma_register_vblank_cb(struct device *dev, |
117 | void (*vblank_cb)(void *), |
118 | void *vblank_cb_data) |
119 | { |
120 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
121 | |
122 | rdma->vblank_cb = vblank_cb; |
123 | rdma->vblank_cb_data = vblank_cb_data; |
124 | } |
125 | |
126 | void mtk_rdma_unregister_vblank_cb(struct device *dev) |
127 | { |
128 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
129 | |
130 | rdma->vblank_cb = NULL; |
131 | rdma->vblank_cb_data = NULL; |
132 | } |
133 | |
134 | void mtk_rdma_enable_vblank(struct device *dev) |
135 | { |
136 | rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, |
137 | RDMA_FRAME_END_INT); |
138 | } |
139 | |
140 | void mtk_rdma_disable_vblank(struct device *dev) |
141 | { |
142 | rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, val: 0); |
143 | } |
144 | |
145 | const u32 *mtk_rdma_get_formats(struct device *dev) |
146 | { |
147 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
148 | |
149 | return rdma->data->formats; |
150 | } |
151 | |
152 | size_t mtk_rdma_get_num_formats(struct device *dev) |
153 | { |
154 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
155 | |
156 | return rdma->data->num_formats; |
157 | } |
158 | |
159 | int mtk_rdma_clk_enable(struct device *dev) |
160 | { |
161 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
162 | |
163 | return clk_prepare_enable(clk: rdma->clk); |
164 | } |
165 | |
166 | void mtk_rdma_clk_disable(struct device *dev) |
167 | { |
168 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
169 | |
170 | clk_disable_unprepare(clk: rdma->clk); |
171 | } |
172 | |
173 | void mtk_rdma_start(struct device *dev) |
174 | { |
175 | rdma_update_bits(dev, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, |
176 | RDMA_ENGINE_EN); |
177 | } |
178 | |
179 | void mtk_rdma_stop(struct device *dev) |
180 | { |
181 | rdma_update_bits(dev, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, val: 0); |
182 | } |
183 | |
184 | void mtk_rdma_config(struct device *dev, unsigned int width, |
185 | unsigned int height, unsigned int vrefresh, |
186 | unsigned int bpc, struct cmdq_pkt *cmdq_pkt) |
187 | { |
188 | unsigned int threshold; |
189 | unsigned int reg; |
190 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
191 | u32 rdma_fifo_size; |
192 | |
193 | mtk_ddp_write_mask(cmdq_pkt, value: width, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, |
194 | DISP_REG_RDMA_SIZE_CON_0, mask: 0xfff); |
195 | mtk_ddp_write_mask(cmdq_pkt, value: height, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, |
196 | DISP_REG_RDMA_SIZE_CON_1, mask: 0xfffff); |
197 | |
198 | if (rdma->fifo_size) |
199 | rdma_fifo_size = rdma->fifo_size; |
200 | else |
201 | rdma_fifo_size = RDMA_FIFO_SIZE(rdma); |
202 | |
203 | /* |
204 | * Enable FIFO underflow since DSI and DPI can't be blocked. |
205 | * Keep the FIFO pseudo size reset default of 8 KiB. Set the |
206 | * output threshold to 70% of max fifo size to make sure the |
207 | * threhold will not overflow |
208 | */ |
209 | threshold = rdma_fifo_size * 7 / 10; |
210 | reg = RDMA_FIFO_UNDERFLOW_EN | |
211 | RDMA_FIFO_PSEUDO_SIZE(rdma_fifo_size) | |
212 | RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold); |
213 | mtk_ddp_write(cmdq_pkt, value: reg, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, DISP_REG_RDMA_FIFO_CON); |
214 | } |
215 | |
216 | static unsigned int rdma_fmt_convert(struct mtk_disp_rdma *rdma, |
217 | unsigned int fmt) |
218 | { |
219 | /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX" |
220 | * is defined in mediatek HW data sheet. |
221 | * The alphabet order in XXX is no relation to data |
222 | * arrangement in memory. |
223 | */ |
224 | switch (fmt) { |
225 | default: |
226 | case DRM_FORMAT_RGB565: |
227 | return MEM_MODE_INPUT_FORMAT_RGB565; |
228 | case DRM_FORMAT_BGR565: |
229 | return MEM_MODE_INPUT_FORMAT_RGB565 | MEM_MODE_INPUT_SWAP; |
230 | case DRM_FORMAT_RGB888: |
231 | return MEM_MODE_INPUT_FORMAT_RGB888; |
232 | case DRM_FORMAT_BGR888: |
233 | return MEM_MODE_INPUT_FORMAT_RGB888 | MEM_MODE_INPUT_SWAP; |
234 | case DRM_FORMAT_RGBX8888: |
235 | case DRM_FORMAT_RGBA8888: |
236 | return MEM_MODE_INPUT_FORMAT_ARGB8888; |
237 | case DRM_FORMAT_BGRX8888: |
238 | case DRM_FORMAT_BGRA8888: |
239 | return MEM_MODE_INPUT_FORMAT_ARGB8888 | MEM_MODE_INPUT_SWAP; |
240 | case DRM_FORMAT_XRGB8888: |
241 | case DRM_FORMAT_ARGB8888: |
242 | return MEM_MODE_INPUT_FORMAT_RGBA8888; |
243 | case DRM_FORMAT_XBGR8888: |
244 | case DRM_FORMAT_ABGR8888: |
245 | return MEM_MODE_INPUT_FORMAT_RGBA8888 | MEM_MODE_INPUT_SWAP; |
246 | case DRM_FORMAT_UYVY: |
247 | return MEM_MODE_INPUT_FORMAT_UYVY; |
248 | case DRM_FORMAT_YUYV: |
249 | return MEM_MODE_INPUT_FORMAT_YUYV; |
250 | } |
251 | } |
252 | |
253 | unsigned int mtk_rdma_layer_nr(struct device *dev) |
254 | { |
255 | return 1; |
256 | } |
257 | |
258 | void mtk_rdma_layer_config(struct device *dev, unsigned int idx, |
259 | struct mtk_plane_state *state, |
260 | struct cmdq_pkt *cmdq_pkt) |
261 | { |
262 | struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); |
263 | struct mtk_plane_pending_state *pending = &state->pending; |
264 | unsigned int addr = pending->addr; |
265 | unsigned int pitch = pending->pitch & 0xffff; |
266 | unsigned int fmt = pending->format; |
267 | unsigned int con; |
268 | |
269 | con = rdma_fmt_convert(rdma, fmt); |
270 | mtk_ddp_write_relaxed(cmdq_pkt, value: con, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, DISP_RDMA_MEM_CON); |
271 | |
272 | if (fmt == DRM_FORMAT_UYVY || fmt == DRM_FORMAT_YUYV) { |
273 | mtk_ddp_write_mask(cmdq_pkt, RDMA_MATRIX_ENABLE, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, |
274 | DISP_REG_RDMA_SIZE_CON_0, |
275 | RDMA_MATRIX_ENABLE); |
276 | mtk_ddp_write_mask(cmdq_pkt, RDMA_MATRIX_INT_MTX_BT601_to_RGB, |
277 | cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, DISP_REG_RDMA_SIZE_CON_0, |
278 | RDMA_MATRIX_INT_MTX_SEL); |
279 | } else { |
280 | mtk_ddp_write_mask(cmdq_pkt, value: 0, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, |
281 | DISP_REG_RDMA_SIZE_CON_0, |
282 | RDMA_MATRIX_ENABLE); |
283 | } |
284 | mtk_ddp_write_relaxed(cmdq_pkt, value: addr, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, |
285 | DISP_RDMA_MEM_START_ADDR); |
286 | mtk_ddp_write_relaxed(cmdq_pkt, value: pitch, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, |
287 | DISP_RDMA_MEM_SRC_PITCH); |
288 | mtk_ddp_write(cmdq_pkt, RDMA_MEM_GMC, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, |
289 | DISP_RDMA_MEM_GMC_SETTING_0); |
290 | mtk_ddp_write_mask(cmdq_pkt, RDMA_MODE_MEMORY, cmdq_reg: &rdma->cmdq_reg, regs: rdma->regs, |
291 | DISP_REG_RDMA_GLOBAL_CON, RDMA_MODE_MEMORY); |
292 | |
293 | } |
294 | |
295 | static int mtk_disp_rdma_bind(struct device *dev, struct device *master, |
296 | void *data) |
297 | { |
298 | return 0; |
299 | |
300 | } |
301 | |
302 | static void mtk_disp_rdma_unbind(struct device *dev, struct device *master, |
303 | void *data) |
304 | { |
305 | } |
306 | |
307 | static const struct component_ops mtk_disp_rdma_component_ops = { |
308 | .bind = mtk_disp_rdma_bind, |
309 | .unbind = mtk_disp_rdma_unbind, |
310 | }; |
311 | |
312 | static int mtk_disp_rdma_probe(struct platform_device *pdev) |
313 | { |
314 | struct device *dev = &pdev->dev; |
315 | struct mtk_disp_rdma *priv; |
316 | struct resource *res; |
317 | int irq; |
318 | int ret; |
319 | |
320 | priv = devm_kzalloc(dev, size: sizeof(*priv), GFP_KERNEL); |
321 | if (!priv) |
322 | return -ENOMEM; |
323 | |
324 | irq = platform_get_irq(pdev, 0); |
325 | if (irq < 0) |
326 | return irq; |
327 | |
328 | priv->clk = devm_clk_get(dev, NULL); |
329 | if (IS_ERR(ptr: priv->clk)) { |
330 | dev_err(dev, "failed to get rdma clk\n" ); |
331 | return PTR_ERR(ptr: priv->clk); |
332 | } |
333 | |
334 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
335 | priv->regs = devm_ioremap_resource(dev, res); |
336 | if (IS_ERR(ptr: priv->regs)) { |
337 | dev_err(dev, "failed to ioremap rdma\n" ); |
338 | return PTR_ERR(ptr: priv->regs); |
339 | } |
340 | #if IS_REACHABLE(CONFIG_MTK_CMDQ) |
341 | ret = cmdq_dev_get_client_reg(dev, client_reg: &priv->cmdq_reg, idx: 0); |
342 | if (ret) |
343 | dev_dbg(dev, "get mediatek,gce-client-reg fail!\n" ); |
344 | #endif |
345 | |
346 | if (of_find_property(np: dev->of_node, name: "mediatek,rdma-fifo-size" , lenp: &ret)) { |
347 | ret = of_property_read_u32(np: dev->of_node, |
348 | propname: "mediatek,rdma-fifo-size" , |
349 | out_value: &priv->fifo_size); |
350 | if (ret) { |
351 | dev_err(dev, "Failed to get rdma fifo size\n" ); |
352 | return ret; |
353 | } |
354 | } |
355 | |
356 | /* Disable and clear pending interrupts */ |
357 | writel(val: 0x0, addr: priv->regs + DISP_REG_RDMA_INT_ENABLE); |
358 | writel(val: 0x0, addr: priv->regs + DISP_REG_RDMA_INT_STATUS); |
359 | |
360 | ret = devm_request_irq(dev, irq, handler: mtk_disp_rdma_irq_handler, |
361 | IRQF_TRIGGER_NONE, devname: dev_name(dev), dev_id: priv); |
362 | if (ret < 0) { |
363 | dev_err(dev, "Failed to request irq %d: %d\n" , irq, ret); |
364 | return ret; |
365 | } |
366 | |
367 | priv->data = of_device_get_match_data(dev); |
368 | |
369 | platform_set_drvdata(pdev, data: priv); |
370 | |
371 | pm_runtime_enable(dev); |
372 | |
373 | ret = component_add(dev, &mtk_disp_rdma_component_ops); |
374 | if (ret) { |
375 | pm_runtime_disable(dev); |
376 | dev_err(dev, "Failed to add component: %d\n" , ret); |
377 | } |
378 | |
379 | return ret; |
380 | } |
381 | |
382 | static void mtk_disp_rdma_remove(struct platform_device *pdev) |
383 | { |
384 | component_del(&pdev->dev, &mtk_disp_rdma_component_ops); |
385 | |
386 | pm_runtime_disable(dev: &pdev->dev); |
387 | } |
388 | |
389 | static const struct mtk_disp_rdma_data mt2701_rdma_driver_data = { |
390 | .fifo_size = SZ_4K, |
391 | .formats = mt8173_formats, |
392 | .num_formats = ARRAY_SIZE(mt8173_formats), |
393 | }; |
394 | |
395 | static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = { |
396 | .fifo_size = SZ_8K, |
397 | .formats = mt8173_formats, |
398 | .num_formats = ARRAY_SIZE(mt8173_formats), |
399 | }; |
400 | |
401 | static const struct mtk_disp_rdma_data mt8183_rdma_driver_data = { |
402 | .fifo_size = 5 * SZ_1K, |
403 | .formats = mt8173_formats, |
404 | .num_formats = ARRAY_SIZE(mt8173_formats), |
405 | }; |
406 | |
407 | static const struct mtk_disp_rdma_data mt8195_rdma_driver_data = { |
408 | .fifo_size = 1920, |
409 | .formats = mt8173_formats, |
410 | .num_formats = ARRAY_SIZE(mt8173_formats), |
411 | }; |
412 | |
413 | static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = { |
414 | { .compatible = "mediatek,mt2701-disp-rdma" , |
415 | .data = &mt2701_rdma_driver_data}, |
416 | { .compatible = "mediatek,mt8173-disp-rdma" , |
417 | .data = &mt8173_rdma_driver_data}, |
418 | { .compatible = "mediatek,mt8183-disp-rdma" , |
419 | .data = &mt8183_rdma_driver_data}, |
420 | { .compatible = "mediatek,mt8195-disp-rdma" , |
421 | .data = &mt8195_rdma_driver_data}, |
422 | {}, |
423 | }; |
424 | MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match); |
425 | |
426 | struct platform_driver mtk_disp_rdma_driver = { |
427 | .probe = mtk_disp_rdma_probe, |
428 | .remove_new = mtk_disp_rdma_remove, |
429 | .driver = { |
430 | .name = "mediatek-disp-rdma" , |
431 | .owner = THIS_MODULE, |
432 | .of_match_table = mtk_disp_rdma_driver_dt_match, |
433 | }, |
434 | }; |
435 | |