1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2017 Free Electrons
4  * Maxime Ripard <maxime.ripard@free-electrons.com>
5  */
6 #include <drm/drmP.h>
7 #include <drm/drm_gem_cma_helper.h>
8 #include <drm/drm_fb_cma_helper.h>
9 
10 #include <linux/clk.h>
11 #include <linux/component.h>
12 #include <linux/module.h>
13 #include <linux/platform_device.h>
14 #include <linux/pm_runtime.h>
15 #include <linux/regmap.h>
16 #include <linux/reset.h>
17 
18 #include "sun4i_drv.h"
19 #include "sun4i_frontend.h"
20 
21 static const u32 sun4i_frontend_vert_coef[32] = {
22 	0x00004000, 0x000140ff, 0x00033ffe, 0x00043ffd,
23 	0x00063efc, 0xff083dfc, 0x000a3bfb, 0xff0d39fb,
24 	0xff0f37fb, 0xff1136fa, 0xfe1433fb, 0xfe1631fb,
25 	0xfd192ffb, 0xfd1c2cfb, 0xfd1f29fb, 0xfc2127fc,
26 	0xfc2424fc, 0xfc2721fc, 0xfb291ffd, 0xfb2c1cfd,
27 	0xfb2f19fd, 0xfb3116fe, 0xfb3314fe, 0xfa3611ff,
28 	0xfb370fff, 0xfb390dff, 0xfb3b0a00, 0xfc3d08ff,
29 	0xfc3e0600, 0xfd3f0400, 0xfe3f0300, 0xff400100,
30 };
31 
32 static const u32 sun4i_frontend_horz_coef[64] = {
33 	0x40000000, 0x00000000, 0x40fe0000, 0x0000ff03,
34 	0x3ffd0000, 0x0000ff05, 0x3ffc0000, 0x0000ff06,
35 	0x3efb0000, 0x0000ff08, 0x3dfb0000, 0x0000ff09,
36 	0x3bfa0000, 0x0000fe0d, 0x39fa0000, 0x0000fe0f,
37 	0x38fa0000, 0x0000fe10, 0x36fa0000, 0x0000fe12,
38 	0x33fa0000, 0x0000fd16, 0x31fa0000, 0x0000fd18,
39 	0x2ffa0000, 0x0000fd1a, 0x2cfa0000, 0x0000fc1e,
40 	0x29fa0000, 0x0000fc21, 0x27fb0000, 0x0000fb23,
41 	0x24fb0000, 0x0000fb26, 0x21fb0000, 0x0000fb29,
42 	0x1ffc0000, 0x0000fa2b, 0x1cfc0000, 0x0000fa2e,
43 	0x19fd0000, 0x0000fa30, 0x16fd0000, 0x0000fa33,
44 	0x14fd0000, 0x0000fa35, 0x11fe0000, 0x0000fa37,
45 	0x0ffe0000, 0x0000fa39, 0x0dfe0000, 0x0000fa3b,
46 	0x0afe0000, 0x0000fa3e, 0x08ff0000, 0x0000fb3e,
47 	0x06ff0000, 0x0000fb40, 0x05ff0000, 0x0000fc40,
48 	0x03ff0000, 0x0000fd41, 0x01ff0000, 0x0000fe42,
49 };
50 
51 static void sun4i_frontend_scaler_init(struct sun4i_frontend *frontend)
52 {
53 	int i;
54 
55 	for (i = 0; i < 32; i++) {
56 		regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF0_REG(i),
57 			     sun4i_frontend_horz_coef[2 * i]);
58 		regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF0_REG(i),
59 			     sun4i_frontend_horz_coef[2 * i]);
60 		regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZCOEF1_REG(i),
61 			     sun4i_frontend_horz_coef[2 * i + 1]);
62 		regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZCOEF1_REG(i),
63 			     sun4i_frontend_horz_coef[2 * i + 1]);
64 		regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTCOEF_REG(i),
65 			     sun4i_frontend_vert_coef[i]);
66 		regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTCOEF_REG(i),
67 			     sun4i_frontend_vert_coef[i]);
68 	}
69 
70 	regmap_update_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
71 			   SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL,
72 			   SUN4I_FRONTEND_FRM_CTRL_COEF_ACCESS_CTRL);
73 }
74 
75 int sun4i_frontend_init(struct sun4i_frontend *frontend)
76 {
77 	return pm_runtime_get_sync(frontend->dev);
78 }
79 EXPORT_SYMBOL(sun4i_frontend_init);
80 
81 void sun4i_frontend_exit(struct sun4i_frontend *frontend)
82 {
83 	pm_runtime_put(frontend->dev);
84 }
85 EXPORT_SYMBOL(sun4i_frontend_exit);
86 
87 void sun4i_frontend_update_buffer(struct sun4i_frontend *frontend,
88 				  struct drm_plane *plane)
89 {
90 	struct drm_plane_state *state = plane->state;
91 	struct drm_framebuffer *fb = state->fb;
92 	dma_addr_t paddr;
93 
94 	/* Set the line width */
95 	DRM_DEBUG_DRIVER("Frontend stride: %d bytes\n", fb->pitches[0]);
96 	regmap_write(frontend->regs, SUN4I_FRONTEND_LINESTRD0_REG,
97 		     fb->pitches[0]);
98 
99 	/* Set the physical address of the buffer in memory */
100 	paddr = drm_fb_cma_get_gem_addr(fb, state, 0);
101 	paddr -= PHYS_OFFSET;
102 	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
103 	regmap_write(frontend->regs, SUN4I_FRONTEND_BUF_ADDR0_REG, paddr);
104 }
105 EXPORT_SYMBOL(sun4i_frontend_update_buffer);
106 
107 static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val)
108 {
109 	switch (fmt) {
110 	case DRM_FORMAT_XRGB8888:
111 		*val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB;
112 		return 0;
113 
114 	default:
115 		return -EINVAL;
116 	}
117 }
118 
119 static int sun4i_frontend_drm_format_to_input_mode(uint32_t fmt, u32 *val)
120 {
121 	if (drm_format_num_planes(fmt) == 1)
122 		*val = SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED;
123 	else
124 		return -EINVAL;
125 
126 	return 0;
127 }
128 
129 static int sun4i_frontend_drm_format_to_input_sequence(uint32_t fmt, u32 *val)
130 {
131 	switch (fmt) {
132 	case DRM_FORMAT_BGRX8888:
133 		*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX;
134 		return 0;
135 
136 	case DRM_FORMAT_XRGB8888:
137 		*val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB;
138 		return 0;
139 
140 	default:
141 		return -EINVAL;
142 	}
143 }
144 
145 static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val)
146 {
147 	switch (fmt) {
148 	case DRM_FORMAT_BGRX8888:
149 		*val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_BGRX8888;
150 		return 0;
151 
152 	case DRM_FORMAT_XRGB8888:
153 		*val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_XRGB8888;
154 		return 0;
155 
156 	default:
157 		return -EINVAL;
158 	}
159 }
160 
161 static const uint32_t sun4i_frontend_formats[] = {
162 	DRM_FORMAT_BGRX8888,
163 	DRM_FORMAT_XRGB8888,
164 };
165 
166 bool sun4i_frontend_format_is_supported(uint32_t fmt, uint64_t modifier)
167 {
168 	unsigned int i;
169 
170 	if (modifier != DRM_FORMAT_MOD_LINEAR)
171 		return false;
172 
173 	for (i = 0; i < ARRAY_SIZE(sun4i_frontend_formats); i++)
174 		if (sun4i_frontend_formats[i] == fmt)
175 			return true;
176 
177 	return false;
178 }
179 EXPORT_SYMBOL(sun4i_frontend_format_is_supported);
180 
181 int sun4i_frontend_update_formats(struct sun4i_frontend *frontend,
182 				  struct drm_plane *plane, uint32_t out_fmt)
183 {
184 	struct drm_plane_state *state = plane->state;
185 	struct drm_framebuffer *fb = state->fb;
186 	uint32_t format = fb->format->format;
187 	u32 out_fmt_val;
188 	u32 in_fmt_val, in_mod_val, in_ps_val;
189 	int ret;
190 
191 	ret = sun4i_frontend_drm_format_to_input_fmt(format, &in_fmt_val);
192 	if (ret) {
193 		DRM_DEBUG_DRIVER("Invalid input format\n");
194 		return ret;
195 	}
196 
197 	ret = sun4i_frontend_drm_format_to_input_mode(format, &in_mod_val);
198 	if (ret) {
199 		DRM_DEBUG_DRIVER("Invalid input mode\n");
200 		return ret;
201 	}
202 
203 	ret = sun4i_frontend_drm_format_to_input_sequence(format, &in_ps_val);
204 	if (ret) {
205 		DRM_DEBUG_DRIVER("Invalid pixel sequence\n");
206 		return ret;
207 	}
208 
209 	ret = sun4i_frontend_drm_format_to_output_fmt(out_fmt, &out_fmt_val);
210 	if (ret) {
211 		DRM_DEBUG_DRIVER("Invalid output format\n");
212 		return ret;
213 	}
214 
215 	/*
216 	 * I have no idea what this does exactly, but it seems to be
217 	 * related to the scaler FIR filter phase parameters.
218 	 */
219 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZPHASE_REG, 0x400);
220 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZPHASE_REG, 0x400);
221 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE0_REG, 0x400);
222 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE0_REG, 0x400);
223 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 0x400);
224 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 0x400);
225 
226 	regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG,
227 			   SUN4I_FRONTEND_BYPASS_CSC_EN,
228 			   SUN4I_FRONTEND_BYPASS_CSC_EN);
229 
230 	regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG,
231 		     in_mod_val | in_fmt_val | in_ps_val);
232 
233 	/*
234 	 * TODO: It look like the A31 and A80 at least will need the
235 	 * bit 7 (ALPHA_EN) enabled when using a format with alpha (so
236 	 * ARGB8888).
237 	 */
238 	regmap_write(frontend->regs, SUN4I_FRONTEND_OUTPUT_FMT_REG,
239 		     out_fmt_val);
240 
241 	return 0;
242 }
243 EXPORT_SYMBOL(sun4i_frontend_update_formats);
244 
245 void sun4i_frontend_update_coord(struct sun4i_frontend *frontend,
246 				 struct drm_plane *plane)
247 {
248 	struct drm_plane_state *state = plane->state;
249 	struct drm_framebuffer *fb = state->fb;
250 	uint32_t luma_width, luma_height;
251 	uint32_t chroma_width, chroma_height;
252 
253 	/* Set height and width */
254 	DRM_DEBUG_DRIVER("Frontend size W: %u H: %u\n",
255 			 state->crtc_w, state->crtc_h);
256 
257 	luma_width = state->src_w >> 16;
258 	luma_height = state->src_h >> 16;
259 
260 	chroma_width = DIV_ROUND_UP(luma_width, fb->format->hsub);
261 	chroma_height = DIV_ROUND_UP(luma_height, fb->format->vsub);
262 
263 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_INSIZE_REG,
264 		     SUN4I_FRONTEND_INSIZE(luma_height, luma_width));
265 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_INSIZE_REG,
266 		     SUN4I_FRONTEND_INSIZE(chroma_height, chroma_width));
267 
268 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_OUTSIZE_REG,
269 		     SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w));
270 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_OUTSIZE_REG,
271 		     SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w));
272 
273 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZFACT_REG,
274 		     (luma_width << 16) / state->crtc_w);
275 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZFACT_REG,
276 		     (chroma_width << 16) / state->crtc_w);
277 
278 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTFACT_REG,
279 		     (luma_height << 16) / state->crtc_h);
280 	regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTFACT_REG,
281 		     (chroma_height << 16) / state->crtc_h);
282 
283 	regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
284 			  SUN4I_FRONTEND_FRM_CTRL_REG_RDY,
285 			  SUN4I_FRONTEND_FRM_CTRL_REG_RDY);
286 }
287 EXPORT_SYMBOL(sun4i_frontend_update_coord);
288 
289 int sun4i_frontend_enable(struct sun4i_frontend *frontend)
290 {
291 	regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG,
292 			  SUN4I_FRONTEND_FRM_CTRL_FRM_START,
293 			  SUN4I_FRONTEND_FRM_CTRL_FRM_START);
294 
295 	return 0;
296 }
297 EXPORT_SYMBOL(sun4i_frontend_enable);
298 
299 static struct regmap_config sun4i_frontend_regmap_config = {
300 	.reg_bits	= 32,
301 	.val_bits	= 32,
302 	.reg_stride	= 4,
303 	.max_register	= 0x0a14,
304 };
305 
306 static int sun4i_frontend_bind(struct device *dev, struct device *master,
307 			 void *data)
308 {
309 	struct platform_device *pdev = to_platform_device(dev);
310 	struct sun4i_frontend *frontend;
311 	struct drm_device *drm = data;
312 	struct sun4i_drv *drv = drm->dev_private;
313 	struct resource *res;
314 	void __iomem *regs;
315 
316 	frontend = devm_kzalloc(dev, sizeof(*frontend), GFP_KERNEL);
317 	if (!frontend)
318 		return -ENOMEM;
319 
320 	dev_set_drvdata(dev, frontend);
321 	frontend->dev = dev;
322 	frontend->node = dev->of_node;
323 
324 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
325 	regs = devm_ioremap_resource(dev, res);
326 	if (IS_ERR(regs))
327 		return PTR_ERR(regs);
328 
329 	frontend->regs = devm_regmap_init_mmio(dev, regs,
330 					       &sun4i_frontend_regmap_config);
331 	if (IS_ERR(frontend->regs)) {
332 		dev_err(dev, "Couldn't create the frontend regmap\n");
333 		return PTR_ERR(frontend->regs);
334 	}
335 
336 	frontend->reset = devm_reset_control_get(dev, NULL);
337 	if (IS_ERR(frontend->reset)) {
338 		dev_err(dev, "Couldn't get our reset line\n");
339 		return PTR_ERR(frontend->reset);
340 	}
341 
342 	frontend->bus_clk = devm_clk_get(dev, "ahb");
343 	if (IS_ERR(frontend->bus_clk)) {
344 		dev_err(dev, "Couldn't get our bus clock\n");
345 		return PTR_ERR(frontend->bus_clk);
346 	}
347 
348 	frontend->mod_clk = devm_clk_get(dev, "mod");
349 	if (IS_ERR(frontend->mod_clk)) {
350 		dev_err(dev, "Couldn't get our mod clock\n");
351 		return PTR_ERR(frontend->mod_clk);
352 	}
353 
354 	frontend->ram_clk = devm_clk_get(dev, "ram");
355 	if (IS_ERR(frontend->ram_clk)) {
356 		dev_err(dev, "Couldn't get our ram clock\n");
357 		return PTR_ERR(frontend->ram_clk);
358 	}
359 
360 	list_add_tail(&frontend->list, &drv->frontend_list);
361 	pm_runtime_enable(dev);
362 
363 	return 0;
364 }
365 
366 static void sun4i_frontend_unbind(struct device *dev, struct device *master,
367 			    void *data)
368 {
369 	struct sun4i_frontend *frontend = dev_get_drvdata(dev);
370 
371 	list_del(&frontend->list);
372 	pm_runtime_force_suspend(dev);
373 }
374 
375 static const struct component_ops sun4i_frontend_ops = {
376 	.bind	= sun4i_frontend_bind,
377 	.unbind	= sun4i_frontend_unbind,
378 };
379 
380 static int sun4i_frontend_probe(struct platform_device *pdev)
381 {
382 	return component_add(&pdev->dev, &sun4i_frontend_ops);
383 }
384 
385 static int sun4i_frontend_remove(struct platform_device *pdev)
386 {
387 	component_del(&pdev->dev, &sun4i_frontend_ops);
388 
389 	return 0;
390 }
391 
392 static int sun4i_frontend_runtime_resume(struct device *dev)
393 {
394 	struct sun4i_frontend *frontend = dev_get_drvdata(dev);
395 	int ret;
396 
397 	clk_set_rate(frontend->mod_clk, 300000000);
398 
399 	clk_prepare_enable(frontend->bus_clk);
400 	clk_prepare_enable(frontend->mod_clk);
401 	clk_prepare_enable(frontend->ram_clk);
402 
403 	ret = reset_control_reset(frontend->reset);
404 	if (ret) {
405 		dev_err(dev, "Couldn't reset our device\n");
406 		return ret;
407 	}
408 
409 	regmap_update_bits(frontend->regs, SUN4I_FRONTEND_EN_REG,
410 			   SUN4I_FRONTEND_EN_EN,
411 			   SUN4I_FRONTEND_EN_EN);
412 
413 	sun4i_frontend_scaler_init(frontend);
414 
415 	return 0;
416 }
417 
418 static int sun4i_frontend_runtime_suspend(struct device *dev)
419 {
420 	struct sun4i_frontend *frontend = dev_get_drvdata(dev);
421 
422 	clk_disable_unprepare(frontend->ram_clk);
423 	clk_disable_unprepare(frontend->mod_clk);
424 	clk_disable_unprepare(frontend->bus_clk);
425 
426 	reset_control_assert(frontend->reset);
427 
428 	return 0;
429 }
430 
431 static const struct dev_pm_ops sun4i_frontend_pm_ops = {
432 	.runtime_resume		= sun4i_frontend_runtime_resume,
433 	.runtime_suspend	= sun4i_frontend_runtime_suspend,
434 };
435 
436 const struct of_device_id sun4i_frontend_of_table[] = {
437 	{ .compatible = "allwinner,sun8i-a33-display-frontend" },
438 	{ }
439 };
440 EXPORT_SYMBOL(sun4i_frontend_of_table);
441 MODULE_DEVICE_TABLE(of, sun4i_frontend_of_table);
442 
443 static struct platform_driver sun4i_frontend_driver = {
444 	.probe		= sun4i_frontend_probe,
445 	.remove		= sun4i_frontend_remove,
446 	.driver		= {
447 		.name		= "sun4i-frontend",
448 		.of_match_table	= sun4i_frontend_of_table,
449 		.pm		= &sun4i_frontend_pm_ops,
450 	},
451 };
452 module_platform_driver(sun4i_frontend_driver);
453 
454 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
455 MODULE_DESCRIPTION("Allwinner A10 Display Engine Frontend Driver");
456 MODULE_LICENSE("GPL");
457