linux/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2014 Traphandler
   3 * Copyright (C) 2014 Free Electrons
   4 *
   5 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
   6 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License version 2 as published by
  10 * the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but WITHOUT
  13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15 * more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along with
  18 * this program.  If not, see <http://www.gnu.org/licenses/>.
  19 */
  20
  21#include <linux/clk.h>
  22#include <linux/pm.h>
  23#include <linux/pm_runtime.h>
  24#include <linux/pinctrl/consumer.h>
  25
  26#include <drm/drm_crtc.h>
  27#include <drm/drm_crtc_helper.h>
  28#include <drm/drmP.h>
  29
  30#include <video/videomode.h>
  31
  32#include "atmel_hlcdc_dc.h"
  33
  34/**
  35 * Atmel HLCDC CRTC state structure
  36 *
  37 * @base: base CRTC state
  38 * @output_mode: RGBXXX output mode
  39 */
  40struct atmel_hlcdc_crtc_state {
  41        struct drm_crtc_state base;
  42        unsigned int output_mode;
  43};
  44
  45static inline struct atmel_hlcdc_crtc_state *
  46drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state)
  47{
  48        return container_of(state, struct atmel_hlcdc_crtc_state, base);
  49}
  50
  51/**
  52 * Atmel HLCDC CRTC structure
  53 *
  54 * @base: base DRM CRTC structure
  55 * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device
  56 * @event: pointer to the current page flip event
  57 * @id: CRTC id (returned by drm_crtc_index)
  58 */
  59struct atmel_hlcdc_crtc {
  60        struct drm_crtc base;
  61        struct atmel_hlcdc_dc *dc;
  62        struct drm_pending_vblank_event *event;
  63        int id;
  64};
  65
  66static inline struct atmel_hlcdc_crtc *
  67drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
  68{
  69        return container_of(crtc, struct atmel_hlcdc_crtc, base);
  70}
  71
  72static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
  73{
  74        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
  75        struct regmap *regmap = crtc->dc->hlcdc->regmap;
  76        struct drm_display_mode *adj = &c->state->adjusted_mode;
  77        struct atmel_hlcdc_crtc_state *state;
  78        unsigned long mode_rate;
  79        struct videomode vm;
  80        unsigned long prate;
  81        unsigned int cfg;
  82        int div;
  83
  84        vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
  85        vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
  86        vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
  87        vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay;
  88        vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end;
  89        vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start;
  90
  91        regmap_write(regmap, ATMEL_HLCDC_CFG(1),
  92                     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
  93
  94        regmap_write(regmap, ATMEL_HLCDC_CFG(2),
  95                     (vm.vfront_porch - 1) | (vm.vback_porch << 16));
  96
  97        regmap_write(regmap, ATMEL_HLCDC_CFG(3),
  98                     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
  99
 100        regmap_write(regmap, ATMEL_HLCDC_CFG(4),
 101                     (adj->crtc_hdisplay - 1) |
 102                     ((adj->crtc_vdisplay - 1) << 16));
 103
 104        cfg = 0;
 105
 106        prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
 107        mode_rate = adj->crtc_clock * 1000;
 108        if ((prate / 2) < mode_rate) {
 109                prate *= 2;
 110                cfg |= ATMEL_HLCDC_CLKSEL;
 111        }
 112
 113        div = DIV_ROUND_UP(prate, mode_rate);
 114        if (div < 2)
 115                div = 2;
 116
 117        cfg |= ATMEL_HLCDC_CLKDIV(div);
 118
 119        regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0),
 120                           ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK |
 121                           ATMEL_HLCDC_CLKPOL, cfg);
 122
 123        cfg = 0;
 124
 125        if (adj->flags & DRM_MODE_FLAG_NVSYNC)
 126                cfg |= ATMEL_HLCDC_VSPOL;
 127
 128        if (adj->flags & DRM_MODE_FLAG_NHSYNC)
 129                cfg |= ATMEL_HLCDC_HSPOL;
 130
 131        state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state);
 132        cfg |= state->output_mode << 8;
 133
 134        regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),
 135                           ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
 136                           ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
 137                           ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
 138                           ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
 139                           ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK,
 140                           cfg);
 141}
 142
 143static enum drm_mode_status
 144atmel_hlcdc_crtc_mode_valid(struct drm_crtc *c,
 145                            const struct drm_display_mode *mode)
 146{
 147        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 148
 149        return atmel_hlcdc_dc_mode_valid(crtc->dc, mode);
 150}
 151
 152static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c,
 153                                            struct drm_crtc_state *old_state)
 154{
 155        struct drm_device *dev = c->dev;
 156        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 157        struct regmap *regmap = crtc->dc->hlcdc->regmap;
 158        unsigned int status;
 159
 160        drm_crtc_vblank_off(c);
 161
 162        pm_runtime_get_sync(dev->dev);
 163
 164        regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
 165        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 166               (status & ATMEL_HLCDC_DISP))
 167                cpu_relax();
 168
 169        regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
 170        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 171               (status & ATMEL_HLCDC_SYNC))
 172                cpu_relax();
 173
 174        regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
 175        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 176               (status & ATMEL_HLCDC_PIXEL_CLK))
 177                cpu_relax();
 178
 179        clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
 180        pinctrl_pm_select_sleep_state(dev->dev);
 181
 182        pm_runtime_allow(dev->dev);
 183
 184        pm_runtime_put_sync(dev->dev);
 185}
 186
 187static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
 188                                           struct drm_crtc_state *old_state)
 189{
 190        struct drm_device *dev = c->dev;
 191        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 192        struct regmap *regmap = crtc->dc->hlcdc->regmap;
 193        unsigned int status;
 194
 195        pm_runtime_get_sync(dev->dev);
 196
 197        pm_runtime_forbid(dev->dev);
 198
 199        pinctrl_pm_select_default_state(dev->dev);
 200        clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
 201
 202        regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
 203        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 204               !(status & ATMEL_HLCDC_PIXEL_CLK))
 205                cpu_relax();
 206
 207
 208        regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
 209        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 210               !(status & ATMEL_HLCDC_SYNC))
 211                cpu_relax();
 212
 213        regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
 214        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 215               !(status & ATMEL_HLCDC_DISP))
 216                cpu_relax();
 217
 218        pm_runtime_put_sync(dev->dev);
 219
 220        drm_crtc_vblank_on(c);
 221}
 222
 223#define ATMEL_HLCDC_RGB444_OUTPUT       BIT(0)
 224#define ATMEL_HLCDC_RGB565_OUTPUT       BIT(1)
 225#define ATMEL_HLCDC_RGB666_OUTPUT       BIT(2)
 226#define ATMEL_HLCDC_RGB888_OUTPUT       BIT(3)
 227#define ATMEL_HLCDC_OUTPUT_MODE_MASK    GENMASK(3, 0)
 228
 229static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state)
 230{
 231        unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK;
 232        struct atmel_hlcdc_crtc_state *hstate;
 233        struct drm_connector_state *cstate;
 234        struct drm_connector *connector;
 235        struct atmel_hlcdc_crtc *crtc;
 236        int i;
 237
 238        crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc);
 239
 240        for_each_new_connector_in_state(state->state, connector, cstate, i) {
 241                struct drm_display_info *info = &connector->display_info;
 242                unsigned int supported_fmts = 0;
 243                int j;
 244
 245                if (!cstate->crtc)
 246                        continue;
 247
 248                for (j = 0; j < info->num_bus_formats; j++) {
 249                        switch (info->bus_formats[j]) {
 250                        case MEDIA_BUS_FMT_RGB444_1X12:
 251                                supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT;
 252                                break;
 253                        case MEDIA_BUS_FMT_RGB565_1X16:
 254                                supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT;
 255                                break;
 256                        case MEDIA_BUS_FMT_RGB666_1X18:
 257                                supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
 258                                break;
 259                        case MEDIA_BUS_FMT_RGB888_1X24:
 260                                supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
 261                                break;
 262                        default:
 263                                break;
 264                        }
 265                }
 266
 267                if (crtc->dc->desc->conflicting_output_formats)
 268                        output_fmts &= supported_fmts;
 269                else
 270                        output_fmts |= supported_fmts;
 271        }
 272
 273        if (!output_fmts)
 274                return -EINVAL;
 275
 276        hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state);
 277        hstate->output_mode = fls(output_fmts) - 1;
 278
 279        return 0;
 280}
 281
 282static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c,
 283                                         struct drm_crtc_state *s)
 284{
 285        int ret;
 286
 287        ret = atmel_hlcdc_crtc_select_output_mode(s);
 288        if (ret)
 289                return ret;
 290
 291        ret = atmel_hlcdc_plane_prepare_disc_area(s);
 292        if (ret)
 293                return ret;
 294
 295        return atmel_hlcdc_plane_prepare_ahb_routing(s);
 296}
 297
 298static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c,
 299                                          struct drm_crtc_state *old_s)
 300{
 301        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 302
 303        if (c->state->event) {
 304                c->state->event->pipe = drm_crtc_index(c);
 305
 306                WARN_ON(drm_crtc_vblank_get(c) != 0);
 307
 308                crtc->event = c->state->event;
 309                c->state->event = NULL;
 310        }
 311}
 312
 313static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc,
 314                                          struct drm_crtc_state *old_s)
 315{
 316        /* TODO: write common plane control register if available */
 317}
 318
 319static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
 320        .mode_valid = atmel_hlcdc_crtc_mode_valid,
 321        .mode_set = drm_helper_crtc_mode_set,
 322        .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb,
 323        .mode_set_base = drm_helper_crtc_mode_set_base,
 324        .atomic_check = atmel_hlcdc_crtc_atomic_check,
 325        .atomic_begin = atmel_hlcdc_crtc_atomic_begin,
 326        .atomic_flush = atmel_hlcdc_crtc_atomic_flush,
 327        .atomic_enable = atmel_hlcdc_crtc_atomic_enable,
 328        .atomic_disable = atmel_hlcdc_crtc_atomic_disable,
 329};
 330
 331static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
 332{
 333        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 334
 335        drm_crtc_cleanup(c);
 336        kfree(crtc);
 337}
 338
 339static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc)
 340{
 341        struct drm_device *dev = crtc->base.dev;
 342        unsigned long flags;
 343
 344        spin_lock_irqsave(&dev->event_lock, flags);
 345        if (crtc->event) {
 346                drm_crtc_send_vblank_event(&crtc->base, crtc->event);
 347                drm_crtc_vblank_put(&crtc->base);
 348                crtc->event = NULL;
 349        }
 350        spin_unlock_irqrestore(&dev->event_lock, flags);
 351}
 352
 353void atmel_hlcdc_crtc_irq(struct drm_crtc *c)
 354{
 355        drm_crtc_handle_vblank(c);
 356        atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
 357}
 358
 359static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc)
 360{
 361        struct atmel_hlcdc_crtc_state *state;
 362
 363        if (crtc->state) {
 364                __drm_atomic_helper_crtc_destroy_state(crtc->state);
 365                state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
 366                kfree(state);
 367                crtc->state = NULL;
 368        }
 369
 370        state = kzalloc(sizeof(*state), GFP_KERNEL);
 371        if (state) {
 372                crtc->state = &state->base;
 373                crtc->state->crtc = crtc;
 374        }
 375}
 376
 377static struct drm_crtc_state *
 378atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc)
 379{
 380        struct atmel_hlcdc_crtc_state *state, *cur;
 381
 382        if (WARN_ON(!crtc->state))
 383                return NULL;
 384
 385        state = kmalloc(sizeof(*state), GFP_KERNEL);
 386        if (!state)
 387                return NULL;
 388        __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
 389
 390        cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
 391        state->output_mode = cur->output_mode;
 392
 393        return &state->base;
 394}
 395
 396static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc,
 397                                           struct drm_crtc_state *s)
 398{
 399        struct atmel_hlcdc_crtc_state *state;
 400
 401        state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s);
 402        __drm_atomic_helper_crtc_destroy_state(s);
 403        kfree(state);
 404}
 405
 406static int atmel_hlcdc_crtc_enable_vblank(struct drm_crtc *c)
 407{
 408        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 409        struct regmap *regmap = crtc->dc->hlcdc->regmap;
 410
 411        /* Enable SOF (Start Of Frame) interrupt for vblank counting */
 412        regmap_write(regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF);
 413
 414        return 0;
 415}
 416
 417static void atmel_hlcdc_crtc_disable_vblank(struct drm_crtc *c)
 418{
 419        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 420        struct regmap *regmap = crtc->dc->hlcdc->regmap;
 421
 422        regmap_write(regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF);
 423}
 424
 425static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
 426        .page_flip = drm_atomic_helper_page_flip,
 427        .set_config = drm_atomic_helper_set_config,
 428        .destroy = atmel_hlcdc_crtc_destroy,
 429        .reset = atmel_hlcdc_crtc_reset,
 430        .atomic_duplicate_state =  atmel_hlcdc_crtc_duplicate_state,
 431        .atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
 432        .enable_vblank = atmel_hlcdc_crtc_enable_vblank,
 433        .disable_vblank = atmel_hlcdc_crtc_disable_vblank,
 434        .gamma_set = drm_atomic_helper_legacy_gamma_set,
 435};
 436
 437int atmel_hlcdc_crtc_create(struct drm_device *dev)
 438{
 439        struct atmel_hlcdc_plane *primary = NULL, *cursor = NULL;
 440        struct atmel_hlcdc_dc *dc = dev->dev_private;
 441        struct atmel_hlcdc_crtc *crtc;
 442        int ret;
 443        int i;
 444
 445        crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
 446        if (!crtc)
 447                return -ENOMEM;
 448
 449        crtc->dc = dc;
 450
 451        for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
 452                if (!dc->layers[i])
 453                        continue;
 454
 455                switch (dc->layers[i]->desc->type) {
 456                case ATMEL_HLCDC_BASE_LAYER:
 457                        primary = atmel_hlcdc_layer_to_plane(dc->layers[i]);
 458                        break;
 459
 460                case ATMEL_HLCDC_CURSOR_LAYER:
 461                        cursor = atmel_hlcdc_layer_to_plane(dc->layers[i]);
 462                        break;
 463
 464                default:
 465                        break;
 466                }
 467        }
 468
 469        ret = drm_crtc_init_with_planes(dev, &crtc->base, &primary->base,
 470                                        &cursor->base, &atmel_hlcdc_crtc_funcs,
 471                                        NULL);
 472        if (ret < 0)
 473                goto fail;
 474
 475        crtc->id = drm_crtc_index(&crtc->base);
 476
 477        for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) {
 478                struct atmel_hlcdc_plane *overlay;
 479
 480                if (dc->layers[i] &&
 481                    dc->layers[i]->desc->type == ATMEL_HLCDC_OVERLAY_LAYER) {
 482                        overlay = atmel_hlcdc_layer_to_plane(dc->layers[i]);
 483                        overlay->base.possible_crtcs = 1 << crtc->id;
 484                }
 485        }
 486
 487        drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
 488        drm_crtc_vblank_reset(&crtc->base);
 489
 490        drm_mode_crtc_set_gamma_size(&crtc->base, ATMEL_HLCDC_CLUT_SIZE);
 491        drm_crtc_enable_color_mgmt(&crtc->base, 0, false,
 492                                   ATMEL_HLCDC_CLUT_SIZE);
 493
 494        dc->crtc = &crtc->base;
 495
 496        return 0;
 497
 498fail:
 499        atmel_hlcdc_crtc_destroy(&crtc->base);
 500        return ret;
 501}
 502