]>
Commit | Line | Data |
---|---|---|
9026e0d1 MR |
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 | ||
c222f399 MR |
86 | static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane, |
87 | u32 format, u32 *mode) | |
9026e0d1 | 88 | { |
c222f399 MR |
89 | if ((plane->type == DRM_PLANE_TYPE_PRIMARY) && |
90 | (format == DRM_FORMAT_ARGB8888)) | |
91 | format = DRM_FORMAT_XRGB8888; | |
92 | ||
9026e0d1 MR |
93 | switch (format) { |
94 | case DRM_FORMAT_ARGB8888: | |
95 | *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888; | |
96 | break; | |
97 | ||
47d7fbb3 MR |
98 | case DRM_FORMAT_ARGB4444: |
99 | *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB4444; | |
100 | break; | |
101 | ||
102 | case DRM_FORMAT_ARGB1555: | |
103 | *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB1555; | |
104 | break; | |
105 | ||
106 | case DRM_FORMAT_RGBA5551: | |
107 | *mode = SUN4I_BACKEND_LAY_FBFMT_RGBA5551; | |
108 | break; | |
109 | ||
110 | case DRM_FORMAT_RGBA4444: | |
111 | *mode = SUN4I_BACKEND_LAY_FBFMT_RGBA4444; | |
112 | break; | |
113 | ||
9026e0d1 MR |
114 | case DRM_FORMAT_XRGB8888: |
115 | *mode = SUN4I_BACKEND_LAY_FBFMT_XRGB8888; | |
116 | break; | |
117 | ||
118 | case DRM_FORMAT_RGB888: | |
119 | *mode = SUN4I_BACKEND_LAY_FBFMT_RGB888; | |
120 | break; | |
121 | ||
47d7fbb3 MR |
122 | case DRM_FORMAT_RGB565: |
123 | *mode = SUN4I_BACKEND_LAY_FBFMT_RGB565; | |
124 | break; | |
125 | ||
9026e0d1 MR |
126 | default: |
127 | return -EINVAL; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
133 | int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, | |
134 | int layer, struct drm_plane *plane) | |
135 | { | |
136 | struct drm_plane_state *state = plane->state; | |
137 | struct drm_framebuffer *fb = state->fb; | |
138 | ||
139 | DRM_DEBUG_DRIVER("Updating layer %d\n", layer); | |
140 | ||
141 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) { | |
142 | DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n", | |
143 | state->crtc_w, state->crtc_h); | |
144 | regmap_write(backend->regs, SUN4I_BACKEND_DISSIZE_REG, | |
145 | SUN4I_BACKEND_DISSIZE(state->crtc_w, | |
146 | state->crtc_h)); | |
147 | } | |
148 | ||
149 | /* Set the line width */ | |
150 | DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); | |
151 | regmap_write(backend->regs, SUN4I_BACKEND_LAYLINEWIDTH_REG(layer), | |
152 | fb->pitches[0] * 8); | |
153 | ||
154 | /* Set height and width */ | |
155 | DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", | |
156 | state->crtc_w, state->crtc_h); | |
157 | regmap_write(backend->regs, SUN4I_BACKEND_LAYSIZE_REG(layer), | |
158 | SUN4I_BACKEND_LAYSIZE(state->crtc_w, | |
159 | state->crtc_h)); | |
160 | ||
161 | /* Set base coordinates */ | |
162 | DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n", | |
163 | state->crtc_x, state->crtc_y); | |
164 | regmap_write(backend->regs, SUN4I_BACKEND_LAYCOOR_REG(layer), | |
165 | SUN4I_BACKEND_LAYCOOR(state->crtc_x, | |
166 | state->crtc_y)); | |
167 | ||
168 | return 0; | |
169 | } | |
170 | EXPORT_SYMBOL(sun4i_backend_update_layer_coord); | |
171 | ||
172 | int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, | |
173 | int layer, struct drm_plane *plane) | |
174 | { | |
175 | struct drm_plane_state *state = plane->state; | |
176 | struct drm_framebuffer *fb = state->fb; | |
177 | bool interlaced = false; | |
178 | u32 val; | |
179 | int ret; | |
180 | ||
181 | if (plane->state->crtc) | |
182 | interlaced = plane->state->crtc->state->adjusted_mode.flags | |
183 | & DRM_MODE_FLAG_INTERLACE; | |
184 | ||
185 | regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG, | |
186 | SUN4I_BACKEND_MODCTL_ITLMOD_EN, | |
187 | interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0); | |
188 | ||
189 | DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n", | |
190 | interlaced ? "on" : "off"); | |
191 | ||
438b74a5 VS |
192 | ret = sun4i_backend_drm_format_to_layer(plane, fb->format->format, |
193 | &val); | |
9026e0d1 MR |
194 | if (ret) { |
195 | DRM_DEBUG_DRIVER("Invalid format\n"); | |
196 | return val; | |
197 | } | |
198 | ||
199 | regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG1(layer), | |
200 | SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val); | |
201 | ||
202 | return 0; | |
203 | } | |
204 | EXPORT_SYMBOL(sun4i_backend_update_layer_formats); | |
205 | ||
206 | int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | |
207 | int layer, struct drm_plane *plane) | |
208 | { | |
209 | struct drm_plane_state *state = plane->state; | |
210 | struct drm_framebuffer *fb = state->fb; | |
211 | struct drm_gem_cma_object *gem; | |
212 | u32 lo_paddr, hi_paddr; | |
213 | dma_addr_t paddr; | |
214 | int bpp; | |
215 | ||
216 | /* Get the physical address of the buffer in memory */ | |
217 | gem = drm_fb_cma_get_gem_obj(fb, 0); | |
218 | ||
f1b78f0e | 219 | DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); |
9026e0d1 MR |
220 | |
221 | /* Compute the start of the displayed memory */ | |
353c8598 | 222 | bpp = fb->format->cpp[0]; |
9026e0d1 MR |
223 | paddr = gem->paddr + fb->offsets[0]; |
224 | paddr += (state->src_x >> 16) * bpp; | |
225 | paddr += (state->src_y >> 16) * fb->pitches[0]; | |
226 | ||
f1b78f0e | 227 | DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); |
9026e0d1 MR |
228 | |
229 | /* Write the 32 lower bits of the address (in bits) */ | |
230 | lo_paddr = paddr << 3; | |
231 | DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr); | |
232 | regmap_write(backend->regs, SUN4I_BACKEND_LAYFB_L32ADD_REG(layer), | |
233 | lo_paddr); | |
234 | ||
235 | /* And the upper bits */ | |
236 | hi_paddr = paddr >> 29; | |
237 | DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr); | |
238 | regmap_update_bits(backend->regs, SUN4I_BACKEND_LAYFB_H4ADD_REG, | |
239 | SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer), | |
240 | SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr)); | |
241 | ||
242 | return 0; | |
243 | } | |
244 | EXPORT_SYMBOL(sun4i_backend_update_layer_buffer); | |
245 | ||
440d2c7b MR |
246 | static int sun4i_backend_init_sat(struct device *dev) { |
247 | struct sun4i_backend *backend = dev_get_drvdata(dev); | |
248 | int ret; | |
249 | ||
250 | backend->sat_reset = devm_reset_control_get(dev, "sat"); | |
251 | if (IS_ERR(backend->sat_reset)) { | |
252 | dev_err(dev, "Couldn't get the SAT reset line\n"); | |
253 | return PTR_ERR(backend->sat_reset); | |
254 | } | |
255 | ||
256 | ret = reset_control_deassert(backend->sat_reset); | |
257 | if (ret) { | |
258 | dev_err(dev, "Couldn't deassert the SAT reset line\n"); | |
259 | return ret; | |
260 | } | |
261 | ||
262 | backend->sat_clk = devm_clk_get(dev, "sat"); | |
263 | if (IS_ERR(backend->sat_clk)) { | |
264 | dev_err(dev, "Couldn't get our SAT clock\n"); | |
265 | ret = PTR_ERR(backend->sat_clk); | |
266 | goto err_assert_reset; | |
267 | } | |
268 | ||
269 | ret = clk_prepare_enable(backend->sat_clk); | |
270 | if (ret) { | |
271 | dev_err(dev, "Couldn't enable the SAT clock\n"); | |
272 | return ret; | |
273 | } | |
274 | ||
275 | return 0; | |
276 | ||
277 | err_assert_reset: | |
278 | reset_control_assert(backend->sat_reset); | |
279 | return ret; | |
280 | } | |
281 | ||
282 | static int sun4i_backend_free_sat(struct device *dev) { | |
283 | struct sun4i_backend *backend = dev_get_drvdata(dev); | |
284 | ||
285 | clk_disable_unprepare(backend->sat_clk); | |
286 | reset_control_assert(backend->sat_reset); | |
287 | ||
288 | return 0; | |
289 | } | |
290 | ||
9026e0d1 MR |
291 | static struct regmap_config sun4i_backend_regmap_config = { |
292 | .reg_bits = 32, | |
293 | .val_bits = 32, | |
294 | .reg_stride = 4, | |
295 | .max_register = 0x5800, | |
296 | }; | |
297 | ||
298 | static int sun4i_backend_bind(struct device *dev, struct device *master, | |
299 | void *data) | |
300 | { | |
301 | struct platform_device *pdev = to_platform_device(dev); | |
302 | struct drm_device *drm = data; | |
303 | struct sun4i_drv *drv = drm->dev_private; | |
304 | struct sun4i_backend *backend; | |
305 | struct resource *res; | |
306 | void __iomem *regs; | |
307 | int i, ret; | |
308 | ||
309 | backend = devm_kzalloc(dev, sizeof(*backend), GFP_KERNEL); | |
310 | if (!backend) | |
311 | return -ENOMEM; | |
312 | dev_set_drvdata(dev, backend); | |
313 | drv->backend = backend; | |
314 | ||
315 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
316 | regs = devm_ioremap_resource(dev, res); | |
9a8aa939 | 317 | if (IS_ERR(regs)) |
9026e0d1 | 318 | return PTR_ERR(regs); |
9026e0d1 MR |
319 | |
320 | backend->regs = devm_regmap_init_mmio(dev, regs, | |
321 | &sun4i_backend_regmap_config); | |
322 | if (IS_ERR(backend->regs)) { | |
323 | dev_err(dev, "Couldn't create the backend0 regmap\n"); | |
324 | return PTR_ERR(backend->regs); | |
325 | } | |
326 | ||
327 | backend->reset = devm_reset_control_get(dev, NULL); | |
328 | if (IS_ERR(backend->reset)) { | |
329 | dev_err(dev, "Couldn't get our reset line\n"); | |
330 | return PTR_ERR(backend->reset); | |
331 | } | |
332 | ||
333 | ret = reset_control_deassert(backend->reset); | |
334 | if (ret) { | |
335 | dev_err(dev, "Couldn't deassert our reset line\n"); | |
336 | return ret; | |
337 | } | |
338 | ||
339 | backend->bus_clk = devm_clk_get(dev, "ahb"); | |
340 | if (IS_ERR(backend->bus_clk)) { | |
341 | dev_err(dev, "Couldn't get the backend bus clock\n"); | |
342 | ret = PTR_ERR(backend->bus_clk); | |
343 | goto err_assert_reset; | |
344 | } | |
345 | clk_prepare_enable(backend->bus_clk); | |
346 | ||
347 | backend->mod_clk = devm_clk_get(dev, "mod"); | |
348 | if (IS_ERR(backend->mod_clk)) { | |
349 | dev_err(dev, "Couldn't get the backend module clock\n"); | |
350 | ret = PTR_ERR(backend->mod_clk); | |
351 | goto err_disable_bus_clk; | |
352 | } | |
353 | clk_prepare_enable(backend->mod_clk); | |
354 | ||
355 | backend->ram_clk = devm_clk_get(dev, "ram"); | |
356 | if (IS_ERR(backend->ram_clk)) { | |
357 | dev_err(dev, "Couldn't get the backend RAM clock\n"); | |
358 | ret = PTR_ERR(backend->ram_clk); | |
359 | goto err_disable_mod_clk; | |
360 | } | |
361 | clk_prepare_enable(backend->ram_clk); | |
362 | ||
440d2c7b MR |
363 | if (of_device_is_compatible(dev->of_node, |
364 | "allwinner,sun8i-a33-display-backend")) { | |
365 | ret = sun4i_backend_init_sat(dev); | |
366 | if (ret) { | |
367 | dev_err(dev, "Couldn't init SAT resources\n"); | |
368 | goto err_disable_ram_clk; | |
369 | } | |
370 | } | |
371 | ||
9026e0d1 MR |
372 | /* Reset the registers */ |
373 | for (i = 0x800; i < 0x1000; i += 4) | |
374 | regmap_write(backend->regs, i, 0); | |
375 | ||
376 | /* Disable registers autoloading */ | |
377 | regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG, | |
378 | SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS); | |
379 | ||
380 | /* Enable the backend */ | |
381 | regmap_write(backend->regs, SUN4I_BACKEND_MODCTL_REG, | |
382 | SUN4I_BACKEND_MODCTL_DEBE_EN | | |
383 | SUN4I_BACKEND_MODCTL_START_CTL); | |
384 | ||
385 | return 0; | |
386 | ||
440d2c7b MR |
387 | err_disable_ram_clk: |
388 | clk_disable_unprepare(backend->ram_clk); | |
9026e0d1 MR |
389 | err_disable_mod_clk: |
390 | clk_disable_unprepare(backend->mod_clk); | |
391 | err_disable_bus_clk: | |
392 | clk_disable_unprepare(backend->bus_clk); | |
393 | err_assert_reset: | |
394 | reset_control_assert(backend->reset); | |
395 | return ret; | |
396 | } | |
397 | ||
398 | static void sun4i_backend_unbind(struct device *dev, struct device *master, | |
399 | void *data) | |
400 | { | |
401 | struct sun4i_backend *backend = dev_get_drvdata(dev); | |
402 | ||
440d2c7b MR |
403 | if (of_device_is_compatible(dev->of_node, |
404 | "allwinner,sun8i-a33-display-backend")) | |
405 | sun4i_backend_free_sat(dev); | |
406 | ||
9026e0d1 MR |
407 | clk_disable_unprepare(backend->ram_clk); |
408 | clk_disable_unprepare(backend->mod_clk); | |
409 | clk_disable_unprepare(backend->bus_clk); | |
410 | reset_control_assert(backend->reset); | |
411 | } | |
412 | ||
dfeb693d | 413 | static const struct component_ops sun4i_backend_ops = { |
9026e0d1 MR |
414 | .bind = sun4i_backend_bind, |
415 | .unbind = sun4i_backend_unbind, | |
416 | }; | |
417 | ||
418 | static int sun4i_backend_probe(struct platform_device *pdev) | |
419 | { | |
420 | return component_add(&pdev->dev, &sun4i_backend_ops); | |
421 | } | |
422 | ||
423 | static int sun4i_backend_remove(struct platform_device *pdev) | |
424 | { | |
425 | component_del(&pdev->dev, &sun4i_backend_ops); | |
426 | ||
427 | return 0; | |
428 | } | |
429 | ||
430 | static const struct of_device_id sun4i_backend_of_table[] = { | |
431 | { .compatible = "allwinner,sun5i-a13-display-backend" }, | |
49c440e8 | 432 | { .compatible = "allwinner,sun6i-a31-display-backend" }, |
4a408f1f | 433 | { .compatible = "allwinner,sun8i-a33-display-backend" }, |
9026e0d1 MR |
434 | { } |
435 | }; | |
436 | MODULE_DEVICE_TABLE(of, sun4i_backend_of_table); | |
437 | ||
438 | static struct platform_driver sun4i_backend_platform_driver = { | |
439 | .probe = sun4i_backend_probe, | |
440 | .remove = sun4i_backend_remove, | |
441 | .driver = { | |
442 | .name = "sun4i-backend", | |
443 | .of_match_table = sun4i_backend_of_table, | |
444 | }, | |
445 | }; | |
446 | module_platform_driver(sun4i_backend_platform_driver); | |
447 | ||
448 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); | |
449 | MODULE_DESCRIPTION("Allwinner A10 Display Backend Driver"); | |
450 | MODULE_LICENSE("GPL"); |