xref: /linux/drivers/gpu/drm/sun4i/sun4i_backend.c (revision 0883c2c06fb5bcf5b9e008270827e63c09a88c1e)
1 /*
2  * Copyright (C) 2015 Free Electrons
3  * Copyright (C) 2015 NextThing Co
4  *
5  * Maxime Ripard <maxime.ripard@free-electrons.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of
10  * the License, or (at your option) any later version.
11  */
12 
13 #include <drm/drmP.h>
14 #include <drm/drm_atomic_helper.h>
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_crtc_helper.h>
17 #include <drm/drm_fb_cma_helper.h>
18 #include <drm/drm_gem_cma_helper.h>
19 #include <drm/drm_plane_helper.h>
20 
21 #include <linux/component.h>
22 #include <linux/reset.h>
23 
24 #include "sun4i_backend.h"
25 #include "sun4i_drv.h"
26 
27 static u32 sunxi_rgb2yuv_coef[12] = {
28 	0x00000107, 0x00000204, 0x00000064, 0x00000108,
29 	0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
30 	0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
31 };
32 
33 void sun4i_backend_apply_color_correction(struct sun4i_backend *backend)
34 {
35 	int i;
36 
37 	DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n");
38 
39 	/* Set color correction */
40 	regmap_write(backend->regs, SUN4I_BACKEND_OCCTL_REG,
41 		     SUN4I_BACKEND_OCCTL_ENABLE);
42 
43 	for (i = 0; i < 12; i++)
44 		regmap_write(backend->regs, SUN4I_BACKEND_OCRCOEF_REG(i),
45 			     sunxi_rgb2yuv_coef[i]);
46 }
47 EXPORT_SYMBOL(sun4i_backend_apply_color_correction);
48 
49 void sun4i_backend_disable_color_correction(struct sun4i_backend *backend)
50 {
51 	DRM_DEBUG_DRIVER("Disabling color correction\n");
52 
53 	/* Disable color correction */
54 	regmap_update_bits(backend->regs, SUN4I_BACKEND_OCCTL_REG,
55 			   SUN4I_BACKEND_OCCTL_ENABLE, 0);
56 }
57 EXPORT_SYMBOL(sun4i_backend_disable_color_correction);
58 
59 void sun4i_backend_commit(struct sun4i_backend *backend)
60 {
61 	DRM_DEBUG_DRIVER("Committing changes\n");
62 
63 	regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
64 		     SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS |
65 		     SUN4I_BACKEND_REGBUFFCTL_LOADCTL);
66 }
67 EXPORT_SYMBOL(sun4i_backend_commit);
68 
69 void sun4i_backend_layer_enable(struct sun4i_backend *backend,
70 				int layer, bool enable)
71 {
72 	u32 val;
73 
74 	DRM_DEBUG_DRIVER("Enabling layer %d\n", layer);
75 
76 	if (enable)
77 		val = SUN4I_BACKEND_MODCTL_LAY_EN(layer);
78 	else
79 		val = 0;
80 
81 	regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG,
82 			   SUN4I_BACKEND_MODCTL_LAY_EN(layer), val);
83 }
84 EXPORT_SYMBOL(sun4i_backend_layer_enable);
85 
86 static int sun4i_backend_drm_format_to_layer(u32 format, u32 *mode)
87 {
88 	switch (format) {
89 	case DRM_FORMAT_ARGB8888:
90 		*mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888;
91 		break;
92 
93 	case DRM_FORMAT_XRGB8888:
94 		*mode = SUN4I_BACKEND_LAY_FBFMT_XRGB8888;
95 		break;
96 
97 	case DRM_FORMAT_RGB888:
98 		*mode = SUN4I_BACKEND_LAY_FBFMT_RGB888;
99 		break;
100 
101 	default:
102 		return -EINVAL;
103 	}
104 
105 	return 0;
106 }
107 
108 int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
109 				     int layer, struct drm_plane *plane)
110 {
111 	struct drm_plane_state *state = plane->state;
112 	struct drm_framebuffer *fb = state->fb;
113 
114 	DRM_DEBUG_DRIVER("Updating layer %d\n", layer);
115 
116 	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
117 		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
118 				 state->crtc_w, state->crtc_h);
119 		regmap_write(backend->regs, SUN4I_BACKEND_DISSIZE_REG,
120 			     SUN4I_BACKEND_DISSIZE(state->crtc_w,
121 						   state->crtc_h));
122 	}
123 
124 	/* Set the line width */
125 	DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
126 	regmap_write(backend->regs, SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
127 		     fb->pitches[0] * 8);
128 
129 	/* Set height and width */
130 	DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n",
131 			 state->crtc_w, state->crtc_h);
132 	regmap_write(backend->regs, SUN4I_BACKEND_LAYSIZE_REG(layer),
133 		     SUN4I_BACKEND_LAYSIZE(state->crtc_w,
134 					   state->crtc_h));
135 
136 	/* Set base coordinates */
137 	DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
138 			 state->crtc_x, state->crtc_y);
139 	regmap_write(backend->regs, SUN4I_BACKEND_LAYCOOR_REG(layer),
140 		     SUN4I_BACKEND_LAYCOOR(state->crtc_x,
141 					   state->crtc_y));
142 
143 	return 0;
144 }
145 EXPORT_SYMBOL(sun4i_backend_update_layer_coord);
146 
147 int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
148 				       int layer, struct drm_plane *plane)
149 {
150 	struct drm_plane_state *state = plane->state;
151 	struct drm_framebuffer *fb = state->fb;
152 	bool interlaced = false;
153 	u32 val;
154 	int ret;
155 
156 	if (plane->state->crtc)
157 		interlaced = plane->state->crtc->state->adjusted_mode.flags
158 			& DRM_MODE_FLAG_INTERLACE;
159 
160 	regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG,
161 			   SUN4I_BACKEND_MODCTL_ITLMOD_EN,
162 			   interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
163 
164 	DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
165 			 interlaced ? "on" : "off");
166 
167 	ret = sun4i_backend_drm_format_to_layer(fb->pixel_format, &val);
168 	if (ret) {
169 		DRM_DEBUG_DRIVER("Invalid format\n");
170 		return val;
171 	}
172 
173 	regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG1(layer),
174 			   SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);
175 
176 	return 0;
177 }
178 EXPORT_SYMBOL(sun4i_backend_update_layer_formats);
179 
180 int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
181 				      int layer, struct drm_plane *plane)
182 {
183 	struct drm_plane_state *state = plane->state;
184 	struct drm_framebuffer *fb = state->fb;
185 	struct drm_gem_cma_object *gem;
186 	u32 lo_paddr, hi_paddr;
187 	dma_addr_t paddr;
188 	int bpp;
189 
190 	/* Get the physical address of the buffer in memory */
191 	gem = drm_fb_cma_get_gem_obj(fb, 0);
192 
193 	DRM_DEBUG_DRIVER("Using GEM @ 0x%x\n", gem->paddr);
194 
195 	/* Compute the start of the displayed memory */
196 	bpp = drm_format_plane_cpp(fb->pixel_format, 0);
197 	paddr = gem->paddr + fb->offsets[0];
198 	paddr += (state->src_x >> 16) * bpp;
199 	paddr += (state->src_y >> 16) * fb->pitches[0];
200 
201 	DRM_DEBUG_DRIVER("Setting buffer address to 0x%x\n", paddr);
202 
203 	/* Write the 32 lower bits of the address (in bits) */
204 	lo_paddr = paddr << 3;
205 	DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr);
206 	regmap_write(backend->regs, SUN4I_BACKEND_LAYFB_L32ADD_REG(layer),
207 		     lo_paddr);
208 
209 	/* And the upper bits */
210 	hi_paddr = paddr >> 29;
211 	DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr);
212 	regmap_update_bits(backend->regs, SUN4I_BACKEND_LAYFB_H4ADD_REG,
213 			   SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer),
214 			   SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr));
215 
216 	return 0;
217 }
218 EXPORT_SYMBOL(sun4i_backend_update_layer_buffer);
219 
220 static struct regmap_config sun4i_backend_regmap_config = {
221 	.reg_bits	= 32,
222 	.val_bits	= 32,
223 	.reg_stride	= 4,
224 	.max_register	= 0x5800,
225 };
226 
227 static int sun4i_backend_bind(struct device *dev, struct device *master,
228 			      void *data)
229 {
230 	struct platform_device *pdev = to_platform_device(dev);
231 	struct drm_device *drm = data;
232 	struct sun4i_drv *drv = drm->dev_private;
233 	struct sun4i_backend *backend;
234 	struct resource *res;
235 	void __iomem *regs;
236 	int i, ret;
237 
238 	backend = devm_kzalloc(dev, sizeof(*backend), GFP_KERNEL);
239 	if (!backend)
240 		return -ENOMEM;
241 	dev_set_drvdata(dev, backend);
242 	drv->backend = backend;
243 
244 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
245 	regs = devm_ioremap_resource(dev, res);
246 	if (IS_ERR(regs)) {
247 		dev_err(dev, "Couldn't map the backend registers\n");
248 		return PTR_ERR(regs);
249 	}
250 
251 	backend->regs = devm_regmap_init_mmio(dev, regs,
252 					      &sun4i_backend_regmap_config);
253 	if (IS_ERR(backend->regs)) {
254 		dev_err(dev, "Couldn't create the backend0 regmap\n");
255 		return PTR_ERR(backend->regs);
256 	}
257 
258 	backend->reset = devm_reset_control_get(dev, NULL);
259 	if (IS_ERR(backend->reset)) {
260 		dev_err(dev, "Couldn't get our reset line\n");
261 		return PTR_ERR(backend->reset);
262 	}
263 
264 	ret = reset_control_deassert(backend->reset);
265 	if (ret) {
266 		dev_err(dev, "Couldn't deassert our reset line\n");
267 		return ret;
268 	}
269 
270 	backend->bus_clk = devm_clk_get(dev, "ahb");
271 	if (IS_ERR(backend->bus_clk)) {
272 		dev_err(dev, "Couldn't get the backend bus clock\n");
273 		ret = PTR_ERR(backend->bus_clk);
274 		goto err_assert_reset;
275 	}
276 	clk_prepare_enable(backend->bus_clk);
277 
278 	backend->mod_clk = devm_clk_get(dev, "mod");
279 	if (IS_ERR(backend->mod_clk)) {
280 		dev_err(dev, "Couldn't get the backend module clock\n");
281 		ret = PTR_ERR(backend->mod_clk);
282 		goto err_disable_bus_clk;
283 	}
284 	clk_prepare_enable(backend->mod_clk);
285 
286 	backend->ram_clk = devm_clk_get(dev, "ram");
287 	if (IS_ERR(backend->ram_clk)) {
288 		dev_err(dev, "Couldn't get the backend RAM clock\n");
289 		ret = PTR_ERR(backend->ram_clk);
290 		goto err_disable_mod_clk;
291 	}
292 	clk_prepare_enable(backend->ram_clk);
293 
294 	/* Reset the registers */
295 	for (i = 0x800; i < 0x1000; i += 4)
296 		regmap_write(backend->regs, i, 0);
297 
298 	/* Disable registers autoloading */
299 	regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
300 		     SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS);
301 
302 	/* Enable the backend */
303 	regmap_write(backend->regs, SUN4I_BACKEND_MODCTL_REG,
304 		     SUN4I_BACKEND_MODCTL_DEBE_EN |
305 		     SUN4I_BACKEND_MODCTL_START_CTL);
306 
307 	return 0;
308 
309 err_disable_mod_clk:
310 	clk_disable_unprepare(backend->mod_clk);
311 err_disable_bus_clk:
312 	clk_disable_unprepare(backend->bus_clk);
313 err_assert_reset:
314 	reset_control_assert(backend->reset);
315 	return ret;
316 }
317 
318 static void sun4i_backend_unbind(struct device *dev, struct device *master,
319 				 void *data)
320 {
321 	struct sun4i_backend *backend = dev_get_drvdata(dev);
322 
323 	clk_disable_unprepare(backend->ram_clk);
324 	clk_disable_unprepare(backend->mod_clk);
325 	clk_disable_unprepare(backend->bus_clk);
326 	reset_control_assert(backend->reset);
327 }
328 
329 static struct component_ops sun4i_backend_ops = {
330 	.bind	= sun4i_backend_bind,
331 	.unbind	= sun4i_backend_unbind,
332 };
333 
334 static int sun4i_backend_probe(struct platform_device *pdev)
335 {
336 	return component_add(&pdev->dev, &sun4i_backend_ops);
337 }
338 
339 static int sun4i_backend_remove(struct platform_device *pdev)
340 {
341 	component_del(&pdev->dev, &sun4i_backend_ops);
342 
343 	return 0;
344 }
345 
346 static const struct of_device_id sun4i_backend_of_table[] = {
347 	{ .compatible = "allwinner,sun5i-a13-display-backend" },
348 	{ }
349 };
350 MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);
351 
352 static struct platform_driver sun4i_backend_platform_driver = {
353 	.probe		= sun4i_backend_probe,
354 	.remove		= sun4i_backend_remove,
355 	.driver		= {
356 		.name		= "sun4i-backend",
357 		.of_match_table	= sun4i_backend_of_table,
358 	},
359 };
360 module_platform_driver(sun4i_backend_platform_driver);
361 
362 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
363 MODULE_DESCRIPTION("Allwinner A10 Display Backend Driver");
364 MODULE_LICENSE("GPL");
365