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 * @enabled: CRTC state
  59 */
  60struct atmel_hlcdc_crtc {
  61        struct drm_crtc base;
  62        struct atmel_hlcdc_dc *dc;
  63        struct drm_pending_vblank_event *event;
  64        int id;
  65        bool enabled;
  66};
  67
  68static inline struct atmel_hlcdc_crtc *
  69drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
  70{
  71        return container_of(crtc, struct atmel_hlcdc_crtc, base);
  72}
  73
  74static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
  75{
  76        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
  77        struct regmap *regmap = crtc->dc->hlcdc->regmap;
  78        struct drm_display_mode *adj = &c->state->adjusted_mode;
  79        struct atmel_hlcdc_crtc_state *state;
  80        unsigned long mode_rate;
  81        struct videomode vm;
  82        unsigned long prate;
  83        unsigned int cfg;
  84        int div;
  85
  86        vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
  87        vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
  88        vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
  89        vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay;
  90        vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end;
  91        vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start;
  92
  93        regmap_write(regmap, ATMEL_HLCDC_CFG(1),
  94                     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
  95
  96        regmap_write(regmap, ATMEL_HLCDC_CFG(2),
  97                     (vm.vfront_porch - 1) | (vm.vback_porch << 16));
  98
  99        regmap_write(regmap, ATMEL_HLCDC_CFG(3),
 100                     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
 101
 102        regmap_write(regmap, ATMEL_HLCDC_CFG(4),
 103                     (adj->crtc_hdisplay - 1) |
 104                     ((adj->crtc_vdisplay - 1) << 16));
 105
 106        cfg = 0;
 107
 108        prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
 109        mode_rate = adj->crtc_clock * 1000;
 110        if ((prate / 2) < mode_rate) {
 111                prate *= 2;
 112                cfg |= ATMEL_HLCDC_CLKSEL;
 113        }
 114
 115        div = DIV_ROUND_UP(prate, mode_rate);
 116        if (div < 2)
 117                div = 2;
 118
 119        cfg |= ATMEL_HLCDC_CLKDIV(div);
 120
 121        regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0),
 122                           ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK |
 123                           ATMEL_HLCDC_CLKPOL, cfg);
 124
 125        cfg = 0;
 126
 127        if (adj->flags & DRM_MODE_FLAG_NVSYNC)
 128                cfg |= ATMEL_HLCDC_VSPOL;
 129
 130        if (adj->flags & DRM_MODE_FLAG_NHSYNC)
 131                cfg |= ATMEL_HLCDC_HSPOL;
 132
 133        state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state);
 134        cfg |= state->output_mode << 8;
 135
 136        regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5),
 137                           ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL |
 138                           ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE |
 139                           ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY |
 140                           ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO |
 141                           ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK,
 142                           cfg);
 143}
 144
 145static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *c,
 146                                        const struct drm_display_mode *mode,
 147                                        struct drm_display_mode *adjusted_mode)
 148{
 149        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 150
 151        return atmel_hlcdc_dc_mode_valid(crtc->dc, adjusted_mode) == MODE_OK;
 152}
 153
 154static void atmel_hlcdc_crtc_disable(struct drm_crtc *c)
 155{
 156        struct drm_device *dev = c->dev;
 157        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 158        struct regmap *regmap = crtc->dc->hlcdc->regmap;
 159        unsigned int status;
 160
 161        if (!crtc->enabled)
 162                return;
 163
 164        drm_crtc_vblank_off(c);
 165
 166        pm_runtime_get_sync(dev->dev);
 167
 168        regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
 169        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 170               (status & ATMEL_HLCDC_DISP))
 171                cpu_relax();
 172
 173        regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
 174        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 175               (status & ATMEL_HLCDC_SYNC))
 176                cpu_relax();
 177
 178        regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
 179        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 180               (status & ATMEL_HLCDC_PIXEL_CLK))
 181                cpu_relax();
 182
 183        clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
 184        pinctrl_pm_select_sleep_state(dev->dev);
 185
 186        pm_runtime_allow(dev->dev);
 187
 188        pm_runtime_put_sync(dev->dev);
 189
 190        crtc->enabled = false;
 191}
 192
 193static void atmel_hlcdc_crtc_enable(struct drm_crtc *c)
 194{
 195        struct drm_device *dev = c->dev;
 196        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 197        struct regmap *regmap = crtc->dc->hlcdc->regmap;
 198        unsigned int status;
 199
 200        if (crtc->enabled)
 201                return;
 202
 203        pm_runtime_get_sync(dev->dev);
 204
 205        pm_runtime_forbid(dev->dev);
 206
 207        pinctrl_pm_select_default_state(dev->dev);
 208        clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
 209
 210        regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
 211        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 212               !(status & ATMEL_HLCDC_PIXEL_CLK))
 213                cpu_relax();
 214
 215
 216        regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
 217        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 218               !(status & ATMEL_HLCDC_SYNC))
 219                cpu_relax();
 220
 221        regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
 222        while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) &&
 223               !(status & ATMEL_HLCDC_DISP))
 224                cpu_relax();
 225
 226        pm_runtime_put_sync(dev->dev);
 227
 228        drm_crtc_vblank_on(c);
 229
 230        crtc->enabled = true;
 231}
 232
 233void atmel_hlcdc_crtc_suspend(struct drm_crtc *c)
 234{
 235        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 236
 237        if (crtc->enabled) {
 238                atmel_hlcdc_crtc_disable(c);
 239                /* save enable state for resume */
 240                crtc->enabled = true;
 241        }
 242}
 243
 244void atmel_hlcdc_crtc_resume(struct drm_crtc *c)
 245{
 246        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 247
 248        if (crtc->enabled) {
 249                crtc->enabled = false;
 250                atmel_hlcdc_crtc_enable(c);
 251        }
 252}
 253
 254#define ATMEL_HLCDC_RGB444_OUTPUT       BIT(0)
 255#define ATMEL_HLCDC_RGB565_OUTPUT       BIT(1)
 256#define ATMEL_HLCDC_RGB666_OUTPUT       BIT(2)
 257#define ATMEL_HLCDC_RGB888_OUTPUT       BIT(3)
 258#define ATMEL_HLCDC_OUTPUT_MODE_MASK    GENMASK(3, 0)
 259
 260static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state)
 261{
 262        unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK;
 263        struct atmel_hlcdc_crtc_state *hstate;
 264        struct drm_connector_state *cstate;
 265        struct drm_connector *connector;
 266        struct atmel_hlcdc_crtc *crtc;
 267        int i;
 268
 269        crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc);
 270
 271        for_each_connector_in_state(state->state, connector, cstate, i) {
 272                struct drm_display_info *info = &connector->display_info;
 273                unsigned int supported_fmts = 0;
 274                int j;
 275
 276                if (!cstate->crtc)
 277                        continue;
 278
 279                for (j = 0; j < info->num_bus_formats; j++) {
 280                        switch (info->bus_formats[j]) {
 281                        case MEDIA_BUS_FMT_RGB444_1X12:
 282                                supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT;
 283                                break;
 284                        case MEDIA_BUS_FMT_RGB565_1X16:
 285                                supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT;
 286                                break;
 287                        case MEDIA_BUS_FMT_RGB666_1X18:
 288                                supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
 289                                break;
 290                        case MEDIA_BUS_FMT_RGB888_1X24:
 291                                supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
 292                                break;
 293                        default:
 294                                break;
 295                        }
 296                }
 297
 298                if (crtc->dc->desc->conflicting_output_formats)
 299                        output_fmts &= supported_fmts;
 300                else
 301                        output_fmts |= supported_fmts;
 302        }
 303
 304        if (!output_fmts)
 305                return -EINVAL;
 306
 307        hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state);
 308        hstate->output_mode = fls(output_fmts) - 1;
 309
 310        return 0;
 311}
 312
 313static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c,
 314                                         struct drm_crtc_state *s)
 315{
 316        int ret;
 317
 318        ret = atmel_hlcdc_crtc_select_output_mode(s);
 319        if (ret)
 320                return ret;
 321
 322        ret = atmel_hlcdc_plane_prepare_disc_area(s);
 323        if (ret)
 324                return ret;
 325
 326        return atmel_hlcdc_plane_prepare_ahb_routing(s);
 327}
 328
 329static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c,
 330                                          struct drm_crtc_state *old_s)
 331{
 332        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 333
 334        if (c->state->event) {
 335                c->state->event->pipe = drm_crtc_index(c);
 336
 337                WARN_ON(drm_crtc_vblank_get(c) != 0);
 338
 339                crtc->event = c->state->event;
 340                c->state->event = NULL;
 341        }
 342}
 343
 344static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc,
 345                                          struct drm_crtc_state *old_s)
 346{
 347        /* TODO: write common plane control register if available */
 348}
 349
 350static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = {
 351        .mode_fixup = atmel_hlcdc_crtc_mode_fixup,
 352        .mode_set = drm_helper_crtc_mode_set,
 353        .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb,
 354        .mode_set_base = drm_helper_crtc_mode_set_base,
 355        .disable = atmel_hlcdc_crtc_disable,
 356        .enable = atmel_hlcdc_crtc_enable,
 357        .atomic_check = atmel_hlcdc_crtc_atomic_check,
 358        .atomic_begin = atmel_hlcdc_crtc_atomic_begin,
 359        .atomic_flush = atmel_hlcdc_crtc_atomic_flush,
 360};
 361
 362static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c)
 363{
 364        struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
 365
 366        drm_crtc_cleanup(c);
 367        kfree(crtc);
 368}
 369
 370static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc)
 371{
 372        struct drm_device *dev = crtc->base.dev;
 373        unsigned long flags;
 374
 375        spin_lock_irqsave(&dev->event_lock, flags);
 376        if (crtc->event) {
 377                drm_crtc_send_vblank_event(&crtc->base, crtc->event);
 378                drm_crtc_vblank_put(&crtc->base);
 379                crtc->event = NULL;
 380        }
 381        spin_unlock_irqrestore(&dev->event_lock, flags);
 382}
 383
 384void atmel_hlcdc_crtc_irq(struct drm_crtc *c)
 385{
 386        drm_crtc_handle_vblank(c);
 387        atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c));
 388}
 389
 390static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc)
 391{
 392        struct atmel_hlcdc_crtc_state *state;
 393
 394        if (crtc->state) {
 395                __drm_atomic_helper_crtc_destroy_state(crtc->state);
 396                state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
 397                kfree(state);
 398                crtc->state = NULL;
 399        }
 400
 401        state = kzalloc(sizeof(*state), GFP_KERNEL);
 402        if (state) {
 403                crtc->state = &state->base;
 404                crtc->state->crtc = crtc;
 405        }
 406}
 407
 408static struct drm_crtc_state *
 409atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc)
 410{
 411        struct atmel_hlcdc_crtc_state *state, *cur;
 412
 413        if (WARN_ON(!crtc->state))
 414                return NULL;
 415
 416        state = kmalloc(sizeof(*state), GFP_KERNEL);
 417        if (!state)
 418                return NULL;
 419        __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base);
 420
 421        cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state);
 422        state->output_mode = cur->output_mode;
 423
 424        return &state->base;
 425}
 426
 427static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc,
 428                                           struct drm_crtc_state *s)
 429{
 430        struct atmel_hlcdc_crtc_state *state;
 431
 432        state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s);
 433        __drm_atomic_helper_crtc_destroy_state(s);
 434        kfree(state);
 435}
 436
 437static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = {
 438        .page_flip = drm_atomic_helper_page_flip,
 439        .set_config = drm_atomic_helper_set_config,
 440        .destroy = atmel_hlcdc_crtc_destroy,
 441        .reset = atmel_hlcdc_crtc_reset,
 442        .atomic_duplicate_state =  atmel_hlcdc_crtc_duplicate_state,
 443        .atomic_destroy_state = atmel_hlcdc_crtc_destroy_state,
 444};
 445
 446int atmel_hlcdc_crtc_create(struct drm_device *dev)
 447{
 448        struct atmel_hlcdc_dc *dc = dev->dev_private;
 449        struct atmel_hlcdc_planes *planes = dc->planes;
 450        struct atmel_hlcdc_crtc *crtc;
 451        int ret;
 452        int i;
 453
 454        crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
 455        if (!crtc)
 456                return -ENOMEM;
 457
 458        crtc->dc = dc;
 459
 460        ret = drm_crtc_init_with_planes(dev, &crtc->base,
 461                                &planes->primary->base,
 462                                planes->cursor ? &planes->cursor->base : NULL,
 463                                &atmel_hlcdc_crtc_funcs, NULL);
 464        if (ret < 0)
 465                goto fail;
 466
 467        crtc->id = drm_crtc_index(&crtc->base);
 468
 469        if (planes->cursor)
 470                planes->cursor->base.possible_crtcs = 1 << crtc->id;
 471
 472        for (i = 0; i < planes->noverlays; i++)
 473                planes->overlays[i]->base.possible_crtcs = 1 << crtc->id;
 474
 475        drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs);
 476        drm_crtc_vblank_reset(&crtc->base);
 477
 478        dc->crtc = &crtc->base;
 479
 480        return 0;
 481
 482fail:
 483        atmel_hlcdc_crtc_destroy(&crtc->base);
 484        return ret;
 485}
 486