linux/drivers/gpu/drm/sti/sti_crtc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) STMicroelectronics SA 2014
   4 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com>
   5 *          Fabien Dessenne <fabien.dessenne@st.com>
   6 *          for STMicroelectronics.
   7 */
   8
   9#include <linux/clk.h>
  10
  11#include <drm/drm_atomic.h>
  12#include <drm/drm_atomic_helper.h>
  13#include <drm/drm_device.h>
  14#include <drm/drm_plane_helper.h>
  15#include <drm/drm_print.h>
  16#include <drm/drm_probe_helper.h>
  17#include <drm/drm_vblank.h>
  18
  19#include "sti_compositor.h"
  20#include "sti_crtc.h"
  21#include "sti_drv.h"
  22#include "sti_vid.h"
  23#include "sti_vtg.h"
  24
  25static void sti_crtc_atomic_enable(struct drm_crtc *crtc,
  26                                   struct drm_crtc_state *old_state)
  27{
  28        struct sti_mixer *mixer = to_sti_mixer(crtc);
  29
  30        DRM_DEBUG_DRIVER("\n");
  31
  32        mixer->status = STI_MIXER_READY;
  33
  34        drm_crtc_vblank_on(crtc);
  35}
  36
  37static void sti_crtc_atomic_disable(struct drm_crtc *crtc,
  38                                    struct drm_crtc_state *old_state)
  39{
  40        struct sti_mixer *mixer = to_sti_mixer(crtc);
  41
  42        DRM_DEBUG_DRIVER("\n");
  43
  44        mixer->status = STI_MIXER_DISABLING;
  45
  46        drm_crtc_wait_one_vblank(crtc);
  47}
  48
  49static int
  50sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode)
  51{
  52        struct sti_mixer *mixer = to_sti_mixer(crtc);
  53        struct device *dev = mixer->dev;
  54        struct sti_compositor *compo = dev_get_drvdata(dev);
  55        struct clk *compo_clk, *pix_clk;
  56        int rate = mode->clock * 1000;
  57
  58        DRM_DEBUG_KMS("CRTC:%d (%s) mode: (%s)\n",
  59                      crtc->base.id, sti_mixer_to_str(mixer), mode->name);
  60
  61        DRM_DEBUG_KMS(DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));
  62
  63        if (mixer->id == STI_MIXER_MAIN) {
  64                compo_clk = compo->clk_compo_main;
  65                pix_clk = compo->clk_pix_main;
  66        } else {
  67                compo_clk = compo->clk_compo_aux;
  68                pix_clk = compo->clk_pix_aux;
  69        }
  70
  71        /* Prepare and enable the compo IP clock */
  72        if (clk_prepare_enable(compo_clk)) {
  73                DRM_INFO("Failed to prepare/enable compositor clk\n");
  74                goto compo_error;
  75        }
  76
  77        /* Set rate and prepare/enable pixel clock */
  78        if (clk_set_rate(pix_clk, rate) < 0) {
  79                DRM_ERROR("Cannot set rate (%dHz) for pix clk\n", rate);
  80                goto pix_error;
  81        }
  82        if (clk_prepare_enable(pix_clk)) {
  83                DRM_ERROR("Failed to prepare/enable pix clk\n");
  84                goto pix_error;
  85        }
  86
  87        sti_vtg_set_config(compo->vtg[mixer->id], &crtc->mode);
  88
  89        if (sti_mixer_active_video_area(mixer, &crtc->mode)) {
  90                DRM_ERROR("Can't set active video area\n");
  91                goto mixer_error;
  92        }
  93
  94        return 0;
  95
  96mixer_error:
  97        clk_disable_unprepare(pix_clk);
  98pix_error:
  99        clk_disable_unprepare(compo_clk);
 100compo_error:
 101        return -EINVAL;
 102}
 103
 104static void sti_crtc_disable(struct drm_crtc *crtc)
 105{
 106        struct sti_mixer *mixer = to_sti_mixer(crtc);
 107        struct device *dev = mixer->dev;
 108        struct sti_compositor *compo = dev_get_drvdata(dev);
 109
 110        DRM_DEBUG_KMS("CRTC:%d (%s)\n", crtc->base.id, sti_mixer_to_str(mixer));
 111
 112        /* Disable Background */
 113        sti_mixer_set_background_status(mixer, false);
 114
 115        drm_crtc_vblank_off(crtc);
 116
 117        /* Disable pixel clock and compo IP clocks */
 118        if (mixer->id == STI_MIXER_MAIN) {
 119                clk_disable_unprepare(compo->clk_pix_main);
 120                clk_disable_unprepare(compo->clk_compo_main);
 121        } else {
 122                clk_disable_unprepare(compo->clk_pix_aux);
 123                clk_disable_unprepare(compo->clk_compo_aux);
 124        }
 125
 126        mixer->status = STI_MIXER_DISABLED;
 127}
 128
 129static void
 130sti_crtc_mode_set_nofb(struct drm_crtc *crtc)
 131{
 132        sti_crtc_mode_set(crtc, &crtc->state->adjusted_mode);
 133}
 134
 135static void sti_crtc_atomic_flush(struct drm_crtc *crtc,
 136                                  struct drm_crtc_state *old_crtc_state)
 137{
 138        struct drm_device *drm_dev = crtc->dev;
 139        struct sti_mixer *mixer = to_sti_mixer(crtc);
 140        struct sti_compositor *compo = dev_get_drvdata(mixer->dev);
 141        struct drm_plane *p;
 142        struct drm_pending_vblank_event *event;
 143        unsigned long flags;
 144
 145        DRM_DEBUG_DRIVER("\n");
 146
 147        /* perform plane actions */
 148        list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) {
 149                struct sti_plane *plane = to_sti_plane(p);
 150
 151                switch (plane->status) {
 152                case STI_PLANE_UPDATED:
 153                        /* ignore update for other CRTC */
 154                        if (p->state->crtc != crtc)
 155                                continue;
 156
 157                        /* update planes tag as updated */
 158                        DRM_DEBUG_DRIVER("update plane %s\n",
 159                                         sti_plane_to_str(plane));
 160
 161                        if (sti_mixer_set_plane_depth(mixer, plane)) {
 162                                DRM_ERROR("Cannot set plane %s depth\n",
 163                                          sti_plane_to_str(plane));
 164                                break;
 165                        }
 166
 167                        if (sti_mixer_set_plane_status(mixer, plane, true)) {
 168                                DRM_ERROR("Cannot enable plane %s at mixer\n",
 169                                          sti_plane_to_str(plane));
 170                                break;
 171                        }
 172
 173                        /* if plane is HQVDP_0 then commit the vid[0] */
 174                        if (plane->desc == STI_HQVDP_0)
 175                                sti_vid_commit(compo->vid[0], p->state);
 176
 177                        plane->status = STI_PLANE_READY;
 178
 179                        break;
 180                case STI_PLANE_DISABLING:
 181                        /* disabling sequence for planes tag as disabling */
 182                        DRM_DEBUG_DRIVER("disable plane %s from mixer\n",
 183                                         sti_plane_to_str(plane));
 184
 185                        if (sti_mixer_set_plane_status(mixer, plane, false)) {
 186                                DRM_ERROR("Cannot disable plane %s at mixer\n",
 187                                          sti_plane_to_str(plane));
 188                                continue;
 189                        }
 190
 191                        if (plane->desc == STI_CURSOR)
 192                                /* tag plane status for disabled */
 193                                plane->status = STI_PLANE_DISABLED;
 194                        else
 195                                /* tag plane status for flushing */
 196                                plane->status = STI_PLANE_FLUSHING;
 197
 198                        /* if plane is HQVDP_0 then disable the vid[0] */
 199                        if (plane->desc == STI_HQVDP_0)
 200                                sti_vid_disable(compo->vid[0]);
 201
 202                        break;
 203                default:
 204                        /* Other status case are not handled */
 205                        break;
 206                }
 207        }
 208
 209        event = crtc->state->event;
 210        if (event) {
 211                crtc->state->event = NULL;
 212
 213                spin_lock_irqsave(&crtc->dev->event_lock, flags);
 214                if (drm_crtc_vblank_get(crtc) == 0)
 215                        drm_crtc_arm_vblank_event(crtc, event);
 216                else
 217                        drm_crtc_send_vblank_event(crtc, event);
 218                spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
 219        }
 220}
 221
 222static const struct drm_crtc_helper_funcs sti_crtc_helper_funcs = {
 223        .mode_set_nofb = sti_crtc_mode_set_nofb,
 224        .atomic_flush = sti_crtc_atomic_flush,
 225        .atomic_enable = sti_crtc_atomic_enable,
 226        .atomic_disable = sti_crtc_atomic_disable,
 227};
 228
 229static void sti_crtc_destroy(struct drm_crtc *crtc)
 230{
 231        DRM_DEBUG_KMS("\n");
 232        drm_crtc_cleanup(crtc);
 233}
 234
 235static int sti_crtc_set_property(struct drm_crtc *crtc,
 236                                 struct drm_property *property,
 237                                 uint64_t val)
 238{
 239        DRM_DEBUG_KMS("\n");
 240        return 0;
 241}
 242
 243int sti_crtc_vblank_cb(struct notifier_block *nb,
 244                       unsigned long event, void *data)
 245{
 246        struct sti_compositor *compo;
 247        struct drm_crtc *crtc = data;
 248        struct sti_mixer *mixer;
 249        unsigned int pipe;
 250
 251        pipe = drm_crtc_index(crtc);
 252        compo = container_of(nb, struct sti_compositor, vtg_vblank_nb[pipe]);
 253        mixer = compo->mixer[pipe];
 254
 255        if ((event != VTG_TOP_FIELD_EVENT) &&
 256            (event != VTG_BOTTOM_FIELD_EVENT)) {
 257                DRM_ERROR("unknown event: %lu\n", event);
 258                return -EINVAL;
 259        }
 260
 261        drm_crtc_handle_vblank(crtc);
 262
 263        if (mixer->status == STI_MIXER_DISABLING) {
 264                struct drm_plane *p;
 265
 266                /* Disable mixer only if all overlay planes (GDP and VDP)
 267                 * are disabled */
 268                list_for_each_entry(p, &crtc->dev->mode_config.plane_list,
 269                                    head) {
 270                        struct sti_plane *plane = to_sti_plane(p);
 271
 272                        if ((plane->desc & STI_PLANE_TYPE_MASK) <= STI_VDP)
 273                                if (plane->status != STI_PLANE_DISABLED)
 274                                        return 0;
 275                }
 276                sti_crtc_disable(crtc);
 277        }
 278
 279        return 0;
 280}
 281
 282int sti_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe)
 283{
 284        struct sti_private *dev_priv = dev->dev_private;
 285        struct sti_compositor *compo = dev_priv->compo;
 286        struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe];
 287        struct drm_crtc *crtc = &compo->mixer[pipe]->drm_crtc;
 288        struct sti_vtg *vtg = compo->vtg[pipe];
 289
 290        DRM_DEBUG_DRIVER("\n");
 291
 292        if (sti_vtg_register_client(vtg, vtg_vblank_nb, crtc)) {
 293                DRM_ERROR("Cannot register VTG notifier\n");
 294                return -EINVAL;
 295        }
 296
 297        return 0;
 298}
 299
 300void sti_crtc_disable_vblank(struct drm_device *drm_dev, unsigned int pipe)
 301{
 302        struct sti_private *priv = drm_dev->dev_private;
 303        struct sti_compositor *compo = priv->compo;
 304        struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe];
 305        struct sti_vtg *vtg = compo->vtg[pipe];
 306
 307        DRM_DEBUG_DRIVER("\n");
 308
 309        if (sti_vtg_unregister_client(vtg, vtg_vblank_nb))
 310                DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
 311}
 312
 313static int sti_crtc_late_register(struct drm_crtc *crtc)
 314{
 315        struct sti_mixer *mixer = to_sti_mixer(crtc);
 316        struct sti_compositor *compo = dev_get_drvdata(mixer->dev);
 317
 318        if (drm_crtc_index(crtc) == 0)
 319                return sti_compositor_debugfs_init(compo, crtc->dev->primary);
 320
 321        return 0;
 322}
 323
 324static const struct drm_crtc_funcs sti_crtc_funcs = {
 325        .set_config = drm_atomic_helper_set_config,
 326        .page_flip = drm_atomic_helper_page_flip,
 327        .destroy = sti_crtc_destroy,
 328        .set_property = sti_crtc_set_property,
 329        .reset = drm_atomic_helper_crtc_reset,
 330        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
 331        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 332        .late_register = sti_crtc_late_register,
 333};
 334
 335bool sti_crtc_is_main(struct drm_crtc *crtc)
 336{
 337        struct sti_mixer *mixer = to_sti_mixer(crtc);
 338
 339        if (mixer->id == STI_MIXER_MAIN)
 340                return true;
 341
 342        return false;
 343}
 344
 345int sti_crtc_init(struct drm_device *drm_dev, struct sti_mixer *mixer,
 346                  struct drm_plane *primary, struct drm_plane *cursor)
 347{
 348        struct drm_crtc *crtc = &mixer->drm_crtc;
 349        int res;
 350
 351        res = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
 352                                        &sti_crtc_funcs, NULL);
 353        if (res) {
 354                DRM_ERROR("Can't initialize CRTC\n");
 355                return -EINVAL;
 356        }
 357
 358        drm_crtc_helper_add(crtc, &sti_crtc_helper_funcs);
 359
 360        DRM_DEBUG_DRIVER("drm CRTC:%d mapped to %s\n",
 361                         crtc->base.id, sti_mixer_to_str(mixer));
 362
 363        return 0;
 364}
 365