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