1 /* 2 * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io> 3 * 4 * Based on sun4i_backend.c, which is: 5 * Copyright (C) 2015 Free Electrons 6 * Copyright (C) 2015 NextThing Co 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License as 10 * published by the Free Software Foundation; either version 2 of 11 * the License, or (at your option) any later version. 12 */ 13 14 #include <drm/drmP.h> 15 #include <drm/drm_atomic_helper.h> 16 #include <drm/drm_crtc.h> 17 #include <drm/drm_crtc_helper.h> 18 #include <drm/drm_fb_cma_helper.h> 19 #include <drm/drm_gem_cma_helper.h> 20 #include <drm/drm_plane_helper.h> 21 22 #include <linux/component.h> 23 #include <linux/dma-mapping.h> 24 #include <linux/reset.h> 25 #include <linux/of_device.h> 26 27 #include "sun4i_drv.h" 28 #include "sun8i_mixer.h" 29 #include "sun8i_layer.h" 30 #include "sunxi_engine.h" 31 32 static void sun8i_mixer_commit(struct sunxi_engine *engine) 33 { 34 DRM_DEBUG_DRIVER("Committing changes\n"); 35 36 regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, 37 SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); 38 } 39 40 void sun8i_mixer_layer_enable(struct sun8i_mixer *mixer, 41 int layer, bool enable) 42 { 43 u32 val; 44 /* Currently the first UI channel is used */ 45 int chan = mixer->cfg->vi_num; 46 47 DRM_DEBUG_DRIVER("Enabling layer %d in channel %d\n", layer, chan); 48 49 if (enable) 50 val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN; 51 else 52 val = 0; 53 54 regmap_update_bits(mixer->engine.regs, 55 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), 56 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); 57 58 /* Set the alpha configuration */ 59 regmap_update_bits(mixer->engine.regs, 60 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), 61 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK, 62 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_DEF); 63 regmap_update_bits(mixer->engine.regs, 64 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), 65 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK, 66 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_DEF); 67 } 68 69 static int sun8i_mixer_drm_format_to_layer(struct drm_plane *plane, 70 u32 format, u32 *mode) 71 { 72 switch (format) { 73 case DRM_FORMAT_ARGB8888: 74 *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_ARGB8888; 75 break; 76 77 case DRM_FORMAT_XRGB8888: 78 *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_XRGB8888; 79 break; 80 81 case DRM_FORMAT_RGB888: 82 *mode = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_RGB888; 83 break; 84 85 default: 86 return -EINVAL; 87 } 88 89 return 0; 90 } 91 92 int sun8i_mixer_update_layer_coord(struct sun8i_mixer *mixer, 93 int layer, struct drm_plane *plane) 94 { 95 struct drm_plane_state *state = plane->state; 96 struct drm_framebuffer *fb = state->fb; 97 /* Currently the first UI channel is used */ 98 int chan = mixer->cfg->vi_num; 99 100 DRM_DEBUG_DRIVER("Updating layer %d\n", layer); 101 102 if (plane->type == DRM_PLANE_TYPE_PRIMARY) { 103 DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n", 104 state->crtc_w, state->crtc_h); 105 regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_SIZE, 106 SUN8I_MIXER_SIZE(state->crtc_w, 107 state->crtc_h)); 108 DRM_DEBUG_DRIVER("Updating blender size\n"); 109 regmap_write(mixer->engine.regs, 110 SUN8I_MIXER_BLEND_ATTR_INSIZE(0), 111 SUN8I_MIXER_SIZE(state->crtc_w, 112 state->crtc_h)); 113 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTSIZE, 114 SUN8I_MIXER_SIZE(state->crtc_w, 115 state->crtc_h)); 116 DRM_DEBUG_DRIVER("Updating channel size\n"); 117 regmap_write(mixer->engine.regs, 118 SUN8I_MIXER_CHAN_UI_OVL_SIZE(chan), 119 SUN8I_MIXER_SIZE(state->crtc_w, 120 state->crtc_h)); 121 } 122 123 /* Set the line width */ 124 DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]); 125 regmap_write(mixer->engine.regs, 126 SUN8I_MIXER_CHAN_UI_LAYER_PITCH(chan, layer), 127 fb->pitches[0]); 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(mixer->engine.regs, 133 SUN8I_MIXER_CHAN_UI_LAYER_SIZE(chan, layer), 134 SUN8I_MIXER_SIZE(state->crtc_w, 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(mixer->engine.regs, 140 SUN8I_MIXER_CHAN_UI_LAYER_COORD(chan, layer), 141 SUN8I_MIXER_COORD(state->crtc_x, state->crtc_y)); 142 143 return 0; 144 } 145 146 int sun8i_mixer_update_layer_formats(struct sun8i_mixer *mixer, 147 int layer, struct drm_plane *plane) 148 { 149 struct drm_plane_state *state = plane->state; 150 struct drm_framebuffer *fb = state->fb; 151 bool interlaced = false; 152 u32 val; 153 /* Currently the first UI channel is used */ 154 int chan = mixer->cfg->vi_num; 155 int ret; 156 157 if (plane->state->crtc) 158 interlaced = plane->state->crtc->state->adjusted_mode.flags 159 & DRM_MODE_FLAG_INTERLACE; 160 161 regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_OUTCTL, 162 SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, 163 interlaced ? 164 SUN8I_MIXER_BLEND_OUTCTL_INTERLACED : 0); 165 166 DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", 167 interlaced ? "on" : "off"); 168 169 ret = sun8i_mixer_drm_format_to_layer(plane, fb->format->format, 170 &val); 171 if (ret) { 172 DRM_DEBUG_DRIVER("Invalid format\n"); 173 return ret; 174 } 175 176 regmap_update_bits(mixer->engine.regs, 177 SUN8I_MIXER_CHAN_UI_LAYER_ATTR(chan, layer), 178 SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val); 179 180 return 0; 181 } 182 183 int sun8i_mixer_update_layer_buffer(struct sun8i_mixer *mixer, 184 int layer, struct drm_plane *plane) 185 { 186 struct drm_plane_state *state = plane->state; 187 struct drm_framebuffer *fb = state->fb; 188 struct drm_gem_cma_object *gem; 189 dma_addr_t paddr; 190 /* Currently the first UI channel is used */ 191 int chan = mixer->cfg->vi_num; 192 int bpp; 193 194 /* Get the physical address of the buffer in memory */ 195 gem = drm_fb_cma_get_gem_obj(fb, 0); 196 197 DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr); 198 199 /* Compute the start of the displayed memory */ 200 bpp = fb->format->cpp[0]; 201 paddr = gem->paddr + fb->offsets[0]; 202 203 /* Fixup framebuffer address for src coordinates */ 204 paddr += (state->src_x >> 16) * bpp; 205 paddr += (state->src_y >> 16) * fb->pitches[0]; 206 207 /* 208 * The hardware cannot correctly deal with negative crtc 209 * coordinates, the display is cropped to the requested size, 210 * but the display content is not moved. 211 * Manually move the display content by fixup the framebuffer 212 * address when crtc_x or crtc_y is negative, like what we 213 * have did for src_x and src_y. 214 */ 215 if (state->crtc_x < 0) 216 paddr += -state->crtc_x * bpp; 217 if (state->crtc_y < 0) 218 paddr += -state->crtc_y * fb->pitches[0]; 219 220 DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); 221 222 regmap_write(mixer->engine.regs, 223 SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(chan, layer), 224 lower_32_bits(paddr)); 225 226 return 0; 227 } 228 229 static const struct sunxi_engine_ops sun8i_engine_ops = { 230 .commit = sun8i_mixer_commit, 231 .layers_init = sun8i_layers_init, 232 }; 233 234 static struct regmap_config sun8i_mixer_regmap_config = { 235 .reg_bits = 32, 236 .val_bits = 32, 237 .reg_stride = 4, 238 .max_register = 0xbfffc, /* guessed */ 239 }; 240 241 static int sun8i_mixer_bind(struct device *dev, struct device *master, 242 void *data) 243 { 244 struct platform_device *pdev = to_platform_device(dev); 245 struct drm_device *drm = data; 246 struct sun4i_drv *drv = drm->dev_private; 247 struct sun8i_mixer *mixer; 248 struct resource *res; 249 void __iomem *regs; 250 int i, ret; 251 252 /* 253 * The mixer uses single 32-bit register to store memory 254 * addresses, so that it cannot deal with 64-bit memory 255 * addresses. 256 * Restrict the DMA mask so that the mixer won't be 257 * allocated some memory that is too high. 258 */ 259 ret = dma_set_mask(dev, DMA_BIT_MASK(32)); 260 if (ret) { 261 dev_err(dev, "Cannot do 32-bit DMA.\n"); 262 return ret; 263 } 264 265 mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); 266 if (!mixer) 267 return -ENOMEM; 268 dev_set_drvdata(dev, mixer); 269 mixer->engine.ops = &sun8i_engine_ops; 270 mixer->engine.node = dev->of_node; 271 /* The ID of the mixer currently doesn't matter */ 272 mixer->engine.id = -1; 273 274 mixer->cfg = of_device_get_match_data(dev); 275 if (!mixer->cfg) 276 return -EINVAL; 277 278 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 279 regs = devm_ioremap_resource(dev, res); 280 if (IS_ERR(regs)) 281 return PTR_ERR(regs); 282 283 mixer->engine.regs = devm_regmap_init_mmio(dev, regs, 284 &sun8i_mixer_regmap_config); 285 if (IS_ERR(mixer->engine.regs)) { 286 dev_err(dev, "Couldn't create the mixer regmap\n"); 287 return PTR_ERR(mixer->engine.regs); 288 } 289 290 mixer->reset = devm_reset_control_get(dev, NULL); 291 if (IS_ERR(mixer->reset)) { 292 dev_err(dev, "Couldn't get our reset line\n"); 293 return PTR_ERR(mixer->reset); 294 } 295 296 ret = reset_control_deassert(mixer->reset); 297 if (ret) { 298 dev_err(dev, "Couldn't deassert our reset line\n"); 299 return ret; 300 } 301 302 mixer->bus_clk = devm_clk_get(dev, "bus"); 303 if (IS_ERR(mixer->bus_clk)) { 304 dev_err(dev, "Couldn't get the mixer bus clock\n"); 305 ret = PTR_ERR(mixer->bus_clk); 306 goto err_assert_reset; 307 } 308 clk_prepare_enable(mixer->bus_clk); 309 310 mixer->mod_clk = devm_clk_get(dev, "mod"); 311 if (IS_ERR(mixer->mod_clk)) { 312 dev_err(dev, "Couldn't get the mixer module clock\n"); 313 ret = PTR_ERR(mixer->mod_clk); 314 goto err_disable_bus_clk; 315 } 316 clk_prepare_enable(mixer->mod_clk); 317 318 list_add_tail(&mixer->engine.list, &drv->engine_list); 319 320 /* Reset the registers */ 321 for (i = 0x0; i < 0x20000; i += 4) 322 regmap_write(mixer->engine.regs, i, 0); 323 324 /* Enable the mixer */ 325 regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, 326 SUN8I_MIXER_GLOBAL_CTL_RT_EN); 327 328 /* Initialize blender */ 329 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_FCOLOR_CTL, 330 SUN8I_MIXER_BLEND_FCOLOR_CTL_DEF); 331 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PREMULTIPLY, 332 SUN8I_MIXER_BLEND_PREMULTIPLY_DEF); 333 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR, 334 SUN8I_MIXER_BLEND_BKCOLOR_DEF); 335 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_MODE(0), 336 SUN8I_MIXER_BLEND_MODE_DEF); 337 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_CK_CTL, 338 SUN8I_MIXER_BLEND_CK_CTL_DEF); 339 340 regmap_write(mixer->engine.regs, 341 SUN8I_MIXER_BLEND_ATTR_FCOLOR(0), 342 SUN8I_MIXER_BLEND_ATTR_FCOLOR_DEF); 343 344 /* Select the first UI channel */ 345 DRM_DEBUG_DRIVER("Selecting channel %d (first UI channel)\n", 346 mixer->cfg->vi_num); 347 regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ROUTE, 348 mixer->cfg->vi_num); 349 350 return 0; 351 352 err_disable_bus_clk: 353 clk_disable_unprepare(mixer->bus_clk); 354 err_assert_reset: 355 reset_control_assert(mixer->reset); 356 return ret; 357 } 358 359 static void sun8i_mixer_unbind(struct device *dev, struct device *master, 360 void *data) 361 { 362 struct sun8i_mixer *mixer = dev_get_drvdata(dev); 363 364 list_del(&mixer->engine.list); 365 366 clk_disable_unprepare(mixer->mod_clk); 367 clk_disable_unprepare(mixer->bus_clk); 368 reset_control_assert(mixer->reset); 369 } 370 371 static const struct component_ops sun8i_mixer_ops = { 372 .bind = sun8i_mixer_bind, 373 .unbind = sun8i_mixer_unbind, 374 }; 375 376 static int sun8i_mixer_probe(struct platform_device *pdev) 377 { 378 return component_add(&pdev->dev, &sun8i_mixer_ops); 379 } 380 381 static int sun8i_mixer_remove(struct platform_device *pdev) 382 { 383 component_del(&pdev->dev, &sun8i_mixer_ops); 384 385 return 0; 386 } 387 388 static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { 389 .vi_num = 2, 390 .ui_num = 1, 391 }; 392 393 static const struct of_device_id sun8i_mixer_of_table[] = { 394 { 395 .compatible = "allwinner,sun8i-v3s-de2-mixer", 396 .data = &sun8i_v3s_mixer_cfg, 397 }, 398 { } 399 }; 400 MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); 401 402 static struct platform_driver sun8i_mixer_platform_driver = { 403 .probe = sun8i_mixer_probe, 404 .remove = sun8i_mixer_remove, 405 .driver = { 406 .name = "sun8i-mixer", 407 .of_match_table = sun8i_mixer_of_table, 408 }, 409 }; 410 module_platform_driver(sun8i_mixer_platform_driver); 411 412 MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); 413 MODULE_DESCRIPTION("Allwinner DE2 Mixer driver"); 414 MODULE_LICENSE("GPL"); 415