linux/drivers/gpu/drm/sun4i/sun4i_backend.c
<<
>>
Prefs
   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
  27static u32 sunxi_rgb2yuv_coef[12] = {
  28        0x00000107, 0x00000204, 0x00000064, 0x00000108,
  29        0x00003f69, 0x00003ed6, 0x000001c1, 0x00000808,
  30        0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
  31};
  32
  33void 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}
  47EXPORT_SYMBOL(sun4i_backend_apply_color_correction);
  48
  49void 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}
  57EXPORT_SYMBOL(sun4i_backend_disable_color_correction);
  58
  59void 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}
  67EXPORT_SYMBOL(sun4i_backend_commit);
  68
  69void 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}
  84EXPORT_SYMBOL(sun4i_backend_layer_enable);
  85
  86static 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
 108int 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}
 145EXPORT_SYMBOL(sun4i_backend_update_layer_coord);
 146
 147int 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}
 178EXPORT_SYMBOL(sun4i_backend_update_layer_formats);
 179
 180int 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 @ %pad\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 %pad\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}
 218EXPORT_SYMBOL(sun4i_backend_update_layer_buffer);
 219
 220static 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
 227static 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
 309err_disable_mod_clk:
 310        clk_disable_unprepare(backend->mod_clk);
 311err_disable_bus_clk:
 312        clk_disable_unprepare(backend->bus_clk);
 313err_assert_reset:
 314        reset_control_assert(backend->reset);
 315        return ret;
 316}
 317
 318static 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
 329static struct component_ops sun4i_backend_ops = {
 330        .bind   = sun4i_backend_bind,
 331        .unbind = sun4i_backend_unbind,
 332};
 333
 334static int sun4i_backend_probe(struct platform_device *pdev)
 335{
 336        return component_add(&pdev->dev, &sun4i_backend_ops);
 337}
 338
 339static int sun4i_backend_remove(struct platform_device *pdev)
 340{
 341        component_del(&pdev->dev, &sun4i_backend_ops);
 342
 343        return 0;
 344}
 345
 346static const struct of_device_id sun4i_backend_of_table[] = {
 347        { .compatible = "allwinner,sun5i-a13-display-backend" },
 348        { }
 349};
 350MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);
 351
 352static 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};
 360module_platform_driver(sun4i_backend_platform_driver);
 361
 362MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
 363MODULE_DESCRIPTION("Allwinner A10 Display Backend Driver");
 364MODULE_LICENSE("GPL");
 365