LiteX: driver for LiteVideo
[linux/fpc-iii.git] / drivers / gpu / drm / litevideo / litevideo.c
blob61d96db32a9caa194556d2515cc200c3e816c531
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2020 Antmicro <www.antmicro.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <drm/drm_device.h>
16 #include <drm/drm_drv.h>
17 #include <drm/drm_ioctl.h>
18 #include <drm/drm_prime.h>
19 #include <linux/module.h>
20 #include <linux/of.h>
21 #include <linux/wait.h>
22 #include <linux/platform_device.h>
23 #include <linux/types.h>
24 #include <linux/litex.h>
25 #include <linux/errno.h>
27 #include "litevideo.h"
28 #include "mmcm.h"
30 #define DRIVER_NAME "litevideo"
32 #define DMA_DISABLE 0
33 #define DMA_ENABLE 1
35 static void litevideo_get_md(u32 pixel_clock, u32 *best_m, u32 *best_d)
37 u32 ideal_m, ideal_d, curr_m, curr_d, m, d;
38 u32 curr_diff, test_diff;
40 ideal_m = pixel_clock;
41 ideal_d = LITEVIDEO_IDEAL_DIV_VALUE;
43 /* Start searching from minimum values */
45 curr_m = MMCM_MIN_M;
46 curr_d = MMCM_MIN_D;
48 for (d = MMCM_MIN_D; d < MMCM_MAX_D; d++) {
49 for (m = MMCM_MIN_M; m < MMCM_MAX_M; m++) {
51 /* Clocks cannot be set perfectly, therefore all
52 * combinations for multiplier (m) and divisor (d)
53 * are checked to find the closest possible clock value
56 curr_diff = abs((d * ideal_d * curr_m) -
57 (d * curr_d * ideal_m));
58 test_diff = abs((curr_d * ideal_d * m) -
59 (d * curr_d * ideal_m));
61 if (test_diff < curr_diff) {
62 curr_m = m;
63 curr_d = d;
68 *best_m = curr_m;
69 *best_d = curr_d;
72 static int litevideo_mmcm_write(struct litevideo_prv *prv, u32 addr, u32 data)
74 /* write MMCM register address */
76 litex_set_reg(prv->base + LITEVIDEO_MMCM_ADDR_OFF,
77 LITEVIDEO_MMCM_ADDR_SIZE, addr);
79 /* write data to send to MMCM register */
81 litex_set_reg(prv->base + LITEVIDEO_MMCM_DATA_OFF,
82 LITEVIDEO_MMCM_DATA_SIZE, data);
84 /* send the data */
86 litex_set_reg(prv->base + LITEVIDEO_MMCM_WRITE_OFF,
87 LITEVIDEO_MMCM_WRITE_SIZE, MMCM_WRITE);
89 /* wait for transfer finish */
91 if (!wait_event_timeout(prv->wq,
92 litex_get_reg(prv->base + LITEVIDEO_MMCM_READY_OFF,
93 LITEVIDEO_MMCM_READY_SIZE), HZ))
94 return -ETIMEDOUT;
96 return 0;
99 static int litevideo_clkgen_write(struct litevideo_prv *prv, u32 m, u32 d)
101 /* write M */
103 int ret;
105 ret = litevideo_mmcm_write(prv, MMCM_CLKFBOUT1,
106 MMCM_HT_FALLING_EDGE |
107 (m / 2) << MMCM_HT_SHIFT |
108 (m / 2 + (m % 2)) << MMCM_LT_SHIFT);
110 if (ret < 0)
111 return ret;
113 /* write D */
115 if (d == 1)
116 ret = litevideo_mmcm_write(prv, MMCM_DIVCLK,
117 MMCM_HT_FALLING_EDGE);
118 else
119 ret = litevideo_mmcm_write(prv, MMCM_DIVCLK,
120 (d / 2) << MMCM_HT_SHIFT |
121 (d / 2 + (d % 2)) << MMCM_LT_SHIFT);
123 if (ret < 0)
124 return ret;
126 /* clkout0_divide = 10 */
128 ret = litevideo_mmcm_write(prv, MMCM_CLKOUT0,
129 MMCM_HT_FALLING_EDGE |
130 MMCM_CLKOUT_DIV10 << MMCM_HT_SHIFT |
131 MMCM_CLKOUT_DIV10 << MMCM_LT_SHIFT);
133 if (ret < 0)
134 return ret;
136 /* clkout1_divide = 2 */
138 ret = litevideo_mmcm_write(prv, MMCM_CLKOUT1,
139 MMCM_HT_FALLING_EDGE |
140 MMCM_CLKOUT_DIV2 << MMCM_HT_SHIFT |
141 MMCM_CLKOUT_DIV2 << MMCM_LT_SHIFT);
143 return ret;
146 static int litevideo_drv_init(struct litevideo_prv *prv, u32 m, u32 d)
148 int ret;
150 /* initialize waitqueue for timeouts in litex_mmcm_write() */
152 init_waitqueue_head(&prv->wq);
154 /* generate clock */
156 ret = litevideo_clkgen_write(prv, m, d);
157 if (ret < 0)
158 return ret;
160 /* timings - horizontal */
162 litex_set_reg(prv->base + LITEVIDEO_CORE_HRES_OFF,
163 LITEVIDEO_CORE_HRES_SIZE, prv->h_active);
164 litex_set_reg(prv->base + LITEVIDEO_CORE_HSYNC_START_OFF,
165 LITEVIDEO_CORE_HSYNC_START_SIZE,
166 prv->h_active + prv->h_front_porch);
167 litex_set_reg(prv->base + LITEVIDEO_CORE_HSYNC_END_OFF,
168 LITEVIDEO_CORE_HSYNC_END_SIZE,
169 prv->h_active + prv->h_front_porch + prv->v_sync);
170 litex_set_reg(prv->base + LITEVIDEO_CORE_HSCAN_OFF,
171 LITEVIDEO_CORE_HSCAN_SIZE,
172 prv->h_active + prv->h_blanking);
174 /* timings - vertical */
176 litex_set_reg(prv->base + LITEVIDEO_CORE_VRES_OFF,
177 LITEVIDEO_CORE_VRES_SIZE, prv->v_active);
178 litex_set_reg(prv->base + LITEVIDEO_CORE_VSYNC_START_OFF,
179 LITEVIDEO_CORE_VSYNC_START_SIZE,
180 prv->v_active + prv->v_front_porch);
181 litex_set_reg(prv->base + LITEVIDEO_CORE_VSYNC_END_OFF,
182 LITEVIDEO_CORE_VSYNC_END_SIZE,
183 prv->v_active + prv->v_front_porch + prv->v_sync);
184 litex_set_reg(prv->base + LITEVIDEO_CORE_VSCAN_OFF,
185 LITEVIDEO_CORE_VSCAN_SIZE,
186 prv->v_active + prv->v_blanking);
188 /* configure DMA */
190 litex_set_reg(prv->base + LITEVIDEO_DMA_ENABLE_OFF,
191 LITEVIDEO_DMA_ENABLE_SIZE, DMA_DISABLE);
193 litex_set_reg(prv->base + LITEVIDEO_DMA_BASE_ADDR_OFF,
194 LITEVIDEO_DMA_BASE_ADDR_SIZE, prv->dma_offset);
196 litex_set_reg(prv->base + LITEVIDEO_DMA_LENGTH_OFF,
197 LITEVIDEO_DMA_LENGTH_SIZE, prv->dma_length);
199 litex_set_reg(prv->base + LITEVIDEO_DMA_ENABLE_OFF,
200 LITEVIDEO_DMA_ENABLE_SIZE, DMA_ENABLE);
202 return 0;
205 static int litevideo_probe(struct platform_device *pdev)
207 struct device_node *np = pdev->dev.of_node;
208 struct litevideo_prv *prv;
209 struct resource *res;
210 int ret;
211 u32 val;
212 u32 m, d;
214 /* no device tree */
216 if (!np)
217 return -ENODEV;
219 prv = devm_kzalloc(&pdev->dev, sizeof(*prv), GFP_KERNEL);
220 if (!prv)
221 return -ENOMEM;
223 /* pixel clock */
225 ret = of_property_read_u32(np, "litevideo,pixel-clock", &val);
226 if (ret)
227 return -EINVAL;
228 prv->pixel_clock = val;
230 /* timings - vertical */
232 ret = of_property_read_u32(np, "litevideo,v-active", &val);
233 if (ret)
234 return -EINVAL;
235 prv->v_active = val;
237 ret = of_property_read_u32(np, "litevideo,v-blanking", &val);
238 if (ret)
239 return -EINVAL;
240 prv->v_blanking = val;
242 ret = of_property_read_u32(np, "litevideo,v-front-porch", &val);
243 if (ret)
244 return -EINVAL;
245 prv->v_front_porch = val;
247 ret = of_property_read_u32(np, "litevideo,v-sync", &val);
248 if (ret)
249 return -EINVAL;
250 prv->v_sync = val;
252 /* timings - horizontal */
254 ret = of_property_read_u32(np, "litevideo,h-active", &val);
255 if (ret)
256 return -EINVAL;
257 prv->h_active = val;
259 ret = of_property_read_u32(np, "litevideo,h-blanking", &val);
260 if (ret)
261 return -EINVAL;
262 prv->h_blanking = val;
264 ret = of_property_read_u32(np, "litevideo,h-front-porch", &val);
265 if (ret)
266 return -EINVAL;
267 prv->h_front_porch = val;
269 ret = of_property_read_u32(np, "litevideo,h-sync", &val);
270 if (ret)
271 return -EINVAL;
272 prv->h_sync = val;
274 /* DMA */
276 ret = of_property_read_u32(np, "litevideo,dma-offset", &val);
277 if (ret)
278 return -EINVAL;
279 prv->dma_offset = val;
281 ret = of_property_read_u32(np, "litevideo,dma-length", &val);
282 if (ret)
283 return -EINVAL;
284 prv->dma_length = val;
286 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
287 prv->base = devm_ioremap_resource(&pdev->dev, res);
288 if (!prv->base)
289 return -ENXIO;
291 litevideo_get_md(prv->pixel_clock, &m, &d);
292 return litevideo_drv_init(prv, m, d);
295 static const struct of_device_id litevideo_of_match[] = {
296 { .compatible = "litex,litevideo" },
299 MODULE_DEVICE_TABLE(of, litevideo_of_match);
301 static struct platform_driver litevideo_platform_driver = {
302 .probe = litevideo_probe,
303 .driver = {
304 .name = DRIVER_NAME,
305 .owner = THIS_MODULE,
306 .of_match_table = litevideo_of_match,
310 module_platform_driver(litevideo_platform_driver);
312 MODULE_LICENSE("GPL");
313 MODULE_DESCRIPTION("LiteVideo driver");
314 MODULE_AUTHOR("Antmicro <www.antmicro.com>");
315 MODULE_ALIAS("platform:" DRIVER_NAME);