root/drivers/gpu/drm/meson/meson_venc_cvbs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. meson_cvbs_get_mode
  2. meson_cvbs_connector_destroy
  3. meson_cvbs_connector_detect
  4. meson_cvbs_connector_get_modes
  5. meson_cvbs_connector_mode_valid
  6. meson_venc_cvbs_encoder_destroy
  7. meson_venc_cvbs_encoder_atomic_check
  8. meson_venc_cvbs_encoder_disable
  9. meson_venc_cvbs_encoder_enable
  10. meson_venc_cvbs_encoder_mode_set
  11. meson_venc_cvbs_connector_is_available
  12. meson_venc_cvbs_create

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright (C) 2016 BayLibre, SAS
   4  * Author: Neil Armstrong <narmstrong@baylibre.com>
   5  * Copyright (C) 2015 Amlogic, Inc. All rights reserved.
   6  * Copyright (C) 2014 Endless Mobile
   7  *
   8  * Written by:
   9  *     Jasper St. Pierre <jstpierre@mecheye.net>
  10  */
  11 
  12 #include <linux/export.h>
  13 #include <linux/of_graph.h>
  14 
  15 #include <drm/drm_atomic_helper.h>
  16 #include <drm/drm_device.h>
  17 #include <drm/drm_edid.h>
  18 #include <drm/drm_probe_helper.h>
  19 #include <drm/drm_print.h>
  20 
  21 #include "meson_registers.h"
  22 #include "meson_vclk.h"
  23 #include "meson_venc_cvbs.h"
  24 
  25 /* HHI VDAC Registers */
  26 #define HHI_VDAC_CNTL0          0x2F4 /* 0xbd offset in data sheet */
  27 #define HHI_VDAC_CNTL0_G12A     0x2EC /* 0xbd offset in data sheet */
  28 #define HHI_VDAC_CNTL1          0x2F8 /* 0xbe offset in data sheet */
  29 #define HHI_VDAC_CNTL1_G12A     0x2F0 /* 0xbe offset in data sheet */
  30 
  31 struct meson_venc_cvbs {
  32         struct drm_encoder      encoder;
  33         struct drm_connector    connector;
  34         struct meson_drm        *priv;
  35 };
  36 #define encoder_to_meson_venc_cvbs(x) \
  37         container_of(x, struct meson_venc_cvbs, encoder)
  38 
  39 #define connector_to_meson_venc_cvbs(x) \
  40         container_of(x, struct meson_venc_cvbs, connector)
  41 
  42 /* Supported Modes */
  43 
  44 struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = {
  45         { /* PAL */
  46                 .enci = &meson_cvbs_enci_pal,
  47                 .mode = {
  48                         DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
  49                                  720, 732, 795, 864, 0, 576, 580, 586, 625, 0,
  50                                  DRM_MODE_FLAG_INTERLACE),
  51                         .vrefresh = 50,
  52                         .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
  53                 },
  54         },
  55         { /* NTSC */
  56                 .enci = &meson_cvbs_enci_ntsc,
  57                 .mode = {
  58                         DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
  59                                 720, 739, 801, 858, 0, 480, 488, 494, 525, 0,
  60                                 DRM_MODE_FLAG_INTERLACE),
  61                         .vrefresh = 60,
  62                         .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3,
  63                 },
  64         },
  65 };
  66 
  67 static const struct meson_cvbs_mode *
  68 meson_cvbs_get_mode(const struct drm_display_mode *req_mode)
  69 {
  70         int i;
  71 
  72         for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
  73                 struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
  74 
  75                 if (drm_mode_match(req_mode, &meson_mode->mode,
  76                                    DRM_MODE_MATCH_TIMINGS |
  77                                    DRM_MODE_MATCH_CLOCK |
  78                                    DRM_MODE_MATCH_FLAGS |
  79                                    DRM_MODE_MATCH_3D_FLAGS))
  80                         return meson_mode;
  81         }
  82 
  83         return NULL;
  84 }
  85 
  86 /* Connector */
  87 
  88 static void meson_cvbs_connector_destroy(struct drm_connector *connector)
  89 {
  90         drm_connector_cleanup(connector);
  91 }
  92 
  93 static enum drm_connector_status
  94 meson_cvbs_connector_detect(struct drm_connector *connector, bool force)
  95 {
  96         /* FIXME: Add load-detect or jack-detect if possible */
  97         return connector_status_connected;
  98 }
  99 
 100 static int meson_cvbs_connector_get_modes(struct drm_connector *connector)
 101 {
 102         struct drm_device *dev = connector->dev;
 103         struct drm_display_mode *mode;
 104         int i;
 105 
 106         for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) {
 107                 struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i];
 108 
 109                 mode = drm_mode_duplicate(dev, &meson_mode->mode);
 110                 if (!mode) {
 111                         DRM_ERROR("Failed to create a new display mode\n");
 112                         return 0;
 113                 }
 114 
 115                 drm_mode_probed_add(connector, mode);
 116         }
 117 
 118         return i;
 119 }
 120 
 121 static int meson_cvbs_connector_mode_valid(struct drm_connector *connector,
 122                                            struct drm_display_mode *mode)
 123 {
 124         /* Validate the modes added in get_modes */
 125         return MODE_OK;
 126 }
 127 
 128 static const struct drm_connector_funcs meson_cvbs_connector_funcs = {
 129         .detect                 = meson_cvbs_connector_detect,
 130         .fill_modes             = drm_helper_probe_single_connector_modes,
 131         .destroy                = meson_cvbs_connector_destroy,
 132         .reset                  = drm_atomic_helper_connector_reset,
 133         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 134         .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
 135 };
 136 
 137 static const
 138 struct drm_connector_helper_funcs meson_cvbs_connector_helper_funcs = {
 139         .get_modes      = meson_cvbs_connector_get_modes,
 140         .mode_valid     = meson_cvbs_connector_mode_valid,
 141 };
 142 
 143 /* Encoder */
 144 
 145 static void meson_venc_cvbs_encoder_destroy(struct drm_encoder *encoder)
 146 {
 147         drm_encoder_cleanup(encoder);
 148 }
 149 
 150 static const struct drm_encoder_funcs meson_venc_cvbs_encoder_funcs = {
 151         .destroy        = meson_venc_cvbs_encoder_destroy,
 152 };
 153 
 154 static int meson_venc_cvbs_encoder_atomic_check(struct drm_encoder *encoder,
 155                                         struct drm_crtc_state *crtc_state,
 156                                         struct drm_connector_state *conn_state)
 157 {
 158         if (meson_cvbs_get_mode(&crtc_state->mode))
 159                 return 0;
 160 
 161         return -EINVAL;
 162 }
 163 
 164 static void meson_venc_cvbs_encoder_disable(struct drm_encoder *encoder)
 165 {
 166         struct meson_venc_cvbs *meson_venc_cvbs =
 167                                         encoder_to_meson_venc_cvbs(encoder);
 168         struct meson_drm *priv = meson_venc_cvbs->priv;
 169 
 170         /* Disable CVBS VDAC */
 171         if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
 172                 regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0);
 173                 regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
 174         } else {
 175                 regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0);
 176                 regmap_write(priv->hhi, HHI_VDAC_CNTL1, 8);
 177         }
 178 }
 179 
 180 static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder)
 181 {
 182         struct meson_venc_cvbs *meson_venc_cvbs =
 183                                         encoder_to_meson_venc_cvbs(encoder);
 184         struct meson_drm *priv = meson_venc_cvbs->priv;
 185 
 186         /* VDAC0 source is not from ATV */
 187         writel_bits_relaxed(VENC_VDAC_SEL_ATV_DMD, 0,
 188                             priv->io_base + _REG(VENC_VDAC_DACSEL0));
 189 
 190         if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXBB)) {
 191                 regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1);
 192                 regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
 193         } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXM) ||
 194                  meson_vpu_is_compatible(priv, VPU_COMPATIBLE_GXL)) {
 195                 regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001);
 196                 regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0);
 197         } else if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A)) {
 198                 regmap_write(priv->hhi, HHI_VDAC_CNTL0_G12A, 0x906001);
 199                 regmap_write(priv->hhi, HHI_VDAC_CNTL1_G12A, 0);
 200         }
 201 }
 202 
 203 static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder,
 204                                    struct drm_display_mode *mode,
 205                                    struct drm_display_mode *adjusted_mode)
 206 {
 207         const struct meson_cvbs_mode *meson_mode = meson_cvbs_get_mode(mode);
 208         struct meson_venc_cvbs *meson_venc_cvbs =
 209                                         encoder_to_meson_venc_cvbs(encoder);
 210         struct meson_drm *priv = meson_venc_cvbs->priv;
 211 
 212         if (meson_mode) {
 213                 meson_venci_cvbs_mode_set(priv, meson_mode->enci);
 214 
 215                 /* Setup 27MHz vclk2 for ENCI and VDAC */
 216                 meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS,
 217                                  MESON_VCLK_CVBS, MESON_VCLK_CVBS, true);
 218         }
 219 }
 220 
 221 static const struct drm_encoder_helper_funcs
 222                                 meson_venc_cvbs_encoder_helper_funcs = {
 223         .atomic_check   = meson_venc_cvbs_encoder_atomic_check,
 224         .disable        = meson_venc_cvbs_encoder_disable,
 225         .enable         = meson_venc_cvbs_encoder_enable,
 226         .mode_set       = meson_venc_cvbs_encoder_mode_set,
 227 };
 228 
 229 static bool meson_venc_cvbs_connector_is_available(struct meson_drm *priv)
 230 {
 231         struct device_node *remote;
 232 
 233         remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0);
 234         if (!remote)
 235                 return false;
 236 
 237         of_node_put(remote);
 238         return true;
 239 }
 240 
 241 int meson_venc_cvbs_create(struct meson_drm *priv)
 242 {
 243         struct drm_device *drm = priv->drm;
 244         struct meson_venc_cvbs *meson_venc_cvbs;
 245         struct drm_connector *connector;
 246         struct drm_encoder *encoder;
 247         int ret;
 248 
 249         if (!meson_venc_cvbs_connector_is_available(priv)) {
 250                 dev_info(drm->dev, "CVBS Output connector not available\n");
 251                 return 0;
 252         }
 253 
 254         meson_venc_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_venc_cvbs),
 255                                        GFP_KERNEL);
 256         if (!meson_venc_cvbs)
 257                 return -ENOMEM;
 258 
 259         meson_venc_cvbs->priv = priv;
 260         encoder = &meson_venc_cvbs->encoder;
 261         connector = &meson_venc_cvbs->connector;
 262 
 263         /* Connector */
 264 
 265         drm_connector_helper_add(connector,
 266                                  &meson_cvbs_connector_helper_funcs);
 267 
 268         ret = drm_connector_init(drm, connector, &meson_cvbs_connector_funcs,
 269                                  DRM_MODE_CONNECTOR_Composite);
 270         if (ret) {
 271                 dev_err(priv->dev, "Failed to init CVBS connector\n");
 272                 return ret;
 273         }
 274 
 275         connector->interlace_allowed = 1;
 276 
 277         /* Encoder */
 278 
 279         drm_encoder_helper_add(encoder, &meson_venc_cvbs_encoder_helper_funcs);
 280 
 281         ret = drm_encoder_init(drm, encoder, &meson_venc_cvbs_encoder_funcs,
 282                                DRM_MODE_ENCODER_TVDAC, "meson_venc_cvbs");
 283         if (ret) {
 284                 dev_err(priv->dev, "Failed to init CVBS encoder\n");
 285                 return ret;
 286         }
 287 
 288         encoder->possible_crtcs = BIT(0);
 289 
 290         drm_connector_attach_encoder(connector, encoder);
 291 
 292         return 0;
 293 }

/* [<][>][^][v][top][bottom][index][help] */