linux/drivers/gpu/drm/sti/sti_tvout.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 *          Vincent Abriou <vincent.abriou@st.com>
   6 *          for STMicroelectronics.
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/component.h>
  11#include <linux/io.h>
  12#include <linux/module.h>
  13#include <linux/of_platform.h>
  14#include <linux/platform_device.h>
  15#include <linux/reset.h>
  16#include <linux/seq_file.h>
  17
  18#include <drm/drm_atomic_helper.h>
  19#include <drm/drm_debugfs.h>
  20#include <drm/drm_device.h>
  21#include <drm/drm_file.h>
  22#include <drm/drm_print.h>
  23
  24#include "sti_crtc.h"
  25#include "sti_drv.h"
  26#include "sti_vtg.h"
  27
  28/* glue registers */
  29#define TVO_CSC_MAIN_M0                  0x000
  30#define TVO_CSC_MAIN_M1                  0x004
  31#define TVO_CSC_MAIN_M2                  0x008
  32#define TVO_CSC_MAIN_M3                  0x00c
  33#define TVO_CSC_MAIN_M4                  0x010
  34#define TVO_CSC_MAIN_M5                  0x014
  35#define TVO_CSC_MAIN_M6                  0x018
  36#define TVO_CSC_MAIN_M7                  0x01c
  37#define TVO_MAIN_IN_VID_FORMAT           0x030
  38#define TVO_CSC_AUX_M0                   0x100
  39#define TVO_CSC_AUX_M1                   0x104
  40#define TVO_CSC_AUX_M2                   0x108
  41#define TVO_CSC_AUX_M3                   0x10c
  42#define TVO_CSC_AUX_M4                   0x110
  43#define TVO_CSC_AUX_M5                   0x114
  44#define TVO_CSC_AUX_M6                   0x118
  45#define TVO_CSC_AUX_M7                   0x11c
  46#define TVO_AUX_IN_VID_FORMAT            0x130
  47#define TVO_VIP_HDF                      0x400
  48#define TVO_HD_SYNC_SEL                  0x418
  49#define TVO_HD_DAC_CFG_OFF               0x420
  50#define TVO_VIP_HDMI                     0x500
  51#define TVO_HDMI_FORCE_COLOR_0           0x504
  52#define TVO_HDMI_FORCE_COLOR_1           0x508
  53#define TVO_HDMI_CLIP_VALUE_B_CB         0x50c
  54#define TVO_HDMI_CLIP_VALUE_Y_G          0x510
  55#define TVO_HDMI_CLIP_VALUE_R_CR         0x514
  56#define TVO_HDMI_SYNC_SEL                0x518
  57#define TVO_HDMI_DFV_OBS                 0x540
  58#define TVO_VIP_DVO                      0x600
  59#define TVO_DVO_SYNC_SEL                 0x618
  60#define TVO_DVO_CONFIG                   0x620
  61
  62#define TVO_IN_FMT_SIGNED                BIT(0)
  63#define TVO_SYNC_EXT                     BIT(4)
  64
  65#define TVO_VIP_REORDER_R_SHIFT          24
  66#define TVO_VIP_REORDER_G_SHIFT          20
  67#define TVO_VIP_REORDER_B_SHIFT          16
  68#define TVO_VIP_REORDER_MASK             0x3
  69#define TVO_VIP_REORDER_Y_G_SEL          0
  70#define TVO_VIP_REORDER_CB_B_SEL         1
  71#define TVO_VIP_REORDER_CR_R_SEL         2
  72
  73#define TVO_VIP_CLIP_SHIFT               8
  74#define TVO_VIP_CLIP_MASK                0x7
  75#define TVO_VIP_CLIP_DISABLED            0
  76#define TVO_VIP_CLIP_EAV_SAV             1
  77#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2
  78#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3
  79#define TVO_VIP_CLIP_PROG_RANGE          4
  80
  81#define TVO_VIP_RND_SHIFT                4
  82#define TVO_VIP_RND_MASK                 0x3
  83#define TVO_VIP_RND_8BIT_ROUNDED         0
  84#define TVO_VIP_RND_10BIT_ROUNDED        1
  85#define TVO_VIP_RND_12BIT_ROUNDED        2
  86
  87#define TVO_VIP_SEL_INPUT_MASK           0xf
  88#define TVO_VIP_SEL_INPUT_MAIN           0x0
  89#define TVO_VIP_SEL_INPUT_AUX            0x8
  90#define TVO_VIP_SEL_INPUT_FORCE_COLOR    0xf
  91#define TVO_VIP_SEL_INPUT_BYPASS_MASK    0x1
  92#define TVO_VIP_SEL_INPUT_BYPASSED       1
  93
  94#define TVO_SYNC_MAIN_VTG_SET_REF        0x00
  95#define TVO_SYNC_AUX_VTG_SET_REF         0x10
  96
  97#define TVO_SYNC_HD_DCS_SHIFT            8
  98
  99#define TVO_SYNC_DVO_PAD_HSYNC_SHIFT     8
 100#define TVO_SYNC_DVO_PAD_VSYNC_SHIFT     16
 101
 102#define ENCODER_CRTC_MASK                (BIT(0) | BIT(1))
 103
 104#define TVO_MIN_HD_HEIGHT                720
 105
 106/* enum listing the supported output data format */
 107enum sti_tvout_video_out_type {
 108        STI_TVOUT_VIDEO_OUT_RGB,
 109        STI_TVOUT_VIDEO_OUT_YUV,
 110};
 111
 112struct sti_tvout {
 113        struct device *dev;
 114        struct drm_device *drm_dev;
 115        void __iomem *regs;
 116        struct reset_control *reset;
 117        struct drm_encoder *hdmi;
 118        struct drm_encoder *hda;
 119        struct drm_encoder *dvo;
 120        bool debugfs_registered;
 121};
 122
 123struct sti_tvout_encoder {
 124        struct drm_encoder encoder;
 125        struct sti_tvout *tvout;
 126};
 127
 128#define to_sti_tvout_encoder(x) \
 129        container_of(x, struct sti_tvout_encoder, encoder)
 130
 131#define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout
 132
 133/* preformatter conversion matrix */
 134static const u32 rgb_to_ycbcr_601[8] = {
 135        0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D,
 136        0x0000082E, 0x00002000, 0x00002000, 0x00000000
 137};
 138
 139/* 709 RGB to YCbCr */
 140static const u32 rgb_to_ycbcr_709[8] = {
 141        0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20,
 142        0x0000082F, 0x00002000, 0x00002000, 0x00000000
 143};
 144
 145static u32 tvout_read(struct sti_tvout *tvout, int offset)
 146{
 147        return readl(tvout->regs + offset);
 148}
 149
 150static void tvout_write(struct sti_tvout *tvout, u32 val, int offset)
 151{
 152        writel(val, tvout->regs + offset);
 153}
 154
 155/**
 156 * Set the clipping mode of a VIP
 157 *
 158 * @tvout: tvout structure
 159 * @reg: register to set
 160 * @cr_r:
 161 * @y_g:
 162 * @cb_b:
 163 */
 164static void tvout_vip_set_color_order(struct sti_tvout *tvout, int reg,
 165                                      u32 cr_r, u32 y_g, u32 cb_b)
 166{
 167        u32 val = tvout_read(tvout, reg);
 168
 169        val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT);
 170        val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT);
 171        val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT);
 172        val |= cr_r << TVO_VIP_REORDER_R_SHIFT;
 173        val |= y_g << TVO_VIP_REORDER_G_SHIFT;
 174        val |= cb_b << TVO_VIP_REORDER_B_SHIFT;
 175
 176        tvout_write(tvout, val, reg);
 177}
 178
 179/**
 180 * Set the clipping mode of a VIP
 181 *
 182 * @tvout: tvout structure
 183 * @reg: register to set
 184 * @range: clipping range
 185 */
 186static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, int reg, u32 range)
 187{
 188        u32 val = tvout_read(tvout, reg);
 189
 190        val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT);
 191        val |= range << TVO_VIP_CLIP_SHIFT;
 192        tvout_write(tvout, val, reg);
 193}
 194
 195/**
 196 * Set the rounded value of a VIP
 197 *
 198 * @tvout: tvout structure
 199 * @reg: register to set
 200 * @rnd: rounded val per component
 201 */
 202static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd)
 203{
 204        u32 val = tvout_read(tvout, reg);
 205
 206        val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT);
 207        val |= rnd << TVO_VIP_RND_SHIFT;
 208        tvout_write(tvout, val, reg);
 209}
 210
 211/**
 212 * Select the VIP input
 213 *
 214 * @tvout: tvout structure
 215 * @reg: register to set
 216 * @main_path: main or auxiliary path
 217 * @sel_input: selected_input (main/aux + conv)
 218 */
 219static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
 220                                    int reg,
 221                                    bool main_path,
 222                                    enum sti_tvout_video_out_type video_out)
 223{
 224        u32 sel_input;
 225        u32 val = tvout_read(tvout, reg);
 226
 227        if (main_path)
 228                sel_input = TVO_VIP_SEL_INPUT_MAIN;
 229        else
 230                sel_input = TVO_VIP_SEL_INPUT_AUX;
 231
 232        switch (video_out) {
 233        case STI_TVOUT_VIDEO_OUT_RGB:
 234                sel_input |= TVO_VIP_SEL_INPUT_BYPASSED;
 235                break;
 236        case STI_TVOUT_VIDEO_OUT_YUV:
 237                sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED;
 238                break;
 239        }
 240
 241        /* on stih407 chip the sel_input bypass mode logic is inverted */
 242        sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
 243
 244        val &= ~TVO_VIP_SEL_INPUT_MASK;
 245        val |= sel_input;
 246        tvout_write(tvout, val, reg);
 247}
 248
 249/**
 250 * Select the input video signed or unsigned
 251 *
 252 * @tvout: tvout structure
 253 * @reg: register to set
 254 * @in_vid_signed: used video input format
 255 */
 256static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout,
 257                int reg, u32 in_vid_fmt)
 258{
 259        u32 val = tvout_read(tvout, reg);
 260
 261        val &= ~TVO_IN_FMT_SIGNED;
 262        val |= in_vid_fmt;
 263        tvout_write(tvout, val, reg);
 264}
 265
 266/**
 267 * Set preformatter matrix
 268 *
 269 * @tvout: tvout structure
 270 * @mode: display mode structure
 271 */
 272static void tvout_preformatter_set_matrix(struct sti_tvout *tvout,
 273                                          struct drm_display_mode *mode)
 274{
 275        unsigned int i;
 276        const u32 *pf_matrix;
 277
 278        if (mode->vdisplay >= TVO_MIN_HD_HEIGHT)
 279                pf_matrix = rgb_to_ycbcr_709;
 280        else
 281                pf_matrix = rgb_to_ycbcr_601;
 282
 283        for (i = 0; i < 8; i++) {
 284                tvout_write(tvout, *(pf_matrix + i),
 285                            TVO_CSC_MAIN_M0 + (i * 4));
 286                tvout_write(tvout, *(pf_matrix + i),
 287                            TVO_CSC_AUX_M0 + (i * 4));
 288        }
 289}
 290
 291/**
 292 * Start VIP block for DVO output
 293 *
 294 * @tvout: pointer on tvout structure
 295 * @main_path: true if main path has to be used in the vip configuration
 296 *        else aux path is used.
 297 */
 298static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)
 299{
 300        u32 tvo_in_vid_format;
 301        int val, tmp;
 302
 303        dev_dbg(tvout->dev, "%s\n", __func__);
 304
 305        if (main_path) {
 306                DRM_DEBUG_DRIVER("main vip for DVO\n");
 307                /* Select the input sync for dvo */
 308                tmp = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_DVO;
 309                val  = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
 310                val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
 311                val |= tmp;
 312                tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
 313                tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
 314        } else {
 315                DRM_DEBUG_DRIVER("aux vip for DVO\n");
 316                /* Select the input sync for dvo */
 317                tmp = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_DVO;
 318                val  = tmp << TVO_SYNC_DVO_PAD_VSYNC_SHIFT;
 319                val |= tmp << TVO_SYNC_DVO_PAD_HSYNC_SHIFT;
 320                val |= tmp;
 321                tvout_write(tvout, val, TVO_DVO_SYNC_SEL);
 322                tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
 323        }
 324
 325        /* Set color channel order */
 326        tvout_vip_set_color_order(tvout, TVO_VIP_DVO,
 327                                  TVO_VIP_REORDER_CR_R_SEL,
 328                                  TVO_VIP_REORDER_Y_G_SEL,
 329                                  TVO_VIP_REORDER_CB_B_SEL);
 330
 331        /* Set clipping mode */
 332        tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, TVO_VIP_CLIP_DISABLED);
 333
 334        /* Set round mode (rounded to 8-bit per component) */
 335        tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED);
 336
 337        /* Set input video format */
 338        tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 339
 340        /* Input selection */
 341        tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path,
 342                                STI_TVOUT_VIDEO_OUT_RGB);
 343}
 344
 345/**
 346 * Start VIP block for HDMI output
 347 *
 348 * @tvout: pointer on tvout structure
 349 * @main_path: true if main path has to be used in the vip configuration
 350 *        else aux path is used.
 351 */
 352static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
 353{
 354        u32 tvo_in_vid_format;
 355
 356        dev_dbg(tvout->dev, "%s\n", __func__);
 357
 358        if (main_path) {
 359                DRM_DEBUG_DRIVER("main vip for hdmi\n");
 360                /* select the input sync for hdmi */
 361                tvout_write(tvout,
 362                            TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDMI,
 363                            TVO_HDMI_SYNC_SEL);
 364                tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
 365        } else {
 366                DRM_DEBUG_DRIVER("aux vip for hdmi\n");
 367                /* select the input sync for hdmi */
 368                tvout_write(tvout,
 369                            TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDMI,
 370                            TVO_HDMI_SYNC_SEL);
 371                tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
 372        }
 373
 374        /* set color channel order */
 375        tvout_vip_set_color_order(tvout, TVO_VIP_HDMI,
 376                                  TVO_VIP_REORDER_CR_R_SEL,
 377                                  TVO_VIP_REORDER_Y_G_SEL,
 378                                  TVO_VIP_REORDER_CB_B_SEL);
 379
 380        /* set clipping mode */
 381        tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, TVO_VIP_CLIP_DISABLED);
 382
 383        /* set round mode (rounded to 8-bit per component) */
 384        tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED);
 385
 386        /* set input video format */
 387        tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 388
 389        /* input selection */
 390        tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path,
 391                                STI_TVOUT_VIDEO_OUT_RGB);
 392}
 393
 394/**
 395 * Start HDF VIP and HD DAC
 396 *
 397 * @tvout: pointer on tvout structure
 398 * @main_path: true if main path has to be used in the vip configuration
 399 *        else aux path is used.
 400 */
 401static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
 402{
 403        u32 tvo_in_vid_format;
 404        int val;
 405
 406        dev_dbg(tvout->dev, "%s\n", __func__);
 407
 408        if (main_path) {
 409                DRM_DEBUG_DRIVER("main vip for HDF\n");
 410                /* Select the input sync for HD analog and HD DCS */
 411                val  = TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDDCS;
 412                val  = val << TVO_SYNC_HD_DCS_SHIFT;
 413                val |= TVO_SYNC_MAIN_VTG_SET_REF | VTG_SYNC_ID_HDF;
 414                tvout_write(tvout, val, TVO_HD_SYNC_SEL);
 415                tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT;
 416        } else {
 417                DRM_DEBUG_DRIVER("aux vip for HDF\n");
 418                /* Select the input sync for HD analog and HD DCS */
 419                val  = TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDDCS;
 420                val  = val << TVO_SYNC_HD_DCS_SHIFT;
 421                val |= TVO_SYNC_AUX_VTG_SET_REF | VTG_SYNC_ID_HDF;
 422                tvout_write(tvout, val, TVO_HD_SYNC_SEL);
 423                tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT;
 424        }
 425
 426        /* set color channel order */
 427        tvout_vip_set_color_order(tvout, TVO_VIP_HDF,
 428                                  TVO_VIP_REORDER_CR_R_SEL,
 429                                  TVO_VIP_REORDER_Y_G_SEL,
 430                                  TVO_VIP_REORDER_CB_B_SEL);
 431
 432        /* set clipping mode */
 433        tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_DISABLED);
 434
 435        /* set round mode (rounded to 10-bit per component) */
 436        tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
 437
 438        /* Set input video format */
 439        tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 440
 441        /* Input selection */
 442        tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path,
 443                                STI_TVOUT_VIDEO_OUT_YUV);
 444
 445        /* power up HD DAC */
 446        tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF);
 447}
 448
 449#define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \
 450                                   readl(tvout->regs + reg))
 451
 452static void tvout_dbg_vip(struct seq_file *s, int val)
 453{
 454        int r, g, b, tmp, mask;
 455        char *const reorder[] = {"Y_G", "Cb_B", "Cr_R"};
 456        char *const clipping[] = {"No", "EAV/SAV", "Limited range RGB/Y",
 457                                  "Limited range Cb/Cr", "decided by register"};
 458        char *const round[] = {"8-bit", "10-bit", "12-bit"};
 459        char *const input_sel[] = {"Main (color matrix enabled)",
 460                                   "Main (color matrix by-passed)",
 461                                   "", "", "", "", "", "",
 462                                   "Aux (color matrix enabled)",
 463                                   "Aux (color matrix by-passed)",
 464                                   "", "", "", "", "", "Force value"};
 465
 466        seq_putc(s, '\t');
 467        mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;
 468        r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;
 469        mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;
 470        g = (val & mask) >> TVO_VIP_REORDER_G_SHIFT;
 471        mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT;
 472        b = (val & mask) >> TVO_VIP_REORDER_B_SHIFT;
 473        seq_printf(s, "%-24s %s->%s %s->%s %s->%s\n", "Reorder:",
 474                   reorder[r], reorder[TVO_VIP_REORDER_CR_R_SEL],
 475                   reorder[g], reorder[TVO_VIP_REORDER_Y_G_SEL],
 476                   reorder[b], reorder[TVO_VIP_REORDER_CB_B_SEL]);
 477        seq_puts(s, "\t\t\t\t\t");
 478        mask = TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT;
 479        tmp = (val & mask) >> TVO_VIP_CLIP_SHIFT;
 480        seq_printf(s, "%-24s %s\n", "Clipping:", clipping[tmp]);
 481        seq_puts(s, "\t\t\t\t\t");
 482        mask = TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT;
 483        tmp = (val & mask) >> TVO_VIP_RND_SHIFT;
 484        seq_printf(s, "%-24s input data rounded to %s per component\n",
 485                   "Round:", round[tmp]);
 486        seq_puts(s, "\t\t\t\t\t");
 487        tmp = (val & TVO_VIP_SEL_INPUT_MASK);
 488        seq_printf(s, "%-24s %s", "Input selection:", input_sel[tmp]);
 489}
 490
 491static void tvout_dbg_hd_dac_cfg(struct seq_file *s, int val)
 492{
 493        seq_printf(s, "\t%-24s %s", "HD DAC:",
 494                   val & 1 ? "disabled" : "enabled");
 495}
 496
 497static int tvout_dbg_show(struct seq_file *s, void *data)
 498{
 499        struct drm_info_node *node = s->private;
 500        struct sti_tvout *tvout = (struct sti_tvout *)node->info_ent->data;
 501        struct drm_crtc *crtc;
 502
 503        seq_printf(s, "TVOUT: (vaddr = 0x%p)", tvout->regs);
 504
 505        seq_puts(s, "\n\n  HDMI encoder: ");
 506        crtc = tvout->hdmi->crtc;
 507        if (crtc) {
 508                seq_printf(s, "connected to %s path",
 509                           sti_crtc_is_main(crtc) ? "main" : "aux");
 510                DBGFS_DUMP(TVO_HDMI_SYNC_SEL);
 511                DBGFS_DUMP(TVO_VIP_HDMI);
 512                tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDMI));
 513        } else {
 514                seq_puts(s, "disabled");
 515        }
 516
 517        seq_puts(s, "\n\n  DVO encoder: ");
 518        crtc = tvout->dvo->crtc;
 519        if (crtc) {
 520                seq_printf(s, "connected to %s path",
 521                           sti_crtc_is_main(crtc) ? "main" : "aux");
 522                DBGFS_DUMP(TVO_DVO_SYNC_SEL);
 523                DBGFS_DUMP(TVO_DVO_CONFIG);
 524                DBGFS_DUMP(TVO_VIP_DVO);
 525                tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_DVO));
 526        } else {
 527                seq_puts(s, "disabled");
 528        }
 529
 530        seq_puts(s, "\n\n  HDA encoder: ");
 531        crtc = tvout->hda->crtc;
 532        if (crtc) {
 533                seq_printf(s, "connected to %s path",
 534                           sti_crtc_is_main(crtc) ? "main" : "aux");
 535                DBGFS_DUMP(TVO_HD_SYNC_SEL);
 536                DBGFS_DUMP(TVO_HD_DAC_CFG_OFF);
 537                tvout_dbg_hd_dac_cfg(s,
 538                                     readl(tvout->regs + TVO_HD_DAC_CFG_OFF));
 539                DBGFS_DUMP(TVO_VIP_HDF);
 540                tvout_dbg_vip(s, readl(tvout->regs + TVO_VIP_HDF));
 541        } else {
 542                seq_puts(s, "disabled");
 543        }
 544
 545        seq_puts(s, "\n\n  main path configuration");
 546        DBGFS_DUMP(TVO_CSC_MAIN_M0);
 547        DBGFS_DUMP(TVO_CSC_MAIN_M1);
 548        DBGFS_DUMP(TVO_CSC_MAIN_M2);
 549        DBGFS_DUMP(TVO_CSC_MAIN_M3);
 550        DBGFS_DUMP(TVO_CSC_MAIN_M4);
 551        DBGFS_DUMP(TVO_CSC_MAIN_M5);
 552        DBGFS_DUMP(TVO_CSC_MAIN_M6);
 553        DBGFS_DUMP(TVO_CSC_MAIN_M7);
 554        DBGFS_DUMP(TVO_MAIN_IN_VID_FORMAT);
 555
 556        seq_puts(s, "\n\n  auxiliary path configuration");
 557        DBGFS_DUMP(TVO_CSC_AUX_M0);
 558        DBGFS_DUMP(TVO_CSC_AUX_M2);
 559        DBGFS_DUMP(TVO_CSC_AUX_M3);
 560        DBGFS_DUMP(TVO_CSC_AUX_M4);
 561        DBGFS_DUMP(TVO_CSC_AUX_M5);
 562        DBGFS_DUMP(TVO_CSC_AUX_M6);
 563        DBGFS_DUMP(TVO_CSC_AUX_M7);
 564        DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);
 565        seq_putc(s, '\n');
 566        return 0;
 567}
 568
 569static struct drm_info_list tvout_debugfs_files[] = {
 570        { "tvout", tvout_dbg_show, 0, NULL },
 571};
 572
 573static int tvout_debugfs_init(struct sti_tvout *tvout, struct drm_minor *minor)
 574{
 575        unsigned int i;
 576
 577        for (i = 0; i < ARRAY_SIZE(tvout_debugfs_files); i++)
 578                tvout_debugfs_files[i].data = tvout;
 579
 580        return drm_debugfs_create_files(tvout_debugfs_files,
 581                                        ARRAY_SIZE(tvout_debugfs_files),
 582                                        minor->debugfs_root, minor);
 583}
 584
 585static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode)
 586{
 587}
 588
 589static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder,
 590                                       struct drm_display_mode *mode,
 591                                       struct drm_display_mode *adjusted_mode)
 592{
 593}
 594
 595static void sti_tvout_encoder_destroy(struct drm_encoder *encoder)
 596{
 597        struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder);
 598
 599        drm_encoder_cleanup(encoder);
 600        kfree(sti_encoder);
 601}
 602
 603static int sti_tvout_late_register(struct drm_encoder *encoder)
 604{
 605        struct sti_tvout *tvout = to_sti_tvout(encoder);
 606        int ret;
 607
 608        if (tvout->debugfs_registered)
 609                return 0;
 610
 611        ret = tvout_debugfs_init(tvout, encoder->dev->primary);
 612        if (ret)
 613                return ret;
 614
 615        tvout->debugfs_registered = true;
 616        return 0;
 617}
 618
 619static void sti_tvout_early_unregister(struct drm_encoder *encoder)
 620{
 621        struct sti_tvout *tvout = to_sti_tvout(encoder);
 622
 623        if (!tvout->debugfs_registered)
 624                return;
 625
 626        tvout->debugfs_registered = false;
 627}
 628
 629static const struct drm_encoder_funcs sti_tvout_encoder_funcs = {
 630        .destroy = sti_tvout_encoder_destroy,
 631        .late_register = sti_tvout_late_register,
 632        .early_unregister = sti_tvout_early_unregister,
 633};
 634
 635static void sti_dvo_encoder_enable(struct drm_encoder *encoder)
 636{
 637        struct sti_tvout *tvout = to_sti_tvout(encoder);
 638
 639        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 640
 641        tvout_dvo_start(tvout, sti_crtc_is_main(encoder->crtc));
 642}
 643
 644static void sti_dvo_encoder_disable(struct drm_encoder *encoder)
 645{
 646        struct sti_tvout *tvout = to_sti_tvout(encoder);
 647
 648        /* Reset VIP register */
 649        tvout_write(tvout, 0x0, TVO_VIP_DVO);
 650}
 651
 652static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = {
 653        .dpms = sti_tvout_encoder_dpms,
 654        .mode_set = sti_tvout_encoder_mode_set,
 655        .enable = sti_dvo_encoder_enable,
 656        .disable = sti_dvo_encoder_disable,
 657};
 658
 659static struct drm_encoder *
 660sti_tvout_create_dvo_encoder(struct drm_device *dev,
 661                             struct sti_tvout *tvout)
 662{
 663        struct sti_tvout_encoder *encoder;
 664        struct drm_encoder *drm_encoder;
 665
 666        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 667        if (!encoder)
 668                return NULL;
 669
 670        encoder->tvout = tvout;
 671
 672        drm_encoder = (struct drm_encoder *)encoder;
 673
 674        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 675        drm_encoder->possible_clones = 1 << 0;
 676
 677        drm_encoder_init(dev, drm_encoder,
 678                         &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS,
 679                         NULL);
 680
 681        drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs);
 682
 683        return drm_encoder;
 684}
 685
 686static void sti_hda_encoder_enable(struct drm_encoder *encoder)
 687{
 688        struct sti_tvout *tvout = to_sti_tvout(encoder);
 689
 690        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 691
 692        tvout_hda_start(tvout, sti_crtc_is_main(encoder->crtc));
 693}
 694
 695static void sti_hda_encoder_disable(struct drm_encoder *encoder)
 696{
 697        struct sti_tvout *tvout = to_sti_tvout(encoder);
 698
 699        /* reset VIP register */
 700        tvout_write(tvout, 0x0, TVO_VIP_HDF);
 701
 702        /* power down HD DAC */
 703        tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF);
 704}
 705
 706static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = {
 707        .dpms = sti_tvout_encoder_dpms,
 708        .mode_set = sti_tvout_encoder_mode_set,
 709        .commit = sti_hda_encoder_enable,
 710        .disable = sti_hda_encoder_disable,
 711};
 712
 713static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev,
 714                struct sti_tvout *tvout)
 715{
 716        struct sti_tvout_encoder *encoder;
 717        struct drm_encoder *drm_encoder;
 718
 719        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 720        if (!encoder)
 721                return NULL;
 722
 723        encoder->tvout = tvout;
 724
 725        drm_encoder = (struct drm_encoder *) encoder;
 726
 727        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 728        drm_encoder->possible_clones = 1 << 0;
 729
 730        drm_encoder_init(dev, drm_encoder,
 731                        &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC, NULL);
 732
 733        drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs);
 734
 735        return drm_encoder;
 736}
 737
 738static void sti_hdmi_encoder_enable(struct drm_encoder *encoder)
 739{
 740        struct sti_tvout *tvout = to_sti_tvout(encoder);
 741
 742        tvout_preformatter_set_matrix(tvout, &encoder->crtc->mode);
 743
 744        tvout_hdmi_start(tvout, sti_crtc_is_main(encoder->crtc));
 745}
 746
 747static void sti_hdmi_encoder_disable(struct drm_encoder *encoder)
 748{
 749        struct sti_tvout *tvout = to_sti_tvout(encoder);
 750
 751        /* reset VIP register */
 752        tvout_write(tvout, 0x0, TVO_VIP_HDMI);
 753}
 754
 755static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = {
 756        .dpms = sti_tvout_encoder_dpms,
 757        .mode_set = sti_tvout_encoder_mode_set,
 758        .commit = sti_hdmi_encoder_enable,
 759        .disable = sti_hdmi_encoder_disable,
 760};
 761
 762static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev,
 763                struct sti_tvout *tvout)
 764{
 765        struct sti_tvout_encoder *encoder;
 766        struct drm_encoder *drm_encoder;
 767
 768        encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL);
 769        if (!encoder)
 770                return NULL;
 771
 772        encoder->tvout = tvout;
 773
 774        drm_encoder = (struct drm_encoder *) encoder;
 775
 776        drm_encoder->possible_crtcs = ENCODER_CRTC_MASK;
 777        drm_encoder->possible_clones = 1 << 1;
 778
 779        drm_encoder_init(dev, drm_encoder,
 780                        &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL);
 781
 782        drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs);
 783
 784        return drm_encoder;
 785}
 786
 787static void sti_tvout_create_encoders(struct drm_device *dev,
 788                struct sti_tvout *tvout)
 789{
 790        tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout);
 791        tvout->hda = sti_tvout_create_hda_encoder(dev, tvout);
 792        tvout->dvo = sti_tvout_create_dvo_encoder(dev, tvout);
 793}
 794
 795static void sti_tvout_destroy_encoders(struct sti_tvout *tvout)
 796{
 797        if (tvout->hdmi)
 798                drm_encoder_cleanup(tvout->hdmi);
 799        tvout->hdmi = NULL;
 800
 801        if (tvout->hda)
 802                drm_encoder_cleanup(tvout->hda);
 803        tvout->hda = NULL;
 804
 805        if (tvout->dvo)
 806                drm_encoder_cleanup(tvout->dvo);
 807        tvout->dvo = NULL;
 808}
 809
 810static int sti_tvout_bind(struct device *dev, struct device *master, void *data)
 811{
 812        struct sti_tvout *tvout = dev_get_drvdata(dev);
 813        struct drm_device *drm_dev = data;
 814
 815        tvout->drm_dev = drm_dev;
 816
 817        sti_tvout_create_encoders(drm_dev, tvout);
 818
 819        return 0;
 820}
 821
 822static void sti_tvout_unbind(struct device *dev, struct device *master,
 823        void *data)
 824{
 825        struct sti_tvout *tvout = dev_get_drvdata(dev);
 826
 827        sti_tvout_destroy_encoders(tvout);
 828}
 829
 830static const struct component_ops sti_tvout_ops = {
 831        .bind   = sti_tvout_bind,
 832        .unbind = sti_tvout_unbind,
 833};
 834
 835static int sti_tvout_probe(struct platform_device *pdev)
 836{
 837        struct device *dev = &pdev->dev;
 838        struct device_node *node = dev->of_node;
 839        struct sti_tvout *tvout;
 840        struct resource *res;
 841
 842        DRM_INFO("%s\n", __func__);
 843
 844        if (!node)
 845                return -ENODEV;
 846
 847        tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL);
 848        if (!tvout)
 849                return -ENOMEM;
 850
 851        tvout->dev = dev;
 852
 853        /* get memory resources */
 854        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
 855        if (!res) {
 856                DRM_ERROR("Invalid glue resource\n");
 857                return -ENOMEM;
 858        }
 859        tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
 860        if (!tvout->regs)
 861                return -ENOMEM;
 862
 863        /* get reset resources */
 864        tvout->reset = devm_reset_control_get(dev, "tvout");
 865        /* take tvout out of reset */
 866        if (!IS_ERR(tvout->reset))
 867                reset_control_deassert(tvout->reset);
 868
 869        platform_set_drvdata(pdev, tvout);
 870
 871        return component_add(dev, &sti_tvout_ops);
 872}
 873
 874static int sti_tvout_remove(struct platform_device *pdev)
 875{
 876        component_del(&pdev->dev, &sti_tvout_ops);
 877        return 0;
 878}
 879
 880static const struct of_device_id tvout_of_match[] = {
 881        { .compatible = "st,stih407-tvout", },
 882        { /* end node */ }
 883};
 884MODULE_DEVICE_TABLE(of, tvout_of_match);
 885
 886struct platform_driver sti_tvout_driver = {
 887        .driver = {
 888                .name = "sti-tvout",
 889                .owner = THIS_MODULE,
 890                .of_match_table = tvout_of_match,
 891        },
 892        .probe = sti_tvout_probe,
 893        .remove = sti_tvout_remove,
 894};
 895
 896MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
 897MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
 898MODULE_LICENSE("GPL");
 899